1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-25 06:03:40 +03:00

Merge pull request #9783 from poettering/get-user-creds-flags

beef up get_user_creds() a bit and other improvements
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-08-21 10:09:33 +02:00 committed by GitHub
commit 7692fed98b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 216 additions and 124 deletions

View File

@ -87,17 +87,31 @@ char *getusername_malloc(void) {
return uid_to_name(getuid()); 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, const char **username,
uid_t *uid, gid_t *gid, uid_t *uid, gid_t *gid,
const char **home, const char **home,
const char **shell) { const char **shell,
UserCredsFlags flags) {
struct passwd *p;
uid_t u;
assert(username);
assert(*username);
/* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode /* 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. */ * their user record data. */
@ -129,32 +143,85 @@ int get_user_creds(
*gid = GID_NOBODY; *gid = GID_NOBODY;
if (home) if (home)
*home = "/"; *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
if (shell) if (shell)
*shell = "/sbin/nologin"; *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/sbin/nologin";
return 0; 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) { if (parse_uid(*username, &u) >= 0) {
errno = 0; errno = 0;
p = getpwuid(u); p = getpwuid(u);
/* If there are multiple users with the same id, make /* If there are multiple users with the same id, make sure to leave $USER to the configured value
* sure to leave $USER to the configured value instead * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
* of the first occurrence in the database. However if * then let's pick the real username from /etc/passwd. */
* the uid was configured by a numeric uid, then let's
* pick the real username from /etc/passwd. */
if (p) if (p)
*username = p->pw_name; *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 { } else {
errno = 0; errno = 0;
p = getpwnam(*username); p = getpwnam(*username);
} }
if (!p) {
r = errno > 0 ? -errno : -ESRCH;
if (!p) /* If the user requested that we only synthesize as fallback, do so now */
return errno > 0 ? -errno : -ESRCH; 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) {
if (!uid_is_valid(p->pw_uid)) if (!uid_is_valid(p->pw_uid))
@ -170,66 +237,30 @@ int get_user_creds(
*gid = p->pw_gid; *gid = p->pw_gid;
} }
if (home) if (home) {
*home = p->pw_dir; if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir))
*home = NULL;
else
*home = p->pw_dir;
}
if (shell) if (shell) {
*shell = p->pw_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; return 0;
} }
static inline bool is_nologin_shell(const char *shell) { int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
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 && empty_or_root(*home))
*home = NULL;
return 0;
}
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g; struct group *g;
gid_t id; gid_t id;
assert(groupname); assert(groupname);
/* We enforce some special rules for gid=0: in order to avoid /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
* NSS lookups for root we hardcode its data. */
if (STR_IN_SET(*groupname, "root", "0")) { if (STR_IN_SET(*groupname, "root", "0")) {
*groupname = "root"; *groupname = "root";
@ -256,6 +287,12 @@ int get_group_creds(const char **groupname, gid_t *gid) {
if (g) if (g)
*groupname = g->gr_name; *groupname = g->gr_name;
else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
if (gid)
*gid = id;
return 0;
}
} else { } else {
errno = 0; errno = 0;
g = getgrnam(*groupname); g = getgrnam(*groupname);
@ -391,7 +428,7 @@ int in_group(const char *name) {
int r; int r;
gid_t gid; gid_t gid;
r = get_group_creds(&name, &gid); r = get_group_creds(&name, &gid, 0);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -25,9 +25,14 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
char* getlogname_malloc(void); char* getlogname_malloc(void);
char* getusername_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); typedef enum UserCredsFlags {
int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); 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 */
int get_group_creds(const char **groupname, gid_t *gid); 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* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid); char* gid_to_name(gid_t gid);

View File

@ -921,7 +921,7 @@ static int get_fixed_user(const ExecContext *c, const char **user,
* (i.e. are "/" or "/bin/nologin"). */ * (i.e. are "/" or "/bin/nologin"). */
name = c->user; 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) if (r < 0)
return r; return r;
@ -939,7 +939,7 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid)
return 0; return 0;
name = c->group; name = c->group;
r = get_group_creds(&name, gid); r = get_group_creds(&name, gid, 0);
if (r < 0) if (r < 0)
return r; return r;
@ -1011,7 +1011,7 @@ static int get_supplementary_groups(const ExecContext *c, const char *user,
return -E2BIG; return -E2BIG;
g = *i; g = *i;
r = get_group_creds(&g, l_gids+k); r = get_group_creds(&g, l_gids+k, 0);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -570,36 +570,44 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t
*n = t - m; *n = t - m;
} }
static int clone_device_node(const char *d, const char *temporary_mount, bool *make_devnode) { static int clone_device_node(
const char *dn; const char *d,
const char *temporary_mount,
bool *make_devnode) {
_cleanup_free_ char *sl = NULL;
const char *dn, *bn, *t;
struct stat st; struct stat st;
int r; int r;
if (stat(d, &st) < 0) { 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 -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) && 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; return -EINVAL;
}
if (st.st_rdev == 0)
return -ENXIO;
dn = strjoina(temporary_mount, d); dn = strjoina(temporary_mount, d);
/* First, try to create device node properly */
if (*make_devnode) { if (*make_devnode) {
mac_selinux_create_file_prepare(d, st.st_mode); mac_selinux_create_file_prepare(d, st.st_mode);
r = mknod(dn, st.st_mode, st.st_rdev); r = mknod(dn, st.st_mode, st.st_rdev);
mac_selinux_create_file_clear(); mac_selinux_create_file_clear();
if (r >= 0)
if (r == 0) goto add_symlink;
return 0;
if (errno != EPERM) if (errno != EPERM)
return log_debug_errno(errno, "mknod failed for %s: %m", d); 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; *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); mac_selinux_create_file_prepare(d, 0);
r = mknod(dn, S_IFREG, 0); r = mknod(dn, S_IFREG, 0);
mac_selinux_create_file_clear(); mac_selinux_create_file_clear();
if (r < 0 && errno != EEXIST) 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: /* Fallback to bind-mounting:
* The assumption here is that all used device nodes carry standard * 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) * either be owned by root:root or root:tty (e.g. /dev/tty, /dev/ptmx)
* and should not carry ACLs. */ * and should not carry ACLs. */
if (mount(d, dn, NULL, MS_BIND, NULL) < 0) 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; return 0;
} }

View File

@ -1934,7 +1934,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
if (!isempty(s->user)) { if (!isempty(s->user)) {
const char *user = 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) { if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to resolve user %s: %m", user); log_unit_error_errno(UNIT(s), r, "Failed to resolve user %s: %m", user);
_exit(EXIT_USER); _exit(EXIT_USER);
@ -1944,7 +1944,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
if (!isempty(s->group)) { if (!isempty(s->group)) {
const char *group = s->group; const char *group = s->group;
r = get_group_creds(&group, &gid); r = get_group_creds(&group, &gid, 0);
if (r < 0) { if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to resolve group %s: %m", group); log_unit_error_errno(UNIT(s), r, "Failed to resolve group %s: %m", group);
_exit(EXIT_GROUP); _exit(EXIT_GROUP);

View File

@ -672,7 +672,7 @@ static int change_uid_gid(const char *context[]) {
if (uid <= SYSTEM_UID_MAX) { if (uid <= SYSTEM_UID_MAX) {
const char *user = "systemd-coredump"; 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) { if (r < 0) {
log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user); log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user);
uid = gid = 0; uid = gid = 0;

View File

@ -909,7 +909,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
const char *path = NULL; const char *path = NULL;
uid_t uid; 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) if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); 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])) if (isempty(argv[i]))
uid = UID_INVALID; uid = UID_INVALID;
else { 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) if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); 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++) { for (i = 1; i < argc; i++) {
uid_t uid; 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) if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); 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++) { for (i = 1; i < argc; i++) {
uid_t uid; 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) if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);

View File

@ -152,7 +152,7 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
assert(m); assert(m);
assert(name); assert(name);
r = get_user_creds(&name, &uid, &gid, NULL, NULL); r = get_user_creds(&name, &uid, &gid, NULL, NULL, 0);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -117,7 +117,7 @@ static int do_mount(const char *user) {
gid_t gid; gid_t gid;
int r; int r;
r = get_user_creds(&user, &uid, &gid, NULL, NULL); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, return log_error_errno(r,
r == -ESRCH ? "No such user \"%s\"" : 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(), /* 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().*/ * and if it fails, fallback to get_user_creds().*/
if (parse_uid(user, &uid) < 0) { 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) if (r < 0)
return log_error_errno(r, return log_error_errno(r,
r == -ESRCH ? "No such user \"%s\"" : r == -ESRCH ? "No such user \"%s\"" :

View File

@ -223,7 +223,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_OWNER: { case ARG_OWNER: {
const char *user = optarg; 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) if (r < 0)
return log_error_errno(r, return log_error_errno(r,
r == -EBADMSG ? "UID or GID of user %s are invalid." r == -EBADMSG ? "UID or GID of user %s are invalid."

View File

@ -60,7 +60,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
assert(netdev); assert(netdev);
assert(ifr); assert(ifr);
fd = open(TUN_DEV, O_RDWR); fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
if (fd < 0) if (fd < 0)
return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); 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); assert(t);
if (t->user_name) { if (t->user_name) {
user = 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) if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); 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) { if (t->group_name) {
group = 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) if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name);

View File

@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
goto out; goto out;
} }
r = get_user_creds(&user, &uid, &gid, NULL, NULL); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Cannot resolve user name %s: %m", user); log_error_errno(r, "Cannot resolve user name %s: %m", user);
goto out; goto out;

View File

@ -115,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_UID: { case ARG_UID: {
const char *u = optarg; 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 */ if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
r = parse_uid(u, &arg_uid); r = parse_uid(u, &arg_uid);
if (r < 0) if (r < 0)

View File

@ -1790,7 +1790,12 @@ static int copy_devnodes(const char *dest) {
struct stat st; struct stat st;
from = strappend("/dev/", d); from = strappend("/dev/", d);
if (!from)
return log_oom();
to = prefix_root(dest, from); to = prefix_root(dest, from);
if (!to)
return log_oom();
if (stat(from, &st) < 0) { if (stat(from, &st) < 0) {
@ -1803,6 +1808,8 @@ static int copy_devnodes(const char *dest) {
return -EIO; return -EIO;
} else { } else {
_cleanup_free_ char *sl = NULL, *prefixed = NULL, *dn = NULL, *t = NULL;
if (mknod(to, st.st_mode, st.st_rdev) < 0) { if (mknod(to, st.st_mode, st.st_rdev) < 0) {
/* Explicitly warn the user when /dev is already populated. */ /* Explicitly warn the user when /dev is already populated. */
if (errno == EEXIST) if (errno == EEXIST)
@ -1810,8 +1817,7 @@ static int copy_devnodes(const char *dest) {
if (errno != EPERM) if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to); return log_error_errno(errno, "mknod(%s) failed: %m", to);
/* Some systems abusively restrict mknod but /* Some systems abusively restrict mknod but allow bind mounts. */
* allow bind mounts. */
r = touch(to); r = touch(to);
if (r < 0) if (r < 0)
return log_error_errno(r, "touch (%s) failed: %m", to); 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); r = userns_lchown(to, 0, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "chown() of device node %s failed: %m", to); 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);
} }
} }

View File

@ -37,7 +37,7 @@ int main(int argc, char *argv[]) {
goto finish; goto finish;
} }
r = get_user_creds(&user, &uid, &gid, NULL, NULL); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Cannot resolve user name %s: %m", user); log_error_errno(r, "Cannot resolve user name %s: %m", user);
goto finish; goto finish;

View File

@ -34,6 +34,7 @@ static void test_parse_etc_hosts(const char *fname) {
assert_se(fd >= 0); assert_se(fd >= 0);
f = fdopen(fd, "r+"); f = fdopen(fd, "r+");
assert_se(f);
fputs("1.2.3.4 some.where\n", f); fputs("1.2.3.4 some.where\n", f);
fputs("1.2.3.5 some.where\n", f); fputs("1.2.3.5 some.where\n", f);
fputs("::0 some.where some.other\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) if (fname)
return; return;
EtcHostsItemByName *bn; EtcHostsItemByName *bn;
assert_se(bn = hashmap_get(hosts.by_name, "some.where")); assert_se(bn = hashmap_get(hosts.by_name, "some.where"));
assert_se(bn->n_addresses == 3); assert_se(bn->n_addresses == 3);

View File

@ -1239,7 +1239,7 @@ static int start_transient_scope(
if (arg_exec_group) { if (arg_exec_group) {
gid_t gid; gid_t gid;
r = get_group_creds(&arg_exec_group, &gid); r = get_group_creds(&arg_exec_group, &gid, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); 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; uid_t uid;
gid_t gid; 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) if (r < 0)
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);

View File

@ -219,7 +219,7 @@ static int condition_test_user(Condition *c) {
return streq(c->parameter, "root"); return streq(c->parameter, "root");
u = c->parameter; 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) if (r < 0)
return 0; return 0;

View File

@ -32,7 +32,7 @@ static void test_add_acls_for_user(void) {
if (getuid() == 0) { if (getuid() == 0) {
const char *nobody = NOBODY_USER_NAME; 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) if (r < 0)
uid = 0; uid = 0;
} else } else

View File

@ -9,7 +9,7 @@ int main(int argc, char *argv[]) {
int r; int r;
const char* name = argv[1] ?: NOBODY_USER_NAME; 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) { if (r < 0) {
log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR, log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR,
r, "Failed to resolve \"%s\": %m", name); r, "Failed to resolve \"%s\": %m", name);

View File

@ -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\") */", log_info("/* %s(\"%s\", \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\") */",
__func__, id, name, uid, gid, home, shell); __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", log_info_errno(r, "got \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\": %m",
id, ruid, rgid, strnull(rhome), strnull(rshell)); id, ruid, rgid, strnull(rhome), strnull(rshell));
if (!synthesize_nobody() && streq(name, NOBODY_USER_NAME)) { 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); 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); log_info_errno(r, "got \"%s\", "GID_FMT": %m", id, rgid);
if (!synthesize_nobody() && streq(name, NOBODY_GROUP_NAME)) { if (!synthesize_nobody() && streq(name, NOBODY_GROUP_NAME)) {
log_info("(skipping detailed tests because nobody is not synthesized)"); log_info("(skipping detailed tests because nobody is not synthesized)");

View File

@ -107,7 +107,7 @@ int main(int argc, char *argv[]) {
gid = getegid(); gid = getegid();
if (uid_current == 0) { 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) { if (r < 0) {
log_error_errno(r, "Cannot resolve user name %s: %m", user); log_error_errno(r, "Cannot resolve user name %s: %m", user);
goto finish; goto finish;

View File

@ -2728,7 +2728,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
if (!isempty(user) && !streq(user, "-")) { if (!isempty(user) && !streq(user, "-")) {
const char *u = 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) { if (r < 0) {
*invalid_config = true; *invalid_config = true;
return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user); 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, "-")) { if (!isempty(group) && !streq(group, "-")) {
const char *g = 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) { if (r < 0) {
*invalid_config = true; *invalid_config = true;
log_error("[%s:%u] Unknown group '%s'.", fname, line, group); log_error("[%s:%u] Unknown group '%s'.", fname, line, group);

View File

@ -481,7 +481,7 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
return uid; 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) if (r < 0)
log_unknown_owner(r, "user", owner); log_unknown_owner(r, "user", owner);
@ -524,7 +524,7 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
return gid; return gid;
} }
} }
r = get_group_creds(&group, &gid); r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
if (r < 0) if (r < 0)
log_unknown_owner(r, "group", group); log_unknown_owner(r, "group", group);
@ -2110,7 +2110,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->owner_final = true; event->owner_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false); udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
event->owner_set = true; 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) { if (r < 0) {
log_unknown_owner(r, "user", owner); log_unknown_owner(r, "user", owner);
event->uid = 0; event->uid = 0;
@ -2131,7 +2131,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->group_final = true; event->group_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false); udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
event->group_set = true; 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) { if (r < 0) {
log_unknown_owner(r, "group", group); log_unknown_owner(r, "group", group);
event->gid = 0; event->gid = 0;