]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
ea8fa89034a8e5e0710ffe3ce90898b9a993900b
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2016 Lennart Poettering
4 ***/
5
6 #include <nss.h>
7 #include <pthread.h>
8
9 #include "sd-bus.h"
10
11 #include "alloc-util.h"
12 #include "bus-common-errors.h"
13 #include "dirent-util.h"
14 #include "env-util.h"
15 #include "fd-util.h"
16 #include "fs-util.h"
17 #include "list.h"
18 #include "macro.h"
19 #include "nss-util.h"
20 #include "signal-util.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
23 #include "user-util.h"
24 #include "util.h"
25
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
31 static 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
41 static const struct passwd nobody_passwd = {
42 .pw_name = (char*) NOBODY_USER_NAME,
43 .pw_passwd = (char*) "*", /* locked */
44 .pw_uid = UID_NOBODY,
45 .pw_gid = GID_NOBODY,
46 .pw_gecos = (char*) "User Nobody",
47 .pw_dir = (char*) "/",
48 .pw_shell = (char*) "/sbin/nologin",
49 };
50
51 static 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
58 static const struct group nobody_group = {
59 .gr_name = (char*) NOBODY_GROUP_NAME,
60 .gr_gid = GID_NOBODY,
61 .gr_passwd = (char*) "*", /* locked */
62 .gr_mem = (char*[]) { NULL },
63 };
64
65 typedef struct UserEntry UserEntry;
66 typedef struct GetentData GetentData;
67
68 struct UserEntry {
69 uid_t id;
70 char *name;
71
72 GetentData *data;
73 LIST_FIELDS(UserEntry, entries);
74 };
75
76 struct 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
88 static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
89 static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
90
91 NSS_GETPW_PROTOTYPES(systemd);
92 NSS_GETGR_PROTOTYPES(systemd);
93 enum nss_status _nss_systemd_endpwent(void) _public_;
94 enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
95 enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
96 enum nss_status _nss_systemd_endgrent(void) _public_;
97 enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
98 enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
99
100 static 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
120 static int direct_lookup_uid(uid_t uid, char **ret) {
121 char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
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
138 enum nss_status _nss_systemd_getpwnam_r(
139 const char *name,
140 struct passwd *pwd,
141 char *buffer, size_t buflen,
142 int *errnop) {
143
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;
147 uint32_t translated;
148 size_t l;
149 int bypass, r;
150
151 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
152
153 assert(name);
154 assert(pwd);
155
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;
160
161 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
162 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
163 if (streq(name, root_passwd.pw_name)) {
164 *pwd = root_passwd;
165 *errnop = 0;
166 return NSS_STATUS_SUCCESS;
167 }
168 if (synthesize_nobody() &&
169 streq(name, nobody_passwd.pw_name)) {
170 *pwd = nobody_passwd;
171 *errnop = 0;
172 return NSS_STATUS_SUCCESS;
173 }
174 }
175
176 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
177 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
178 goto not_found;
179
180 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
181 if (bypass <= 0) {
182 r = sd_bus_open_system(&bus);
183 if (r < 0)
184 bypass = 1;
185 }
186
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 {
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;
213 }
214
215 l = strlen(name);
216 if (buflen < l+1) {
217 *errnop = ERANGE;
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;
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;
230
231 *errnop = 0;
232 return NSS_STATUS_SUCCESS;
233
234 not_found:
235 *errnop = 0;
236 return NSS_STATUS_NOTFOUND;
237
238 fail:
239 *errnop = -r;
240 return NSS_STATUS_UNAVAIL;
241 }
242
243 enum 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;
252 _cleanup_free_ char *direct = NULL;
253 const char *translated;
254 size_t l;
255 int bypass, r;
256
257 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
258
259 if (!uid_is_valid(uid))
260 goto not_found;
261
262 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
263 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
264 if (uid == root_passwd.pw_uid) {
265 *pwd = root_passwd;
266 *errnop = 0;
267 return NSS_STATUS_SUCCESS;
268 }
269 if (synthesize_nobody() &&
270 uid == nobody_passwd.pw_uid) {
271 *pwd = nobody_passwd;
272 *errnop = 0;
273 return NSS_STATUS_SUCCESS;
274 }
275 }
276
277 if (!uid_is_dynamic(uid))
278 goto not_found;
279
280 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
281 goto not_found;
282
283 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
284 if (bypass <= 0) {
285 r = sd_bus_open_system(&bus);
286 if (r < 0)
287 bypass = 1;
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;
298
299 } else {
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;
319 }
320
321 l = strlen(translated) + 1;
322 if (buflen < l) {
323 *errnop = ERANGE;
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;
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;
336
337 *errnop = 0;
338 return NSS_STATUS_SUCCESS;
339
340 not_found:
341 *errnop = 0;
342 return NSS_STATUS_NOTFOUND;
343
344 fail:
345 *errnop = -r;
346 return NSS_STATUS_UNAVAIL;
347 }
348
349 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
350
351 enum nss_status _nss_systemd_getgrnam_r(
352 const char *name,
353 struct group *gr,
354 char *buffer, size_t buflen,
355 int *errnop) {
356
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;
360 uint32_t translated;
361 size_t l;
362 int bypass, r;
363
364 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
365
366 assert(name);
367 assert(gr);
368
369 if (!valid_user_group_name(name))
370 goto not_found;
371
372 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
373 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
374 if (streq(name, root_group.gr_name)) {
375 *gr = root_group;
376 *errnop = 0;
377 return NSS_STATUS_SUCCESS;
378 }
379 if (synthesize_nobody() &&
380 streq(name, nobody_group.gr_name)) {
381 *gr = nobody_group;
382 *errnop = 0;
383 return NSS_STATUS_SUCCESS;
384 }
385 }
386
387 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
388 goto not_found;
389
390 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
391 if (bypass <= 0) {
392 r = sd_bus_open_system(&bus);
393 if (r < 0)
394 bypass = 1;
395 }
396
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 {
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;
423 }
424
425 l = sizeof(char*) + strlen(name) + 1;
426 if (buflen < l) {
427 *errnop = ERANGE;
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;
436 gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
437 gr->gr_mem = (char**) buffer;
438
439 *errnop = 0;
440 return NSS_STATUS_SUCCESS;
441
442 not_found:
443 *errnop = 0;
444 return NSS_STATUS_NOTFOUND;
445
446 fail:
447 *errnop = -r;
448 return NSS_STATUS_UNAVAIL;
449 }
450
451 enum 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;
460 _cleanup_free_ char *direct = NULL;
461 const char *translated;
462 size_t l;
463 int bypass, r;
464
465 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
466
467 if (!gid_is_valid(gid))
468 goto not_found;
469
470 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
471 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
472 if (gid == root_group.gr_gid) {
473 *gr = root_group;
474 *errnop = 0;
475 return NSS_STATUS_SUCCESS;
476 }
477 if (synthesize_nobody() &&
478 gid == nobody_group.gr_gid) {
479 *gr = nobody_group;
480 *errnop = 0;
481 return NSS_STATUS_SUCCESS;
482 }
483 }
484
485 if (!gid_is_dynamic(gid))
486 goto not_found;
487
488 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
489 goto not_found;
490
491 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
492 if (bypass <= 0) {
493 r = sd_bus_open_system(&bus);
494 if (r < 0)
495 bypass = 1;
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;
506
507 } else {
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;
527 }
528
529 l = sizeof(char*) + strlen(translated) + 1;
530 if (buflen < l) {
531 *errnop = ERANGE;
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;
540 gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
541 gr->gr_mem = (char**) buffer;
542
543 *errnop = 0;
544 return NSS_STATUS_SUCCESS;
545
546 not_found:
547 *errnop = 0;
548 return NSS_STATUS_NOTFOUND;
549
550 fail:
551 *errnop = -r;
552 return NSS_STATUS_UNAVAIL;
553 }
554
555 static 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
566 static 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
592 static 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
603 static 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
613 enum nss_status _nss_systemd_endpwent(void) {
614 return nss_systemd_endent(&getpwent_data);
615 }
616
617 enum nss_status _nss_systemd_endgrent(void) {
618 return nss_systemd_endent(&getgrent_data);
619 }
620
621 static 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
666 static 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
730 finish:
731 p->position = p->entries;
732 assert_se(pthread_mutex_unlock(&p->mutex) == 0);
733
734 return NSS_STATUS_SUCCESS;
735
736 fail:
737 systemd_endent(p);
738 assert_se(pthread_mutex_unlock(&p->mutex) == 0);
739
740 return NSS_STATUS_UNAVAIL;
741 }
742
743 enum nss_status _nss_systemd_setpwent(int stayopen) {
744 return systemd_setent(&getpwent_data);
745 }
746
747 enum nss_status _nss_systemd_setgrent(int stayopen) {
748 return systemd_setent(&getgrent_data);
749 }
750
751 enum 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
793 finalize:
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
802 enum 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
842 finalize:
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 }