Bug Summary

File:build-scan/../src/core/dynamic-user.c
Warning:line 278, column 25
Value stored to 'lock_fd' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name dynamic-user.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/core/libcore.a.p -I src/core -I ../src/core -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/libmount -I /usr/include/blkid -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/core/dynamic-user.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <grp.h>
4#include <pwd.h>
5#include <sys/file.h>
6
7#include "clean-ipc.h"
8#include "dynamic-user.h"
9#include "fd-util.h"
10#include "fileio.h"
11#include "fs-util.h"
12#include "io-util.h"
13#include "parse-util.h"
14#include "random-util.h"
15#include "socket-util.h"
16#include "stdio-util.h"
17#include "string-util.h"
18#include "user-util.h"
19
20/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
21#define UID_CLAMP_INTO_RANGE(rnd)(((uid_t) (rnd) % (65519 - 61184 + 1)) + 61184) (((uid_t) (rnd) % (DYNAMIC_UID_MAX65519 - DYNAMIC_UID_MIN61184 + 1)) + DYNAMIC_UID_MIN61184)
22
23static DynamicUser* dynamic_user_free(DynamicUser *d) {
24 if (!d)
25 return NULL((void*)0);
26
27 if (d->manager)
28 (void) hashmap_remove(d->manager->dynamic_users, d->name);
29
30 safe_close_pair(d->storage_socket);
31 return mfree(d);
32}
33
34static int dynamic_user_add(Manager *m, const char *name, int storage_socket[2], DynamicUser **ret) {
35 DynamicUser *d = NULL((void*)0);
36 int r;
37
38 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 38, __PRETTY_FUNCTION__
); } while (0)
;
39 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/core/dynamic-user.c", 39
, __PRETTY_FUNCTION__); } while (0)
;
40 assert(storage_socket)do { if ((__builtin_expect(!!(!(storage_socket)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("storage_socket"), "../src/core/dynamic-user.c"
, 40, __PRETTY_FUNCTION__); } while (0)
;
41
42 r = hashmap_ensure_allocated(&m->dynamic_users, &string_hash_ops)internal_hashmap_ensure_allocated(&m->dynamic_users, &
string_hash_ops )
;
43 if (r < 0)
44 return r;
45
46 d = malloc0(offsetof(DynamicUser, name) + strlen(name) + 1)(calloc(1, (__builtin_offsetof(DynamicUser, name) + strlen(name
) + 1)))
;
47 if (!d)
48 return -ENOMEM12;
49
50 strcpy(d->name, name);
51
52 d->storage_socket[0] = storage_socket[0];
53 d->storage_socket[1] = storage_socket[1];
54
55 r = hashmap_put(m->dynamic_users, d->name, d);
56 if (r < 0) {
57 free(d);
58 return r;
59 }
60
61 d->manager = m;
62
63 if (ret)
64 *ret = d;
65
66 return 0;
67}
68
69static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
70 _cleanup_close_pair___attribute__((cleanup(close_pairp))) int storage_socket[2] = { -1, -1 };
71 DynamicUser *d;
72 int r;
73
74 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 74, __PRETTY_FUNCTION__
); } while (0)
;
75 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/core/dynamic-user.c", 75
, __PRETTY_FUNCTION__); } while (0)
;
76
77 /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for
78 * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really
79 * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do
80 * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous
81 * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the
82 * allocated UID number, plus an fd referencing the lock file for the UID
83 * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can
84 * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair
85 * may exist in three different states:
86 *
87 * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized.
88 *
89 * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a
90 * statically assigned UID by the same name, which we are reusing.
91 *
92 * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic
93 * UID and locked it in the file system, using the lock fd.
94 *
95 * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it
96 * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here:
97 * 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
98 * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX
99 * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous,
100 * nobody else could get any access to it except via our own fd) and we want to synchronize access between all
101 * processes that have access to it. */
102
103 d = hashmap_get(m->dynamic_users, name);
104 if (d) {
105 /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
106 d->n_ref++;
107 *ret = d;
108 return 0;
109 }
110
111 if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC))
112 return -EINVAL22;
113
114 if (socketpair(AF_UNIX1, SOCK_DGRAMSOCK_DGRAM|SOCK_CLOEXECSOCK_CLOEXEC, 0, storage_socket) < 0)
115 return -errno(*__errno_location ());
116
117 r = dynamic_user_add(m, name, storage_socket, &d);
118 if (r < 0)
119 return r;
120
121 storage_socket[0] = storage_socket[1] = -1;
122
123 if (ret) {
124 d->n_ref++;
125 *ret = d;
126 }
127
128 return 1;
129}
130
131static int make_uid_symlinks(uid_t uid, const char *name, bool_Bool b) {
132
133 char path1[STRLEN("/run/systemd/dynamic-uid/direct:")(sizeof("""/run/systemd/dynamic-uid/direct:""") - 1) + DECIMAL_STR_MAX(uid_t)(2+(sizeof(uid_t) <= 1 ? 3 : sizeof(uid_t) <= 2 ? 5 : sizeof
(uid_t) <= 4 ? 10 : sizeof(uid_t) <= 8 ? 20 : sizeof(int
[-2*(sizeof(uid_t) > 8)])))
+ 1];
134 const char *path2;
135 int r = 0, k;
136
137 /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
138 * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
139 * would be its own client then). We hence keep these world-readable symlinks in place, so that the
140 * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
141 * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
142 * on them and as those may be taken by any user with read access we can't make them world-readable. */
143
144 xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid)do { if ((__builtin_expect(!!(!(((size_t) snprintf(path1, __extension__
(__builtin_choose_expr( !__builtin_types_compatible_p(typeof
(path1), typeof(&*(path1))), sizeof(path1)/sizeof((path1)
[0]), ((void)0))), "/run/systemd/dynamic-uid/direct:" "%" "u"
, uid) < (__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(path1), typeof(&*(path1))), sizeof(path1)/sizeof(
(path1)[0]), ((void)0))))))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("xsprintf: " "path1" "[] must be big enough"), "../src/core/dynamic-user.c"
, 144, __PRETTY_FUNCTION__); } while (0)
;
145 if (unlink(path1) < 0 && errno(*__errno_location ()) != ENOENT2)
146 r = -errno(*__errno_location ());
147
148 if (b && symlink(name, path1) < 0) {
149 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1)({ int _level = ((4)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/core/dynamic-user.c", 149, __func__
, "Failed to symlink \"%s\": %m", path1) : -abs(_e); })
;
150 if (r == 0)
151 r = k;
152 }
153
154 path2 = strjoina("/run/systemd/dynamic-uid/direct:", name)({ const char *_appendees_[] = { "/run/systemd/dynamic-uid/direct:"
, name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (
_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_
)/sizeof((_appendees_)[0]), ((void)0))) && _appendees_
[_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca
(_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(_appendees_), typeof(&
*(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0]
), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy
(_p_, _appendees_[_i_]); *_p_ = 0; _d_; })
;
155 if (unlink(path2) < 0 && errno(*__errno_location ()) != ENOENT2) {
156 k = -errno(*__errno_location ());
157 if (r == 0)
158 r = k;
159 }
160
161 if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:")(sizeof("""/run/systemd/dynamic-uid/direct:""") - 1), path2) < 0) {
162 k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2)({ int _level = ((4)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/core/dynamic-user.c", 162, __func__
, "Failed to symlink \"%s\": %m", path2) : -abs(_e); })
;
163 if (r == 0)
164 r = k;
165 }
166
167 return r;
168}
169
170static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
171
172 /* Find a suitable free UID. We use the following strategy to find a suitable UID:
173 *
174 * 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
175 * them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
176 * LogDirectory= are used, as reusing the UID these directories are owned by saves us from having to
177 * recursively chown() them to new users.
178 *
179 * 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
180 * pretty good, as the use ris by default derived from the unit name, and hence the same service and same
181 * user should usually get the same UID as long as our hashing doesn't clash.
182 *
183 * 3. Finally, if that didn't work, we randomly pick UIDs, until we find one that is empty.
184 *
185 * Since the dynamic UID space is relatively small we'll stop trying after 100 iterations, giving up. */
186
187 enum {
188 PHASE_SUGGESTED, /* the first phase, reusing directory ownership UIDs */
189 PHASE_HASHED, /* the second phase, deriving a UID from the username by hashing */
190 PHASE_RANDOM, /* the last phase, randomly picking UIDs */
191 } phase = PHASE_SUGGESTED;
192
193 static const uint8_t hash_key[] = {
194 0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5,
195 0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59
196 };
197
198 unsigned n_tries = 100, current_suggested = 0;
199 int r;
200
201 (void) mkdir("/run/systemd/dynamic-uid", 0755);
202
203 for (;;) {
204 char lock_path[STRLEN("/run/systemd/dynamic-uid/")(sizeof("""/run/systemd/dynamic-uid/""") - 1) + DECIMAL_STR_MAX(uid_t)(2+(sizeof(uid_t) <= 1 ? 3 : sizeof(uid_t) <= 2 ? 5 : sizeof
(uid_t) <= 4 ? 10 : sizeof(uid_t) <= 8 ? 20 : sizeof(int
[-2*(sizeof(uid_t) > 8)])))
+ 1];
205 _cleanup_close___attribute__((cleanup(closep))) int lock_fd = -1;
206 uid_t candidate;
207 ssize_t l;
208
209 if (--n_tries <= 0) /* Give up retrying eventually */
210 return -EBUSY16;
211
212 switch (phase) {
213
214 case PHASE_SUGGESTED: {
215 struct stat st;
216
217 if (!suggested_paths || !suggested_paths[current_suggested]) {
218 /* We reached the end of the suggested paths list, let's try by hashing the name */
219 phase = PHASE_HASHED;
220 continue;
221 }
222
223 if (stat(suggested_paths[current_suggested++], &st) < 0)
224 continue; /* We can't read the UID of this path, but that doesn't matter, just try the next */
225
226 candidate = st.st_uid;
227 break;
228 }
229
230 case PHASE_HASHED:
231 /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We
232 * start with a UID generated as hash from the user name. */
233 candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key))(((uid_t) (siphash24(name, strlen(name), hash_key)) % (65519 -
61184 + 1)) + 61184)
;
234
235 /* If this one fails, we should proceed with random tries */
236 phase = PHASE_RANDOM;
237 break;
238
239 case PHASE_RANDOM:
240
241 /* Pick another random UID, and see if that works for us. */
242 random_bytes(&candidate, sizeof(candidate));
243 candidate = UID_CLAMP_INTO_RANGE(candidate)(((uid_t) (candidate) % (65519 - 61184 + 1)) + 61184);
244 break;
245
246 default:
247 assert_not_reached("unknown phase")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"unknown phase"), "../src/core/dynamic-user.c", 247, __PRETTY_FUNCTION__
); } while (0)
;
248 }
249
250 /* Make sure whatever we picked here actually is in the right range */
251 if (!uid_is_dynamic(candidate))
252 continue;
253
254 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate)do { if ((__builtin_expect(!!(!(((size_t) snprintf(lock_path,
__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(lock_path), typeof(&*(lock_path))), sizeof(lock_path
)/sizeof((lock_path)[0]), ((void)0))), "/run/systemd/dynamic-uid/"
"%" "u", candidate) < (__extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(lock_path), typeof(&
*(lock_path))), sizeof(lock_path)/sizeof((lock_path)[0]), ((void
)0))))))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("xsprintf: "
"lock_path" "[] must be big enough"), "../src/core/dynamic-user.c"
, 254, __PRETTY_FUNCTION__); } while (0)
;
255
256 for (;;) {
257 struct stat st;
258
259 lock_fd = open(lock_path, O_CREAT0100|O_RDWR02|O_NOFOLLOW0400000|O_CLOEXEC02000000|O_NOCTTY0400, 0600);
260 if (lock_fd < 0)
261 return -errno(*__errno_location ());
262
263 r = flock(lock_fd, LOCK_EX2|LOCK_NB4); /* Try to get a BSD file lock on the UID lock file */
264 if (r < 0) {
265 if (IN_SET(errno, EBUSY, EAGAIN)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){16, 11})/sizeof(int)]; switch((*__errno_location
())) { case 16: case 11: _found = 1; break; default: break; }
_found; })
)
266 goto next; /* already in use */
267
268 return -errno(*__errno_location ());
269 }
270
271 if (fstat(lock_fd, &st) < 0)
272 return -errno(*__errno_location ());
273 if (st.st_nlink > 0)
274 break;
275
276 /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
277 * got the lock. Close it, and try again. */
278 lock_fd = safe_close(lock_fd);
Value stored to 'lock_fd' is never read
279 }
280
281 /* Some superficial check whether this UID/GID might already be taken by some static user */
282 if (getpwuid(candidate) ||
283 getgrgid((gid_t) candidate) ||
284 search_ipc(candidate, (gid_t) candidate) != 0) {
285 (void) unlink(lock_path);
286 continue;
287 }
288
289 /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
290 l = pwritev(lock_fd,
291 (struct iovec[2]) {
292 IOVEC_INIT_STRING(name){ .iov_base = ((char*) name), .iov_len = (strlen(name)) },
293 IOVEC_INIT((char[1]) { '\n' }, 1){ .iov_base = ((char[1]) { '\n' }), .iov_len = (1) },
294 }, 2, 0);
295 if (l < 0) {
296 r = -errno(*__errno_location ());
297 (void) unlink(lock_path);
298 return r;
299 }
300
301 (void) ftruncate(lock_fd, l);
302 (void) make_uid_symlinks(candidate, name, true1); /* also add direct lookup symlinks */
303
304 *ret_uid = candidate;
305 return TAKE_FD(lock_fd)({ int _fd_ = (lock_fd); (lock_fd) = -1; _fd_; });
306
307 next:
308 ;
309 }
310}
311
312static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
313 uid_t uid = UID_INVALID((uid_t) -1);
314 struct iovec iov = IOVEC_INIT(&uid, sizeof(uid)){ .iov_base = (&uid), .iov_len = (sizeof(uid)) };
315 int lock_fd;
316 ssize_t k;
317
318 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/core/dynamic-user.c", 318,
__PRETTY_FUNCTION__); } while (0)
;
319 assert(ret_uid)do { if ((__builtin_expect(!!(!(ret_uid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret_uid"), "../src/core/dynamic-user.c"
, 319, __PRETTY_FUNCTION__); } while (0)
;
320 assert(ret_lock_fd)do { if ((__builtin_expect(!!(!(ret_lock_fd)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret_lock_fd"), "../src/core/dynamic-user.c"
, 320, __PRETTY_FUNCTION__); } while (0)
;
321
322 /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
323 * on the socket taken. */
324
325 k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAITMSG_DONTWAIT, &lock_fd);
326 if (k < 0)
327 return (int) k;
328
329 *ret_uid = uid;
330 *ret_lock_fd = lock_fd;
331
332 return 0;
333}
334
335static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
336 struct iovec iov = IOVEC_INIT(&uid, sizeof(uid)){ .iov_base = (&uid), .iov_len = (sizeof(uid)) };
337
338 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/core/dynamic-user.c", 338,
__PRETTY_FUNCTION__); } while (0)
;
339
340 /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
341 return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT)send_one_fd_iov_sa(d->storage_socket[1], lock_fd, &iov
, 1, ((void*)0), 0, MSG_DONTWAIT)
;
342}
343
344static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
345 char lock_path[STRLEN("/run/systemd/dynamic-uid/")(sizeof("""/run/systemd/dynamic-uid/""") - 1) + DECIMAL_STR_MAX(uid_t)(2+(sizeof(uid_t) <= 1 ? 3 : sizeof(uid_t) <= 2 ? 5 : sizeof
(uid_t) <= 4 ? 10 : sizeof(uid_t) <= 8 ? 20 : sizeof(int
[-2*(sizeof(uid_t) > 8)])))
+ 1];
346
347 if (lock_fd < 0)
348 return;
349
350 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid)do { if ((__builtin_expect(!!(!(((size_t) snprintf(lock_path,
__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(lock_path), typeof(&*(lock_path))), sizeof(lock_path
)/sizeof((lock_path)[0]), ((void)0))), "/run/systemd/dynamic-uid/"
"%" "u", uid) < (__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(lock_path), typeof(&*(lock_path))), sizeof(lock_path
)/sizeof((lock_path)[0]), ((void)0))))))),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("xsprintf: " "lock_path" "[] must be big enough"
), "../src/core/dynamic-user.c", 350, __PRETTY_FUNCTION__); }
while (0)
;
351 (void) unlink(lock_path);
352
353 (void) make_uid_symlinks(uid, name, false0); /* remove direct lookup symlinks */
354}
355
356static int lockfp(int fd, int *fd_lock) {
357 if (lockf(fd, F_LOCK1, 0) < 0)
358 return -errno(*__errno_location ());
359 *fd_lock = fd;
360 return 0;
361}
362
363static void unlockfp(int *fd_lock) {
364 if (*fd_lock < 0)
365 return;
366 lockf(*fd_lock, F_ULOCK0, 0);
367 *fd_lock = -1;
368}
369
370static int dynamic_user_realize(
371 DynamicUser *d,
372 char **suggested_dirs,
373 uid_t *ret_uid, gid_t *ret_gid,
374 bool_Bool is_user) {
375
376 _cleanup_(unlockfp)__attribute__((cleanup(unlockfp))) int storage_socket0_lock = -1;
377 _cleanup_close___attribute__((cleanup(closep))) int uid_lock_fd = -1;
378 _cleanup_close___attribute__((cleanup(closep))) int etc_passwd_lock_fd = -1;
379 uid_t num = UID_INVALID((uid_t) -1); /* a uid if is_user, and a gid otherwise */
380 gid_t gid = GID_INVALID((gid_t) -1); /* a gid if is_user, ignored otherwise */
381 int r;
382
383 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/core/dynamic-user.c", 383,
__PRETTY_FUNCTION__); } while (0)
;
384 assert(is_user == !!ret_uid)do { if ((__builtin_expect(!!(!(is_user == !!ret_uid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("is_user == !!ret_uid"), "../src/core/dynamic-user.c"
, 384, __PRETTY_FUNCTION__); } while (0)
;
385 assert(ret_gid)do { if ((__builtin_expect(!!(!(ret_gid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret_gid"), "../src/core/dynamic-user.c"
, 385, __PRETTY_FUNCTION__); } while (0)
;
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
390 r = lockfp(d->storage_socket[0], &storage_socket0_lock);
391 if (r < 0)
392 return r;
393
394 r = dynamic_user_pop(d, &num, &uid_lock_fd);
395 if (r < 0) {
396 int new_uid_lock_fd;
397 uid_t new_uid;
398
399 if (r != -EAGAIN11)
400 return r;
401
402 /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the
403 * lock however, so that nobody else blocks on our NSS lookups. */
404 unlockfp(&storage_socket0_lock);
405
406 /* Let's see if a proper, static user or group by this name exists. Try to take the lock on
407 * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't
408 * take the lock, given that users can't be added there anyway in this case. */
409 etc_passwd_lock_fd = take_etc_passwd_lock(NULL((void*)0));
410 if (etc_passwd_lock_fd < 0 && etc_passwd_lock_fd != -EROFS30)
411 return etc_passwd_lock_fd;
412
413 /* First, let's parse this as numeric UID */
414 r = parse_uid(d->name, &num);
415 if (r < 0) {
416 struct passwd *p;
417 struct group *g;
418
419 if (is_user) {
420 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
421 p = getpwnam(d->name);
422 if (p) {
423 num = p->pw_uid;
424 gid = p->pw_gid;
425 } else {
426 /* if the user does not exist but the group with the same name exists, refuse operation */
427 g = getgrnam(d->name);
428 if (g)
429 return -EILSEQ84;
430 }
431 } else {
432 /* Let's see if there's a group by this name */
433 g = getgrnam(d->name);
434 if (g)
435 num = (uid_t) g->gr_gid;
436 else {
437 /* if the group does not exist but the user with the same name exists, refuse operation */
438 p = getpwnam(d->name);
439 if (p)
440 return -EILSEQ84;
441 }
442 }
443 }
444
445 if (num == UID_INVALID((uid_t) -1)) {
446 /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
447
448 uid_lock_fd = pick_uid(suggested_dirs, d->name, &num);
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. */
454 r = lockfp(d->storage_socket[0], &storage_socket0_lock);
455 if (r < 0) {
456 unlink_uid_lock(uid_lock_fd, num, d->name);
457 return r;
458 }
459
460 r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd);
461 if (r < 0) {
462 if (r != -EAGAIN11) {
463 /* OK, something bad happened, let's get rid of the bits we acquired. */
464 unlink_uid_lock(uid_lock_fd, num, d->name);
465 return r;
466 }
467
468 /* Great! Nothing is stored here, still. Store our newly acquired data. */
469 } else {
470 /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
471 * acquired, and use what's stored now. */
472
473 unlink_uid_lock(uid_lock_fd, num, d->name);
474 safe_close(uid_lock_fd);
475
476 num = new_uid;
477 uid_lock_fd = new_uid_lock_fd;
478 }
479 }
480
481 /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
482 * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
483 * dynamically right here, push that in along with the lock fd for it. */
484 r = dynamic_user_push(d, num, uid_lock_fd);
485 if (r < 0)
486 return r;
487
488 if (is_user) {
489 *ret_uid = num;
490 *ret_gid = gid != GID_INVALID((gid_t) -1) ? gid : num;
491 } else
492 *ret_gid = num;
493
494 return 0;
495}
496
497int dynamic_user_current(DynamicUser *d, uid_t *ret) {
498 _cleanup_(unlockfp)__attribute__((cleanup(unlockfp))) int storage_socket0_lock = -1;
499 _cleanup_close___attribute__((cleanup(closep))) int lock_fd = -1;
500 uid_t uid;
501 int r;
502
503 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/core/dynamic-user.c", 503,
__PRETTY_FUNCTION__); } while (0)
;
504 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/core/dynamic-user.c", 504
, __PRETTY_FUNCTION__); } while (0)
;
505
506 /* 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. */
507
508 r = lockfp(d->storage_socket[0], &storage_socket0_lock);
509 if (r < 0)
510 return r;
511
512 r = dynamic_user_pop(d, &uid, &lock_fd);
513 if (r < 0)
514 return r;
515
516 r = dynamic_user_push(d, uid, lock_fd);
517 if (r < 0)
518 return r;
519
520 *ret = uid;
521 return 0;
522}
523
524static DynamicUser* dynamic_user_ref(DynamicUser *d) {
525 if (!d)
526 return NULL((void*)0);
527
528 assert(d->n_ref > 0)do { if ((__builtin_expect(!!(!(d->n_ref > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d->n_ref > 0"), "../src/core/dynamic-user.c"
, 528, __PRETTY_FUNCTION__); } while (0)
;
529 d->n_ref++;
530
531 return d;
532}
533
534static DynamicUser* dynamic_user_unref(DynamicUser *d) {
535 if (!d)
536 return NULL((void*)0);
537
538 /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed
539 * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries
540 * with no references, which is commonly the case right before a daemon reload. */
541
542 assert(d->n_ref > 0)do { if ((__builtin_expect(!!(!(d->n_ref > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d->n_ref > 0"), "../src/core/dynamic-user.c"
, 542, __PRETTY_FUNCTION__); } while (0)
;
543 d->n_ref--;
544
545 return NULL((void*)0);
546}
547
548static int dynamic_user_close(DynamicUser *d) {
549 _cleanup_(unlockfp)__attribute__((cleanup(unlockfp))) int storage_socket0_lock = -1;
550 _cleanup_close___attribute__((cleanup(closep))) int lock_fd = -1;
551 uid_t uid;
552 int r;
553
554 /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is
555 * unrealized again, much like it was after it the DynamicUser object was first allocated. */
556
557 r = lockfp(d->storage_socket[0], &storage_socket0_lock);
558 if (r < 0)
559 return r;
560
561 r = dynamic_user_pop(d, &uid, &lock_fd);
562 if (r == -EAGAIN11)
563 /* User wasn't realized yet, nothing to do. */
564 return 0;
565 if (r < 0)
566 return r;
567
568 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
569 unlink_uid_lock(lock_fd, uid, d->name);
570 return 1;
571}
572
573static DynamicUser* dynamic_user_destroy(DynamicUser *d) {
574 if (!d)
575 return NULL((void*)0);
576
577 /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
578 * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
579 * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
580 * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
581
582 dynamic_user_unref(d);
583
584 if (d->n_ref > 0)
585 return NULL((void*)0);
586
587 (void) dynamic_user_close(d);
588 return dynamic_user_free(d);
589}
590
591int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
592 DynamicUser *d;
593 Iterator i;
594
595 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 595,
__PRETTY_FUNCTION__); } while (0)
;
596 assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("f"), "../src/core/dynamic-user.c", 596,
__PRETTY_FUNCTION__); } while (0)
;
597 assert(fds)do { if ((__builtin_expect(!!(!(fds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fds"), "../src/core/dynamic-user.c", 597
, __PRETTY_FUNCTION__); } while (0)
;
598
599 /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
600
601 HASHMAP_FOREACH(d, m->dynamic_users, i)for ((i) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .
next_key = ((void*)0) }); hashmap_iterate((m->dynamic_users
), &(i), (void**)&(d), ((void*)0)); )
{
602 int copy0, copy1;
603
604 copy0 = fdset_put_dup(fds, d->storage_socket[0]);
605 if (copy0 < 0)
606 return copy0;
607
608 copy1 = fdset_put_dup(fds, d->storage_socket[1]);
609 if (copy1 < 0)
610 return copy1;
611
612 fprintf(f, "dynamic-user=%s %i %i\n", d->name, copy0, copy1);
613 }
614
615 return 0;
616}
617
618void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds) {
619 _cleanup_free___attribute__((cleanup(freep))) char *name = NULL((void*)0), *s0 = NULL((void*)0), *s1 = NULL((void*)0);
620 int r, fd0, fd1;
621
622 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 622,
__PRETTY_FUNCTION__); } while (0)
;
623 assert(value)do { if ((__builtin_expect(!!(!(value)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("value"), "../src/core/dynamic-user.c", 623
, __PRETTY_FUNCTION__); } while (0)
;
624 assert(fds)do { if ((__builtin_expect(!!(!(fds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fds"), "../src/core/dynamic-user.c", 624
, __PRETTY_FUNCTION__); } while (0)
;
625
626 /* Parse the serialization again, after a daemon reload */
627
628 r = extract_many_words(&value, NULL((void*)0), 0, &name, &s0, &s1, NULL((void*)0));
629 if (r != 3 || !isempty(value)) {
630 log_debug("Unable to parse dynamic user line.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/dynamic-user.c", 630, __func__, "Unable to parse dynamic user line."
) : -abs(_e); })
;
631 return;
632 }
633
634 if (safe_atoi(s0, &fd0) < 0 || !fdset_contains(fds, fd0)) {
635 log_debug("Unable to process dynamic user fd specification.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/dynamic-user.c", 635, __func__, "Unable to process dynamic user fd specification."
) : -abs(_e); })
;
636 return;
637 }
638
639 if (safe_atoi(s1, &fd1) < 0 || !fdset_contains(fds, fd1)) {
640 log_debug("Unable to process dynamic user fd specification.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/dynamic-user.c", 640, __func__, "Unable to process dynamic user fd specification."
) : -abs(_e); })
;
641 return;
642 }
643
644 r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, NULL((void*)0));
645 if (r < 0) {
646 log_debug_errno(r, "Failed to add dynamic user: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/dynamic-user.c", 646, __func__, "Failed to add dynamic user: %m"
) : -abs(_e); })
;
647 return;
648 }
649
650 (void) fdset_remove(fds, fd0);
651 (void) fdset_remove(fds, fd1);
652}
653
654void dynamic_user_vacuum(Manager *m, bool_Bool close_user) {
655 DynamicUser *d;
656 Iterator i;
657
658 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 658,
__PRETTY_FUNCTION__); } while (0)
;
659
660 /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
661 * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
662 * might not be referenced anymore. */
663
664 HASHMAP_FOREACH(d, m->dynamic_users, i)for ((i) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .
next_key = ((void*)0) }); hashmap_iterate((m->dynamic_users
), &(i), (void**)&(d), ((void*)0)); )
{
665 if (d->n_ref > 0)
666 continue;
667
668 if (close_user) {
669 log_debug("Removing orphaned dynamic user %s", d->name)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/dynamic-user.c", 669, __func__, "Removing orphaned dynamic user %s"
, d->name) : -abs(_e); })
;
670 (void) dynamic_user_close(d);
671 }
672
673 dynamic_user_free(d);
674 }
675}
676
677int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
678 char lock_path[STRLEN("/run/systemd/dynamic-uid/")(sizeof("""/run/systemd/dynamic-uid/""") - 1) + DECIMAL_STR_MAX(uid_t)(2+(sizeof(uid_t) <= 1 ? 3 : sizeof(uid_t) <= 2 ? 5 : sizeof
(uid_t) <= 4 ? 10 : sizeof(uid_t) <= 8 ? 20 : sizeof(int
[-2*(sizeof(uid_t) > 8)])))
+ 1];
679 _cleanup_free___attribute__((cleanup(freep))) char *user = NULL((void*)0);
680 uid_t check_uid;
681 int r;
682
683 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 683,
__PRETTY_FUNCTION__); } while (0)
;
684 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/core/dynamic-user.c", 684
, __PRETTY_FUNCTION__); } while (0)
;
685
686 /* A friendly way to translate a dynamic user's UID into a name. */
687 if (!uid_is_dynamic(uid))
688 return -ESRCH3;
689
690 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid)do { if ((__builtin_expect(!!(!(((size_t) snprintf(lock_path,
__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(lock_path), typeof(&*(lock_path))), sizeof(lock_path
)/sizeof((lock_path)[0]), ((void)0))), "/run/systemd/dynamic-uid/"
"%" "u", uid) < (__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(lock_path), typeof(&*(lock_path))), sizeof(lock_path
)/sizeof((lock_path)[0]), ((void)0))))))),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("xsprintf: " "lock_path" "[] must be big enough"
), "../src/core/dynamic-user.c", 690, __PRETTY_FUNCTION__); }
while (0)
;
691 r = read_one_line_file(lock_path, &user);
692 if (r == -ENOENT2)
693 return -ESRCH3;
694 if (r < 0)
695 return r;
696
697 /* The lock file might be stale, hence let's verify the data before we return it */
698 r = dynamic_user_lookup_name(m, user, &check_uid);
699 if (r < 0)
700 return r;
701 if (check_uid != uid) /* lock file doesn't match our own idea */
702 return -ESRCH3;
703
704 *ret = TAKE_PTR(user)({ typeof(user) _ptr_ = (user); (user) = ((void*)0); _ptr_; }
)
;
705
706 return 0;
707}
708
709int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
710 DynamicUser *d;
711 int r;
712
713 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 713,
__PRETTY_FUNCTION__); } while (0)
;
714 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/core/dynamic-user.c", 714
, __PRETTY_FUNCTION__); } while (0)
;
715 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/core/dynamic-user.c", 715
, __PRETTY_FUNCTION__); } while (0)
;
716
717 /* A friendly call for translating a dynamic user's name into its UID */
718
719 d = hashmap_get(m->dynamic_users, name);
720 if (!d)
721 return -ESRCH3;
722
723 r = dynamic_user_current(d, ret);
724 if (r == -EAGAIN11) /* not realized yet? */
725 return -ESRCH3;
726
727 return r;
728}
729
730int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group) {
731 bool_Bool acquired = false0;
732 int r;
733
734 assert(creds)do { if ((__builtin_expect(!!(!(creds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("creds"), "../src/core/dynamic-user.c", 734
, __PRETTY_FUNCTION__); } while (0)
;
735 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/dynamic-user.c", 735,
__PRETTY_FUNCTION__); } while (0)
;
736
737 /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
738 * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
739 * and group. This call allocates a pair. */
740
741 if (!creds->user && user) {
742 r = dynamic_user_acquire(m, user, &creds->user);
743 if (r < 0)
744 return r;
745
746 acquired = true1;
747 }
748
749 if (!creds->group) {
750
751 if (creds->user && (!group || streq_ptr(user, group)))
752 creds->group = dynamic_user_ref(creds->user);
753 else {
754 r = dynamic_user_acquire(m, group, &creds->group);
755 if (r < 0) {
756 if (acquired)
757 creds->user = dynamic_user_unref(creds->user);
758 return r;
759 }
760 }
761 }
762
763 return 0;
764}
765
766int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid) {
767 uid_t u = UID_INVALID((uid_t) -1);
768 gid_t g = GID_INVALID((gid_t) -1);
769 int r;
770
771 assert(creds)do { if ((__builtin_expect(!!(!(creds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("creds"), "../src/core/dynamic-user.c", 771
, __PRETTY_FUNCTION__); } while (0)
;
772 assert(uid)do { if ((__builtin_expect(!!(!(uid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("uid"), "../src/core/dynamic-user.c", 772
, __PRETTY_FUNCTION__); } while (0)
;
773 assert(gid)do { if ((__builtin_expect(!!(!(gid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("gid"), "../src/core/dynamic-user.c", 773
, __PRETTY_FUNCTION__); } while (0)
;
774
775 /* Realize both the referenced user and group */
776
777 if (creds->user) {
778 r = dynamic_user_realize(creds->user, suggested_paths, &u, &g, true1);
779 if (r < 0)
780 return r;
781 }
782
783 if (creds->group && creds->group != creds->user) {
784 r = dynamic_user_realize(creds->group, suggested_paths, NULL((void*)0), &g, false0);
785 if (r < 0)
786 return r;
787 }
788
789 *uid = u;
790 *gid = g;
791 return 0;
792}
793
794void dynamic_creds_unref(DynamicCreds *creds) {
795 assert(creds)do { if ((__builtin_expect(!!(!(creds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("creds"), "../src/core/dynamic-user.c", 795
, __PRETTY_FUNCTION__); } while (0)
;
796
797 creds->user = dynamic_user_unref(creds->user);
798 creds->group = dynamic_user_unref(creds->group);
799}
800
801void dynamic_creds_destroy(DynamicCreds *creds) {
802 assert(creds)do { if ((__builtin_expect(!!(!(creds)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("creds"), "../src/core/dynamic-user.c", 802
, __PRETTY_FUNCTION__); } while (0)
;
803
804 creds->user = dynamic_user_destroy(creds->user);
805 creds->group = dynamic_user_destroy(creds->group);
806}