mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
udev: replace get_user/group_creds() with userdb/groupdb_by_name() (#37304)
This also makes networkd not refuse User=/Group=root.
This commit is contained in:
@@ -1086,7 +1086,7 @@ int getpwnam_malloc(const char *name, struct passwd **ret) {
|
||||
for (;;) {
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
|
||||
buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
|
||||
buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1127,7 +1127,7 @@ int getpwuid_malloc(uid_t uid, struct passwd **ret) {
|
||||
for (;;) {
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
|
||||
buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
|
||||
buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1171,7 +1171,7 @@ int getgrnam_malloc(const char *name, struct group **ret) {
|
||||
for (;;) {
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
|
||||
buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
|
||||
buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1210,7 +1210,7 @@ int getgrgid_malloc(gid_t gid, struct group **ret) {
|
||||
for (;;) {
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
|
||||
buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
|
||||
buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bitfield.h"
|
||||
#include "daemon-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "networkd-link.h"
|
||||
@@ -175,11 +174,11 @@ static int netdev_create_tuntap(NetDev *netdev) {
|
||||
return log_netdev_error_errno(netdev, errno, "TUNSETQUEUE failed: %m");
|
||||
}
|
||||
|
||||
if (t->uid != 0)
|
||||
if (uid_is_valid(t->uid))
|
||||
if (ioctl(fd, TUNSETOWNER, t->uid) < 0)
|
||||
return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed: %m");
|
||||
|
||||
if (t->gid != 0)
|
||||
if (gid_is_valid(t->gid))
|
||||
if (ioctl(fd, TUNSETGROUP, t->gid) < 0)
|
||||
return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed: %m");
|
||||
|
||||
@@ -209,6 +208,13 @@ static void tuntap_done(NetDev *netdev) {
|
||||
t->group_name = mfree(t->group_name);
|
||||
}
|
||||
|
||||
static void tuntap_init(NetDev *netdev) {
|
||||
TunTap *t = TUNTAP(netdev);
|
||||
|
||||
t->uid = UID_INVALID;
|
||||
t->gid = GID_INVALID;
|
||||
}
|
||||
|
||||
static int tuntap_verify(NetDev *netdev, const char *filename) {
|
||||
TunTap *t = TUNTAP(netdev);
|
||||
int r;
|
||||
@@ -229,11 +235,8 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
|
||||
|
||||
if (t->user_name) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
UserDBMatch match = USERDB_MATCH_NULL;
|
||||
|
||||
match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_SYSTEM);
|
||||
|
||||
r = userdb_by_name(t->user_name, &match, USERDB_PARSE_NUMERIC, &ur);
|
||||
r = userdb_by_name(t->user_name, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC, &ur);
|
||||
if (r == -ENOEXEC)
|
||||
log_netdev_warning_errno(netdev, r, "User %s is not a system user, ignoring.", t->user_name);
|
||||
else if (r < 0)
|
||||
@@ -244,11 +247,8 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
|
||||
|
||||
if (t->group_name) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
UserDBMatch match = USERDB_MATCH_NULL;
|
||||
|
||||
match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_SYSTEM);
|
||||
|
||||
r = groupdb_by_name(t->group_name, &match, USERDB_PARSE_NUMERIC, &gr);
|
||||
r = groupdb_by_name(t->group_name, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC, &gr);
|
||||
if (r == -ENOEXEC)
|
||||
log_netdev_warning_errno(netdev, r, "Group %s is not a system group, ignoring.", t->group_name);
|
||||
else if (r < 0)
|
||||
@@ -263,6 +263,7 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
|
||||
const NetDevVTable tun_vtable = {
|
||||
.object_size = sizeof(TunTap),
|
||||
.sections = NETDEV_COMMON_SECTIONS "Tun\0",
|
||||
.init = tuntap_init,
|
||||
.config_verify = tuntap_verify,
|
||||
.drop = tuntap_drop,
|
||||
.done = tuntap_done,
|
||||
@@ -274,6 +275,7 @@ const NetDevVTable tun_vtable = {
|
||||
const NetDevVTable tap_vtable = {
|
||||
.object_size = sizeof(TunTap),
|
||||
.sections = NETDEV_COMMON_SECTIONS "Tap\0",
|
||||
.init = tuntap_init,
|
||||
.config_verify = tuntap_verify,
|
||||
.drop = tuntap_drop,
|
||||
.done = tuntap_done,
|
||||
|
||||
@@ -42,6 +42,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(GroupRecord*, group_record_unref);
|
||||
|
||||
int group_record_load(GroupRecord *h, sd_json_variant *v, UserRecordLoadFlags flags);
|
||||
int group_record_build(GroupRecord **ret, ...);
|
||||
#define group_record_buildo(ret, ...) \
|
||||
group_record_build((ret), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
|
||||
|
||||
bool group_record_match(GroupRecord *h, const UserDBMatch *match);
|
||||
|
||||
@@ -177,9 +177,9 @@ int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
struct spwd spwd, *result;
|
||||
struct spwd spwd = {}, *result = NULL;
|
||||
|
||||
buf = malloc(buflen);
|
||||
buf = malloc0(buflen);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -349,9 +349,9 @@ int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **re
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
struct sgrp sgrp, *result;
|
||||
struct sgrp sgrp = {}, *result = NULL;
|
||||
|
||||
buf = malloc(buflen);
|
||||
buf = malloc0(buflen);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "sd-id128.h"
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "bitfield.h"
|
||||
#include "hashmap.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "strv.h"
|
||||
@@ -434,6 +435,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UserRecord*, user_record_unref);
|
||||
|
||||
int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags flags);
|
||||
int user_record_build(UserRecord **ret, ...);
|
||||
#define user_record_buildo(ret, ...) \
|
||||
user_record_build((ret), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
|
||||
const char* user_record_user_name_and_realm(UserRecord *h);
|
||||
UserStorage user_record_storage(UserRecord *h);
|
||||
@@ -525,12 +528,21 @@ typedef struct UserDBMatch {
|
||||
#define USER_DISPOSITION_MASK_ALL ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
|
||||
#define USERDB_MATCH_NULL \
|
||||
(UserDBMatch) { \
|
||||
(const UserDBMatch) { \
|
||||
.disposition_mask = USER_DISPOSITION_MASK_ALL, \
|
||||
.uid_min = 0, \
|
||||
.uid_max = UID_INVALID-1, \
|
||||
}
|
||||
|
||||
/* Maybe useful when we want to resolve root and system user/group but want to refuse nobody user/group. */
|
||||
#define USERDB_MATCH_ROOT_AND_SYSTEM \
|
||||
(const UserDBMatch) { \
|
||||
.disposition_mask = \
|
||||
INDEXES_TO_MASK(uint64_t, USER_INTRINSIC, USER_SYSTEM), \
|
||||
.uid_min = 0, \
|
||||
.uid_max = UID_NOBODY - 1, \
|
||||
}
|
||||
|
||||
static inline bool userdb_match_is_set(const UserDBMatch *match) {
|
||||
if (!match)
|
||||
return false;
|
||||
|
||||
@@ -695,24 +695,24 @@ static int userdb_process(
|
||||
}
|
||||
|
||||
static int synthetic_root_user_build(UserRecord **ret) {
|
||||
return user_record_build(
|
||||
return user_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("userName", JSON_BUILD_CONST_STRING("root")),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/root")),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
SD_JSON_BUILD_PAIR("userName", JSON_BUILD_CONST_STRING("root")),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/root")),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic")));
|
||||
}
|
||||
|
||||
static int synthetic_nobody_user_build(UserRecord **ret) {
|
||||
return user_record_build(
|
||||
return user_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("userName", JSON_BUILD_CONST_STRING(NOBODY_USER_NAME)),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(UID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(GID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
|
||||
SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
SD_JSON_BUILD_PAIR("userName", JSON_BUILD_CONST_STRING(NOBODY_USER_NAME)),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(UID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(GID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
|
||||
SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic")));
|
||||
}
|
||||
|
||||
static int synthetic_foreign_user_build(uid_t foreign_uid, UserRecord **ret) {
|
||||
@@ -731,16 +731,40 @@ static int synthetic_foreign_user_build(uid_t foreign_uid, UserRecord **ret) {
|
||||
if (asprintf(&rn, "Foreign System Image UID " UID_FMT, foreign_uid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return user_record_build(
|
||||
return user_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(
|
||||
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(un)),
|
||||
SD_JSON_BUILD_PAIR("realName", SD_JSON_BUILD_STRING(rn)),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
|
||||
SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign"))));
|
||||
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(un)),
|
||||
SD_JSON_BUILD_PAIR("realName", SD_JSON_BUILD_STRING(rn)),
|
||||
SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
|
||||
SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign")));
|
||||
}
|
||||
|
||||
static int synthetic_numeric_user_build(uid_t uid, UserRecord **ret) {
|
||||
assert(ret);
|
||||
|
||||
if (uid == 0) /* This should be handled by synthetic_root_user_build() */
|
||||
return -ESRCH;
|
||||
|
||||
if (!uid_is_system(uid))
|
||||
return -ESRCH;
|
||||
|
||||
_cleanup_free_ char *un = NULL;
|
||||
if (asprintf(&un, "unknown-" UID_FMT, uid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
_cleanup_free_ char *rn = NULL;
|
||||
if (asprintf(&rn, "Unknown System UID " UID_FMT, uid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return user_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_STRING("userName", un),
|
||||
SD_JSON_BUILD_PAIR_STRING("realName", rn),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("uid", uid),
|
||||
SD_JSON_BUILD_PAIR_STRING("disposition", "system"));
|
||||
}
|
||||
|
||||
static int user_name_foreign_extract_uid(const char *name, uid_t *ret_uid) {
|
||||
@@ -964,6 +988,9 @@ static int userdb_by_uid_fallbacks(
|
||||
if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && uid_is_foreign(uid))
|
||||
return synthetic_foreign_user_build(uid - FOREIGN_UID_BASE, ret);
|
||||
|
||||
if (FLAGS_SET(flags, USERDB_SYNTHESIZE_NUMERIC))
|
||||
return synthetic_numeric_user_build(uid, ret);
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
@@ -1194,19 +1221,19 @@ int userdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, User
|
||||
}
|
||||
|
||||
static int synthetic_root_group_build(GroupRecord **ret) {
|
||||
return group_record_build(
|
||||
return group_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("groupName", JSON_BUILD_CONST_STRING("root")),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
SD_JSON_BUILD_PAIR("groupName", JSON_BUILD_CONST_STRING("root")),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(0)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic")));
|
||||
}
|
||||
|
||||
static int synthetic_nobody_group_build(GroupRecord **ret) {
|
||||
return group_record_build(
|
||||
return group_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("groupName", JSON_BUILD_CONST_STRING(NOBODY_GROUP_NAME)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(GID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
SD_JSON_BUILD_PAIR("groupName", JSON_BUILD_CONST_STRING(NOBODY_GROUP_NAME)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(GID_NOBODY)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic")));
|
||||
}
|
||||
|
||||
static int synthetic_foreign_group_build(gid_t foreign_gid, GroupRecord **ret) {
|
||||
@@ -1225,13 +1252,37 @@ static int synthetic_foreign_group_build(gid_t foreign_gid, GroupRecord **ret) {
|
||||
if (asprintf(&d, "Foreign System Image GID " GID_FMT, foreign_gid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return group_record_build(
|
||||
return group_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_OBJECT(
|
||||
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(gn)),
|
||||
SD_JSON_BUILD_PAIR("description", SD_JSON_BUILD_STRING(d)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_gid)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign"))));
|
||||
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(gn)),
|
||||
SD_JSON_BUILD_PAIR("description", SD_JSON_BUILD_STRING(d)),
|
||||
SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_gid)),
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign")));
|
||||
}
|
||||
|
||||
static int synthetic_numeric_group_build(gid_t gid, GroupRecord **ret) {
|
||||
assert(ret);
|
||||
|
||||
if (gid == 0) /* This should be handled by synthetic_root_group_build() */
|
||||
return -ESRCH;
|
||||
|
||||
if (!gid_is_system(gid))
|
||||
return -ESRCH;
|
||||
|
||||
_cleanup_free_ char *gn = NULL;
|
||||
if (asprintf(&gn, "unknown-" GID_FMT, gid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
_cleanup_free_ char *d = NULL;
|
||||
if (asprintf(&d, "Unknown System GID " UID_FMT, gid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return group_record_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_STRING("groupName", gn),
|
||||
SD_JSON_BUILD_PAIR_STRING("description", d),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("gid", gid),
|
||||
SD_JSON_BUILD_PAIR_STRING("disposition", "system"));
|
||||
}
|
||||
|
||||
static int query_append_gid_match(sd_json_variant **query, const UserDBMatch *match) {
|
||||
@@ -1389,6 +1440,9 @@ static int groupdb_by_gid_fallbacks(
|
||||
if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && gid_is_foreign(gid))
|
||||
return synthetic_foreign_group_build(gid - FOREIGN_UID_BASE, ret);
|
||||
|
||||
if (FLAGS_SET(flags, USERDB_SYNTHESIZE_NUMERIC))
|
||||
return synthetic_numeric_group_build(gid, ret);
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ typedef enum UserDBFlags {
|
||||
USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN,
|
||||
|
||||
USERDB_PARSE_NUMERIC = 1 << 8, /* if a numeric UID is specified as name, parse it and look up by UID/GID */
|
||||
USERDB_SYNTHESIZE_NUMERIC = 1 << 8, /* synthesize system UID/GID even if it does not exist */
|
||||
} UserDBFlags;
|
||||
|
||||
/* Well-known errors we'll return here:
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
#include "udev-trace.h"
|
||||
#include "udev-util.h"
|
||||
#include "udev-worker.h"
|
||||
#include "uid-classification.h"
|
||||
#include "user-util.h"
|
||||
#include "userdb.h"
|
||||
#include "virt.h"
|
||||
|
||||
#define RULES_DIRS ((const char* const*) CONF_PATHS_STRV("udev/rules.d"))
|
||||
@@ -490,83 +489,71 @@ UdevRules* udev_rules_free(UdevRules *rules) {
|
||||
|
||||
static int rule_resolve_user(UdevRuleLine *rule_line, const char *name, uid_t *ret) {
|
||||
Hashmap **known_users = &LINE_GET_RULES(rule_line)->known_users;
|
||||
_cleanup_free_ char *n = NULL;
|
||||
uid_t uid;
|
||||
void *val;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
val = hashmap_get(*known_users, name);
|
||||
void *val = hashmap_get(*known_users, name);
|
||||
if (val) {
|
||||
*ret = PTR_TO_UID(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = get_user_creds(
|
||||
&name,
|
||||
&uid,
|
||||
/* ret_gid = */ NULL,
|
||||
/* ret_home = */ NULL,
|
||||
/* ret_shell = */ NULL,
|
||||
USER_CREDS_ALLOW_MISSING);
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
r = userdb_by_name(name, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, &ur);
|
||||
if (r == -ESRCH)
|
||||
return log_line_error_errno(rule_line, r, "Unknown user '%s', ignoring.", name);
|
||||
if (r == -ENOEXEC)
|
||||
return log_line_error_errno(rule_line, r, "User '%s' is not a system user, ignoring.", name);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve user '%s', ignoring: %m", name);
|
||||
if (!uid_is_system(uid))
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"User '%s' is not a system user (UID="UID_FMT"), ignoring.", name, uid);
|
||||
|
||||
n = strdup(name);
|
||||
_cleanup_free_ char *n = strdup(name);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
r = hashmap_ensure_put(known_users, &string_hash_ops_free, n, UID_TO_PTR(uid));
|
||||
r = hashmap_ensure_put(known_users, &string_hash_ops_free, n, UID_TO_PTR(ur->uid));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(n);
|
||||
*ret = uid;
|
||||
*ret = ur->uid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rule_resolve_group(UdevRuleLine *rule_line, const char *name, gid_t *ret) {
|
||||
Hashmap **known_groups = &LINE_GET_RULES(rule_line)->known_groups;
|
||||
_cleanup_free_ char *n = NULL;
|
||||
gid_t gid;
|
||||
void *val;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
val = hashmap_get(*known_groups, name);
|
||||
void *val = hashmap_get(*known_groups, name);
|
||||
if (val) {
|
||||
*ret = PTR_TO_GID(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = get_group_creds(&name, &gid, USER_CREDS_ALLOW_MISSING);
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
r = groupdb_by_name(name, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, &gr);
|
||||
if (r == -ESRCH)
|
||||
return log_line_error_errno(rule_line, r, "Unknown group '%s', ignoring.", name);
|
||||
if (r == -ENOEXEC)
|
||||
return log_line_error_errno(rule_line, r, "Group '%s' is not a system group, ignoring.", name);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve group '%s', ignoring: %m", name);
|
||||
if (!gid_is_system(gid))
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Group '%s' is not a system group (GID="GID_FMT"), ignoring.", name, gid);
|
||||
|
||||
n = strdup(name);
|
||||
_cleanup_free_ char *n = strdup(name);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
r = hashmap_ensure_put(known_groups, &string_hash_ops_free, n, GID_TO_PTR(gid));
|
||||
r = hashmap_ensure_put(known_groups, &string_hash_ops_free, n, GID_TO_PTR(gr->gid));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(n);
|
||||
*ret = gid;
|
||||
*ret = gr->gid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1048,8 +1035,6 @@ static int parse_token(
|
||||
return 0;
|
||||
}
|
||||
} else if (streq(key, "OWNER")) {
|
||||
uid_t uid;
|
||||
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (is_match || op == OP_REMOVE)
|
||||
@@ -1059,18 +1044,15 @@ static int parse_token(
|
||||
op = OP_ASSIGN;
|
||||
}
|
||||
|
||||
if (parse_uid(value, &uid) >= 0) {
|
||||
if (!uid_is_system(uid))
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"UID="UID_FMT" is not in the system user range, ignoring.", uid);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false, token_str);
|
||||
} else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
if (in_charset(value, DIGITS) ||
|
||||
(resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN)) {
|
||||
uid_t uid = UID_INVALID; /* avoid false maybe-uninitialized warning */
|
||||
|
||||
r = rule_resolve_user(rule_line, value, &uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(uid_is_valid(uid));
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false, token_str);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
@@ -1081,8 +1063,6 @@ static int parse_token(
|
||||
return 0;
|
||||
}
|
||||
} else if (streq(key, "GROUP")) {
|
||||
gid_t gid;
|
||||
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (is_match || op == OP_REMOVE)
|
||||
@@ -1092,18 +1072,15 @@ static int parse_token(
|
||||
op = OP_ASSIGN;
|
||||
}
|
||||
|
||||
if (parse_gid(value, &gid) >= 0) {
|
||||
if (!gid_is_system(gid))
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"GID="GID_FMT" is not in the system group range, ignoring.", gid);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false, token_str);
|
||||
} else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
if (in_charset(value, DIGITS) ||
|
||||
(resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN)) {
|
||||
gid_t gid = GID_INVALID; /* avoid false maybe-uninitialized warning */
|
||||
|
||||
r = rule_resolve_group(rule_line, value, &gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(gid_is_valid(gid));
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false, token_str);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
@@ -2678,7 +2655,6 @@ static int udev_rule_apply_token_to_event(
|
||||
}
|
||||
case TK_A_OWNER: {
|
||||
char owner[UDEV_NAME_SIZE];
|
||||
const char *ow = owner;
|
||||
|
||||
if (event->owner_final)
|
||||
return log_event_final_set(event, token);
|
||||
@@ -2689,29 +2665,22 @@ static int udev_rule_apply_token_to_event(
|
||||
if (!apply_format_value(event, token, owner, sizeof(owner), "user name"))
|
||||
return true;
|
||||
|
||||
uid_t uid;
|
||||
r = get_user_creds(
|
||||
&ow,
|
||||
&uid,
|
||||
/* ret_gid = */ NULL,
|
||||
/* ret_home = */ NULL,
|
||||
/* ret_shell = */ NULL,
|
||||
USER_CREDS_ALLOW_MISSING);
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
r = userdb_by_name(owner, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, &ur);
|
||||
if (r == -ESRCH)
|
||||
log_event_error_errno(event, token, r, "Unknown user \"%s\", ignoring.", owner);
|
||||
else if (r == -ENOEXEC)
|
||||
log_event_error(event, token, "User \"%s\" is not a system user, ignoring.", owner);
|
||||
else if (r < 0)
|
||||
log_event_error_errno(event, token, r, "Failed to resolve user \"%s\", ignoring: %m", owner);
|
||||
else if (!uid_is_system(uid))
|
||||
log_event_error(event, token, "User \"%s\" is not a system user (UID="UID_FMT"), ignoring.", owner, uid);
|
||||
else {
|
||||
event->uid = uid;
|
||||
log_event_debug(event, token, "Set owner: %s(%u)", owner, event->uid);
|
||||
event->uid = ur->uid;
|
||||
log_event_debug(event, token, "Set owner: %s("UID_FMT")", owner, event->uid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TK_A_GROUP: {
|
||||
char group[UDEV_NAME_SIZE];
|
||||
const char *gr = group;
|
||||
|
||||
if (event->group_final)
|
||||
return log_event_final_set(event, token);
|
||||
@@ -2722,17 +2691,17 @@ static int udev_rule_apply_token_to_event(
|
||||
if (!apply_format_value(event, token, group, sizeof(group), "group name"))
|
||||
return true;
|
||||
|
||||
gid_t gid;
|
||||
r = get_group_creds(&gr, &gid, USER_CREDS_ALLOW_MISSING);
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
r = groupdb_by_name(group, &USERDB_MATCH_ROOT_AND_SYSTEM, USERDB_PARSE_NUMERIC | USERDB_SYNTHESIZE_NUMERIC, &gr);
|
||||
if (r == -ESRCH)
|
||||
log_event_error_errno(event, token, r, "Unknown group \"%s\", ignoring.", group);
|
||||
else if (r == -ENOEXEC)
|
||||
log_event_error(event, token, "Group \"%s\" is not a system group, ignoring.", group);
|
||||
else if (r < 0)
|
||||
log_event_error_errno(event, token, r, "Failed to resolve group \"%s\", ignoring: %m", group);
|
||||
else if (!gid_is_system(gid))
|
||||
log_event_error(event, token, "Group \"%s\" is not a system group (GID="GID_FMT"), ignoring.", group, gid);
|
||||
else {
|
||||
event->gid = gid;
|
||||
log_event_debug(event, token, "Set group: %s(%u)", group, event->gid);
|
||||
event->gid = gr->gid;
|
||||
log_event_debug(event, token, "Set group: %s("GID_FMT")", group, event->gid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -291,16 +291,52 @@ test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
|
||||
test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
|
||||
test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
|
||||
test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
|
||||
test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
|
||||
test_syntax_error 'OWNER="testuser"' "User 'testuser' is not a system user (UID=$(id -u testuser 2>/dev/null)), ignoring."
|
||||
test_syntax_error 'OWNER="12345"' "UID=12345 is not in the system user range, ignoring."
|
||||
# numeric system UID is valid even if it does not exist
|
||||
SYS_UID_MAX=999
|
||||
if [[ -e /etc/login.defs ]]; then
|
||||
SYS_UID_MAX=$(awk '$1 == "SYS_UID_MAX" { print $2 }' /etc/login.defs)
|
||||
fi
|
||||
for ((i=0;i<=SYS_UID_MAX;i++)); do
|
||||
echo "OWNER=\"$i\""
|
||||
done >"${rules}"
|
||||
assert_0 "${rules}"
|
||||
# invalid user name
|
||||
test_syntax_error 'OWNER=":nosuchuser:"' "Failed to resolve user ':nosuchuser:', ignoring: Invalid argument"
|
||||
# nonexistent user
|
||||
if ! getent passwd nosuchuser >/dev/null; then
|
||||
test_syntax_error 'OWNER="nosuchuser"' "Unknown user 'nosuchuser', ignoring."
|
||||
fi
|
||||
if ! getent passwd 12345 >/dev/null; then
|
||||
test_syntax_error 'OWNER="12345"' "Unknown user '12345', ignoring."
|
||||
fi
|
||||
# regular user
|
||||
test_syntax_error 'OWNER="testuser"' "User 'testuser' is not a system user, ignoring."
|
||||
test_syntax_error "OWNER=\"$(id -u testuser)\"" "User '$(id -u testuser)' is not a system user, ignoring."
|
||||
test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
|
||||
test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
|
||||
test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
|
||||
test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
|
||||
test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
|
||||
test_syntax_error 'GROUP="testuser"' "Group 'testuser' is not a system group (GID=$(id -u testuser 2>/dev/null)), ignoring."
|
||||
test_syntax_error 'GROUP="12345"' "GID=12345 is not in the system group range, ignoring."
|
||||
# numeric system GID is valid even if it does not exist
|
||||
SYS_GID_MAX=999
|
||||
if [[ -e /etc/login.defs ]]; then
|
||||
SYS_GID_MAX=$(awk '$1 == "SYS_GID_MAX" { print $2 }' /etc/login.defs)
|
||||
fi
|
||||
for ((i=0;i<=SYS_GID_MAX;i++)); do
|
||||
echo "GROUP=\"$i\""
|
||||
done >"${rules}"
|
||||
assert_0 "${rules}"
|
||||
# invalid group name
|
||||
test_syntax_error 'GROUP=":nosuchgroup:"' "Failed to resolve group ':nosuchgroup:', ignoring: Invalid argument"
|
||||
# nonexistent group
|
||||
if ! getent group nosuchgroup >/dev/null; then
|
||||
test_syntax_error 'GROUP="nosuchgroup"' "Unknown group 'nosuchgroup', ignoring."
|
||||
fi
|
||||
if ! getent group 12345 >/dev/null; then
|
||||
test_syntax_error 'GROUP="12345"' "Unknown group '12345', ignoring."
|
||||
fi
|
||||
# regular group
|
||||
test_syntax_error 'GROUP="testuser"' "Group 'testuser' is not a system group, ignoring."
|
||||
test_syntax_error "GROUP=\"$(id -g testuser)\"" "Group '$(id -g testuser)' is not a system group, ignoring."
|
||||
test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
|
||||
test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
|
||||
test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
|
||||
|
||||
Reference in New Issue
Block a user