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