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_allocated(&iterator
->links
, &link_hash_ops
);
382 return log_debug_errno(r
, "Failed to allocate set: %m");
384 r
= set_put(iterator
->links
, vl
);
386 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
392 static int userdb_start_query(
393 UserDBIterator
*iterator
,
399 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
400 _cleanup_(closedirp
) DIR *d
= NULL
;
408 e
= getenv("SYSTEMD_BYPASS_USERDB");
410 r
= parse_boolean(e
);
414 except
= strv_split(e
, ":");
420 e
= getenv("SYSTEMD_ONLY_USERDB");
422 only
= strv_split(e
, ":");
427 /* First, let's talk to the multiplexer, if we can */
428 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_AVOID_DYNAMIC_USER
|USERDB_AVOID_NSS
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
429 !strv_contains(except
, "io.systemd.Multiplexer") &&
430 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
431 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
433 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
435 return log_debug_errno(r
, "Unable to set service JSON field: %m");
437 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
439 iterator
->nss_covered
= true; /* The multiplexer does NSS */
444 d
= opendir("/run/systemd/userdb/");
452 FOREACH_DIRENT(de
, d
, return -errno
) {
453 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
454 _cleanup_free_
char *p
= NULL
;
457 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
460 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
461 streq(de
->d_name
, "io.systemd.DynamicUser"))
464 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
465 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
466 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
468 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
469 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
472 if (strv_contains(except
, de
->d_name
))
475 if (only
&& !strv_contains(only
, de
->d_name
))
478 p
= path_join("/run/systemd/userdb/", de
->d_name
);
482 patched_query
= json_variant_ref(query
);
483 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
485 return log_debug_errno(r
, "Unable to set service JSON field: %m");
487 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
488 if (is_nss
&& r
>= 0) /* Turn off fallback NSS if we found the NSS service and could connect
490 iterator
->nss_covered
= true;
492 if (ret
== 0 && r
< 0)
496 if (set_isempty(iterator
->links
))
497 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
499 /* We connected to some services, in this case, ignore the ones we failed on */
503 static int userdb_process(
504 UserDBIterator
*iterator
,
505 UserRecord
**ret_user_record
,
506 GroupRecord
**ret_group_record
,
507 char **ret_user_name
,
508 char **ret_group_name
) {
515 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
517 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
519 iterator
->found_user
= user_record_unref(iterator
->found_user
);
521 if (ret_group_record
)
522 *ret_group_record
= NULL
;
524 *ret_user_name
= NULL
;
526 *ret_group_name
= NULL
;
531 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
532 if (ret_group_record
)
533 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
535 iterator
->found_group
= group_record_unref(iterator
->found_group
);
538 *ret_user_record
= NULL
;
540 *ret_user_name
= NULL
;
542 *ret_group_name
= NULL
;
547 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
549 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
551 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
554 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
556 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
559 *ret_user_record
= NULL
;
560 if (ret_group_record
)
561 *ret_group_record
= NULL
;
566 if (set_isempty(iterator
->links
)) {
567 if (iterator
->error
== 0)
570 return -abs(iterator
->error
);
573 if (!iterator
->event
)
576 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
582 static int synthetic_root_user_build(UserRecord
**ret
) {
583 return user_record_build(
585 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
586 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
587 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
588 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
589 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
592 static int synthetic_nobody_user_build(UserRecord
**ret
) {
593 return user_record_build(
595 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
596 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
597 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
598 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
599 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
600 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
603 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
604 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
605 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
608 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
611 r
= json_build(&query
, JSON_BUILD_OBJECT(
612 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
616 iterator
= userdb_iterator_new(LOOKUP_USER
);
620 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
622 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
627 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
628 /* Make sure the NSS lookup doesn't recurse back to us. */
630 r
= userdb_iterator_block_nss_systemd(iterator
);
632 /* Client-side NSS fallback */
633 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
639 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
640 if (streq(name
, "root"))
641 return synthetic_root_user_build(ret
);
643 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
644 return synthetic_nobody_user_build(ret
);
650 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
651 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
652 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
655 if (!uid_is_valid(uid
))
658 r
= json_build(&query
, JSON_BUILD_OBJECT(
659 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
663 iterator
= userdb_iterator_new(LOOKUP_USER
);
667 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
669 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
674 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
675 r
= userdb_iterator_block_nss_systemd(iterator
);
677 /* Client-side NSS fallback */
678 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
684 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
686 return synthetic_root_user_build(ret
);
688 if (uid
== UID_NOBODY
&& synthesize_nobody())
689 return synthetic_nobody_user_build(ret
);
695 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
696 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
701 iterator
= userdb_iterator_new(LOOKUP_USER
);
705 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
707 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
709 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
710 r
= userdb_iterator_block_nss_systemd(iterator
);
715 iterator
->nss_iterating
= true;
719 *ret
= TAKE_PTR(iterator
);
723 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
727 assert(iterator
->what
== LOOKUP_USER
);
729 if (iterator
->nss_iterating
) {
732 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
733 * the more traditional sources, which are probably good to show first. */
737 _cleanup_free_
char *buffer
= NULL
;
738 bool incomplete
= false;
741 if (streq_ptr(pw
->pw_name
, "root"))
742 iterator
->synthesize_root
= false;
743 if (pw
->pw_uid
== UID_NOBODY
)
744 iterator
->synthesize_nobody
= false;
746 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
748 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
749 incomplete
= ERRNO_IS_PRIVILEGE(r
);
752 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
757 (*ret
)->incomplete
= incomplete
;
762 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
764 iterator
->nss_iterating
= false;
768 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
771 if (iterator
->synthesize_root
) {
772 iterator
->synthesize_root
= false;
774 return synthetic_root_user_build(ret
);
777 if (iterator
->synthesize_nobody
) {
778 iterator
->synthesize_nobody
= false;
780 return synthetic_nobody_user_build(ret
);
784 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
785 if (r
< 0 && iterator
->n_found
> 0)
791 static int synthetic_root_group_build(GroupRecord
**ret
) {
792 return group_record_build(
794 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
795 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
796 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
799 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
800 return group_record_build(
802 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
803 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
804 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
807 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
808 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
809 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
812 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
815 r
= json_build(&query
, JSON_BUILD_OBJECT(
816 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
820 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
824 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
826 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
831 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
832 r
= userdb_iterator_block_nss_systemd(iterator
);
834 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
840 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
841 if (streq(name
, "root"))
842 return synthetic_root_group_build(ret
);
844 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
845 return synthetic_nobody_group_build(ret
);
851 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
852 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
853 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
856 if (!gid_is_valid(gid
))
859 r
= json_build(&query
, JSON_BUILD_OBJECT(
860 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
864 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
868 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
870 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
875 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
876 r
= userdb_iterator_block_nss_systemd(iterator
);
878 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
884 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
886 return synthetic_root_group_build(ret
);
888 if (gid
== GID_NOBODY
&& synthesize_nobody())
889 return synthetic_nobody_group_build(ret
);
895 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
896 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
901 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
905 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
907 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
909 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
910 r
= userdb_iterator_block_nss_systemd(iterator
);
915 iterator
->nss_iterating
= true;
919 *ret
= TAKE_PTR(iterator
);
923 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
927 assert(iterator
->what
== LOOKUP_GROUP
);
929 if (iterator
->nss_iterating
) {
935 _cleanup_free_
char *buffer
= NULL
;
936 bool incomplete
= false;
939 if (streq_ptr(gr
->gr_name
, "root"))
940 iterator
->synthesize_root
= false;
941 if (gr
->gr_gid
== GID_NOBODY
)
942 iterator
->synthesize_nobody
= false;
944 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
946 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
947 incomplete
= ERRNO_IS_PRIVILEGE(r
);
950 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
955 (*ret
)->incomplete
= incomplete
;
960 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
962 iterator
->nss_iterating
= false;
966 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
968 if (iterator
->synthesize_root
) {
969 iterator
->synthesize_root
= false;
971 return synthetic_root_group_build(ret
);
974 if (iterator
->synthesize_nobody
) {
975 iterator
->synthesize_nobody
= false;
977 return synthetic_nobody_group_build(ret
);
981 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
982 if (r
< 0 && iterator
->n_found
> 0)
988 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
989 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
990 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
995 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
998 r
= json_build(&query
, JSON_BUILD_OBJECT(
999 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
1003 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1007 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1008 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1011 r
= userdb_iterator_block_nss_systemd(iterator
);
1015 iterator
->filter_user_name
= strdup(name
);
1016 if (!iterator
->filter_user_name
)
1020 iterator
->nss_iterating
= true;
1026 *ret
= TAKE_PTR(iterator
);
1030 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1031 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1032 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1033 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1038 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1041 r
= json_build(&query
, JSON_BUILD_OBJECT(
1042 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1046 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1050 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1051 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1054 r
= userdb_iterator_block_nss_systemd(iterator
);
1058 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1059 (void) nss_group_record_by_name(name
, false, &gr
);
1061 iterator
->members_of_group
= strv_copy(gr
->members
);
1062 if (!iterator
->members_of_group
)
1065 iterator
->index_members_of_group
= 0;
1067 iterator
->found_group_name
= strdup(name
);
1068 if (!iterator
->found_group_name
)
1076 *ret
= TAKE_PTR(iterator
);
1081 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1082 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1087 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1091 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1092 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1095 r
= userdb_iterator_block_nss_systemd(iterator
);
1100 iterator
->nss_iterating
= true;
1106 *ret
= TAKE_PTR(iterator
);
1111 int membershipdb_iterator_get(
1112 UserDBIterator
*iterator
,
1121 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1122 if (!iterator
->members_of_group
) {
1125 if (!iterator
->nss_iterating
)
1128 assert(!iterator
->found_user_name
);
1134 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1138 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1139 strv_isempty(g
->gr_mem
));
1142 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1146 if (iterator
->filter_user_name
)
1147 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1149 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1150 if (!iterator
->members_of_group
)
1153 iterator
->index_members_of_group
= 0;
1155 iterator
->nss_iterating
= false;
1161 assert(iterator
->found_group_name
);
1162 assert(iterator
->members_of_group
);
1163 assert(!iterator
->found_user_name
);
1165 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1166 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1169 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1175 cg
= strdup(iterator
->found_group_name
);
1181 *ret_user
= TAKE_PTR(cu
);
1184 *ret_group
= TAKE_PTR(cg
);
1186 iterator
->index_members_of_group
++;
1190 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1191 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1194 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1195 if (r
< 0 && iterator
->n_found
> 0)
1201 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1202 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1203 _cleanup_strv_free_
char **members
= NULL
;
1209 r
= membershipdb_by_group(name
, flags
, &iterator
);
1214 _cleanup_free_
char *user_name
= NULL
;
1216 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1222 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1230 *ret
= TAKE_PTR(members
);
1234 int userdb_block_nss_systemd(int b
) {
1235 _cleanup_(dlclosep
) void *dl
= NULL
;
1236 int (*call
)(bool b
);
1238 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1240 dl
= dlopen(ROOTLIBDIR
"libnss_systemd.so.2", RTLD_LAZY
|RTLD_NODELETE
);
1242 /* If the file isn't installed, don't complain loudly */
1243 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1247 call
= (int (*)(bool b
)) dlsym(dl
, "_nss_systemd_block");
1249 /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
1250 return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD
),
1251 "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());