]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dynamic-user.c
fd-uitl: rename PIPE_EBADF → EBADF_PAIR, and add EBADF_TRIPLET
[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"
bd1ae178 13#include "iovec-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
154eb43f 29DEFINE_TRIVIAL_REF_FUNC(DynamicUser, dynamic_user);
8301aa0b 30
bb5232b6 31DynamicUser* dynamic_user_free(DynamicUser *d) {
29206d46
LP
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
154eb43f 46 assert(m || ret);
29206d46
LP
47 assert(name);
48 assert(storage_socket);
49
154eb43f
LB
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 }
29206d46
LP
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
154eb43f
LB
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 }
29206d46
LP
71 }
72
73 d->manager = m;
74
75 if (ret)
76 *ret = d;
77
78 return 0;
79}
80
9da440b1 81static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
71136404 82 _cleanup_close_pair_ int storage_socket[2] = EBADF_PAIR;
29206d46
LP
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) {
288ca7af
YW
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 }
29206d46
LP
122 return 0;
123 }
124
7a8867ab 125 if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC))
29206d46
LP
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
5bb1d7fb 135 storage_socket[0] = storage_socket[1] = -EBADF;
29206d46
LP
136
137 if (ret) {
138 d->n_ref++;
139 *ret = d;
140 }
141
142 return 1;
143}
144
fd63e712
LP
145static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
146
fbd0b64f 147 char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
fd63e712 148 const char *path2;
986a34a6 149 int r = 0, k;
fd63e712
LP
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);
986a34a6
ZJS
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;
fd63e712
LP
166 }
167
168 path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
986a34a6
ZJS
169 if (unlink(path2) < 0 && errno != ENOENT) {
170 k = -errno;
171 if (r == 0)
172 r = k;
fd63e712 173 }
986a34a6 174
fbd0b64f 175 if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
986a34a6
ZJS
176 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2);
177 if (r == 0)
178 r = k;
fd63e712
LP
179 }
180
181 return r;
182}
183
da50b85a
LP
184static 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
d7ef1257 190 * LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
da50b85a
LP
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;
29206d46
LP
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
da50b85a 212 unsigned n_tries = 100, current_suggested = 0;
29206d46
LP
213 int r;
214
29206d46
LP
215 (void) mkdir("/run/systemd/dynamic-uid", 0755);
216
217 for (;;) {
fbd0b64f 218 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
254d1313 219 _cleanup_close_ int lock_fd = -EBADF;
da50b85a 220 uid_t candidate;
29206d46
LP
221 ssize_t l;
222
223 if (--n_tries <= 0) /* Give up retrying eventually */
224 return -EBUSY;
225
da50b85a
LP
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:
04499a70 261 assert_not_reached();
da50b85a
LP
262 }
263
264 /* Make sure whatever we picked here actually is in the right range */
61755fda 265 if (!uid_is_dynamic(candidate))
da50b85a 266 continue;
29206d46
LP
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) {
3742095b 279 if (IN_SET(errno, EBUSY, EAGAIN))
29206d46
LP
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
629ff674 290 /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
29206d46
LP
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 */
98e4fcec
LP
296 if (getpwuid(candidate) ||
297 getgrgid((gid_t) candidate) ||
298 search_ipc(candidate, (gid_t) candidate) != 0) {
29206d46 299 (void) unlink(lock_path);
da50b85a 300 continue;
29206d46
LP
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]) {
ce16d177
YW
306 IOVEC_MAKE_STRING(name),
307 IOVEC_MAKE((char[1]) { '\n' }, 1),
29206d46
LP
308 }, 2, 0);
309 if (l < 0) {
e53c42ca 310 r = -errno;
29206d46 311 (void) unlink(lock_path);
e53c42ca 312 return r;
29206d46
LP
313 }
314
315 (void) ftruncate(lock_fd, l);
fd63e712 316 (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
29206d46
LP
317
318 *ret_uid = candidate;
c10d6bdb 319 return TAKE_FD(lock_fd);
29206d46
LP
320
321 next:
da50b85a 322 ;
29206d46
LP
323 }
324}
325
326static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
327 uid_t uid = UID_INVALID;
ce16d177 328 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
d34673ec 329 int lock_fd;
29206d46 330 ssize_t k;
29206d46
LP
331
332 assert(d);
333 assert(ret_uid);
334 assert(ret_lock_fd);
335
8b98cfb7
ZJS
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. */
29206d46 338
d34673ec 339 k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
29206d46 340 if (k < 0)
d34673ec 341 return (int) k;
29206d46
LP
342
343 *ret_uid = uid;
344 *ret_lock_fd = lock_fd;
345
346 return 0;
347}
348
349static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
ce16d177 350 struct iovec iov = IOVEC_MAKE(&uid, sizeof(uid));
29206d46
LP
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. */
d34673ec 355 return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
29206d46
LP
356}
357
fd63e712 358static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
fbd0b64f 359 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
29206d46
LP
360
361 if (lock_fd < 0)
362 return;
363
364 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
fd63e712
LP
365 (void) unlink(lock_path);
366
367 (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
29206d46
LP
368}
369
c2983a7f
ZJS
370static int dynamic_user_realize(
371 DynamicUser *d,
372 char **suggested_dirs,
373 uid_t *ret_uid, gid_t *ret_gid,
374 bool is_user) {
29206d46 375
254d1313
ZJS
376 _cleanup_close_ int uid_lock_fd = -EBADF;
377 _cleanup_close_ int etc_passwd_lock_fd = -EBADF;
c2983a7f
ZJS
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 */
460ec549 380 bool flush_cache = false;
29206d46
LP
381 int r;
382
383 assert(d);
c2983a7f
ZJS
384 assert(is_user == !!ret_uid);
385 assert(ret_gid);
29206d46
LP
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
1fe15cb7 390 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7
ZJS
391 if (r < 0)
392 return r;
29206d46 393
1fe15cb7 394 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
b4cbfa5f 395
c2983a7f 396 r = dynamic_user_pop(d, &num, &uid_lock_fd);
29206d46
LP
397 if (r < 0) {
398 int new_uid_lock_fd;
399 uid_t new_uid;
400
401 if (r != -EAGAIN)
362d90b7 402 return r;
29206d46
LP
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. */
1fe15cb7 406 r = posix_lock(d->storage_socket[0], LOCK_UN);
b4cbfa5f
DDM
407 if (r < 0)
408 return r;
29206d46
LP
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 */
c2983a7f 418 r = parse_uid(d->name, &num);
29206d46
LP
419 if (r < 0) {
420 struct passwd *p;
421 struct group *g;
422
9ec655cb
YW
423 if (is_user) {
424 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
425 p = getpwnam(d->name);
c2983a7f
ZJS
426 if (p) {
427 num = p->pw_uid;
428 gid = p->pw_gid;
429 } else {
9ec655cb
YW
430 /* if the user does not exist but the group with the same name exists, refuse operation */
431 g = getgrnam(d->name);
432 if (g)
433 return -EILSEQ;
434 }
435 } else {
436 /* Let's see if there's a group by this name */
437 g = getgrnam(d->name);
438 if (g)
c2983a7f 439 num = (uid_t) g->gr_gid;
9ec655cb
YW
440 else {
441 /* if the group does not exist but the user with the same name exists, refuse operation */
442 p = getpwnam(d->name);
443 if (p)
444 return -EILSEQ;
445 }
29206d46
LP
446 }
447 }
448
c2983a7f 449 if (num == UID_INVALID) {
29206d46
LP
450 /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
451
c2983a7f 452 uid_lock_fd = pick_uid(suggested_dirs, d->name, &num);
29206d46
LP
453 if (uid_lock_fd < 0)
454 return uid_lock_fd;
455 }
456
457 /* So, we found a working UID/lock combination. Let's see if we actually still need it. */
1fe15cb7 458 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7 459 if (r < 0) {
c2983a7f 460 unlink_uid_lock(uid_lock_fd, num, d->name);
362d90b7 461 return r;
29206d46
LP
462 }
463
464 r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd);
465 if (r < 0) {
466 if (r != -EAGAIN) {
467 /* OK, something bad happened, let's get rid of the bits we acquired. */
c2983a7f 468 unlink_uid_lock(uid_lock_fd, num, d->name);
362d90b7 469 return r;
29206d46
LP
470 }
471
472 /* Great! Nothing is stored here, still. Store our newly acquired data. */
460ec549 473 flush_cache = true;
29206d46
LP
474 } else {
475 /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
476 * acquired, and use what's stored now. */
477
c2983a7f 478 unlink_uid_lock(uid_lock_fd, num, d->name);
29206d46
LP
479 safe_close(uid_lock_fd);
480
c2983a7f 481 num = new_uid;
29206d46
LP
482 uid_lock_fd = new_uid_lock_fd;
483 }
25a1df7c
YW
484 } else if (is_user && !uid_is_dynamic(num)) {
485 struct passwd *p;
486
487 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
488 errno = 0;
489 p = getpwuid(num);
490 if (!p)
66855de7 491 return errno_or_else(ESRCH);
25a1df7c
YW
492
493 gid = p->pw_gid;
29206d46
LP
494 }
495
496 /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
497 * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
498 * dynamically right here, push that in along with the lock fd for it. */
c2983a7f 499 r = dynamic_user_push(d, num, uid_lock_fd);
29206d46 500 if (r < 0)
362d90b7 501 return r;
29206d46 502
460ec549
LP
503 if (flush_cache) {
504 /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
505 * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
506 * potential for nscd wanting to lock that for completing the invalidation. */
507 etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
508 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
509 }
510
c2983a7f
ZJS
511 if (is_user) {
512 *ret_uid = num;
513 *ret_gid = gid != GID_INVALID ? gid : num;
514 } else
515 *ret_gid = num;
516
362d90b7 517 return 0;
29206d46
LP
518}
519
f9bfa696 520int dynamic_user_current(DynamicUser *d, uid_t *ret) {
254d1313 521 _cleanup_close_ int lock_fd = -EBADF;
29206d46
LP
522 uid_t uid;
523 int r;
524
525 assert(d);
29206d46 526
8b98cfb7
ZJS
527 /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the
528 * storage socket, and pushes it back in right-away. */
29206d46 529
1fe15cb7 530 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7
ZJS
531 if (r < 0)
532 return r;
29206d46 533
1fe15cb7 534 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
b4cbfa5f 535
29206d46
LP
536 r = dynamic_user_pop(d, &uid, &lock_fd);
537 if (r < 0)
362d90b7 538 return r;
29206d46
LP
539
540 r = dynamic_user_push(d, uid, lock_fd);
541 if (r < 0)
362d90b7 542 return r;
29206d46 543
4bad7eed
LP
544 if (ret)
545 *ret = uid;
546
362d90b7 547 return 0;
29206d46
LP
548}
549
9da440b1 550static DynamicUser* dynamic_user_unref(DynamicUser *d) {
29206d46
LP
551 if (!d)
552 return NULL;
553
8b98cfb7
ZJS
554 /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully
555 * destroyed and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may
556 * contain entries with no references, which is commonly the case right before a daemon reload. */
29206d46
LP
557
558 assert(d->n_ref > 0);
559 d->n_ref--;
560
561 return NULL;
562}
563
564static int dynamic_user_close(DynamicUser *d) {
254d1313 565 _cleanup_close_ int lock_fd = -EBADF;
29206d46
LP
566 uid_t uid;
567 int r;
568
8b98cfb7
ZJS
569 /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the
570 * user is unrealized again, much like it was after it the DynamicUser object was first allocated. */
29206d46 571
1fe15cb7 572 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7
ZJS
573 if (r < 0)
574 return r;
29206d46 575
1fe15cb7 576 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
b4cbfa5f 577
29206d46 578 r = dynamic_user_pop(d, &uid, &lock_fd);
362d90b7 579 if (r == -EAGAIN)
29206d46 580 /* User wasn't realized yet, nothing to do. */
362d90b7 581 return 0;
29206d46 582 if (r < 0)
362d90b7 583 return r;
29206d46
LP
584
585 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
fd63e712 586 unlink_uid_lock(lock_fd, uid, d->name);
460ec549
LP
587
588 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
362d90b7 589 return 1;
29206d46
LP
590}
591
9da440b1 592static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
29206d46
LP
593 if (!d)
594 return NULL;
595
596 /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
597 * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
598 * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
599 * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
600
601 dynamic_user_unref(d);
602
603 if (d->n_ref > 0)
604 return NULL;
605
606 (void) dynamic_user_close(d);
607 return dynamic_user_free(d);
608}
609
154eb43f
LB
610int dynamic_user_serialize_one(DynamicUser *d, const char *key, FILE *f, FDSet *fds) {
611 int copy0, copy1;
29206d46 612
154eb43f 613 assert(key);
29206d46
LP
614 assert(f);
615 assert(fds);
616
154eb43f
LB
617 if (!d)
618 return 0;
29206d46 619
154eb43f
LB
620 if (d->storage_socket[0] < 0 || d->storage_socket[1] < 0)
621 return 0;
29206d46 622
154eb43f
LB
623 copy0 = fdset_put_dup(fds, d->storage_socket[0]);
624 if (copy0 < 0)
625 return log_error_errno(copy0, "Failed to add dynamic user storage fd to serialization: %m");
29206d46 626
154eb43f
LB
627 copy1 = fdset_put_dup(fds, d->storage_socket[1]);
628 if (copy1 < 0)
629 return log_error_errno(copy1, "Failed to add dynamic user storage fd to serialization: %m");
29206d46 630
154eb43f
LB
631 (void) serialize_item_format(f, key, "%s %i %i", d->name, copy0, copy1);
632
633 return 0;
634}
635
636int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
637 DynamicUser *d;
638
639 assert(m);
640
641 /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
642
643 HASHMAP_FOREACH(d, m->dynamic_users)
644 (void) dynamic_user_serialize_one(d, "dynamic-user", f, fds);
29206d46
LP
645
646 return 0;
647}
648
154eb43f 649void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds, DynamicUser **ret) {
29206d46 650 _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
dff9808a
LP
651 _cleanup_close_ int fd0 = -EBADF, fd1 = -EBADF;
652 int r;
29206d46 653
29206d46
LP
654 assert(value);
655 assert(fds);
656
657 /* Parse the serialization again, after a daemon reload */
658
659 r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL);
660 if (r != 3 || !isempty(value)) {
661 log_debug("Unable to parse dynamic user line.");
662 return;
663 }
664
dff9808a
LP
665 fd0 = deserialize_fd(fds, s0);
666 if (fd0 < 0)
29206d46 667 return;
29206d46 668
dff9808a
LP
669 fd1 = deserialize_fd(fds, s1);
670 if (fd1 < 0)
29206d46 671 return;
29206d46 672
154eb43f 673 r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, ret);
29206d46
LP
674 if (r < 0) {
675 log_debug_errno(r, "Failed to add dynamic user: %m");
676 return;
677 }
678
dff9808a
LP
679 TAKE_FD(fd0);
680 TAKE_FD(fd1);
154eb43f
LB
681
682 if (ret) /* If the caller uses it directly, increment the refcount */
683 (*ret)->n_ref++;
29206d46
LP
684}
685
686void dynamic_user_vacuum(Manager *m, bool close_user) {
687 DynamicUser *d;
29206d46
LP
688
689 assert(m);
690
691 /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
692 * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
693 * might not be referenced anymore. */
694
90e74a66 695 HASHMAP_FOREACH(d, m->dynamic_users) {
29206d46
LP
696 if (d->n_ref > 0)
697 continue;
698
699 if (close_user) {
700 log_debug("Removing orphaned dynamic user %s", d->name);
701 (void) dynamic_user_close(d);
702 }
703
704 dynamic_user_free(d);
705 }
706}
707
708int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
fbd0b64f 709 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
29206d46
LP
710 _cleanup_free_ char *user = NULL;
711 uid_t check_uid;
712 int r;
713
714 assert(m);
715 assert(ret);
716
61755fda
ZJS
717 /* A friendly way to translate a dynamic user's UID into a name. */
718 if (!uid_is_dynamic(uid))
29206d46
LP
719 return -ESRCH;
720
721 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
722 r = read_one_line_file(lock_path, &user);
a2176045 723 if (IN_SET(r, -ENOENT, 0))
29206d46
LP
724 return -ESRCH;
725 if (r < 0)
726 return r;
727
728 /* The lock file might be stale, hence let's verify the data before we return it */
729 r = dynamic_user_lookup_name(m, user, &check_uid);
730 if (r < 0)
731 return r;
732 if (check_uid != uid) /* lock file doesn't match our own idea */
733 return -ESRCH;
734
ae2a15bc 735 *ret = TAKE_PTR(user);
29206d46
LP
736
737 return 0;
738}
739
740int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
741 DynamicUser *d;
742 int r;
743
744 assert(m);
745 assert(name);
29206d46
LP
746
747 /* A friendly call for translating a dynamic user's name into its UID */
748
749 d = hashmap_get(m->dynamic_users, name);
750 if (!d)
751 return -ESRCH;
752
753 r = dynamic_user_current(d, ret);
754 if (r == -EAGAIN) /* not realized yet? */
755 return -ESRCH;
756
757 return r;
758}
759
15220772
DDM
760int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
761 _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
29206d46
LP
762 bool acquired = false;
763 int r;
764
29206d46 765 assert(m);
15220772
DDM
766 assert(ret);
767
768 if (!user && !group) {
769 *ret = NULL;
770 return 0;
771 }
772
773 creds = new0(DynamicCreds, 1);
774 if (!creds)
775 return -ENOMEM;
29206d46
LP
776
777 /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
778 * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
779 * and group. This call allocates a pair. */
780
15220772 781 if (user) {
29206d46
LP
782 r = dynamic_user_acquire(m, user, &creds->user);
783 if (r < 0)
784 return r;
785
786 acquired = true;
787 }
788
15220772
DDM
789 if (creds->user && (!group || streq_ptr(user, group)))
790 creds->group = dynamic_user_ref(creds->user);
791 else if (group) {
792 r = dynamic_user_acquire(m, group, &creds->group);
793 if (r < 0) {
794 if (acquired)
795 creds->user = dynamic_user_unref(creds->user);
796 return r;
29206d46
LP
797 }
798 }
799
15220772
DDM
800 *ret = TAKE_PTR(creds);
801
29206d46
LP
802 return 0;
803}
804
da50b85a 805int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
29206d46
LP
806 uid_t u = UID_INVALID;
807 gid_t g = GID_INVALID;
808 int r;
809
810 assert(creds);
811 assert(uid);
812 assert(gid);
813
814 /* Realize both the referenced user and group */
815
816 if (creds->user) {
c2983a7f 817 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true);
29206d46
LP
818 if (r < 0)
819 return r;
820 }
821
822 if (creds->group && creds->group != creds->user) {
c2983a7f 823 r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false);
29206d46
LP
824 if (r < 0)
825 return r;
c2983a7f 826 }
29206d46
LP
827
828 *uid = u;
829 *gid = g;
29206d46
LP
830 return 0;
831}
832
15220772
DDM
833DynamicCreds* dynamic_creds_unref(DynamicCreds *creds) {
834 if (!creds)
835 return NULL;
29206d46
LP
836
837 creds->user = dynamic_user_unref(creds->user);
838 creds->group = dynamic_user_unref(creds->group);
15220772
DDM
839
840 return mfree(creds);
29206d46
LP
841}
842
15220772
DDM
843DynamicCreds* dynamic_creds_destroy(DynamicCreds *creds) {
844 if (!creds)
845 return NULL;
29206d46
LP
846
847 creds->user = dynamic_user_destroy(creds->user);
848 creds->group = dynamic_user_destroy(creds->group);
15220772
DDM
849
850 return mfree(creds);
29206d46 851}
bb5232b6
LB
852
853void dynamic_creds_done(DynamicCreds *creds) {
854 if (!creds)
855 return;
856
857 if (creds->group != creds->user)
858 dynamic_user_free(creds->group);
859 creds->group = creds->user = dynamic_user_free(creds->user);
860}