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