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