1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "dirent-util.h"
6 #include "dlfcn-util.h"
7 #include "errno-util.h"
9 #include "missing_syscall.h"
10 #include "parse-util.h"
12 #include "socket-util.h"
14 #include "user-record-nss.h"
15 #include "user-util.h"
19 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops
, void, trivial_hash_func
, trivial_compare_func
, Varlink
, varlink_unref
);
21 typedef enum LookupWhat
{
28 struct UserDBIterator
{
33 bool synthesize_root
:1;
34 bool synthesize_nobody
:1;
35 bool nss_systemd_blocked
:1;
39 UserRecord
*found_user
; /* when .what == LOOKUP_USER */
40 GroupRecord
*found_group
; /* when .what == LOOKUP_GROUP */
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
;
48 UserDBIterator
* userdb_iterator_free(UserDBIterator
*iterator
) {
52 set_free(iterator
->links
);
54 switch (iterator
->what
) {
57 user_record_unref(iterator
->found_user
);
59 if (iterator
->nss_iterating
)
65 group_record_unref(iterator
->found_group
);
67 if (iterator
->nss_iterating
)
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
);
78 if (iterator
->nss_iterating
)
84 assert_not_reached("Unexpected state?");
87 sd_event_unref(iterator
->event
);
89 if (iterator
->nss_systemd_blocked
)
90 assert_se(userdb_block_nss_systemd(false) >= 0);
92 return mfree(iterator
);
95 static UserDBIterator
* userdb_iterator_new(LookupWhat what
) {
99 assert(what
< _LOOKUP_WHAT_MAX
);
101 i
= new(UserDBIterator
, 1);
105 *i
= (UserDBIterator
) {
112 static int userdb_iterator_block_nss_systemd(UserDBIterator
*iterator
) {
117 if (iterator
->nss_systemd_blocked
)
120 r
= userdb_block_nss_systemd(true);
124 iterator
->nss_systemd_blocked
= true;
128 struct user_group_data
{
133 static void user_group_data_release(struct user_group_data
*d
) {
134 json_variant_unref(d
->record
);
137 static int userdb_on_query_reply(
139 JsonVariant
*parameters
,
140 const char *error_id
,
141 VarlinkReplyFlags flags
,
144 UserDBIterator
*iterator
= userdata
;
150 log_debug("Got lookup error: %s", error_id
);
152 if (STR_IN_SET(error_id
,
153 "io.systemd.UserDatabase.NoRecordFound",
154 "io.systemd.UserDatabase.ConflictingRecordFound"))
156 else if (streq(error_id
, "io.systemd.UserDatabase.ServiceNotAvailable"))
158 else if (streq(error_id
, "io.systemd.UserDatabase.EnumerationNotSupported"))
160 else if (streq(error_id
, VARLINK_ERROR_TIMEOUT
))
168 switch (iterator
->what
) {
171 _cleanup_(user_group_data_release
) struct user_group_data user_data
= {};
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 },
178 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
180 assert_se(!iterator
->found_user
);
182 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &user_data
);
186 if (!user_data
.record
) {
187 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
191 hr
= user_record_new();
197 r
= user_record_load(hr
, user_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
202 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "User record does not carry service information, refusing.");
206 hr
->incomplete
= user_data
.incomplete
;
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;
215 iterator
->found_user
= TAKE_PTR(hr
);
218 /* More stuff coming? then let's just exit cleanly here */
219 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
222 /* Otherwise, let's remove this link and exit cleanly then */
228 _cleanup_(user_group_data_release
) struct user_group_data group_data
= {};
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 },
235 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
237 assert_se(!iterator
->found_group
);
239 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &group_data
);
243 if (!group_data
.record
) {
244 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
248 g
= group_record_new();
254 r
= group_record_load(g
, group_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
259 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Group record does not carry service information, refusing.");
263 g
->incomplete
= group_data
.incomplete
;
265 if (streq_ptr(g
->group_name
, "root"))
266 iterator
->synthesize_root
= false;
267 if (g
->gid
== GID_NOBODY
)
268 iterator
->synthesize_nobody
= false;
270 iterator
->found_group
= TAKE_PTR(g
);
273 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
280 case LOOKUP_MEMBERSHIP
: {
281 struct membership_data
{
282 const char *user_name
;
283 const char *group_name
;
284 } membership_data
= {};
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
},
292 assert(!iterator
->found_user_name
);
293 assert(!iterator
->found_group_name
);
295 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &membership_data
);
299 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
300 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
302 iterator
->found_user_name
= strdup(membership_data
.user_name
);
303 if (!iterator
->found_user_name
) {
308 iterator
->found_group_name
= strdup(membership_data
.group_name
);
309 if (!iterator
->found_group_name
) {
316 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
324 assert_not_reached("unexpected lookup");
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
;
333 assert_se(set_remove(iterator
->links
, link
) == link
);
334 link
= varlink_unref(link
);
338 static int userdb_connect(
339 UserDBIterator
*iterator
,
343 JsonVariant
*query
) {
345 _cleanup_(varlink_unrefp
) Varlink
*vl
= NULL
;
352 r
= varlink_connect_address(&vl
, path
);
354 return log_debug_errno(r
, "Unable to connect to %s: %m", path
);
356 varlink_set_userdata(vl
, iterator
);
358 if (!iterator
->event
) {
359 r
= sd_event_new(&iterator
->event
);
361 return log_debug_errno(r
, "Unable to allocate event loop: %m");
364 r
= varlink_attach_event(vl
, iterator
->event
, SD_EVENT_PRIORITY_NORMAL
);
366 return log_debug_errno(r
, "Failed to attach varlink connection to event loop: %m");
368 (void) varlink_set_description(vl
, path
);
370 r
= varlink_bind_reply(vl
, userdb_on_query_reply
);
372 return log_debug_errno(r
, "Failed to bind reply callback: %m");
375 r
= varlink_observe(vl
, method
, query
);
377 r
= varlink_invoke(vl
, method
, query
);
379 return log_debug_errno(r
, "Failed to invoke varlink method: %m");
381 r
= set_ensure_consume(&iterator
->links
, &link_hash_ops
, TAKE_PTR(vl
));
383 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
387 static int userdb_start_query(
388 UserDBIterator
*iterator
,
394 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
395 _cleanup_(closedirp
) DIR *d
= NULL
;
403 e
= getenv("SYSTEMD_BYPASS_USERDB");
405 r
= parse_boolean(e
);
409 except
= strv_split(e
, ":");
415 e
= getenv("SYSTEMD_ONLY_USERDB");
417 only
= strv_split(e
, ":");
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
);
428 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
430 return log_debug_errno(r
, "Unable to set service JSON field: %m");
432 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
434 iterator
->nss_covered
= true; /* The multiplexer does NSS */
439 d
= opendir("/run/systemd/userdb/");
447 FOREACH_DIRENT(de
, d
, return -errno
) {
448 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
449 _cleanup_free_
char *p
= NULL
;
452 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
455 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
456 streq(de
->d_name
, "io.systemd.DynamicUser"))
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
463 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
464 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
467 if (strv_contains(except
, de
->d_name
))
470 if (only
&& !strv_contains(only
, de
->d_name
))
473 p
= path_join("/run/systemd/userdb/", de
->d_name
);
477 patched_query
= json_variant_ref(query
);
478 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
480 return log_debug_errno(r
, "Unable to set service JSON field: %m");
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
485 iterator
->nss_covered
= true;
487 if (ret
== 0 && r
< 0)
491 if (set_isempty(iterator
->links
))
492 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
494 /* We connected to some services, in this case, ignore the ones we failed on */
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
) {
510 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
512 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
514 iterator
->found_user
= user_record_unref(iterator
->found_user
);
516 if (ret_group_record
)
517 *ret_group_record
= NULL
;
519 *ret_user_name
= NULL
;
521 *ret_group_name
= NULL
;
526 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
527 if (ret_group_record
)
528 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
530 iterator
->found_group
= group_record_unref(iterator
->found_group
);
533 *ret_user_record
= NULL
;
535 *ret_user_name
= NULL
;
537 *ret_group_name
= NULL
;
542 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
544 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
546 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
549 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
551 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
554 *ret_user_record
= NULL
;
555 if (ret_group_record
)
556 *ret_group_record
= NULL
;
561 if (set_isempty(iterator
->links
)) {
562 if (iterator
->error
== 0)
565 return -abs(iterator
->error
);
568 if (!iterator
->event
)
571 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
577 static int synthetic_root_user_build(UserRecord
**ret
) {
578 return user_record_build(
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"))));
587 static int synthetic_nobody_user_build(UserRecord
**ret
) {
588 return user_record_build(
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"))));
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
;
603 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
606 r
= json_build(&query
, JSON_BUILD_OBJECT(
607 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
611 iterator
= userdb_iterator_new(LOOKUP_USER
);
615 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
617 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
622 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
623 /* Make sure the NSS lookup doesn't recurse back to us. */
625 r
= userdb_iterator_block_nss_systemd(iterator
);
627 /* Client-side NSS fallback */
628 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
634 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
635 if (streq(name
, "root"))
636 return synthetic_root_user_build(ret
);
638 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
639 return synthetic_nobody_user_build(ret
);
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
;
650 if (!uid_is_valid(uid
))
653 r
= json_build(&query
, JSON_BUILD_OBJECT(
654 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
658 iterator
= userdb_iterator_new(LOOKUP_USER
);
662 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
664 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
669 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
670 r
= userdb_iterator_block_nss_systemd(iterator
);
672 /* Client-side NSS fallback */
673 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
679 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
681 return synthetic_root_user_build(ret
);
683 if (uid
== UID_NOBODY
&& synthesize_nobody())
684 return synthetic_nobody_user_build(ret
);
690 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
691 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
696 iterator
= userdb_iterator_new(LOOKUP_USER
);
700 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
702 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
704 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
705 r
= userdb_iterator_block_nss_systemd(iterator
);
710 iterator
->nss_iterating
= true;
714 *ret
= TAKE_PTR(iterator
);
718 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
722 assert(iterator
->what
== LOOKUP_USER
);
724 if (iterator
->nss_iterating
) {
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. */
732 _cleanup_free_
char *buffer
= NULL
;
733 bool incomplete
= false;
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;
741 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
743 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
744 incomplete
= ERRNO_IS_PRIVILEGE(r
);
747 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
752 (*ret
)->incomplete
= incomplete
;
757 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
759 iterator
->nss_iterating
= false;
763 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
766 if (iterator
->synthesize_root
) {
767 iterator
->synthesize_root
= false;
769 return synthetic_root_user_build(ret
);
772 if (iterator
->synthesize_nobody
) {
773 iterator
->synthesize_nobody
= false;
775 return synthetic_nobody_user_build(ret
);
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)
786 static int synthetic_root_group_build(GroupRecord
**ret
) {
787 return group_record_build(
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"))));
794 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
795 return group_record_build(
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"))));
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
;
807 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
810 r
= json_build(&query
, JSON_BUILD_OBJECT(
811 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
815 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
819 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
821 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
826 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
827 r
= userdb_iterator_block_nss_systemd(iterator
);
829 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
835 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
836 if (streq(name
, "root"))
837 return synthetic_root_group_build(ret
);
839 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
840 return synthetic_nobody_group_build(ret
);
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
;
851 if (!gid_is_valid(gid
))
854 r
= json_build(&query
, JSON_BUILD_OBJECT(
855 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
859 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
863 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
865 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
870 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
871 r
= userdb_iterator_block_nss_systemd(iterator
);
873 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
879 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
881 return synthetic_root_group_build(ret
);
883 if (gid
== GID_NOBODY
&& synthesize_nobody())
884 return synthetic_nobody_group_build(ret
);
890 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
891 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
896 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
900 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
902 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
904 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
905 r
= userdb_iterator_block_nss_systemd(iterator
);
910 iterator
->nss_iterating
= true;
914 *ret
= TAKE_PTR(iterator
);
918 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
922 assert(iterator
->what
== LOOKUP_GROUP
);
924 if (iterator
->nss_iterating
) {
930 _cleanup_free_
char *buffer
= NULL
;
931 bool incomplete
= false;
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;
939 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
941 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
942 incomplete
= ERRNO_IS_PRIVILEGE(r
);
945 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
950 (*ret
)->incomplete
= incomplete
;
955 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
957 iterator
->nss_iterating
= false;
961 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
963 if (iterator
->synthesize_root
) {
964 iterator
->synthesize_root
= false;
966 return synthetic_root_group_build(ret
);
969 if (iterator
->synthesize_nobody
) {
970 iterator
->synthesize_nobody
= false;
972 return synthetic_nobody_group_build(ret
);
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)
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
;
990 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
993 r
= json_build(&query
, JSON_BUILD_OBJECT(
994 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
998 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
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
))
1006 r
= userdb_iterator_block_nss_systemd(iterator
);
1010 iterator
->filter_user_name
= strdup(name
);
1011 if (!iterator
->filter_user_name
)
1015 iterator
->nss_iterating
= true;
1021 *ret
= TAKE_PTR(iterator
);
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
;
1033 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1036 r
= json_build(&query
, JSON_BUILD_OBJECT(
1037 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1041 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
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
))
1049 r
= userdb_iterator_block_nss_systemd(iterator
);
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
);
1056 iterator
->members_of_group
= strv_copy(gr
->members
);
1057 if (!iterator
->members_of_group
)
1060 iterator
->index_members_of_group
= 0;
1062 iterator
->found_group_name
= strdup(name
);
1063 if (!iterator
->found_group_name
)
1071 *ret
= TAKE_PTR(iterator
);
1076 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1077 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1082 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
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
))
1090 r
= userdb_iterator_block_nss_systemd(iterator
);
1095 iterator
->nss_iterating
= true;
1101 *ret
= TAKE_PTR(iterator
);
1106 int membershipdb_iterator_get(
1107 UserDBIterator
*iterator
,
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
) {
1120 if (!iterator
->nss_iterating
)
1123 assert(!iterator
->found_user_name
);
1129 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1133 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1134 strv_isempty(g
->gr_mem
));
1137 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1141 if (iterator
->filter_user_name
)
1142 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1144 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1145 if (!iterator
->members_of_group
)
1148 iterator
->index_members_of_group
= 0;
1150 iterator
->nss_iterating
= false;
1156 assert(iterator
->found_group_name
);
1157 assert(iterator
->members_of_group
);
1158 assert(!iterator
->found_user_name
);
1160 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1161 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1164 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1170 cg
= strdup(iterator
->found_group_name
);
1176 *ret_user
= TAKE_PTR(cu
);
1179 *ret_group
= TAKE_PTR(cg
);
1181 iterator
->index_members_of_group
++;
1185 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1186 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1189 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1190 if (r
< 0 && iterator
->n_found
> 0)
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
;
1204 r
= membershipdb_by_group(name
, flags
, &iterator
);
1209 _cleanup_free_
char *user_name
= NULL
;
1211 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1217 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1225 *ret
= TAKE_PTR(members
);
1229 int userdb_block_nss_systemd(int b
) {
1230 _cleanup_(dlclosep
) void *dl
= NULL
;
1231 int (*call
)(bool b
);
1233 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1235 dl
= dlopen(ROOTLIBDIR
"/libnss_systemd.so.2", RTLD_LAZY
|RTLD_NODELETE
);
1237 /* If the file isn't installed, don't complain loudly */
1238 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1242 call
= (int (*)(bool b
)) dlsym(dl
, "_nss_systemd_block");
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());