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 
setup_logging_once(void)119 static void setup_logging_once(void) {
120         static pthread_once_t once = PTHREAD_ONCE_INIT;
121         assert_se(pthread_once(&once, log_parse_environment_variables) == 0);
122 }
123 
124 #define NSS_ENTRYPOINT_BEGIN                    \
125         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);       \
126         setup_logging_once()
127 
128 NSS_GETPW_PROTOTYPES(systemd);
129 NSS_GETSP_PROTOTYPES(systemd);
130 NSS_GETGR_PROTOTYPES(systemd);
131 NSS_GETSG_PROTOTYPES(systemd);
132 NSS_PWENT_PROTOTYPES(systemd);
133 NSS_SPENT_PROTOTYPES(systemd);
134 NSS_GRENT_PROTOTYPES(systemd);
135 NSS_SGENT_PROTOTYPES(systemd);
136 NSS_INITGROUPS_PROTOTYPE(systemd);
137 
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 
copy_synthesized_passwd(struct passwd * dest,const struct passwd * src,char * buffer,size_t buflen,int * errnop)142 static 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 
copy_synthesized_spwd(struct spwd * dest,const struct spwd * src,char * buffer,size_t buflen,int * errnop)184 static 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 
copy_synthesized_group(struct group * dest,const struct group * src,char * buffer,size_t buflen,int * errnop)217 static 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;
234         required += sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
235 
236         if (buflen < ALIGN(required)) {
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;
248         dest->gr_mem = ALIGN_PTR(stpcpy(dest->gr_passwd, src->gr_passwd) + 1);
249         *dest->gr_mem = NULL;
250 
251         return NSS_STATUS_SUCCESS;
252 }
253 
copy_synthesized_sgrp(struct sgrp * dest,const struct sgrp * src,char * buffer,size_t buflen,int * errnop)254 static 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 
_nss_systemd_getpwnam_r(const char * name,struct passwd * pwd,char * buffer,size_t buflen,int * errnop)287 enum nss_status _nss_systemd_getpwnam_r(
288                 const char *name,
289                 struct passwd *pwd,
290                 char *buffer, size_t buflen,
291                 int *errnop) {
292 
293         enum nss_status status;
294         int e;
295 
296         PROTECT_ERRNO;
297         NSS_ENTRYPOINT_BEGIN;
298 
299         assert(name);
300         assert(pwd);
301         assert(errnop);
302 
303         /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
304          * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
305          * invalid user names. */
306         if (!valid_user_group_name(name, VALID_USER_RELAX))
307                 return NSS_STATUS_NOTFOUND;
308 
309         /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
310         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
311 
312                 if (streq(name, root_passwd.pw_name))
313                         return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
314 
315                 if (streq(name, nobody_passwd.pw_name)) {
316                         if (!synthesize_nobody())
317                                 return NSS_STATUS_NOTFOUND;
318 
319                         return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
320                 }
321 
322         } else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
323                 return NSS_STATUS_NOTFOUND;
324 
325         status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
326         if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
327                 UNPROTECT_ERRNO;
328                 *errnop = e;
329                 return status;
330         }
331 
332         return status;
333 }
334 
_nss_systemd_getpwuid_r(uid_t uid,struct passwd * pwd,char * buffer,size_t buflen,int * errnop)335 enum nss_status _nss_systemd_getpwuid_r(
336                 uid_t uid,
337                 struct passwd *pwd,
338                 char *buffer, size_t buflen,
339                 int *errnop) {
340 
341         enum nss_status status;
342         int e;
343 
344         PROTECT_ERRNO;
345         NSS_ENTRYPOINT_BEGIN;
346 
347         assert(pwd);
348         assert(errnop);
349 
350         if (!uid_is_valid(uid))
351                 return NSS_STATUS_NOTFOUND;
352 
353         /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
354         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
355 
356                 if (uid == root_passwd.pw_uid)
357                         return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
358 
359                 if (uid == nobody_passwd.pw_uid) {
360                         if (!synthesize_nobody())
361                                 return NSS_STATUS_NOTFOUND;
362 
363                         return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
364                 }
365 
366         } else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
367                 return NSS_STATUS_NOTFOUND;
368 
369         status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
370         if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
371                 UNPROTECT_ERRNO;
372                 *errnop = e;
373                 return status;
374         }
375 
376         return status;
377 }
378 
_nss_systemd_getspnam_r(const char * name,struct spwd * spwd,char * buffer,size_t buflen,int * errnop)379 enum 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 
401                 if (streq(name, root_spwd.sp_namp))
402                         return copy_synthesized_spwd(spwd, &root_spwd, buffer, buflen, errnop);
403 
404                 if (streq(name, nobody_spwd.sp_namp)) {
405                         if (!synthesize_nobody())
406                                 return NSS_STATUS_NOTFOUND;
407 
408                         return copy_synthesized_spwd(spwd, &nobody_spwd, buffer, buflen, errnop);
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 
424 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
425 
_nss_systemd_getgrnam_r(const char * name,struct group * gr,char * buffer,size_t buflen,int * errnop)426 enum nss_status _nss_systemd_getgrnam_r(
427                 const char *name,
428                 struct group *gr,
429                 char *buffer, size_t buflen,
430                 int *errnop) {
431 
432         enum nss_status status;
433         int e;
434 
435         PROTECT_ERRNO;
436         NSS_ENTRYPOINT_BEGIN;
437 
438         assert(name);
439         assert(gr);
440         assert(errnop);
441 
442         if (!valid_user_group_name(name, VALID_USER_RELAX))
443                 return NSS_STATUS_NOTFOUND;
444 
445         /* Synthesize records for root and nobody, in case they are missing from /etc/group */
446         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
447 
448                 if (streq(name, root_group.gr_name))
449                         return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
450 
451                 if (streq(name, nobody_group.gr_name)) {
452                         if (!synthesize_nobody())
453                                 return NSS_STATUS_NOTFOUND;
454 
455                         return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
456                 }
457 
458         } else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
459                 return NSS_STATUS_NOTFOUND;
460 
461         status = userdb_getgrnam(name, gr, buffer, buflen, &e);
462         if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
463                 UNPROTECT_ERRNO;
464                 *errnop = e;
465                 return status;
466         }
467 
468         return status;
469 }
470 
_nss_systemd_getgrgid_r(gid_t gid,struct group * gr,char * buffer,size_t buflen,int * errnop)471 enum nss_status _nss_systemd_getgrgid_r(
472                 gid_t gid,
473                 struct group *gr,
474                 char *buffer, size_t buflen,
475                 int *errnop) {
476 
477         enum nss_status status;
478         int e;
479 
480         PROTECT_ERRNO;
481         NSS_ENTRYPOINT_BEGIN;
482 
483         assert(gr);
484         assert(errnop);
485 
486         if (!gid_is_valid(gid))
487                 return NSS_STATUS_NOTFOUND;
488 
489         /* Synthesize records for root and nobody, in case they are missing from /etc/group */
490         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
491 
492                 if (gid == root_group.gr_gid)
493                         return copy_synthesized_group(gr, &root_group, buffer, buflen, errnop);
494 
495                 if (gid == nobody_group.gr_gid) {
496                         if (!synthesize_nobody())
497                                 return NSS_STATUS_NOTFOUND;
498 
499                         return copy_synthesized_group(gr, &nobody_group, buffer, buflen, errnop);
500                 }
501 
502         } else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
503                 return NSS_STATUS_NOTFOUND;
504 
505         status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
506         if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
507                 UNPROTECT_ERRNO;
508                 *errnop = e;
509                 return status;
510         }
511 
512         return status;
513 }
514 
_nss_systemd_getsgnam_r(const char * name,struct sgrp * sgrp,char * buffer,size_t buflen,int * errnop)515 enum 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 
537                 if (streq(name, root_sgrp.sg_namp))
538                         return copy_synthesized_sgrp(sgrp, &root_sgrp, buffer, buflen, errnop);
539 
540                 if (streq(name, nobody_sgrp.sg_namp)) {
541                         if (!synthesize_nobody())
542                                 return NSS_STATUS_NOTFOUND;
543 
544                         return copy_synthesized_sgrp(sgrp, &nobody_sgrp, buffer, buflen, errnop);
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 
nss_systemd_endent(GetentData * p)560 static enum nss_status nss_systemd_endent(GetentData *p) {
561         PROTECT_ERRNO;
562         NSS_ENTRYPOINT_BEGIN;
563 
564         assert(p);
565 
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. */
568 
569         p->iterator = userdb_iterator_free(p->iterator);
570         p->by_membership = false;
571 
572         return NSS_STATUS_SUCCESS;
573 }
574 
_nss_systemd_endpwent(void)575 enum nss_status _nss_systemd_endpwent(void) {
576         return nss_systemd_endent(&getpwent_data);
577 }
578 
_nss_systemd_endspent(void)579 enum nss_status _nss_systemd_endspent(void) {
580         return nss_systemd_endent(&getspent_data);
581 }
582 
_nss_systemd_endgrent(void)583 enum nss_status _nss_systemd_endgrent(void) {
584         return nss_systemd_endent(&getgrent_data);
585 }
586 
_nss_systemd_endsgent(void)587 enum nss_status _nss_systemd_endsgent(void) {
588         return nss_systemd_endent(&getsgent_data);
589 }
590 
_nss_systemd_setpwent(int stayopen)591 enum nss_status _nss_systemd_setpwent(int stayopen) {
592         int r;
593 
594         PROTECT_ERRNO;
595         NSS_ENTRYPOINT_BEGIN;
596 
597         if (_nss_systemd_is_blocked())
598                 return NSS_STATUS_NOTFOUND;
599 
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. */
602 
603         getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
604         getpwent_data.by_membership = false;
605 
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);
612         return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
613 }
614 
_nss_systemd_setgrent(int stayopen)615 enum nss_status _nss_systemd_setgrent(int stayopen) {
616         int r;
617 
618         PROTECT_ERRNO;
619         NSS_ENTRYPOINT_BEGIN;
620 
621         if (_nss_systemd_is_blocked())
622                 return NSS_STATUS_NOTFOUND;
623 
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. */
626 
627         getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
628         getgrent_data.by_membership = false;
629 
630         /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
631         r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator);
632         return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
633 }
634 
_nss_systemd_setspent(int stayopen)635 enum 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 
_nss_systemd_setsgent(int stayopen)655 enum 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 
_nss_systemd_getpwent_r(struct passwd * result,char * buffer,size_t buflen,int * errnop)675 enum 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;
681         int r;
682 
683         PROTECT_ERRNO;
684         NSS_ENTRYPOINT_BEGIN;
685 
686         assert(result);
687         assert(errnop);
688 
689         if (_nss_systemd_is_blocked())
690                 return NSS_STATUS_NOTFOUND;
691 
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. */
694 
695         if (!getpwent_data.iterator) {
696                 UNPROTECT_ERRNO;
697                 *errnop = EHOSTDOWN;
698                 return NSS_STATUS_UNAVAIL;
699         }
700 
701         r = userdb_iterator_get(getpwent_data.iterator, &ur);
702         if (r == -ESRCH)
703                 return NSS_STATUS_NOTFOUND;
704         if (r < 0) {
705                 UNPROTECT_ERRNO;
706                 *errnop = -r;
707                 return NSS_STATUS_UNAVAIL;
708         }
709 
710         r = nss_pack_user_record(ur, result, buffer, buflen);
711         if (r < 0) {
712                 UNPROTECT_ERRNO;
713                 *errnop = -r;
714                 return NSS_STATUS_TRYAGAIN;
715         }
716 
717         return NSS_STATUS_SUCCESS;
718 }
719 
_nss_systemd_getgrent_r(struct group * result,char * buffer,size_t buflen,int * errnop)720 enum nss_status _nss_systemd_getgrent_r(
721                 struct group *result,
722                 char *buffer, size_t buflen,
723                 int *errnop) {
724 
725         _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
726         _cleanup_free_ char **members = NULL;
727         int r;
728 
729         PROTECT_ERRNO;
730         NSS_ENTRYPOINT_BEGIN;
731 
732         assert(result);
733         assert(errnop);
734 
735         if (_nss_systemd_is_blocked())
736                 return NSS_STATUS_NOTFOUND;
737 
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. */
740 
741         if (!getgrent_data.iterator) {
742                 UNPROTECT_ERRNO;
743                 *errnop = EHOSTDOWN;
744                 return NSS_STATUS_UNAVAIL;
745         }
746 
747         if (!getgrent_data.by_membership) {
748                 r = groupdb_iterator_get(getgrent_data.iterator, &gr);
749                 if (r == -ESRCH) {
750                         /* So we finished iterating native groups now. Let's now continue with iterating
751                          * native memberships, and generate additional group entries for any groups
752                          * referenced there that are defined in NSS only. This means for those groups there
753                          * will be two or more entries generated during iteration, but this is apparently how
754                          * this is supposed to work, and what other implementations do too. Clients are
755                          * supposed to merge the group records found during iteration automatically. */
756                         getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
757 
758                         r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
759                         if (r < 0 && r != -ESRCH) {
760                                 UNPROTECT_ERRNO;
761                                 *errnop = -r;
762                                 return NSS_STATUS_UNAVAIL;
763                         }
764 
765                         getgrent_data.by_membership = true;
766                 } else if (r < 0) {
767                         UNPROTECT_ERRNO;
768                         *errnop = -r;
769                         return NSS_STATUS_UNAVAIL;
770                 } else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) {
771                         r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members);
772                         if (r < 0 && r != -ESRCH) {
773                                 UNPROTECT_ERRNO;
774                                 *errnop = -r;
775                                 return NSS_STATUS_UNAVAIL;
776                         }
777                 }
778         }
779 
780         if (getgrent_data.by_membership) {
781                 _cleanup_(_nss_systemd_unblockp) bool blocked = false;
782 
783                 if (!getgrent_data.iterator)
784                         return NSS_STATUS_NOTFOUND;
785 
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);
790                         if (r == -ESRCH)
791                                 return NSS_STATUS_NOTFOUND;
792                         if (r < 0) {
793                                 UNPROTECT_ERRNO;
794                                 *errnop = -r;
795                                 return NSS_STATUS_UNAVAIL;
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. */
804                         if (!blocked) {
805                                 r = _nss_systemd_block(true);
806                                 if (r < 0) {
807                                         UNPROTECT_ERRNO;
808                                         *errnop = -r;
809                                         return NSS_STATUS_UNAVAIL;
810                                 }
811 
812                                 blocked = true;
813                         }
814 
815                         r = nss_group_record_by_name(group_name, false, &gr);
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;
827                                 return NSS_STATUS_TRYAGAIN;
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                 }
835         }
836 
837         r = nss_pack_group_record(gr, members, result, buffer, buflen);
838         if (r < 0) {
839                 UNPROTECT_ERRNO;
840                 *errnop = -r;
841                 return NSS_STATUS_TRYAGAIN;
842         }
843 
844         return NSS_STATUS_SUCCESS;
845 }
846 
_nss_systemd_getspent_r(struct spwd * result,char * buffer,size_t buflen,int * errnop)847 enum 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 
_nss_systemd_getsgent_r(struct sgrp * result,char * buffer,size_t buflen,int * errnop)899 enum 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 
_nss_systemd_initgroups_dyn(const char * user_name,gid_t gid,long * start,long * size,gid_t ** groupsp,long int limit,int * errnop)951 enum 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;
963 
964         PROTECT_ERRNO;
965         NSS_ENTRYPOINT_BEGIN;
966 
967         assert(user_name);
968         assert(start);
969         assert(size);
970         assert(groupsp);
971         assert(errnop);
972 
973         if (!valid_user_group_name(user_name, VALID_USER_RELAX))
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 
980         if (_nss_systemd_is_blocked())
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         }
989 
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) {
998                         UNPROTECT_ERRNO;
999                         *errnop = -r;
1000                         return NSS_STATUS_UNAVAIL;
1001                 }
1002 
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. */
1005 
1006                 r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g);
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                 }
1013 
1014                 if (g->gid == gid)
1015                         continue;
1016 
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 */
1035                         new_groups = reallocarray(*groupsp, new_size, sizeof(**groupsp));
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                 }
1045 
1046                 (*groupsp)[(*start)++] = g->gid;
1047                 any = true;
1048         }
1049 
1050         return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
1051 }
1052 
1053 static thread_local unsigned _blocked = 0;
1054 
_nss_systemd_block(bool b)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 
_nss_systemd_is_blocked(void)1075 _public_ bool _nss_systemd_is_blocked(void) {
1076         return _blocked > 0;
1077 }
1078