]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dynamic-user.c
user-util: add get{pw,gr}{uid,gid,name}_malloc() helpers
[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
147 char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
148 const char *path2;
149 int r = 0, k;
150
151 /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
152 * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
153 * would be its own client then). We hence keep these world-readable symlinks in place, so that the
154 * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
155 * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
156 * on them and as those may be taken by any user with read access we can't make them world-readable. */
157
158 xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
159 if (unlink(path1) < 0 && errno != ENOENT)
160 r = -errno;
161
162 if (b && symlink(name, path1) < 0) {
163 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
164 if (r == 0)
165 r = k;
166 }
167
168 path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
169 if (unlink(path2) < 0 && errno != ENOENT) {
170 k = -errno;
171 if (r == 0)
172 r = k;
173 }
174
175 if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
176 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2);
177 if (r == 0)
178 r = k;
179 }
180
181 return r;
182 }
183
184 static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
185
186 /* Find a suitable free UID. We use the following strategy to find a suitable UID:
187 *
188 * 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
189 * them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
190 * LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
191 * recursively chown() them to new users.
192 *
193 * 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
194 * pretty good, as the use ris by default derived from the unit name, and hence the same service and same
195 * user should usually get the same UID as long as our hashing doesn't clash.
196 *
197 * 3. Finally, if that didn't work, we randomly pick UIDs, until we find one that is empty.
198 *
199 * Since the dynamic UID space is relatively small we'll stop trying after 100 iterations, giving up. */
200
201 enum {
202 PHASE_SUGGESTED, /* the first phase, reusing directory ownership UIDs */
203 PHASE_HASHED, /* the second phase, deriving a UID from the username by hashing */
204 PHASE_RANDOM, /* the last phase, randomly picking UIDs */
205 } phase = PHASE_SUGGESTED;
206
207 static const uint8_t hash_key[] = {
208 0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5,
209 0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59
210 };
211
212 unsigned n_tries = 100, current_suggested = 0;
213 int r;
214
215 (void) mkdir("/run/systemd/dynamic-uid", 0755);
216
217 for (;;) {
218 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
219 _cleanup_close_ int lock_fd = -EBADF;
220 uid_t candidate;
221 ssize_t l;
222
223 if (--n_tries <= 0) /* Give up retrying eventually */
224 return -EBUSY;
225
226 switch (phase) {
227
228 case PHASE_SUGGESTED: {
229 struct stat st;
230
231 if (!suggested_paths || !suggested_paths[current_suggested]) {
232 /* We reached the end of the suggested paths list, let's try by hashing the name */
233 phase = PHASE_HASHED;
234 continue;
235 }
236
237 if (stat(suggested_paths[current_suggested++], &st) < 0)
238 continue; /* We can't read the UID of this path, but that doesn't matter, just try the next */
239
240 candidate = st.st_uid;
241 break;
242 }
243
244 case PHASE_HASHED:
245 /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We
246 * start with a UID generated as hash from the user name. */
247 candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key));
248
249 /* If this one fails, we should proceed with random tries */
250 phase = PHASE_RANDOM;
251 break;
252
253 case PHASE_RANDOM:
254
255 /* Pick another random UID, and see if that works for us. */
256 random_bytes(&candidate, sizeof(candidate));
257 candidate = UID_CLAMP_INTO_RANGE(candidate);
258 break;
259
260 default:
261 assert_not_reached();
262 }
263
264 /* Make sure whatever we picked here actually is in the right range */
265 if (!uid_is_dynamic(candidate))
266 continue;
267
268 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);
269
270 for (;;) {
271 struct stat st;
272
273 lock_fd = open(lock_path, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
274 if (lock_fd < 0)
275 return -errno;
276
277 r = flock(lock_fd, LOCK_EX|LOCK_NB); /* Try to get a BSD file lock on the UID lock file */
278 if (r < 0) {
279 if (IN_SET(errno, EBUSY, EAGAIN))
280 goto next; /* already in use */
281
282 return -errno;
283 }
284
285 if (fstat(lock_fd, &st) < 0)
286 return -errno;
287 if (st.st_nlink > 0)
288 break;
289
290 /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
291 * got the lock. Close it, and try again. */
292 lock_fd = safe_close(lock_fd);
293 }
294
295 /* Some superficial check whether this UID/GID might already be taken by some static user */
296 if (getpwuid_malloc(candidate, /* ret= */ NULL) >= 0 ||
297 getgrgid_malloc((gid_t) candidate, /* ret= */ NULL) >= 0 ||
298 search_ipc(candidate, (gid_t) candidate) != 0) {
299 (void) unlink(lock_path);
300 continue;
301 }
302
303 /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
304 l = pwritev(lock_fd,
305 (struct iovec[2]) {
306 IOVEC_MAKE_STRING(name),
307 IOVEC_MAKE((char[1]) { '\n' }, 1),
308 }, 2, 0);
309 if (l < 0) {
310 r = -errno;
311 (void) unlink(lock_path);
312 return r;
313 }
314
315 (void) ftruncate(lock_fd, l);
316 (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
317
318 *ret_uid = candidate;
319 return TAKE_FD(lock_fd);
320
321 next:
322 ;
323 }
324 }
325
326 static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
327 uid_t uid = UID_INVALID;
328 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
329 int lock_fd;
330 ssize_t k;
331
332 assert(d);
333 assert(ret_uid);
334 assert(ret_lock_fd);
335
336 /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with
337 * the lock on the socket taken. */
338
339 k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
340 if (k < 0)
341 return (int) k;
342
343 *ret_uid = uid;
344 *ret_lock_fd = lock_fd;
345
346 return 0;
347 }
348
349 static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
350 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
351
352 assert(d);
353
354 /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
355 return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
356 }
357
358 static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
359 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
360
361 if (lock_fd < 0)
362 return;
363
364 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
365 (void) unlink(lock_path);
366
367 (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
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 is_user) {
375
376 _cleanup_close_ int uid_lock_fd = -EBADF;
377 _cleanup_close_ int etc_passwd_lock_fd = -EBADF;
378 uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */
379 gid_t gid = GID_INVALID; /* a gid if is_user, ignored otherwise */
380 bool flush_cache = false;
381 int r;
382
383 assert(d);
384 assert(is_user == !!ret_uid);
385 assert(ret_gid);
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 = posix_lock(d->storage_socket[0], LOCK_EX);
391 if (r < 0)
392 return r;
393
394 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
395
396 r = dynamic_user_pop(d, &num, &uid_lock_fd);
397 if (r < 0) {
398 int new_uid_lock_fd;
399 uid_t new_uid;
400
401 if (r != -EAGAIN)
402 return r;
403
404 /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the
405 * lock however, so that nobody else blocks on our NSS lookups. */
406 r = posix_lock(d->storage_socket[0], LOCK_UN);
407 if (r < 0)
408 return r;
409
410 /* Let's see if a proper, static user or group by this name exists. Try to take the lock on
411 * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't
412 * take the lock, given that users can't be added there anyway in this case. */
413 etc_passwd_lock_fd = take_etc_passwd_lock(NULL);
414 if (etc_passwd_lock_fd < 0 && etc_passwd_lock_fd != -EROFS)
415 return etc_passwd_lock_fd;
416
417 /* First, let's parse this as numeric UID */
418 r = parse_uid(d->name, &num);
419 if (r < 0) {
420 _cleanup_free_ struct passwd *p = NULL;
421 _cleanup_free_ struct group *g = NULL;
422
423 if (is_user) {
424 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
425 if (getpwnam_malloc(d->name, &p) >= 0) {
426 num = p->pw_uid;
427 gid = p->pw_gid;
428 } else {
429 /* if the user does not exist but the group with the same name exists, refuse operation */
430 if (getgrnam_malloc(d->name, /* ret= */ NULL) >= 0)
431 return -EILSEQ;
432 }
433 } else {
434 /* Let's see if there's a group by this name */
435 if (getgrnam_malloc(d->name, &g) >= 0)
436 num = (uid_t) g->gr_gid;
437 else {
438 /* if the group does not exist but the user with the same name exists, refuse operation */
439 if (getpwnam_malloc(d->name, /* ret= */ NULL) >= 0)
440 return -EILSEQ;
441 }
442 }
443 }
444
445 if (num == UID_INVALID) {
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 = posix_lock(d->storage_socket[0], LOCK_EX);
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 != -EAGAIN) {
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 flush_cache = true;
470 } else {
471 /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
472 * acquired, and use what's stored now. */
473
474 unlink_uid_lock(uid_lock_fd, num, d->name);
475 safe_close(uid_lock_fd);
476
477 num = new_uid;
478 uid_lock_fd = new_uid_lock_fd;
479 }
480 } else if (is_user && !uid_is_dynamic(num)) {
481 _cleanup_free_ struct passwd *p = NULL;
482
483 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
484 r = getpwuid_malloc(num, &p);
485 if (r < 0)
486 return r;
487
488 gid = p->pw_gid;
489 }
490
491 /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
492 * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
493 * dynamically right here, push that in along with the lock fd for it. */
494 r = dynamic_user_push(d, num, uid_lock_fd);
495 if (r < 0)
496 return r;
497
498 if (flush_cache) {
499 /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
500 * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
501 * potential for nscd wanting to lock that for completing the invalidation. */
502 etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
503 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
504 }
505
506 if (is_user) {
507 *ret_uid = num;
508 *ret_gid = gid != GID_INVALID ? gid : num;
509 } else
510 *ret_gid = num;
511
512 return 0;
513 }
514
515 int dynamic_user_current(DynamicUser *d, uid_t *ret) {
516 _cleanup_close_ int lock_fd = -EBADF;
517 uid_t uid;
518 int r;
519
520 assert(d);
521
522 /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the
523 * storage socket, and pushes it back in right-away. */
524
525 r = posix_lock(d->storage_socket[0], LOCK_EX);
526 if (r < 0)
527 return r;
528
529 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
530
531 r = dynamic_user_pop(d, &uid, &lock_fd);
532 if (r < 0)
533 return r;
534
535 r = dynamic_user_push(d, uid, lock_fd);
536 if (r < 0)
537 return r;
538
539 if (ret)
540 *ret = uid;
541
542 return 0;
543 }
544
545 static DynamicUser* dynamic_user_unref(DynamicUser *d) {
546 if (!d)
547 return NULL;
548
549 /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully
550 * destroyed and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may
551 * contain entries with no references, which is commonly the case right before a daemon reload. */
552
553 assert(d->n_ref > 0);
554 d->n_ref--;
555
556 return NULL;
557 }
558
559 static int dynamic_user_close(DynamicUser *d) {
560 _cleanup_close_ int lock_fd = -EBADF;
561 uid_t uid;
562 int r;
563
564 /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the
565 * user is unrealized again, much like it was after it the DynamicUser object was first allocated. */
566
567 r = posix_lock(d->storage_socket[0], LOCK_EX);
568 if (r < 0)
569 return r;
570
571 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
572
573 r = dynamic_user_pop(d, &uid, &lock_fd);
574 if (r == -EAGAIN)
575 /* User wasn't realized yet, nothing to do. */
576 return 0;
577 if (r < 0)
578 return r;
579
580 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
581 unlink_uid_lock(lock_fd, uid, d->name);
582
583 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
584 return 1;
585 }
586
587 static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
588 if (!d)
589 return NULL;
590
591 /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
592 * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
593 * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
594 * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
595
596 dynamic_user_unref(d);
597
598 if (d->n_ref > 0)
599 return NULL;
600
601 (void) dynamic_user_close(d);
602 return dynamic_user_free(d);
603 }
604
605 int dynamic_user_serialize_one(DynamicUser *d, const char *key, FILE *f, FDSet *fds) {
606 int copy0, copy1;
607
608 assert(key);
609 assert(f);
610 assert(fds);
611
612 if (!d)
613 return 0;
614
615 if (d->storage_socket[0] < 0 || d->storage_socket[1] < 0)
616 return 0;
617
618 copy0 = fdset_put_dup(fds, d->storage_socket[0]);
619 if (copy0 < 0)
620 return log_error_errno(copy0, "Failed to add dynamic user storage fd to serialization: %m");
621
622 copy1 = fdset_put_dup(fds, d->storage_socket[1]);
623 if (copy1 < 0)
624 return log_error_errno(copy1, "Failed to add dynamic user storage fd to serialization: %m");
625
626 (void) serialize_item_format(f, key, "%s %i %i", d->name, copy0, copy1);
627
628 return 0;
629 }
630
631 int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
632 DynamicUser *d;
633
634 assert(m);
635
636 /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
637
638 HASHMAP_FOREACH(d, m->dynamic_users)
639 (void) dynamic_user_serialize_one(d, "dynamic-user", f, fds);
640
641 return 0;
642 }
643
644 void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds, DynamicUser **ret) {
645 _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
646 _cleanup_close_ int fd0 = -EBADF, fd1 = -EBADF;
647 int r;
648
649 assert(value);
650 assert(fds);
651
652 /* Parse the serialization again, after a daemon reload */
653
654 r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL);
655 if (r != 3 || !isempty(value)) {
656 log_debug("Unable to parse dynamic user line.");
657 return;
658 }
659
660 fd0 = deserialize_fd(fds, s0);
661 if (fd0 < 0)
662 return;
663
664 fd1 = deserialize_fd(fds, s1);
665 if (fd1 < 0)
666 return;
667
668 r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, ret);
669 if (r < 0) {
670 log_debug_errno(r, "Failed to add dynamic user: %m");
671 return;
672 }
673
674 TAKE_FD(fd0);
675 TAKE_FD(fd1);
676
677 if (ret) /* If the caller uses it directly, increment the refcount */
678 (*ret)->n_ref++;
679 }
680
681 void dynamic_user_vacuum(Manager *m, bool close_user) {
682 DynamicUser *d;
683
684 assert(m);
685
686 /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
687 * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
688 * might not be referenced anymore. */
689
690 HASHMAP_FOREACH(d, m->dynamic_users) {
691 if (d->n_ref > 0)
692 continue;
693
694 if (close_user) {
695 log_debug("Removing orphaned dynamic user %s", d->name);
696 (void) dynamic_user_close(d);
697 }
698
699 dynamic_user_free(d);
700 }
701 }
702
703 int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
704 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
705 _cleanup_free_ char *user = NULL;
706 uid_t check_uid;
707 int r;
708
709 assert(m);
710 assert(ret);
711
712 /* A friendly way to translate a dynamic user's UID into a name. */
713 if (!uid_is_dynamic(uid))
714 return -ESRCH;
715
716 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
717 r = read_one_line_file(lock_path, &user);
718 if (IN_SET(r, -ENOENT, 0))
719 return -ESRCH;
720 if (r < 0)
721 return r;
722
723 /* The lock file might be stale, hence let's verify the data before we return it */
724 r = dynamic_user_lookup_name(m, user, &check_uid);
725 if (r < 0)
726 return r;
727 if (check_uid != uid) /* lock file doesn't match our own idea */
728 return -ESRCH;
729
730 *ret = TAKE_PTR(user);
731
732 return 0;
733 }
734
735 int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
736 DynamicUser *d;
737 int r;
738
739 assert(m);
740 assert(name);
741
742 /* A friendly call for translating a dynamic user's name into its UID */
743
744 d = hashmap_get(m->dynamic_users, name);
745 if (!d)
746 return -ESRCH;
747
748 r = dynamic_user_current(d, ret);
749 if (r == -EAGAIN) /* not realized yet? */
750 return -ESRCH;
751
752 return r;
753 }
754
755 int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
756 _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
757 bool acquired = false;
758 int r;
759
760 assert(m);
761 assert(ret);
762
763 if (!user && !group) {
764 *ret = NULL;
765 return 0;
766 }
767
768 creds = new0(DynamicCreds, 1);
769 if (!creds)
770 return -ENOMEM;
771
772 /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
773 * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
774 * and group. This call allocates a pair. */
775
776 if (user) {
777 r = dynamic_user_acquire(m, user, &creds->user);
778 if (r < 0)
779 return r;
780
781 acquired = true;
782 }
783
784 if (creds->user && (!group || streq_ptr(user, group)))
785 creds->group = dynamic_user_ref(creds->user);
786 else if (group) {
787 r = dynamic_user_acquire(m, group, &creds->group);
788 if (r < 0) {
789 if (acquired)
790 creds->user = dynamic_user_unref(creds->user);
791 return r;
792 }
793 }
794
795 *ret = TAKE_PTR(creds);
796
797 return 0;
798 }
799
800 int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
801 uid_t u = UID_INVALID;
802 gid_t g = GID_INVALID;
803 int r;
804
805 assert(creds);
806 assert(uid);
807 assert(gid);
808
809 /* Realize both the referenced user and group */
810
811 if (creds->user) {
812 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true);
813 if (r < 0)
814 return r;
815 }
816
817 if (creds->group && creds->group != creds->user) {
818 r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false);
819 if (r < 0)
820 return r;
821 }
822
823 *uid = u;
824 *gid = g;
825 return 0;
826 }
827
828 DynamicCreds* dynamic_creds_unref(DynamicCreds *creds) {
829 if (!creds)
830 return NULL;
831
832 creds->user = dynamic_user_unref(creds->user);
833 creds->group = dynamic_user_unref(creds->group);
834
835 return mfree(creds);
836 }
837
838 DynamicCreds* dynamic_creds_destroy(DynamicCreds *creds) {
839 if (!creds)
840 return NULL;
841
842 creds->user = dynamic_user_destroy(creds->user);
843 creds->group = dynamic_user_destroy(creds->group);
844
845 return mfree(creds);
846 }
847
848 void dynamic_creds_done(DynamicCreds *creds) {
849 if (!creds)
850 return;
851
852 if (creds->group != creds->user)
853 dynamic_user_free(creds->group);
854 creds->group = creds->user = dynamic_user_free(creds->user);
855 }
856
857 void dynamic_creds_close(DynamicCreds *creds) {
858 if (!creds)
859 return;
860
861 if (creds->user)
862 safe_close_pair(creds->user->storage_socket);
863
864 if (creds->group && creds->group != creds->user)
865 safe_close_pair(creds->group->storage_socket);
866 }