diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2018-08-21 10:09:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-21 10:09:33 +0200 |
commit | 7692fed98b784be92f900151d867f0be0975e062 (patch) | |
tree | 74af3a142a54344cb739ff0bc485bb8ad5ef60ec | |
parent | a795e5a9dd7a38817545226fec721075e5080ade (diff) | |
parent | 43ad3ad7ea3dfc93d91e8bc3a47a03596705c93c (diff) | |
download | systemd-7692fed98b784be92f900151d867f0be0975e062.tar.gz systemd-7692fed98b784be92f900151d867f0be0975e062.tar.bz2 systemd-7692fed98b784be92f900151d867f0be0975e062.zip |
Merge pull request #9783 from poettering/get-user-creds-flags
beef up get_user_creds() a bit and other improvements
-rw-r--r-- | src/basic/user-util.c | 173 | ||||
-rw-r--r-- | src/basic/user-util.h | 11 | ||||
-rw-r--r-- | src/core/execute.c | 6 | ||||
-rw-r--r-- | src/core/namespace.c | 51 | ||||
-rw-r--r-- | src/core/socket.c | 4 | ||||
-rw-r--r-- | src/coredump/coredump.c | 2 | ||||
-rw-r--r-- | src/login/loginctl.c | 8 | ||||
-rw-r--r-- | src/login/logind-core.c | 2 | ||||
-rw-r--r-- | src/login/user-runtime-dir.c | 4 | ||||
-rw-r--r-- | src/mount/mount-tool.c | 2 | ||||
-rw-r--r-- | src/network/netdev/tuntap.c | 8 | ||||
-rw-r--r-- | src/network/networkd.c | 2 | ||||
-rw-r--r-- | src/notify/notify.c | 2 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 32 | ||||
-rw-r--r-- | src/resolve/resolved.c | 2 | ||||
-rw-r--r-- | src/resolve/test-resolved-etc-hosts.c | 3 | ||||
-rw-r--r-- | src/run/run.c | 4 | ||||
-rw-r--r-- | src/shared/condition.c | 2 | ||||
-rw-r--r-- | src/test/test-acl-util.c | 2 | ||||
-rw-r--r-- | src/test/test-ipcrm.c | 2 | ||||
-rw-r--r-- | src/test/test-user-util.c | 4 | ||||
-rw-r--r-- | src/timesync/timesyncd.c | 2 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 4 | ||||
-rw-r--r-- | src/udev/udev-rules.c | 8 |
24 files changed, 216 insertions, 124 deletions
diff --git a/src/basic/user-util.c b/src/basic/user-util.c index a562a397c7..d410c9068b 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -87,17 +87,31 @@ char *getusername_malloc(void) { return uid_to_name(getuid()); } -int get_user_creds( +static inline bool is_nologin_shell(const char *shell) { + + return PATH_IN_SET(shell, + /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice + * message and exits. Different distributions place the binary at different places though, + * hence let's list them all. */ + "/bin/nologin", + "/sbin/nologin", + "/usr/bin/nologin", + "/usr/sbin/nologin", + /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do + * any message printing. Different distributions place the binary at various places but at + * least not in the 'sbin' directory. */ + "/bin/false", + "/usr/bin/false", + "/bin/true", + "/usr/bin/true"); +} + +static int synthesize_user_creds( const char **username, uid_t *uid, gid_t *gid, const char **home, - const char **shell) { - - struct passwd *p; - uid_t u; - - assert(username); - assert(*username); + const char **shell, + UserCredsFlags flags) { /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode * their user record data. */ @@ -129,32 +143,85 @@ int get_user_creds( *gid = GID_NOBODY; if (home) - *home = "/"; + *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/"; if (shell) - *shell = "/sbin/nologin"; + *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/sbin/nologin"; return 0; } + return -ENOMEDIUM; +} + +int get_user_creds( + const char **username, + uid_t *uid, gid_t *gid, + const char **home, + const char **shell, + UserCredsFlags flags) { + + uid_t u = UID_INVALID; + struct passwd *p; + int r; + + assert(username); + assert(*username); + + if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) || + (!home && !shell)) { + + /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override + * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the + * user database will override the synthetic records instead — except if the user is only interested in + * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override + * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why? + * Simply because there are valid usecase where the user might change the home directory or the shell + * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't + * support. */ + + r = synthesize_user_creds(username, uid, gid, home, shell, flags); + if (r >= 0) + return 0; + if (r != -ENOMEDIUM) /* not a username we can synthesize */ + return r; + } + if (parse_uid(*username, &u) >= 0) { errno = 0; p = getpwuid(u); - /* If there are multiple users with the same id, make - * sure to leave $USER to the configured value instead - * of the first occurrence in the database. However if - * the uid was configured by a numeric uid, then let's - * pick the real username from /etc/passwd. */ + /* If there are multiple users with the same id, make sure to leave $USER to the configured value + * instead of the first occurrence in the database. However if the uid was configured by a numeric uid, + * then let's pick the real username from /etc/passwd. */ if (p) *username = p->pw_name; + else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) { + + /* If the specified user is a numeric UID and it isn't in the user database, and the caller + * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that + * and don't complain. */ + + if (uid) + *uid = u; + + return 0; + } } else { errno = 0; p = getpwnam(*username); } + if (!p) { + r = errno > 0 ? -errno : -ESRCH; - if (!p) - return errno > 0 ? -errno : -ESRCH; + /* If the user requested that we only synthesize as fallback, do so now */ + if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) { + if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0) + return 0; + } + + return r; + } if (uid) { if (!uid_is_valid(p->pw_uid)) @@ -170,66 +237,30 @@ int get_user_creds( *gid = p->pw_gid; } - if (home) - *home = p->pw_dir; - - if (shell) - *shell = p->pw_shell; - - return 0; -} - -static inline bool is_nologin_shell(const char *shell) { - - return PATH_IN_SET(shell, - /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice - * message and exits. Different distributions place the binary at different places though, - * hence let's list them all. */ - "/bin/nologin", - "/sbin/nologin", - "/usr/bin/nologin", - "/usr/sbin/nologin", - /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do - * any message printing. Different distributions place the binary at various places but at - * least not in the 'sbin' directory. */ - "/bin/false", - "/usr/bin/false", - "/bin/true", - "/usr/bin/true"); -} - -int get_user_creds_clean( - const char **username, - uid_t *uid, gid_t *gid, - const char **home, - const char **shell) { - - int r; - - /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ - - r = get_user_creds(username, uid, gid, home, shell); - if (r < 0) - return r; - - if (shell && - (isempty(*shell) || is_nologin_shell(*shell))) - *shell = NULL; + if (home) { + if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir)) + *home = NULL; + else + *home = p->pw_dir; + } - if (home && empty_or_root(*home)) - *home = NULL; + if (shell) { + if (FLAGS_SET(flags, USER_CREDS_CLEAN) && (isempty(p->pw_shell) || is_nologin_shell(p->pw_shell))) + *shell = NULL; + else + *shell = p->pw_shell; + } return 0; } -int get_group_creds(const char **groupname, gid_t *gid) { +int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) { struct group *g; gid_t id; assert(groupname); - /* We enforce some special rules for gid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ + /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */ if (STR_IN_SET(*groupname, "root", "0")) { *groupname = "root"; @@ -256,6 +287,12 @@ int get_group_creds(const char **groupname, gid_t *gid) { if (g) *groupname = g->gr_name; + else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) { + if (gid) + *gid = id; + + return 0; + } } else { errno = 0; g = getgrnam(*groupname); @@ -391,7 +428,7 @@ int in_group(const char *name) { int r; gid_t gid; - r = get_group_creds(&name, &gid); + r = get_group_creds(&name, &gid, 0); if (r < 0) return r; diff --git a/src/basic/user-util.h b/src/basic/user-util.h index b2f198c89b..b6de0e4234 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -25,9 +25,14 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) { char* getlogname_malloc(void); char* getusername_malloc(void); -int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); -int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); -int get_group_creds(const char **groupname, gid_t *gid); +typedef enum UserCredsFlags { + USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */ + USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */ + USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */ +} UserCredsFlags; + +int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags); +int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags); char* uid_to_name(uid_t uid); char* gid_to_name(gid_t gid); diff --git a/src/core/execute.c b/src/core/execute.c index e265102433..881c73595f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -921,7 +921,7 @@ static int get_fixed_user(const ExecContext *c, const char **user, * (i.e. are "/" or "/bin/nologin"). */ name = c->user; - r = get_user_creds_clean(&name, uid, gid, home, shell); + r = get_user_creds(&name, uid, gid, home, shell, USER_CREDS_CLEAN); if (r < 0) return r; @@ -939,7 +939,7 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid) return 0; name = c->group; - r = get_group_creds(&name, gid); + r = get_group_creds(&name, gid, 0); if (r < 0) return r; @@ -1011,7 +1011,7 @@ static int get_supplementary_groups(const ExecContext *c, const char *user, return -E2BIG; g = *i; - r = get_group_creds(&g, l_gids+k); + r = get_group_creds(&g, l_gids+k, 0); if (r < 0) return r; diff --git a/src/core/namespace.c b/src/core/namespace.c index 3488758e82..9031061df8 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -570,36 +570,44 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t *n = t - m; } -static int clone_device_node(const char *d, const char *temporary_mount, bool *make_devnode) { - const char *dn; +static int clone_device_node( + const char *d, + const char *temporary_mount, + bool *make_devnode) { + + _cleanup_free_ char *sl = NULL; + const char *dn, *bn, *t; struct stat st; int r; if (stat(d, &st) < 0) { - if (errno == ENOENT) + if (errno == ENOENT) { + log_debug_errno(errno, "Device node '%s' to clone does not exist, ignoring.", d); return -ENXIO; - return -errno; + } + + return log_debug_errno(errno, "Failed to stat() device node '%s' to clone, ignoring: %m", d); } if (!S_ISBLK(st.st_mode) && - !S_ISCHR(st.st_mode)) + !S_ISCHR(st.st_mode)) { + log_debug("Device node '%s' to clone is not a device node, ignoring.", d); return -EINVAL; - - if (st.st_rdev == 0) - return -ENXIO; + } dn = strjoina(temporary_mount, d); + /* First, try to create device node properly */ if (*make_devnode) { mac_selinux_create_file_prepare(d, st.st_mode); r = mknod(dn, st.st_mode, st.st_rdev); mac_selinux_create_file_clear(); - - if (r == 0) - return 0; + if (r >= 0) + goto add_symlink; if (errno != EPERM) return log_debug_errno(errno, "mknod failed for %s: %m", d); + /* This didn't work, let's not try this again for the next iterations. */ *make_devnode = false; } @@ -608,9 +616,8 @@ static int clone_device_node(const char *d, const char *temporary_mount, bool *m mac_selinux_create_file_prepare(d, 0); r = mknod(dn, S_IFREG, 0); mac_selinux_create_file_clear(); - if (r < 0 && errno != EEXIST) - return log_debug_errno(errno, "mknod fallback failed for %s: %m", d); + return log_debug_errno(errno, "mknod() fallback failed for '%s': %m", d); /* Fallback to bind-mounting: * The assumption here is that all used device nodes carry standard @@ -618,7 +625,23 @@ static int clone_device_node(const char *d, const char *temporary_mount, bool *m * either be owned by root:root or root:tty (e.g. /dev/tty, /dev/ptmx) * and should not carry ACLs. */ if (mount(d, dn, NULL, MS_BIND, NULL) < 0) - return log_debug_errno(errno, "mount failed for %s: %m", d); + return log_debug_errno(errno, "Bind mounting failed for '%s': %m", d); + +add_symlink: + bn = path_startswith(d, "/dev/"); + if (!bn) + return 0; + + /* Create symlinks like /dev/char/1:9 → ../urandom */ + if (asprintf(&sl, "%s/dev/%s/%u:%u", temporary_mount, S_ISCHR(st.st_mode) ? "char" : "block", major(st.st_rdev), minor(st.st_rdev)) < 0) + return log_oom(); + + (void) mkdir_parents(sl, 0755); + + t = strjoina("../", bn); + + if (symlink(t, sl) < 0) + log_debug_errno(errno, "Failed to symlink '%s' to '%s': %m", t, sl); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index fb17b3be2d..1dcd8162f4 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1934,7 +1934,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { if (!isempty(s->user)) { const char *user = s->user; - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) { log_unit_error_errno(UNIT(s), r, "Failed to resolve user %s: %m", user); _exit(EXIT_USER); @@ -1944,7 +1944,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { if (!isempty(s->group)) { const char *group = s->group; - r = get_group_creds(&group, &gid); + r = get_group_creds(&group, &gid, 0); if (r < 0) { log_unit_error_errno(UNIT(s), r, "Failed to resolve group %s: %m", group); _exit(EXIT_GROUP); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 20a1cbdd45..31274ee602 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -672,7 +672,7 @@ static int change_uid_gid(const char *context[]) { if (uid <= SYSTEM_UID_MAX) { const char *user = "systemd-coredump"; - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) { log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user); uid = gid = 0; diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 9faeecbddd..bdffca0e4a 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -909,7 +909,7 @@ static int show_user(int argc, char *argv[], void *userdata) { const char *path = NULL; uid_t uid; - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); @@ -1097,7 +1097,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) { if (isempty(argv[i])) uid = UID_INVALID; else { - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); } @@ -1130,7 +1130,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { uid_t uid; - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); @@ -1165,7 +1165,7 @@ static int kill_user(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { uid_t uid; - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index d78d928d03..ad7f9aecac 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -152,7 +152,7 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) { assert(m); assert(name); - r = get_user_creds(&name, &uid, &gid, NULL, NULL); + r = get_user_creds(&name, &uid, &gid, NULL, NULL, 0); if (r < 0) return r; diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index 51b864c9f8..9f2c594a05 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -117,7 +117,7 @@ static int do_mount(const char *user) { gid_t gid; int r; - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) return log_error_errno(r, r == -ESRCH ? "No such user \"%s\"" : @@ -141,7 +141,7 @@ static int do_umount(const char *user) { /* The user may be already removed. So, first try to parse the string by parse_uid(), * and if it fails, fallback to get_user_creds().*/ if (parse_uid(user, &uid) < 0) { - r = get_user_creds(&user, &uid, NULL, NULL, NULL); + r = get_user_creds(&user, &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, r == -ESRCH ? "No such user \"%s\"" : diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index 2e88e40646..6c446d7549 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -223,7 +223,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_OWNER: { const char *user = optarg; - r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL); + r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL, 0); if (r < 0) return log_error_errno(r, r == -EBADMSG ? "UID or GID of user %s are invalid." diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index 47c56cfd70..951138d257 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -60,7 +60,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(netdev); assert(ifr); - fd = open(TUN_DEV, O_RDWR); + fd = open(TUN_DEV, O_RDWR|O_CLOEXEC); if (fd < 0) return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); @@ -75,10 +75,9 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(t); if (t->user_name) { - user = t->user_name; - r = get_user_creds(&user, &uid, NULL, NULL, NULL); + r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); @@ -87,10 +86,9 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { } if (t->group_name) { - group = t->group_name; - r = get_group_creds(&group, &gid); + r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); diff --git a/src/network/networkd.c b/src/network/networkd.c index d2467b70ce..8f7b5b74fd 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { goto out; } - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto out; diff --git a/src/notify/notify.c b/src/notify/notify.c index 7ee7c342ff..243af7ba81 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -115,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_UID: { const char *u = optarg; - r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL); + r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL, 0); if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */ r = parse_uid(u, &arg_uid); if (r < 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b39afb0f9c..ad508bab76 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1790,7 +1790,12 @@ static int copy_devnodes(const char *dest) { struct stat st; from = strappend("/dev/", d); + if (!from) + return log_oom(); + to = prefix_root(dest, from); + if (!to) + return log_oom(); if (stat(from, &st) < 0) { @@ -1803,6 +1808,8 @@ static int copy_devnodes(const char *dest) { return -EIO; } else { + _cleanup_free_ char *sl = NULL, *prefixed = NULL, *dn = NULL, *t = NULL; + if (mknod(to, st.st_mode, st.st_rdev) < 0) { /* Explicitly warn the user when /dev is already populated. */ if (errno == EEXIST) @@ -1810,8 +1817,7 @@ static int copy_devnodes(const char *dest) { if (errno != EPERM) return log_error_errno(errno, "mknod(%s) failed: %m", to); - /* Some systems abusively restrict mknod but - * allow bind mounts. */ + /* Some systems abusively restrict mknod but allow bind mounts. */ r = touch(to); if (r < 0) return log_error_errno(r, "touch (%s) failed: %m", to); @@ -1823,6 +1829,28 @@ static int copy_devnodes(const char *dest) { r = userns_lchown(to, 0, 0); if (r < 0) return log_error_errno(r, "chown() of device node %s failed: %m", to); + + dn = strjoin("/dev/", S_ISCHR(st.st_mode) ? "char" : "block"); + if (!dn) + return log_oom(); + + r = userns_mkdir(dest, dn, 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create '%s': %m", dn); + + if (asprintf(&sl, "%s/%u:%u", dn, major(st.st_rdev), minor(st.st_rdev)) < 0) + return log_oom(); + + prefixed = prefix_root(dest, sl); + if (!prefixed) + return log_oom(); + + t = strjoin("../", d); + if (!t) + return log_oom(); + + if (symlink(t, prefixed) < 0) + log_debug_errno(errno, "Failed to symlink '%s' to '%s': %m", t, prefixed); } } diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 6ff56bc974..d4d6cba201 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -37,7 +37,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c index b7dba0d780..1b22e38202 100644 --- a/src/resolve/test-resolved-etc-hosts.c +++ b/src/resolve/test-resolved-etc-hosts.c @@ -34,6 +34,7 @@ static void test_parse_etc_hosts(const char *fname) { assert_se(fd >= 0); f = fdopen(fd, "r+"); + assert_se(f); fputs("1.2.3.4 some.where\n", f); fputs("1.2.3.5 some.where\n", f); fputs("::0 some.where some.other\n", f); @@ -49,7 +50,7 @@ static void test_parse_etc_hosts(const char *fname) { if (fname) return; - + EtcHostsItemByName *bn; assert_se(bn = hashmap_get(hosts.by_name, "some.where")); assert_se(bn->n_addresses == 3); diff --git a/src/run/run.c b/src/run/run.c index 5fe54d277a..697b517c65 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -1239,7 +1239,7 @@ static int start_transient_scope( if (arg_exec_group) { gid_t gid; - r = get_group_creds(&arg_exec_group, &gid); + r = get_group_creds(&arg_exec_group, &gid, 0); if (r < 0) return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); @@ -1252,7 +1252,7 @@ static int start_transient_scope( uid_t uid; gid_t gid; - r = get_user_creds_clean(&arg_exec_user, &uid, &gid, &home, &shell); + r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS); if (r < 0) return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); diff --git a/src/shared/condition.c b/src/shared/condition.c index 2969a89b4e..d0b17fe7ff 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -219,7 +219,7 @@ static int condition_test_user(Condition *c) { return streq(c->parameter, "root"); u = c->parameter; - r = get_user_creds(&u, &id, NULL, NULL, NULL); + r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) return 0; diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c index 81eb40444b..a8d413533e 100644 --- a/src/test/test-acl-util.c +++ b/src/test/test-acl-util.c @@ -32,7 +32,7 @@ static void test_add_acls_for_user(void) { if (getuid() == 0) { const char *nobody = NOBODY_USER_NAME; - r = get_user_creds(&nobody, &uid, NULL, NULL, NULL); + r = get_user_creds(&nobody, &uid, NULL, NULL, NULL, 0); if (r < 0) uid = 0; } else diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c index 106c29951e..6cdf48a490 100644 --- a/src/test/test-ipcrm.c +++ b/src/test/test-ipcrm.c @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { int r; const char* name = argv[1] ?: NOBODY_USER_NAME; - r = get_user_creds(&name, &uid, NULL, NULL, NULL); + r = get_user_creds(&name, &uid, NULL, NULL, NULL, 0); if (r < 0) { log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR, r, "Failed to resolve \"%s\": %m", name); diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index c1428fab02..b291aa0f04 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -159,7 +159,7 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, log_info("/* %s(\"%s\", \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\") */", __func__, id, name, uid, gid, home, shell); - r = get_user_creds(&id, &ruid, &rgid, &rhome, &rshell); + r = get_user_creds(&id, &ruid, &rgid, &rhome, &rshell, 0); log_info_errno(r, "got \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\": %m", id, ruid, rgid, strnull(rhome), strnull(rshell)); if (!synthesize_nobody() && streq(name, NOBODY_USER_NAME)) { @@ -180,7 +180,7 @@ static void test_get_group_creds_one(const char *id, const char *name, gid_t gid log_info("/* %s(\"%s\", \"%s\", "GID_FMT") */", __func__, id, name, gid); - r = get_group_creds(&id, &rgid); + r = get_group_creds(&id, &rgid, 0); log_info_errno(r, "got \"%s\", "GID_FMT": %m", id, rgid); if (!synthesize_nobody() && streq(name, NOBODY_GROUP_NAME)) { log_info("(skipping detailed tests because nobody is not synthesized)"); diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 987cded009..9a43c6193b 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -107,7 +107,7 @@ int main(int argc, char *argv[]) { gid = getegid(); if (uid_current == 0) { - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 4b522d6398..48c0fb6cb6 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -2728,7 +2728,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (!isempty(user) && !streq(user, "-")) { const char *u = user; - r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); + r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) { *invalid_config = true; return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user); @@ -2740,7 +2740,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (!isempty(group) && !streq(group, "-")) { const char *g = group; - r = get_group_creds(&g, &i.gid); + r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING); if (r < 0) { *invalid_config = true; log_error("[%s:%u] Unknown group '%s'.", fname, line, group); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index f9eff70295..58ce71c794 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -481,7 +481,7 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) { return uid; } } - r = get_user_creds(&owner, &uid, NULL, NULL, NULL); + r = get_user_creds(&owner, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(r, "user", owner); @@ -524,7 +524,7 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) { return gid; } } - r = get_group_creds(&group, &gid); + r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(r, "group", group); @@ -2110,7 +2110,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules, event->owner_final = true; udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false); event->owner_set = true; - r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); + r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) { log_unknown_owner(r, "user", owner); event->uid = 0; @@ -2131,7 +2131,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules, event->group_final = true; udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false); event->group_set = true; - r = get_group_creds(&gr, &event->gid); + r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING); if (r < 0) { log_unknown_owner(r, "group", group); event->gid = 0; |