]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/userdb.c
Merge pull request #16112 from poettering/nss-systemd-block-fix
[thirdparty/systemd.git] / src / shared / userdb.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <sys/auxv.h>
4
5 #include "dirent-util.h"
6 #include "dlfcn-util.h"
7 #include "errno-util.h"
8 #include "fd-util.h"
9 #include "group-record-nss.h"
10 #include "missing_syscall.h"
11 #include "parse-util.h"
12 #include "set.h"
13 #include "socket-util.h"
14 #include "strv.h"
15 #include "user-record-nss.h"
16 #include "user-util.h"
17 #include "userdb.h"
18 #include "varlink.h"
19
20 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops, void, trivial_hash_func, trivial_compare_func, Varlink, varlink_unref);
21
22 typedef enum LookupWhat {
23 LOOKUP_USER,
24 LOOKUP_GROUP,
25 LOOKUP_MEMBERSHIP,
26 _LOOKUP_WHAT_MAX,
27 } LookupWhat;
28
29 struct UserDBIterator {
30 LookupWhat what;
31 Set *links;
32 bool nss_covered:1;
33 bool nss_iterating:1;
34 bool synthesize_root:1;
35 bool synthesize_nobody:1;
36 bool nss_systemd_blocked:1;
37 int error;
38 unsigned n_found;
39 sd_event *event;
40 UserRecord *found_user; /* when .what == LOOKUP_USER */
41 GroupRecord *found_group; /* when .what == LOOKUP_GROUP */
42
43 char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
44 char **members_of_group;
45 size_t index_members_of_group;
46 char *filter_user_name;
47 };
48
49 UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
50 if (!iterator)
51 return NULL;
52
53 set_free(iterator->links);
54
55 switch (iterator->what) {
56
57 case LOOKUP_USER:
58 user_record_unref(iterator->found_user);
59
60 if (iterator->nss_iterating)
61 endpwent();
62
63 break;
64
65 case LOOKUP_GROUP:
66 group_record_unref(iterator->found_group);
67
68 if (iterator->nss_iterating)
69 endgrent();
70
71 break;
72
73 case LOOKUP_MEMBERSHIP:
74 free(iterator->found_user_name);
75 free(iterator->found_group_name);
76 strv_free(iterator->members_of_group);
77 free(iterator->filter_user_name);
78
79 if (iterator->nss_iterating)
80 endgrent();
81
82 break;
83
84 default:
85 assert_not_reached("Unexpected state?");
86 }
87
88 sd_event_unref(iterator->event);
89
90 if (iterator->nss_systemd_blocked)
91 assert_se(userdb_block_nss_systemd(false) >= 0);
92
93 return mfree(iterator);
94 }
95
96 static UserDBIterator* userdb_iterator_new(LookupWhat what) {
97 UserDBIterator *i;
98
99 assert(what >= 0);
100 assert(what < _LOOKUP_WHAT_MAX);
101
102 i = new(UserDBIterator, 1);
103 if (!i)
104 return NULL;
105
106 *i = (UserDBIterator) {
107 .what = what,
108 };
109
110 return i;
111 }
112
113 static int userdb_iterator_block_nss_systemd(UserDBIterator *iterator) {
114 int r;
115
116 assert(iterator);
117
118 if (iterator->nss_systemd_blocked)
119 return 0;
120
121 r = userdb_block_nss_systemd(true);
122 if (r < 0)
123 return r;
124
125 iterator->nss_systemd_blocked = true;
126 return 1;
127 }
128
129 struct user_group_data {
130 JsonVariant *record;
131 bool incomplete;
132 };
133
134 static void user_group_data_release(struct user_group_data *d) {
135 json_variant_unref(d->record);
136 }
137
138 static int userdb_on_query_reply(
139 Varlink *link,
140 JsonVariant *parameters,
141 const char *error_id,
142 VarlinkReplyFlags flags,
143 void *userdata) {
144
145 UserDBIterator *iterator = userdata;
146 int r;
147
148 assert(iterator);
149
150 if (error_id) {
151 log_debug("Got lookup error: %s", error_id);
152
153 if (STR_IN_SET(error_id,
154 "io.systemd.UserDatabase.NoRecordFound",
155 "io.systemd.UserDatabase.ConflictingRecordFound"))
156 r = -ESRCH;
157 else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
158 r = -EHOSTDOWN;
159 else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
160 r = -ETIMEDOUT;
161 else
162 r = -EIO;
163
164 goto finish;
165 }
166
167 switch (iterator->what) {
168
169 case LOOKUP_USER: {
170 _cleanup_(user_group_data_release) struct user_group_data user_data = {};
171
172 static const JsonDispatch dispatch_table[] = {
173 { "record", _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record), 0 },
174 { "incomplete", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
175 {}
176 };
177 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
178
179 assert_se(!iterator->found_user);
180
181 r = json_dispatch(parameters, dispatch_table, NULL, 0, &user_data);
182 if (r < 0)
183 goto finish;
184
185 if (!user_data.record) {
186 r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
187 goto finish;
188 }
189
190 hr = user_record_new();
191 if (!hr) {
192 r = -ENOMEM;
193 goto finish;
194 }
195
196 r = user_record_load(hr, user_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
197 if (r < 0)
198 goto finish;
199
200 if (!hr->service) {
201 r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "User record does not carry service information, refusing.");
202 goto finish;
203 }
204
205 hr->incomplete = user_data.incomplete;
206
207 /* We match the root user by the name since the name is our primary key. We match the nobody
208 * use by UID though, since the name might differ on OSes */
209 if (streq_ptr(hr->user_name, "root"))
210 iterator->synthesize_root = false;
211 if (hr->uid == UID_NOBODY)
212 iterator->synthesize_nobody = false;
213
214 iterator->found_user = TAKE_PTR(hr);
215 iterator->n_found++;
216
217 /* More stuff coming? then let's just exit cleanly here */
218 if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
219 return 0;
220
221 /* Otherwise, let's remove this link and exit cleanly then */
222 r = 0;
223 goto finish;
224 }
225
226 case LOOKUP_GROUP: {
227 _cleanup_(user_group_data_release) struct user_group_data group_data = {};
228
229 static const JsonDispatch dispatch_table[] = {
230 { "record", _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record), 0 },
231 { "incomplete", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
232 {}
233 };
234 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
235
236 assert_se(!iterator->found_group);
237
238 r = json_dispatch(parameters, dispatch_table, NULL, 0, &group_data);
239 if (r < 0)
240 goto finish;
241
242 if (!group_data.record) {
243 r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
244 goto finish;
245 }
246
247 g = group_record_new();
248 if (!g) {
249 r = -ENOMEM;
250 goto finish;
251 }
252
253 r = group_record_load(g, group_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
254 if (r < 0)
255 goto finish;
256
257 if (!g->service) {
258 r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Group record does not carry service information, refusing.");
259 goto finish;
260 }
261
262 g->incomplete = group_data.incomplete;
263
264 if (streq_ptr(g->group_name, "root"))
265 iterator->synthesize_root = false;
266 if (g->gid == GID_NOBODY)
267 iterator->synthesize_nobody = false;
268
269 iterator->found_group = TAKE_PTR(g);
270 iterator->n_found++;
271
272 if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
273 return 0;
274
275 r = 0;
276 goto finish;
277 }
278
279 case LOOKUP_MEMBERSHIP: {
280 struct membership_data {
281 const char *user_name;
282 const char *group_name;
283 } membership_data = {};
284
285 static const JsonDispatch dispatch_table[] = {
286 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, user_name), JSON_SAFE },
287 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, group_name), JSON_SAFE },
288 {}
289 };
290
291 assert(!iterator->found_user_name);
292 assert(!iterator->found_group_name);
293
294 r = json_dispatch(parameters, dispatch_table, NULL, 0, &membership_data);
295 if (r < 0)
296 goto finish;
297
298 iterator->found_user_name = mfree(iterator->found_user_name);
299 iterator->found_group_name = mfree(iterator->found_group_name);
300
301 iterator->found_user_name = strdup(membership_data.user_name);
302 if (!iterator->found_user_name) {
303 r = -ENOMEM;
304 goto finish;
305 }
306
307 iterator->found_group_name = strdup(membership_data.group_name);
308 if (!iterator->found_group_name) {
309 r = -ENOMEM;
310 goto finish;
311 }
312
313 iterator->n_found++;
314
315 if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
316 return 0;
317
318 r = 0;
319 goto finish;
320 }
321
322 default:
323 assert_not_reached("unexpected lookup");
324 }
325
326 finish:
327 /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
328 * errors if at least one connection ended cleanly */
329 if (r == -ESRCH || iterator->error == 0)
330 iterator->error = -r;
331
332 assert_se(set_remove(iterator->links, link) == link);
333 link = varlink_unref(link);
334 return 0;
335 }
336
337 static int userdb_connect(
338 UserDBIterator *iterator,
339 const char *path,
340 const char *method,
341 bool more,
342 JsonVariant *query) {
343
344 _cleanup_(varlink_unrefp) Varlink *vl = NULL;
345 int r;
346
347 assert(iterator);
348 assert(path);
349 assert(method);
350
351 r = varlink_connect_address(&vl, path);
352 if (r < 0)
353 return log_debug_errno(r, "Unable to connect to %s: %m", path);
354
355 varlink_set_userdata(vl, iterator);
356
357 if (!iterator->event) {
358 r = sd_event_new(&iterator->event);
359 if (r < 0)
360 return log_debug_errno(r, "Unable to allocate event loop: %m");
361 }
362
363 r = varlink_attach_event(vl, iterator->event, SD_EVENT_PRIORITY_NORMAL);
364 if (r < 0)
365 return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
366
367 (void) varlink_set_description(vl, path);
368
369 r = varlink_bind_reply(vl, userdb_on_query_reply);
370 if (r < 0)
371 return log_debug_errno(r, "Failed to bind reply callback: %m");
372
373 if (more)
374 r = varlink_observe(vl, method, query);
375 else
376 r = varlink_invoke(vl, method, query);
377 if (r < 0)
378 return log_debug_errno(r, "Failed to invoke varlink method: %m");
379
380 r = set_ensure_allocated(&iterator->links, &link_hash_ops);
381 if (r < 0)
382 return log_debug_errno(r, "Failed to allocate set: %m");
383
384 r = set_put(iterator->links, vl);
385 if (r < 0)
386 return log_debug_errno(r, "Failed to add varlink connection to set: %m");
387
388 TAKE_PTR(vl);
389 return r;
390 }
391
392 static int userdb_start_query(
393 UserDBIterator *iterator,
394 const char *method,
395 bool more,
396 JsonVariant *query,
397 UserDBFlags flags) {
398
399 _cleanup_(strv_freep) char **except = NULL, **only = NULL;
400 _cleanup_(closedirp) DIR *d = NULL;
401 struct dirent *de;
402 const char *e;
403 int r, ret = 0;
404
405 assert(iterator);
406 assert(method);
407
408 e = getenv("SYSTEMD_BYPASS_USERDB");
409 if (e) {
410 r = parse_boolean(e);
411 if (r > 0)
412 return -ENOLINK;
413 if (r < 0) {
414 except = strv_split(e, ":");
415 if (!except)
416 return -ENOMEM;
417 }
418 }
419
420 e = getenv("SYSTEMD_ONLY_USERDB");
421 if (e) {
422 only = strv_split(e, ":");
423 if (!only)
424 return -ENOMEM;
425 }
426
427 /* First, let's talk to the multiplexer, if we can */
428 if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_AVOID_DYNAMIC_USER|USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
429 !strv_contains(except, "io.systemd.Multiplexer") &&
430 (!only || strv_contains(only, "io.systemd.Multiplexer"))) {
431 _cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
432
433 r = json_variant_set_field_string(&patched_query, "service", "io.systemd.Multiplexer");
434 if (r < 0)
435 return log_debug_errno(r, "Unable to set service JSON field: %m");
436
437 r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
438 if (r >= 0) {
439 iterator->nss_covered = true; /* The multiplexer does NSS */
440 return 0;
441 }
442 }
443
444 d = opendir("/run/systemd/userdb/");
445 if (!d) {
446 if (errno == ENOENT)
447 return -ESRCH;
448
449 return -errno;
450 }
451
452 FOREACH_DIRENT(de, d, return -errno) {
453 _cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
454 _cleanup_free_ char *p = NULL;
455 bool is_nss;
456
457 if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
458 continue;
459
460 if (FLAGS_SET(flags, USERDB_AVOID_DYNAMIC_USER) &&
461 streq(de->d_name, "io.systemd.DynamicUser"))
462 continue;
463
464 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
465 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
466 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
467 * anyway). */
468 is_nss = streq(de->d_name, "io.systemd.NameServiceSwitch");
469 if ((flags & (USERDB_AVOID_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
470 continue;
471
472 if (strv_contains(except, de->d_name))
473 continue;
474
475 if (only && !strv_contains(only, de->d_name))
476 continue;
477
478 p = path_join("/run/systemd/userdb/", de->d_name);
479 if (!p)
480 return -ENOMEM;
481
482 patched_query = json_variant_ref(query);
483 r = json_variant_set_field_string(&patched_query, "service", de->d_name);
484 if (r < 0)
485 return log_debug_errno(r, "Unable to set service JSON field: %m");
486
487 r = userdb_connect(iterator, p, method, more, patched_query);
488 if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
489 * to it */
490 iterator->nss_covered = true;
491
492 if (ret == 0 && r < 0)
493 ret = r;
494 }
495
496 if (set_isempty(iterator->links))
497 return ret; /* propagate last error we saw if we couldn't connect to anything. */
498
499 /* We connected to some services, in this case, ignore the ones we failed on */
500 return 0;
501 }
502
503 static int userdb_process(
504 UserDBIterator *iterator,
505 UserRecord **ret_user_record,
506 GroupRecord **ret_group_record,
507 char **ret_user_name,
508 char **ret_group_name) {
509
510 int r;
511
512 assert(iterator);
513
514 for (;;) {
515 if (iterator->what == LOOKUP_USER && iterator->found_user) {
516 if (ret_user_record)
517 *ret_user_record = TAKE_PTR(iterator->found_user);
518 else
519 iterator->found_user = user_record_unref(iterator->found_user);
520
521 if (ret_group_record)
522 *ret_group_record = NULL;
523 if (ret_user_name)
524 *ret_user_name = NULL;
525 if (ret_group_name)
526 *ret_group_name = NULL;
527
528 return 0;
529 }
530
531 if (iterator->what == LOOKUP_GROUP && iterator->found_group) {
532 if (ret_group_record)
533 *ret_group_record = TAKE_PTR(iterator->found_group);
534 else
535 iterator->found_group = group_record_unref(iterator->found_group);
536
537 if (ret_user_record)
538 *ret_user_record = NULL;
539 if (ret_user_name)
540 *ret_user_name = NULL;
541 if (ret_group_name)
542 *ret_group_name = NULL;
543
544 return 0;
545 }
546
547 if (iterator->what == LOOKUP_MEMBERSHIP && iterator->found_user_name && iterator->found_group_name) {
548 if (ret_user_name)
549 *ret_user_name = TAKE_PTR(iterator->found_user_name);
550 else
551 iterator->found_user_name = mfree(iterator->found_user_name);
552
553 if (ret_group_name)
554 *ret_group_name = TAKE_PTR(iterator->found_group_name);
555 else
556 iterator->found_group_name = mfree(iterator->found_group_name);
557
558 if (ret_user_record)
559 *ret_user_record = NULL;
560 if (ret_group_record)
561 *ret_group_record = NULL;
562
563 return 0;
564 }
565
566 if (set_isempty(iterator->links)) {
567 if (iterator->error == 0)
568 return -ESRCH;
569
570 return -abs(iterator->error);
571 }
572
573 if (!iterator->event)
574 return -ESRCH;
575
576 r = sd_event_run(iterator->event, UINT64_MAX);
577 if (r < 0)
578 return r;
579 }
580 }
581
582 static int synthetic_root_user_build(UserRecord **ret) {
583 return user_record_build(
584 ret,
585 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
586 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
587 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
588 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
589 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
590 }
591
592 static int synthetic_nobody_user_build(UserRecord **ret) {
593 return user_record_build(
594 ret,
595 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME)),
596 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY)),
597 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
598 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
599 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
600 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
601 }
602
603 int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
604 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
605 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
606 int r;
607
608 if (!valid_user_group_name(name, VALID_USER_RELAX))
609 return -EINVAL;
610
611 r = json_build(&query, JSON_BUILD_OBJECT(
612 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
613 if (r < 0)
614 return r;
615
616 iterator = userdb_iterator_new(LOOKUP_USER);
617 if (!iterator)
618 return -ENOMEM;
619
620 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
621 if (r >= 0) {
622 r = userdb_process(iterator, ret, NULL, NULL, NULL);
623 if (r >= 0)
624 return r;
625 }
626
627 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
628 /* Make sure the NSS lookup doesn't recurse back to us. */
629
630 r = userdb_iterator_block_nss_systemd(iterator);
631 if (r >= 0) {
632 /* Client-side NSS fallback */
633 r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
634 if (r >= 0)
635 return r;
636 }
637 }
638
639 if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
640 if (streq(name, "root"))
641 return synthetic_root_user_build(ret);
642
643 if (streq(name, NOBODY_USER_NAME) && synthesize_nobody())
644 return synthetic_nobody_user_build(ret);
645 }
646
647 return r;
648 }
649
650 int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
651 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
652 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
653 int r;
654
655 if (!uid_is_valid(uid))
656 return -EINVAL;
657
658 r = json_build(&query, JSON_BUILD_OBJECT(
659 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid))));
660 if (r < 0)
661 return r;
662
663 iterator = userdb_iterator_new(LOOKUP_USER);
664 if (!iterator)
665 return -ENOMEM;
666
667 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
668 if (r >= 0) {
669 r = userdb_process(iterator, ret, NULL, NULL, NULL);
670 if (r >= 0)
671 return r;
672 }
673
674 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
675 r = userdb_iterator_block_nss_systemd(iterator);
676 if (r >= 0) {
677 /* Client-side NSS fallback */
678 r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
679 if (r >= 0)
680 return r;
681 }
682 }
683
684 if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
685 if (uid == 0)
686 return synthetic_root_user_build(ret);
687
688 if (uid == UID_NOBODY && synthesize_nobody())
689 return synthetic_nobody_user_build(ret);
690 }
691
692 return r;
693 }
694
695 int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
696 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
697 int r;
698
699 assert(ret);
700
701 iterator = userdb_iterator_new(LOOKUP_USER);
702 if (!iterator)
703 return -ENOMEM;
704
705 iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
706
707 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
708
709 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
710 r = userdb_iterator_block_nss_systemd(iterator);
711 if (r < 0)
712 return r;
713
714 setpwent();
715 iterator->nss_iterating = true;
716 } else if (r < 0)
717 return r;
718
719 *ret = TAKE_PTR(iterator);
720 return 0;
721 }
722
723 int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
724 int r;
725
726 assert(iterator);
727 assert(iterator->what == LOOKUP_USER);
728
729 if (iterator->nss_iterating) {
730 struct passwd *pw;
731
732 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
733 * the more traditional sources, which are probably good to show first. */
734
735 pw = getpwent();
736 if (pw) {
737 _cleanup_free_ char *buffer = NULL;
738 bool incomplete = false;
739 struct spwd spwd;
740
741 if (streq_ptr(pw->pw_name, "root"))
742 iterator->synthesize_root = false;
743 if (pw->pw_uid == UID_NOBODY)
744 iterator->synthesize_nobody = false;
745
746 r = nss_spwd_for_passwd(pw, &spwd, &buffer);
747 if (r < 0) {
748 log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
749 incomplete = ERRNO_IS_PRIVILEGE(r);
750 }
751
752 r = nss_passwd_to_user_record(pw, r >= 0 ? &spwd : NULL, ret);
753 if (r < 0)
754 return r;
755
756 if (ret)
757 (*ret)->incomplete = incomplete;
758 return r;
759 }
760
761 if (errno != 0)
762 log_debug_errno(errno, "Failure to iterate NSS user database, ignoring: %m");
763
764 iterator->nss_iterating = false;
765 endpwent();
766 }
767
768 r = userdb_process(iterator, ret, NULL, NULL, NULL);
769
770 if (r < 0) {
771 if (iterator->synthesize_root) {
772 iterator->synthesize_root = false;
773 iterator->n_found++;
774 return synthetic_root_user_build(ret);
775 }
776
777 if (iterator->synthesize_nobody) {
778 iterator->synthesize_nobody = false;
779 iterator->n_found++;
780 return synthetic_nobody_user_build(ret);
781 }
782 }
783
784 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
785 if (r < 0 && iterator->n_found > 0)
786 return -ESRCH;
787
788 return r;
789 }
790
791 static int synthetic_root_group_build(GroupRecord **ret) {
792 return group_record_build(
793 ret,
794 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
795 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
796 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
797 }
798
799 static int synthetic_nobody_group_build(GroupRecord **ret) {
800 return group_record_build(
801 ret,
802 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME)),
803 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
804 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
805 }
806
807 int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
808 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
809 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
810 int r;
811
812 if (!valid_user_group_name(name, VALID_USER_RELAX))
813 return -EINVAL;
814
815 r = json_build(&query, JSON_BUILD_OBJECT(
816 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
817 if (r < 0)
818 return r;
819
820 iterator = userdb_iterator_new(LOOKUP_GROUP);
821 if (!iterator)
822 return -ENOMEM;
823
824 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
825 if (r >= 0) {
826 r = userdb_process(iterator, NULL, ret, NULL, NULL);
827 if (r >= 0)
828 return r;
829 }
830
831 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
832 r = userdb_iterator_block_nss_systemd(iterator);
833 if (r >= 0) {
834 r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
835 if (r >= 0)
836 return r;
837 }
838 }
839
840 if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
841 if (streq(name, "root"))
842 return synthetic_root_group_build(ret);
843
844 if (streq(name, NOBODY_GROUP_NAME) && synthesize_nobody())
845 return synthetic_nobody_group_build(ret);
846 }
847
848 return r;
849 }
850
851 int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
852 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
853 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
854 int r;
855
856 if (!gid_is_valid(gid))
857 return -EINVAL;
858
859 r = json_build(&query, JSON_BUILD_OBJECT(
860 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))));
861 if (r < 0)
862 return r;
863
864 iterator = userdb_iterator_new(LOOKUP_GROUP);
865 if (!iterator)
866 return -ENOMEM;
867
868 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
869 if (r >= 0) {
870 r = userdb_process(iterator, NULL, ret, NULL, NULL);
871 if (r >= 0)
872 return r;
873 }
874
875 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
876 r = userdb_iterator_block_nss_systemd(iterator);
877 if (r >= 0) {
878 r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
879 if (r >= 0)
880 return r;
881 }
882 }
883
884 if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
885 if (gid == 0)
886 return synthetic_root_group_build(ret);
887
888 if (gid == GID_NOBODY && synthesize_nobody())
889 return synthetic_nobody_group_build(ret);
890 }
891
892 return r;
893 }
894
895 int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
896 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
897 int r;
898
899 assert(ret);
900
901 iterator = userdb_iterator_new(LOOKUP_GROUP);
902 if (!iterator)
903 return -ENOMEM;
904
905 iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
906
907 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
908
909 if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
910 r = userdb_iterator_block_nss_systemd(iterator);
911 if (r < 0)
912 return r;
913
914 setgrent();
915 iterator->nss_iterating = true;
916 } if (r < 0)
917 return r;
918
919 *ret = TAKE_PTR(iterator);
920 return 0;
921 }
922
923 int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
924 int r;
925
926 assert(iterator);
927 assert(iterator->what == LOOKUP_GROUP);
928
929 if (iterator->nss_iterating) {
930 struct group *gr;
931
932 errno = 0;
933 gr = getgrent();
934 if (gr) {
935 _cleanup_free_ char *buffer = NULL;
936 bool incomplete = false;
937 struct sgrp sgrp;
938
939 if (streq_ptr(gr->gr_name, "root"))
940 iterator->synthesize_root = false;
941 if (gr->gr_gid == GID_NOBODY)
942 iterator->synthesize_nobody = false;
943
944 r = nss_sgrp_for_group(gr, &sgrp, &buffer);
945 if (r < 0) {
946 log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", gr->gr_name);
947 incomplete = ERRNO_IS_PRIVILEGE(r);
948 }
949
950 r = nss_group_to_group_record(gr, r >= 0 ? &sgrp : NULL, ret);
951 if (r < 0)
952 return r;
953
954 if (ret)
955 (*ret)->incomplete = incomplete;
956 return r;
957 }
958
959 if (errno != 0)
960 log_debug_errno(errno, "Failure to iterate NSS group database, ignoring: %m");
961
962 iterator->nss_iterating = false;
963 endgrent();
964 }
965
966 r = userdb_process(iterator, NULL, ret, NULL, NULL);
967 if (r < 0) {
968 if (iterator->synthesize_root) {
969 iterator->synthesize_root = false;
970 iterator->n_found++;
971 return synthetic_root_group_build(ret);
972 }
973
974 if (iterator->synthesize_nobody) {
975 iterator->synthesize_nobody = false;
976 iterator->n_found++;
977 return synthetic_nobody_group_build(ret);
978 }
979 }
980
981 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
982 if (r < 0 && iterator->n_found > 0)
983 return -ESRCH;
984
985 return r;
986 }
987
988 int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
989 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
990 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
991 int r;
992
993 assert(ret);
994
995 if (!valid_user_group_name(name, VALID_USER_RELAX))
996 return -EINVAL;
997
998 r = json_build(&query, JSON_BUILD_OBJECT(
999 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
1000 if (r < 0)
1001 return r;
1002
1003 iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
1004 if (!iterator)
1005 return -ENOMEM;
1006
1007 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
1008 if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
1009 goto finish;
1010
1011 r = userdb_iterator_block_nss_systemd(iterator);
1012 if (r < 0)
1013 return r;
1014
1015 iterator->filter_user_name = strdup(name);
1016 if (!iterator->filter_user_name)
1017 return -ENOMEM;
1018
1019 setgrent();
1020 iterator->nss_iterating = true;
1021
1022 r = 0;
1023
1024 finish:
1025 if (r >= 0)
1026 *ret = TAKE_PTR(iterator);
1027 return r;
1028 }
1029
1030 int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
1031 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
1032 _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
1033 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
1034 int r;
1035
1036 assert(ret);
1037
1038 if (!valid_user_group_name(name, VALID_USER_RELAX))
1039 return -EINVAL;
1040
1041 r = json_build(&query, JSON_BUILD_OBJECT(
1042 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
1043 if (r < 0)
1044 return r;
1045
1046 iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
1047 if (!iterator)
1048 return -ENOMEM;
1049
1050 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
1051 if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
1052 goto finish;
1053
1054 r = userdb_iterator_block_nss_systemd(iterator);
1055 if (r < 0)
1056 return r;
1057
1058 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1059 (void) nss_group_record_by_name(name, false, &gr);
1060 if (gr) {
1061 iterator->members_of_group = strv_copy(gr->members);
1062 if (!iterator->members_of_group)
1063 return -ENOMEM;
1064
1065 iterator->index_members_of_group = 0;
1066
1067 iterator->found_group_name = strdup(name);
1068 if (!iterator->found_group_name)
1069 return -ENOMEM;
1070 }
1071
1072 r = 0;
1073
1074 finish:
1075 if (r >= 0)
1076 *ret = TAKE_PTR(iterator);
1077
1078 return r;
1079 }
1080
1081 int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
1082 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
1083 int r;
1084
1085 assert(ret);
1086
1087 iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
1088 if (!iterator)
1089 return -ENOMEM;
1090
1091 r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
1092 if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
1093 goto finish;
1094
1095 r = userdb_iterator_block_nss_systemd(iterator);
1096 if (r < 0)
1097 return r;
1098
1099 setgrent();
1100 iterator->nss_iterating = true;
1101
1102 r = 0;
1103
1104 finish:
1105 if (r >= 0)
1106 *ret = TAKE_PTR(iterator);
1107
1108 return r;
1109 }
1110
1111 int membershipdb_iterator_get(
1112 UserDBIterator *iterator,
1113 char **ret_user,
1114 char **ret_group) {
1115
1116 int r;
1117
1118 assert(iterator);
1119
1120 for (;;) {
1121 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1122 if (!iterator->members_of_group) {
1123 struct group *g;
1124
1125 if (!iterator->nss_iterating)
1126 break;
1127
1128 assert(!iterator->found_user_name);
1129 do {
1130 errno = 0;
1131 g = getgrent();
1132 if (!g) {
1133 if (errno != 0)
1134 log_debug_errno(errno, "Failure during NSS group iteration, ignoring: %m");
1135 break;
1136 }
1137
1138 } while (iterator->filter_user_name ? !strv_contains(g->gr_mem, iterator->filter_user_name) :
1139 strv_isempty(g->gr_mem));
1140
1141 if (g) {
1142 r = free_and_strdup(&iterator->found_group_name, g->gr_name);
1143 if (r < 0)
1144 return r;
1145
1146 if (iterator->filter_user_name)
1147 iterator->members_of_group = strv_new(iterator->filter_user_name);
1148 else
1149 iterator->members_of_group = strv_copy(g->gr_mem);
1150 if (!iterator->members_of_group)
1151 return -ENOMEM;
1152
1153 iterator->index_members_of_group = 0;
1154 } else {
1155 iterator->nss_iterating = false;
1156 endgrent();
1157 break;
1158 }
1159 }
1160
1161 assert(iterator->found_group_name);
1162 assert(iterator->members_of_group);
1163 assert(!iterator->found_user_name);
1164
1165 if (iterator->members_of_group[iterator->index_members_of_group]) {
1166 _cleanup_free_ char *cu = NULL, *cg = NULL;
1167
1168 if (ret_user) {
1169 cu = strdup(iterator->members_of_group[iterator->index_members_of_group]);
1170 if (!cu)
1171 return -ENOMEM;
1172 }
1173
1174 if (ret_group) {
1175 cg = strdup(iterator->found_group_name);
1176 if (!cg)
1177 return -ENOMEM;
1178 }
1179
1180 if (ret_user)
1181 *ret_user = TAKE_PTR(cu);
1182
1183 if (ret_group)
1184 *ret_group = TAKE_PTR(cg);
1185
1186 iterator->index_members_of_group++;
1187 return 0;
1188 }
1189
1190 iterator->members_of_group = strv_free(iterator->members_of_group);
1191 iterator->found_group_name = mfree(iterator->found_group_name);
1192 }
1193
1194 r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
1195 if (r < 0 && iterator->n_found > 0)
1196 return -ESRCH;
1197
1198 return r;
1199 }
1200
1201 int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret) {
1202 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
1203 _cleanup_strv_free_ char **members = NULL;
1204 int r;
1205
1206 assert(name);
1207 assert(ret);
1208
1209 r = membershipdb_by_group(name, flags, &iterator);
1210 if (r < 0)
1211 return r;
1212
1213 for (;;) {
1214 _cleanup_free_ char *user_name = NULL;
1215
1216 r = membershipdb_iterator_get(iterator, &user_name, NULL);
1217 if (r == -ESRCH)
1218 break;
1219 if (r < 0)
1220 return r;
1221
1222 r = strv_consume(&members, TAKE_PTR(user_name));
1223 if (r < 0)
1224 return r;
1225 }
1226
1227 strv_sort(members);
1228 strv_uniq(members);
1229
1230 *ret = TAKE_PTR(members);
1231 return 0;
1232 }
1233
1234 int userdb_block_nss_systemd(int b) {
1235 _cleanup_(dlclosep) void *dl = NULL;
1236 int (*call)(bool b);
1237
1238 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1239
1240 dl = dlopen(ROOTLIBDIR "libnss_systemd.so.2", RTLD_LAZY|RTLD_NODELETE);
1241 if (!dl) {
1242 /* If the file isn't installed, don't complain loudly */
1243 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1244 return 0;
1245 }
1246
1247 call = (int (*)(bool b)) dlsym(dl, "_nss_systemd_block");
1248 if (!call)
1249 /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
1250 return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD),
1251 "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());
1252
1253 return call(b);
1254 }