summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2018-08-21 10:09:33 +0200
committerGitHub <noreply@github.com>2018-08-21 10:09:33 +0200
commit7692fed98b784be92f900151d867f0be0975e062 (patch)
tree74af3a142a54344cb739ff0bc485bb8ad5ef60ec
parentMerge pull request #9811 from poettering/random-seed-tweaks (diff)
parentRename USER_CREDS_SYNTHESIZE_FALLBACK to …_PREFER_NSS (diff)
downloadsystemd-7692fed98b784be92f900151d867f0be0975e062.tar.xz
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.c173
-rw-r--r--src/basic/user-util.h11
-rw-r--r--src/core/execute.c6
-rw-r--r--src/core/namespace.c51
-rw-r--r--src/core/socket.c4
-rw-r--r--src/coredump/coredump.c2
-rw-r--r--src/login/loginctl.c8
-rw-r--r--src/login/logind-core.c2
-rw-r--r--src/login/user-runtime-dir.c4
-rw-r--r--src/mount/mount-tool.c2
-rw-r--r--src/network/netdev/tuntap.c8
-rw-r--r--src/network/networkd.c2
-rw-r--r--src/notify/notify.c2
-rw-r--r--src/nspawn/nspawn.c32
-rw-r--r--src/resolve/resolved.c2
-rw-r--r--src/resolve/test-resolved-etc-hosts.c3
-rw-r--r--src/run/run.c4
-rw-r--r--src/shared/condition.c2
-rw-r--r--src/test/test-acl-util.c2
-rw-r--r--src/test/test-ipcrm.c2
-rw-r--r--src/test/test-user-util.c4
-rw-r--r--src/timesync/timesyncd.c2
-rw-r--r--src/tmpfiles/tmpfiles.c4
-rw-r--r--src/udev/udev-rules.c8
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;