]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dynamic-user.c
core/dynamic-user: trivial modernization
[thirdparty/systemd.git] / src / core / dynamic-user.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/file.h>
4 #include <sys/stat.h>
5 #include <sys/types.h>
6
7 #include "clean-ipc.h"
8 #include "dynamic-user.h"
9 #include "fd-util.h"
10 #include "fileio.h"
11 #include "format-util.h"
12 #include "fs-util.h"
13 #include "iovec-util.h"
14 #include "lock-util.h"
15 #include "nscd-flush.h"
16 #include "parse-util.h"
17 #include "random-util.h"
18 #include "serialize.h"
19 #include "socket-util.h"
20 #include "stdio-util.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "uid-classification.h"
24 #include "user-util.h"
25
26 /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
27 #define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
28
29 DEFINE_TRIVIAL_REF_FUNC(DynamicUser, dynamic_user);
30
31 DynamicUser* dynamic_user_free(DynamicUser *d) {
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);
39 return mfree(d);
40 }
41
42 static int dynamic_user_add(Manager *m, const char *name, int storage_socket[static 2], DynamicUser **ret) {
43 DynamicUser *d;
44 int r;
45
46 assert(m || ret);
47 assert(name);
48 assert(storage_socket);
49
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 }
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
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 }
71 }
72
73 d->manager = m;
74
75 if (ret)
76 *ret = d;
77
78 return 0;
79 }
80
81 static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
82 _cleanup_close_pair_ int storage_socket[2] = EBADF_PAIR;
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) {
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 }
122 return 0;
123 }
124
125 if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC))
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
135 storage_socket[0] = storage_socket[1] = -EBADF;
136
137 if (ret) {
138 d->n_ref++;
139 *ret = d;
140 }
141
142 return 1;
143 }
144
145 static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
146 char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
147 const char *path2;
148 int r = 0, k;
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);
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;
165 }
166
167 path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
168 if (unlink(path2) < 0 && errno != ENOENT) {
169 k = -errno;
170 if (r == 0)
171 r = k;
172 }
173
174 if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
175 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2);
176 if (r == 0)
177 r = k;
178 }
179
180 return r;
181 }
182
183 static 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
189 * LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
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;
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
211 unsigned n_tries = 100, current_suggested = 0;
212 int r;
213
214 (void) mkdir("/run/systemd/dynamic-uid", 0755);
215
216 for (;;) {
217 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
218 _cleanup_close_ int lock_fd = -EBADF;
219 uid_t candidate;
220 ssize_t l;
221
222 if (--n_tries <= 0) /* Give up retrying eventually */
223 return -EBUSY;
224
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:
260 assert_not_reached();
261 }
262
263 /* Make sure whatever we picked here actually is in the right range */
264 if (!uid_is_dynamic(candidate))
265 continue;
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) {
278 if (IN_SET(errno, EBUSY, EAGAIN))
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
289 /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
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 */
295 if (getpwuid_malloc(candidate, /* ret= */ NULL) >= 0 ||
296 getgrgid_malloc((gid_t) candidate, /* ret= */ NULL) >= 0 ||
297 search_ipc(candidate, (gid_t) candidate) != 0) {
298 (void) unlink(lock_path);
299 continue;
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]) {
305 IOVEC_MAKE_STRING(name),
306 IOVEC_MAKE((char[1]) { '\n' }, 1),
307 }, 2, 0);
308 if (l < 0) {
309 r = -errno;
310 (void) unlink(lock_path);
311 return r;
312 }
313
314 (void) ftruncate(lock_fd, l);
315 (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
316
317 *ret_uid = candidate;
318 return TAKE_FD(lock_fd);
319
320 next:
321 ;
322 }
323 }
324
325 static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
326 uid_t uid = UID_INVALID;
327 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
328 int lock_fd;
329 ssize_t k;
330
331 assert(d);
332 assert(ret_uid);
333 assert(ret_lock_fd);
334
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. */
337
338 k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
339 if (k < 0)
340 return (int) k;
341
342 *ret_uid = uid;
343 *ret_lock_fd = lock_fd;
344
345 return 0;
346 }
347
348 static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
349 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
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. */
354 return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
355 }
356
357 static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
358 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
359
360 if (lock_fd < 0)
361 return;
362
363 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
364 (void) unlink(lock_path);
365
366 (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
367 }
368
369 static int dynamic_user_realize(
370 DynamicUser *d,
371 char **suggested_dirs,
372 uid_t *ret_uid, gid_t *ret_gid,
373 bool is_user) {
374
375 _cleanup_close_ int uid_lock_fd = -EBADF;
376 _cleanup_close_ int etc_passwd_lock_fd = -EBADF;
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 */
379 bool flush_cache = false;
380 int r;
381
382 assert(d);
383 assert(is_user == !!ret_uid);
384 assert(ret_gid);
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
389 r = posix_lock(d->storage_socket[0], LOCK_EX);
390 if (r < 0)
391 return r;
392
393 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
394
395 r = dynamic_user_pop(d, &num, &uid_lock_fd);
396 if (r < 0) {
397 int new_uid_lock_fd;
398 uid_t new_uid;
399
400 if (r != -EAGAIN)
401 return r;
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. */
405 r = posix_lock(d->storage_socket[0], LOCK_UN);
406 if (r < 0)
407 return r;
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 */
417 r = parse_uid(d->name, &num);
418 if (r < 0) {
419 _cleanup_free_ struct passwd *p = NULL;
420 _cleanup_free_ struct group *g = NULL;
421
422 if (is_user) {
423 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
424 if (getpwnam_malloc(d->name, &p) >= 0) {
425 num = p->pw_uid;
426 gid = p->pw_gid;
427 } else {
428 /* if the user does not exist but the group with the same name exists, refuse operation */
429 if (getgrnam_malloc(d->name, /* ret= */ NULL) >= 0)
430 return -EILSEQ;
431 }
432 } else {
433 /* Let's see if there's a group by this name */
434 if (getgrnam_malloc(d->name, &g) >= 0)
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 if (getpwnam_malloc(d->name, /* ret= */ NULL) >= 0)
439 return -EILSEQ;
440 }
441 }
442 }
443
444 if (num == UID_INVALID) {
445 /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
446
447 uid_lock_fd = pick_uid(suggested_dirs, d->name, &num);
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. */
453 r = posix_lock(d->storage_socket[0], LOCK_EX);
454 if (r < 0) {
455 unlink_uid_lock(uid_lock_fd, num, d->name);
456 return r;
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. */
463 unlink_uid_lock(uid_lock_fd, num, d->name);
464 return r;
465 }
466
467 /* Great! Nothing is stored here, still. Store our newly acquired data. */
468 flush_cache = true;
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 } else if (is_user && !uid_is_dynamic(num)) {
480 _cleanup_free_ struct passwd *p = NULL;
481
482 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
483 r = getpwuid_malloc(num, &p);
484 if (r < 0)
485 return r;
486
487 gid = p->pw_gid;
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. */
493 r = dynamic_user_push(d, num, uid_lock_fd);
494 if (r < 0)
495 return r;
496
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
505 if (is_user) {
506 *ret_uid = num;
507 *ret_gid = gid != GID_INVALID ? gid : num;
508 } else
509 *ret_gid = num;
510
511 return 0;
512 }
513
514 int dynamic_user_current(DynamicUser *d, uid_t *ret) {
515 _cleanup_close_ int lock_fd = -EBADF;
516 uid_t uid;
517 int r;
518
519 assert(d);
520
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. */
523
524 r = posix_lock(d->storage_socket[0], LOCK_EX);
525 if (r < 0)
526 return r;
527
528 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
529
530 r = dynamic_user_pop(d, &uid, &lock_fd);
531 if (r < 0)
532 return r;
533
534 r = dynamic_user_push(d, uid, lock_fd);
535 if (r < 0)
536 return r;
537
538 if (ret)
539 *ret = uid;
540
541 return 0;
542 }
543
544 static DynamicUser* dynamic_user_unref(DynamicUser *d) {
545 if (!d)
546 return NULL;
547
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. */
551
552 assert(d->n_ref > 0);
553 d->n_ref--;
554
555 return NULL;
556 }
557
558 static int dynamic_user_close(DynamicUser *d) {
559 _cleanup_close_ int lock_fd = -EBADF;
560 uid_t uid;
561 int r;
562
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. */
565
566 r = posix_lock(d->storage_socket[0], LOCK_EX);
567 if (r < 0)
568 return r;
569
570 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
571
572 r = dynamic_user_pop(d, &uid, &lock_fd);
573 if (r == -EAGAIN)
574 /* User wasn't realized yet, nothing to do. */
575 return 0;
576 if (r < 0)
577 return r;
578
579 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
580 unlink_uid_lock(lock_fd, uid, d->name);
581
582 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
583 return 1;
584 }
585
586 static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
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
604 int dynamic_user_serialize_one(DynamicUser *d, const char *key, FILE *f, FDSet *fds) {
605 int copy0, copy1;
606
607 assert(key);
608 assert(f);
609 assert(fds);
610
611 if (!d)
612 return 0;
613
614 if (d->storage_socket[0] < 0 || d->storage_socket[1] < 0)
615 return 0;
616
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");
620
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");
624
625 (void) serialize_item_format(f, key, "%s %i %i", d->name, copy0, copy1);
626
627 return 0;
628 }
629
630 int 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);
639
640 return 0;
641 }
642
643 void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds, DynamicUser **ret) {
644 _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
645 _cleanup_close_ int fd0 = -EBADF, fd1 = -EBADF;
646 int r;
647
648 assert(value);
649 assert(fds);
650
651 /* Parse the serialization again, after a daemon reload */
652
653 r = extract_many_words(&value, NULL, 0, &name, &s0, &s1);
654 if (r != 3 || !isempty(value)) {
655 log_debug("Unable to parse dynamic user line.");
656 return;
657 }
658
659 fd0 = deserialize_fd(fds, s0);
660 if (fd0 < 0)
661 return;
662
663 fd1 = deserialize_fd(fds, s1);
664 if (fd1 < 0)
665 return;
666
667 r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, ret);
668 if (r < 0) {
669 log_debug_errno(r, "Failed to add dynamic user: %m");
670 return;
671 }
672
673 TAKE_FD(fd0);
674 TAKE_FD(fd1);
675
676 if (ret) /* If the caller uses it directly, increment the refcount */
677 (*ret)->n_ref++;
678 }
679
680 void dynamic_user_vacuum(Manager *m, bool close_user) {
681 DynamicUser *d;
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
689 HASHMAP_FOREACH(d, m->dynamic_users) {
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
702 int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
703 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
704 _cleanup_free_ char *user = NULL;
705 uid_t check_uid;
706 int r;
707
708 assert(m);
709 assert(ret);
710
711 /* A friendly way to translate a dynamic user's UID into a name. */
712 if (!uid_is_dynamic(uid))
713 return -ESRCH;
714
715 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
716 r = read_one_line_file(lock_path, &user);
717 if (IN_SET(r, -ENOENT, 0))
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
729 *ret = TAKE_PTR(user);
730
731 return 0;
732 }
733
734 int 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);
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
754 int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
755 _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
756 int r;
757
758 assert(m);
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;
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
774 if (user) {
775 r = dynamic_user_acquire(m, user, &creds->user);
776 if (r < 0)
777 return r;
778 }
779
780 if (group && !streq_ptr(user, group)) {
781 r = dynamic_user_acquire(m, group, &creds->group);
782 if (r < 0)
783 return r;
784 } else
785 creds->group = ASSERT_PTR(dynamic_user_ref(creds->user));
786
787 *ret = TAKE_PTR(creds);
788
789 return 0;
790 }
791
792 int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
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) {
804 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true);
805 if (r < 0)
806 return r;
807 }
808
809 if (creds->group && creds->group != creds->user) {
810 r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false);
811 if (r < 0)
812 return r;
813 }
814
815 *uid = u;
816 *gid = g;
817 return 0;
818 }
819
820 DynamicCreds* dynamic_creds_unref(DynamicCreds *creds) {
821 if (!creds)
822 return NULL;
823
824 creds->user = dynamic_user_unref(creds->user);
825 creds->group = dynamic_user_unref(creds->group);
826
827 return mfree(creds);
828 }
829
830 DynamicCreds* dynamic_creds_destroy(DynamicCreds *creds) {
831 if (!creds)
832 return NULL;
833
834 creds->user = dynamic_user_destroy(creds->user);
835 creds->group = dynamic_user_destroy(creds->group);
836
837 return mfree(creds);
838 }
839
840 void 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 }
848
849 void 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 }