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