1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "errno-util.h"
12 #include "missing_threads.h"
13 #include "nss-systemd.h"
15 #include "pthread-util.h"
16 #include "signal-util.h"
18 #include "user-record-nss.h"
19 #include "user-util.h"
20 #include "userdb-glue.h"
23 static const struct passwd root_passwd
= {
24 .pw_name
= (char*) "root",
25 .pw_passwd
= (char*) PASSWORD_SEE_SHADOW
,
28 .pw_gecos
= (char*) "Super User",
29 .pw_dir
= (char*) "/root",
33 static const struct spwd root_spwd
= {
34 .sp_namp
= (char*) "root",
35 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
42 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
45 static const struct passwd nobody_passwd
= {
46 .pw_name
= (char*) NOBODY_USER_NAME
,
47 .pw_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
50 .pw_gecos
= (char*) "Kernel Overflow User",
51 .pw_dir
= (char*) "/",
52 .pw_shell
= (char*) NOLOGIN
,
55 static const struct spwd nobody_spwd
= {
56 .sp_namp
= (char*) NOBODY_USER_NAME
,
57 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
64 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
67 static const struct group root_group
= {
68 .gr_name
= (char*) "root",
70 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
71 .gr_mem
= (char*[]) { NULL
},
74 static const struct sgrp root_sgrp
= {
75 .sg_namp
= (char*) "root",
76 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
79 static const struct group nobody_group
= {
80 .gr_name
= (char*) NOBODY_GROUP_NAME
,
82 .gr_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
83 .gr_mem
= (char*[]) { NULL
},
86 static const struct sgrp nobody_sgrp
= {
87 .sg_namp
= (char*) NOBODY_GROUP_NAME
,
88 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
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
;
99 /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
104 static GetentData getpwent_data
= {
105 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
108 static GetentData getgrent_data
= {
109 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
112 static GetentData getspent_data
= {
113 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
116 static GetentData getsgent_data
= {
117 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
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);
125 #define NSS_ENTRYPOINT_BEGIN \
126 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
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
);
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. */
143 static enum nss_status
copy_synthesized_passwd(
145 const struct passwd
*src
,
146 const char *fallback_shell
,
147 char *buffer
, size_t buflen
,
152 assert(src
->pw_name
);
153 assert(src
->pw_passwd
);
154 assert(src
->pw_gecos
);
157 const char *shell
= ASSERT_PTR(src
->pw_shell
?: fallback_shell
);
160 strlen(src
->pw_name
) + 1 +
161 strlen(src
->pw_passwd
) + 1 +
162 strlen(src
->pw_gecos
) + 1 +
163 strlen(src
->pw_dir
) + 1 +
166 if (buflen
< required
) {
168 return NSS_STATUS_TRYAGAIN
;
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
);
183 return NSS_STATUS_SUCCESS
;
186 static enum nss_status
copy_synthesized_spwd(
188 const struct spwd
*src
,
189 char *buffer
, size_t buflen
,
194 assert(src
->sp_namp
);
195 assert(src
->sp_pwdp
);
198 strlen(src
->sp_namp
) + 1 +
199 strlen(src
->sp_pwdp
) + 1;
201 if (buflen
< required
) {
203 return NSS_STATUS_TRYAGAIN
;
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
);
215 return NSS_STATUS_SUCCESS
;
218 static enum nss_status
copy_synthesized_group(
220 const struct group
*src
,
221 char *buffer
, size_t buflen
,
226 assert(src
->gr_name
);
227 assert(src
->gr_passwd
);
229 assert(!*src
->gr_mem
); /* Our synthesized records' gr_mem is always just NULL... */
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! */
236 if (buflen
< ALIGN(required
)) {
238 return NSS_STATUS_TRYAGAIN
;
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
;
251 return NSS_STATUS_SUCCESS
;
254 static enum nss_status
copy_synthesized_sgrp(
256 const struct sgrp
*src
,
257 char *buffer
, size_t buflen
,
262 assert(src
->sg_namp
);
263 assert(src
->sg_passwd
);
266 strlen(src
->sg_namp
) + 1 +
267 strlen(src
->sg_passwd
) + 1;
269 if (buflen
< required
) {
271 return NSS_STATUS_TRYAGAIN
;
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
);
283 return NSS_STATUS_SUCCESS
;
286 enum nss_status
_nss_systemd_getpwnam_r(
289 char *buffer
, size_t buflen
,
292 enum nss_status status
;
296 NSS_ENTRYPOINT_BEGIN
;
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
;
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) {
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
);
316 if (streq(name
, nobody_passwd
.pw_name
)) {
317 if (!synthesize_nobody())
318 return NSS_STATUS_NOTFOUND
;
320 return copy_synthesized_passwd(pwd
, &nobody_passwd
,
322 buffer
, buflen
, errnop
);
325 } else if (STR_IN_SET(name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
326 return NSS_STATUS_NOTFOUND
;
328 status
= userdb_getpwnam(name
, pwd
, buffer
, buflen
, &e
);
329 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
338 enum nss_status
_nss_systemd_getpwuid_r(
341 char *buffer
, size_t buflen
,
344 enum nss_status status
;
348 NSS_ENTRYPOINT_BEGIN
;
353 if (!uid_is_valid(uid
))
354 return NSS_STATUS_NOTFOUND
;
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) {
359 if (uid
== root_passwd
.pw_uid
)
360 return copy_synthesized_passwd(pwd
, &root_passwd
,
361 default_root_shell(NULL
),
362 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
,
370 buffer
, buflen
, errnop
);
373 } else if (uid
== root_passwd
.pw_uid
|| uid
== nobody_passwd
.pw_uid
)
374 return NSS_STATUS_NOTFOUND
;
376 status
= userdb_getpwuid(uid
, pwd
, buffer
, buflen
, &e
);
377 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
386 enum nss_status
_nss_systemd_getspnam_r(
389 char *buffer
, size_t buflen
,
392 enum nss_status status
;
396 NSS_ENTRYPOINT_BEGIN
;
402 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
403 return NSS_STATUS_NOTFOUND
;
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) {
408 if (streq(name
, root_spwd
.sp_namp
))
409 return copy_synthesized_spwd(spwd
, &root_spwd
, buffer
, buflen
, errnop
);
411 if (streq(name
, nobody_spwd
.sp_namp
)) {
412 if (!synthesize_nobody())
413 return NSS_STATUS_NOTFOUND
;
415 return copy_synthesized_spwd(spwd
, &nobody_spwd
, buffer
, buflen
, errnop
);
418 } else if (STR_IN_SET(name
, root_spwd
.sp_namp
, nobody_spwd
.sp_namp
))
419 return NSS_STATUS_NOTFOUND
;
421 status
= userdb_getspnam(name
, spwd
, buffer
, buflen
, &e
);
422 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
431 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
433 enum nss_status
_nss_systemd_getgrnam_r(
436 char *buffer
, size_t buflen
,
439 enum nss_status status
;
443 NSS_ENTRYPOINT_BEGIN
;
449 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
450 return NSS_STATUS_NOTFOUND
;
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) {
455 if (streq(name
, root_group
.gr_name
))
456 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
458 if (streq(name
, nobody_group
.gr_name
)) {
459 if (!synthesize_nobody())
460 return NSS_STATUS_NOTFOUND
;
462 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
465 } else if (STR_IN_SET(name
, root_group
.gr_name
, nobody_group
.gr_name
))
466 return NSS_STATUS_NOTFOUND
;
468 status
= userdb_getgrnam(name
, gr
, buffer
, buflen
, &e
);
469 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
478 enum nss_status
_nss_systemd_getgrgid_r(
481 char *buffer
, size_t buflen
,
484 enum nss_status status
;
488 NSS_ENTRYPOINT_BEGIN
;
493 if (!gid_is_valid(gid
))
494 return NSS_STATUS_NOTFOUND
;
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) {
499 if (gid
== root_group
.gr_gid
)
500 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
502 if (gid
== nobody_group
.gr_gid
) {
503 if (!synthesize_nobody())
504 return NSS_STATUS_NOTFOUND
;
506 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
509 } else if (gid
== root_group
.gr_gid
|| gid
== nobody_group
.gr_gid
)
510 return NSS_STATUS_NOTFOUND
;
512 status
= userdb_getgrgid(gid
, gr
, buffer
, buflen
, &e
);
513 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
522 enum nss_status
_nss_systemd_getsgnam_r(
525 char *buffer
, size_t buflen
,
528 enum nss_status status
;
532 NSS_ENTRYPOINT_BEGIN
;
538 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
539 return NSS_STATUS_NOTFOUND
;
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) {
544 if (streq(name
, root_sgrp
.sg_namp
))
545 return copy_synthesized_sgrp(sgrp
, &root_sgrp
, buffer
, buflen
, errnop
);
547 if (streq(name
, nobody_sgrp
.sg_namp
)) {
548 if (!synthesize_nobody())
549 return NSS_STATUS_NOTFOUND
;
551 return copy_synthesized_sgrp(sgrp
, &nobody_sgrp
, buffer
, buflen
, errnop
);
554 } else if (STR_IN_SET(name
, root_sgrp
.sg_namp
, nobody_sgrp
.sg_namp
))
555 return NSS_STATUS_NOTFOUND
;
557 status
= userdb_getsgnam(name
, sgrp
, buffer
, buflen
, &e
);
558 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
567 static enum nss_status
nss_systemd_endent(GetentData
*p
) {
569 NSS_ENTRYPOINT_BEGIN
;
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. */
576 p
->iterator
= userdb_iterator_free(p
->iterator
);
577 p
->by_membership
= false;
579 return NSS_STATUS_SUCCESS
;
582 enum nss_status
_nss_systemd_endpwent(void) {
583 return nss_systemd_endent(&getpwent_data
);
586 enum nss_status
_nss_systemd_endspent(void) {
587 return nss_systemd_endent(&getspent_data
);
590 enum nss_status
_nss_systemd_endgrent(void) {
591 return nss_systemd_endent(&getgrent_data
);
594 enum nss_status
_nss_systemd_endsgent(void) {
595 return nss_systemd_endent(&getsgent_data
);
598 enum nss_status
_nss_systemd_setpwent(int stayopen
) {
602 NSS_ENTRYPOINT_BEGIN
;
604 if (_nss_systemd_is_blocked())
605 return NSS_STATUS_NOTFOUND
;
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. */
610 getpwent_data
.iterator
= userdb_iterator_free(getpwent_data
.iterator
);
611 getpwent_data
.by_membership
= false;
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
618 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getpwent_data
.iterator
);
619 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
622 enum nss_status
_nss_systemd_setgrent(int stayopen
) {
626 NSS_ENTRYPOINT_BEGIN
;
628 if (_nss_systemd_is_blocked())
629 return NSS_STATUS_NOTFOUND
;
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. */
634 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
635 getgrent_data
.by_membership
= false;
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
;
642 enum nss_status
_nss_systemd_setspent(int stayopen
) {
646 NSS_ENTRYPOINT_BEGIN
;
648 if (_nss_systemd_is_blocked())
649 return NSS_STATUS_NOTFOUND
;
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. */
654 getspent_data
.iterator
= userdb_iterator_free(getspent_data
.iterator
);
655 getspent_data
.by_membership
= false;
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
;
662 enum nss_status
_nss_systemd_setsgent(int stayopen
) {
666 NSS_ENTRYPOINT_BEGIN
;
668 if (_nss_systemd_is_blocked())
669 return NSS_STATUS_NOTFOUND
;
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. */
674 getsgent_data
.iterator
= userdb_iterator_free(getsgent_data
.iterator
);
675 getsgent_data
.by_membership
= false;
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
;
682 enum nss_status
_nss_systemd_getpwent_r(
683 struct passwd
*result
,
684 char *buffer
, size_t buflen
,
687 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
691 NSS_ENTRYPOINT_BEGIN
;
696 if (_nss_systemd_is_blocked())
697 return NSS_STATUS_NOTFOUND
;
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. */
702 if (!getpwent_data
.iterator
) {
705 return NSS_STATUS_UNAVAIL
;
708 r
= userdb_iterator_get(getpwent_data
.iterator
, &ur
);
710 return NSS_STATUS_NOTFOUND
;
714 return NSS_STATUS_UNAVAIL
;
717 r
= nss_pack_user_record(ur
, result
, buffer
, buflen
);
721 return NSS_STATUS_TRYAGAIN
;
724 return NSS_STATUS_SUCCESS
;
727 enum nss_status
_nss_systemd_getgrent_r(
728 struct group
*result
,
729 char *buffer
, size_t buflen
,
732 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
733 _cleanup_free_
char **members
= NULL
;
737 NSS_ENTRYPOINT_BEGIN
;
742 if (_nss_systemd_is_blocked())
743 return NSS_STATUS_NOTFOUND
;
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. */
748 if (!getgrent_data
.iterator
) {
751 return NSS_STATUS_UNAVAIL
;
754 if (!getgrent_data
.by_membership
) {
755 r
= groupdb_iterator_get(getgrent_data
.iterator
, &gr
);
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
);
765 r
= membershipdb_all(nss_glue_userdb_flags(), &getgrent_data
.iterator
);
766 if (r
< 0 && r
!= -ESRCH
) {
769 return NSS_STATUS_UNAVAIL
;
772 getgrent_data
.by_membership
= true;
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
) {
782 return NSS_STATUS_UNAVAIL
;
787 if (getgrent_data
.by_membership
) {
788 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
790 if (!getgrent_data
.iterator
)
791 return NSS_STATUS_NOTFOUND
;
794 _cleanup_free_
char *user_name
= NULL
, *group_name
= NULL
;
796 r
= membershipdb_iterator_get(getgrent_data
.iterator
, &user_name
, &group_name
);
798 return NSS_STATUS_NOTFOUND
;
802 return NSS_STATUS_UNAVAIL
;
805 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
807 if (STR_IN_SET(group_name
, root_group
.gr_name
, nobody_group
.gr_name
))
810 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
812 r
= _nss_systemd_block(true);
816 return NSS_STATUS_UNAVAIL
;
822 r
= nss_group_record_by_name(group_name
, false, &gr
);
826 log_debug_errno(r
, "Failed to do NSS check for group '%s', ignoring: %m", group_name
);
830 members
= strv_new(user_name
);
834 return NSS_STATUS_TRYAGAIN
;
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. */
844 r
= nss_pack_group_record(gr
, members
, result
, buffer
, buflen
);
848 return NSS_STATUS_TRYAGAIN
;
851 return NSS_STATUS_SUCCESS
;
854 enum nss_status
_nss_systemd_getspent_r(
856 char *buffer
, size_t buflen
,
859 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
863 NSS_ENTRYPOINT_BEGIN
;
868 if (_nss_systemd_is_blocked())
869 return NSS_STATUS_NOTFOUND
;
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. */
874 if (!getspent_data
.iterator
) {
877 return NSS_STATUS_UNAVAIL
;
881 r
= userdb_iterator_get(getspent_data
.iterator
, &ur
);
883 return NSS_STATUS_NOTFOUND
;
887 return NSS_STATUS_UNAVAIL
;
890 if (!ur
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
893 ur
= user_record_unref(ur
);
896 r
= nss_pack_user_record_shadow(ur
, result
, buffer
, buflen
);
900 return NSS_STATUS_TRYAGAIN
;
903 return NSS_STATUS_SUCCESS
;
906 enum nss_status
_nss_systemd_getsgent_r(
908 char *buffer
, size_t buflen
,
911 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
915 NSS_ENTRYPOINT_BEGIN
;
920 if (_nss_systemd_is_blocked())
921 return NSS_STATUS_NOTFOUND
;
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. */
926 if (!getsgent_data
.iterator
) {
929 return NSS_STATUS_UNAVAIL
;
933 r
= groupdb_iterator_get(getsgent_data
.iterator
, &gr
);
935 return NSS_STATUS_NOTFOUND
;
939 return NSS_STATUS_UNAVAIL
;
942 if (!gr
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
945 gr
= group_record_unref(gr
);
948 r
= nss_pack_group_record_shadow(gr
, result
, buffer
, buflen
);
952 return NSS_STATUS_TRYAGAIN
;
955 return NSS_STATUS_SUCCESS
;
958 enum nss_status
_nss_systemd_initgroups_dyn(
959 const char *user_name
,
967 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
972 NSS_ENTRYPOINT_BEGIN
;
980 if (!valid_user_group_name(user_name
, VALID_USER_RELAX
))
981 return NSS_STATUS_NOTFOUND
;
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
;
987 if (_nss_systemd_is_blocked())
988 return NSS_STATUS_NOTFOUND
;
990 r
= membershipdb_by_user(user_name
, nss_glue_userdb_flags(), &iterator
);
994 return NSS_STATUS_UNAVAIL
;
998 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
999 _cleanup_free_
char *group_name
= NULL
;
1001 r
= membershipdb_iterator_get(iterator
, NULL
, &group_name
);
1007 return NSS_STATUS_UNAVAIL
;
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. */
1013 r
= groupdb_by_name(group_name
, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS
) | USERDB_SUPPRESS_SHADOW
, &g
);
1017 log_debug_errno(r
, "Failed to resolve group '%s', ignoring: %m", group_name
);
1024 if (*start
>= *size
) {
1028 if (limit
> 0 && *size
>= limit
) /* Reached the limit.? */
1031 if (*size
> LONG_MAX
/2) { /* Check for overflow */
1034 return NSS_STATUS_TRYAGAIN
;
1037 new_size
= *start
* 2;
1038 if (limit
> 0 && new_size
> limit
)
1041 /* Enlarge buffer */
1042 new_groups
= reallocarray(*groupsp
, new_size
, sizeof(**groupsp
));
1046 return NSS_STATUS_TRYAGAIN
;
1049 *groupsp
= new_groups
;
1053 (*groupsp
)[(*start
)++] = g
->gid
;
1057 return any
? NSS_STATUS_SUCCESS
: NSS_STATUS_NOTFOUND
;
1060 static thread_local
unsigned _blocked
= 0;
1062 _public_
int _nss_systemd_block(bool b
) {
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`. */
1068 if (_blocked
>= UINT_MAX
)
1079 return b
; /* Return what is passed in, i.e. the new state from the PoV of the caller */
1082 _public_
bool _nss_systemd_is_blocked(void) {
1083 return _blocked
> 0;