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