diff options
Diffstat (limited to 'src/core/dynamic-user.c')
-rw-r--r-- | src/core/dynamic-user.c | 177 |
1 files changed, 103 insertions, 74 deletions
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index f1b5ee7ecb..3da31bf870 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -82,7 +83,7 @@ static int dynamic_user_add(Manager *m, const char *name, int storage_socket[2], return 0; } -int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { +static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { _cleanup_close_pair_ int storage_socket[2] = { -1, -1 }; DynamicUser *d; int r; @@ -146,7 +147,7 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { static int make_uid_symlinks(uid_t uid, const char *name, bool b) { - char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1]; + char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1]; const char *path2; int r = 0, k; @@ -174,7 +175,7 @@ static int make_uid_symlinks(uid_t uid, const char *name, bool b) { r = k; } - if (b && symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0) { + if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) { k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2); if (r == 0) r = k; @@ -217,7 +218,7 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) { (void) mkdir("/run/systemd/dynamic-uid", 0755); for (;;) { - char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; _cleanup_close_ int lock_fd = -1; uid_t candidate; ssize_t l; @@ -410,7 +411,7 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) { } static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) { - char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; if (lock_fd < 0) return; @@ -421,31 +422,55 @@ static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) { (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */ } -int dynamic_user_realize(DynamicUser *d, char **suggested_dirs, uid_t *ret) { +static int lockfp(int fd, int *fd_lock) { + if (lockf(fd, F_LOCK, 0) < 0) + return -errno; + *fd_lock = fd; + return 0; +} - _cleanup_close_ int etc_passwd_lock_fd = -1, uid_lock_fd = -1; - uid_t uid = UID_INVALID; +static void unlockfp(int *fd_lock) { + if (*fd_lock < 0) + return; + lockf(*fd_lock, F_ULOCK, 0); + *fd_lock = -1; +} + +static int dynamic_user_realize( + DynamicUser *d, + char **suggested_dirs, + uid_t *ret_uid, gid_t *ret_gid, + bool is_user) { + + _cleanup_(unlockfp) int storage_socket0_lock = -1; + _cleanup_close_ int uid_lock_fd = -1; + _cleanup_close_ int etc_passwd_lock_fd = -1; + uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */ + gid_t gid = GID_INVALID; /* a gid if is_user, ignored otherwise */ int r; assert(d); + assert(is_user == !!ret_uid); + assert(ret_gid); /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist * yet. If it already exists its existing UID/GID will be reused. */ - if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) - return -errno; + r = lockfp(d->storage_socket[0], &storage_socket0_lock); + if (r < 0) + return r; - r = dynamic_user_pop(d, &uid, &uid_lock_fd); + r = dynamic_user_pop(d, &num, &uid_lock_fd); if (r < 0) { int new_uid_lock_fd; uid_t new_uid; if (r != -EAGAIN) - goto finish; + return r; /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the * lock however, so that nobody else blocks on our NSS lookups. */ - (void) lockf(d->storage_socket[0], F_ULOCK, 0); + unlockfp(&storage_socket0_lock); /* Let's see if a proper, static user or group by this name exists. Try to take the lock on * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't @@ -455,47 +480,58 @@ int dynamic_user_realize(DynamicUser *d, char **suggested_dirs, uid_t *ret) { return etc_passwd_lock_fd; /* First, let's parse this as numeric UID */ - r = parse_uid(d->name, &uid); + r = parse_uid(d->name, &num); if (r < 0) { struct passwd *p; struct group *g; - /* OK, this is not a numeric UID. Let's see if there's a user by this name */ - p = getpwnam(d->name); - if (p) - uid = p->pw_uid; - - /* Let's see if there's a group by this name */ - g = getgrnam(d->name); - if (g) { - /* If the UID/GID of the user/group of the same don't match, refuse operation */ - if (uid != UID_INVALID && uid != (uid_t) g->gr_gid) - return -EILSEQ; - - uid = (uid_t) g->gr_gid; + if (is_user) { + /* OK, this is not a numeric UID. Let's see if there's a user by this name */ + p = getpwnam(d->name); + if (p) { + num = p->pw_uid; + gid = p->pw_gid; + } else { + /* if the user does not exist but the group with the same name exists, refuse operation */ + g = getgrnam(d->name); + if (g) + return -EILSEQ; + } + } else { + /* Let's see if there's a group by this name */ + g = getgrnam(d->name); + if (g) + num = (uid_t) g->gr_gid; + else { + /* if the group does not exist but the user with the same name exists, refuse operation */ + p = getpwnam(d->name); + if (p) + return -EILSEQ; + } } } - if (uid == UID_INVALID) { + if (num == UID_INVALID) { /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */ - uid_lock_fd = pick_uid(suggested_dirs, d->name, &uid); + uid_lock_fd = pick_uid(suggested_dirs, d->name, &num); if (uid_lock_fd < 0) return uid_lock_fd; } /* So, we found a working UID/lock combination. Let's see if we actually still need it. */ - if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) { - unlink_uid_lock(uid_lock_fd, uid, d->name); - return -errno; + r = lockfp(d->storage_socket[0], &storage_socket0_lock); + if (r < 0) { + unlink_uid_lock(uid_lock_fd, num, d->name); + return r; } r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd); if (r < 0) { if (r != -EAGAIN) { /* OK, something bad happened, let's get rid of the bits we acquired. */ - unlink_uid_lock(uid_lock_fd, uid, d->name); - goto finish; + unlink_uid_lock(uid_lock_fd, num, d->name); + return r; } /* Great! Nothing is stored here, still. Store our newly acquired data. */ @@ -503,10 +539,10 @@ int dynamic_user_realize(DynamicUser *d, char **suggested_dirs, uid_t *ret) { /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we * acquired, and use what's stored now. */ - unlink_uid_lock(uid_lock_fd, uid, d->name); + unlink_uid_lock(uid_lock_fd, num, d->name); safe_close(uid_lock_fd); - uid = new_uid; + num = new_uid; uid_lock_fd = new_uid_lock_fd; } } @@ -514,19 +550,21 @@ int dynamic_user_realize(DynamicUser *d, char **suggested_dirs, uid_t *ret) { /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID * dynamically right here, push that in along with the lock fd for it. */ - r = dynamic_user_push(d, uid, uid_lock_fd); + r = dynamic_user_push(d, num, uid_lock_fd); if (r < 0) - goto finish; + return r; - *ret = uid; - r = 0; + if (is_user) { + *ret_uid = num; + *ret_gid = gid != GID_INVALID ? gid : num; + } else + *ret_gid = num; -finish: - (void) lockf(d->storage_socket[0], F_ULOCK, 0); - return r; + return 0; } -int dynamic_user_current(DynamicUser *d, uid_t *ret) { +static int dynamic_user_current(DynamicUser *d, uid_t *ret) { + _cleanup_(unlockfp) int storage_socket0_lock = -1; _cleanup_close_ int lock_fd = -1; uid_t uid; int r; @@ -536,26 +574,23 @@ int dynamic_user_current(DynamicUser *d, uid_t *ret) { /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */ - if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) - return -errno; + r = lockfp(d->storage_socket[0], &storage_socket0_lock); + if (r < 0) + return r; r = dynamic_user_pop(d, &uid, &lock_fd); if (r < 0) - goto finish; + return r; r = dynamic_user_push(d, uid, lock_fd); if (r < 0) - goto finish; + return r; *ret = uid; - r = 0; - -finish: - (void) lockf(d->storage_socket[0], F_ULOCK, 0); - return r; + return 0; } -DynamicUser* dynamic_user_ref(DynamicUser *d) { +static DynamicUser* dynamic_user_ref(DynamicUser *d) { if (!d) return NULL; @@ -565,7 +600,7 @@ DynamicUser* dynamic_user_ref(DynamicUser *d) { return d; } -DynamicUser* dynamic_user_unref(DynamicUser *d) { +static DynamicUser* dynamic_user_unref(DynamicUser *d) { if (!d) return NULL; @@ -580,6 +615,7 @@ DynamicUser* dynamic_user_unref(DynamicUser *d) { } static int dynamic_user_close(DynamicUser *d) { + _cleanup_(unlockfp) int storage_socket0_lock = -1; _cleanup_close_ int lock_fd = -1; uid_t uid; int r; @@ -587,28 +623,23 @@ static int dynamic_user_close(DynamicUser *d) { /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is * unrealized again, much like it was after it the DynamicUser object was first allocated. */ - if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) - return -errno; + r = lockfp(d->storage_socket[0], &storage_socket0_lock); + if (r < 0) + return r; r = dynamic_user_pop(d, &uid, &lock_fd); - if (r == -EAGAIN) { + if (r == -EAGAIN) /* User wasn't realized yet, nothing to do. */ - r = 0; - goto finish; - } + return 0; if (r < 0) - goto finish; + return r; /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */ unlink_uid_lock(lock_fd, uid, d->name); - r = 1; - -finish: - (void) lockf(d->storage_socket[0], F_ULOCK, 0); - return r; + return 1; } -DynamicUser* dynamic_user_destroy(DynamicUser *d) { +static DynamicUser* dynamic_user_destroy(DynamicUser *d) { if (!d) return NULL; @@ -713,7 +744,7 @@ void dynamic_user_vacuum(Manager *m, bool close_user) { } int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) { - char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; _cleanup_free_ char *user = NULL; uid_t check_uid; int r; @@ -814,21 +845,19 @@ int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *ui /* Realize both the referenced user and group */ if (creds->user) { - r = dynamic_user_realize(creds->user, suggested_paths, &u); + r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true); if (r < 0) return r; } if (creds->group && creds->group != creds->user) { - r = dynamic_user_realize(creds->group, suggested_paths, &g); + r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false); if (r < 0) return r; - } else - g = u; + } *uid = u; *gid = g; - return 0; } |