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