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