1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "errno-util.h"
10 #include "nss-systemd.h"
12 #include "pthread-util.h"
13 #include "signal-util.h"
15 #include "user-record-nss.h"
16 #include "user-util.h"
17 #include "userdb-glue.h"
20 static const struct passwd root_passwd
= {
21 .pw_name
= (char*) "root",
22 .pw_passwd
= (char*) "x", /* see shadow file */
25 .pw_gecos
= (char*) "Super User",
26 .pw_dir
= (char*) "/root",
27 .pw_shell
= (char*) "/bin/sh",
30 static const struct passwd nobody_passwd
= {
31 .pw_name
= (char*) NOBODY_USER_NAME
,
32 .pw_passwd
= (char*) "*", /* locked */
35 .pw_gecos
= (char*) "User Nobody",
36 .pw_dir
= (char*) "/",
37 .pw_shell
= (char*) NOLOGIN
,
40 static const struct group root_group
= {
41 .gr_name
= (char*) "root",
43 .gr_passwd
= (char*) "x", /* see shadow file */
44 .gr_mem
= (char*[]) { NULL
},
47 static const struct group nobody_group
= {
48 .gr_name
= (char*) NOBODY_GROUP_NAME
,
50 .gr_passwd
= (char*) "*", /* locked */
51 .gr_mem
= (char*[]) { NULL
},
54 typedef struct GetentData
{
55 /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really reentrant since it
56 * shares the reading position in the stream with all other threads', we need to protect the data in
57 * UserDBIterator from multithreaded programs which may call setpwent(), getpwent_r(), or endpwent()
58 * simultaneously. So, each function locks the data by using the mutex below. */
59 pthread_mutex_t mutex
;
60 UserDBIterator
*iterator
;
62 /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
67 static GetentData getpwent_data
= {
68 .mutex
= PTHREAD_MUTEX_INITIALIZER
71 static GetentData getgrent_data
= {
72 .mutex
= PTHREAD_MUTEX_INITIALIZER
75 NSS_GETPW_PROTOTYPES(systemd
);
76 NSS_GETGR_PROTOTYPES(systemd
);
77 NSS_PWENT_PROTOTYPES(systemd
);
78 NSS_GRENT_PROTOTYPES(systemd
);
79 NSS_INITGROUPS_PROTOTYPE(systemd
);
81 enum nss_status
_nss_systemd_getpwnam_r(
84 char *buffer
, size_t buflen
,
87 enum nss_status status
;
91 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
97 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
98 * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
99 * invalid user names. */
100 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
101 return NSS_STATUS_NOTFOUND
;
103 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
104 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
106 if (streq(name
, root_passwd
.pw_name
)) {
108 return NSS_STATUS_SUCCESS
;
111 if (streq(name
, nobody_passwd
.pw_name
)) {
112 if (!synthesize_nobody())
113 return NSS_STATUS_NOTFOUND
;
115 *pwd
= nobody_passwd
;
116 return NSS_STATUS_SUCCESS
;
119 } else if (STR_IN_SET(name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
120 return NSS_STATUS_NOTFOUND
;
122 status
= userdb_getpwnam(name
, pwd
, buffer
, buflen
, &e
);
123 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
132 enum nss_status
_nss_systemd_getpwuid_r(
135 char *buffer
, size_t buflen
,
138 enum nss_status status
;
142 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
147 if (!uid_is_valid(uid
))
148 return NSS_STATUS_NOTFOUND
;
150 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
151 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
153 if (uid
== root_passwd
.pw_uid
) {
155 return NSS_STATUS_SUCCESS
;
158 if (uid
== nobody_passwd
.pw_uid
) {
159 if (!synthesize_nobody())
160 return NSS_STATUS_NOTFOUND
;
162 *pwd
= nobody_passwd
;
163 return NSS_STATUS_SUCCESS
;
166 } else if (uid
== root_passwd
.pw_uid
|| uid
== nobody_passwd
.pw_uid
)
167 return NSS_STATUS_NOTFOUND
;
169 status
= userdb_getpwuid(uid
, pwd
, buffer
, buflen
, &e
);
170 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
179 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
181 enum nss_status
_nss_systemd_getgrnam_r(
184 char *buffer
, size_t buflen
,
187 enum nss_status status
;
191 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
197 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
198 return NSS_STATUS_NOTFOUND
;
200 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
201 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
203 if (streq(name
, root_group
.gr_name
)) {
205 return NSS_STATUS_SUCCESS
;
208 if (streq(name
, nobody_group
.gr_name
)) {
209 if (!synthesize_nobody())
210 return NSS_STATUS_NOTFOUND
;
213 return NSS_STATUS_SUCCESS
;
216 } else if (STR_IN_SET(name
, root_group
.gr_name
, nobody_group
.gr_name
))
217 return NSS_STATUS_NOTFOUND
;
219 status
= userdb_getgrnam(name
, gr
, buffer
, buflen
, &e
);
220 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
229 enum nss_status
_nss_systemd_getgrgid_r(
232 char *buffer
, size_t buflen
,
235 enum nss_status status
;
239 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
244 if (!gid_is_valid(gid
))
245 return NSS_STATUS_NOTFOUND
;
247 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
248 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
250 if (gid
== root_group
.gr_gid
) {
252 return NSS_STATUS_SUCCESS
;
255 if (gid
== nobody_group
.gr_gid
) {
256 if (!synthesize_nobody())
257 return NSS_STATUS_NOTFOUND
;
260 return NSS_STATUS_SUCCESS
;
263 } else if (gid
== root_group
.gr_gid
|| gid
== nobody_group
.gr_gid
)
264 return NSS_STATUS_NOTFOUND
;
266 status
= userdb_getgrgid(gid
, gr
, buffer
, buflen
, &e
);
267 if (IN_SET(status
, NSS_STATUS_UNAVAIL
, NSS_STATUS_TRYAGAIN
)) {
276 static enum nss_status
nss_systemd_endent(GetentData
*p
) {
278 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
282 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= NULL
;
283 _l
= pthread_mutex_lock_assert(&p
->mutex
);
285 p
->iterator
= userdb_iterator_free(p
->iterator
);
286 p
->by_membership
= false;
288 return NSS_STATUS_SUCCESS
;
291 enum nss_status
_nss_systemd_endpwent(void) {
292 return nss_systemd_endent(&getpwent_data
);
295 enum nss_status
_nss_systemd_endgrent(void) {
296 return nss_systemd_endent(&getgrent_data
);
299 enum nss_status
_nss_systemd_setpwent(int stayopen
) {
301 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
303 if (_nss_systemd_is_blocked())
304 return NSS_STATUS_NOTFOUND
;
306 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= NULL
;
309 _l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
311 getpwent_data
.iterator
= userdb_iterator_free(getpwent_data
.iterator
);
312 getpwent_data
.by_membership
= false;
314 /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
315 * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
316 * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
317 * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
319 r
= userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getpwent_data
.iterator
);
320 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
323 enum nss_status
_nss_systemd_setgrent(int stayopen
) {
325 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
327 if (_nss_systemd_is_blocked())
328 return NSS_STATUS_NOTFOUND
;
330 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= NULL
;
333 _l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
335 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
336 getpwent_data
.by_membership
= false;
338 /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
339 r
= groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE
, &getgrent_data
.iterator
);
340 return r
< 0 ? NSS_STATUS_UNAVAIL
: NSS_STATUS_SUCCESS
;
343 enum nss_status
_nss_systemd_getpwent_r(
344 struct passwd
*result
,
345 char *buffer
, size_t buflen
,
348 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
352 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
357 if (_nss_systemd_is_blocked())
358 return NSS_STATUS_NOTFOUND
;
360 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= NULL
;
362 _l
= pthread_mutex_lock_assert(&getpwent_data
.mutex
);
364 if (!getpwent_data
.iterator
) {
367 return NSS_STATUS_UNAVAIL
;
370 r
= userdb_iterator_get(getpwent_data
.iterator
, &ur
);
372 return NSS_STATUS_NOTFOUND
;
376 return NSS_STATUS_UNAVAIL
;
379 r
= nss_pack_user_record(ur
, result
, buffer
, buflen
);
383 return NSS_STATUS_TRYAGAIN
;
386 return NSS_STATUS_SUCCESS
;
389 enum nss_status
_nss_systemd_getgrent_r(
390 struct group
*result
,
391 char *buffer
, size_t buflen
,
394 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
395 _cleanup_free_
char **members
= NULL
;
399 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
404 if (_nss_systemd_is_blocked())
405 return NSS_STATUS_NOTFOUND
;
407 _cleanup_(pthread_mutex_unlock_assertp
) pthread_mutex_t
*_l
= NULL
;
409 _l
= pthread_mutex_lock_assert(&getgrent_data
.mutex
);
411 if (!getgrent_data
.iterator
) {
414 return NSS_STATUS_UNAVAIL
;
417 if (!getgrent_data
.by_membership
) {
418 r
= groupdb_iterator_get(getgrent_data
.iterator
, &gr
);
420 /* So we finished iterating native groups now. Let's now continue with iterating
421 * native memberships, and generate additional group entries for any groups
422 * referenced there that are defined in NSS only. This means for those groups there
423 * will be two or more entries generated during iteration, but this is apparently how
424 * this is supposed to work, and what other implementations do too. Clients are
425 * supposed to merge the group records found during iteration automatically. */
426 getgrent_data
.iterator
= userdb_iterator_free(getgrent_data
.iterator
);
428 r
= membershipdb_all(nss_glue_userdb_flags(), &getgrent_data
.iterator
);
432 return NSS_STATUS_UNAVAIL
;
435 getgrent_data
.by_membership
= true;
439 return NSS_STATUS_UNAVAIL
;
440 } else if (!STR_IN_SET(gr
->group_name
, root_group
.gr_name
, nobody_group
.gr_name
)) {
441 r
= membershipdb_by_group_strv(gr
->group_name
, nss_glue_userdb_flags(), &members
);
445 return NSS_STATUS_UNAVAIL
;
450 if (getgrent_data
.by_membership
) {
451 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
454 _cleanup_free_
char *user_name
= NULL
, *group_name
= NULL
;
456 r
= membershipdb_iterator_get(getgrent_data
.iterator
, &user_name
, &group_name
);
458 return NSS_STATUS_NOTFOUND
;
462 return NSS_STATUS_UNAVAIL
;
465 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
467 if (STR_IN_SET(group_name
, root_group
.gr_name
, nobody_group
.gr_name
))
470 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
472 r
= _nss_systemd_block(true);
476 return NSS_STATUS_UNAVAIL
;
482 r
= nss_group_record_by_name(group_name
, false, &gr
);
486 log_debug_errno(r
, "Failed to do NSS check for group '%s', ignoring: %m", group_name
);
490 members
= strv_new(user_name
);
494 return NSS_STATUS_TRYAGAIN
;
497 /* Note that we currently generate one group entry per user that is part of a
498 * group. It's a bit ugly, but equivalent to generating a single entry with a set of
499 * members in them. */
504 r
= nss_pack_group_record(gr
, members
, result
, buffer
, buflen
);
508 return NSS_STATUS_TRYAGAIN
;
511 return NSS_STATUS_SUCCESS
;
514 enum nss_status
_nss_systemd_initgroups_dyn(
515 const char *user_name
,
523 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
528 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
536 if (!valid_user_group_name(user_name
, VALID_USER_RELAX
))
537 return NSS_STATUS_NOTFOUND
;
539 /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
540 if (STR_IN_SET(user_name
, root_passwd
.pw_name
, nobody_passwd
.pw_name
))
541 return NSS_STATUS_NOTFOUND
;
543 if (_nss_systemd_is_blocked())
544 return NSS_STATUS_NOTFOUND
;
546 r
= membershipdb_by_user(user_name
, nss_glue_userdb_flags(), &iterator
);
550 return NSS_STATUS_UNAVAIL
;
554 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
555 _cleanup_free_
char *group_name
= NULL
;
557 r
= membershipdb_iterator_get(iterator
, NULL
, &group_name
);
563 return NSS_STATUS_UNAVAIL
;
566 /* The group might be defined via traditional NSS only, hence let's do a full look-up without
567 * disabling NSS. This means we are operating recursively here. */
569 r
= groupdb_by_name(group_name
, (nss_glue_userdb_flags() & ~USERDB_AVOID_NSS
) | USERDB_AVOID_SHADOW
, &g
);
573 log_debug_errno(r
, "Failed to resolve group '%s', ignoring: %m", group_name
);
580 if (*start
>= *size
) {
584 if (limit
> 0 && *size
>= limit
) /* Reached the limit.? */
587 if (*size
> LONG_MAX
/2) { /* Check for overflow */
590 return NSS_STATUS_TRYAGAIN
;
593 new_size
= *start
* 2;
594 if (limit
> 0 && new_size
> limit
)
598 new_groups
= reallocarray(*groupsp
, new_size
, sizeof(**groupsp
));
602 return NSS_STATUS_TRYAGAIN
;
605 *groupsp
= new_groups
;
609 (*groupsp
)[(*start
)++] = g
->gid
;
613 return any
? NSS_STATUS_SUCCESS
: NSS_STATUS_NOTFOUND
;
616 static thread_local
unsigned _blocked
= 0;
618 _public_
int _nss_systemd_block(bool b
) {
620 /* This blocks recursively: it's blocked for as many times this function is called with `true` until
621 * it is called an equal time with `false`. */
624 if (_blocked
>= UINT_MAX
)
635 return b
; /* Return what is passed in, i.e. the new state from the PoV of the caller */
638 _public_
bool _nss_systemd_is_blocked(void) {