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