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