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