]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
Merge pull request #26669 from YHNdnzj/journalctl-lines-since-until
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <nss.h>
4 #include <pthread.h>
5 #include <string.h>
6
7 #include "env-util.h"
8 #include "errno-util.h"
9 #include "fd-util.h"
10 #include "log.h"
11 #include "macro.h"
12 #include "missing_threads.h"
13 #include "nss-systemd.h"
14 #include "nss-util.h"
15 #include "pthread-util.h"
16 #include "signal-util.h"
17 #include "strv.h"
18 #include "user-record-nss.h"
19 #include "user-util.h"
20 #include "userdb-glue.h"
21 #include "userdb.h"
22
23 static const struct passwd root_passwd = {
24 .pw_name = (char*) "root",
25 .pw_passwd = (char*) PASSWORD_SEE_SHADOW,
26 .pw_uid = 0,
27 .pw_gid = 0,
28 .pw_gecos = (char*) "Super User",
29 .pw_dir = (char*) "/root",
30 .pw_shell = NULL,
31 };
32
33 static const struct spwd root_spwd = {
34 .sp_namp = (char*) "root",
35 .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID,
36 .sp_lstchg = -1,
37 .sp_min = -1,
38 .sp_max = -1,
39 .sp_warn = -1,
40 .sp_inact = -1,
41 .sp_expire = -1,
42 .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
43 };
44
45 static const struct passwd nobody_passwd = {
46 .pw_name = (char*) NOBODY_USER_NAME,
47 .pw_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
48 .pw_uid = UID_NOBODY,
49 .pw_gid = GID_NOBODY,
50 .pw_gecos = (char*) "Kernel Overflow User",
51 .pw_dir = (char*) "/",
52 .pw_shell = (char*) NOLOGIN,
53 };
54
55 static const struct spwd nobody_spwd = {
56 .sp_namp = (char*) NOBODY_USER_NAME,
57 .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID,
58 .sp_lstchg = -1,
59 .sp_min = -1,
60 .sp_max = -1,
61 .sp_warn = -1,
62 .sp_inact = -1,
63 .sp_expire = -1,
64 .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
65 };
66
67 static const struct group root_group = {
68 .gr_name = (char*) "root",
69 .gr_gid = 0,
70 .gr_passwd = (char*) PASSWORD_SEE_SHADOW,
71 .gr_mem = (char*[]) { NULL },
72 };
73
74 static const struct sgrp root_sgrp = {
75 .sg_namp = (char*) "root",
76 .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
77 };
78
79 static const struct group nobody_group = {
80 .gr_name = (char*) NOBODY_GROUP_NAME,
81 .gr_gid = GID_NOBODY,
82 .gr_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
83 .gr_mem = (char*[]) { NULL },
84 };
85
86 static const struct sgrp nobody_sgrp = {
87 .sg_namp = (char*) NOBODY_GROUP_NAME,
88 .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
89 };
90
91 typedef struct GetentData {
92 /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really reentrant since it
93 * shares the reading position in the stream with all other threads', we need to protect the data in
94 * UserDBIterator from multithreaded programs which may call setpwent(), getpwent_r(), or endpwent()
95 * simultaneously. So, each function locks the data by using the mutex below. */
96 pthread_mutex_t mutex;
97 UserDBIterator *iterator;
98
99 /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
100 * otherwise. */
101 bool by_membership;
102 } GetentData;
103
104 static GetentData getpwent_data = {
105 .mutex = PTHREAD_MUTEX_INITIALIZER,
106 };
107
108 static GetentData getgrent_data = {
109 .mutex = PTHREAD_MUTEX_INITIALIZER,
110 };
111
112 static GetentData getspent_data = {
113 .mutex = PTHREAD_MUTEX_INITIALIZER,
114 };
115
116 static GetentData getsgent_data = {
117 .mutex = PTHREAD_MUTEX_INITIALIZER,
118 };
119
120 static void setup_logging_once(void) {
121 static pthread_once_t once = PTHREAD_ONCE_INIT;
122 assert_se(pthread_once(&once, log_parse_environment_variables) == 0);
123 }
124
125 #define NSS_ENTRYPOINT_BEGIN \
126 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
127 setup_logging_once()
128
129 NSS_GETPW_PROTOTYPES(systemd);
130 NSS_GETSP_PROTOTYPES(systemd);
131 NSS_GETGR_PROTOTYPES(systemd);
132 NSS_GETSG_PROTOTYPES(systemd);
133 NSS_PWENT_PROTOTYPES(systemd);
134 NSS_SPENT_PROTOTYPES(systemd);
135 NSS_GRENT_PROTOTYPES(systemd);
136 NSS_SGENT_PROTOTYPES(systemd);
137 NSS_INITGROUPS_PROTOTYPE(systemd);
138
139 /* Since our NSS functions implement reentrant glibc APIs, we have to guarantee
140 * all the string pointers we return point into the buffer provided by the
141 * caller, not into our own static memory. */
142
143 static enum nss_status copy_synthesized_passwd(
144 struct passwd *dest,
145 const struct passwd *src,
146 const char *fallback_shell,
147 char *buffer, size_t buflen,
148 int *errnop) {
149
150 assert(dest);
151 assert(src);
152 assert(src->pw_name);
153 assert(src->pw_passwd);
154 assert(src->pw_gecos);
155 assert(src->pw_dir);
156
157 const char *shell = ASSERT_PTR(src->pw_shell ?: fallback_shell);
158
159 size_t required =
160 strlen(src->pw_name) + 1 +
161 strlen(src->pw_passwd) + 1 +
162 strlen(src->pw_gecos) + 1 +
163 strlen(src->pw_dir) + 1 +
164 strlen(shell) + 1;
165
166 if (buflen < required) {
167 *errnop = ERANGE;
168 return NSS_STATUS_TRYAGAIN;
169 }
170
171 assert(buffer);
172
173 *dest = *src;
174
175 /* String fields point into the user-provided buffer */
176 dest->pw_name = buffer;
177 dest->pw_passwd = stpcpy(dest->pw_name, src->pw_name) + 1;
178 dest->pw_gecos = stpcpy(dest->pw_passwd, src->pw_passwd) + 1;
179 dest->pw_dir = stpcpy(dest->pw_gecos, src->pw_gecos) + 1;
180 dest->pw_shell = stpcpy(dest->pw_dir, src->pw_dir) + 1;
181 strcpy(dest->pw_shell, shell);
182
183 return NSS_STATUS_SUCCESS;
184 }
185
186 static enum nss_status copy_synthesized_spwd(
187 struct spwd *dest,
188 const struct spwd *src,
189 char *buffer, size_t buflen,
190 int *errnop) {
191
192 assert(dest);
193 assert(src);
194 assert(src->sp_namp);
195 assert(src->sp_pwdp);
196
197 size_t required =
198 strlen(src->sp_namp) + 1 +
199 strlen(src->sp_pwdp) + 1;
200
201 if (buflen < required) {
202 *errnop = ERANGE;
203 return NSS_STATUS_TRYAGAIN;
204 }
205
206 assert(buffer);
207
208 *dest = *src;
209
210 /* String fields point into the user-provided buffer */
211 dest->sp_namp = buffer;
212 dest->sp_pwdp = stpcpy(dest->sp_namp, src->sp_namp) + 1;
213 strcpy(dest->sp_pwdp, src->sp_pwdp);
214
215 return NSS_STATUS_SUCCESS;
216 }
217
218 static enum nss_status copy_synthesized_group(
219 struct group *dest,
220 const struct group *src,
221 char *buffer, size_t buflen,
222 int *errnop) {
223
224 assert(dest);
225 assert(src);
226 assert(src->gr_name);
227 assert(src->gr_passwd);
228 assert(src->gr_mem);
229 assert(!*src->gr_mem); /* Our synthesized records' gr_mem is always just NULL... */
230
231 size_t required =
232 strlen(src->gr_name) + 1 +
233 strlen(src->gr_passwd) + 1 +
234 sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
235
236 if (buflen < ALIGN(required)) {
237 *errnop = ERANGE;
238 return NSS_STATUS_TRYAGAIN;
239 }
240
241 assert(buffer);
242
243 *dest = *src;
244
245 /* String fields point into the user-provided buffer */
246 dest->gr_name = buffer;
247 dest->gr_passwd = stpcpy(dest->gr_name, src->gr_name) + 1;
248 dest->gr_mem = ALIGN_PTR(stpcpy(dest->gr_passwd, src->gr_passwd) + 1);
249 *dest->gr_mem = NULL;
250
251 return NSS_STATUS_SUCCESS;
252 }
253
254 static enum nss_status copy_synthesized_sgrp(
255 struct sgrp *dest,
256 const struct sgrp *src,
257 char *buffer, size_t buflen,
258 int *errnop) {
259
260 assert(dest);
261 assert(src);
262 assert(src->sg_namp);
263 assert(src->sg_passwd);
264
265 size_t required =
266 strlen(src->sg_namp) + 1 +
267 strlen(src->sg_passwd) + 1;
268
269 if (buflen < required) {
270 *errnop = ERANGE;
271 return NSS_STATUS_TRYAGAIN;
272 }
273
274 assert(buffer);
275
276 *dest = *src;
277
278 /* String fields point into the user-provided buffer */
279 dest->sg_namp = buffer;
280 dest->sg_passwd = stpcpy(dest->sg_namp, src->sg_namp) + 1;
281 strcpy(dest->sg_passwd, src->sg_passwd);
282
283 return NSS_STATUS_SUCCESS;
284 }
285
286 enum nss_status _nss_systemd_getpwnam_r(
287 const char *name,
288 struct passwd *pwd,
289 char *buffer, size_t buflen,
290 int *errnop) {
291
292 enum nss_status status;
293 int e;
294
295 PROTECT_ERRNO;
296 NSS_ENTRYPOINT_BEGIN;
297
298 assert(name);
299 assert(pwd);
300 assert(errnop);
301
302 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
303 * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
304 * invalid user names. */
305 if (!valid_user_group_name(name, VALID_USER_RELAX))
306 return NSS_STATUS_NOTFOUND;
307
308 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
309 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
310
311 if (streq(name, root_passwd.pw_name))
312 return copy_synthesized_passwd(pwd, &root_passwd,
313 default_root_shell(NULL),
314 buffer, buflen, errnop);
315
316 if (streq(name, nobody_passwd.pw_name)) {
317 if (!synthesize_nobody())
318 return NSS_STATUS_NOTFOUND;
319
320 return copy_synthesized_passwd(pwd, &nobody_passwd,
321 NULL,
322 buffer, buflen, errnop);
323 }
324
325 } else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
326 return NSS_STATUS_NOTFOUND;
327
328 status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
329 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
330 UNPROTECT_ERRNO;
331 *errnop = e;
332 return status;
333 }
334
335 return status;
336 }
337
338 enum nss_status _nss_systemd_getpwuid_r(
339 uid_t uid,
340 struct passwd *pwd,
341 char *buffer, size_t buflen,
342 int *errnop) {
343
344 enum nss_status status;
345 int e;
346
347 PROTECT_ERRNO;
348 NSS_ENTRYPOINT_BEGIN;
349
350 assert(pwd);
351 assert(errnop);
352
353 if (!uid_is_valid(uid))
354 return NSS_STATUS_NOTFOUND;
355
356 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
357 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
358
359 if (uid == root_passwd.pw_uid)
360 return copy_synthesized_passwd(pwd, &root_passwd,
361 default_root_shell(NULL),
362 buffer, buflen, errnop);
363
364 if (uid == nobody_passwd.pw_uid) {
365 if (!synthesize_nobody())
366 return NSS_STATUS_NOTFOUND;
367
368 return copy_synthesized_passwd(pwd, &nobody_passwd,
369 NULL,
370 buffer, buflen, errnop);
371 }
372
373 } else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
374 return NSS_STATUS_NOTFOUND;
375
376 status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
377 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
378 UNPROTECT_ERRNO;
379 *errnop = e;
380 return status;
381 }
382
383 return status;
384 }
385
386 enum nss_status _nss_systemd_getspnam_r(
387 const char *name,
388 struct spwd *spwd,
389 char *buffer, size_t buflen,
390 int *errnop) {
391
392 enum nss_status status;
393 int e;
394
395 PROTECT_ERRNO;
396 NSS_ENTRYPOINT_BEGIN;
397
398 assert(name);
399 assert(spwd);
400 assert(errnop);
401
402 if (!valid_user_group_name(name, VALID_USER_RELAX))
403 return NSS_STATUS_NOTFOUND;
404
405 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
406 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
407
408 if (streq(name, root_spwd.sp_namp))
409 return copy_synthesized_spwd(spwd, &root_spwd, buffer, buflen, errnop);
410
411 if (streq(name, nobody_spwd.sp_namp)) {
412 if (!synthesize_nobody())
413 return NSS_STATUS_NOTFOUND;
414
415 return copy_synthesized_spwd(spwd, &nobody_spwd, buffer, buflen, errnop);
416 }
417
418 } else if (STR_IN_SET(name, root_spwd.sp_namp, nobody_spwd.sp_namp))
419 return NSS_STATUS_NOTFOUND;
420
421 status = userdb_getspnam(name, spwd, buffer, buflen, &e);
422 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
423 UNPROTECT_ERRNO;
424 *errnop = e;
425 return status;
426 }
427
428 return status;
429 }
430
431 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
432
433 enum nss_status _nss_systemd_getgrnam_r(
434 const char *name,
435 struct group *gr,
436 char *buffer, size_t buflen,
437 int *errnop) {
438
439 enum nss_status status;
440 int e;
441
442 PROTECT_ERRNO;
443 NSS_ENTRYPOINT_BEGIN;
444
445 assert(name);
446 assert(gr);
447 assert(errnop);
448
449 if (!valid_user_group_name(name, VALID_USER_RELAX))
450 return NSS_STATUS_NOTFOUND;
451
452 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
453 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
454
455 if (streq(name, root_group.gr_name))
456 return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
457
458 if (streq(name, nobody_group.gr_name)) {
459 if (!synthesize_nobody())
460 return NSS_STATUS_NOTFOUND;
461
462 return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
463 }
464
465 } else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
466 return NSS_STATUS_NOTFOUND;
467
468 status = userdb_getgrnam(name, gr, buffer, buflen, &e);
469 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
470 UNPROTECT_ERRNO;
471 *errnop = e;
472 return status;
473 }
474
475 return status;
476 }
477
478 enum nss_status _nss_systemd_getgrgid_r(
479 gid_t gid,
480 struct group *gr,
481 char *buffer, size_t buflen,
482 int *errnop) {
483
484 enum nss_status status;
485 int e;
486
487 PROTECT_ERRNO;
488 NSS_ENTRYPOINT_BEGIN;
489
490 assert(gr);
491 assert(errnop);
492
493 if (!gid_is_valid(gid))
494 return NSS_STATUS_NOTFOUND;
495
496 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
497 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
498
499 if (gid == root_group.gr_gid)
500 return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
501
502 if (gid == nobody_group.gr_gid) {
503 if (!synthesize_nobody())
504 return NSS_STATUS_NOTFOUND;
505
506 return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
507 }
508
509 } else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
510 return NSS_STATUS_NOTFOUND;
511
512 status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
513 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
514 UNPROTECT_ERRNO;
515 *errnop = e;
516 return status;
517 }
518
519 return status;
520 }
521
522 enum nss_status _nss_systemd_getsgnam_r(
523 const char *name,
524 struct sgrp *sgrp,
525 char *buffer, size_t buflen,
526 int *errnop) {
527
528 enum nss_status status;
529 int e;
530
531 PROTECT_ERRNO;
532 NSS_ENTRYPOINT_BEGIN;
533
534 assert(name);
535 assert(sgrp);
536 assert(errnop);
537
538 if (!valid_user_group_name(name, VALID_USER_RELAX))
539 return NSS_STATUS_NOTFOUND;
540
541 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
542 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
543
544 if (streq(name, root_sgrp.sg_namp))
545 return copy_synthesized_sgrp(sgrp, &root_sgrp, buffer, buflen, errnop);
546
547 if (streq(name, nobody_sgrp.sg_namp)) {
548 if (!synthesize_nobody())
549 return NSS_STATUS_NOTFOUND;
550
551 return copy_synthesized_sgrp(sgrp, &nobody_sgrp, buffer, buflen, errnop);
552 }
553
554 } else if (STR_IN_SET(name, root_sgrp.sg_namp, nobody_sgrp.sg_namp))
555 return NSS_STATUS_NOTFOUND;
556
557 status = userdb_getsgnam(name, sgrp, buffer, buflen, &e);
558 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
559 UNPROTECT_ERRNO;
560 *errnop = e;
561 return status;
562 }
563
564 return status;
565 }
566
567 static enum nss_status nss_systemd_endent(GetentData *p) {
568 PROTECT_ERRNO;
569 NSS_ENTRYPOINT_BEGIN;
570
571 assert(p);
572
573 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&p->mutex);
574 (void) _l; /* make llvm shut up about _l not being used. */
575
576 p->iterator = userdb_iterator_free(p->iterator);
577 p->by_membership = false;
578
579 return NSS_STATUS_SUCCESS;
580 }
581
582 enum nss_status _nss_systemd_endpwent(void) {
583 return nss_systemd_endent(&getpwent_data);
584 }
585
586 enum nss_status _nss_systemd_endspent(void) {
587 return nss_systemd_endent(&getspent_data);
588 }
589
590 enum nss_status _nss_systemd_endgrent(void) {
591 return nss_systemd_endent(&getgrent_data);
592 }
593
594 enum nss_status _nss_systemd_endsgent(void) {
595 return nss_systemd_endent(&getsgent_data);
596 }
597
598 enum nss_status _nss_systemd_setpwent(int stayopen) {
599 int r;
600
601 PROTECT_ERRNO;
602 NSS_ENTRYPOINT_BEGIN;
603
604 if (_nss_systemd_is_blocked())
605 return NSS_STATUS_NOTFOUND;
606
607 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
608 (void) _l; /* make llvm shut up about _l not being used. */
609
610 getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
611 getpwent_data.by_membership = false;
612
613 /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
614 * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
615 * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
616 * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
617 * user database. */
618 r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator);
619 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
620 }
621
622 enum nss_status _nss_systemd_setgrent(int stayopen) {
623 int r;
624
625 PROTECT_ERRNO;
626 NSS_ENTRYPOINT_BEGIN;
627
628 if (_nss_systemd_is_blocked())
629 return NSS_STATUS_NOTFOUND;
630
631 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
632 (void) _l; /* make llvm shut up about _l not being used. */
633
634 getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
635 getgrent_data.by_membership = false;
636
637 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
638 r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator);
639 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
640 }
641
642 enum nss_status _nss_systemd_setspent(int stayopen) {
643 int r;
644
645 PROTECT_ERRNO;
646 NSS_ENTRYPOINT_BEGIN;
647
648 if (_nss_systemd_is_blocked())
649 return NSS_STATUS_NOTFOUND;
650
651 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getspent_data.mutex);
652 (void) _l; /* make llvm shut up about _l not being used. */
653
654 getspent_data.iterator = userdb_iterator_free(getspent_data.iterator);
655 getspent_data.by_membership = false;
656
657 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
658 r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getspent_data.iterator);
659 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
660 }
661
662 enum nss_status _nss_systemd_setsgent(int stayopen) {
663 int r;
664
665 PROTECT_ERRNO;
666 NSS_ENTRYPOINT_BEGIN;
667
668 if (_nss_systemd_is_blocked())
669 return NSS_STATUS_NOTFOUND;
670
671 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getsgent_data.mutex);
672 (void) _l; /* make llvm shut up about _l not being used. */
673
674 getsgent_data.iterator = userdb_iterator_free(getsgent_data.iterator);
675 getsgent_data.by_membership = false;
676
677 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
678 r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getsgent_data.iterator);
679 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
680 }
681
682 enum nss_status _nss_systemd_getpwent_r(
683 struct passwd *result,
684 char *buffer, size_t buflen,
685 int *errnop) {
686
687 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
688 int r;
689
690 PROTECT_ERRNO;
691 NSS_ENTRYPOINT_BEGIN;
692
693 assert(result);
694 assert(errnop);
695
696 if (_nss_systemd_is_blocked())
697 return NSS_STATUS_NOTFOUND;
698
699 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
700 (void) _l; /* make llvm shut up about _l not being used. */
701
702 if (!getpwent_data.iterator) {
703 UNPROTECT_ERRNO;
704 *errnop = EHOSTDOWN;
705 return NSS_STATUS_UNAVAIL;
706 }
707
708 r = userdb_iterator_get(getpwent_data.iterator, &ur);
709 if (r == -ESRCH)
710 return NSS_STATUS_NOTFOUND;
711 if (r < 0) {
712 UNPROTECT_ERRNO;
713 *errnop = -r;
714 return NSS_STATUS_UNAVAIL;
715 }
716
717 r = nss_pack_user_record(ur, result, buffer, buflen);
718 if (r < 0) {
719 UNPROTECT_ERRNO;
720 *errnop = -r;
721 return NSS_STATUS_TRYAGAIN;
722 }
723
724 return NSS_STATUS_SUCCESS;
725 }
726
727 enum nss_status _nss_systemd_getgrent_r(
728 struct group *result,
729 char *buffer, size_t buflen,
730 int *errnop) {
731
732 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
733 _cleanup_free_ char **members = NULL;
734 int r;
735
736 PROTECT_ERRNO;
737 NSS_ENTRYPOINT_BEGIN;
738
739 assert(result);
740 assert(errnop);
741
742 if (_nss_systemd_is_blocked())
743 return NSS_STATUS_NOTFOUND;
744
745 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
746 (void) _l; /* make llvm shut up about _l not being used. */
747
748 if (!getgrent_data.iterator) {
749 UNPROTECT_ERRNO;
750 *errnop = EHOSTDOWN;
751 return NSS_STATUS_UNAVAIL;
752 }
753
754 if (!getgrent_data.by_membership) {
755 r = groupdb_iterator_get(getgrent_data.iterator, &gr);
756 if (r == -ESRCH) {
757 /* So we finished iterating native groups now. Let's now continue with iterating
758 * native memberships, and generate additional group entries for any groups
759 * referenced there that are defined in NSS only. This means for those groups there
760 * will be two or more entries generated during iteration, but this is apparently how
761 * this is supposed to work, and what other implementations do too. Clients are
762 * supposed to merge the group records found during iteration automatically. */
763 getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
764
765 r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
766 if (r < 0 && r != -ESRCH) {
767 UNPROTECT_ERRNO;
768 *errnop = -r;
769 return NSS_STATUS_UNAVAIL;
770 }
771
772 getgrent_data.by_membership = true;
773 } else if (r < 0) {
774 UNPROTECT_ERRNO;
775 *errnop = -r;
776 return NSS_STATUS_UNAVAIL;
777 } else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) {
778 r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members);
779 if (r < 0 && r != -ESRCH) {
780 UNPROTECT_ERRNO;
781 *errnop = -r;
782 return NSS_STATUS_UNAVAIL;
783 }
784 }
785 }
786
787 if (getgrent_data.by_membership) {
788 _cleanup_(_nss_systemd_unblockp) bool blocked = false;
789
790 if (!getgrent_data.iterator)
791 return NSS_STATUS_NOTFOUND;
792
793 for (;;) {
794 _cleanup_free_ char *user_name = NULL, *group_name = NULL;
795
796 r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name);
797 if (r == -ESRCH)
798 return NSS_STATUS_NOTFOUND;
799 if (r < 0) {
800 UNPROTECT_ERRNO;
801 *errnop = -r;
802 return NSS_STATUS_UNAVAIL;
803 }
804
805 if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
806 continue;
807 if (STR_IN_SET(group_name, root_group.gr_name, nobody_group.gr_name))
808 continue;
809
810 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
811 if (!blocked) {
812 r = _nss_systemd_block(true);
813 if (r < 0) {
814 UNPROTECT_ERRNO;
815 *errnop = -r;
816 return NSS_STATUS_UNAVAIL;
817 }
818
819 blocked = true;
820 }
821
822 r = nss_group_record_by_name(group_name, false, &gr);
823 if (r == -ESRCH)
824 continue;
825 if (r < 0) {
826 log_debug_errno(r, "Failed to do NSS check for group '%s', ignoring: %m", group_name);
827 continue;
828 }
829
830 members = strv_new(user_name);
831 if (!members) {
832 UNPROTECT_ERRNO;
833 *errnop = ENOMEM;
834 return NSS_STATUS_TRYAGAIN;
835 }
836
837 /* Note that we currently generate one group entry per user that is part of a
838 * group. It's a bit ugly, but equivalent to generating a single entry with a set of
839 * members in them. */
840 break;
841 }
842 }
843
844 r = nss_pack_group_record(gr, members, result, buffer, buflen);
845 if (r < 0) {
846 UNPROTECT_ERRNO;
847 *errnop = -r;
848 return NSS_STATUS_TRYAGAIN;
849 }
850
851 return NSS_STATUS_SUCCESS;
852 }
853
854 enum nss_status _nss_systemd_getspent_r(
855 struct spwd *result,
856 char *buffer, size_t buflen,
857 int *errnop) {
858
859 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
860 int r;
861
862 PROTECT_ERRNO;
863 NSS_ENTRYPOINT_BEGIN;
864
865 assert(result);
866 assert(errnop);
867
868 if (_nss_systemd_is_blocked())
869 return NSS_STATUS_NOTFOUND;
870
871 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getspent_data.mutex);
872 (void) _l; /* make llvm shut up about _l not being used. */
873
874 if (!getspent_data.iterator) {
875 UNPROTECT_ERRNO;
876 *errnop = EHOSTDOWN;
877 return NSS_STATUS_UNAVAIL;
878 }
879
880 for (;;) {
881 r = userdb_iterator_get(getspent_data.iterator, &ur);
882 if (r == -ESRCH)
883 return NSS_STATUS_NOTFOUND;
884 if (r < 0) {
885 UNPROTECT_ERRNO;
886 *errnop = -r;
887 return NSS_STATUS_UNAVAIL;
888 }
889
890 if (!ur->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */
891 break;
892
893 ur = user_record_unref(ur);
894 }
895
896 r = nss_pack_user_record_shadow(ur, result, buffer, buflen);
897 if (r < 0) {
898 UNPROTECT_ERRNO;
899 *errnop = -r;
900 return NSS_STATUS_TRYAGAIN;
901 }
902
903 return NSS_STATUS_SUCCESS;
904 }
905
906 enum nss_status _nss_systemd_getsgent_r(
907 struct sgrp *result,
908 char *buffer, size_t buflen,
909 int *errnop) {
910
911 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
912 int r;
913
914 PROTECT_ERRNO;
915 NSS_ENTRYPOINT_BEGIN;
916
917 assert(result);
918 assert(errnop);
919
920 if (_nss_systemd_is_blocked())
921 return NSS_STATUS_NOTFOUND;
922
923 _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getsgent_data.mutex);
924 (void) _l; /* make llvm shut up about _l not being used. */
925
926 if (!getsgent_data.iterator) {
927 UNPROTECT_ERRNO;
928 *errnop = EHOSTDOWN;
929 return NSS_STATUS_UNAVAIL;
930 }
931
932 for (;;) {
933 r = groupdb_iterator_get(getsgent_data.iterator, &gr);
934 if (r == -ESRCH)
935 return NSS_STATUS_NOTFOUND;
936 if (r < 0) {
937 UNPROTECT_ERRNO;
938 *errnop = -r;
939 return NSS_STATUS_UNAVAIL;
940 }
941
942 if (!gr->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */
943 break;
944
945 gr = group_record_unref(gr);
946 }
947
948 r = nss_pack_group_record_shadow(gr, result, buffer, buflen);
949 if (r < 0) {
950 UNPROTECT_ERRNO;
951 *errnop = -r;
952 return NSS_STATUS_TRYAGAIN;
953 }
954
955 return NSS_STATUS_SUCCESS;
956 }
957
958 enum nss_status _nss_systemd_initgroups_dyn(
959 const char *user_name,
960 gid_t gid,
961 long *start,
962 long *size,
963 gid_t **groupsp,
964 long int limit,
965 int *errnop) {
966
967 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
968 bool any = false;
969 int r;
970
971 PROTECT_ERRNO;
972 NSS_ENTRYPOINT_BEGIN;
973
974 assert(user_name);
975 assert(start);
976 assert(size);
977 assert(groupsp);
978 assert(errnop);
979
980 if (!valid_user_group_name(user_name, VALID_USER_RELAX))
981 return NSS_STATUS_NOTFOUND;
982
983 /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
984 if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
985 return NSS_STATUS_NOTFOUND;
986
987 if (_nss_systemd_is_blocked())
988 return NSS_STATUS_NOTFOUND;
989
990 r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
991 if (r < 0) {
992 UNPROTECT_ERRNO;
993 *errnop = -r;
994 return NSS_STATUS_UNAVAIL;
995 }
996
997 for (;;) {
998 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
999 _cleanup_free_ char *group_name = NULL;
1000
1001 r = membershipdb_iterator_get(iterator, NULL, &group_name);
1002 if (r == -ESRCH)
1003 break;
1004 if (r < 0) {
1005 UNPROTECT_ERRNO;
1006 *errnop = -r;
1007 return NSS_STATUS_UNAVAIL;
1008 }
1009
1010 /* The group might be defined via traditional NSS only, hence let's do a full look-up without
1011 * disabling NSS. This means we are operating recursively here. */
1012
1013 r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g);
1014 if (r == -ESRCH)
1015 continue;
1016 if (r < 0) {
1017 log_debug_errno(r, "Failed to resolve group '%s', ignoring: %m", group_name);
1018 continue;
1019 }
1020
1021 if (g->gid == gid)
1022 continue;
1023
1024 if (*start >= *size) {
1025 gid_t *new_groups;
1026 long new_size;
1027
1028 if (limit > 0 && *size >= limit) /* Reached the limit.? */
1029 break;
1030
1031 if (*size > LONG_MAX/2) { /* Check for overflow */
1032 UNPROTECT_ERRNO;
1033 *errnop = ENOMEM;
1034 return NSS_STATUS_TRYAGAIN;
1035 }
1036
1037 new_size = *start * 2;
1038 if (limit > 0 && new_size > limit)
1039 new_size = limit;
1040
1041 /* Enlarge buffer */
1042 new_groups = reallocarray(*groupsp, new_size, sizeof(**groupsp));
1043 if (!new_groups) {
1044 UNPROTECT_ERRNO;
1045 *errnop = ENOMEM;
1046 return NSS_STATUS_TRYAGAIN;
1047 }
1048
1049 *groupsp = new_groups;
1050 *size = new_size;
1051 }
1052
1053 (*groupsp)[(*start)++] = g->gid;
1054 any = true;
1055 }
1056
1057 return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
1058 }
1059
1060 static thread_local unsigned _blocked = 0;
1061
1062 _public_ int _nss_systemd_block(bool b) {
1063
1064 /* This blocks recursively: it's blocked for as many times this function is called with `true` until
1065 * it is called an equal time with `false`. */
1066
1067 if (b) {
1068 if (_blocked >= UINT_MAX)
1069 return -EOVERFLOW;
1070
1071 _blocked++;
1072 } else {
1073 if (_blocked <= 0)
1074 return -EOVERFLOW;
1075
1076 _blocked--;
1077 }
1078
1079 return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */
1080 }
1081
1082 _public_ bool _nss_systemd_is_blocked(void) {
1083 return _blocked > 0;
1084 }