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