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_once(void) {
120 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
121 assert_se(pthread_once(&once
, log_parse_environment_variables
) == 0);
124 #define NSS_ENTRYPOINT_BEGIN \
125 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
128 NSS_GETPW_PROTOTYPES(systemd
);
129 NSS_GETSP_PROTOTYPES(systemd
);
130 NSS_GETGR_PROTOTYPES(systemd
);
131 NSS_GETSG_PROTOTYPES(systemd
);
132 NSS_PWENT_PROTOTYPES(systemd
);
133 NSS_SPENT_PROTOTYPES(systemd
);
134 NSS_GRENT_PROTOTYPES(systemd
);
135 NSS_SGENT_PROTOTYPES(systemd
);
136 NSS_INITGROUPS_PROTOTYPE(systemd
);
138 /* Since our NSS functions implement reentrant glibc APIs, we have to guarantee
139 * all the string pointers we return point into the buffer provided by the
140 * caller, not into our own static memory. */
142 static enum nss_status
copy_synthesized_passwd(
144 const struct passwd
*src
,
145 char *buffer
, size_t buflen
,
152 assert(src
->pw_name
);
153 assert(src
->pw_passwd
);
154 assert(src
->pw_gecos
);
156 assert(src
->pw_shell
);
158 required
= strlen(src
->pw_name
) + 1;
159 required
+= strlen(src
->pw_passwd
) + 1;
160 required
+= strlen(src
->pw_gecos
) + 1;
161 required
+= strlen(src
->pw_dir
) + 1;
162 required
+= strlen(src
->pw_shell
) + 1;
164 if (buflen
< required
) {
166 return NSS_STATUS_TRYAGAIN
;
173 /* String fields point into the user-provided buffer */
174 dest
->pw_name
= buffer
;
175 dest
->pw_passwd
= stpcpy(dest
->pw_name
, src
->pw_name
) + 1;
176 dest
->pw_gecos
= stpcpy(dest
->pw_passwd
, src
->pw_passwd
) + 1;
177 dest
->pw_dir
= stpcpy(dest
->pw_gecos
, src
->pw_gecos
) + 1;
178 dest
->pw_shell
= stpcpy(dest
->pw_dir
, src
->pw_dir
) + 1;
179 strcpy(dest
->pw_shell
, src
->pw_shell
);
181 return NSS_STATUS_SUCCESS
;
184 static enum nss_status
copy_synthesized_spwd(
186 const struct spwd
*src
,
187 char *buffer
, size_t buflen
,
194 assert(src
->sp_namp
);
195 assert(src
->sp_pwdp
);
197 required
= strlen(src
->sp_namp
) + 1;
198 required
+= strlen(src
->sp_pwdp
) + 1;
200 if (buflen
< required
) {
202 return NSS_STATUS_TRYAGAIN
;
209 /* String fields point into the user-provided buffer */
210 dest
->sp_namp
= buffer
;
211 dest
->sp_pwdp
= stpcpy(dest
->sp_namp
, src
->sp_namp
) + 1;
212 strcpy(dest
->sp_pwdp
, src
->sp_pwdp
);
214 return NSS_STATUS_SUCCESS
;
217 static enum nss_status
copy_synthesized_group(
219 const struct group
*src
,
220 char *buffer
, size_t buflen
,
227 assert(src
->gr_name
);
228 assert(src
->gr_passwd
);
230 assert(!*src
->gr_mem
); /* Our synthesized records' gr_mem is always just NULL... */
232 required
= strlen(src
->gr_name
) + 1;
233 required
+= strlen(src
->gr_passwd
) + 1;
234 required
+= 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
,
264 assert(src
->sg_namp
);
265 assert(src
->sg_passwd
);
267 required
= strlen(src
->sg_namp
) + 1;
268 required
+= strlen(src
->sg_passwd
) + 1;
270 if (buflen
< required
) {
272 return NSS_STATUS_TRYAGAIN
;
279 /* String fields point into the user-provided buffer */
280 dest
->sg_namp
= buffer
;
281 dest
->sg_passwd
= stpcpy(dest
->sg_namp
, src
->sg_namp
) + 1;
282 strcpy(dest
->sg_passwd
, src
->sg_passwd
);
284 return NSS_STATUS_SUCCESS
;
287 enum nss_status
_nss_systemd_getpwnam_r(
290 char *buffer
, size_t buflen
,
293 enum nss_status status
;
297 NSS_ENTRYPOINT_BEGIN
;
303 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
304 * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
305 * invalid user names. */
306 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
307 return NSS_STATUS_NOTFOUND
;
309 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
310 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
312 if (streq(name
, root_passwd
.pw_name
))
313 return copy_synthesized_passwd(pwd
, &root_passwd
, buffer
, buflen
, errnop
);
315 if (streq(name
, nobody_passwd
.pw_name
)) {
316 if (!synthesize_nobody())
317 return NSS_STATUS_NOTFOUND
;
319 return copy_synthesized_passwd(pwd
, &nobody_passwd
, buffer
, buflen
, errnop
);
322 } else if (STR_IN_SET(name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
323 return NSS_STATUS_NOTFOUND
;
325 status
= userdb_getpwnam(name
, pwd
, buffer
, buflen
, &e
);
326 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
335 enum nss_status
_nss_systemd_getpwuid_r(
338 char *buffer
, size_t buflen
,
341 enum nss_status status
;
345 NSS_ENTRYPOINT_BEGIN
;
350 if (!uid_is_valid(uid
))
351 return NSS_STATUS_NOTFOUND
;
353 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
354 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
356 if (uid
== root_passwd
.pw_uid
)
357 return copy_synthesized_passwd(pwd
, &root_passwd
, buffer
, buflen
, errnop
);
359 if (uid
== nobody_passwd
.pw_uid
) {
360 if (!synthesize_nobody())
361 return NSS_STATUS_NOTFOUND
;
363 return copy_synthesized_passwd(pwd
, &nobody_passwd
, buffer
, buflen
, errnop
);
366 } else if (uid
== root_passwd
.pw_uid
|| uid
== nobody_passwd
.pw_uid
)
367 return NSS_STATUS_NOTFOUND
;
369 status
= userdb_getpwuid(uid
, pwd
, buffer
, buflen
, &e
);
370 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
379 enum nss_status
_nss_systemd_getspnam_r(
382 char *buffer
, size_t buflen
,
385 enum nss_status status
;
389 NSS_ENTRYPOINT_BEGIN
;
395 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
396 return NSS_STATUS_NOTFOUND
;
398 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
399 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
401 if (streq(name
, root_spwd
.sp_namp
))
402 return copy_synthesized_spwd(spwd
, &root_spwd
, buffer
, buflen
, errnop
);
404 if (streq(name
, nobody_spwd
.sp_namp
)) {
405 if (!synthesize_nobody())
406 return NSS_STATUS_NOTFOUND
;
408 return copy_synthesized_spwd(spwd
, &nobody_spwd
, buffer
, buflen
, errnop
);
411 } else if (STR_IN_SET(name
, root_spwd
.sp_namp
, nobody_spwd
.sp_namp
))
412 return NSS_STATUS_NOTFOUND
;
414 status
= userdb_getspnam(name
, spwd
, buffer
, buflen
, &e
);
415 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
424 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
426 enum nss_status
_nss_systemd_getgrnam_r(
429 char *buffer
, size_t buflen
,
432 enum nss_status status
;
436 NSS_ENTRYPOINT_BEGIN
;
442 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
443 return NSS_STATUS_NOTFOUND
;
445 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
446 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
448 if (streq(name
, root_group
.gr_name
))
449 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
451 if (streq(name
, nobody_group
.gr_name
)) {
452 if (!synthesize_nobody())
453 return NSS_STATUS_NOTFOUND
;
455 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
458 } else if (STR_IN_SET(name
, root_group
.gr_name
, nobody_group
.gr_name
))
459 return NSS_STATUS_NOTFOUND
;
461 status
= userdb_getgrnam(name
, gr
, buffer
, buflen
, &e
);
462 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
471 enum nss_status
_nss_systemd_getgrgid_r(
474 char *buffer
, size_t buflen
,
477 enum nss_status status
;
481 NSS_ENTRYPOINT_BEGIN
;
486 if (!gid_is_valid(gid
))
487 return NSS_STATUS_NOTFOUND
;
489 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
490 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
492 if (gid
== root_group
.gr_gid
)
493 return copy_synthesized_group(gr
, &root_group
, buffer
, buflen
, errnop
);
495 if (gid
== nobody_group
.gr_gid
) {
496 if (!synthesize_nobody())
497 return NSS_STATUS_NOTFOUND
;
499 return copy_synthesized_group(gr
, &nobody_group
, buffer
, buflen
, errnop
);
502 } else if (gid
== root_group
.gr_gid
|| gid
== nobody_group
.gr_gid
)
503 return NSS_STATUS_NOTFOUND
;
505 status
= userdb_getgrgid(gid
, gr
, buffer
, buflen
, &e
);
506 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
515 enum nss_status
_nss_systemd_getsgnam_r(
518 char *buffer
, size_t buflen
,
521 enum nss_status status
;
525 NSS_ENTRYPOINT_BEGIN
;
531 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
532 return NSS_STATUS_NOTFOUND
;
534 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
535 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
537 if (streq(name
, root_sgrp
.sg_namp
))
538 return copy_synthesized_sgrp(sgrp
, &root_sgrp
, buffer
, buflen
, errnop
);
540 if (streq(name
, nobody_sgrp
.sg_namp
)) {
541 if (!synthesize_nobody())
542 return NSS_STATUS_NOTFOUND
;
544 return copy_synthesized_sgrp(sgrp
, &nobody_sgrp
, buffer
, buflen
, errnop
);
547 } else if (STR_IN_SET(name
, root_sgrp
.sg_namp
, nobody_sgrp
.sg_namp
))
548 return NSS_STATUS_NOTFOUND
;
550 status
= userdb_getsgnam(name
, sgrp
, buffer
, buflen
, &e
);
551 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
560 static enum nss_status
nss_systemd_endent(GetentData
*p
) {
562 NSS_ENTRYPOINT_BEGIN
;
566 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&p
->mutex
);
567 (void) _l
; /* make llvm shut up about _l not being used. */
569 p
->iterator
= userdb_iterator_free(p
->iterator
);
570 p
->by_membership
= false;
572 return NSS_STATUS_SUCCESS
;
575 enum nss_status
_nss_systemd_endpwent(void) {
576 return nss_systemd_endent(&getpwent_data
);
579 enum nss_status
_nss_systemd_endspent(void) {
580 return nss_systemd_endent(&getspent_data
);
583 enum nss_status
_nss_systemd_endgrent(void) {
584 return nss_systemd_endent(&getgrent_data
);
587 enum nss_status
_nss_systemd_endsgent(void) {
588 return nss_systemd_endent(&getsgent_data
);
591 enum nss_status
_nss_systemd_setpwent(int stayopen
) {
595 NSS_ENTRYPOINT_BEGIN
;
597 if (_nss_systemd_is_blocked())
598 return NSS_STATUS_NOTFOUND
;
600 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
601 (void) _l
; /* make llvm shut up about _l not being used. */
603 getpwent_data
.iterator
= userdb_iterator_free(getpwent_data
.iterator
);
604 getpwent_data
.by_membership
= false;
606 /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
607 * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
608 * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
609 * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
611 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getpwent_data
.iterator
);
612 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
615 enum nss_status
_nss_systemd_setgrent(int stayopen
) {
619 NSS_ENTRYPOINT_BEGIN
;
621 if (_nss_systemd_is_blocked())
622 return NSS_STATUS_NOTFOUND
;
624 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
625 (void) _l
; /* make llvm shut up about _l not being used. */
627 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
628 getgrent_data
.by_membership
= false;
630 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
631 r
= groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getgrent_data
.iterator
);
632 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
635 enum nss_status
_nss_systemd_setspent(int stayopen
) {
639 NSS_ENTRYPOINT_BEGIN
;
641 if (_nss_systemd_is_blocked())
642 return NSS_STATUS_NOTFOUND
;
644 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getspent_data
.mutex
);
645 (void) _l
; /* make llvm shut up about _l not being used. */
647 getspent_data
.iterator
= userdb_iterator_free(getspent_data
.iterator
);
648 getspent_data
.by_membership
= false;
650 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
651 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getspent_data
.iterator
);
652 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
655 enum nss_status
_nss_systemd_setsgent(int stayopen
) {
659 NSS_ENTRYPOINT_BEGIN
;
661 if (_nss_systemd_is_blocked())
662 return NSS_STATUS_NOTFOUND
;
664 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getsgent_data
.mutex
);
665 (void) _l
; /* make llvm shut up about _l not being used. */
667 getsgent_data
.iterator
= userdb_iterator_free(getsgent_data
.iterator
);
668 getsgent_data
.by_membership
= false;
670 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
671 r
= groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getsgent_data
.iterator
);
672 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
675 enum nss_status
_nss_systemd_getpwent_r(
676 struct passwd
*result
,
677 char *buffer
, size_t buflen
,
680 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
684 NSS_ENTRYPOINT_BEGIN
;
689 if (_nss_systemd_is_blocked())
690 return NSS_STATUS_NOTFOUND
;
692 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
693 (void) _l
; /* make llvm shut up about _l not being used. */
695 if (!getpwent_data
.iterator
) {
698 return NSS_STATUS_UNAVAIL
;
701 r
= userdb_iterator_get(getpwent_data
.iterator
, &ur
);
703 return NSS_STATUS_NOTFOUND
;
707 return NSS_STATUS_UNAVAIL
;
710 r
= nss_pack_user_record(ur
, result
, buffer
, buflen
);
714 return NSS_STATUS_TRYAGAIN
;
717 return NSS_STATUS_SUCCESS
;
720 enum nss_status
_nss_systemd_getgrent_r(
721 struct group
*result
,
722 char *buffer
, size_t buflen
,
725 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
726 _cleanup_free_
char **members
= NULL
;
730 NSS_ENTRYPOINT_BEGIN
;
735 if (_nss_systemd_is_blocked())
736 return NSS_STATUS_NOTFOUND
;
738 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
739 (void) _l
; /* make llvm shut up about _l not being used. */
741 if (!getgrent_data
.iterator
) {
744 return NSS_STATUS_UNAVAIL
;
747 if (!getgrent_data
.by_membership
) {
748 r
= groupdb_iterator_get(getgrent_data
.iterator
, &gr
);
750 /* So we finished iterating native groups now. Let's now continue with iterating
751 * native memberships, and generate additional group entries for any groups
752 * referenced there that are defined in NSS only. This means for those groups there
753 * will be two or more entries generated during iteration, but this is apparently how
754 * this is supposed to work, and what other implementations do too. Clients are
755 * supposed to merge the group records found during iteration automatically. */
756 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
758 r
= membershipdb_all(nss_glue_userdb_flags(), &getgrent_data
.iterator
);
759 if (r
< 0 && r
!= -ESRCH
) {
762 return NSS_STATUS_UNAVAIL
;
765 getgrent_data
.by_membership
= true;
769 return NSS_STATUS_UNAVAIL
;
770 } else if (!STR_IN_SET(gr
->group_name
, root_group
.gr_name
, nobody_group
.gr_name
)) {
771 r
= membershipdb_by_group_strv(gr
->group_name
, nss_glue_userdb_flags(), &members
);
772 if (r
< 0 && r
!= -ESRCH
) {
775 return NSS_STATUS_UNAVAIL
;
780 if (getgrent_data
.by_membership
) {
781 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
783 if (!getgrent_data
.iterator
)
784 return NSS_STATUS_NOTFOUND
;
787 _cleanup_free_
char *user_name
= NULL
, *group_name
= NULL
;
789 r
= membershipdb_iterator_get(getgrent_data
.iterator
, &user_name
, &group_name
);
791 return NSS_STATUS_NOTFOUND
;
795 return NSS_STATUS_UNAVAIL
;
798 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
800 if (STR_IN_SET(group_name
, root_group
.gr_name
, nobody_group
.gr_name
))
803 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
805 r
= _nss_systemd_block(true);
809 return NSS_STATUS_UNAVAIL
;
815 r
= nss_group_record_by_name(group_name
, false, &gr
);
819 log_debug_errno(r
, "Failed to do NSS check for group '%s', ignoring: %m", group_name
);
823 members
= strv_new(user_name
);
827 return NSS_STATUS_TRYAGAIN
;
830 /* Note that we currently generate one group entry per user that is part of a
831 * group. It's a bit ugly, but equivalent to generating a single entry with a set of
832 * members in them. */
837 r
= nss_pack_group_record(gr
, members
, result
, buffer
, buflen
);
841 return NSS_STATUS_TRYAGAIN
;
844 return NSS_STATUS_SUCCESS
;
847 enum nss_status
_nss_systemd_getspent_r(
849 char *buffer
, size_t buflen
,
852 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
856 NSS_ENTRYPOINT_BEGIN
;
861 if (_nss_systemd_is_blocked())
862 return NSS_STATUS_NOTFOUND
;
864 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getspent_data
.mutex
);
865 (void) _l
; /* make llvm shut up about _l not being used. */
867 if (!getspent_data
.iterator
) {
870 return NSS_STATUS_UNAVAIL
;
874 r
= userdb_iterator_get(getspent_data
.iterator
, &ur
);
876 return NSS_STATUS_NOTFOUND
;
880 return NSS_STATUS_UNAVAIL
;
883 if (!ur
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
886 ur
= user_record_unref(ur
);
889 r
= nss_pack_user_record_shadow(ur
, result
, buffer
, buflen
);
893 return NSS_STATUS_TRYAGAIN
;
896 return NSS_STATUS_SUCCESS
;
899 enum nss_status
_nss_systemd_getsgent_r(
901 char *buffer
, size_t buflen
,
904 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
908 NSS_ENTRYPOINT_BEGIN
;
913 if (_nss_systemd_is_blocked())
914 return NSS_STATUS_NOTFOUND
;
916 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= pthread_mutex_lock_assert(&getsgent_data
.mutex
);
917 (void) _l
; /* make llvm shut up about _l not being used. */
919 if (!getsgent_data
.iterator
) {
922 return NSS_STATUS_UNAVAIL
;
926 r
= groupdb_iterator_get(getsgent_data
.iterator
, &gr
);
928 return NSS_STATUS_NOTFOUND
;
932 return NSS_STATUS_UNAVAIL
;
935 if (!gr
->incomplete
) /* don't synthesize shadow records for records where we couldn't read shadow data */
938 gr
= group_record_unref(gr
);
941 r
= nss_pack_group_record_shadow(gr
, result
, buffer
, buflen
);
945 return NSS_STATUS_TRYAGAIN
;
948 return NSS_STATUS_SUCCESS
;
951 enum nss_status
_nss_systemd_initgroups_dyn(
952 const char *user_name
,
960 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
965 NSS_ENTRYPOINT_BEGIN
;
973 if (!valid_user_group_name(user_name
, VALID_USER_RELAX
))
974 return NSS_STATUS_NOTFOUND
;
976 /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
977 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
978 return NSS_STATUS_NOTFOUND
;
980 if (_nss_systemd_is_blocked())
981 return NSS_STATUS_NOTFOUND
;
983 r
= membershipdb_by_user(user_name
, nss_glue_userdb_flags(), &iterator
);
987 return NSS_STATUS_UNAVAIL
;
991 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
992 _cleanup_free_
char *group_name
= NULL
;
994 r
= membershipdb_iterator_get(iterator
, NULL
, &group_name
);
1000 return NSS_STATUS_UNAVAIL
;
1003 /* The group might be defined via traditional NSS only, hence let's do a full look-up without
1004 * disabling NSS. This means we are operating recursively here. */
1006 r
= groupdb_by_name(group_name
, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS
) | USERDB_SUPPRESS_SHADOW
, &g
);
1010 log_debug_errno(r
, "Failed to resolve group '%s', ignoring: %m", group_name
);
1017 if (*start
>= *size
) {
1021 if (limit
> 0 && *size
>= limit
) /* Reached the limit.? */
1024 if (*size
> LONG_MAX
/2) { /* Check for overflow */
1027 return NSS_STATUS_TRYAGAIN
;
1030 new_size
= *start
* 2;
1031 if (limit
> 0 && new_size
> limit
)
1034 /* Enlarge buffer */
1035 new_groups
= reallocarray(*groupsp
, new_size
, sizeof(**groupsp
));
1039 return NSS_STATUS_TRYAGAIN
;
1042 *groupsp
= new_groups
;
1046 (*groupsp
)[(*start
)++] = g
->gid
;
1050 return any
? NSS_STATUS_SUCCESS
: NSS_STATUS_NOTFOUND
;
1053 static thread_local
unsigned _blocked
= 0;
1055 _public_
int _nss_systemd_block(bool b
) {
1057 /* This blocks recursively: it's blocked for as many times this function is called with `true` until
1058 * it is called an equal time with `false`. */
1061 if (_blocked
>= UINT_MAX
)
1072 return b
; /* Return what is passed in, i.e. the new state from the PoV of the caller */
1075 _public_
bool _nss_systemd_is_blocked(void) {
1076 return _blocked
> 0;