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_consume(&iterator
->links
, &link_hash_ops
, TAKE_PTR(vl
));
364 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
368 static int userdb_start_query(
369 UserDBIterator
*iterator
,
375 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
376 _cleanup_(closedirp
) DIR *d
= NULL
;
384 e
= getenv("SYSTEMD_BYPASS_USERDB");
386 r
= parse_boolean(e
);
390 except
= strv_split(e
, ":");
396 e
= getenv("SYSTEMD_ONLY_USERDB");
398 only
= strv_split(e
, ":");
403 /* First, let's talk to the multiplexer, if we can */
404 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_AVOID_DYNAMIC_USER
|USERDB_AVOID_NSS
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
405 !strv_contains(except
, "io.systemd.Multiplexer") &&
406 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
407 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
409 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
411 return log_debug_errno(r
, "Unable to set service JSON field: %m");
413 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
415 iterator
->nss_covered
= true; /* The multiplexer does NSS */
420 d
= opendir("/run/systemd/userdb/");
428 FOREACH_DIRENT(de
, d
, return -errno
) {
429 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
430 _cleanup_free_
char *p
= NULL
;
433 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
436 if (FLAGS_SET(flags
, USERDB_AVOID_DYNAMIC_USER
) &&
437 streq(de
->d_name
, "io.systemd.DynamicUser"))
440 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
441 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
442 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
444 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
445 if ((flags
& (USERDB_AVOID_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
448 if (strv_contains(except
, de
->d_name
))
451 if (only
&& !strv_contains(only
, de
->d_name
))
454 p
= path_join("/run/systemd/userdb/", de
->d_name
);
458 patched_query
= json_variant_ref(query
);
459 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
461 return log_debug_errno(r
, "Unable to set service JSON field: %m");
463 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
464 if (is_nss
&& r
>= 0) /* Turn off fallback NSS if we found the NSS service and could connect
466 iterator
->nss_covered
= true;
468 if (ret
== 0 && r
< 0)
472 if (set_isempty(iterator
->links
))
473 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
475 /* We connected to some services, in this case, ignore the ones we failed on */
479 static int userdb_process(
480 UserDBIterator
*iterator
,
481 UserRecord
**ret_user_record
,
482 GroupRecord
**ret_group_record
,
483 char **ret_user_name
,
484 char **ret_group_name
) {
491 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
493 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
495 iterator
->found_user
= user_record_unref(iterator
->found_user
);
497 if (ret_group_record
)
498 *ret_group_record
= NULL
;
500 *ret_user_name
= NULL
;
502 *ret_group_name
= NULL
;
507 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
508 if (ret_group_record
)
509 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
511 iterator
->found_group
= group_record_unref(iterator
->found_group
);
514 *ret_user_record
= NULL
;
516 *ret_user_name
= NULL
;
518 *ret_group_name
= NULL
;
523 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
525 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
527 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
530 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
532 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
535 *ret_user_record
= NULL
;
536 if (ret_group_record
)
537 *ret_group_record
= NULL
;
542 if (set_isempty(iterator
->links
)) {
543 if (iterator
->error
== 0)
546 return -abs(iterator
->error
);
549 if (!iterator
->event
)
552 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
558 static int synthetic_root_user_build(UserRecord
**ret
) {
559 return user_record_build(
561 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
562 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
563 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
564 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
565 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
568 static int synthetic_nobody_user_build(UserRecord
**ret
) {
569 return user_record_build(
571 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
572 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
573 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
574 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
575 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
576 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
579 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
580 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
581 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
584 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
587 r
= json_build(&query
, JSON_BUILD_OBJECT(
588 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
592 iterator
= userdb_iterator_new(LOOKUP_USER
);
596 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
598 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
603 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
604 /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we
605 * already took the lock from our thread, which is totally OK.) */
606 r
= userdb_nss_compat_disable();
607 if (r
>= 0 || r
== -EBUSY
) {
608 iterator
->nss_lock
= r
;
610 /* Client-side NSS fallback */
611 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
617 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
618 if (streq(name
, "root"))
619 return synthetic_root_user_build(ret
);
621 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
622 return synthetic_nobody_user_build(ret
);
628 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
629 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
630 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
633 if (!uid_is_valid(uid
))
636 r
= json_build(&query
, JSON_BUILD_OBJECT(
637 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
641 iterator
= userdb_iterator_new(LOOKUP_USER
);
645 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
647 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
652 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
653 r
= userdb_nss_compat_disable();
654 if (r
>= 0 || r
== -EBUSY
) {
655 iterator
->nss_lock
= r
;
657 /* Client-side NSS fallback */
658 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
664 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
666 return synthetic_root_user_build(ret
);
668 if (uid
== UID_NOBODY
&& synthesize_nobody())
669 return synthetic_nobody_user_build(ret
);
675 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
676 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
681 iterator
= userdb_iterator_new(LOOKUP_USER
);
685 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
687 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
689 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
690 iterator
->nss_lock
= userdb_nss_compat_disable();
691 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
692 return iterator
->nss_lock
;
695 iterator
->nss_iterating
= true;
699 *ret
= TAKE_PTR(iterator
);
703 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
707 assert(iterator
->what
== LOOKUP_USER
);
709 if (iterator
->nss_iterating
) {
712 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
713 * the more traditional sources, which are probably good to show first. */
717 _cleanup_free_
char *buffer
= NULL
;
718 bool incomplete
= false;
721 if (streq_ptr(pw
->pw_name
, "root"))
722 iterator
->synthesize_root
= false;
723 if (pw
->pw_uid
== UID_NOBODY
)
724 iterator
->synthesize_nobody
= false;
726 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
728 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
729 incomplete
= ERRNO_IS_PRIVILEGE(r
);
732 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
737 (*ret
)->incomplete
= incomplete
;
742 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
744 iterator
->nss_iterating
= false;
748 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
751 if (iterator
->synthesize_root
) {
752 iterator
->synthesize_root
= false;
754 return synthetic_root_user_build(ret
);
757 if (iterator
->synthesize_nobody
) {
758 iterator
->synthesize_nobody
= false;
760 return synthetic_nobody_user_build(ret
);
764 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
765 if (r
< 0 && iterator
->n_found
> 0)
771 static int synthetic_root_group_build(GroupRecord
**ret
) {
772 return group_record_build(
774 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
775 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
776 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
779 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
780 return group_record_build(
782 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
783 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
784 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
787 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
788 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
789 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
792 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
795 r
= json_build(&query
, JSON_BUILD_OBJECT(
796 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
800 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
804 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
806 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
811 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
812 r
= userdb_nss_compat_disable();
813 if (r
>= 0 || r
== -EBUSY
) {
814 iterator
->nss_lock
= r
;
816 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
822 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
823 if (streq(name
, "root"))
824 return synthetic_root_group_build(ret
);
826 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
827 return synthetic_nobody_group_build(ret
);
833 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
834 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
835 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
838 if (!gid_is_valid(gid
))
841 r
= json_build(&query
, JSON_BUILD_OBJECT(
842 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
846 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
850 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
852 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
857 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
858 r
= userdb_nss_compat_disable();
859 if (r
>= 0 || r
== -EBUSY
) {
860 iterator
->nss_lock
= r
;
862 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_AVOID_SHADOW
), ret
);
868 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
870 return synthetic_root_group_build(ret
);
872 if (gid
== GID_NOBODY
&& synthesize_nobody())
873 return synthetic_nobody_group_build(ret
);
879 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
880 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
885 iterator
= userdb_iterator_new(LOOKUP_GROUP
);
889 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
891 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
893 if (!FLAGS_SET(flags
, USERDB_AVOID_NSS
) && (r
< 0 || !iterator
->nss_covered
)) {
894 iterator
->nss_lock
= userdb_nss_compat_disable();
895 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
896 return iterator
->nss_lock
;
899 iterator
->nss_iterating
= true;
903 *ret
= TAKE_PTR(iterator
);
907 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
911 assert(iterator
->what
== LOOKUP_GROUP
);
913 if (iterator
->nss_iterating
) {
919 _cleanup_free_
char *buffer
= NULL
;
920 bool incomplete
= false;
923 if (streq_ptr(gr
->gr_name
, "root"))
924 iterator
->synthesize_root
= false;
925 if (gr
->gr_gid
== GID_NOBODY
)
926 iterator
->synthesize_nobody
= false;
928 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
930 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
931 incomplete
= ERRNO_IS_PRIVILEGE(r
);
934 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
939 (*ret
)->incomplete
= incomplete
;
944 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
946 iterator
->nss_iterating
= false;
950 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
952 if (iterator
->synthesize_root
) {
953 iterator
->synthesize_root
= false;
955 return synthetic_root_group_build(ret
);
958 if (iterator
->synthesize_nobody
) {
959 iterator
->synthesize_nobody
= false;
961 return synthetic_nobody_group_build(ret
);
965 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
966 if (r
< 0 && iterator
->n_found
> 0)
972 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
973 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
974 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
979 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
982 r
= json_build(&query
, JSON_BUILD_OBJECT(
983 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
987 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
991 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
992 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
995 iterator
->nss_lock
= userdb_nss_compat_disable();
996 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
997 return iterator
->nss_lock
;
999 iterator
->filter_user_name
= strdup(name
);
1000 if (!iterator
->filter_user_name
)
1004 iterator
->nss_iterating
= true;
1010 *ret
= TAKE_PTR(iterator
);
1014 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1015 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1016 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1017 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1022 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1025 r
= json_build(&query
, JSON_BUILD_OBJECT(
1026 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1030 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1034 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1035 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1038 iterator
->nss_lock
= userdb_nss_compat_disable();
1039 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1040 return iterator
->nss_lock
;
1042 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1043 (void) nss_group_record_by_name(name
, false, &gr
);
1045 iterator
->members_of_group
= strv_copy(gr
->members
);
1046 if (!iterator
->members_of_group
)
1049 iterator
->index_members_of_group
= 0;
1051 iterator
->found_group_name
= strdup(name
);
1052 if (!iterator
->found_group_name
)
1060 *ret
= TAKE_PTR(iterator
);
1065 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1066 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1071 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
);
1075 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1076 if ((r
>= 0 && iterator
->nss_covered
) || FLAGS_SET(flags
, USERDB_AVOID_NSS
))
1079 iterator
->nss_lock
= userdb_nss_compat_disable();
1080 if (iterator
->nss_lock
< 0 && iterator
->nss_lock
!= -EBUSY
)
1081 return iterator
->nss_lock
;
1084 iterator
->nss_iterating
= true;
1090 *ret
= TAKE_PTR(iterator
);
1095 int membershipdb_iterator_get(
1096 UserDBIterator
*iterator
,
1105 /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
1106 if (!iterator
->members_of_group
) {
1109 if (!iterator
->nss_iterating
)
1112 assert(!iterator
->found_user_name
);
1118 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1122 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1123 strv_isempty(g
->gr_mem
));
1126 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1130 if (iterator
->filter_user_name
)
1131 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1133 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1134 if (!iterator
->members_of_group
)
1137 iterator
->index_members_of_group
= 0;
1139 iterator
->nss_iterating
= false;
1145 assert(iterator
->found_group_name
);
1146 assert(iterator
->members_of_group
);
1147 assert(!iterator
->found_user_name
);
1149 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1150 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1153 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1159 cg
= strdup(iterator
->found_group_name
);
1165 *ret_user
= TAKE_PTR(cu
);
1168 *ret_group
= TAKE_PTR(cg
);
1170 iterator
->index_members_of_group
++;
1174 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1175 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1178 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1179 if (r
< 0 && iterator
->n_found
> 0)
1185 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1186 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1187 _cleanup_strv_free_
char **members
= NULL
;
1193 r
= membershipdb_by_group(name
, flags
, &iterator
);
1198 _cleanup_free_
char *user_name
= NULL
;
1200 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1206 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1214 *ret
= TAKE_PTR(members
);
1218 static int userdb_thread_sockaddr(struct sockaddr_un
*ret_sa
, socklen_t
*ret_salen
) {
1219 static const uint8_t
1220 k1
[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
1221 k2
[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
1231 /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
1232 * indicator whether to emulate NSS records for complex user records that are also available via the
1233 * varlink protocol. The name of the socket is picked in a way so that:
1235 * → it is per-thread (by hashing from the TID)
1237 * → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
1238 * value every process gets passed from the kernel
1240 * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
1241 * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
1242 * namespace the lock is automatically cleaned up when the process dies abnormally.
1246 p
= ULONG_TO_PTR(getauxval(AT_RANDOM
));
1252 siphash24_init(&sh
, k1
);
1253 siphash24_compress(p
, 16, &sh
);
1254 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1255 x
= siphash24_finalize(&sh
);
1257 siphash24_init(&sh
, k2
);
1258 siphash24_compress(p
, 16, &sh
);
1259 siphash24_compress(&tid
, sizeof(tid
), &sh
);
1260 y
= siphash24_finalize(&sh
);
1262 *ret_sa
= (struct sockaddr_un
) {
1263 .sun_family
= AF_UNIX
,
1266 sprintf(ret_sa
->sun_path
+ 1, "userdb-%016" PRIx64
"%016" PRIx64
, x
, y
);
1267 *ret_salen
= offsetof(struct sockaddr_un
, sun_path
) + 1 + 7 + 32;
1272 int userdb_nss_compat_is_enabled(void) {
1273 _cleanup_close_
int fd
= -1;
1274 union sockaddr_union sa
;
1278 /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
1279 * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
1282 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1286 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
1290 /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
1291 * address is bound at all. */
1292 if (connect(fd
, &sa
.sa
, salen
) < 0) {
1293 if (errno
== ECONNREFUSED
) /* the socket is not bound, hence NSS emulation shall be done */
1302 int userdb_nss_compat_disable(void) {
1303 _cleanup_close_
int fd
= -1;
1304 union sockaddr_union sa
;
1308 /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
1309 * synthesized for all complex user records looked up via NSS. If this call is invoked this is
1310 * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
1311 * user record protocol may use that to turn off the compatibility for NSS lookups. */
1313 r
= userdb_thread_sockaddr(&sa
.un
, &salen
);
1317 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
1321 if (bind(fd
, &sa
.sa
, salen
) < 0) {
1322 if (errno
== EADDRINUSE
) /* lock already taken, convert this into a recognizable error */