1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "dirent-util.h"
6 #include "errno-util.h"
8 #include "group-record-nss.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;
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
);
88 safe_close(iterator
->nss_lock
);
90 return mfree(iterator
);
93 static UserDBIterator
* userdb_iterator_new(LookupWhat what
) {
97 assert(what
< _LOOKUP_WHAT_MAX
);
99 i
= new(UserDBIterator
, 1);
103 *i
= (UserDBIterator
) {
111 struct user_group_data
{
116 static void user_group_data_release(struct user_group_data
*d
) {
117 json_variant_unref(d
->record
);
120 static int userdb_on_query_reply(
122 JsonVariant
*parameters
,
123 const char *error_id
,
124 VarlinkReplyFlags flags
,
127 UserDBIterator
*iterator
= userdata
;
133 log_debug("Got lookup error: %s", error_id
);
135 if (STR_IN_SET(error_id
,
136 "io.systemd.UserDatabase.NoRecordFound",
137 "io.systemd.UserDatabase.ConflictingRecordFound"))
139 else if (streq(error_id
, "io.systemd.UserDatabase.ServiceNotAvailable"))
141 else if (streq(error_id
, VARLINK_ERROR_TIMEOUT
))
149 switch (iterator
->what
) {
152 _cleanup_(user_group_data_release
) struct user_group_data user_data
= {};
154 static const JsonDispatch dispatch_table
[] = {
155 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
156 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
159 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
161 assert_se(!iterator
->found_user
);
163 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &user_data
);
167 if (!user_data
.record
) {
168 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
172 hr
= user_record_new();
178 r
= user_record_load(hr
, user_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
183 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "User record does not carry service information, refusing.");
187 hr
->incomplete
= user_data
.incomplete
;
189 /* We match the root user by the name since the name is our primary key. We match the nobody
190 * use by UID though, since the name might differ on OSes */
191 if (streq_ptr(hr
->user_name
, "root"))
192 iterator
->synthesize_root
= false;
193 if (hr
->uid
== UID_NOBODY
)
194 iterator
->synthesize_nobody
= false;
196 iterator
->found_user
= TAKE_PTR(hr
);
199 /* More stuff coming? then let's just exit cleanly here */
200 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
203 /* Otherwise, let's remove this link and exit cleanly then */
209 _cleanup_(user_group_data_release
) struct user_group_data group_data
= {};
211 static const JsonDispatch dispatch_table
[] = {
212 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
213 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
216 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
218 assert_se(!iterator
->found_group
);
220 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &group_data
);
224 if (!group_data
.record
) {
225 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
229 g
= group_record_new();
235 r
= group_record_load(g
, group_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
240 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Group record does not carry service information, refusing.");
244 g
->incomplete
= group_data
.incomplete
;
246 if (streq_ptr(g
->group_name
, "root"))
247 iterator
->synthesize_root
= false;
248 if (g
->gid
== GID_NOBODY
)
249 iterator
->synthesize_nobody
= false;
251 iterator
->found_group
= TAKE_PTR(g
);
254 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
261 case LOOKUP_MEMBERSHIP
: {
262 struct membership_data
{
263 const char *user_name
;
264 const char *group_name
;
265 } membership_data
= {};
267 static const JsonDispatch dispatch_table
[] = {
268 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, user_name
), JSON_SAFE
},
269 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, group_name
), JSON_SAFE
},
273 assert(!iterator
->found_user_name
);
274 assert(!iterator
->found_group_name
);
276 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &membership_data
);
280 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
281 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
283 iterator
->found_user_name
= strdup(membership_data
.user_name
);
284 if (!iterator
->found_user_name
) {
289 iterator
->found_group_name
= strdup(membership_data
.group_name
);
290 if (!iterator
->found_group_name
) {
297 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
305 assert_not_reached("unexpected lookup");
309 /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
310 * errors if at least one connection ended cleanly */
311 if (r
== -ESRCH
|| iterator
->error
== 0)
312 iterator
->error
= -r
;
314 assert_se(set_remove(iterator
->links
, link
) == link
);
315 link
= varlink_unref(link
);
319 static int userdb_connect(
320 UserDBIterator
*iterator
,
324 JsonVariant
*query
) {
326 _cleanup_(varlink_unrefp
) Varlink
*vl
= NULL
;
333 r
= varlink_connect_address(&vl
, path
);
335 return log_debug_errno(r
, "Unable to connect to %s: %m", path
);
337 varlink_set_userdata(vl
, iterator
);
339 if (!iterator
->event
) {
340 r
= sd_event_new(&iterator
->event
);
342 return log_debug_errno(r
, "Unable to allocate event loop: %m");
345 r
= varlink_attach_event(vl
, iterator
->event
, SD_EVENT_PRIORITY_NORMAL
);
347 return log_debug_errno(r
, "Failed to attach varlink connection to event loop: %m");
349 (void) varlink_set_description(vl
, path
);
351 r
= varlink_bind_reply(vl
, userdb_on_query_reply
);
353 return log_debug_errno(r
, "Failed to bind reply callback: %m");
356 r
= varlink_observe(vl
, method
, query
);
358 r
= varlink_invoke(vl
, method
, query
);
360 return log_debug_errno(r
, "Failed to invoke varlink method: %m");
362 r
= set_ensure_allocated(&iterator
->links
, &link_hash_ops
);
364 return log_debug_errno(r
, "Failed to allocate set: %m");
366 r
= set_put(iterator
->links
, vl
);
368 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
374 static int userdb_start_query(
375 UserDBIterator
*iterator
,
381 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
382 _cleanup_(closedirp
) DIR *d
= NULL
;
390 e
= getenv("SYSTEMD_BYPASS_USERDB");
392 r
= parse_boolean(e
);
396 except
= strv_split(e
, ":");
402 e
= getenv("SYSTEMD_ONLY_USERDB");
404 only
= strv_split(e
, ":");
409 /* First, let's talk to the multiplexer, if we can */
410 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_AVOID_DYNAMIC_USER
|USERDB_AVOID_NSS
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
411 !strv_contains(except
, "io.systemd.Multiplexer") &&
412 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
413 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
415 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
417 return log_debug_errno(r
, "Unable to set service JSON field: %m");
419 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
421 iterator
->nss_covered
= true; /* The multiplexer does NSS */
426 d
= opendir("/run/systemd/userdb/");
434 FOREACH_DIRENT(de
, d
, return -errno
) {
435 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
436 _cleanup_free_
char *p
= NULL
;
439 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
442 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
443 streq(de
->d_name
, "io.systemd.DynamicUser"))
446 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
447 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
448 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
450 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
451 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
454 if (strv_contains(except
, de
->d_name
))
457 if (only
&& !strv_contains(only
, de
->d_name
))
460 p
= path_join("/run/systemd/userdb/", de
->d_name
);
464 patched_query
= json_variant_ref(query
);
465 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
467 return log_debug_errno(r
, "Unable to set service JSON field: %m");
469 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
470 if (is_nss
&& r
>= 0) /* Turn off fallback NSS if we found the NSS service and could connect
472 iterator
->nss_covered
= true;
474 if (ret
== 0 && r
< 0)
478 if (set_isempty(iterator
->links
))
479 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
481 /* We connected to some services, in this case, ignore the ones we failed on */
485 static int userdb_process(
486 UserDBIterator
*iterator
,
487 UserRecord
**ret_user_record
,
488 GroupRecord
**ret_group_record
,
489 char **ret_user_name
,
490 char **ret_group_name
) {
497 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
499 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
501 iterator
->found_user
= user_record_unref(iterator
->found_user
);
503 if (ret_group_record
)
504 *ret_group_record
= NULL
;
506 *ret_user_name
= NULL
;
508 *ret_group_name
= NULL
;
513 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
514 if (ret_group_record
)
515 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
517 iterator
->found_group
= group_record_unref(iterator
->found_group
);
520 *ret_user_record
= NULL
;
522 *ret_user_name
= NULL
;
524 *ret_group_name
= NULL
;
529 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
531 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
533 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
536 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
538 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
541 *ret_user_record
= NULL
;
542 if (ret_group_record
)
543 *ret_group_record
= NULL
;
548 if (set_isempty(iterator
->links
)) {
549 if (iterator
->error
== 0)
552 return -abs(iterator
->error
);
555 if (!iterator
->event
)
558 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
564 static int synthetic_root_user_build(UserRecord
**ret
) {
565 return user_record_build(
567 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
568 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
569 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
570 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
571 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
574 static int synthetic_nobody_user_build(UserRecord
**ret
) {
575 return user_record_build(
577 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
578 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
579 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
580 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
581 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
582 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
585 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
586 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
587 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
590 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
593 r
= json_build(&query
, JSON_BUILD_OBJECT(
594 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
598 iterator
= userdb_iterator_new(LOOKUP_USER
);
602 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
604 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
609 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
610 /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we
611 * already took the lock from our thread, which is totally OK.) */
612 r
= userdb_nss_compat_disable();
613 if (r
>= 0 || r
== -EBUSY
) {
614 iterator
->nss_lock
= r
;
616 /* Client-side NSS fallback */
617 r
= nss_user_record_by_name(name
, ret
);
623 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
624 if (streq(name
, "root"))
625 return synthetic_root_user_build(ret
);
627 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
628 return synthetic_nobody_user_build(ret
);
634 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
635 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
636 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
639 if (!uid_is_valid(uid
))
642 r
= json_build(&query
, JSON_BUILD_OBJECT(
643 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
647 iterator
= userdb_iterator_new(LOOKUP_USER
);
651 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
653 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
658 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
659 r
= userdb_nss_compat_disable();
660 if (r
>= 0 || r
== -EBUSY
) {
661 iterator
->nss_lock
= r
;
663 /* Client-side NSS fallback */
664 r
= nss_user_record_by_uid(uid
, ret
);
670 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
672 return synthetic_root_user_build(ret
);
674 if (uid
== UID_NOBODY
&& synthesize_nobody())
675 return synthetic_nobody_user_build(ret
);
681 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
682 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
687 iterator
= userdb_iterator_new(LOOKUP_USER
);
691 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
693 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
695 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
696 iterator
->nss_lock
= userdb_nss_compat_disable();
697 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
698 return iterator
->nss_lock
;
701 iterator
->nss_iterating
= true;
705 *ret
= TAKE_PTR(iterator
);
709 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
713 assert(iterator
->what
== LOOKUP_USER
);
715 if (iterator
->nss_iterating
) {
718 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
719 * the more traditional sources, which are probably good to show first. */
723 _cleanup_free_
char *buffer
= NULL
;
724 bool incomplete
= false;
727 if (streq_ptr(pw
->pw_name
, "root"))
728 iterator
->synthesize_root
= false;
729 if (pw
->pw_uid
== UID_NOBODY
)
730 iterator
->synthesize_nobody
= false;
732 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
734 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
735 incomplete
= ERRNO_IS_PRIVILEGE(r
);
738 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
743 (*ret
)->incomplete
= incomplete
;
748 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
750 iterator
->nss_iterating
= false;
754 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
757 if (iterator
->synthesize_root
) {
758 iterator
->synthesize_root
= false;
760 return synthetic_root_user_build(ret
);
763 if (iterator
->synthesize_nobody
) {
764 iterator
->synthesize_nobody
= false;
766 return synthetic_nobody_user_build(ret
);
770 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
771 if (r
< 0 && iterator
->n_found
> 0)
777 static int synthetic_root_group_build(GroupRecord
**ret
) {
778 return group_record_build(
780 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
781 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
782 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
785 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
786 return group_record_build(
788 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
789 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
790 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
793 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
794 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
795 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
798 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
801 r
= json_build(&query
, JSON_BUILD_OBJECT(
802 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
806 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
810 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
812 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
817 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
818 r
= userdb_nss_compat_disable();
819 if (r
>= 0 || r
== -EBUSY
) {
820 iterator
->nss_lock
= r
;
822 r
= nss_group_record_by_name(name
, ret
);
828 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
829 if (streq(name
, "root"))
830 return synthetic_root_group_build(ret
);
832 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
833 return synthetic_nobody_group_build(ret
);
839 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
840 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
841 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
844 if (!gid_is_valid(gid
))
847 r
= json_build(&query
, JSON_BUILD_OBJECT(
848 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
852 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
856 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
858 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
863 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
864 r
= userdb_nss_compat_disable();
865 if (r
>= 0 || r
== -EBUSY
) {
866 iterator
->nss_lock
= r
;
868 r
= nss_group_record_by_gid(gid
, ret
);
874 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
876 return synthetic_root_group_build(ret
);
878 if (gid
== GID_NOBODY
&& synthesize_nobody())
879 return synthetic_nobody_group_build(ret
);
885 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
886 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
891 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
895 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
897 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
899 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
900 iterator
->nss_lock
= userdb_nss_compat_disable();
901 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
902 return iterator
->nss_lock
;
905 iterator
->nss_iterating
= true;
909 *ret
= TAKE_PTR(iterator
);
913 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
917 assert(iterator
->what
== LOOKUP_GROUP
);
919 if (iterator
->nss_iterating
) {
925 _cleanup_free_
char *buffer
= NULL
;
926 bool incomplete
= false;
929 if (streq_ptr(gr
->gr_name
, "root"))
930 iterator
->synthesize_root
= false;
931 if (gr
->gr_gid
== GID_NOBODY
)
932 iterator
->synthesize_nobody
= false;
934 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
936 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
937 incomplete
= ERRNO_IS_PRIVILEGE(r
);
940 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
945 (*ret
)->incomplete
= incomplete
;
950 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
952 iterator
->nss_iterating
= false;
956 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
958 if (iterator
->synthesize_root
) {
959 iterator
->synthesize_root
= false;
961 return synthetic_root_group_build(ret
);
964 if (iterator
->synthesize_nobody
) {
965 iterator
->synthesize_nobody
= false;
967 return synthetic_nobody_group_build(ret
);
971 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
972 if (r
< 0 && iterator
->n_found
> 0)
978 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
979 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
980 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
985 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
988 r
= json_build(&query
, JSON_BUILD_OBJECT(
989 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
993 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
997 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
998 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1001 iterator
->nss_lock
= userdb_nss_compat_disable();
1002 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1003 return iterator
->nss_lock
;
1005 iterator
->filter_user_name
= strdup(name
);
1006 if (!iterator
->filter_user_name
)
1010 iterator
->nss_iterating
= true;
1016 *ret
= TAKE_PTR(iterator
);
1020 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1021 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1022 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1023 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1028 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1031 r
= json_build(&query
, JSON_BUILD_OBJECT(
1032 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1036 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1040 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1041 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1044 iterator
->nss_lock
= userdb_nss_compat_disable();
1045 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1046 return iterator
->nss_lock
;
1048 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1049 (void) nss_group_record_by_name(name
, &gr
);
1051 iterator
->members_of_group
= strv_copy(gr
->members
);
1052 if (!iterator
->members_of_group
)
1055 iterator
->index_members_of_group
= 0;
1057 iterator
->found_group_name
= strdup(name
);
1058 if (!iterator
->found_group_name
)
1066 *ret
= TAKE_PTR(iterator
);
1071 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1072 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1077 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1081 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1082 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1085 iterator
->nss_lock
= userdb_nss_compat_disable();
1086 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1087 return iterator
->nss_lock
;
1090 iterator
->nss_iterating
= true;
1096 *ret
= TAKE_PTR(iterator
);
1101 int membershipdb_iterator_get(
1102 UserDBIterator
*iterator
,
1111 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1112 if (!iterator
->members_of_group
) {
1115 if (!iterator
->nss_iterating
)
1118 assert(!iterator
->found_user_name
);
1124 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1128 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1129 strv_isempty(g
->gr_mem
));
1132 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1136 if (iterator
->filter_user_name
)
1137 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1139 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1140 if (!iterator
->members_of_group
)
1143 iterator
->index_members_of_group
= 0;
1145 iterator
->nss_iterating
= false;
1151 assert(iterator
->found_group_name
);
1152 assert(iterator
->members_of_group
);
1153 assert(!iterator
->found_user_name
);
1155 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1156 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1159 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1165 cg
= strdup(iterator
->found_group_name
);
1171 *ret_user
= TAKE_PTR(cu
);
1174 *ret_group
= TAKE_PTR(cg
);
1176 iterator
->index_members_of_group
++;
1180 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1181 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1184 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1185 if (r
< 0 && iterator
->n_found
> 0)
1191 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1192 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1193 _cleanup_strv_free_
char **members
= NULL
;
1199 r
= membershipdb_by_group(name
, flags
, &iterator
);
1204 _cleanup_free_
char *user_name
= NULL
;
1206 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1212 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1220 *ret
= TAKE_PTR(members
);
1224 static int userdb_thread_sockaddr(struct sockaddr_un
*ret_sa
, socklen_t
*ret_salen
) {
1225 static const uint8_t
1226 k1
[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
1227 k2
[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
1237 /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
1238 * indicator whether to emulate NSS records for complex user records that are also available via the
1239 * varlink protocol. The name of the socket is picked in a way so that:
1241 * → it is per-thread (by hashing from the TID)
1243 * → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
1244 * value every process gets passed from the kernel
1246 * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
1247 * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
1248 * namespace the lock is automatically cleaned up when the process dies abnormally.
1252 p
= ULONG_TO_PTR(getauxval(AT_RANDOM
));
1258 siphash24_init(&sh
, k1
);
1259 siphash24_compress(p
, 16, &sh
);
1260 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1261 x
= siphash24_finalize(&sh
);
1263 siphash24_init(&sh
, k2
);
1264 siphash24_compress(p
, 16, &sh
);
1265 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1266 y
= siphash24_finalize(&sh
);
1268 *ret_sa
= (struct sockaddr_un
) {
1269 .sun_family
= AF_UNIX
,
1272 sprintf(ret_sa
->sun_path
+ 1, "userdb-%016" PRIx64
"%016" PRIx64
, x
, y
);
1273 *ret_salen
= offsetof(struct sockaddr_un
, sun_path
) + 1 + 7 + 32;
1278 int userdb_nss_compat_is_enabled(void) {
1279 _cleanup_close_
int fd
= -1;
1280 union sockaddr_union sa
;
1284 /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
1285 * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
1288 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1292 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
1296 /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
1297 * address is bound at all. */
1298 if (connect(fd
, &sa
.sa
, salen
) < 0) {
1299 if (errno
== ECONNREFUSED
) /* the socket is not bound, hence NSS emulation shall be done */
1308 int userdb_nss_compat_disable(void) {
1309 _cleanup_close_
int fd
= -1;
1310 union sockaddr_union sa
;
1314 /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
1315 * synthesized for all complex user records looked up via NSS. If this call is invoked this is
1316 * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
1317 * user record protocol may use that to turn off the compatibility for NSS lookups. */
1319 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1323 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
1327 if (bind(fd
, &sa
.sa
, salen
) < 0) {
1328 if (errno
== EADDRINUSE
) /* lock already taken, convert this into a recognizable error */