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
, "io.systemd.UserDatabase.EnumerationNotSupported"))
161 else if (streq(error_id
, VARLINK_ERROR_TIMEOUT
))
169 switch (iterator
->what
) {
172 _cleanup_(user_group_data_release
) struct user_group_data user_data
= {};
174 static const JsonDispatch dispatch_table
[] = {
175 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
176 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
179 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
181 assert_se(!iterator
->found_user
);
183 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &user_data
);
187 if (!user_data
.record
) {
188 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
192 hr
= user_record_new();
198 r
= user_record_load(hr
, user_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
203 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "User record does not carry service information, refusing.");
207 hr
->incomplete
= user_data
.incomplete
;
209 /* We match the root user by the name since the name is our primary key. We match the nobody
210 * use by UID though, since the name might differ on OSes */
211 if (streq_ptr(hr
->user_name
, "root"))
212 iterator
->synthesize_root
= false;
213 if (hr
->uid
== UID_NOBODY
)
214 iterator
->synthesize_nobody
= false;
216 iterator
->found_user
= TAKE_PTR(hr
);
219 /* More stuff coming? then let's just exit cleanly here */
220 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
223 /* Otherwise, let's remove this link and exit cleanly then */
229 _cleanup_(user_group_data_release
) struct user_group_data group_data
= {};
231 static const JsonDispatch dispatch_table
[] = {
232 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
233 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
236 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
238 assert_se(!iterator
->found_group
);
240 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &group_data
);
244 if (!group_data
.record
) {
245 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
249 g
= group_record_new();
255 r
= group_record_load(g
, group_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
260 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Group record does not carry service information, refusing.");
264 g
->incomplete
= group_data
.incomplete
;
266 if (streq_ptr(g
->group_name
, "root"))
267 iterator
->synthesize_root
= false;
268 if (g
->gid
== GID_NOBODY
)
269 iterator
->synthesize_nobody
= false;
271 iterator
->found_group
= TAKE_PTR(g
);
274 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
281 case LOOKUP_MEMBERSHIP
: {
282 struct membership_data
{
283 const char *user_name
;
284 const char *group_name
;
285 } membership_data
= {};
287 static const JsonDispatch dispatch_table
[] = {
288 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, user_name
), JSON_SAFE
},
289 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, group_name
), JSON_SAFE
},
293 assert(!iterator
->found_user_name
);
294 assert(!iterator
->found_group_name
);
296 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &membership_data
);
300 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
301 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
303 iterator
->found_user_name
= strdup(membership_data
.user_name
);
304 if (!iterator
->found_user_name
) {
309 iterator
->found_group_name
= strdup(membership_data
.group_name
);
310 if (!iterator
->found_group_name
) {
317 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
325 assert_not_reached("unexpected lookup");
329 /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
330 * errors if at least one connection ended cleanly */
331 if (r
== -ESRCH
|| iterator
->error
== 0)
332 iterator
->error
= -r
;
334 assert_se(set_remove(iterator
->links
, link
) == link
);
335 link
= varlink_unref(link
);
339 static int userdb_connect(
340 UserDBIterator
*iterator
,
344 JsonVariant
*query
) {
346 _cleanup_(varlink_unrefp
) Varlink
*vl
= NULL
;
353 r
= varlink_connect_address(&vl
, path
);
355 return log_debug_errno(r
, "Unable to connect to %s: %m", path
);
357 varlink_set_userdata(vl
, iterator
);
359 if (!iterator
->event
) {
360 r
= sd_event_new(&iterator
->event
);
362 return log_debug_errno(r
, "Unable to allocate event loop: %m");
365 r
= varlink_attach_event(vl
, iterator
->event
, SD_EVENT_PRIORITY_NORMAL
);
367 return log_debug_errno(r
, "Failed to attach varlink connection to event loop: %m");
369 (void) varlink_set_description(vl
, path
);
371 r
= varlink_bind_reply(vl
, userdb_on_query_reply
);
373 return log_debug_errno(r
, "Failed to bind reply callback: %m");
376 r
= varlink_observe(vl
, method
, query
);
378 r
= varlink_invoke(vl
, method
, query
);
380 return log_debug_errno(r
, "Failed to invoke varlink method: %m");
382 r
= set_ensure_consume(&iterator
->links
, &link_hash_ops
, TAKE_PTR(vl
));
384 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
388 static int userdb_start_query(
389 UserDBIterator
*iterator
,
395 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
396 _cleanup_(closedirp
) DIR *d
= NULL
;
404 e
= getenv("SYSTEMD_BYPASS_USERDB");
406 r
= parse_boolean(e
);
410 except
= strv_split(e
, ":");
416 e
= getenv("SYSTEMD_ONLY_USERDB");
418 only
= strv_split(e
, ":");
423 /* First, let's talk to the multiplexer, if we can */
424 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_AVOID_DYNAMIC_USER
|USERDB_AVOID_NSS
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
425 !strv_contains(except
, "io.systemd.Multiplexer") &&
426 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
427 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
429 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
431 return log_debug_errno(r
, "Unable to set service JSON field: %m");
433 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
435 iterator
->nss_covered
= true; /* The multiplexer does NSS */
440 d
= opendir("/run/systemd/userdb/");
448 FOREACH_DIRENT(de
, d
, return -errno
) {
449 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
450 _cleanup_free_
char *p
= NULL
;
453 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
456 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
457 streq(de
->d_name
, "io.systemd.DynamicUser"))
460 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
461 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
462 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
464 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
465 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
468 if (strv_contains(except
, de
->d_name
))
471 if (only
&& !strv_contains(only
, de
->d_name
))
474 p
= path_join("/run/systemd/userdb/", de
->d_name
);
478 patched_query
= json_variant_ref(query
);
479 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
481 return log_debug_errno(r
, "Unable to set service JSON field: %m");
483 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
484 if (is_nss
&& r
>= 0) /* Turn off fallback NSS if we found the NSS service and could connect
486 iterator
->nss_covered
= true;
488 if (ret
== 0 && r
< 0)
492 if (set_isempty(iterator
->links
))
493 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
495 /* We connected to some services, in this case, ignore the ones we failed on */
499 static int userdb_process(
500 UserDBIterator
*iterator
,
501 UserRecord
**ret_user_record
,
502 GroupRecord
**ret_group_record
,
503 char **ret_user_name
,
504 char **ret_group_name
) {
511 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
513 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
515 iterator
->found_user
= user_record_unref(iterator
->found_user
);
517 if (ret_group_record
)
518 *ret_group_record
= NULL
;
520 *ret_user_name
= NULL
;
522 *ret_group_name
= NULL
;
527 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
528 if (ret_group_record
)
529 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
531 iterator
->found_group
= group_record_unref(iterator
->found_group
);
534 *ret_user_record
= NULL
;
536 *ret_user_name
= NULL
;
538 *ret_group_name
= NULL
;
543 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
545 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
547 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
550 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
552 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
555 *ret_user_record
= NULL
;
556 if (ret_group_record
)
557 *ret_group_record
= NULL
;
562 if (set_isempty(iterator
->links
)) {
563 if (iterator
->error
== 0)
566 return -abs(iterator
->error
);
569 if (!iterator
->event
)
572 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
578 static int synthetic_root_user_build(UserRecord
**ret
) {
579 return user_record_build(
581 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
582 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
583 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
584 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
585 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
588 static int synthetic_nobody_user_build(UserRecord
**ret
) {
589 return user_record_build(
591 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
592 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
593 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
594 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
595 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
596 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
599 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
600 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
601 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
604 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
607 r
= json_build(&query
, JSON_BUILD_OBJECT(
608 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
612 iterator
= userdb_iterator_new(LOOKUP_USER
);
616 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
618 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
623 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
624 /* Make sure the NSS lookup doesn't recurse back to us. */
626 r
= userdb_iterator_block_nss_systemd(iterator
);
628 /* Client-side NSS fallback */
629 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
635 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
636 if (streq(name
, "root"))
637 return synthetic_root_user_build(ret
);
639 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
640 return synthetic_nobody_user_build(ret
);
646 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
647 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
648 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
651 if (!uid_is_valid(uid
))
654 r
= json_build(&query
, JSON_BUILD_OBJECT(
655 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
659 iterator
= userdb_iterator_new(LOOKUP_USER
);
663 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
665 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
670 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !iterator
->nss_covered
) {
671 r
= userdb_iterator_block_nss_systemd(iterator
);
673 /* Client-side NSS fallback */
674 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
680 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
682 return synthetic_root_user_build(ret
);
684 if (uid
== UID_NOBODY
&& synthesize_nobody())
685 return synthetic_nobody_user_build(ret
);
691 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
692 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
697 iterator
= userdb_iterator_new(LOOKUP_USER
);
701 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
703 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
705 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
706 r
= userdb_iterator_block_nss_systemd(iterator
);
711 iterator
->nss_iterating
= true;
715 *ret
= TAKE_PTR(iterator
);
719 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
723 assert(iterator
->what
== LOOKUP_USER
);
725 if (iterator
->nss_iterating
) {
728 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
729 * the more traditional sources, which are probably good to show first. */
733 _cleanup_free_
char *buffer
= NULL
;
734 bool incomplete
= false;
737 if (streq_ptr(pw
->pw_name
, "root"))
738 iterator
->synthesize_root
= false;
739 if (pw
->pw_uid
== UID_NOBODY
)
740 iterator
->synthesize_nobody
= false;
742 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
744 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
745 incomplete
= ERRNO_IS_PRIVILEGE(r
);
748 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
753 (*ret
)->incomplete
= incomplete
;
758 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
760 iterator
->nss_iterating
= false;
764 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
767 if (iterator
->synthesize_root
) {
768 iterator
->synthesize_root
= false;
770 return synthetic_root_user_build(ret
);
773 if (iterator
->synthesize_nobody
) {
774 iterator
->synthesize_nobody
= false;
776 return synthetic_nobody_user_build(ret
);
780 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
781 if (r
< 0 && iterator
->n_found
> 0)
787 static int synthetic_root_group_build(GroupRecord
**ret
) {
788 return group_record_build(
790 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
791 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
792 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
795 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
796 return group_record_build(
798 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
799 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
800 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
803 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
804 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
805 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
808 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
811 r
= json_build(&query
, JSON_BUILD_OBJECT(
812 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
816 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
820 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
822 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
827 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
828 r
= userdb_iterator_block_nss_systemd(iterator
);
830 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
836 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
837 if (streq(name
, "root"))
838 return synthetic_root_group_build(ret
);
840 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
841 return synthetic_nobody_group_build(ret
);
847 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
848 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
849 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
852 if (!gid_is_valid(gid
))
855 r
= json_build(&query
, JSON_BUILD_OBJECT(
856 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
860 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
864 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
866 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
871 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
872 r
= userdb_iterator_block_nss_systemd(iterator
);
874 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
880 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
882 return synthetic_root_group_build(ret
);
884 if (gid
== GID_NOBODY
&& synthesize_nobody())
885 return synthetic_nobody_group_build(ret
);
891 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
892 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
897 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
901 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
903 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
905 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
906 r
= userdb_iterator_block_nss_systemd(iterator
);
911 iterator
->nss_iterating
= true;
915 *ret
= TAKE_PTR(iterator
);
919 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
923 assert(iterator
->what
== LOOKUP_GROUP
);
925 if (iterator
->nss_iterating
) {
931 _cleanup_free_
char *buffer
= NULL
;
932 bool incomplete
= false;
935 if (streq_ptr(gr
->gr_name
, "root"))
936 iterator
->synthesize_root
= false;
937 if (gr
->gr_gid
== GID_NOBODY
)
938 iterator
->synthesize_nobody
= false;
940 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
942 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
943 incomplete
= ERRNO_IS_PRIVILEGE(r
);
946 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
951 (*ret
)->incomplete
= incomplete
;
956 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
958 iterator
->nss_iterating
= false;
962 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
964 if (iterator
->synthesize_root
) {
965 iterator
->synthesize_root
= false;
967 return synthetic_root_group_build(ret
);
970 if (iterator
->synthesize_nobody
) {
971 iterator
->synthesize_nobody
= false;
973 return synthetic_nobody_group_build(ret
);
977 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
978 if (r
< 0 && iterator
->n_found
> 0)
984 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
985 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
986 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
991 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
994 r
= json_build(&query
, JSON_BUILD_OBJECT(
995 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
999 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1003 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1004 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1007 r
= userdb_iterator_block_nss_systemd(iterator
);
1011 iterator
->filter_user_name
= strdup(name
);
1012 if (!iterator
->filter_user_name
)
1016 iterator
->nss_iterating
= true;
1022 *ret
= TAKE_PTR(iterator
);
1026 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1027 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1028 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1029 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1034 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1037 r
= json_build(&query
, JSON_BUILD_OBJECT(
1038 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1042 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1046 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1047 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1050 r
= userdb_iterator_block_nss_systemd(iterator
);
1054 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1055 (void) nss_group_record_by_name(name
, false, &gr
);
1057 iterator
->members_of_group
= strv_copy(gr
->members
);
1058 if (!iterator
->members_of_group
)
1061 iterator
->index_members_of_group
= 0;
1063 iterator
->found_group_name
= strdup(name
);
1064 if (!iterator
->found_group_name
)
1072 *ret
= TAKE_PTR(iterator
);
1077 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1078 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1083 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1087 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1088 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1091 r
= userdb_iterator_block_nss_systemd(iterator
);
1096 iterator
->nss_iterating
= true;
1102 *ret
= TAKE_PTR(iterator
);
1107 int membershipdb_iterator_get(
1108 UserDBIterator
*iterator
,
1117 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1118 if (!iterator
->members_of_group
) {
1121 if (!iterator
->nss_iterating
)
1124 assert(!iterator
->found_user_name
);
1130 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1134 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1135 strv_isempty(g
->gr_mem
));
1138 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1142 if (iterator
->filter_user_name
)
1143 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1145 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1146 if (!iterator
->members_of_group
)
1149 iterator
->index_members_of_group
= 0;
1151 iterator
->nss_iterating
= false;
1157 assert(iterator
->found_group_name
);
1158 assert(iterator
->members_of_group
);
1159 assert(!iterator
->found_user_name
);
1161 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1162 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1165 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1171 cg
= strdup(iterator
->found_group_name
);
1177 *ret_user
= TAKE_PTR(cu
);
1180 *ret_group
= TAKE_PTR(cg
);
1182 iterator
->index_members_of_group
++;
1186 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1187 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1190 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1191 if (r
< 0 && iterator
->n_found
> 0)
1197 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1198 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1199 _cleanup_strv_free_
char **members
= NULL
;
1205 r
= membershipdb_by_group(name
, flags
, &iterator
);
1210 _cleanup_free_
char *user_name
= NULL
;
1212 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1218 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1226 *ret
= TAKE_PTR(members
);
1230 int userdb_block_nss_systemd(int b
) {
1231 _cleanup_(dlclosep
) void *dl
= NULL
;
1232 int (*call
)(bool b
);
1234 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1236 dl
= dlopen(ROOTLIBDIR
"/libnss_systemd.so.2", RTLD_LAZY
|RTLD_NODELETE
);
1238 /* If the file isn't installed, don't complain loudly */
1239 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1243 call
= (int (*)(bool b
)) dlsym(dl
, "_nss_systemd_block");
1245 /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
1246 return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD
),
1247 "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());