1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "dirent-util.h"
6 #include "dlfcn-util.h"
7 #include "errno-util.h"
9 #include "group-record-nss.h"
10 #include "missing_syscall.h"
11 #include "parse-util.h"
13 #include "socket-util.h"
15 #include "user-record-nss.h"
16 #include "user-util.h"
20 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops
, void, trivial_hash_func
, trivial_compare_func
, Varlink
, varlink_unref
);
22 typedef enum LookupWhat
{
29 struct UserDBIterator
{
34 bool synthesize_root
:1;
35 bool synthesize_nobody
:1;
36 bool nss_systemd_blocked
:1;
40 UserRecord
*found_user
; /* when .what == LOOKUP_USER */
41 GroupRecord
*found_group
; /* when .what == LOOKUP_GROUP */
43 char *found_user_name
, *found_group_name
; /* when .what == LOOKUP_MEMBERSHIP */
44 char **members_of_group
;
45 size_t index_members_of_group
;
46 char *filter_user_name
;
49 UserDBIterator
* userdb_iterator_free(UserDBIterator
*iterator
) {
53 set_free(iterator
->links
);
55 switch (iterator
->what
) {
58 user_record_unref(iterator
->found_user
);
60 if (iterator
->nss_iterating
)
66 group_record_unref(iterator
->found_group
);
68 if (iterator
->nss_iterating
)
73 case LOOKUP_MEMBERSHIP
:
74 free(iterator
->found_user_name
);
75 free(iterator
->found_group_name
);
76 strv_free(iterator
->members_of_group
);
77 free(iterator
->filter_user_name
);
79 if (iterator
->nss_iterating
)
85 assert_not_reached("Unexpected state?");
88 sd_event_unref(iterator
->event
);
90 if (iterator
->nss_systemd_blocked
)
91 assert_se(userdb_block_nss_systemd(false) >= 0);
93 return mfree(iterator
);
96 static UserDBIterator
* userdb_iterator_new(LookupWhat what
) {
100 assert(what
< _LOOKUP_WHAT_MAX
);
102 i
= new(UserDBIterator
, 1);
106 *i
= (UserDBIterator
) {
113 static int userdb_iterator_block_nss_systemd(UserDBIterator
*iterator
) {
118 if (iterator
->nss_systemd_blocked
)
121 r
= userdb_block_nss_systemd(true);
125 iterator
->nss_systemd_blocked
= true;
129 struct user_group_data
{
134 static void user_group_data_release(struct user_group_data
*d
) {
135 json_variant_unref(d
->record
);
138 static int userdb_on_query_reply(
140 JsonVariant
*parameters
,
141 const char *error_id
,
142 VarlinkReplyFlags flags
,
145 UserDBIterator
*iterator
= userdata
;
151 log_debug("Got lookup error: %s", error_id
);
153 if (STR_IN_SET(error_id
,
154 "io.systemd.UserDatabase.NoRecordFound",
155 "io.systemd.UserDatabase.ConflictingRecordFound"))
157 else if (streq(error_id
, "io.systemd.UserDatabase.ServiceNotAvailable"))
159 else if (streq(error_id
, VARLINK_ERROR_TIMEOUT
))
167 switch (iterator
->what
) {
170 _cleanup_(user_group_data_release
) struct user_group_data user_data
= {};
172 static const JsonDispatch dispatch_table
[] = {
173 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
174 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
177 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
179 assert_se(!iterator
->found_user
);
181 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &user_data
);
185 if (!user_data
.record
) {
186 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
190 hr
= user_record_new();
196 r
= user_record_load(hr
, user_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
201 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "User record does not carry service information, refusing.");
205 hr
->incomplete
= user_data
.incomplete
;
207 /* We match the root user by the name since the name is our primary key. We match the nobody
208 * use by UID though, since the name might differ on OSes */
209 if (streq_ptr(hr
->user_name
, "root"))
210 iterator
->synthesize_root
= false;
211 if (hr
->uid
== UID_NOBODY
)
212 iterator
->synthesize_nobody
= false;
214 iterator
->found_user
= TAKE_PTR(hr
);
217 /* More stuff coming? then let's just exit cleanly here */
218 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
221 /* Otherwise, let's remove this link and exit cleanly then */
227 _cleanup_(user_group_data_release
) struct user_group_data group_data
= {};
229 static const JsonDispatch dispatch_table
[] = {
230 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
231 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
234 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
236 assert_se(!iterator
->found_group
);
238 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &group_data
);
242 if (!group_data
.record
) {
243 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
247 g
= group_record_new();
253 r
= group_record_load(g
, group_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
258 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Group record does not carry service information, refusing.");
262 g
->incomplete
= group_data
.incomplete
;
264 if (streq_ptr(g
->group_name
, "root"))
265 iterator
->synthesize_root
= false;
266 if (g
->gid
== GID_NOBODY
)
267 iterator
->synthesize_nobody
= false;
269 iterator
->found_group
= TAKE_PTR(g
);
272 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
279 case LOOKUP_MEMBERSHIP
: {
280 struct membership_data
{
281 const char *user_name
;
282 const char *group_name
;
283 } membership_data
= {};
285 static const JsonDispatch dispatch_table
[] = {
286 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, user_name
), JSON_SAFE
},
287 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, group_name
), JSON_SAFE
},
291 assert(!iterator
->found_user_name
);
292 assert(!iterator
->found_group_name
);
294 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &membership_data
);
298 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
299 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
301 iterator
->found_user_name
= strdup(membership_data
.user_name
);
302 if (!iterator
->found_user_name
) {
307 iterator
->found_group_name
= strdup(membership_data
.group_name
);
308 if (!iterator
->found_group_name
) {
315 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
323 assert_not_reached("unexpected lookup");
327 /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
328 * errors if at least one connection ended cleanly */
329 if (r
== -ESRCH
|| iterator
->error
== 0)
330 iterator
->error
= -r
;
332 assert_se(set_remove(iterator
->links
, link
) == link
);
333 link
= varlink_unref(link
);
337 static int userdb_connect(
338 UserDBIterator
*iterator
,
342 JsonVariant
*query
) {
344 _cleanup_(varlink_unrefp
) Varlink
*vl
= NULL
;
351 r
= varlink_connect_address(&vl
, path
);
353 return log_debug_errno(r
, "Unable to connect to %s: %m", path
);
355 varlink_set_userdata(vl
, iterator
);
357 if (!iterator
->event
) {
358 r
= sd_event_new(&iterator
->event
);
360 return log_debug_errno(r
, "Unable to allocate event loop: %m");
363 r
= varlink_attach_event(vl
, iterator
->event
, SD_EVENT_PRIORITY_NORMAL
);
365 return log_debug_errno(r
, "Failed to attach varlink connection to event loop: %m");
367 (void) varlink_set_description(vl
, path
);
369 r
= varlink_bind_reply(vl
, userdb_on_query_reply
);
371 return log_debug_errno(r
, "Failed to bind reply callback: %m");
374 r
= varlink_observe(vl
, method
, query
);
376 r
= varlink_invoke(vl
, method
, query
);
378 return log_debug_errno(r
, "Failed to invoke varlink method: %m");
380 r
= set_ensure_consume(&iterator
->links
, &link_hash_ops
, TAKE_PTR(vl
));
382 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
386 static int userdb_start_query(
387 UserDBIterator
*iterator
,
393 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
394 _cleanup_(closedirp
) DIR *d
= NULL
;
402 e
= getenv("SYSTEMD_BYPASS_USERDB");
404 r
= parse_boolean(e
);
408 except
= strv_split(e
, ":");
414 e
= getenv("SYSTEMD_ONLY_USERDB");
416 only
= strv_split(e
, ":");
421 /* First, let's talk to the multiplexer, if we can */
422 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_AVOID_DYNAMIC_USER
|USERDB_AVOID_NSS
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
423 !strv_contains(except
, "io.systemd.Multiplexer") &&
424 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
425 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
427 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
429 return log_debug_errno(r
, "Unable to set service JSON field: %m");
431 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
433 iterator
->nss_covered
= true; /* The multiplexer does NSS */
438 d
= opendir("/run/systemd/userdb/");
446 FOREACH_DIRENT(de
, d
, return -errno
) {
447 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
448 _cleanup_free_
char *p
= NULL
;
451 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
454 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
455 streq(de
->d_name
, "io.systemd.DynamicUser"))
458 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
459 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
460 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
462 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
463 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
466 if (strv_contains(except
, de
->d_name
))
469 if (only
&& !strv_contains(only
, de
->d_name
))
472 p
= path_join("/run/systemd/userdb/", de
->d_name
);
476 patched_query
= json_variant_ref(query
);
477 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
479 return log_debug_errno(r
, "Unable to set service JSON field: %m");
481 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
482 if (is_nss
&& r
>= 0) /* Turn off fallback NSS if we found the NSS service and could connect
484 iterator
->nss_covered
= true;
486 if (ret
== 0 && r
< 0)
490 if (set_isempty(iterator
->links
))
491 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
493 /* We connected to some services, in this case, ignore the ones we failed on */
497 static int userdb_process(
498 UserDBIterator
*iterator
,
499 UserRecord
**ret_user_record
,
500 GroupRecord
**ret_group_record
,
501 char **ret_user_name
,
502 char **ret_group_name
) {
509 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
511 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
513 iterator
->found_user
= user_record_unref(iterator
->found_user
);
515 if (ret_group_record
)
516 *ret_group_record
= NULL
;
518 *ret_user_name
= NULL
;
520 *ret_group_name
= NULL
;
525 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
526 if (ret_group_record
)
527 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
529 iterator
->found_group
= group_record_unref(iterator
->found_group
);
532 *ret_user_record
= NULL
;
534 *ret_user_name
= NULL
;
536 *ret_group_name
= NULL
;
541 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
543 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
545 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
548 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
550 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
553 *ret_user_record
= NULL
;
554 if (ret_group_record
)
555 *ret_group_record
= NULL
;
560 if (set_isempty(iterator
->links
)) {
561 if (iterator
->error
== 0)
564 return -abs(iterator
->error
);
567 if (!iterator
->event
)
570 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
576 static int synthetic_root_user_build(UserRecord
**ret
) {
577 return user_record_build(
579 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
580 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
581 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
582 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
583 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
586 static int synthetic_nobody_user_build(UserRecord
**ret
) {
587 return user_record_build(
589 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
590 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
591 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
592 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
593 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
594 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
597 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
598 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
599 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
602 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
605 r
= json_build(&query
, JSON_BUILD_OBJECT(
606 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
610 iterator
= userdb_iterator_new(LOOKUP_USER
);
614 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
616 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
621 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
622 /* Make sure the NSS lookup doesn't recurse back to us. */
624 r
= userdb_iterator_block_nss_systemd(iterator
);
626 /* Client-side NSS fallback */
627 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
633 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
634 if (streq(name
, "root"))
635 return synthetic_root_user_build(ret
);
637 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
638 return synthetic_nobody_user_build(ret
);
644 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
645 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
646 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
649 if (!uid_is_valid(uid
))
652 r
= json_build(&query
, JSON_BUILD_OBJECT(
653 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
657 iterator
= userdb_iterator_new(LOOKUP_USER
);
661 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
663 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
668 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
669 r
= userdb_iterator_block_nss_systemd(iterator
);
671 /* Client-side NSS fallback */
672 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
678 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
680 return synthetic_root_user_build(ret
);
682 if (uid
== UID_NOBODY
&& synthesize_nobody())
683 return synthetic_nobody_user_build(ret
);
689 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
690 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
695 iterator
= userdb_iterator_new(LOOKUP_USER
);
699 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
701 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
703 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
704 r
= userdb_iterator_block_nss_systemd(iterator
);
709 iterator
->nss_iterating
= true;
713 *ret
= TAKE_PTR(iterator
);
717 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
721 assert(iterator
->what
== LOOKUP_USER
);
723 if (iterator
->nss_iterating
) {
726 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
727 * the more traditional sources, which are probably good to show first. */
731 _cleanup_free_
char *buffer
= NULL
;
732 bool incomplete
= false;
735 if (streq_ptr(pw
->pw_name
, "root"))
736 iterator
->synthesize_root
= false;
737 if (pw
->pw_uid
== UID_NOBODY
)
738 iterator
->synthesize_nobody
= false;
740 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
742 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
743 incomplete
= ERRNO_IS_PRIVILEGE(r
);
746 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
751 (*ret
)->incomplete
= incomplete
;
756 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
758 iterator
->nss_iterating
= false;
762 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
765 if (iterator
->synthesize_root
) {
766 iterator
->synthesize_root
= false;
768 return synthetic_root_user_build(ret
);
771 if (iterator
->synthesize_nobody
) {
772 iterator
->synthesize_nobody
= false;
774 return synthetic_nobody_user_build(ret
);
778 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
779 if (r
< 0 && iterator
->n_found
> 0)
785 static int synthetic_root_group_build(GroupRecord
**ret
) {
786 return group_record_build(
788 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
789 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
790 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
793 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
794 return group_record_build(
796 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
797 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
798 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
801 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
802 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
803 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
806 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
809 r
= json_build(&query
, JSON_BUILD_OBJECT(
810 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
814 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
818 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
820 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
825 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
826 r
= userdb_iterator_block_nss_systemd(iterator
);
828 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
834 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
835 if (streq(name
, "root"))
836 return synthetic_root_group_build(ret
);
838 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
839 return synthetic_nobody_group_build(ret
);
845 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
846 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
847 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
850 if (!gid_is_valid(gid
))
853 r
= json_build(&query
, JSON_BUILD_OBJECT(
854 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
858 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
862 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
864 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
869 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
870 r
= userdb_iterator_block_nss_systemd(iterator
);
872 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
878 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
880 return synthetic_root_group_build(ret
);
882 if (gid
== GID_NOBODY
&& synthesize_nobody())
883 return synthetic_nobody_group_build(ret
);
889 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
890 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
895 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
899 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
901 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
903 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
904 r
= userdb_iterator_block_nss_systemd(iterator
);
909 iterator
->nss_iterating
= true;
913 *ret
= TAKE_PTR(iterator
);
917 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
921 assert(iterator
->what
== LOOKUP_GROUP
);
923 if (iterator
->nss_iterating
) {
929 _cleanup_free_
char *buffer
= NULL
;
930 bool incomplete
= false;
933 if (streq_ptr(gr
->gr_name
, "root"))
934 iterator
->synthesize_root
= false;
935 if (gr
->gr_gid
== GID_NOBODY
)
936 iterator
->synthesize_nobody
= false;
938 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
940 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
941 incomplete
= ERRNO_IS_PRIVILEGE(r
);
944 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
949 (*ret
)->incomplete
= incomplete
;
954 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
956 iterator
->nss_iterating
= false;
960 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
962 if (iterator
->synthesize_root
) {
963 iterator
->synthesize_root
= false;
965 return synthetic_root_group_build(ret
);
968 if (iterator
->synthesize_nobody
) {
969 iterator
->synthesize_nobody
= false;
971 return synthetic_nobody_group_build(ret
);
975 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
976 if (r
< 0 && iterator
->n_found
> 0)
982 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
983 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
984 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
989 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
992 r
= json_build(&query
, JSON_BUILD_OBJECT(
993 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
997 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1001 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1002 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1005 r
= userdb_iterator_block_nss_systemd(iterator
);
1009 iterator
->filter_user_name
= strdup(name
);
1010 if (!iterator
->filter_user_name
)
1014 iterator
->nss_iterating
= true;
1020 *ret
= TAKE_PTR(iterator
);
1024 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1025 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1026 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1027 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1032 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1035 r
= json_build(&query
, JSON_BUILD_OBJECT(
1036 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1040 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1044 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1045 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1048 r
= userdb_iterator_block_nss_systemd(iterator
);
1052 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1053 (void) nss_group_record_by_name(name
, false, &gr
);
1055 iterator
->members_of_group
= strv_copy(gr
->members
);
1056 if (!iterator
->members_of_group
)
1059 iterator
->index_members_of_group
= 0;
1061 iterator
->found_group_name
= strdup(name
);
1062 if (!iterator
->found_group_name
)
1070 *ret
= TAKE_PTR(iterator
);
1075 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1076 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1081 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1085 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1086 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1089 r
= userdb_iterator_block_nss_systemd(iterator
);
1094 iterator
->nss_iterating
= true;
1100 *ret
= TAKE_PTR(iterator
);
1105 int membershipdb_iterator_get(
1106 UserDBIterator
*iterator
,
1115 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1116 if (!iterator
->members_of_group
) {
1119 if (!iterator
->nss_iterating
)
1122 assert(!iterator
->found_user_name
);
1128 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1132 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1133 strv_isempty(g
->gr_mem
));
1136 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1140 if (iterator
->filter_user_name
)
1141 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1143 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1144 if (!iterator
->members_of_group
)
1147 iterator
->index_members_of_group
= 0;
1149 iterator
->nss_iterating
= false;
1155 assert(iterator
->found_group_name
);
1156 assert(iterator
->members_of_group
);
1157 assert(!iterator
->found_user_name
);
1159 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1160 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1163 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1169 cg
= strdup(iterator
->found_group_name
);
1175 *ret_user
= TAKE_PTR(cu
);
1178 *ret_group
= TAKE_PTR(cg
);
1180 iterator
->index_members_of_group
++;
1184 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1185 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1188 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1189 if (r
< 0 && iterator
->n_found
> 0)
1195 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1196 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1197 _cleanup_strv_free_
char **members
= NULL
;
1203 r
= membershipdb_by_group(name
, flags
, &iterator
);
1208 _cleanup_free_
char *user_name
= NULL
;
1210 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1216 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1224 *ret
= TAKE_PTR(members
);
1228 int userdb_block_nss_systemd(int b
) {
1229 _cleanup_(dlclosep
) void *dl
= NULL
;
1230 int (*call
)(bool b
);
1232 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1234 dl
= dlopen(ROOTLIBDIR
"libnss_systemd.so.2", RTLD_LAZY
|RTLD_NODELETE
);
1236 /* If the file isn't installed, don't complain loudly */
1237 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1241 call
= (int (*)(bool b
)) dlsym(dl
, "_nss_systemd_block");
1243 /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
1244 return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD
),
1245 "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());