1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "errno-util.h"
12 #include "nss-systemd.h"
14 #include "pthread-util.h"
15 #include "signal-util.h"
17 #include "user-record-nss.h"
18 #include "user-util.h"
19 #include "userdb-glue.h"
22 static const struct passwd root_passwd
= {
23 .pw_name
= (char*) "root",
24 .pw_passwd
= (char*) PASSWORD_SEE_SHADOW
,
27 .pw_gecos
= (char*) "Super User",
28 .pw_dir
= (char*) "/root",
29 .pw_shell
= (char*) "/bin/sh",
32 static const struct spwd root_spwd
= {
33 .sp_namp
= (char*) "root",
34 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
41 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
44 static const struct passwd nobody_passwd
= {
45 .pw_name
= (char*) NOBODY_USER_NAME
,
46 .pw_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
49 .pw_gecos
= (char*) "User Nobody",
50 .pw_dir
= (char*) "/",
51 .pw_shell
= (char*) NOLOGIN
,
54 static const struct spwd nobody_spwd
= {
55 .sp_namp
= (char*) NOBODY_USER_NAME
,
56 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
63 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
66 static const struct group root_group
= {
67 .gr_name
= (char*) "root",
69 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
70 .gr_mem
= (char*[]) { NULL
},
73 static const struct sgrp root_sgrp
= {
74 .sg_namp
= (char*) "root",
75 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
78 static const struct group nobody_group
= {
79 .gr_name
= (char*) NOBODY_GROUP_NAME
,
81 .gr_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
82 .gr_mem
= (char*[]) { NULL
},
85 static const struct sgrp nobody_sgrp
= {
86 .sg_namp
= (char*) NOBODY_GROUP_NAME
,
87 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
90 typedef struct GetentData
{
91 /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really reentrant since it
92 * shares the reading position in the stream with all other threads', we need to protect the data in
93 * UserDBIterator from multithreaded programs which may call setpwent(), getpwent_r(), or endpwent()
94 * simultaneously. So, each function locks the data by using the mutex below. */
95 pthread_mutex_t mutex
;
96 UserDBIterator
*iterator
;
98 /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
103 static GetentData getpwent_data
= {
104 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
107 static GetentData getgrent_data
= {
108 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
111 static GetentData getspent_data
= {
112 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
115 static GetentData getsgent_data
= {
116 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
119 static void setup_logging(void) {
120 /* We need a dummy function because log_parse_environment is a macro. */
121 log_parse_environment();
124 static void setup_logging_once(void) {
125 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
126 assert_se(pthread_once(&once
, setup_logging
) == 0);
129 #define NSS_ENTRYPOINT_BEGIN \
130 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
133 NSS_GETPW_PROTOTYPES(systemd
);
134 NSS_GETSP_PROTOTYPES(systemd
);
135 NSS_GETGR_PROTOTYPES(systemd
);
136 NSS_GETSG_PROTOTYPES(systemd
);
137 NSS_PWENT_PROTOTYPES(systemd
);
138 NSS_SPENT_PROTOTYPES(systemd
);
139 NSS_GRENT_PROTOTYPES(systemd
);
140 NSS_SGENT_PROTOTYPES(systemd
);
141 NSS_INITGROUPS_PROTOTYPE(systemd
);
143 /* Since our NSS functions implement reentrant glibc APIs, we have to guarantee
144 * all the string pointers we return point into the buffer provided by the
145 * caller, not into our own static memory. */
147 static enum nss_status
copy_synthesized_passwd(
149 const struct passwd
*src
,
150 char *buffer
, size_t buflen
,
157 assert(src
->pw_name
);
158 assert(src
->pw_passwd
);
159 assert(src
->pw_gecos
);
161 assert(src
->pw_shell
);
163 required
= strlen(src
->pw_name
) + 1;
164 required
+= strlen(src
->pw_passwd
) + 1;
165 required
+= strlen(src
->pw_gecos
) + 1;
166 required
+= strlen(src
->pw_dir
) + 1;
167 required
+= strlen(src
->pw_shell
) + 1;
169 if (buflen
< required
) {
171 return NSS_STATUS_TRYAGAIN
;
178 /* String fields point into the user-provided buffer */
179 dest
->pw_name
= buffer
;
180 dest
->pw_passwd
= stpcpy(dest
->pw_name
, src
->pw_name
) + 1;
181 dest
->pw_gecos
= stpcpy(dest
->pw_passwd
, src
->pw_passwd
) + 1;
182 dest
->pw_dir
= stpcpy(dest
->pw_gecos
, src
->pw_gecos
) + 1;
183 dest
->pw_shell
= stpcpy(dest
->pw_dir
, src
->pw_dir
) + 1;
184 strcpy(dest
->pw_shell
, src
->pw_shell
);
186 return NSS_STATUS_SUCCESS
;
189 static enum nss_status
copy_synthesized_spwd(
191 const struct spwd
*src
,
192 char *buffer
, size_t buflen
,
199 assert(src
->sp_namp
);
200 assert(src
->sp_pwdp
);
202 required
= strlen(src
->sp_namp
) + 1;
203 required
+= strlen(src
->sp_pwdp
) + 1;
205 if (buflen
< required
) {
207 return NSS_STATUS_TRYAGAIN
;
214 /* String fields point into the user-provided buffer */
215 dest
->sp_namp
= buffer
;
216 dest
->sp_pwdp
= stpcpy(dest
->sp_namp
, src
->sp_namp
) + 1;
217 strcpy(dest
->sp_pwdp
, src
->sp_pwdp
);
219 return NSS_STATUS_SUCCESS
;
222 static enum nss_status
copy_synthesized_group(
224 const struct group
*src
,
225 char *buffer
, size_t buflen
,
232 assert(src
->gr_name
);
233 assert(src
->gr_passwd
);
235 assert(!*src
->gr_mem
); /* Our synthesized records' gr_mem is always just NULL... */
237 required
= strlen(src
->gr_name
) + 1;
238 required
+= strlen(src
->gr_passwd
) + 1;
239 required
+= sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
241 if (buflen
< ALIGN(required
)) {
243 return NSS_STATUS_TRYAGAIN
;
250 /* String fields point into the user-provided buffer */
251 dest
->gr_name
= buffer
;
252 dest
->gr_passwd
= stpcpy(dest
->gr_name
, src
->gr_name
) + 1;
253 dest
->gr_mem
= ALIGN_PTR(stpcpy(dest
->gr_passwd
, src
->gr_passwd
) + 1);
254 *dest
->gr_mem
= NULL
;
256 return NSS_STATUS_SUCCESS
;
259 static enum nss_status
copy_synthesized_sgrp(
261 const struct sgrp
*src
,
262 char *buffer
, size_t buflen
,
269 assert(src
->sg_namp
);
270 assert(src
->sg_passwd
);
272 required
= strlen(src
->sg_namp
) + 1;
273 required
+= strlen(src
->sg_passwd
) + 1;
275 if (buflen
< required
) {
277 return NSS_STATUS_TRYAGAIN
;
284 /* String fields point into the user-provided buffer */
285 dest
->sg_namp
= buffer
;
286 dest
->sg_passwd
= stpcpy(dest
->sg_namp
, src
->sg_namp
) + 1;
287 strcpy(dest
->sg_passwd
, src
->sg_passwd
);
289 return NSS_STATUS_SUCCESS
;
292 enum nss_status
_nss_systemd_getpwnam_r(
295 char *buffer
, size_t buflen
,
298 enum nss_status status
;
302 NSS_ENTRYPOINT_BEGIN
;
308 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
309 * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
310 * invalid user names. */
311 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
312 return NSS_STATUS_NOTFOUND
;
314 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
315 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
317 if (streq(name
, root_passwd
.pw_name
))
318 return copy_synthesized_passwd(pwd
, &root_passwd
, buffer
, buflen
, errnop
);
320 if (streq(name
, nobody_passwd
.pw_name
)) {
321 if (!synthesize_nobody())
322 return NSS_STATUS_NOTFOUND
;
324 return copy_synthesized_passwd(pwd
, &nobody_passwd
, buffer
, buflen
, errnop
);
327 } else if (STR_IN_SET(name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
328 return NSS_STATUS_NOTFOUND
;
330 status
= userdb_getpwnam(name
, pwd
, buffer
, buflen
, &e
);
331 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
340 enum nss_status
_nss_systemd_getpwuid_r(
343 char *buffer
, size_t buflen
,
346 enum nss_status status
;
350 NSS_ENTRYPOINT_BEGIN
;
355 if (!uid_is_valid(uid
))
356 return NSS_STATUS_NOTFOUND
;
358 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
359 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
361 if (uid
== root_passwd
.pw_uid
)
362 return copy_synthesized_passwd(pwd
, &root_passwd
, buffer
, buflen
, errnop
);
364 if (uid
== nobody_passwd
.pw_uid
) {
365 if (!synthesize_nobody())
366 return NSS_STATUS_NOTFOUND
;
368 return copy_synthesized_passwd(pwd
, &nobody_passwd
, buffer
, buflen
, errnop
);
371 } else if (uid
== root_passwd
.pw_uid
|| uid
== nobody_passwd
.pw_uid
)
372 return NSS_STATUS_NOTFOUND
;
374 status
= userdb_getpwuid(uid
, pwd
, buffer
, buflen
, &e
);
375 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
384 enum nss_status
_nss_systemd_getspnam_r(
387 char *buffer
, size_t buflen
,
390 enum nss_status status
;
394 NSS_ENTRYPOINT_BEGIN
;
400 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
401 return NSS_STATUS_NOTFOUND
;
403 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
404 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
406 if (streq(name
, root_spwd
.sp_namp
))
407 return copy_synthesized_spwd(spwd
, &root_spwd
, buffer
, buflen
, errnop
);
409 if (streq(name
, nobody_spwd
.sp_namp
)) {
410 if (!synthesize_nobody())
411 return NSS_STATUS_NOTFOUND
;
413 return copy_synthesized_spwd(spwd
, &nobody_spwd
, buffer
, buflen
, errnop
);
416 } else if (STR_IN_SET(name
, root_spwd
.sp_namp
, nobody_spwd
.sp_namp
))
417 return NSS_STATUS_NOTFOUND
;
419 status
= userdb_getspnam(name
, spwd
, buffer
, buflen
, &e
);
420 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
429 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
431 enum nss_status
_nss_systemd_getgrnam_r(
434 char *buffer
, size_t buflen
,
437 enum nss_status status
;
441 NSS_ENTRYPOINT_BEGIN
;
447 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
448 return NSS_STATUS_NOTFOUND
;
450 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
451 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
453 if (streq(name
, root_group
.gr_name
))
454 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
456 if (streq(name
, nobody_group
.gr_name
)) {
457 if (!synthesize_nobody())
458 return NSS_STATUS_NOTFOUND
;
460 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
463 } else if (STR_IN_SET(name
, root_group
.gr_name
, nobody_group
.gr_name
))
464 return NSS_STATUS_NOTFOUND
;
466 status
= userdb_getgrnam(name
, gr
, buffer
, buflen
, &e
);
467 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
476 enum nss_status
_nss_systemd_getgrgid_r(
479 char *buffer
, size_t buflen
,
482 enum nss_status status
;
486 NSS_ENTRYPOINT_BEGIN
;
491 if (!gid_is_valid(gid
))
492 return NSS_STATUS_NOTFOUND
;
494 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
495 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
497 if (gid
== root_group
.gr_gid
)
498 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
500 if (gid
== nobody_group
.gr_gid
) {
501 if (!synthesize_nobody())
502 return NSS_STATUS_NOTFOUND
;
504 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
507 } else if (gid
== root_group
.gr_gid
|| gid
== nobody_group
.gr_gid
)
508 return NSS_STATUS_NOTFOUND
;
510 status
= userdb_getgrgid(gid
, gr
, buffer
, buflen
, &e
);
511 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
520 enum nss_status
_nss_systemd_getsgnam_r(
523 char *buffer
, size_t buflen
,
526 enum nss_status status
;
530 NSS_ENTRYPOINT_BEGIN
;
536 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
537 return NSS_STATUS_NOTFOUND
;
539 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
540 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
542 if (streq(name
, root_sgrp
.sg_namp
))
543 return copy_synthesized_sgrp(sgrp
, &root_sgrp
, buffer
, buflen
, errnop
);
545 if (streq(name
, nobody_sgrp
.sg_namp
)) {
546 if (!synthesize_nobody())
547 return NSS_STATUS_NOTFOUND
;
549 return copy_synthesized_sgrp(sgrp
, &nobody_sgrp
, buffer
, buflen
, errnop
);
552 } else if (STR_IN_SET(name
, root_sgrp
.sg_namp
, nobody_sgrp
.sg_namp
))
553 return NSS_STATUS_NOTFOUND
;
555 status
= userdb_getsgnam(name
, sgrp
, buffer
, buflen
, &e
);
556 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
565 static enum nss_status
nss_systemd_endent(GetentData
*p
) {
567 NSS_ENTRYPOINT_BEGIN
;
571 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&p
->mutex
);
572 (void) _l
; /* make llvm shut up about _l not being used. */
574 p
->iterator
= userdb_iterator_free(p
->iterator
);
575 p
->by_membership
= false;
577 return NSS_STATUS_SUCCESS
;
580 enum nss_status
_nss_systemd_endpwent(void) {
581 return nss_systemd_endent(&getpwent_data
);
584 enum nss_status
_nss_systemd_endspent(void) {
585 return nss_systemd_endent(&getspent_data
);
588 enum nss_status
_nss_systemd_endgrent(void) {
589 return nss_systemd_endent(&getgrent_data
);
592 enum nss_status
_nss_systemd_endsgent(void) {
593 return nss_systemd_endent(&getsgent_data
);
596 enum nss_status
_nss_systemd_setpwent(int stayopen
) {
600 NSS_ENTRYPOINT_BEGIN
;
602 if (_nss_systemd_is_blocked())
603 return NSS_STATUS_NOTFOUND
;
605 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
606 (void) _l
; /* make llvm shut up about _l not being used. */
608 getpwent_data
.iterator
= userdb_iterator_free(getpwent_data
.iterator
);
609 getpwent_data
.by_membership
= false;
611 /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
612 * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
613 * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
614 * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
616 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getpwent_data
.iterator
);
617 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
620 enum nss_status
_nss_systemd_setgrent(int stayopen
) {
624 NSS_ENTRYPOINT_BEGIN
;
626 if (_nss_systemd_is_blocked())
627 return NSS_STATUS_NOTFOUND
;
629 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
630 (void) _l
; /* make llvm shut up about _l not being used. */
632 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
633 getgrent_data
.by_membership
= false;
635 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
636 r
= groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getgrent_data
.iterator
);
637 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
640 enum nss_status
_nss_systemd_setspent(int stayopen
) {
644 NSS_ENTRYPOINT_BEGIN
;
646 if (_nss_systemd_is_blocked())
647 return NSS_STATUS_NOTFOUND
;
649 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getspent_data
.mutex
);
650 (void) _l
; /* make llvm shut up about _l not being used. */
652 getspent_data
.iterator
= userdb_iterator_free(getspent_data
.iterator
);
653 getspent_data
.by_membership
= false;
655 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
656 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getspent_data
.iterator
);
657 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
660 enum nss_status
_nss_systemd_setsgent(int stayopen
) {
664 NSS_ENTRYPOINT_BEGIN
;
666 if (_nss_systemd_is_blocked())
667 return NSS_STATUS_NOTFOUND
;
669 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getsgent_data
.mutex
);
670 (void) _l
; /* make llvm shut up about _l not being used. */
672 getsgent_data
.iterator
= userdb_iterator_free(getsgent_data
.iterator
);
673 getsgent_data
.by_membership
= false;
675 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
676 r
= groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getsgent_data
.iterator
);
677 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
680 enum nss_status
_nss_systemd_getpwent_r(
681 struct passwd
*result
,
682 char *buffer
, size_t buflen
,
685 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
689 NSS_ENTRYPOINT_BEGIN
;
694 if (_nss_systemd_is_blocked())
695 return NSS_STATUS_NOTFOUND
;
697 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
698 (void) _l
; /* make llvm shut up about _l not being used. */
700 if (!getpwent_data
.iterator
) {
703 return NSS_STATUS_UNAVAIL
;
706 r
= userdb_iterator_get(getpwent_data
.iterator
, &ur
);
708 return NSS_STATUS_NOTFOUND
;
712 return NSS_STATUS_UNAVAIL
;
715 r
= nss_pack_user_record(ur
, result
, buffer
, buflen
);
719 return NSS_STATUS_TRYAGAIN
;
722 return NSS_STATUS_SUCCESS
;
725 enum nss_status
_nss_systemd_getgrent_r(
726 struct group
*result
,
727 char *buffer
, size_t buflen
,
730 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
731 _cleanup_free_
char **members
= NULL
;
735 NSS_ENTRYPOINT_BEGIN
;
740 if (_nss_systemd_is_blocked())
741 return NSS_STATUS_NOTFOUND
;
743 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
744 (void) _l
; /* make llvm shut up about _l not being used. */
746 if (!getgrent_data
.iterator
) {
749 return NSS_STATUS_UNAVAIL
;
752 if (!getgrent_data
.by_membership
) {
753 r
= groupdb_iterator_get(getgrent_data
.iterator
, &gr
);
755 /* So we finished iterating native groups now. Let's now continue with iterating
756 * native memberships, and generate additional group entries for any groups
757 * referenced there that are defined in NSS only. This means for those groups there
758 * will be two or more entries generated during iteration, but this is apparently how
759 * this is supposed to work, and what other implementations do too. Clients are
760 * supposed to merge the group records found during iteration automatically. */
761 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
763 r
= membershipdb_all(nss_glue_userdb_flags(), &getgrent_data
.iterator
);
764 if (r
< 0 && r
!= -ESRCH
) {
767 return NSS_STATUS_UNAVAIL
;
770 getgrent_data
.by_membership
= true;
774 return NSS_STATUS_UNAVAIL
;
775 } else if (!STR_IN_SET(gr
->group_name
, root_group
.gr_name
, nobody_group
.gr_name
)) {
776 r
= membershipdb_by_group_strv(gr
->group_name
, nss_glue_userdb_flags(), &members
);
777 if (r
< 0 && r
!= -ESRCH
) {
780 return NSS_STATUS_UNAVAIL
;
785 if (getgrent_data
.by_membership
) {
786 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
788 if (!getgrent_data
.iterator
)
789 return NSS_STATUS_NOTFOUND
;
792 _cleanup_free_
char *user_name
= NULL
, *group_name
= NULL
;
794 r
= membershipdb_iterator_get(getgrent_data
.iterator
, &user_name
, &group_name
);
796 return NSS_STATUS_NOTFOUND
;
800 return NSS_STATUS_UNAVAIL
;
803 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
805 if (STR_IN_SET(group_name
, root_group
.gr_name
, nobody_group
.gr_name
))
808 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
810 r
= _nss_systemd_block(true);
814 return NSS_STATUS_UNAVAIL
;
820 r
= nss_group_record_by_name(group_name
, false, &gr
);
824 log_debug_errno(r
, "Failed to do NSS check for group '%s', ignoring: %m", group_name
);
828 members
= strv_new(user_name
);
832 return NSS_STATUS_TRYAGAIN
;
835 /* Note that we currently generate one group entry per user that is part of a
836 * group. It's a bit ugly, but equivalent to generating a single entry with a set of
837 * members in them. */
842 r
= nss_pack_group_record(gr
, members
, result
, buffer
, buflen
);
846 return NSS_STATUS_TRYAGAIN
;
849 return NSS_STATUS_SUCCESS
;
852 enum nss_status
_nss_systemd_getspent_r(
854 char *buffer
, size_t buflen
,
857 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
861 NSS_ENTRYPOINT_BEGIN
;
866 if (_nss_systemd_is_blocked())
867 return NSS_STATUS_NOTFOUND
;
869 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getspent_data
.mutex
);
870 (void) _l
; /* make llvm shut up about _l not being used. */
872 if (!getspent_data
.iterator
) {
875 return NSS_STATUS_UNAVAIL
;
879 r
= userdb_iterator_get(getspent_data
.iterator
, &ur
);
881 return NSS_STATUS_NOTFOUND
;
885 return NSS_STATUS_UNAVAIL
;
888 if (!ur
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
891 ur
= user_record_unref(ur
);
894 r
= nss_pack_user_record_shadow(ur
, result
, buffer
, buflen
);
898 return NSS_STATUS_TRYAGAIN
;
901 return NSS_STATUS_SUCCESS
;
904 enum nss_status
_nss_systemd_getsgent_r(
906 char *buffer
, size_t buflen
,
909 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
913 NSS_ENTRYPOINT_BEGIN
;
918 if (_nss_systemd_is_blocked())
919 return NSS_STATUS_NOTFOUND
;
921 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getsgent_data
.mutex
);
922 (void) _l
; /* make llvm shut up about _l not being used. */
924 if (!getsgent_data
.iterator
) {
927 return NSS_STATUS_UNAVAIL
;
931 r
= groupdb_iterator_get(getsgent_data
.iterator
, &gr
);
933 return NSS_STATUS_NOTFOUND
;
937 return NSS_STATUS_UNAVAIL
;
940 if (!gr
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
943 gr
= group_record_unref(gr
);
946 r
= nss_pack_group_record_shadow(gr
, result
, buffer
, buflen
);
950 return NSS_STATUS_TRYAGAIN
;
953 return NSS_STATUS_SUCCESS
;
956 enum nss_status
_nss_systemd_initgroups_dyn(
957 const char *user_name
,
965 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
970 NSS_ENTRYPOINT_BEGIN
;
978 if (!valid_user_group_name(user_name
, VALID_USER_RELAX
))
979 return NSS_STATUS_NOTFOUND
;
981 /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
982 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
983 return NSS_STATUS_NOTFOUND
;
985 if (_nss_systemd_is_blocked())
986 return NSS_STATUS_NOTFOUND
;
988 r
= membershipdb_by_user(user_name
, nss_glue_userdb_flags(), &iterator
);
992 return NSS_STATUS_UNAVAIL
;
996 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
997 _cleanup_free_
char *group_name
= NULL
;
999 r
= membershipdb_iterator_get(iterator
, NULL
, &group_name
);
1005 return NSS_STATUS_UNAVAIL
;
1008 /* The group might be defined via traditional NSS only, hence let's do a full look-up without
1009 * disabling NSS. This means we are operating recursively here. */
1011 r
= groupdb_by_name(group_name
, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS
) | USERDB_SUPPRESS_SHADOW
, &g
);
1015 log_debug_errno(r
, "Failed to resolve group '%s', ignoring: %m", group_name
);
1022 if (*start
>= *size
) {
1026 if (limit
> 0 && *size
>= limit
) /* Reached the limit.? */
1029 if (*size
> LONG_MAX
/2) { /* Check for overflow */
1032 return NSS_STATUS_TRYAGAIN
;
1035 new_size
= *start
* 2;
1036 if (limit
> 0 && new_size
> limit
)
1039 /* Enlarge buffer */
1040 new_groups
= reallocarray(*groupsp
, new_size
, sizeof(**groupsp
));
1044 return NSS_STATUS_TRYAGAIN
;
1047 *groupsp
= new_groups
;
1051 (*groupsp
)[(*start
)++] = g
->gid
;
1055 return any
? NSS_STATUS_SUCCESS
: NSS_STATUS_NOTFOUND
;
1058 static thread_local
unsigned _blocked
= 0;
1060 _public_
int _nss_systemd_block(bool b
) {
1062 /* This blocks recursively: it's blocked for as many times this function is called with `true` until
1063 * it is called an equal time with `false`. */
1066 if (_blocked
>= UINT_MAX
)
1077 return b
; /* Return what is passed in, i.e. the new state from the PoV of the caller */
1080 _public_
bool _nss_systemd_is_blocked(void) {
1081 return _blocked
> 0;