LCOV - code coverage report
Current view: top level - core - dynamic-user.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 12 399 3.0 %
Date: 2019-08-23 13:36:53 Functions: 3 25 12.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 4 341 1.2 %

           Branch data     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                 :       4472 : static DynamicUser* dynamic_user_unref(DynamicUser *d) {
     555         [ +  - ]:       4472 :         if (!d)
     556                 :       4472 :                 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                 :        108 : void dynamic_user_vacuum(Manager *m, bool close_user) {
     677                 :            :         DynamicUser *d;
     678                 :            :         Iterator i;
     679                 :            : 
     680         [ -  + ]:        108 :         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         [ -  + ]:        108 :         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                 :        108 : }
     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                 :       2236 : void dynamic_creds_unref(DynamicCreds *creds) {
     817         [ -  + ]:       2236 :         assert(creds);
     818                 :            : 
     819                 :       2236 :         creds->user = dynamic_user_unref(creds->user);
     820                 :       2236 :         creds->group = dynamic_user_unref(creds->group);
     821                 :       2236 : }
     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