LCOV - code coverage report
Current view: top level - core - dynamic-user.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 12 399 3.0 %
Date: 2019-08-22 15:41:25 Functions: 3 25 12.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <grp.h>
       4             : #include <pwd.h>
       5             : #include <sys/file.h>
       6             : #include <sys/stat.h>
       7             : #include <sys/types.h>
       8             : 
       9             : #include "clean-ipc.h"
      10             : #include "dynamic-user.h"
      11             : #include "fd-util.h"
      12             : #include "fileio.h"
      13             : #include "format-util.h"
      14             : #include "fs-util.h"
      15             : #include "io-util.h"
      16             : #include "nscd-flush.h"
      17             : #include "parse-util.h"
      18             : #include "random-util.h"
      19             : #include "serialize.h"
      20             : #include "socket-util.h"
      21             : #include "stdio-util.h"
      22             : #include "string-util.h"
      23             : #include "strv.h"
      24             : #include "user-util.h"
      25             : 
      26             : /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
      27             : #define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
      28             : 
      29           0 : DEFINE_PRIVATE_TRIVIAL_REF_FUNC(DynamicUser, dynamic_user);
      30             : 
      31           0 : static DynamicUser* dynamic_user_free(DynamicUser *d) {
      32           0 :         if (!d)
      33           0 :                 return NULL;
      34             : 
      35           0 :         if (d->manager)
      36           0 :                 (void) hashmap_remove(d->manager->dynamic_users, d->name);
      37             : 
      38           0 :         safe_close_pair(d->storage_socket);
      39           0 :         return mfree(d);
      40             : }
      41             : 
      42           0 : static int dynamic_user_add(Manager *m, const char *name, int storage_socket[static 2], DynamicUser **ret) {
      43             :         DynamicUser *d;
      44             :         int r;
      45             : 
      46           0 :         assert(m);
      47           0 :         assert(name);
      48           0 :         assert(storage_socket);
      49             : 
      50           0 :         r = hashmap_ensure_allocated(&m->dynamic_users, &string_hash_ops);
      51           0 :         if (r < 0)
      52           0 :                 return r;
      53             : 
      54           0 :         d = malloc0(offsetof(DynamicUser, name) + strlen(name) + 1);
      55           0 :         if (!d)
      56           0 :                 return -ENOMEM;
      57             : 
      58           0 :         strcpy(d->name, name);
      59             : 
      60           0 :         d->storage_socket[0] = storage_socket[0];
      61           0 :         d->storage_socket[1] = storage_socket[1];
      62             : 
      63           0 :         r = hashmap_put(m->dynamic_users, d->name, d);
      64           0 :         if (r < 0) {
      65           0 :                 free(d);
      66           0 :                 return r;
      67             :         }
      68             : 
      69           0 :         d->manager = m;
      70             : 
      71           0 :         if (ret)
      72           0 :                 *ret = d;
      73             : 
      74           0 :         return 0;
      75             : }
      76             : 
      77           0 : static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
      78           0 :         _cleanup_close_pair_ int storage_socket[2] = { -1, -1 };
      79             :         DynamicUser *d;
      80             :         int r;
      81             : 
      82           0 :         assert(m);
      83           0 :         assert(name);
      84             : 
      85             :         /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for
      86             :          * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really
      87             :          * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do
      88             :          * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous
      89             :          * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the
      90             :          * allocated UID number, plus an fd referencing the lock file for the UID
      91             :          * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can
      92             :          * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair
      93             :          * may exist in three different states:
      94             :          *
      95             :          * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized.
      96             :          *
      97             :          * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a
      98             :          *    statically assigned UID by the same name, which we are reusing.
      99             :          *
     100             :          * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic
     101             :          *    UID and locked it in the file system, using the lock fd.
     102             :          *
     103             :          * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it
     104             :          * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here:
     105             :          * the UID lock on disk is protected via a BSD file lock (i.e. an fd-bound lock), so that the lock is kept in
     106             :          * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX
     107             :          * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous,
     108             :          * nobody else could get any access to it except via our own fd) and we want to synchronize access between all
     109             :          * processes that have access to it. */
     110             : 
     111           0 :         d = hashmap_get(m->dynamic_users, name);
     112           0 :         if (d) {
     113           0 :                 if (ret) {
     114             :                         /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
     115           0 :                         d->n_ref++;
     116           0 :                         *ret = d;
     117             :                 }
     118           0 :                 return 0;
     119             :         }
     120             : 
     121           0 :         if (!valid_user_group_name_or_id(name))
     122           0 :                 return -EINVAL;
     123             : 
     124           0 :         if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0)
     125           0 :                 return -errno;
     126             : 
     127           0 :         r = dynamic_user_add(m, name, storage_socket, &d);
     128           0 :         if (r < 0)
     129           0 :                 return r;
     130             : 
     131           0 :         storage_socket[0] = storage_socket[1] = -1;
     132             : 
     133           0 :         if (ret) {
     134           0 :                 d->n_ref++;
     135           0 :                 *ret = d;
     136             :         }
     137             : 
     138           0 :         return 1;
     139             : }
     140             : 
     141           0 : static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
     142             : 
     143             :         char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
     144             :         const char *path2;
     145           0 :         int r = 0, k;
     146             : 
     147             :         /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
     148             :          * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
     149             :          * would be its own client then). We hence keep these world-readable symlinks in place, so that the
     150             :          * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
     151             :          * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
     152             :          * on them and as those may be taken by any user with read access we can't make them world-readable. */
     153             : 
     154           0 :         xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
     155           0 :         if (unlink(path1) < 0 && errno != ENOENT)
     156           0 :                 r = -errno;
     157             : 
     158           0 :         if (b && symlink(name, path1) < 0) {
     159           0 :                 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
     160           0 :                 if (r == 0)
     161           0 :                         r = k;
     162             :         }
     163             : 
     164           0 :         path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
     165           0 :         if (unlink(path2) < 0 && errno != ENOENT) {
     166           0 :                 k = -errno;
     167           0 :                 if (r == 0)
     168           0 :                         r = k;
     169             :         }
     170             : 
     171           0 :         if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
     172           0 :                 k = log_warning_errno(errno,  "Failed to symlink \"%s\": %m", path2);
     173           0 :                 if (r == 0)
     174           0 :                         r = k;
     175             :         }
     176             : 
     177           0 :         return r;
     178             : }
     179             : 
     180           0 : static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
     181             : 
     182             :         /* Find a suitable free UID. We use the following strategy to find a suitable UID:
     183             :          *
     184             :          * 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
     185             :          *    them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
     186             :          *    LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
     187             :          *    recursively chown() them to new users.
     188             :          *
     189             :          * 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
     190             :          *    pretty good, as the use ris by default derived from the unit name, and hence the same service and same
     191             :          *    user should usually get the same UID as long as our hashing doesn't clash.
     192             :          *
     193             :          * 3. Finally, if that didn't work, we randomly pick UIDs, until we find one that is empty.
     194             :          *
     195             :          * Since the dynamic UID space is relatively small we'll stop trying after 100 iterations, giving up. */
     196             : 
     197             :         enum {
     198             :                 PHASE_SUGGESTED,  /* the first phase, reusing directory ownership UIDs */
     199             :                 PHASE_HASHED,     /* the second phase, deriving a UID from the username by hashing */
     200             :                 PHASE_RANDOM,     /* the last phase, randomly picking UIDs */
     201           0 :         } phase = PHASE_SUGGESTED;
     202             : 
     203             :         static const uint8_t hash_key[] = {
     204             :                 0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5,
     205             :                 0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59
     206             :         };
     207             : 
     208           0 :         unsigned n_tries = 100, current_suggested = 0;
     209             :         int r;
     210             : 
     211           0 :         (void) mkdir("/run/systemd/dynamic-uid", 0755);
     212             : 
     213           0 :         for (;;) {
     214             :                 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
     215           0 :                 _cleanup_close_ int lock_fd = -1;
     216             :                 uid_t candidate;
     217             :                 ssize_t l;
     218             : 
     219           0 :                 if (--n_tries <= 0) /* Give up retrying eventually */
     220           0 :                         return -EBUSY;
     221             : 
     222           0 :                 switch (phase) {
     223             : 
     224           0 :                 case PHASE_SUGGESTED: {
     225             :                         struct stat st;
     226             : 
     227           0 :                         if (!suggested_paths || !suggested_paths[current_suggested]) {
     228             :                                 /* We reached the end of the suggested paths list, let's try by hashing the name */
     229           0 :                                 phase = PHASE_HASHED;
     230           0 :                                 continue;
     231             :                         }
     232             : 
     233           0 :                         if (stat(suggested_paths[current_suggested++], &st) < 0)
     234           0 :                                 continue; /* We can't read the UID of this path, but that doesn't matter, just try the next */
     235             : 
     236           0 :                         candidate = st.st_uid;
     237           0 :                         break;
     238             :                 }
     239             : 
     240           0 :                 case PHASE_HASHED:
     241             :                         /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We
     242             :                          * start with a UID generated as hash from the user name. */
     243           0 :                         candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key));
     244             : 
     245             :                         /* If this one fails, we should proceed with random tries */
     246           0 :                         phase = PHASE_RANDOM;
     247           0 :                         break;
     248             : 
     249           0 :                 case PHASE_RANDOM:
     250             : 
     251             :                         /* Pick another random UID, and see if that works for us. */
     252           0 :                         random_bytes(&candidate, sizeof(candidate));
     253           0 :                         candidate = UID_CLAMP_INTO_RANGE(candidate);
     254           0 :                         break;
     255             : 
     256           0 :                 default:
     257           0 :                         assert_not_reached("unknown phase");
     258             :                 }
     259             : 
     260             :                 /* Make sure whatever we picked here actually is in the right range */
     261           0 :                 if (!uid_is_dynamic(candidate))
     262           0 :                         continue;
     263             : 
     264           0 :                 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);
     265             : 
     266           0 :                 for (;;) {
     267             :                         struct stat st;
     268             : 
     269           0 :                         lock_fd = open(lock_path, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
     270           0 :                         if (lock_fd < 0)
     271           0 :                                 return -errno;
     272             : 
     273           0 :                         r = flock(lock_fd, LOCK_EX|LOCK_NB); /* Try to get a BSD file lock on the UID lock file */
     274           0 :                         if (r < 0) {
     275           0 :                                 if (IN_SET(errno, EBUSY, EAGAIN))
     276           0 :                                         goto next; /* already in use */
     277             : 
     278           0 :                                 return -errno;
     279             :                         }
     280             : 
     281           0 :                         if (fstat(lock_fd, &st) < 0)
     282           0 :                                 return -errno;
     283           0 :                         if (st.st_nlink > 0)
     284           0 :                                 break;
     285             : 
     286             :                         /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
     287             :                          * got the lock. Close it, and try again. */
     288           0 :                         lock_fd = safe_close(lock_fd);
     289             :                 }
     290             : 
     291             :                 /* Some superficial check whether this UID/GID might already be taken by some static user */
     292           0 :                 if (getpwuid(candidate) ||
     293           0 :                     getgrgid((gid_t) candidate) ||
     294           0 :                     search_ipc(candidate, (gid_t) candidate) != 0) {
     295           0 :                         (void) unlink(lock_path);
     296           0 :                         continue;
     297             :                 }
     298             : 
     299             :                 /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
     300           0 :                 l = pwritev(lock_fd,
     301           0 :                             (struct iovec[2]) {
     302           0 :                                     IOVEC_INIT_STRING(name),
     303           0 :                                     IOVEC_INIT((char[1]) { '\n' }, 1),
     304             :                             }, 2, 0);
     305           0 :                 if (l < 0) {
     306           0 :                         r = -errno;
     307           0 :                         (void) unlink(lock_path);
     308           0 :                         return r;
     309             :                 }
     310             : 
     311           0 :                 (void) ftruncate(lock_fd, l);
     312           0 :                 (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
     313             : 
     314           0 :                 *ret_uid = candidate;
     315           0 :                 return TAKE_FD(lock_fd);
     316             : 
     317           0 :         next:
     318             :                 ;
     319             :         }
     320             : }
     321             : 
     322           0 : static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
     323           0 :         uid_t uid = UID_INVALID;
     324           0 :         struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
     325             :         int lock_fd;
     326             :         ssize_t k;
     327             : 
     328           0 :         assert(d);
     329           0 :         assert(ret_uid);
     330           0 :         assert(ret_lock_fd);
     331             : 
     332             :         /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
     333             :          * on the socket taken. */
     334             : 
     335           0 :         k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
     336           0 :         if (k < 0)
     337           0 :                 return (int) k;
     338             : 
     339           0 :         *ret_uid = uid;
     340           0 :         *ret_lock_fd = lock_fd;
     341             : 
     342           0 :         return 0;
     343             : }
     344             : 
     345           0 : static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
     346           0 :         struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
     347             : 
     348           0 :         assert(d);
     349             : 
     350             :         /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
     351           0 :         return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
     352             : }
     353             : 
     354           0 : static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
     355             :         char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
     356             : 
     357           0 :         if (lock_fd < 0)
     358           0 :                 return;
     359             : 
     360           0 :         xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
     361           0 :         (void) unlink(lock_path);
     362             : 
     363           0 :         (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
     364             : }
     365             : 
     366           0 : static int lockfp(int fd, int *fd_lock) {
     367           0 :         if (lockf(fd, F_LOCK, 0) < 0)
     368           0 :                 return -errno;
     369           0 :         *fd_lock = fd;
     370           0 :         return 0;
     371             : }
     372             : 
     373           0 : static void unlockfp(int *fd_lock) {
     374           0 :         if (*fd_lock < 0)
     375           0 :                 return;
     376           0 :         lockf(*fd_lock, F_ULOCK, 0);
     377           0 :         *fd_lock = -1;
     378             : }
     379             : 
     380           0 : static int dynamic_user_realize(
     381             :                 DynamicUser *d,
     382             :                 char **suggested_dirs,
     383             :                 uid_t *ret_uid, gid_t *ret_gid,
     384             :                 bool is_user) {
     385             : 
     386           0 :         _cleanup_(unlockfp) int storage_socket0_lock = -1;
     387           0 :         _cleanup_close_ int uid_lock_fd = -1;
     388           0 :         _cleanup_close_ int etc_passwd_lock_fd = -1;
     389           0 :         uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */
     390           0 :         gid_t gid = GID_INVALID; /* a gid if is_user, ignored otherwise */
     391           0 :         bool flush_cache = false;
     392             :         int r;
     393             : 
     394           0 :         assert(d);
     395           0 :         assert(is_user == !!ret_uid);
     396           0 :         assert(ret_gid);
     397             : 
     398             :         /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist
     399             :          * yet. If it already exists its existing UID/GID will be reused. */
     400             : 
     401           0 :         r = lockfp(d->storage_socket[0], &storage_socket0_lock);
     402           0 :         if (r < 0)
     403           0 :                 return r;
     404             : 
     405           0 :         r = dynamic_user_pop(d, &num, &uid_lock_fd);
     406           0 :         if (r < 0) {
     407             :                 int new_uid_lock_fd;
     408             :                 uid_t new_uid;
     409             : 
     410           0 :                 if (r != -EAGAIN)
     411           0 :                         return r;
     412             : 
     413             :                 /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the
     414             :                  * lock however, so that nobody else blocks on our NSS lookups. */
     415           0 :                 unlockfp(&storage_socket0_lock);
     416             : 
     417             :                 /* Let's see if a proper, static user or group by this name exists. Try to take the lock on
     418             :                  * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't
     419             :                  * take the lock, given that users can't be added there anyway in this case. */
     420           0 :                 etc_passwd_lock_fd = take_etc_passwd_lock(NULL);
     421           0 :                 if (etc_passwd_lock_fd < 0 && etc_passwd_lock_fd != -EROFS)
     422           0 :                         return etc_passwd_lock_fd;
     423             : 
     424             :                 /* First, let's parse this as numeric UID */
     425           0 :                 r = parse_uid(d->name, &num);
     426           0 :                 if (r < 0) {
     427             :                         struct passwd *p;
     428             :                         struct group *g;
     429             : 
     430           0 :                         if (is_user) {
     431             :                                 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
     432           0 :                                 p = getpwnam(d->name);
     433           0 :                                 if (p) {
     434           0 :                                         num = p->pw_uid;
     435           0 :                                         gid = p->pw_gid;
     436             :                                 } else {
     437             :                                         /* if the user does not exist but the group with the same name exists, refuse operation */
     438           0 :                                         g = getgrnam(d->name);
     439           0 :                                         if (g)
     440           0 :                                                 return -EILSEQ;
     441             :                                 }
     442             :                         } else {
     443             :                                 /* Let's see if there's a group by this name */
     444           0 :                                 g = getgrnam(d->name);
     445           0 :                                 if (g)
     446           0 :                                         num = (uid_t) g->gr_gid;
     447             :                                 else {
     448             :                                         /* if the group does not exist but the user with the same name exists, refuse operation */
     449           0 :                                         p = getpwnam(d->name);
     450           0 :                                         if (p)
     451           0 :                                                 return -EILSEQ;
     452             :                                 }
     453             :                         }
     454             :                 }
     455             : 
     456           0 :                 if (num == UID_INVALID) {
     457             :                         /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
     458             : 
     459           0 :                         uid_lock_fd = pick_uid(suggested_dirs, d->name, &num);
     460           0 :                         if (uid_lock_fd < 0)
     461           0 :                                 return uid_lock_fd;
     462             :                 }
     463             : 
     464             :                 /* So, we found a working UID/lock combination. Let's see if we actually still need it. */
     465           0 :                 r = lockfp(d->storage_socket[0], &storage_socket0_lock);
     466           0 :                 if (r < 0) {
     467           0 :                         unlink_uid_lock(uid_lock_fd, num, d->name);
     468           0 :                         return r;
     469             :                 }
     470             : 
     471           0 :                 r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd);
     472           0 :                 if (r < 0) {
     473           0 :                         if (r != -EAGAIN) {
     474             :                                 /* OK, something bad happened, let's get rid of the bits we acquired. */
     475           0 :                                 unlink_uid_lock(uid_lock_fd, num, d->name);
     476           0 :                                 return r;
     477             :                         }
     478             : 
     479             :                         /* Great! Nothing is stored here, still. Store our newly acquired data. */
     480           0 :                         flush_cache = true;
     481             :                 } else {
     482             :                         /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
     483             :                          * acquired, and use what's stored now. */
     484             : 
     485           0 :                         unlink_uid_lock(uid_lock_fd, num, d->name);
     486           0 :                         safe_close(uid_lock_fd);
     487             : 
     488           0 :                         num = new_uid;
     489           0 :                         uid_lock_fd = new_uid_lock_fd;
     490             :                 }
     491           0 :         } else if (is_user && !uid_is_dynamic(num)) {
     492             :                 struct passwd *p;
     493             : 
     494             :                 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
     495           0 :                 errno = 0;
     496           0 :                 p = getpwuid(num);
     497           0 :                 if (!p)
     498           0 :                         return errno_or_else(ESRCH);
     499             : 
     500           0 :                 gid = p->pw_gid;
     501             :         }
     502             : 
     503             :         /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
     504             :          * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
     505             :          * dynamically right here, push that in along with the lock fd for it. */
     506           0 :         r = dynamic_user_push(d, num, uid_lock_fd);
     507           0 :         if (r < 0)
     508           0 :                 return r;
     509             : 
     510           0 :         if (flush_cache) {
     511             :                 /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
     512             :                  * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
     513             :                  * potential for nscd wanting to lock that for completing the invalidation. */
     514           0 :                 etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
     515           0 :                 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
     516             :         }
     517             : 
     518           0 :         if (is_user) {
     519           0 :                 *ret_uid = num;
     520           0 :                 *ret_gid = gid != GID_INVALID ? gid : num;
     521             :         } else
     522           0 :                 *ret_gid = num;
     523             : 
     524           0 :         return 0;
     525             : }
     526             : 
     527           0 : int dynamic_user_current(DynamicUser *d, uid_t *ret) {
     528           0 :         _cleanup_(unlockfp) int storage_socket0_lock = -1;
     529           0 :         _cleanup_close_ int lock_fd = -1;
     530             :         uid_t uid;
     531             :         int r;
     532             : 
     533           0 :         assert(d);
     534           0 :         assert(ret);
     535             : 
     536             :         /* 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. */
     537             : 
     538           0 :         r = lockfp(d->storage_socket[0], &storage_socket0_lock);
     539           0 :         if (r < 0)
     540           0 :                 return r;
     541             : 
     542           0 :         r = dynamic_user_pop(d, &uid, &lock_fd);
     543           0 :         if (r < 0)
     544           0 :                 return r;
     545             : 
     546           0 :         r = dynamic_user_push(d, uid, lock_fd);
     547           0 :         if (r < 0)
     548           0 :                 return r;
     549             : 
     550           0 :         *ret = uid;
     551           0 :         return 0;
     552             : }
     553             : 
     554        1118 : static DynamicUser* dynamic_user_unref(DynamicUser *d) {
     555        1118 :         if (!d)
     556        1118 :                 return NULL;
     557             : 
     558             :         /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed
     559             :          * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries
     560             :          * with no references, which is commonly the case right before a daemon reload. */
     561             : 
     562           0 :         assert(d->n_ref > 0);
     563           0 :         d->n_ref--;
     564             : 
     565           0 :         return NULL;
     566             : }
     567             : 
     568           0 : static int dynamic_user_close(DynamicUser *d) {
     569           0 :         _cleanup_(unlockfp) int storage_socket0_lock = -1;
     570           0 :         _cleanup_close_ int lock_fd = -1;
     571             :         uid_t uid;
     572             :         int r;
     573             : 
     574             :         /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is
     575             :          * unrealized again, much like it was after it the DynamicUser object was first allocated. */
     576             : 
     577           0 :         r = lockfp(d->storage_socket[0], &storage_socket0_lock);
     578           0 :         if (r < 0)
     579           0 :                 return r;
     580             : 
     581           0 :         r = dynamic_user_pop(d, &uid, &lock_fd);
     582           0 :         if (r == -EAGAIN)
     583             :                 /* User wasn't realized yet, nothing to do. */
     584           0 :                 return 0;
     585           0 :         if (r < 0)
     586           0 :                 return r;
     587             : 
     588             :         /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
     589           0 :         unlink_uid_lock(lock_fd, uid, d->name);
     590             : 
     591           0 :         (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
     592           0 :         return 1;
     593             : }
     594             : 
     595           0 : static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
     596           0 :         if (!d)
     597           0 :                 return NULL;
     598             : 
     599             :         /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
     600             :          * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
     601             :          * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
     602             :          * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
     603             : 
     604           0 :         dynamic_user_unref(d);
     605             : 
     606           0 :         if (d->n_ref > 0)
     607           0 :                 return NULL;
     608             : 
     609           0 :         (void) dynamic_user_close(d);
     610           0 :         return dynamic_user_free(d);
     611             : }
     612             : 
     613           0 : int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
     614             :         DynamicUser *d;
     615             :         Iterator i;
     616             : 
     617           0 :         assert(m);
     618           0 :         assert(f);
     619           0 :         assert(fds);
     620             : 
     621             :         /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
     622             : 
     623           0 :         HASHMAP_FOREACH(d, m->dynamic_users, i) {
     624             :                 int copy0, copy1;
     625             : 
     626           0 :                 copy0 = fdset_put_dup(fds, d->storage_socket[0]);
     627           0 :                 if (copy0 < 0)
     628           0 :                         return log_error_errno(copy0, "Failed to add dynamic user storage fd to serialization: %m");
     629             : 
     630           0 :                 copy1 = fdset_put_dup(fds, d->storage_socket[1]);
     631           0 :                 if (copy1 < 0)
     632           0 :                         return log_error_errno(copy1, "Failed to add dynamic user storage fd to serialization: %m");
     633             : 
     634           0 :                 (void) serialize_item_format(f, "dynamic-user", "%s %i %i", d->name, copy0, copy1);
     635             :         }
     636             : 
     637           0 :         return 0;
     638             : }
     639             : 
     640           0 : void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds) {
     641           0 :         _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
     642             :         int r, fd0, fd1;
     643             : 
     644           0 :         assert(m);
     645           0 :         assert(value);
     646           0 :         assert(fds);
     647             : 
     648             :         /* Parse the serialization again, after a daemon reload */
     649             : 
     650           0 :         r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL);
     651           0 :         if (r != 3 || !isempty(value)) {
     652           0 :                 log_debug("Unable to parse dynamic user line.");
     653           0 :                 return;
     654             :         }
     655             : 
     656           0 :         if (safe_atoi(s0, &fd0) < 0 || !fdset_contains(fds, fd0)) {
     657           0 :                 log_debug("Unable to process dynamic user fd specification.");
     658           0 :                 return;
     659             :         }
     660             : 
     661           0 :         if (safe_atoi(s1, &fd1) < 0 || !fdset_contains(fds, fd1)) {
     662           0 :                 log_debug("Unable to process dynamic user fd specification.");
     663           0 :                 return;
     664             :         }
     665             : 
     666           0 :         r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, NULL);
     667           0 :         if (r < 0) {
     668           0 :                 log_debug_errno(r, "Failed to add dynamic user: %m");
     669           0 :                 return;
     670             :         }
     671             : 
     672           0 :         (void) fdset_remove(fds, fd0);
     673           0 :         (void) fdset_remove(fds, fd1);
     674             : }
     675             : 
     676          27 : void dynamic_user_vacuum(Manager *m, bool close_user) {
     677             :         DynamicUser *d;
     678             :         Iterator i;
     679             : 
     680          27 :         assert(m);
     681             : 
     682             :         /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
     683             :          * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
     684             :          * might not be referenced anymore. */
     685             : 
     686          27 :         HASHMAP_FOREACH(d, m->dynamic_users, i) {
     687           0 :                 if (d->n_ref > 0)
     688           0 :                         continue;
     689             : 
     690           0 :                 if (close_user) {
     691           0 :                         log_debug("Removing orphaned dynamic user %s", d->name);
     692           0 :                         (void) dynamic_user_close(d);
     693             :                 }
     694             : 
     695           0 :                 dynamic_user_free(d);
     696             :         }
     697          27 : }
     698             : 
     699           0 : int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
     700             :         char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
     701           0 :         _cleanup_free_ char *user = NULL;
     702             :         uid_t check_uid;
     703             :         int r;
     704             : 
     705           0 :         assert(m);
     706           0 :         assert(ret);
     707             : 
     708             :         /* A friendly way to translate a dynamic user's UID into a name. */
     709           0 :         if (!uid_is_dynamic(uid))
     710           0 :                 return -ESRCH;
     711             : 
     712           0 :         xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
     713           0 :         r = read_one_line_file(lock_path, &user);
     714           0 :         if (IN_SET(r, -ENOENT, 0))
     715           0 :                 return -ESRCH;
     716           0 :         if (r < 0)
     717           0 :                 return r;
     718             : 
     719             :         /* The lock file might be stale, hence let's verify the data before we return it */
     720           0 :         r = dynamic_user_lookup_name(m, user, &check_uid);
     721           0 :         if (r < 0)
     722           0 :                 return r;
     723           0 :         if (check_uid != uid) /* lock file doesn't match our own idea */
     724           0 :                 return -ESRCH;
     725             : 
     726           0 :         *ret = TAKE_PTR(user);
     727             : 
     728           0 :         return 0;
     729             : }
     730             : 
     731           0 : int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
     732             :         DynamicUser *d;
     733             :         int r;
     734             : 
     735           0 :         assert(m);
     736           0 :         assert(name);
     737           0 :         assert(ret);
     738             : 
     739             :         /* A friendly call for translating a dynamic user's name into its UID */
     740             : 
     741           0 :         d = hashmap_get(m->dynamic_users, name);
     742           0 :         if (!d)
     743           0 :                 return -ESRCH;
     744             : 
     745           0 :         r = dynamic_user_current(d, ret);
     746           0 :         if (r == -EAGAIN) /* not realized yet? */
     747           0 :                 return -ESRCH;
     748             : 
     749           0 :         return r;
     750             : }
     751             : 
     752           0 : int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group) {
     753           0 :         bool acquired = false;
     754             :         int r;
     755             : 
     756           0 :         assert(creds);
     757           0 :         assert(m);
     758             : 
     759             :         /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
     760             :          * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
     761             :          * and group. This call allocates a pair. */
     762             : 
     763           0 :         if (!creds->user && user) {
     764           0 :                 r = dynamic_user_acquire(m, user, &creds->user);
     765           0 :                 if (r < 0)
     766           0 :                         return r;
     767             : 
     768           0 :                 acquired = true;
     769             :         }
     770             : 
     771           0 :         if (!creds->group) {
     772             : 
     773           0 :                 if (creds->user && (!group || streq_ptr(user, group)))
     774           0 :                         creds->group = dynamic_user_ref(creds->user);
     775             :                 else {
     776           0 :                         r = dynamic_user_acquire(m, group, &creds->group);
     777           0 :                         if (r < 0) {
     778           0 :                                 if (acquired)
     779           0 :                                         creds->user = dynamic_user_unref(creds->user);
     780           0 :                                 return r;
     781             :                         }
     782             :                 }
     783             :         }
     784             : 
     785           0 :         return 0;
     786             : }
     787             : 
     788           0 : int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
     789           0 :         uid_t u = UID_INVALID;
     790           0 :         gid_t g = GID_INVALID;
     791             :         int r;
     792             : 
     793           0 :         assert(creds);
     794           0 :         assert(uid);
     795           0 :         assert(gid);
     796             : 
     797             :         /* Realize both the referenced user and group */
     798             : 
     799           0 :         if (creds->user) {
     800           0 :                 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true);
     801           0 :                 if (r < 0)
     802           0 :                         return r;
     803             :         }
     804             : 
     805           0 :         if (creds->group && creds->group != creds->user) {
     806           0 :                 r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false);
     807           0 :                 if (r < 0)
     808           0 :                         return r;
     809             :         }
     810             : 
     811           0 :         *uid = u;
     812           0 :         *gid = g;
     813           0 :         return 0;
     814             : }
     815             : 
     816         559 : void dynamic_creds_unref(DynamicCreds *creds) {
     817         559 :         assert(creds);
     818             : 
     819         559 :         creds->user = dynamic_user_unref(creds->user);
     820         559 :         creds->group = dynamic_user_unref(creds->group);
     821         559 : }
     822             : 
     823           0 : void dynamic_creds_destroy(DynamicCreds *creds) {
     824           0 :         assert(creds);
     825             : 
     826           0 :         creds->user = dynamic_user_destroy(creds->user);
     827           0 :         creds->group = dynamic_user_destroy(creds->group);
     828           0 : }

Generated by: LCOV version 1.14