Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <nss.h>
4 : #include <pthread.h>
5 :
6 : #include "sd-bus.h"
7 :
8 : #include "alloc-util.h"
9 : #include "bus-common-errors.h"
10 : #include "dirent-util.h"
11 : #include "env-util.h"
12 : #include "fd-util.h"
13 : #include "format-util.h"
14 : #include "fs-util.h"
15 : #include "list.h"
16 : #include "macro.h"
17 : #include "nss-util.h"
18 : #include "signal-util.h"
19 : #include "stdio-util.h"
20 : #include "string-util.h"
21 : #include "user-util.h"
22 : #include "util.h"
23 :
24 : #define DYNAMIC_USER_GECOS "Dynamic User"
25 : #define DYNAMIC_USER_PASSWD "*" /* locked */
26 : #define DYNAMIC_USER_DIR "/"
27 : #define DYNAMIC_USER_SHELL NOLOGIN
28 :
29 : static const struct passwd root_passwd = {
30 : .pw_name = (char*) "root",
31 : .pw_passwd = (char*) "x", /* see shadow file */
32 : .pw_uid = 0,
33 : .pw_gid = 0,
34 : .pw_gecos = (char*) "Super User",
35 : .pw_dir = (char*) "/root",
36 : .pw_shell = (char*) "/bin/sh",
37 : };
38 :
39 : static const struct passwd nobody_passwd = {
40 : .pw_name = (char*) NOBODY_USER_NAME,
41 : .pw_passwd = (char*) "*", /* locked */
42 : .pw_uid = UID_NOBODY,
43 : .pw_gid = GID_NOBODY,
44 : .pw_gecos = (char*) "User Nobody",
45 : .pw_dir = (char*) "/",
46 : .pw_shell = (char*) NOLOGIN,
47 : };
48 :
49 : static const struct group root_group = {
50 : .gr_name = (char*) "root",
51 : .gr_gid = 0,
52 : .gr_passwd = (char*) "x", /* see shadow file */
53 : .gr_mem = (char*[]) { NULL },
54 : };
55 :
56 : static const struct group nobody_group = {
57 : .gr_name = (char*) NOBODY_GROUP_NAME,
58 : .gr_gid = GID_NOBODY,
59 : .gr_passwd = (char*) "*", /* locked */
60 : .gr_mem = (char*[]) { NULL },
61 : };
62 :
63 : typedef struct UserEntry UserEntry;
64 : typedef struct GetentData GetentData;
65 :
66 : struct UserEntry {
67 : uid_t id;
68 : char *name;
69 :
70 : GetentData *data;
71 : LIST_FIELDS(UserEntry, entries);
72 : };
73 :
74 : struct GetentData {
75 : /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
76 : * reentrant since it shares the reading position in the stream with all other threads',
77 : * we need to protect the data in UserEntry from multithreaded programs which may call
78 : * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
79 : * data by using the mutex below. */
80 : pthread_mutex_t mutex;
81 :
82 : UserEntry *position;
83 : LIST_HEAD(UserEntry, entries);
84 : };
85 :
86 : static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
87 : static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
88 :
89 : NSS_GETPW_PROTOTYPES(systemd);
90 : NSS_GETGR_PROTOTYPES(systemd);
91 : enum nss_status _nss_systemd_endpwent(void) _public_;
92 : enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
93 : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
94 : enum nss_status _nss_systemd_endgrent(void) _public_;
95 : enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
96 : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
97 :
98 0 : static int direct_lookup_name(const char *name, uid_t *ret) {
99 0 : _cleanup_free_ char *s = NULL;
100 : const char *path;
101 : int r;
102 :
103 0 : assert(name);
104 :
105 : /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
106 : * namespace and subject to proper authentication. However, there's one problem: if our module is called from
107 : * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
108 : * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
109 :
110 0 : path = strjoina("/run/systemd/dynamic-uid/direct:", name);
111 0 : r = readlink_malloc(path, &s);
112 0 : if (r < 0)
113 0 : return r;
114 :
115 0 : return parse_uid(s, ret);
116 : }
117 :
118 0 : static int direct_lookup_uid(uid_t uid, char **ret) {
119 : char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
120 : int r;
121 :
122 0 : xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
123 :
124 0 : r = readlink_malloc(path, &s);
125 0 : if (r < 0)
126 0 : return r;
127 0 : if (!valid_user_group_name(s)) { /* extra safety check */
128 0 : free(s);
129 0 : return -EINVAL;
130 : }
131 :
132 0 : *ret = s;
133 0 : return 0;
134 : }
135 :
136 0 : enum nss_status _nss_systemd_getpwnam_r(
137 : const char *name,
138 : struct passwd *pwd,
139 : char *buffer, size_t buflen,
140 : int *errnop) {
141 :
142 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
143 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
144 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
145 : uint32_t translated;
146 : size_t l;
147 : int bypass, r;
148 :
149 0 : PROTECT_ERRNO;
150 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
151 :
152 0 : assert(name);
153 0 : assert(pwd);
154 :
155 : /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
156 : * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
157 0 : if (!valid_user_group_name(name))
158 0 : return NSS_STATUS_NOTFOUND;
159 :
160 : /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
161 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
162 0 : if (streq(name, root_passwd.pw_name)) {
163 0 : *pwd = root_passwd;
164 0 : return NSS_STATUS_SUCCESS;
165 : }
166 0 : if (synthesize_nobody() &&
167 0 : streq(name, nobody_passwd.pw_name)) {
168 0 : *pwd = nobody_passwd;
169 0 : return NSS_STATUS_SUCCESS;
170 : }
171 : }
172 :
173 : /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
174 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
175 0 : return NSS_STATUS_NOTFOUND;
176 :
177 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
178 0 : if (bypass <= 0) {
179 0 : r = sd_bus_open_system(&bus);
180 0 : if (r < 0)
181 0 : bypass = 1;
182 : }
183 :
184 0 : if (bypass > 0) {
185 0 : r = direct_lookup_name(name, (uid_t*) &translated);
186 0 : if (r == -ENOENT)
187 0 : return NSS_STATUS_NOTFOUND;
188 0 : if (r < 0)
189 0 : goto fail;
190 : } else {
191 0 : r = sd_bus_call_method(bus,
192 : "org.freedesktop.systemd1",
193 : "/org/freedesktop/systemd1",
194 : "org.freedesktop.systemd1.Manager",
195 : "LookupDynamicUserByName",
196 : &error,
197 : &reply,
198 : "s",
199 : name);
200 0 : if (r < 0) {
201 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
202 0 : return NSS_STATUS_NOTFOUND;
203 :
204 0 : goto fail;
205 : }
206 :
207 0 : r = sd_bus_message_read(reply, "u", &translated);
208 0 : if (r < 0)
209 0 : goto fail;
210 : }
211 :
212 0 : l = strlen(name);
213 0 : if (buflen < l+1) {
214 0 : UNPROTECT_ERRNO;
215 0 : *errnop = ERANGE;
216 0 : return NSS_STATUS_TRYAGAIN;
217 : }
218 :
219 0 : memcpy(buffer, name, l+1);
220 :
221 0 : pwd->pw_name = buffer;
222 0 : pwd->pw_uid = (uid_t) translated;
223 0 : pwd->pw_gid = (uid_t) translated;
224 0 : pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
225 0 : pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
226 0 : pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
227 0 : pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
228 :
229 0 : return NSS_STATUS_SUCCESS;
230 :
231 0 : fail:
232 0 : UNPROTECT_ERRNO;
233 0 : *errnop = -r;
234 0 : return NSS_STATUS_UNAVAIL;
235 : }
236 :
237 0 : enum nss_status _nss_systemd_getpwuid_r(
238 : uid_t uid,
239 : struct passwd *pwd,
240 : char *buffer, size_t buflen,
241 : int *errnop) {
242 :
243 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
244 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
245 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
246 0 : _cleanup_free_ char *direct = NULL;
247 : const char *translated;
248 : size_t l;
249 : int bypass, r;
250 :
251 0 : PROTECT_ERRNO;
252 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
253 :
254 0 : if (!uid_is_valid(uid))
255 0 : return NSS_STATUS_NOTFOUND;
256 :
257 : /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
258 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
259 0 : if (uid == root_passwd.pw_uid) {
260 0 : *pwd = root_passwd;
261 0 : return NSS_STATUS_SUCCESS;
262 : }
263 0 : if (synthesize_nobody() &&
264 0 : uid == nobody_passwd.pw_uid) {
265 0 : *pwd = nobody_passwd;
266 0 : return NSS_STATUS_SUCCESS;
267 : }
268 : }
269 :
270 0 : if (!uid_is_dynamic(uid))
271 0 : return NSS_STATUS_NOTFOUND;
272 :
273 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
274 0 : return NSS_STATUS_NOTFOUND;
275 :
276 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
277 0 : if (bypass <= 0) {
278 0 : r = sd_bus_open_system(&bus);
279 0 : if (r < 0)
280 0 : bypass = 1;
281 : }
282 :
283 0 : if (bypass > 0) {
284 0 : r = direct_lookup_uid(uid, &direct);
285 0 : if (r == -ENOENT)
286 0 : return NSS_STATUS_NOTFOUND;
287 0 : if (r < 0)
288 0 : goto fail;
289 :
290 0 : translated = direct;
291 :
292 : } else {
293 0 : r = sd_bus_call_method(bus,
294 : "org.freedesktop.systemd1",
295 : "/org/freedesktop/systemd1",
296 : "org.freedesktop.systemd1.Manager",
297 : "LookupDynamicUserByUID",
298 : &error,
299 : &reply,
300 : "u",
301 : (uint32_t) uid);
302 0 : if (r < 0) {
303 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
304 0 : return NSS_STATUS_NOTFOUND;
305 :
306 0 : goto fail;
307 : }
308 :
309 0 : r = sd_bus_message_read(reply, "s", &translated);
310 0 : if (r < 0)
311 0 : goto fail;
312 : }
313 :
314 0 : l = strlen(translated) + 1;
315 0 : if (buflen < l) {
316 0 : UNPROTECT_ERRNO;
317 0 : *errnop = ERANGE;
318 0 : return NSS_STATUS_TRYAGAIN;
319 : }
320 :
321 0 : memcpy(buffer, translated, l);
322 :
323 0 : pwd->pw_name = buffer;
324 0 : pwd->pw_uid = uid;
325 0 : pwd->pw_gid = uid;
326 0 : pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
327 0 : pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
328 0 : pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
329 0 : pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
330 :
331 0 : return NSS_STATUS_SUCCESS;
332 :
333 0 : fail:
334 0 : UNPROTECT_ERRNO;
335 0 : *errnop = -r;
336 0 : return NSS_STATUS_UNAVAIL;
337 : }
338 :
339 : #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
340 :
341 0 : enum nss_status _nss_systemd_getgrnam_r(
342 : const char *name,
343 : struct group *gr,
344 : char *buffer, size_t buflen,
345 : int *errnop) {
346 :
347 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
348 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
349 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
350 : uint32_t translated;
351 : size_t l;
352 : int bypass, r;
353 :
354 0 : PROTECT_ERRNO;
355 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
356 :
357 0 : assert(name);
358 0 : assert(gr);
359 :
360 0 : if (!valid_user_group_name(name))
361 0 : return NSS_STATUS_NOTFOUND;
362 :
363 : /* Synthesize records for root and nobody, in case they are missing form /etc/group */
364 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
365 0 : if (streq(name, root_group.gr_name)) {
366 0 : *gr = root_group;
367 0 : return NSS_STATUS_SUCCESS;
368 : }
369 0 : if (synthesize_nobody() &&
370 0 : streq(name, nobody_group.gr_name)) {
371 0 : *gr = nobody_group;
372 0 : return NSS_STATUS_SUCCESS;
373 : }
374 : }
375 :
376 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
377 0 : return NSS_STATUS_NOTFOUND;
378 :
379 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
380 0 : if (bypass <= 0) {
381 0 : r = sd_bus_open_system(&bus);
382 0 : if (r < 0)
383 0 : bypass = 1;
384 : }
385 :
386 0 : if (bypass > 0) {
387 0 : r = direct_lookup_name(name, (uid_t*) &translated);
388 0 : if (r == -ENOENT)
389 0 : return NSS_STATUS_NOTFOUND;
390 0 : if (r < 0)
391 0 : goto fail;
392 : } else {
393 0 : r = sd_bus_call_method(bus,
394 : "org.freedesktop.systemd1",
395 : "/org/freedesktop/systemd1",
396 : "org.freedesktop.systemd1.Manager",
397 : "LookupDynamicUserByName",
398 : &error,
399 : &reply,
400 : "s",
401 : name);
402 0 : if (r < 0) {
403 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
404 0 : return NSS_STATUS_NOTFOUND;
405 :
406 0 : goto fail;
407 : }
408 :
409 0 : r = sd_bus_message_read(reply, "u", &translated);
410 0 : if (r < 0)
411 0 : goto fail;
412 : }
413 :
414 0 : l = sizeof(char*) + strlen(name) + 1;
415 0 : if (buflen < l) {
416 0 : UNPROTECT_ERRNO;
417 0 : *errnop = ERANGE;
418 0 : return NSS_STATUS_TRYAGAIN;
419 : }
420 :
421 0 : memzero(buffer, sizeof(char*));
422 0 : strcpy(buffer + sizeof(char*), name);
423 :
424 0 : gr->gr_name = buffer + sizeof(char*);
425 0 : gr->gr_gid = (gid_t) translated;
426 0 : gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
427 0 : gr->gr_mem = (char**) buffer;
428 :
429 0 : return NSS_STATUS_SUCCESS;
430 :
431 0 : fail:
432 0 : UNPROTECT_ERRNO;
433 0 : *errnop = -r;
434 0 : return NSS_STATUS_UNAVAIL;
435 : }
436 :
437 0 : enum nss_status _nss_systemd_getgrgid_r(
438 : gid_t gid,
439 : struct group *gr,
440 : char *buffer, size_t buflen,
441 : int *errnop) {
442 :
443 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
444 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
445 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
446 0 : _cleanup_free_ char *direct = NULL;
447 : const char *translated;
448 : size_t l;
449 : int bypass, r;
450 :
451 0 : PROTECT_ERRNO;
452 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
453 :
454 0 : if (!gid_is_valid(gid))
455 0 : return NSS_STATUS_NOTFOUND;
456 :
457 : /* Synthesize records for root and nobody, in case they are missing from /etc/group */
458 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
459 0 : if (gid == root_group.gr_gid) {
460 0 : *gr = root_group;
461 0 : return NSS_STATUS_SUCCESS;
462 : }
463 0 : if (synthesize_nobody() &&
464 0 : gid == nobody_group.gr_gid) {
465 0 : *gr = nobody_group;
466 0 : return NSS_STATUS_SUCCESS;
467 : }
468 : }
469 :
470 0 : if (!gid_is_dynamic(gid))
471 0 : return NSS_STATUS_NOTFOUND;
472 :
473 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
474 0 : return NSS_STATUS_NOTFOUND;
475 :
476 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
477 0 : if (bypass <= 0) {
478 0 : r = sd_bus_open_system(&bus);
479 0 : if (r < 0)
480 0 : bypass = 1;
481 : }
482 :
483 0 : if (bypass > 0) {
484 0 : r = direct_lookup_uid(gid, &direct);
485 0 : if (r == -ENOENT)
486 0 : return NSS_STATUS_NOTFOUND;
487 0 : if (r < 0)
488 0 : goto fail;
489 :
490 0 : translated = direct;
491 :
492 : } else {
493 0 : r = sd_bus_call_method(bus,
494 : "org.freedesktop.systemd1",
495 : "/org/freedesktop/systemd1",
496 : "org.freedesktop.systemd1.Manager",
497 : "LookupDynamicUserByUID",
498 : &error,
499 : &reply,
500 : "u",
501 : (uint32_t) gid);
502 0 : if (r < 0) {
503 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
504 0 : return NSS_STATUS_NOTFOUND;
505 :
506 0 : goto fail;
507 : }
508 :
509 0 : r = sd_bus_message_read(reply, "s", &translated);
510 0 : if (r < 0)
511 0 : goto fail;
512 : }
513 :
514 0 : l = sizeof(char*) + strlen(translated) + 1;
515 0 : if (buflen < l) {
516 0 : UNPROTECT_ERRNO;
517 0 : *errnop = ERANGE;
518 0 : return NSS_STATUS_TRYAGAIN;
519 : }
520 :
521 0 : memzero(buffer, sizeof(char*));
522 0 : strcpy(buffer + sizeof(char*), translated);
523 :
524 0 : gr->gr_name = buffer + sizeof(char*);
525 0 : gr->gr_gid = gid;
526 0 : gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
527 0 : gr->gr_mem = (char**) buffer;
528 :
529 0 : return NSS_STATUS_SUCCESS;
530 :
531 0 : fail:
532 0 : UNPROTECT_ERRNO;
533 0 : *errnop = -r;
534 0 : return NSS_STATUS_UNAVAIL;
535 : }
536 :
537 0 : static void user_entry_free(UserEntry *p) {
538 0 : if (!p)
539 0 : return;
540 :
541 0 : if (p->data)
542 0 : LIST_REMOVE(entries, p->data->entries, p);
543 :
544 0 : free(p->name);
545 0 : free(p);
546 : }
547 :
548 0 : static int user_entry_add(GetentData *data, const char *name, uid_t id) {
549 : UserEntry *p;
550 :
551 0 : assert(data);
552 :
553 : /* This happens when User= or Group= already exists statically. */
554 0 : if (!uid_is_dynamic(id))
555 0 : return -EINVAL;
556 :
557 0 : p = new0(UserEntry, 1);
558 0 : if (!p)
559 0 : return -ENOMEM;
560 :
561 0 : p->name = strdup(name);
562 0 : if (!p->name) {
563 0 : free(p);
564 0 : return -ENOMEM;
565 : }
566 0 : p->id = id;
567 0 : p->data = data;
568 :
569 0 : LIST_PREPEND(entries, data->entries, p);
570 :
571 0 : return 0;
572 : }
573 :
574 0 : static void systemd_endent(GetentData *data) {
575 : UserEntry *p;
576 :
577 0 : assert(data);
578 :
579 0 : while ((p = data->entries))
580 0 : user_entry_free(p);
581 :
582 0 : data->position = NULL;
583 0 : }
584 :
585 0 : static enum nss_status nss_systemd_endent(GetentData *p) {
586 0 : PROTECT_ERRNO;
587 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
588 :
589 0 : assert_se(pthread_mutex_lock(&p->mutex) == 0);
590 0 : systemd_endent(p);
591 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
592 :
593 0 : return NSS_STATUS_SUCCESS;
594 : }
595 :
596 0 : enum nss_status _nss_systemd_endpwent(void) {
597 0 : return nss_systemd_endent(&getpwent_data);
598 : }
599 :
600 0 : enum nss_status _nss_systemd_endgrent(void) {
601 0 : return nss_systemd_endent(&getgrent_data);
602 : }
603 :
604 0 : static int direct_enumeration(GetentData *p) {
605 0 : _cleanup_closedir_ DIR *d = NULL;
606 : struct dirent *de;
607 : int r;
608 :
609 0 : assert(p);
610 :
611 0 : d = opendir("/run/systemd/dynamic-uid/");
612 0 : if (!d)
613 0 : return -errno;
614 :
615 0 : FOREACH_DIRENT(de, d, return -errno) {
616 0 : _cleanup_free_ char *name = NULL;
617 : uid_t uid, verified;
618 :
619 0 : if (!dirent_is_file(de))
620 0 : continue;
621 :
622 0 : r = parse_uid(de->d_name, &uid);
623 0 : if (r < 0)
624 0 : continue;
625 :
626 0 : r = direct_lookup_uid(uid, &name);
627 0 : if (r == -ENOMEM)
628 0 : return r;
629 0 : if (r < 0)
630 0 : continue;
631 :
632 0 : r = direct_lookup_name(name, &verified);
633 0 : if (r < 0)
634 0 : continue;
635 :
636 0 : if (uid != verified)
637 0 : continue;
638 :
639 0 : r = user_entry_add(p, name, uid);
640 0 : if (r == -ENOMEM)
641 0 : return r;
642 0 : if (r < 0)
643 0 : continue;
644 : }
645 :
646 0 : return 0;
647 : }
648 :
649 0 : static enum nss_status systemd_setent(GetentData *p) {
650 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
651 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
652 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
653 : const char *name;
654 : uid_t id;
655 : int bypass, r;
656 :
657 0 : PROTECT_ERRNO;
658 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
659 :
660 0 : assert(p);
661 :
662 0 : assert_se(pthread_mutex_lock(&p->mutex) == 0);
663 :
664 0 : systemd_endent(p);
665 :
666 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
667 0 : goto finish;
668 :
669 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
670 :
671 0 : if (bypass <= 0) {
672 0 : r = sd_bus_open_system(&bus);
673 0 : if (r < 0)
674 0 : bypass = 1;
675 : }
676 :
677 0 : if (bypass > 0) {
678 0 : r = direct_enumeration(p);
679 0 : if (r < 0)
680 0 : goto fail;
681 :
682 0 : goto finish;
683 : }
684 :
685 0 : r = sd_bus_call_method(bus,
686 : "org.freedesktop.systemd1",
687 : "/org/freedesktop/systemd1",
688 : "org.freedesktop.systemd1.Manager",
689 : "GetDynamicUsers",
690 : &error,
691 : &reply,
692 : NULL);
693 0 : if (r < 0)
694 0 : goto fail;
695 :
696 0 : r = sd_bus_message_enter_container(reply, 'a', "(us)");
697 0 : if (r < 0)
698 0 : goto fail;
699 :
700 0 : while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
701 0 : r = user_entry_add(p, name, id);
702 0 : if (r == -ENOMEM)
703 0 : goto fail;
704 0 : if (r < 0)
705 0 : continue;
706 : }
707 0 : if (r < 0)
708 0 : goto fail;
709 :
710 0 : r = sd_bus_message_exit_container(reply);
711 0 : if (r < 0)
712 0 : goto fail;
713 :
714 0 : finish:
715 0 : p->position = p->entries;
716 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
717 :
718 0 : return NSS_STATUS_SUCCESS;
719 :
720 0 : fail:
721 0 : systemd_endent(p);
722 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
723 :
724 0 : return NSS_STATUS_UNAVAIL;
725 : }
726 :
727 0 : enum nss_status _nss_systemd_setpwent(int stayopen) {
728 0 : return systemd_setent(&getpwent_data);
729 : }
730 :
731 0 : enum nss_status _nss_systemd_setgrent(int stayopen) {
732 0 : return systemd_setent(&getgrent_data);
733 : }
734 :
735 0 : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
736 : enum nss_status ret;
737 : UserEntry *p;
738 : size_t len;
739 :
740 0 : PROTECT_ERRNO;
741 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
742 :
743 0 : assert(result);
744 0 : assert(buffer);
745 0 : assert(errnop);
746 :
747 0 : assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
748 :
749 0 : LIST_FOREACH(entries, p, getpwent_data.position) {
750 0 : len = strlen(p->name) + 1;
751 0 : if (buflen < len) {
752 0 : UNPROTECT_ERRNO;
753 0 : *errnop = ERANGE;
754 0 : ret = NSS_STATUS_TRYAGAIN;
755 0 : goto finalize;
756 : }
757 :
758 0 : memcpy(buffer, p->name, len);
759 :
760 0 : result->pw_name = buffer;
761 0 : result->pw_uid = p->id;
762 0 : result->pw_gid = p->id;
763 0 : result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
764 0 : result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
765 0 : result->pw_dir = (char*) DYNAMIC_USER_DIR;
766 0 : result->pw_shell = (char*) DYNAMIC_USER_SHELL;
767 0 : break;
768 : }
769 0 : if (!p) {
770 0 : ret = NSS_STATUS_NOTFOUND;
771 0 : goto finalize;
772 : }
773 :
774 : /* On success, step to the next entry. */
775 0 : p = p->entries_next;
776 0 : ret = NSS_STATUS_SUCCESS;
777 :
778 0 : finalize:
779 : /* Save position for the next call. */
780 0 : getpwent_data.position = p;
781 :
782 0 : assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
783 :
784 0 : return ret;
785 : }
786 :
787 0 : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
788 : enum nss_status ret;
789 : UserEntry *p;
790 : size_t len;
791 :
792 0 : PROTECT_ERRNO;
793 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
794 :
795 0 : assert(result);
796 0 : assert(buffer);
797 0 : assert(errnop);
798 :
799 0 : assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
800 :
801 0 : LIST_FOREACH(entries, p, getgrent_data.position) {
802 0 : len = sizeof(char*) + strlen(p->name) + 1;
803 0 : if (buflen < len) {
804 0 : UNPROTECT_ERRNO;
805 0 : *errnop = ERANGE;
806 0 : ret = NSS_STATUS_TRYAGAIN;
807 0 : goto finalize;
808 : }
809 :
810 0 : memzero(buffer, sizeof(char*));
811 0 : strcpy(buffer + sizeof(char*), p->name);
812 :
813 0 : result->gr_name = buffer + sizeof(char*);
814 0 : result->gr_gid = p->id;
815 0 : result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
816 0 : result->gr_mem = (char**) buffer;
817 0 : break;
818 : }
819 0 : if (!p) {
820 0 : ret = NSS_STATUS_NOTFOUND;
821 0 : goto finalize;
822 : }
823 :
824 : /* On success, step to the next entry. */
825 0 : p = p->entries_next;
826 0 : ret = NSS_STATUS_SUCCESS;
827 :
828 0 : finalize:
829 : /* Save position for the next call. */
830 0 : getgrent_data.position = p;
831 :
832 0 : assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
833 :
834 0 : return ret;
835 : }
|