1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "conf-files.h"
6 #include "dirent-util.h"
7 #include "dlfcn-util.h"
8 #include "errno-util.h"
10 #include "format-util.h"
11 #include "missing_syscall.h"
12 #include "parse-util.h"
14 #include "socket-util.h"
16 #include "user-record-nss.h"
17 #include "user-util.h"
18 #include "userdb-dropin.h"
22 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops
, void, trivial_hash_func
, trivial_compare_func
, Varlink
, varlink_unref
);
24 typedef enum LookupWhat
{
31 struct UserDBIterator
{
37 bool dropin_covered
:1;
38 bool synthesize_root
:1;
39 bool synthesize_nobody
:1;
40 bool nss_systemd_blocked
:1;
42 size_t current_dropin
;
46 UserRecord
*found_user
; /* when .what == LOOKUP_USER */
47 GroupRecord
*found_group
; /* when .what == LOOKUP_GROUP */
49 char *found_user_name
, *found_group_name
; /* when .what == LOOKUP_MEMBERSHIP */
50 char **members_of_group
;
51 size_t index_members_of_group
;
52 char *filter_user_name
, *filter_group_name
;
55 UserDBIterator
* userdb_iterator_free(UserDBIterator
*iterator
) {
59 set_free(iterator
->links
);
60 strv_free(iterator
->dropins
);
62 switch (iterator
->what
) {
65 user_record_unref(iterator
->found_user
);
67 if (iterator
->nss_iterating
)
73 group_record_unref(iterator
->found_group
);
75 if (iterator
->nss_iterating
)
80 case LOOKUP_MEMBERSHIP
:
81 free(iterator
->found_user_name
);
82 free(iterator
->found_group_name
);
83 strv_free(iterator
->members_of_group
);
84 free(iterator
->filter_user_name
);
85 free(iterator
->filter_group_name
);
87 if (iterator
->nss_iterating
)
93 assert_not_reached("Unexpected state?");
96 sd_event_unref(iterator
->event
);
98 if (iterator
->nss_systemd_blocked
)
99 assert_se(userdb_block_nss_systemd(false) >= 0);
101 return mfree(iterator
);
104 static UserDBIterator
* userdb_iterator_new(LookupWhat what
, UserDBFlags flags
) {
108 assert(what
< _LOOKUP_WHAT_MAX
);
110 i
= new(UserDBIterator
, 1);
114 *i
= (UserDBIterator
) {
122 static int userdb_iterator_block_nss_systemd(UserDBIterator
*iterator
) {
127 if (iterator
->nss_systemd_blocked
)
130 r
= userdb_block_nss_systemd(true);
134 iterator
->nss_systemd_blocked
= true;
138 struct user_group_data
{
143 static void user_group_data_release(struct user_group_data
*d
) {
144 json_variant_unref(d
->record
);
147 static int userdb_on_query_reply(
149 JsonVariant
*parameters
,
150 const char *error_id
,
151 VarlinkReplyFlags flags
,
154 UserDBIterator
*iterator
= userdata
;
160 log_debug("Got lookup error: %s", error_id
);
162 if (STR_IN_SET(error_id
,
163 "io.systemd.UserDatabase.NoRecordFound",
164 "io.systemd.UserDatabase.ConflictingRecordFound"))
166 else if (streq(error_id
, "io.systemd.UserDatabase.ServiceNotAvailable"))
168 else if (streq(error_id
, "io.systemd.UserDatabase.EnumerationNotSupported"))
170 else if (streq(error_id
, VARLINK_ERROR_TIMEOUT
))
178 switch (iterator
->what
) {
181 _cleanup_(user_group_data_release
) struct user_group_data user_data
= {};
183 static const JsonDispatch dispatch_table
[] = {
184 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
185 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
188 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
190 assert_se(!iterator
->found_user
);
192 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &user_data
);
196 if (!user_data
.record
) {
197 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
201 hr
= user_record_new();
207 r
= user_record_load(hr
, user_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
212 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "User record does not carry service information, refusing.");
216 hr
->incomplete
= user_data
.incomplete
;
218 /* We match the root user by the name since the name is our primary key. We match the nobody
219 * use by UID though, since the name might differ on OSes */
220 if (streq_ptr(hr
->user_name
, "root"))
221 iterator
->synthesize_root
= false;
222 if (hr
->uid
== UID_NOBODY
)
223 iterator
->synthesize_nobody
= false;
225 iterator
->found_user
= TAKE_PTR(hr
);
228 /* More stuff coming? then let's just exit cleanly here */
229 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
232 /* Otherwise, let's remove this link and exit cleanly then */
238 _cleanup_(user_group_data_release
) struct user_group_data group_data
= {};
240 static const JsonDispatch dispatch_table
[] = {
241 { "record", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_variant
, offsetof(struct user_group_data
, record
), 0 },
242 { "incomplete", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, offsetof(struct user_group_data
, incomplete
), 0 },
245 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
247 assert_se(!iterator
->found_group
);
249 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &group_data
);
253 if (!group_data
.record
) {
254 r
= log_debug_errno(SYNTHETIC_ERRNO(EIO
), "Reply is missing record key");
258 g
= group_record_new();
264 r
= group_record_load(g
, group_data
.record
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
269 r
= log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Group record does not carry service information, refusing.");
273 g
->incomplete
= group_data
.incomplete
;
275 if (streq_ptr(g
->group_name
, "root"))
276 iterator
->synthesize_root
= false;
277 if (g
->gid
== GID_NOBODY
)
278 iterator
->synthesize_nobody
= false;
280 iterator
->found_group
= TAKE_PTR(g
);
283 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
290 case LOOKUP_MEMBERSHIP
: {
291 struct membership_data
{
292 const char *user_name
;
293 const char *group_name
;
294 } membership_data
= {};
296 static const JsonDispatch dispatch_table
[] = {
297 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, user_name
), JSON_SAFE
},
298 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(struct membership_data
, group_name
), JSON_SAFE
},
302 assert(!iterator
->found_user_name
);
303 assert(!iterator
->found_group_name
);
305 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &membership_data
);
309 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
310 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
312 iterator
->found_user_name
= strdup(membership_data
.user_name
);
313 if (!iterator
->found_user_name
) {
318 iterator
->found_group_name
= strdup(membership_data
.group_name
);
319 if (!iterator
->found_group_name
) {
326 if (FLAGS_SET(flags
, VARLINK_REPLY_CONTINUES
))
334 assert_not_reached("unexpected lookup");
338 /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
339 * errors if at least one connection ended cleanly */
340 if (r
== -ESRCH
|| iterator
->error
== 0)
341 iterator
->error
= -r
;
343 assert_se(set_remove(iterator
->links
, link
) == link
);
344 link
= varlink_unref(link
);
348 static int userdb_connect(
349 UserDBIterator
*iterator
,
353 JsonVariant
*query
) {
355 _cleanup_(varlink_unrefp
) Varlink
*vl
= NULL
;
362 r
= varlink_connect_address(&vl
, path
);
364 return log_debug_errno(r
, "Unable to connect to %s: %m", path
);
366 varlink_set_userdata(vl
, iterator
);
368 if (!iterator
->event
) {
369 r
= sd_event_new(&iterator
->event
);
371 return log_debug_errno(r
, "Unable to allocate event loop: %m");
374 r
= varlink_attach_event(vl
, iterator
->event
, SD_EVENT_PRIORITY_NORMAL
);
376 return log_debug_errno(r
, "Failed to attach varlink connection to event loop: %m");
378 (void) varlink_set_description(vl
, path
);
380 r
= varlink_bind_reply(vl
, userdb_on_query_reply
);
382 return log_debug_errno(r
, "Failed to bind reply callback: %m");
385 r
= varlink_observe(vl
, method
, query
);
387 r
= varlink_invoke(vl
, method
, query
);
389 return log_debug_errno(r
, "Failed to invoke varlink method: %m");
391 r
= set_ensure_consume(&iterator
->links
, &link_hash_ops
, TAKE_PTR(vl
));
393 return log_debug_errno(r
, "Failed to add varlink connection to set: %m");
397 static int userdb_start_query(
398 UserDBIterator
*iterator
,
404 _cleanup_(strv_freep
) char **except
= NULL
, **only
= NULL
;
405 _cleanup_(closedirp
) DIR *d
= NULL
;
413 if (FLAGS_SET(flags
, USERDB_EXCLUDE_VARLINK
))
416 e
= getenv("SYSTEMD_BYPASS_USERDB");
418 r
= parse_boolean(e
);
422 except
= strv_split(e
, ":");
428 e
= getenv("SYSTEMD_ONLY_USERDB");
430 only
= strv_split(e
, ":");
435 /* First, let's talk to the multiplexer, if we can */
436 if ((flags
& (USERDB_AVOID_MULTIPLEXER
|USERDB_EXCLUDE_DYNAMIC_USER
|USERDB_EXCLUDE_NSS
|USERDB_EXCLUDE_DROPIN
|USERDB_DONT_SYNTHESIZE
)) == 0 &&
437 !strv_contains(except
, "io.systemd.Multiplexer") &&
438 (!only
|| strv_contains(only
, "io.systemd.Multiplexer"))) {
439 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= json_variant_ref(query
);
441 r
= json_variant_set_field_string(&patched_query
, "service", "io.systemd.Multiplexer");
443 return log_debug_errno(r
, "Unable to set service JSON field: %m");
445 r
= userdb_connect(iterator
, "/run/systemd/userdb/io.systemd.Multiplexer", method
, more
, patched_query
);
447 iterator
->nss_covered
= true; /* The multiplexer does NSS */
448 iterator
->dropin_covered
= true; /* It also handles drop-in stuff */
453 d
= opendir("/run/systemd/userdb/");
461 FOREACH_DIRENT(de
, d
, return -errno
) {
462 _cleanup_(json_variant_unrefp
) JsonVariant
*patched_query
= NULL
;
463 _cleanup_free_
char *p
= NULL
;
464 bool is_nss
, is_dropin
;
466 if (streq(de
->d_name
, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
469 if (FLAGS_SET(flags
, USERDB_EXCLUDE_DYNAMIC_USER
) &&
470 streq(de
->d_name
, "io.systemd.DynamicUser"))
473 /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
474 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
475 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
477 is_nss
= streq(de
->d_name
, "io.systemd.NameServiceSwitch");
478 if ((flags
& (USERDB_EXCLUDE_NSS
|USERDB_AVOID_MULTIPLEXER
)) && is_nss
)
481 /* Similar for the drop-in service */
482 is_dropin
= streq(de
->d_name
, "io.systemd.DropIn");
483 if ((flags
& (USERDB_EXCLUDE_DROPIN
|USERDB_AVOID_MULTIPLEXER
)) && is_dropin
)
486 if (strv_contains(except
, de
->d_name
))
489 if (only
&& !strv_contains(only
, de
->d_name
))
492 p
= path_join("/run/systemd/userdb/", de
->d_name
);
496 patched_query
= json_variant_ref(query
);
497 r
= json_variant_set_field_string(&patched_query
, "service", de
->d_name
);
499 return log_debug_errno(r
, "Unable to set service JSON field: %m");
501 r
= userdb_connect(iterator
, p
, method
, more
, patched_query
);
502 if (is_nss
&& r
>= 0) /* Turn off fallback NSS + dropin if we found the NSS/dropin service
503 * and could connect to it */
504 iterator
->nss_covered
= true;
505 if (is_dropin
&& r
>= 0)
506 iterator
->dropin_covered
= true;
508 if (ret
== 0 && r
< 0)
512 if (set_isempty(iterator
->links
))
513 return ret
; /* propagate last error we saw if we couldn't connect to anything. */
515 /* We connected to some services, in this case, ignore the ones we failed on */
519 static int userdb_process(
520 UserDBIterator
*iterator
,
521 UserRecord
**ret_user_record
,
522 GroupRecord
**ret_group_record
,
523 char **ret_user_name
,
524 char **ret_group_name
) {
531 if (iterator
->what
== LOOKUP_USER
&& iterator
->found_user
) {
533 *ret_user_record
= TAKE_PTR(iterator
->found_user
);
535 iterator
->found_user
= user_record_unref(iterator
->found_user
);
537 if (ret_group_record
)
538 *ret_group_record
= NULL
;
540 *ret_user_name
= NULL
;
542 *ret_group_name
= NULL
;
547 if (iterator
->what
== LOOKUP_GROUP
&& iterator
->found_group
) {
548 if (ret_group_record
)
549 *ret_group_record
= TAKE_PTR(iterator
->found_group
);
551 iterator
->found_group
= group_record_unref(iterator
->found_group
);
554 *ret_user_record
= NULL
;
556 *ret_user_name
= NULL
;
558 *ret_group_name
= NULL
;
563 if (iterator
->what
== LOOKUP_MEMBERSHIP
&& iterator
->found_user_name
&& iterator
->found_group_name
) {
565 *ret_user_name
= TAKE_PTR(iterator
->found_user_name
);
567 iterator
->found_user_name
= mfree(iterator
->found_user_name
);
570 *ret_group_name
= TAKE_PTR(iterator
->found_group_name
);
572 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
575 *ret_user_record
= NULL
;
576 if (ret_group_record
)
577 *ret_group_record
= NULL
;
582 if (set_isempty(iterator
->links
)) {
583 if (iterator
->error
== 0)
586 return -abs(iterator
->error
);
589 if (!iterator
->event
)
592 r
= sd_event_run(iterator
->event
, UINT64_MAX
);
598 static int synthetic_root_user_build(UserRecord
**ret
) {
599 return user_record_build(
601 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
602 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
603 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
604 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
605 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
608 static int synthetic_nobody_user_build(UserRecord
**ret
) {
609 return user_record_build(
611 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME
)),
612 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY
)),
613 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
614 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
615 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
616 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
619 int userdb_by_name(const char *name
, UserDBFlags flags
, UserRecord
**ret
) {
620 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
621 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
624 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
627 r
= json_build(&query
, JSON_BUILD_OBJECT(
628 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
632 iterator
= userdb_iterator_new(LOOKUP_USER
, flags
);
636 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
638 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
643 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && !iterator
->dropin_covered
) {
644 r
= dropin_user_record_by_name(name
, NULL
, flags
, ret
);
649 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && !iterator
->nss_covered
) {
650 /* Make sure the NSS lookup doesn't recurse back to us. */
652 r
= userdb_iterator_block_nss_systemd(iterator
);
654 /* Client-side NSS fallback */
655 r
= nss_user_record_by_name(name
, !FLAGS_SET(flags
, USERDB_SUPPRESS_SHADOW
), ret
);
661 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
662 if (streq(name
, "root"))
663 return synthetic_root_user_build(ret
);
665 if (streq(name
, NOBODY_USER_NAME
) && synthesize_nobody())
666 return synthetic_nobody_user_build(ret
);
672 int userdb_by_uid(uid_t uid
, UserDBFlags flags
, UserRecord
**ret
) {
673 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
674 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
677 if (!uid_is_valid(uid
))
680 r
= json_build(&query
, JSON_BUILD_OBJECT(
681 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
))));
685 iterator
= userdb_iterator_new(LOOKUP_USER
, flags
);
689 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", false, query
, flags
);
691 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
696 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && !iterator
->dropin_covered
) {
697 r
= dropin_user_record_by_uid(uid
, NULL
, flags
, ret
);
702 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && !iterator
->nss_covered
) {
703 r
= userdb_iterator_block_nss_systemd(iterator
);
705 /* Client-side NSS fallback */
706 r
= nss_user_record_by_uid(uid
, !FLAGS_SET(flags
, USERDB_SUPPRESS_SHADOW
), ret
);
712 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
714 return synthetic_root_user_build(ret
);
716 if (uid
== UID_NOBODY
&& synthesize_nobody())
717 return synthetic_nobody_user_build(ret
);
723 int userdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
724 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
729 iterator
= userdb_iterator_new(LOOKUP_USER
, flags
);
733 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
735 qr
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetUserRecord", true, NULL
, flags
);
737 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && (qr
< 0 || !iterator
->nss_covered
)) {
738 r
= userdb_iterator_block_nss_systemd(iterator
);
743 iterator
->nss_iterating
= true;
746 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && (qr
< 0 || !iterator
->dropin_covered
)) {
747 r
= conf_files_list_nulstr(
751 CONF_FILES_REGULAR
|CONF_FILES_FILTER_MASKED
,
752 USERDB_DROPIN_DIR_NULSTR("userdb"));
754 log_debug_errno(r
, "Failed to find user drop-ins, ignoring: %m");
757 /* propagate IPC error, but only if there are no drop-ins */
759 !iterator
->nss_iterating
&&
760 strv_isempty(iterator
->dropins
))
763 *ret
= TAKE_PTR(iterator
);
767 int userdb_iterator_get(UserDBIterator
*iterator
, UserRecord
**ret
) {
771 assert(iterator
->what
== LOOKUP_USER
);
773 if (iterator
->nss_iterating
) {
776 /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
777 * the more traditional sources, which are probably good to show first. */
781 _cleanup_free_
char *buffer
= NULL
;
782 bool incomplete
= false;
785 if (streq_ptr(pw
->pw_name
, "root"))
786 iterator
->synthesize_root
= false;
787 if (pw
->pw_uid
== UID_NOBODY
)
788 iterator
->synthesize_nobody
= false;
790 if (!FLAGS_SET(iterator
->flags
, USERDB_SUPPRESS_SHADOW
)) {
791 r
= nss_spwd_for_passwd(pw
, &spwd
, &buffer
);
793 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
794 incomplete
= ERRNO_IS_PRIVILEGE(r
);
801 r
= nss_passwd_to_user_record(pw
, r
>= 0 ? &spwd
: NULL
, ret
);
806 (*ret
)->incomplete
= incomplete
;
813 log_debug_errno(errno
, "Failure to iterate NSS user database, ignoring: %m");
815 iterator
->nss_iterating
= false;
819 for (; iterator
->dropins
&& iterator
->dropins
[iterator
->current_dropin
]; iterator
->current_dropin
++) {
820 const char *i
= iterator
->dropins
[iterator
->current_dropin
];
821 _cleanup_free_
char *fn
= NULL
;
825 /* Next, let's add in the static drop-ins, which are quick to retrieve */
827 r
= path_extract_filename(i
, &fn
);
831 e
= endswith(fn
, ".user"); /* not actually a .user file? Then skip to next */
835 *e
= 0; /* Chop off suffix */
837 if (parse_uid(fn
, &uid
) < 0) /* not a UID .user file? Then skip to next */
840 r
= dropin_user_record_by_uid(uid
, i
, iterator
->flags
, ret
);
842 log_debug_errno(r
, "Failed to parse user record for UID " UID_FMT
", ignoring: %m", uid
);
843 continue; /* If we failed to parse this record, let's suppress it from enumeration,
844 * and continue with the next record. Maybe someone is dropping it files
845 * and only partially wrote this one. */
848 iterator
->current_dropin
++; /* make sure on the next call of userdb_iterator_get() we continue with the next dropin */
853 /* Then, let's return the users provided by varlink IPC */
854 r
= userdb_process(iterator
, ret
, NULL
, NULL
, NULL
);
857 /* Finally, synthesize root + nobody if not done yet */
858 if (iterator
->synthesize_root
) {
859 iterator
->synthesize_root
= false;
861 return synthetic_root_user_build(ret
);
864 if (iterator
->synthesize_nobody
) {
865 iterator
->synthesize_nobody
= false;
867 return synthetic_nobody_user_build(ret
);
870 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
871 if (iterator
->n_found
> 0)
878 static int synthetic_root_group_build(GroupRecord
**ret
) {
879 return group_record_build(
881 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
882 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
883 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
886 static int synthetic_nobody_group_build(GroupRecord
**ret
) {
887 return group_record_build(
889 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME
)),
890 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY
)),
891 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
894 int groupdb_by_name(const char *name
, UserDBFlags flags
, GroupRecord
**ret
) {
895 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
896 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
899 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
902 r
= json_build(&query
, JSON_BUILD_OBJECT(
903 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
907 iterator
= userdb_iterator_new(LOOKUP_GROUP
, flags
);
911 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
913 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
918 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && !(iterator
&& iterator
->dropin_covered
)) {
919 r
= dropin_group_record_by_name(name
, NULL
, flags
, ret
);
925 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
926 r
= userdb_iterator_block_nss_systemd(iterator
);
928 r
= nss_group_record_by_name(name
, !FLAGS_SET(flags
, USERDB_SUPPRESS_SHADOW
), ret
);
934 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
935 if (streq(name
, "root"))
936 return synthetic_root_group_build(ret
);
938 if (streq(name
, NOBODY_GROUP_NAME
) && synthesize_nobody())
939 return synthetic_nobody_group_build(ret
);
945 int groupdb_by_gid(gid_t gid
, UserDBFlags flags
, GroupRecord
**ret
) {
946 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
947 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
950 if (!gid_is_valid(gid
))
953 r
= json_build(&query
, JSON_BUILD_OBJECT(
954 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))));
958 iterator
= userdb_iterator_new(LOOKUP_GROUP
, flags
);
962 r
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", false, query
, flags
);
964 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
969 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && !(iterator
&& iterator
->dropin_covered
)) {
970 r
= dropin_group_record_by_gid(gid
, NULL
, flags
, ret
);
975 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && !(iterator
&& iterator
->nss_covered
)) {
976 r
= userdb_iterator_block_nss_systemd(iterator
);
978 r
= nss_group_record_by_gid(gid
, !FLAGS_SET(flags
, USERDB_SUPPRESS_SHADOW
), ret
);
984 if (!FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
)) {
986 return synthetic_root_group_build(ret
);
988 if (gid
== GID_NOBODY
&& synthesize_nobody())
989 return synthetic_nobody_group_build(ret
);
995 int groupdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
996 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1001 iterator
= userdb_iterator_new(LOOKUP_GROUP
, flags
);
1005 iterator
->synthesize_root
= iterator
->synthesize_nobody
= !FLAGS_SET(flags
, USERDB_DONT_SYNTHESIZE
);
1007 qr
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetGroupRecord", true, NULL
, flags
);
1009 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && (qr
< 0 || !iterator
->nss_covered
)) {
1010 r
= userdb_iterator_block_nss_systemd(iterator
);
1015 iterator
->nss_iterating
= true;
1018 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && (qr
< 0 || !iterator
->dropin_covered
)) {
1019 r
= conf_files_list_nulstr(
1023 CONF_FILES_REGULAR
|CONF_FILES_FILTER_MASKED
,
1024 USERDB_DROPIN_DIR_NULSTR("userdb"));
1026 log_debug_errno(r
, "Failed to find group drop-ins, ignoring: %m");
1030 !iterator
->nss_iterating
&&
1031 strv_isempty(iterator
->dropins
))
1034 *ret
= TAKE_PTR(iterator
);
1038 int groupdb_iterator_get(UserDBIterator
*iterator
, GroupRecord
**ret
) {
1042 assert(iterator
->what
== LOOKUP_GROUP
);
1044 if (iterator
->nss_iterating
) {
1050 _cleanup_free_
char *buffer
= NULL
;
1051 bool incomplete
= false;
1054 if (streq_ptr(gr
->gr_name
, "root"))
1055 iterator
->synthesize_root
= false;
1056 if (gr
->gr_gid
== GID_NOBODY
)
1057 iterator
->synthesize_nobody
= false;
1059 if (!FLAGS_SET(iterator
->flags
, USERDB_SUPPRESS_SHADOW
)) {
1060 r
= nss_sgrp_for_group(gr
, &sgrp
, &buffer
);
1062 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", gr
->gr_name
);
1063 incomplete
= ERRNO_IS_PRIVILEGE(r
);
1070 r
= nss_group_to_group_record(gr
, r
>= 0 ? &sgrp
: NULL
, ret
);
1075 (*ret
)->incomplete
= incomplete
;
1077 iterator
->n_found
++;
1082 log_debug_errno(errno
, "Failure to iterate NSS group database, ignoring: %m");
1084 iterator
->nss_iterating
= false;
1088 for (; iterator
->dropins
&& iterator
->dropins
[iterator
->current_dropin
]; iterator
->current_dropin
++) {
1089 const char *i
= iterator
->dropins
[iterator
->current_dropin
];
1090 _cleanup_free_
char *fn
= NULL
;
1094 r
= path_extract_filename(i
, &fn
);
1098 e
= endswith(fn
, ".group");
1102 *e
= 0; /* Chop off suffix */
1104 if (parse_gid(fn
, &gid
) < 0)
1107 r
= dropin_group_record_by_gid(gid
, i
, iterator
->flags
, ret
);
1109 log_debug_errno(r
, "Failed to parse group record for GID " GID_FMT
", ignoring: %m", gid
);
1113 iterator
->current_dropin
++;
1114 iterator
->n_found
++;
1118 r
= userdb_process(iterator
, NULL
, ret
, NULL
, NULL
);
1120 if (iterator
->synthesize_root
) {
1121 iterator
->synthesize_root
= false;
1122 iterator
->n_found
++;
1123 return synthetic_root_group_build(ret
);
1126 if (iterator
->synthesize_nobody
) {
1127 iterator
->synthesize_nobody
= false;
1128 iterator
->n_found
++;
1129 return synthetic_nobody_group_build(ret
);
1132 /* if we found at least one entry, then ignore errors and indicate that we reached the end */
1133 if (iterator
->n_found
> 0)
1140 static void discover_membership_dropins(UserDBIterator
*i
, UserDBFlags flags
) {
1143 r
= conf_files_list_nulstr(
1147 CONF_FILES_REGULAR
|CONF_FILES_BASENAME
|CONF_FILES_FILTER_MASKED
,
1148 USERDB_DROPIN_DIR_NULSTR("userdb"));
1150 log_debug_errno(r
, "Failed to find membership drop-ins, ignoring: %m");
1153 int membershipdb_by_user(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1154 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1155 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1160 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1163 r
= json_build(&query
, JSON_BUILD_OBJECT(
1164 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name
))));
1168 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
, flags
);
1172 iterator
->filter_user_name
= strdup(name
);
1173 if (!iterator
->filter_user_name
)
1176 qr
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1178 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && (qr
< 0 || !iterator
->nss_covered
)) {
1179 r
= userdb_iterator_block_nss_systemd(iterator
);
1184 iterator
->nss_iterating
= true;
1187 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && (qr
< 0 || !iterator
->dropin_covered
))
1188 discover_membership_dropins(iterator
, flags
);
1191 !iterator
->nss_iterating
&&
1192 strv_isempty(iterator
->dropins
))
1195 *ret
= TAKE_PTR(iterator
);
1199 int membershipdb_by_group(const char *name
, UserDBFlags flags
, UserDBIterator
**ret
) {
1200 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1201 _cleanup_(json_variant_unrefp
) JsonVariant
*query
= NULL
;
1206 if (!valid_user_group_name(name
, VALID_USER_RELAX
))
1209 r
= json_build(&query
, JSON_BUILD_OBJECT(
1210 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name
))));
1214 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
, flags
);
1218 iterator
->filter_group_name
= strdup(name
);
1219 if (!iterator
->filter_group_name
)
1222 qr
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, query
, flags
);
1224 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && (qr
< 0 || !iterator
->nss_covered
)) {
1225 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
1227 r
= userdb_iterator_block_nss_systemd(iterator
);
1231 /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
1232 (void) nss_group_record_by_name(name
, false, &gr
);
1234 iterator
->members_of_group
= strv_copy(gr
->members
);
1235 if (!iterator
->members_of_group
)
1238 iterator
->index_members_of_group
= 0;
1240 iterator
->found_group_name
= strdup(name
);
1241 if (!iterator
->found_group_name
)
1246 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && (qr
< 0 || !iterator
->dropin_covered
))
1247 discover_membership_dropins(iterator
, flags
);
1250 strv_isempty(iterator
->members_of_group
) &&
1251 strv_isempty(iterator
->dropins
))
1254 *ret
= TAKE_PTR(iterator
);
1258 int membershipdb_all(UserDBFlags flags
, UserDBIterator
**ret
) {
1259 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1264 iterator
= userdb_iterator_new(LOOKUP_MEMBERSHIP
, flags
);
1268 qr
= userdb_start_query(iterator
, "io.systemd.UserDatabase.GetMemberships", true, NULL
, flags
);
1270 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_NSS
) && (qr
< 0 || !iterator
->nss_covered
)) {
1271 r
= userdb_iterator_block_nss_systemd(iterator
);
1276 iterator
->nss_iterating
= true;
1279 if (!FLAGS_SET(flags
, USERDB_EXCLUDE_DROPIN
) && (qr
< 0 || !iterator
->dropin_covered
))
1280 discover_membership_dropins(iterator
, flags
);
1283 !iterator
->nss_iterating
&&
1284 strv_isempty(iterator
->dropins
))
1287 *ret
= TAKE_PTR(iterator
);
1291 int membershipdb_iterator_get(
1292 UserDBIterator
*iterator
,
1301 /* If we are iterating through NSS acquire a new group entry if we haven't acquired one yet. */
1302 if (!iterator
->members_of_group
) {
1305 if (!iterator
->nss_iterating
)
1308 assert(!iterator
->found_user_name
);
1314 log_debug_errno(errno
, "Failure during NSS group iteration, ignoring: %m");
1318 } while (iterator
->filter_user_name
? !strv_contains(g
->gr_mem
, iterator
->filter_user_name
) :
1319 strv_isempty(g
->gr_mem
));
1322 r
= free_and_strdup(&iterator
->found_group_name
, g
->gr_name
);
1326 if (iterator
->filter_user_name
)
1327 iterator
->members_of_group
= strv_new(iterator
->filter_user_name
);
1329 iterator
->members_of_group
= strv_copy(g
->gr_mem
);
1330 if (!iterator
->members_of_group
)
1333 iterator
->index_members_of_group
= 0;
1335 iterator
->nss_iterating
= false;
1341 assert(iterator
->found_group_name
);
1342 assert(iterator
->members_of_group
);
1343 assert(!iterator
->found_user_name
);
1345 if (iterator
->members_of_group
[iterator
->index_members_of_group
]) {
1346 _cleanup_free_
char *cu
= NULL
, *cg
= NULL
;
1349 cu
= strdup(iterator
->members_of_group
[iterator
->index_members_of_group
]);
1355 cg
= strdup(iterator
->found_group_name
);
1361 *ret_user
= TAKE_PTR(cu
);
1364 *ret_group
= TAKE_PTR(cg
);
1366 iterator
->index_members_of_group
++;
1370 iterator
->members_of_group
= strv_free(iterator
->members_of_group
);
1371 iterator
->found_group_name
= mfree(iterator
->found_group_name
);
1374 for (; iterator
->dropins
&& iterator
->dropins
[iterator
->current_dropin
]; iterator
->current_dropin
++) {
1375 const char *i
= iterator
->dropins
[iterator
->current_dropin
], *e
, *c
;
1376 _cleanup_free_
char *un
= NULL
, *gn
= NULL
;
1378 e
= endswith(i
, ".membership");
1382 c
= memchr(i
, ':', e
- i
);
1386 un
= strndup(i
, c
- i
);
1389 if (iterator
->filter_user_name
) {
1390 if (!streq(un
, iterator
->filter_user_name
))
1392 } else if (!valid_user_group_name(un
, VALID_USER_RELAX
))
1395 c
++; /* skip over ':' */
1396 gn
= strndup(c
, e
- c
);
1399 if (iterator
->filter_group_name
) {
1400 if (!streq(gn
, iterator
->filter_group_name
))
1402 } else if (!valid_user_group_name(gn
, VALID_USER_RELAX
))
1405 iterator
->current_dropin
++;
1406 iterator
->n_found
++;
1409 *ret_user
= TAKE_PTR(un
);
1411 *ret_group
= TAKE_PTR(gn
);
1416 r
= userdb_process(iterator
, NULL
, NULL
, ret_user
, ret_group
);
1417 if (r
< 0 && iterator
->n_found
> 0)
1423 int membershipdb_by_group_strv(const char *name
, UserDBFlags flags
, char ***ret
) {
1424 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
1425 _cleanup_strv_free_
char **members
= NULL
;
1431 r
= membershipdb_by_group(name
, flags
, &iterator
);
1436 _cleanup_free_
char *user_name
= NULL
;
1438 r
= membershipdb_iterator_get(iterator
, &user_name
, NULL
);
1444 r
= strv_consume(&members
, TAKE_PTR(user_name
));
1452 *ret
= TAKE_PTR(members
);
1456 int userdb_block_nss_systemd(int b
) {
1457 _cleanup_(dlclosep
) void *dl
= NULL
;
1458 int (*call
)(bool b
);
1460 /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
1462 dl
= dlopen(ROOTLIBDIR
"/libnss_systemd.so.2", RTLD_LAZY
|RTLD_NODELETE
);
1464 /* If the file isn't installed, don't complain loudly */
1465 log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
1469 call
= (int (*)(bool b
)) dlsym(dl
, "_nss_systemd_block");
1471 /* If the file is installed but lacks the symbol we expect, things are weird, let's complain */
1472 return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD
),
1473 "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());