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
))
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 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
))
711 *ret
= TAKE_PTR(iterator
);
715 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
719 assert(iterator
->what
== LOOKUP_USER
);
721 if (iterator
->nss_iterating
) {
724 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
725 * the more traditional sources, which are probably good to show first. */
729 _cleanup_free_
char *buffer
= NULL
;
730 bool incomplete
= false;
733 if (streq_ptr(pw
->pw_name
, "root"))
734 iterator
->synthesize_root
= false;
735 if (pw
->pw_uid
== UID_NOBODY
)
736 iterator
->synthesize_nobody
= false;
738 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
740 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
741 incomplete
= ERRNO_IS_PRIVILEGE(r
);
744 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
749 (*ret
)->incomplete
= incomplete
;
754 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
756 iterator
->nss_iterating
= false;
760 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
763 if (iterator
->synthesize_root
) {
764 iterator
->synthesize_root
= false;
766 return synthetic_root_user_build(ret
);
769 if (iterator
->synthesize_nobody
) {
770 iterator
->synthesize_nobody
= false;
772 return synthetic_nobody_user_build(ret
);
776 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
777 if (r
< 0 && iterator
->n_found
> 0)
783 static int synthetic_root_group_build(GroupRecord
**ret
) {
784 return group_record_build(
786 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
787 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
788 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
791 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
792 return group_record_build(
794 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
795 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
796 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
799 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
800 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
801 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
804 if (!valid_user_group_name(name
))
807 r
= json_build(&query
, JSON_BUILD_OBJECT(
808 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
812 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
816 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
818 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
823 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
824 r
= userdb_nss_compat_disable();
825 if (r
>= 0 || r
== -EBUSY
) {
826 iterator
->nss_lock
= r
;
828 r
= nss_group_record_by_name(name
, 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_nss_compat_disable();
871 if (r
>= 0 || r
== -EBUSY
) {
872 iterator
->nss_lock
= r
;
874 r
= nss_group_record_by_gid(gid
, 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 iterator
->nss_lock
= userdb_nss_compat_disable();
907 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
908 return iterator
->nss_lock
;
911 iterator
->nss_iterating
= true;
915 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
))
921 *ret
= TAKE_PTR(iterator
);
925 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
929 assert(iterator
->what
== LOOKUP_GROUP
);
931 if (iterator
->nss_iterating
) {
937 _cleanup_free_
char *buffer
= NULL
;
938 bool incomplete
= false;
941 if (streq_ptr(gr
->gr_name
, "root"))
942 iterator
->synthesize_root
= false;
943 if (gr
->gr_gid
== GID_NOBODY
)
944 iterator
->synthesize_nobody
= false;
946 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
948 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
949 incomplete
= ERRNO_IS_PRIVILEGE(r
);
952 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
957 (*ret
)->incomplete
= incomplete
;
962 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
964 iterator
->nss_iterating
= false;
968 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
970 if (iterator
->synthesize_root
) {
971 iterator
->synthesize_root
= false;
973 return synthetic_root_group_build(ret
);
976 if (iterator
->synthesize_nobody
) {
977 iterator
->synthesize_nobody
= false;
979 return synthetic_nobody_group_build(ret
);
983 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
984 if (r
< 0 && iterator
->n_found
> 0)
990 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
991 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
992 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
997 if (!valid_user_group_name(name
))
1000 r
= json_build(&query
, JSON_BUILD_OBJECT(
1001 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
1005 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1009 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1010 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1013 iterator
->nss_lock
= userdb_nss_compat_disable();
1014 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1015 return iterator
->nss_lock
;
1017 iterator
->filter_user_name
= strdup(name
);
1018 if (!iterator
->filter_user_name
)
1022 iterator
->nss_iterating
= true;
1028 *ret
= TAKE_PTR(iterator
);
1032 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1033 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1034 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1035 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1040 if (!valid_user_group_name(name
))
1043 r
= json_build(&query
, JSON_BUILD_OBJECT(
1044 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1048 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1052 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1053 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1056 iterator
->nss_lock
= userdb_nss_compat_disable();
1057 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1058 return iterator
->nss_lock
;
1060 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1061 (void) nss_group_record_by_name(name
, &gr
);
1063 iterator
->members_of_group
= strv_copy(gr
->members
);
1064 if (!iterator
->members_of_group
)
1067 iterator
->index_members_of_group
= 0;
1069 iterator
->found_group_name
= strdup(name
);
1070 if (!iterator
->found_group_name
)
1078 *ret
= TAKE_PTR(iterator
);
1083 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1084 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1089 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1093 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1094 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1097 iterator
->nss_lock
= userdb_nss_compat_disable();
1098 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1099 return iterator
->nss_lock
;
1102 iterator
->nss_iterating
= true;
1108 *ret
= TAKE_PTR(iterator
);
1113 int membershipdb_iterator_get(
1114 UserDBIterator
*iterator
,
1123 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1124 if (!iterator
->members_of_group
) {
1127 if (!iterator
->nss_iterating
)
1130 assert(!iterator
->found_user_name
);
1136 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1140 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1141 strv_isempty(g
->gr_mem
));
1144 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1148 if (iterator
->filter_user_name
)
1149 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1151 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1152 if (!iterator
->members_of_group
)
1155 iterator
->index_members_of_group
= 0;
1157 iterator
->nss_iterating
= false;
1163 assert(iterator
->found_group_name
);
1164 assert(iterator
->members_of_group
);
1165 assert(!iterator
->found_user_name
);
1167 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1168 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1171 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1177 cg
= strdup(iterator
->found_group_name
);
1183 *ret_user
= TAKE_PTR(cu
);
1186 *ret_group
= TAKE_PTR(cg
);
1188 iterator
->index_members_of_group
++;
1192 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1193 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1196 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1197 if (r
< 0 && iterator
->n_found
> 0)
1203 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1204 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1205 _cleanup_strv_free_
char **members
= NULL
;
1211 r
= membershipdb_by_group(name
, flags
, &iterator
);
1216 _cleanup_free_
char *user_name
= NULL
;
1218 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1224 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1232 *ret
= TAKE_PTR(members
);
1236 static int userdb_thread_sockaddr(struct sockaddr_un
*ret_sa
, socklen_t
*ret_salen
) {
1237 static const uint8_t
1238 k1
[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
1239 k2
[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
1249 /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
1250 * indicator whether to emulate NSS records for complex user records that are also available via the
1251 * varlink protocol. The name of the socket is picked in a way so that:
1253 * → it is per-thread (by hashing from the TID)
1255 * → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
1256 * value every process gets passed from the kernel
1258 * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
1259 * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
1260 * namespace the lock is automatically cleaned up when the process dies abnormally.
1264 p
= ULONG_TO_PTR(getauxval(AT_RANDOM
));
1270 siphash24_init(&sh
, k1
);
1271 siphash24_compress(p
, 16, &sh
);
1272 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1273 x
= siphash24_finalize(&sh
);
1275 siphash24_init(&sh
, k2
);
1276 siphash24_compress(p
, 16, &sh
);
1277 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1278 y
= siphash24_finalize(&sh
);
1280 *ret_sa
= (struct sockaddr_un
) {
1281 .sun_family
= AF_UNIX
,
1284 sprintf(ret_sa
->sun_path
+ 1, "userdb-%016" PRIx64
"%016" PRIx64
, x
, y
);
1285 *ret_salen
= offsetof(struct sockaddr_un
, sun_path
) + 1 + 7 + 32;
1290 int userdb_nss_compat_is_enabled(void) {
1291 _cleanup_close_
int fd
= -1;
1292 union sockaddr_union sa
;
1296 /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
1297 * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
1300 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1304 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
1308 /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
1309 * address is bound at all. */
1310 if (connect(fd
, &sa
.sa
, salen
) < 0) {
1311 if (errno
== ECONNREFUSED
) /* the socket is not bound, hence NSS emulation shall be done */
1320 int userdb_nss_compat_disable(void) {
1321 _cleanup_close_
int fd
= -1;
1322 union sockaddr_union sa
;
1326 /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
1327 * synthesized for all complex user records looked up via NSS. If this call is invoked this is
1328 * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
1329 * user record protocol may use that to turn off the compatibility for NSS lookups. */
1331 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1335 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
1339 if (bind(fd
, &sa
.sa
, salen
) < 0) {
1340 if (errno
== EADDRINUSE
) /* lock already taken, convert this into a recognizable error */