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