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