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