]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dynamic-user.c
user-util: add get{pw,gr}{uid,gid,name}_malloc() helpers
[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"
8e1ac16b 23#include "uid-classification.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 */
75673cd8
LP
296 if (getpwuid_malloc(candidate, /* ret= */ NULL) >= 0 ||
297 getgrgid_malloc((gid_t) candidate, /* ret= */ NULL) >= 0 ||
98e4fcec 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 419 if (r < 0) {
75673cd8
LP
420 _cleanup_free_ struct passwd *p = NULL;
421 _cleanup_free_ struct group *g = NULL;
29206d46 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 */
75673cd8 425 if (getpwnam_malloc(d->name, &p) >= 0) {
c2983a7f
ZJS
426 num = p->pw_uid;
427 gid = p->pw_gid;
428 } else {
9ec655cb 429 /* if the user does not exist but the group with the same name exists, refuse operation */
75673cd8 430 if (getgrnam_malloc(d->name, /* ret= */ NULL) >= 0)
9ec655cb
YW
431 return -EILSEQ;
432 }
433 } else {
434 /* Let's see if there's a group by this name */
75673cd8 435 if (getgrnam_malloc(d->name, &g) >= 0)
c2983a7f 436 num = (uid_t) g->gr_gid;
9ec655cb
YW
437 else {
438 /* if the group does not exist but the user with the same name exists, refuse operation */
75673cd8 439 if (getpwnam_malloc(d->name, /* ret= */ NULL) >= 0)
9ec655cb
YW
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 480 } else if (is_user && !uid_is_dynamic(num)) {
75673cd8 481 _cleanup_free_ struct passwd *p = NULL;
25a1df7c
YW
482
483 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
75673cd8
LP
484 r = getpwuid_malloc(num, &p);
485 if (r < 0)
486 return r;
25a1df7c
YW
487
488 gid = p->pw_gid;
29206d46
LP
489 }
490
491 /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
492 * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
493 * dynamically right here, push that in along with the lock fd for it. */
c2983a7f 494 r = dynamic_user_push(d, num, uid_lock_fd);
29206d46 495 if (r < 0)
362d90b7 496 return r;
29206d46 497
460ec549
LP
498 if (flush_cache) {
499 /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
500 * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
501 * potential for nscd wanting to lock that for completing the invalidation. */
502 etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
503 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
504 }
505
c2983a7f
ZJS
506 if (is_user) {
507 *ret_uid = num;
508 *ret_gid = gid != GID_INVALID ? gid : num;
509 } else
510 *ret_gid = num;
511
362d90b7 512 return 0;
29206d46
LP
513}
514
f9bfa696 515int dynamic_user_current(DynamicUser *d, uid_t *ret) {
254d1313 516 _cleanup_close_ int lock_fd = -EBADF;
29206d46
LP
517 uid_t uid;
518 int r;
519
520 assert(d);
29206d46 521
8b98cfb7
ZJS
522 /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the
523 * storage socket, and pushes it back in right-away. */
29206d46 524
1fe15cb7 525 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7
ZJS
526 if (r < 0)
527 return r;
29206d46 528
1fe15cb7 529 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
b4cbfa5f 530
29206d46
LP
531 r = dynamic_user_pop(d, &uid, &lock_fd);
532 if (r < 0)
362d90b7 533 return r;
29206d46
LP
534
535 r = dynamic_user_push(d, uid, lock_fd);
536 if (r < 0)
362d90b7 537 return r;
29206d46 538
4bad7eed
LP
539 if (ret)
540 *ret = uid;
541
362d90b7 542 return 0;
29206d46
LP
543}
544
9da440b1 545static DynamicUser* dynamic_user_unref(DynamicUser *d) {
29206d46
LP
546 if (!d)
547 return NULL;
548
8b98cfb7
ZJS
549 /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully
550 * destroyed and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may
551 * contain entries with no references, which is commonly the case right before a daemon reload. */
29206d46
LP
552
553 assert(d->n_ref > 0);
554 d->n_ref--;
555
556 return NULL;
557}
558
559static int dynamic_user_close(DynamicUser *d) {
254d1313 560 _cleanup_close_ int lock_fd = -EBADF;
29206d46
LP
561 uid_t uid;
562 int r;
563
8b98cfb7
ZJS
564 /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the
565 * user is unrealized again, much like it was after it the DynamicUser object was first allocated. */
29206d46 566
1fe15cb7 567 r = posix_lock(d->storage_socket[0], LOCK_EX);
362d90b7
ZJS
568 if (r < 0)
569 return r;
29206d46 570
1fe15cb7 571 CLEANUP_POSIX_UNLOCK(d->storage_socket[0]);
b4cbfa5f 572
29206d46 573 r = dynamic_user_pop(d, &uid, &lock_fd);
362d90b7 574 if (r == -EAGAIN)
29206d46 575 /* User wasn't realized yet, nothing to do. */
362d90b7 576 return 0;
29206d46 577 if (r < 0)
362d90b7 578 return r;
29206d46
LP
579
580 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
fd63e712 581 unlink_uid_lock(lock_fd, uid, d->name);
460ec549
LP
582
583 (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
362d90b7 584 return 1;
29206d46
LP
585}
586
9da440b1 587static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
29206d46
LP
588 if (!d)
589 return NULL;
590
591 /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
592 * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
593 * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
594 * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
595
596 dynamic_user_unref(d);
597
598 if (d->n_ref > 0)
599 return NULL;
600
601 (void) dynamic_user_close(d);
602 return dynamic_user_free(d);
603}
604
154eb43f
LB
605int dynamic_user_serialize_one(DynamicUser *d, const char *key, FILE *f, FDSet *fds) {
606 int copy0, copy1;
29206d46 607
154eb43f 608 assert(key);
29206d46
LP
609 assert(f);
610 assert(fds);
611
154eb43f
LB
612 if (!d)
613 return 0;
29206d46 614
154eb43f
LB
615 if (d->storage_socket[0] < 0 || d->storage_socket[1] < 0)
616 return 0;
29206d46 617
154eb43f
LB
618 copy0 = fdset_put_dup(fds, d->storage_socket[0]);
619 if (copy0 < 0)
620 return log_error_errno(copy0, "Failed to add dynamic user storage fd to serialization: %m");
29206d46 621
154eb43f
LB
622 copy1 = fdset_put_dup(fds, d->storage_socket[1]);
623 if (copy1 < 0)
624 return log_error_errno(copy1, "Failed to add dynamic user storage fd to serialization: %m");
29206d46 625
154eb43f
LB
626 (void) serialize_item_format(f, key, "%s %i %i", d->name, copy0, copy1);
627
628 return 0;
629}
630
631int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
632 DynamicUser *d;
633
634 assert(m);
635
636 /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
637
638 HASHMAP_FOREACH(d, m->dynamic_users)
639 (void) dynamic_user_serialize_one(d, "dynamic-user", f, fds);
29206d46
LP
640
641 return 0;
642}
643
154eb43f 644void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds, DynamicUser **ret) {
29206d46 645 _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
dff9808a
LP
646 _cleanup_close_ int fd0 = -EBADF, fd1 = -EBADF;
647 int r;
29206d46 648
29206d46
LP
649 assert(value);
650 assert(fds);
651
652 /* Parse the serialization again, after a daemon reload */
653
654 r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL);
655 if (r != 3 || !isempty(value)) {
656 log_debug("Unable to parse dynamic user line.");
657 return;
658 }
659
dff9808a
LP
660 fd0 = deserialize_fd(fds, s0);
661 if (fd0 < 0)
29206d46 662 return;
29206d46 663
dff9808a
LP
664 fd1 = deserialize_fd(fds, s1);
665 if (fd1 < 0)
29206d46 666 return;
29206d46 667
154eb43f 668 r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, ret);
29206d46
LP
669 if (r < 0) {
670 log_debug_errno(r, "Failed to add dynamic user: %m");
671 return;
672 }
673
dff9808a
LP
674 TAKE_FD(fd0);
675 TAKE_FD(fd1);
154eb43f
LB
676
677 if (ret) /* If the caller uses it directly, increment the refcount */
678 (*ret)->n_ref++;
29206d46
LP
679}
680
681void dynamic_user_vacuum(Manager *m, bool close_user) {
682 DynamicUser *d;
29206d46
LP
683
684 assert(m);
685
686 /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
687 * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
688 * might not be referenced anymore. */
689
90e74a66 690 HASHMAP_FOREACH(d, m->dynamic_users) {
29206d46
LP
691 if (d->n_ref > 0)
692 continue;
693
694 if (close_user) {
695 log_debug("Removing orphaned dynamic user %s", d->name);
696 (void) dynamic_user_close(d);
697 }
698
699 dynamic_user_free(d);
700 }
701}
702
703int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
fbd0b64f 704 char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
29206d46
LP
705 _cleanup_free_ char *user = NULL;
706 uid_t check_uid;
707 int r;
708
709 assert(m);
710 assert(ret);
711
61755fda
ZJS
712 /* A friendly way to translate a dynamic user's UID into a name. */
713 if (!uid_is_dynamic(uid))
29206d46
LP
714 return -ESRCH;
715
716 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
717 r = read_one_line_file(lock_path, &user);
a2176045 718 if (IN_SET(r, -ENOENT, 0))
29206d46
LP
719 return -ESRCH;
720 if (r < 0)
721 return r;
722
723 /* The lock file might be stale, hence let's verify the data before we return it */
724 r = dynamic_user_lookup_name(m, user, &check_uid);
725 if (r < 0)
726 return r;
727 if (check_uid != uid) /* lock file doesn't match our own idea */
728 return -ESRCH;
729
ae2a15bc 730 *ret = TAKE_PTR(user);
29206d46
LP
731
732 return 0;
733}
734
735int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
736 DynamicUser *d;
737 int r;
738
739 assert(m);
740 assert(name);
29206d46
LP
741
742 /* A friendly call for translating a dynamic user's name into its UID */
743
744 d = hashmap_get(m->dynamic_users, name);
745 if (!d)
746 return -ESRCH;
747
748 r = dynamic_user_current(d, ret);
749 if (r == -EAGAIN) /* not realized yet? */
750 return -ESRCH;
751
752 return r;
753}
754
15220772
DDM
755int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
756 _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
29206d46
LP
757 bool acquired = false;
758 int r;
759
29206d46 760 assert(m);
15220772
DDM
761 assert(ret);
762
763 if (!user && !group) {
764 *ret = NULL;
765 return 0;
766 }
767
768 creds = new0(DynamicCreds, 1);
769 if (!creds)
770 return -ENOMEM;
29206d46
LP
771
772 /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
773 * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
774 * and group. This call allocates a pair. */
775
15220772 776 if (user) {
29206d46
LP
777 r = dynamic_user_acquire(m, user, &creds->user);
778 if (r < 0)
779 return r;
780
781 acquired = true;
782 }
783
15220772
DDM
784 if (creds->user && (!group || streq_ptr(user, group)))
785 creds->group = dynamic_user_ref(creds->user);
786 else if (group) {
787 r = dynamic_user_acquire(m, group, &creds->group);
788 if (r < 0) {
789 if (acquired)
790 creds->user = dynamic_user_unref(creds->user);
791 return r;
29206d46
LP
792 }
793 }
794
15220772
DDM
795 *ret = TAKE_PTR(creds);
796
29206d46
LP
797 return 0;
798}
799
da50b85a 800int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
29206d46
LP
801 uid_t u = UID_INVALID;
802 gid_t g = GID_INVALID;
803 int r;
804
805 assert(creds);
806 assert(uid);
807 assert(gid);
808
809 /* Realize both the referenced user and group */
810
811 if (creds->user) {
c2983a7f 812 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true);
29206d46
LP
813 if (r < 0)
814 return r;
815 }
816
817 if (creds->group && creds->group != creds->user) {
c2983a7f 818 r = dynamic_user_realize(creds->group, suggested_paths, NULL, &g, false);
29206d46
LP
819 if (r < 0)
820 return r;
c2983a7f 821 }
29206d46
LP
822
823 *uid = u;
824 *gid = g;
29206d46
LP
825 return 0;
826}
827
15220772
DDM
828DynamicCreds* dynamic_creds_unref(DynamicCreds *creds) {
829 if (!creds)
830 return NULL;
29206d46
LP
831
832 creds->user = dynamic_user_unref(creds->user);
833 creds->group = dynamic_user_unref(creds->group);
15220772
DDM
834
835 return mfree(creds);
29206d46
LP
836}
837
15220772
DDM
838DynamicCreds* dynamic_creds_destroy(DynamicCreds *creds) {
839 if (!creds)
840 return NULL;
29206d46
LP
841
842 creds->user = dynamic_user_destroy(creds->user);
843 creds->group = dynamic_user_destroy(creds->group);
15220772
DDM
844
845 return mfree(creds);
29206d46 846}
bb5232b6
LB
847
848void dynamic_creds_done(DynamicCreds *creds) {
849 if (!creds)
850 return;
851
852 if (creds->group != creds->user)
853 dynamic_user_free(creds->group);
854 creds->group = creds->user = dynamic_user_free(creds->user);
855}
7b6d3dcd
LB
856
857void dynamic_creds_close(DynamicCreds *creds) {
858 if (!creds)
859 return;
860
861 if (creds->user)
862 safe_close_pair(creds->user->storage_socket);
863
864 if (creds->group && creds->group != creds->user)
865 safe_close_pair(creds->group->storage_socket);
866}