]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-systemd/nss-systemd.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
409093fe 2/***
409093fe 3 Copyright 2016 Lennart Poettering
409093fe
LP
4***/
5
6#include <nss.h>
12c2c56d 7#include <pthread.h>
409093fe
LP
8
9#include "sd-bus.h"
10
fd63e712 11#include "alloc-util.h"
409093fe 12#include "bus-common-errors.h"
12c2c56d 13#include "dirent-util.h"
409093fe 14#include "env-util.h"
12c2c56d 15#include "fd-util.h"
fd63e712 16#include "fs-util.h"
12c2c56d 17#include "list.h"
409093fe
LP
18#include "macro.h"
19#include "nss-util.h"
20#include "signal-util.h"
fd63e712 21#include "stdio-util.h"
2129011e 22#include "string-util.h"
409093fe
LP
23#include "user-util.h"
24#include "util.h"
25
9b5eaef3
YW
26#define DYNAMIC_USER_GECOS "Dynamic User"
27#define DYNAMIC_USER_PASSWD "*" /* locked */
28#define DYNAMIC_USER_DIR "/"
29#define DYNAMIC_USER_SHELL "/sbin/nologin"
30
2129011e
LP
31static const struct passwd root_passwd = {
32 .pw_name = (char*) "root",
33 .pw_passwd = (char*) "x", /* see shadow file */
34 .pw_uid = 0,
35 .pw_gid = 0,
36 .pw_gecos = (char*) "Super User",
37 .pw_dir = (char*) "/root",
38 .pw_shell = (char*) "/bin/sh",
39};
40
41static const struct passwd nobody_passwd = {
42 .pw_name = (char*) NOBODY_USER_NAME,
43 .pw_passwd = (char*) "*", /* locked */
3a664727
LP
44 .pw_uid = UID_NOBODY,
45 .pw_gid = GID_NOBODY,
2129011e
LP
46 .pw_gecos = (char*) "User Nobody",
47 .pw_dir = (char*) "/",
48 .pw_shell = (char*) "/sbin/nologin",
49};
50
51static const struct group root_group = {
52 .gr_name = (char*) "root",
53 .gr_gid = 0,
54 .gr_passwd = (char*) "x", /* see shadow file */
55 .gr_mem = (char*[]) { NULL },
56};
57
58static const struct group nobody_group = {
59 .gr_name = (char*) NOBODY_GROUP_NAME,
3a664727 60 .gr_gid = GID_NOBODY,
2129011e
LP
61 .gr_passwd = (char*) "*", /* locked */
62 .gr_mem = (char*[]) { NULL },
63};
64
12c2c56d
YW
65typedef struct UserEntry UserEntry;
66typedef struct GetentData GetentData;
67
68struct UserEntry {
69 uid_t id;
70 char *name;
71
72 GetentData *data;
73 LIST_FIELDS(UserEntry, entries);
74};
75
76struct GetentData {
77 /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
78 * reentrant since it shares the reading position in the stream with all other threads',
79 * we need to protect the data in UserEntry from multithreaded programs which may call
80 * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
81 * data by using the mutex below. */
82 pthread_mutex_t mutex;
83
84 UserEntry *position;
85 LIST_HEAD(UserEntry, entries);
86};
87
88static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
89static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
90
409093fe
LP
91NSS_GETPW_PROTOTYPES(systemd);
92NSS_GETGR_PROTOTYPES(systemd);
12c2c56d
YW
93enum nss_status _nss_systemd_endpwent(void) _public_;
94enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
95enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
96enum nss_status _nss_systemd_endgrent(void) _public_;
97enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
98enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
409093fe 99
fd63e712
LP
100static int direct_lookup_name(const char *name, uid_t *ret) {
101 _cleanup_free_ char *s = NULL;
102 const char *path;
103 int r;
104
105 assert(name);
106
107 /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
108 * namespace and subject to proper authentication. However, there's one problem: if our module is called from
109 * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
110 * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
111
112 path = strjoina("/run/systemd/dynamic-uid/direct:", name);
113 r = readlink_malloc(path, &s);
114 if (r < 0)
115 return r;
116
117 return parse_uid(s, ret);
118}
119
120static int direct_lookup_uid(uid_t uid, char **ret) {
fbd0b64f 121 char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
fd63e712
LP
122 int r;
123
124 xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
125
126 r = readlink_malloc(path, &s);
127 if (r < 0)
128 return r;
129 if (!valid_user_group_name(s)) { /* extra safety check */
130 free(s);
131 return -EINVAL;
132 }
133
134 *ret = s;
135 return 0;
136}
137
409093fe
LP
138enum nss_status _nss_systemd_getpwnam_r(
139 const char *name,
140 struct passwd *pwd,
141 char *buffer, size_t buflen,
142 int *errnop) {
143
24585419
YW
144 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
145 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
146 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
409093fe
LP
147 uint32_t translated;
148 size_t l;
689ca202 149 int bypass, r;
409093fe
LP
150
151 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
152
153 assert(name);
154 assert(pwd);
155
d6c575e3
LP
156 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
157 * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
158 if (!valid_user_group_name(name))
159 goto not_found;
2129011e
LP
160
161 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
71e0accc 162 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
163 if (streq(name, root_passwd.pw_name)) {
164 *pwd = root_passwd;
165 *errnop = 0;
166 return NSS_STATUS_SUCCESS;
167 }
24eccc34
LP
168 if (synthesize_nobody() &&
169 streq(name, nobody_passwd.pw_name)) {
fe102d6a
LP
170 *pwd = nobody_passwd;
171 *errnop = 0;
172 return NSS_STATUS_SUCCESS;
173 }
2129011e
LP
174 }
175
409093fe 176 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
71e0accc 177 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
178 goto not_found;
179
689ca202
YW
180 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
181 if (bypass <= 0) {
fd63e712 182 r = sd_bus_open_system(&bus);
24585419 183 if (r < 0)
689ca202 184 bypass = 1;
24585419 185 }
fd63e712 186
24585419
YW
187 if (bypass > 0) {
188 r = direct_lookup_name(name, (uid_t*) &translated);
189 if (r == -ENOENT)
190 goto not_found;
191 if (r < 0)
192 goto fail;
193 } else {
fd63e712
LP
194 r = sd_bus_call_method(bus,
195 "org.freedesktop.systemd1",
196 "/org/freedesktop/systemd1",
197 "org.freedesktop.systemd1.Manager",
198 "LookupDynamicUserByName",
199 &error,
200 &reply,
201 "s",
202 name);
203 if (r < 0) {
204 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
205 goto not_found;
206
207 goto fail;
208 }
209
210 r = sd_bus_message_read(reply, "u", &translated);
211 if (r < 0)
212 goto fail;
409093fe
LP
213 }
214
409093fe
LP
215 l = strlen(name);
216 if (buflen < l+1) {
cda458a5 217 *errnop = ERANGE;
409093fe
LP
218 return NSS_STATUS_TRYAGAIN;
219 }
220
221 memcpy(buffer, name, l+1);
222
223 pwd->pw_name = buffer;
224 pwd->pw_uid = (uid_t) translated;
225 pwd->pw_gid = (uid_t) translated;
9b5eaef3
YW
226 pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
227 pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
228 pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
229 pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
409093fe
LP
230
231 *errnop = 0;
232 return NSS_STATUS_SUCCESS;
233
234not_found:
235 *errnop = 0;
236 return NSS_STATUS_NOTFOUND;
237
238fail:
239 *errnop = -r;
240 return NSS_STATUS_UNAVAIL;
241}
242
243enum nss_status _nss_systemd_getpwuid_r(
244 uid_t uid,
245 struct passwd *pwd,
246 char *buffer, size_t buflen,
247 int *errnop) {
248
249 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
250 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
251 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
fd63e712 252 _cleanup_free_ char *direct = NULL;
409093fe
LP
253 const char *translated;
254 size_t l;
689ca202 255 int bypass, r;
409093fe
LP
256
257 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
258
d6c575e3
LP
259 if (!uid_is_valid(uid))
260 goto not_found;
409093fe 261
2129011e 262 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
71e0accc 263 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
264 if (uid == root_passwd.pw_uid) {
265 *pwd = root_passwd;
266 *errnop = 0;
267 return NSS_STATUS_SUCCESS;
268 }
24eccc34
LP
269 if (synthesize_nobody() &&
270 uid == nobody_passwd.pw_uid) {
fe102d6a
LP
271 *pwd = nobody_passwd;
272 *errnop = 0;
273 return NSS_STATUS_SUCCESS;
274 }
2129011e
LP
275 }
276
83438277 277 if (!uid_is_dynamic(uid))
409093fe
LP
278 goto not_found;
279
71e0accc 280 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
281 goto not_found;
282
689ca202
YW
283 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
284 if (bypass <= 0) {
fd63e712 285 r = sd_bus_open_system(&bus);
24585419 286 if (r < 0)
689ca202 287 bypass = 1;
24585419
YW
288 }
289
290 if (bypass > 0) {
291 r = direct_lookup_uid(uid, &direct);
292 if (r == -ENOENT)
293 goto not_found;
294 if (r < 0)
295 goto fail;
296
297 translated = direct;
fd63e712 298
24585419 299 } else {
fd63e712
LP
300 r = sd_bus_call_method(bus,
301 "org.freedesktop.systemd1",
302 "/org/freedesktop/systemd1",
303 "org.freedesktop.systemd1.Manager",
304 "LookupDynamicUserByUID",
305 &error,
306 &reply,
307 "u",
308 (uint32_t) uid);
309 if (r < 0) {
310 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
311 goto not_found;
312
313 goto fail;
314 }
315
316 r = sd_bus_message_read(reply, "s", &translated);
317 if (r < 0)
318 goto fail;
409093fe
LP
319 }
320
409093fe
LP
321 l = strlen(translated) + 1;
322 if (buflen < l) {
cda458a5 323 *errnop = ERANGE;
409093fe
LP
324 return NSS_STATUS_TRYAGAIN;
325 }
326
327 memcpy(buffer, translated, l);
328
329 pwd->pw_name = buffer;
330 pwd->pw_uid = uid;
331 pwd->pw_gid = uid;
9b5eaef3
YW
332 pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
333 pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
334 pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
335 pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
409093fe
LP
336
337 *errnop = 0;
338 return NSS_STATUS_SUCCESS;
339
340not_found:
341 *errnop = 0;
342 return NSS_STATUS_NOTFOUND;
343
344fail:
345 *errnop = -r;
346 return NSS_STATUS_UNAVAIL;
347}
348
3c3d384a
ZJS
349#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
350
409093fe
LP
351enum nss_status _nss_systemd_getgrnam_r(
352 const char *name,
353 struct group *gr,
354 char *buffer, size_t buflen,
355 int *errnop) {
356
24585419
YW
357 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
358 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
359 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
409093fe
LP
360 uint32_t translated;
361 size_t l;
689ca202 362 int bypass, r;
409093fe
LP
363
364 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
365
366 assert(name);
367 assert(gr);
368
d6c575e3
LP
369 if (!valid_user_group_name(name))
370 goto not_found;
2129011e
LP
371
372 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
71e0accc 373 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
374 if (streq(name, root_group.gr_name)) {
375 *gr = root_group;
376 *errnop = 0;
377 return NSS_STATUS_SUCCESS;
378 }
24eccc34
LP
379 if (synthesize_nobody() &&
380 streq(name, nobody_group.gr_name)) {
fe102d6a
LP
381 *gr = nobody_group;
382 *errnop = 0;
383 return NSS_STATUS_SUCCESS;
384 }
2129011e
LP
385 }
386
71e0accc 387 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
388 goto not_found;
389
689ca202
YW
390 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
391 if (bypass <= 0) {
fd63e712 392 r = sd_bus_open_system(&bus);
24585419 393 if (r < 0)
689ca202 394 bypass = 1;
24585419 395 }
fd63e712 396
24585419
YW
397 if (bypass > 0) {
398 r = direct_lookup_name(name, (uid_t*) &translated);
399 if (r == -ENOENT)
400 goto not_found;
401 if (r < 0)
402 goto fail;
403 } else {
fd63e712
LP
404 r = sd_bus_call_method(bus,
405 "org.freedesktop.systemd1",
406 "/org/freedesktop/systemd1",
407 "org.freedesktop.systemd1.Manager",
408 "LookupDynamicUserByName",
409 &error,
410 &reply,
411 "s",
412 name);
413 if (r < 0) {
414 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
415 goto not_found;
416
417 goto fail;
418 }
419
420 r = sd_bus_message_read(reply, "u", &translated);
421 if (r < 0)
422 goto fail;
409093fe
LP
423 }
424
409093fe
LP
425 l = sizeof(char*) + strlen(name) + 1;
426 if (buflen < l) {
cda458a5 427 *errnop = ERANGE;
409093fe
LP
428 return NSS_STATUS_TRYAGAIN;
429 }
430
431 memzero(buffer, sizeof(char*));
432 strcpy(buffer + sizeof(char*), name);
433
434 gr->gr_name = buffer + sizeof(char*);
435 gr->gr_gid = (gid_t) translated;
9b5eaef3 436 gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
409093fe
LP
437 gr->gr_mem = (char**) buffer;
438
439 *errnop = 0;
440 return NSS_STATUS_SUCCESS;
441
442not_found:
443 *errnop = 0;
444 return NSS_STATUS_NOTFOUND;
445
446fail:
447 *errnop = -r;
448 return NSS_STATUS_UNAVAIL;
449}
450
451enum nss_status _nss_systemd_getgrgid_r(
452 gid_t gid,
453 struct group *gr,
454 char *buffer, size_t buflen,
455 int *errnop) {
456
457 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
458 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
459 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
fd63e712 460 _cleanup_free_ char *direct = NULL;
409093fe
LP
461 const char *translated;
462 size_t l;
689ca202 463 int bypass, r;
409093fe
LP
464
465 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
466
d6c575e3
LP
467 if (!gid_is_valid(gid))
468 goto not_found;
409093fe 469
2129011e 470 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
71e0accc 471 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
472 if (gid == root_group.gr_gid) {
473 *gr = root_group;
474 *errnop = 0;
475 return NSS_STATUS_SUCCESS;
476 }
24eccc34
LP
477 if (synthesize_nobody() &&
478 gid == nobody_group.gr_gid) {
fe102d6a
LP
479 *gr = nobody_group;
480 *errnop = 0;
481 return NSS_STATUS_SUCCESS;
482 }
2129011e
LP
483 }
484
83438277 485 if (!gid_is_dynamic(gid))
409093fe
LP
486 goto not_found;
487
71e0accc 488 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
489 goto not_found;
490
689ca202
YW
491 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
492 if (bypass <= 0) {
fd63e712 493 r = sd_bus_open_system(&bus);
24585419 494 if (r < 0)
689ca202 495 bypass = 1;
24585419
YW
496 }
497
498 if (bypass > 0) {
499 r = direct_lookup_uid(gid, &direct);
500 if (r == -ENOENT)
501 goto not_found;
502 if (r < 0)
503 goto fail;
504
505 translated = direct;
fd63e712 506
24585419 507 } else {
fd63e712
LP
508 r = sd_bus_call_method(bus,
509 "org.freedesktop.systemd1",
510 "/org/freedesktop/systemd1",
511 "org.freedesktop.systemd1.Manager",
512 "LookupDynamicUserByUID",
513 &error,
514 &reply,
515 "u",
516 (uint32_t) gid);
517 if (r < 0) {
518 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
519 goto not_found;
520
521 goto fail;
522 }
523
524 r = sd_bus_message_read(reply, "s", &translated);
525 if (r < 0)
526 goto fail;
409093fe
LP
527 }
528
409093fe
LP
529 l = sizeof(char*) + strlen(translated) + 1;
530 if (buflen < l) {
cda458a5 531 *errnop = ERANGE;
409093fe
LP
532 return NSS_STATUS_TRYAGAIN;
533 }
534
535 memzero(buffer, sizeof(char*));
536 strcpy(buffer + sizeof(char*), translated);
537
538 gr->gr_name = buffer + sizeof(char*);
539 gr->gr_gid = gid;
9b5eaef3 540 gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
409093fe
LP
541 gr->gr_mem = (char**) buffer;
542
543 *errnop = 0;
544 return NSS_STATUS_SUCCESS;
545
546not_found:
547 *errnop = 0;
548 return NSS_STATUS_NOTFOUND;
549
550fail:
551 *errnop = -r;
552 return NSS_STATUS_UNAVAIL;
553}
12c2c56d
YW
554
555static void user_entry_free(UserEntry *p) {
556 if (!p)
557 return;
558
559 if (p->data)
560 LIST_REMOVE(entries, p->data->entries, p);
561
562 free(p->name);
563 free(p);
564}
565
566static int user_entry_add(GetentData *data, const char *name, uid_t id) {
567 UserEntry *p;
568
569 assert(data);
570
571 /* This happens when User= or Group= already exists statically. */
572 if (!uid_is_dynamic(id))
573 return -EINVAL;
574
575 p = new0(UserEntry, 1);
576 if (!p)
577 return -ENOMEM;
578
579 p->name = strdup(name);
580 if (!p->name) {
581 free(p);
582 return -ENOMEM;
583 }
584 p->id = id;
585 p->data = data;
586
587 LIST_PREPEND(entries, data->entries, p);
588
589 return 0;
590}
591
592static void systemd_endent(GetentData *data) {
593 UserEntry *p;
594
595 assert(data);
596
597 while ((p = data->entries))
598 user_entry_free(p);
599
600 data->position = NULL;
601}
602
603static enum nss_status nss_systemd_endent(GetentData *p) {
604 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
605
606 assert_se(pthread_mutex_lock(&p->mutex) == 0);
607 systemd_endent(p);
608 assert_se(pthread_mutex_unlock(&p->mutex) == 0);
609
610 return NSS_STATUS_SUCCESS;
611}
612
613enum nss_status _nss_systemd_endpwent(void) {
614 return nss_systemd_endent(&getpwent_data);
615}
616
617enum nss_status _nss_systemd_endgrent(void) {
618 return nss_systemd_endent(&getgrent_data);
619}
620
621static int direct_enumeration(GetentData *p) {
622 _cleanup_closedir_ DIR *d = NULL;
623 struct dirent *de;
624 int r;
625
626 assert(p);
627
628 d = opendir("/run/systemd/dynamic-uid/");
629 if (!d)
630 return -errno;
631
632 FOREACH_DIRENT(de, d, return -errno) {
633 _cleanup_free_ char *name = NULL;
634 uid_t uid, verified;
635
636 if (!dirent_is_file(de))
637 continue;
638
639 r = parse_uid(de->d_name, &uid);
640 if (r < 0)
641 continue;
642
643 r = direct_lookup_uid(uid, &name);
644 if (r == -ENOMEM)
645 return r;
646 if (r < 0)
647 continue;
648
649 r = direct_lookup_name(name, &verified);
650 if (r < 0)
651 continue;
652
653 if (uid != verified)
654 continue;
655
656 r = user_entry_add(p, name, uid);
657 if (r == -ENOMEM)
658 return r;
659 if (r < 0)
660 continue;
661 }
662
663 return 0;
664}
665
666static enum nss_status systemd_setent(GetentData *p) {
667 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
668 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
669 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
670 const char *name;
671 uid_t id;
672 int bypass, r;
673
674 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
675
676 assert(p);
677
678 assert_se(pthread_mutex_lock(&p->mutex) == 0);
679
680 systemd_endent(p);
681
682 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
683 goto finish;
684
685 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
686
687 if (bypass <= 0) {
688 r = sd_bus_open_system(&bus);
689 if (r < 0)
690 bypass = 1;
691 }
692
693 if (bypass > 0) {
694 r = direct_enumeration(p);
695 if (r < 0)
696 goto fail;
697
698 goto finish;
699 }
700
701 r = sd_bus_call_method(bus,
702 "org.freedesktop.systemd1",
703 "/org/freedesktop/systemd1",
704 "org.freedesktop.systemd1.Manager",
705 "GetDynamicUsers",
706 &error,
707 &reply,
708 NULL);
709 if (r < 0)
710 goto fail;
711
712 r = sd_bus_message_enter_container(reply, 'a', "(us)");
713 if (r < 0)
714 goto fail;
715
716 while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
717 r = user_entry_add(p, name, id);
718 if (r == -ENOMEM)
719 goto fail;
720 if (r < 0)
721 continue;
722 }
723 if (r < 0)
724 goto fail;
725
726 r = sd_bus_message_exit_container(reply);
727 if (r < 0)
728 goto fail;
729
730finish:
731 p->position = p->entries;
732 assert_se(pthread_mutex_unlock(&p->mutex) == 0);
733
734 return NSS_STATUS_SUCCESS;
735
736fail:
737 systemd_endent(p);
738 assert_se(pthread_mutex_unlock(&p->mutex) == 0);
739
740 return NSS_STATUS_UNAVAIL;
741}
742
743enum nss_status _nss_systemd_setpwent(int stayopen) {
744 return systemd_setent(&getpwent_data);
745}
746
747enum nss_status _nss_systemd_setgrent(int stayopen) {
748 return systemd_setent(&getgrent_data);
749}
750
751enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
752 enum nss_status ret;
753 UserEntry *p;
754 size_t len;
755
756 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
757
758 assert(result);
759 assert(buffer);
760 assert(errnop);
761
762 assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
763
764 LIST_FOREACH(entries, p, getpwent_data.position) {
765 len = strlen(p->name) + 1;
766 if (buflen < len) {
767 *errnop = ERANGE;
768 ret = NSS_STATUS_TRYAGAIN;
769 goto finalize;
770 }
771
772 memcpy(buffer, p->name, len);
773
774 result->pw_name = buffer;
775 result->pw_uid = p->id;
776 result->pw_gid = p->id;
777 result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
778 result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
779 result->pw_dir = (char*) DYNAMIC_USER_DIR;
780 result->pw_shell = (char*) DYNAMIC_USER_SHELL;
781 break;
782 }
783 if (!p) {
784 *errnop = ENOENT;
785 ret = NSS_STATUS_NOTFOUND;
786 goto finalize;
787 }
788
789 /* On success, step to the next entry. */
790 p = p->entries_next;
791 ret = NSS_STATUS_SUCCESS;
792
793finalize:
794 /* Save position for the next call. */
795 getpwent_data.position = p;
796
797 assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
798
799 return ret;
800}
801
802enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
803 enum nss_status ret;
804 UserEntry *p;
805 size_t len;
806
807 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
808
809 assert(result);
810 assert(buffer);
811 assert(errnop);
812
813 assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
814
815 LIST_FOREACH(entries, p, getgrent_data.position) {
816 len = sizeof(char*) + strlen(p->name) + 1;
817 if (buflen < len) {
818 *errnop = ERANGE;
819 ret = NSS_STATUS_TRYAGAIN;
820 goto finalize;
821 }
822
823 memzero(buffer, sizeof(char*));
824 strcpy(buffer + sizeof(char*), p->name);
825
826 result->gr_name = buffer + sizeof(char*);
827 result->gr_gid = p->id;
828 result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
829 result->gr_mem = (char**) buffer;
830 break;
831 }
832 if (!p) {
833 *errnop = ENOENT;
834 ret = NSS_STATUS_NOTFOUND;
835 goto finalize;
836 }
837
838 /* On success, step to the next entry. */
839 p = p->entries_next;
840 ret = NSS_STATUS_SUCCESS;
841
842finalize:
843 /* Save position for the next call. */
844 getgrent_data.position = p;
845
846 assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
847
848 return ret;
849}