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