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