]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-systemd/nss-systemd.c
Use descriptive name for nobody
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
409093fe
LP
2
3#include <nss.h>
12c2c56d 4#include <pthread.h>
47fd7fa6 5#include <string.h>
409093fe 6
409093fe 7#include "env-util.h"
1684c56f 8#include "errno-util.h"
12c2c56d 9#include "fd-util.h"
3339381f 10#include "log.h"
409093fe 11#include "macro.h"
037b0a47 12#include "nss-systemd.h"
409093fe 13#include "nss-util.h"
37bc9dcc 14#include "pthread-util.h"
409093fe 15#include "signal-util.h"
1684c56f 16#include "strv.h"
e60775cb 17#include "user-record-nss.h"
409093fe 18#include "user-util.h"
1684c56f
LP
19#include "userdb-glue.h"
20#include "userdb.h"
9b5eaef3 21
2129011e
LP
22static const struct passwd root_passwd = {
23 .pw_name = (char*) "root",
53c25ac9 24 .pw_passwd = (char*) PASSWORD_SEE_SHADOW,
2129011e
LP
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
f43a19ec
LP
32static 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
2129011e
LP
44static const struct passwd nobody_passwd = {
45 .pw_name = (char*) NOBODY_USER_NAME,
53c25ac9 46 .pw_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
3a664727
LP
47 .pw_uid = UID_NOBODY,
48 .pw_gid = GID_NOBODY,
94b84a07 49 .pw_gecos = (char*) "Kernel Overflow User",
2129011e 50 .pw_dir = (char*) "/",
6db90462 51 .pw_shell = (char*) NOLOGIN,
2129011e
LP
52};
53
f43a19ec
LP
54static 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
2129011e
LP
66static const struct group root_group = {
67 .gr_name = (char*) "root",
68 .gr_gid = 0,
53c25ac9 69 .gr_passwd = (char*) PASSWORD_SEE_SHADOW,
2129011e
LP
70 .gr_mem = (char*[]) { NULL },
71};
72
f43a19ec
LP
73static const struct sgrp root_sgrp = {
74 .sg_namp = (char*) "root",
75 .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
76};
77
2129011e
LP
78static const struct group nobody_group = {
79 .gr_name = (char*) NOBODY_GROUP_NAME,
3a664727 80 .gr_gid = GID_NOBODY,
53c25ac9 81 .gr_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
2129011e
LP
82 .gr_mem = (char*[]) { NULL },
83};
84
f43a19ec
LP
85static const struct sgrp nobody_sgrp = {
86 .sg_namp = (char*) NOBODY_GROUP_NAME,
87 .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
88};
89
1684c56f
LP
90typedef 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;
12c2c56d 97
1684c56f
LP
98 /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
99 * otherwise. */
100 bool by_membership;
101} GetentData;
12c2c56d 102
1684c56f 103static GetentData getpwent_data = {
f43a19ec 104 .mutex = PTHREAD_MUTEX_INITIALIZER,
12c2c56d
YW
105};
106
1684c56f 107static GetentData getgrent_data = {
f43a19ec
LP
108 .mutex = PTHREAD_MUTEX_INITIALIZER,
109};
110
111static GetentData getspent_data = {
112 .mutex = PTHREAD_MUTEX_INITIALIZER,
113};
114
115static GetentData getsgent_data = {
116 .mutex = PTHREAD_MUTEX_INITIALIZER,
12c2c56d
YW
117};
118
3339381f
ZJS
119static void setup_logging_once(void) {
120 static pthread_once_t once = PTHREAD_ONCE_INIT;
a7d15a24 121 assert_se(pthread_once(&once, log_parse_environment_variables) == 0);
3339381f
ZJS
122}
123
124#define NSS_ENTRYPOINT_BEGIN \
125 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
126 setup_logging_once()
127
409093fe 128NSS_GETPW_PROTOTYPES(systemd);
f43a19ec 129NSS_GETSP_PROTOTYPES(systemd);
409093fe 130NSS_GETGR_PROTOTYPES(systemd);
f43a19ec 131NSS_GETSG_PROTOTYPES(systemd);
1684c56f 132NSS_PWENT_PROTOTYPES(systemd);
f43a19ec 133NSS_SPENT_PROTOTYPES(systemd);
1684c56f 134NSS_GRENT_PROTOTYPES(systemd);
f43a19ec 135NSS_SGENT_PROTOTYPES(systemd);
1684c56f 136NSS_INITGROUPS_PROTOTYPE(systemd);
fd63e712 137
47fd7fa6
MC
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. */
141
142static enum nss_status copy_synthesized_passwd(
143 struct passwd *dest,
144 const struct passwd *src,
145 char *buffer, size_t buflen,
146 int *errnop) {
147
148 size_t required;
149
150 assert(dest);
151 assert(src);
152 assert(src->pw_name);
153 assert(src->pw_passwd);
154 assert(src->pw_gecos);
155 assert(src->pw_dir);
156 assert(src->pw_shell);
157
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;
163
164 if (buflen < required) {
165 *errnop = ERANGE;
166 return NSS_STATUS_TRYAGAIN;
167 }
168
169 assert(buffer);
170
171 *dest = *src;
172
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);
180
181 return NSS_STATUS_SUCCESS;
182}
183
184static enum nss_status copy_synthesized_spwd(
185 struct spwd *dest,
186 const struct spwd *src,
187 char *buffer, size_t buflen,
188 int *errnop) {
189
190 size_t required;
191
192 assert(dest);
193 assert(src);
194 assert(src->sp_namp);
195 assert(src->sp_pwdp);
196
197 required = strlen(src->sp_namp) + 1;
198 required += strlen(src->sp_pwdp) + 1;
199
200 if (buflen < required) {
201 *errnop = ERANGE;
202 return NSS_STATUS_TRYAGAIN;
203 }
204
205 assert(buffer);
206
207 *dest = *src;
208
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);
213
214 return NSS_STATUS_SUCCESS;
215}
216
217static enum nss_status copy_synthesized_group(
218 struct group *dest,
219 const struct group *src,
220 char *buffer, size_t buflen,
221 int *errnop) {
222
223 size_t required;
224
225 assert(dest);
226 assert(src);
227 assert(src->gr_name);
228 assert(src->gr_passwd);
229 assert(src->gr_mem);
230 assert(!*src->gr_mem); /* Our synthesized records' gr_mem is always just NULL... */
231
232 required = strlen(src->gr_name) + 1;
233 required += strlen(src->gr_passwd) + 1;
1e65eb8f 234 required += sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
47fd7fa6 235
420a35c1 236 if (buflen < ALIGN(required)) {
47fd7fa6
MC
237 *errnop = ERANGE;
238 return NSS_STATUS_TRYAGAIN;
239 }
240
241 assert(buffer);
242
243 *dest = *src;
244
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;
420a35c1 248 dest->gr_mem = ALIGN_PTR(stpcpy(dest->gr_passwd, src->gr_passwd) + 1);
47fd7fa6
MC
249 *dest->gr_mem = NULL;
250
251 return NSS_STATUS_SUCCESS;
252}
253
254static enum nss_status copy_synthesized_sgrp(
255 struct sgrp *dest,
256 const struct sgrp *src,
257 char *buffer, size_t buflen,
258 int *errnop) {
259
260 size_t required;
261
262 assert(dest);
263 assert(src);
264 assert(src->sg_namp);
265 assert(src->sg_passwd);
266
267 required = strlen(src->sg_namp) + 1;
268 required += strlen(src->sg_passwd) + 1;
269
270 if (buflen < required) {
271 *errnop = ERANGE;
272 return NSS_STATUS_TRYAGAIN;
273 }
274
275 assert(buffer);
276
277 *dest = *src;
278
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);
283
284 return NSS_STATUS_SUCCESS;
285}
286
409093fe
LP
287enum nss_status _nss_systemd_getpwnam_r(
288 const char *name,
289 struct passwd *pwd,
290 char *buffer, size_t buflen,
291 int *errnop) {
292
1684c56f
LP
293 enum nss_status status;
294 int e;
409093fe 295
06202b9e 296 PROTECT_ERRNO;
3339381f 297 NSS_ENTRYPOINT_BEGIN;
409093fe
LP
298
299 assert(name);
300 assert(pwd);
1684c56f 301 assert(errnop);
409093fe 302
1684c56f
LP
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. */
7a8867ab 306 if (!valid_user_group_name(name, VALID_USER_RELAX))
06202b9e 307 return NSS_STATUS_NOTFOUND;
2129011e
LP
308
309 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
71e0accc 310 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
1684c56f 311
47fd7fa6
MC
312 if (streq(name, root_passwd.pw_name))
313 return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
409093fe 314
1684c56f
LP
315 if (streq(name, nobody_passwd.pw_name)) {
316 if (!synthesize_nobody())
06202b9e 317 return NSS_STATUS_NOTFOUND;
fd63e712 318
47fd7fa6 319 return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
fd63e712
LP
320 }
321
1684c56f
LP
322 } else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
323 return NSS_STATUS_NOTFOUND;
409093fe 324
1684c56f
LP
325 status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
326 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
cdccd29f 327 UNPROTECT_ERRNO;
1684c56f
LP
328 *errnop = e;
329 return status;
409093fe
LP
330 }
331
1684c56f 332 return status;
409093fe
LP
333}
334
335enum nss_status _nss_systemd_getpwuid_r(
336 uid_t uid,
337 struct passwd *pwd,
338 char *buffer, size_t buflen,
339 int *errnop) {
340
1684c56f
LP
341 enum nss_status status;
342 int e;
409093fe 343
06202b9e 344 PROTECT_ERRNO;
3339381f 345 NSS_ENTRYPOINT_BEGIN;
409093fe 346
1684c56f
LP
347 assert(pwd);
348 assert(errnop);
349
d6c575e3 350 if (!uid_is_valid(uid))
06202b9e 351 return NSS_STATUS_NOTFOUND;
409093fe 352
2129011e 353 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
71e0accc 354 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
1684c56f 355
47fd7fa6
MC
356 if (uid == root_passwd.pw_uid)
357 return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
2129011e 358
1684c56f
LP
359 if (uid == nobody_passwd.pw_uid) {
360 if (!synthesize_nobody())
06202b9e 361 return NSS_STATUS_NOTFOUND;
fd63e712 362
47fd7fa6 363 return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
fd63e712
LP
364 }
365
1684c56f
LP
366 } else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
367 return NSS_STATUS_NOTFOUND;
409093fe 368
1684c56f
LP
369 status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
370 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
cdccd29f 371 UNPROTECT_ERRNO;
1684c56f
LP
372 *errnop = e;
373 return status;
409093fe
LP
374 }
375
1684c56f 376 return status;
409093fe
LP
377}
378
f43a19ec
LP
379enum nss_status _nss_systemd_getspnam_r(
380 const char *name,
381 struct spwd *spwd,
382 char *buffer, size_t buflen,
383 int *errnop) {
384
385 enum nss_status status;
386 int e;
387
388 PROTECT_ERRNO;
389 NSS_ENTRYPOINT_BEGIN;
390
391 assert(name);
392 assert(spwd);
393 assert(errnop);
394
395 if (!valid_user_group_name(name, VALID_USER_RELAX))
396 return NSS_STATUS_NOTFOUND;
397
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) {
400
47fd7fa6
MC
401 if (streq(name, root_spwd.sp_namp))
402 return copy_synthesized_spwd(spwd, &root_spwd, buffer, buflen, errnop);
f43a19ec
LP
403
404 if (streq(name, nobody_spwd.sp_namp)) {
405 if (!synthesize_nobody())
406 return NSS_STATUS_NOTFOUND;
407
47fd7fa6 408 return copy_synthesized_spwd(spwd, &nobody_spwd, buffer, buflen, errnop);
f43a19ec
LP
409 }
410
411 } else if (STR_IN_SET(name, root_spwd.sp_namp, nobody_spwd.sp_namp))
412 return NSS_STATUS_NOTFOUND;
413
414 status = userdb_getspnam(name, spwd, buffer, buflen, &e);
415 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
416 UNPROTECT_ERRNO;
417 *errnop = e;
418 return status;
419 }
420
421 return status;
422}
423
3c3d384a
ZJS
424#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
425
409093fe
LP
426enum nss_status _nss_systemd_getgrnam_r(
427 const char *name,
428 struct group *gr,
429 char *buffer, size_t buflen,
430 int *errnop) {
431
1684c56f
LP
432 enum nss_status status;
433 int e;
409093fe 434
06202b9e 435 PROTECT_ERRNO;
3339381f 436 NSS_ENTRYPOINT_BEGIN;
409093fe
LP
437
438 assert(name);
439 assert(gr);
1684c56f 440 assert(errnop);
409093fe 441
7a8867ab 442 if (!valid_user_group_name(name, VALID_USER_RELAX))
06202b9e 443 return NSS_STATUS_NOTFOUND;
2129011e 444
3e93027b 445 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
71e0accc 446 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
1684c56f 447
47fd7fa6
MC
448 if (streq(name, root_group.gr_name))
449 return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
409093fe 450
1684c56f
LP
451 if (streq(name, nobody_group.gr_name)) {
452 if (!synthesize_nobody())
06202b9e 453 return NSS_STATUS_NOTFOUND;
fd63e712 454
47fd7fa6 455 return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
fd63e712
LP
456 }
457
1684c56f
LP
458 } else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
459 return NSS_STATUS_NOTFOUND;
409093fe 460
1684c56f
LP
461 status = userdb_getgrnam(name, gr, buffer, buflen, &e);
462 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
cdccd29f 463 UNPROTECT_ERRNO;
1684c56f
LP
464 *errnop = e;
465 return status;
409093fe
LP
466 }
467
1684c56f 468 return status;
409093fe
LP
469}
470
471enum nss_status _nss_systemd_getgrgid_r(
472 gid_t gid,
473 struct group *gr,
474 char *buffer, size_t buflen,
475 int *errnop) {
476
1684c56f
LP
477 enum nss_status status;
478 int e;
409093fe 479
06202b9e 480 PROTECT_ERRNO;
3339381f 481 NSS_ENTRYPOINT_BEGIN;
409093fe 482
1684c56f
LP
483 assert(gr);
484 assert(errnop);
485
d6c575e3 486 if (!gid_is_valid(gid))
06202b9e 487 return NSS_STATUS_NOTFOUND;
409093fe 488
2129011e 489 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
71e0accc 490 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
1684c56f 491
47fd7fa6
MC
492 if (gid == root_group.gr_gid)
493 return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
409093fe 494
1684c56f
LP
495 if (gid == nobody_group.gr_gid) {
496 if (!synthesize_nobody())
06202b9e 497 return NSS_STATUS_NOTFOUND;
fd63e712 498
47fd7fa6 499 return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
fd63e712
LP
500 }
501
1684c56f
LP
502 } else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
503 return NSS_STATUS_NOTFOUND;
409093fe 504
1684c56f
LP
505 status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
506 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
cdccd29f 507 UNPROTECT_ERRNO;
1684c56f
LP
508 *errnop = e;
509 return status;
409093fe
LP
510 }
511
1684c56f 512 return status;
12c2c56d
YW
513}
514
f43a19ec
LP
515enum nss_status _nss_systemd_getsgnam_r(
516 const char *name,
517 struct sgrp *sgrp,
518 char *buffer, size_t buflen,
519 int *errnop) {
520
521 enum nss_status status;
522 int e;
523
524 PROTECT_ERRNO;
525 NSS_ENTRYPOINT_BEGIN;
526
527 assert(name);
528 assert(sgrp);
529 assert(errnop);
530
531 if (!valid_user_group_name(name, VALID_USER_RELAX))
532 return NSS_STATUS_NOTFOUND;
533
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) {
536
47fd7fa6
MC
537 if (streq(name, root_sgrp.sg_namp))
538 return copy_synthesized_sgrp(sgrp, &root_sgrp, buffer, buflen, errnop);
f43a19ec
LP
539
540 if (streq(name, nobody_sgrp.sg_namp)) {
541 if (!synthesize_nobody())
542 return NSS_STATUS_NOTFOUND;
543
47fd7fa6 544 return copy_synthesized_sgrp(sgrp, &nobody_sgrp, buffer, buflen, errnop);
f43a19ec
LP
545 }
546
547 } else if (STR_IN_SET(name, root_sgrp.sg_namp, nobody_sgrp.sg_namp))
548 return NSS_STATUS_NOTFOUND;
549
550 status = userdb_getsgnam(name, sgrp, buffer, buflen, &e);
551 if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
552 UNPROTECT_ERRNO;
553 *errnop = e;
554 return status;
555 }
556
557 return status;
558}
559
12c2c56d 560static enum nss_status nss_systemd_endent(GetentData *p) {
06202b9e 561 PROTECT_ERRNO;
3339381f 562 NSS_ENTRYPOINT_BEGIN;
12c2c56d 563
1684c56f
LP
564 assert(p);
565
2d882d35
LP
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. */
37bc9dcc 568
1684c56f
LP
569 p->iterator = userdb_iterator_free(p->iterator);
570 p->by_membership = false;
12c2c56d
YW
571
572 return NSS_STATUS_SUCCESS;
573}
574
575enum nss_status _nss_systemd_endpwent(void) {
576 return nss_systemd_endent(&getpwent_data);
577}
578
f43a19ec
LP
579enum nss_status _nss_systemd_endspent(void) {
580 return nss_systemd_endent(&getspent_data);
581}
582
12c2c56d
YW
583enum nss_status _nss_systemd_endgrent(void) {
584 return nss_systemd_endent(&getgrent_data);
585}
586
f43a19ec
LP
587enum nss_status _nss_systemd_endsgent(void) {
588 return nss_systemd_endent(&getsgent_data);
589}
590
1684c56f 591enum nss_status _nss_systemd_setpwent(int stayopen) {
2d882d35
LP
592 int r;
593
1684c56f 594 PROTECT_ERRNO;
3339381f 595 NSS_ENTRYPOINT_BEGIN;
12c2c56d 596
037b0a47 597 if (_nss_systemd_is_blocked())
1684c56f 598 return NSS_STATUS_NOTFOUND;
12c2c56d 599
2d882d35
LP
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. */
12c2c56d 602
1684c56f
LP
603 getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
604 getpwent_data.by_membership = false;
12c2c56d 605
9494da41
LP
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
610 * user database. */
611 r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator);
37bc9dcc 612 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
1684c56f 613}
12c2c56d 614
1684c56f 615enum nss_status _nss_systemd_setgrent(int stayopen) {
2d882d35
LP
616 int r;
617
1684c56f 618 PROTECT_ERRNO;
3339381f 619 NSS_ENTRYPOINT_BEGIN;
12c2c56d 620
037b0a47 621 if (_nss_systemd_is_blocked())
1684c56f 622 return NSS_STATUS_NOTFOUND;
12c2c56d 623
2d882d35
LP
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. */
1684c56f
LP
626
627 getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
1fdfca4d 628 getgrent_data.by_membership = false;
1684c56f 629
9494da41
LP
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);
37bc9dcc 632 return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
12c2c56d
YW
633}
634
f43a19ec
LP
635enum nss_status _nss_systemd_setspent(int stayopen) {
636 int r;
637
638 PROTECT_ERRNO;
639 NSS_ENTRYPOINT_BEGIN;
640
641 if (_nss_systemd_is_blocked())
642 return NSS_STATUS_NOTFOUND;
643
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. */
646
647 getspent_data.iterator = userdb_iterator_free(getspent_data.iterator);
648 getspent_data.by_membership = false;
649
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;
653}
654
655enum nss_status _nss_systemd_setsgent(int stayopen) {
656 int r;
657
658 PROTECT_ERRNO;
659 NSS_ENTRYPOINT_BEGIN;
660
661 if (_nss_systemd_is_blocked())
662 return NSS_STATUS_NOTFOUND;
663
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. */
666
667 getsgent_data.iterator = userdb_iterator_free(getsgent_data.iterator);
668 getsgent_data.by_membership = false;
669
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;
673}
674
1684c56f
LP
675enum nss_status _nss_systemd_getpwent_r(
676 struct passwd *result,
677 char *buffer, size_t buflen,
678 int *errnop) {
679
680 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
1684c56f 681 int r;
12c2c56d 682
06202b9e 683 PROTECT_ERRNO;
3339381f 684 NSS_ENTRYPOINT_BEGIN;
12c2c56d 685
1684c56f
LP
686 assert(result);
687 assert(errnop);
12c2c56d 688
037b0a47 689 if (_nss_systemd_is_blocked())
1684c56f 690 return NSS_STATUS_NOTFOUND;
12c2c56d 691
2d882d35
LP
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. */
12c2c56d 694
1684c56f
LP
695 if (!getpwent_data.iterator) {
696 UNPROTECT_ERRNO;
697 *errnop = EHOSTDOWN;
37bc9dcc 698 return NSS_STATUS_UNAVAIL;
12c2c56d
YW
699 }
700
1684c56f 701 r = userdb_iterator_get(getpwent_data.iterator, &ur);
37bc9dcc
ZJS
702 if (r == -ESRCH)
703 return NSS_STATUS_NOTFOUND;
1684c56f
LP
704 if (r < 0) {
705 UNPROTECT_ERRNO;
706 *errnop = -r;
37bc9dcc 707 return NSS_STATUS_UNAVAIL;
12c2c56d
YW
708 }
709
1684c56f
LP
710 r = nss_pack_user_record(ur, result, buffer, buflen);
711 if (r < 0) {
712 UNPROTECT_ERRNO;
713 *errnop = -r;
37bc9dcc 714 return NSS_STATUS_TRYAGAIN;
12c2c56d 715 }
12c2c56d 716
37bc9dcc 717 return NSS_STATUS_SUCCESS;
12c2c56d
YW
718}
719
1684c56f
LP
720enum nss_status _nss_systemd_getgrent_r(
721 struct group *result,
722 char *buffer, size_t buflen,
723 int *errnop) {
12c2c56d 724
1684c56f
LP
725 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
726 _cleanup_free_ char **members = NULL;
1684c56f 727 int r;
12c2c56d 728
06202b9e 729 PROTECT_ERRNO;
3339381f 730 NSS_ENTRYPOINT_BEGIN;
12c2c56d
YW
731
732 assert(result);
12c2c56d
YW
733 assert(errnop);
734
037b0a47
LP
735 if (_nss_systemd_is_blocked())
736 return NSS_STATUS_NOTFOUND;
1684c56f 737
2d882d35
LP
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. */
12c2c56d 740
1684c56f
LP
741 if (!getgrent_data.iterator) {
742 UNPROTECT_ERRNO;
743 *errnop = EHOSTDOWN;
37bc9dcc 744 return NSS_STATUS_UNAVAIL;
1684c56f
LP
745 }
746
747 if (!getgrent_data.by_membership) {
748 r = groupdb_iterator_get(getgrent_data.iterator, &gr);
749 if (r == -ESRCH) {
e7e9a9d0 750 /* So we finished iterating native groups now. Let's now continue with iterating
1684c56f
LP
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);
757
758 r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
a1aa41e4 759 if (r < 0 && r != -ESRCH) {
1684c56f
LP
760 UNPROTECT_ERRNO;
761 *errnop = -r;
37bc9dcc 762 return NSS_STATUS_UNAVAIL;
1684c56f
LP
763 }
764
765 getgrent_data.by_membership = true;
766 } else if (r < 0) {
cdccd29f 767 UNPROTECT_ERRNO;
1684c56f 768 *errnop = -r;
37bc9dcc 769 return NSS_STATUS_UNAVAIL;
1684c56f
LP
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);
a1aa41e4 772 if (r < 0 && r != -ESRCH) {
1684c56f
LP
773 UNPROTECT_ERRNO;
774 *errnop = -r;
37bc9dcc 775 return NSS_STATUS_UNAVAIL;
1684c56f 776 }
12c2c56d 777 }
1684c56f 778 }
12c2c56d 779
1684c56f 780 if (getgrent_data.by_membership) {
037b0a47 781 _cleanup_(_nss_systemd_unblockp) bool blocked = false;
1684c56f 782
a1aa41e4
LP
783 if (!getgrent_data.iterator)
784 return NSS_STATUS_NOTFOUND;
785
1684c56f
LP
786 for (;;) {
787 _cleanup_free_ char *user_name = NULL, *group_name = NULL;
788
789 r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name);
37bc9dcc
ZJS
790 if (r == -ESRCH)
791 return NSS_STATUS_NOTFOUND;
1684c56f
LP
792 if (r < 0) {
793 UNPROTECT_ERRNO;
794 *errnop = -r;
37bc9dcc 795 return NSS_STATUS_UNAVAIL;
1684c56f
LP
796 }
797
798 if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
799 continue;
800 if (STR_IN_SET(group_name, root_group.gr_name, nobody_group.gr_name))
801 continue;
802
803 /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
037b0a47
LP
804 if (!blocked) {
805 r = _nss_systemd_block(true);
806 if (r < 0) {
1684c56f 807 UNPROTECT_ERRNO;
037b0a47 808 *errnop = -r;
37bc9dcc 809 return NSS_STATUS_UNAVAIL;
1684c56f 810 }
037b0a47
LP
811
812 blocked = true;
1684c56f
LP
813 }
814
ed30170e 815 r = nss_group_record_by_name(group_name, false, &gr);
1684c56f
LP
816 if (r == -ESRCH)
817 continue;
818 if (r < 0) {
819 log_debug_errno(r, "Failed to do NSS check for group '%s', ignoring: %m", group_name);
820 continue;
821 }
822
823 members = strv_new(user_name);
824 if (!members) {
825 UNPROTECT_ERRNO;
826 *errnop = ENOMEM;
37bc9dcc 827 return NSS_STATUS_TRYAGAIN;
1684c56f
LP
828 }
829
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. */
833 break;
834 }
12c2c56d 835 }
1684c56f
LP
836
837 r = nss_pack_group_record(gr, members, result, buffer, buflen);
838 if (r < 0) {
839 UNPROTECT_ERRNO;
840 *errnop = -r;
37bc9dcc 841 return NSS_STATUS_TRYAGAIN;
12c2c56d
YW
842 }
843
37bc9dcc 844 return NSS_STATUS_SUCCESS;
12c2c56d
YW
845}
846
f43a19ec
LP
847enum nss_status _nss_systemd_getspent_r(
848 struct spwd *result,
849 char *buffer, size_t buflen,
850 int *errnop) {
851
852 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
853 int r;
854
855 PROTECT_ERRNO;
856 NSS_ENTRYPOINT_BEGIN;
857
858 assert(result);
859 assert(errnop);
860
861 if (_nss_systemd_is_blocked())
862 return NSS_STATUS_NOTFOUND;
863
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. */
866
867 if (!getspent_data.iterator) {
868 UNPROTECT_ERRNO;
869 *errnop = EHOSTDOWN;
870 return NSS_STATUS_UNAVAIL;
871 }
872
873 for (;;) {
874 r = userdb_iterator_get(getspent_data.iterator, &ur);
875 if (r == -ESRCH)
876 return NSS_STATUS_NOTFOUND;
877 if (r < 0) {
878 UNPROTECT_ERRNO;
879 *errnop = -r;
880 return NSS_STATUS_UNAVAIL;
881 }
882
883 if (!ur->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */
884 break;
885
886 ur = user_record_unref(ur);
887 }
888
889 r = nss_pack_user_record_shadow(ur, result, buffer, buflen);
890 if (r < 0) {
891 UNPROTECT_ERRNO;
892 *errnop = -r;
893 return NSS_STATUS_TRYAGAIN;
894 }
895
896 return NSS_STATUS_SUCCESS;
897}
898
899enum nss_status _nss_systemd_getsgent_r(
900 struct sgrp *result,
901 char *buffer, size_t buflen,
902 int *errnop) {
903
904 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
905 int r;
906
907 PROTECT_ERRNO;
908 NSS_ENTRYPOINT_BEGIN;
909
910 assert(result);
911 assert(errnop);
912
913 if (_nss_systemd_is_blocked())
914 return NSS_STATUS_NOTFOUND;
915
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. */
918
919 if (!getsgent_data.iterator) {
920 UNPROTECT_ERRNO;
921 *errnop = EHOSTDOWN;
922 return NSS_STATUS_UNAVAIL;
923 }
924
925 for (;;) {
926 r = groupdb_iterator_get(getsgent_data.iterator, &gr);
927 if (r == -ESRCH)
928 return NSS_STATUS_NOTFOUND;
929 if (r < 0) {
930 UNPROTECT_ERRNO;
931 *errnop = -r;
932 return NSS_STATUS_UNAVAIL;
933 }
934
935 if (!gr->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */
936 break;
937
938 gr = group_record_unref(gr);
939 }
940
941 r = nss_pack_group_record_shadow(gr, result, buffer, buflen);
942 if (r < 0) {
943 UNPROTECT_ERRNO;
944 *errnop = -r;
945 return NSS_STATUS_TRYAGAIN;
946 }
947
948 return NSS_STATUS_SUCCESS;
949}
950
1684c56f
LP
951enum nss_status _nss_systemd_initgroups_dyn(
952 const char *user_name,
953 gid_t gid,
954 long *start,
955 long *size,
956 gid_t **groupsp,
957 long int limit,
958 int *errnop) {
959
960 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
961 bool any = false;
962 int r;
12c2c56d 963
06202b9e 964 PROTECT_ERRNO;
3339381f 965 NSS_ENTRYPOINT_BEGIN;
12c2c56d 966
1684c56f
LP
967 assert(user_name);
968 assert(start);
969 assert(size);
970 assert(groupsp);
12c2c56d
YW
971 assert(errnop);
972
7a8867ab 973 if (!valid_user_group_name(user_name, VALID_USER_RELAX))
1684c56f
LP
974 return NSS_STATUS_NOTFOUND;
975
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;
979
037b0a47 980 if (_nss_systemd_is_blocked())
1684c56f
LP
981 return NSS_STATUS_NOTFOUND;
982
983 r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
984 if (r < 0) {
985 UNPROTECT_ERRNO;
986 *errnop = -r;
987 return NSS_STATUS_UNAVAIL;
988 }
12c2c56d 989
1684c56f
LP
990 for (;;) {
991 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
992 _cleanup_free_ char *group_name = NULL;
993
994 r = membershipdb_iterator_get(iterator, NULL, &group_name);
995 if (r == -ESRCH)
996 break;
997 if (r < 0) {
cdccd29f 998 UNPROTECT_ERRNO;
1684c56f
LP
999 *errnop = -r;
1000 return NSS_STATUS_UNAVAIL;
12c2c56d
YW
1001 }
1002
1684c56f
LP
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. */
12c2c56d 1005
80d88a82 1006 r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g);
1684c56f
LP
1007 if (r == -ESRCH)
1008 continue;
1009 if (r < 0) {
1010 log_debug_errno(r, "Failed to resolve group '%s', ignoring: %m", group_name);
1011 continue;
1012 }
12c2c56d 1013
1684c56f
LP
1014 if (g->gid == gid)
1015 continue;
12c2c56d 1016
1684c56f
LP
1017 if (*start >= *size) {
1018 gid_t *new_groups;
1019 long new_size;
1020
1021 if (limit > 0 && *size >= limit) /* Reached the limit.? */
1022 break;
1023
1024 if (*size > LONG_MAX/2) { /* Check for overflow */
1025 UNPROTECT_ERRNO;
1026 *errnop = ENOMEM;
1027 return NSS_STATUS_TRYAGAIN;
1028 }
1029
1030 new_size = *start * 2;
1031 if (limit > 0 && new_size > limit)
1032 new_size = limit;
1033
1034 /* Enlarge buffer */
d7a0f1f4 1035 new_groups = reallocarray(*groupsp, new_size, sizeof(**groupsp));
1684c56f
LP
1036 if (!new_groups) {
1037 UNPROTECT_ERRNO;
1038 *errnop = ENOMEM;
1039 return NSS_STATUS_TRYAGAIN;
1040 }
1041
1042 *groupsp = new_groups;
1043 *size = new_size;
1044 }
12c2c56d 1045
1684c56f
LP
1046 (*groupsp)[(*start)++] = g->gid;
1047 any = true;
1048 }
12c2c56d 1049
1684c56f 1050 return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
12c2c56d 1051}
037b0a47
LP
1052
1053static thread_local unsigned _blocked = 0;
1054
1055_public_ int _nss_systemd_block(bool b) {
1056
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`. */
1059
1060 if (b) {
1061 if (_blocked >= UINT_MAX)
1062 return -EOVERFLOW;
1063
1064 _blocked++;
1065 } else {
1066 if (_blocked <= 0)
1067 return -EOVERFLOW;
1068
1069 _blocked--;
1070 }
1071
1072 return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */
1073}
1074
1075_public_ bool _nss_systemd_is_blocked(void) {
1076 return _blocked > 0;
1077}