diff --git a/src/home/homectl.c b/src/home/homectl.c index 825db583b0..fa91e55c75 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -2405,14 +2405,14 @@ static int has_regular_user(void) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; int r; - r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator); + r = userdb_all(/* match= */ NULL, USERDB_SUPPRESS_SHADOW, &iterator); if (r < 0) return log_error_errno(r, "Failed to create user enumerator: %m"); for (;;) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; - r = userdb_iterator_get(iterator, &ur); + r = userdb_iterator_get(iterator, /* match= */ NULL, &ur); if (r == -ESRCH) break; if (r < 0) @@ -2432,7 +2432,7 @@ static int acquire_group_list(char ***ret) { assert(ret); - r = groupdb_all(USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &iterator); + r = groupdb_all(/* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &iterator); if (r == -ENOLINK) log_debug_errno(r, "No groups found. (Didn't check via Varlink.)"); else if (r == -ESRCH) @@ -2443,7 +2443,7 @@ static int acquire_group_list(char ***ret) { for (;;) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; - r = groupdb_iterator_get(iterator, &gr); + r = groupdb_iterator_get(iterator, /* match= */ NULL, &gr); if (r == -ESRCH) break; if (r < 0) @@ -2457,7 +2457,7 @@ static int acquire_group_list(char ***ret) { /* Filter groups here that belong to a specific user, and are named like them */ - r = userdb_by_name(gr->group_name, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &ur); + r = userdb_by_name(gr->group_name, /* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &ur); if (r < 0 && r != -ESRCH) return log_debug_errno(r, "Failed to check if matching user exists for group '%s': %m", gr->group_name); @@ -2508,7 +2508,7 @@ static int create_interactively(void) { continue; } - r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL); + r = userdb_by_name(username, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL); if (r == -ESRCH) break; if (r < 0) @@ -2578,7 +2578,7 @@ static int create_interactively(void) { continue; } - r = groupdb_by_name(s, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, /*ret=*/ NULL); + r = groupdb_by_name(s, /* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, /*ret=*/ NULL); if (r == -ESRCH) { log_notice("Specified auxiliary group does not exist, try again: %s", s); continue; diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 719aeef017..98d3718c86 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -191,7 +191,7 @@ int manager_add_user_by_name( assert(m); assert(name); - r = userdb_by_name(name, USERDB_SUPPRESS_SHADOW, &ur); + r = userdb_by_name(name, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, &ur); if (r < 0) return r; @@ -209,7 +209,7 @@ int manager_add_user_by_uid( assert(m); assert(uid_is_valid(uid)); - r = userdb_by_uid(uid, USERDB_SUPPRESS_SHADOW, &ur); + r = userdb_by_uid(uid, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, &ur); if (r < 0) return r; diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index ffca6045b6..cf46eba195 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -223,7 +223,7 @@ static int acquire_user_record( _cleanup_free_ char *formatted = NULL; /* Request the record ourselves */ - r = userdb_by_name(username, /* flags= */ 0, &ur); + r = userdb_by_name(username, /* match= */ NULL, /* flags= */ 0, &ur); if (r < 0) { pam_syslog_errno(handle, LOG_ERR, r, "Failed to get user record: %m"); return PAM_USER_UNKNOWN; diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index 6c2fef95db..94117c95db 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -341,7 +341,7 @@ static int run(int argc, char *argv[]) { if (streq(verb, "start")) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; - r = userdb_by_name(user, USERDB_PARSE_NUMERIC|USERDB_SUPPRESS_SHADOW, &ur); + r = userdb_by_name(user, /* match= */ NULL, USERDB_PARSE_NUMERIC|USERDB_SUPPRESS_SHADOW, &ur); if (r == -ESRCH) return log_error_errno(r, "User '%s' does not exist: %m", user); if (r < 0) diff --git a/src/nspawn/nspawn-bind-user.c b/src/nspawn/nspawn-bind-user.c index 749accdce8..8964de22a1 100644 --- a/src/nspawn/nspawn-bind-user.c +++ b/src/nspawn/nspawn-bind-user.c @@ -231,7 +231,7 @@ int bind_user_prepare( _cleanup_(group_record_unrefp) GroupRecord *g = NULL, *cg = NULL; _cleanup_free_ char *sm = NULL, *sd = NULL; - r = userdb_by_name(*n, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &u); + r = userdb_by_name(*n, /* match= */ NULL, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &u); if (r < 0) return log_error_errno(r, "Failed to resolve user '%s': %m", *n); @@ -252,7 +252,7 @@ int bind_user_prepare( if (u->uid >= uid_shift && u->uid < uid_shift + uid_range) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID of user '%s' to map is already in container UID range, refusing.", u->user_name); - r = groupdb_by_gid(u->gid, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &g); + r = groupdb_by_gid(u->gid, /* match= */ NULL, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &g); if (r < 0) return log_error_errno(r, "Failed to resolve group of user '%s': %m", u->user_name); diff --git a/src/nsresourced/nsresourcework.c b/src/nsresourced/nsresourcework.c index 08277473e2..f5b6a96d07 100644 --- a/src/nsresourced/nsresourcework.c +++ b/src/nsresourced/nsresourcework.c @@ -362,13 +362,13 @@ static int uid_is_available( if (r > 0) return false; - r = userdb_by_uid(candidate, USERDB_AVOID_MULTIPLEXER, NULL); + r = userdb_by_uid(candidate, /* match= */ NULL, USERDB_AVOID_MULTIPLEXER, /* ret_record= */ NULL); if (r >= 0) return false; if (r != -ESRCH) return r; - r = groupdb_by_gid(candidate, USERDB_AVOID_MULTIPLEXER, NULL); + r = groupdb_by_gid(candidate, /* match= */ NULL, USERDB_AVOID_MULTIPLEXER, /* ret_record= */ NULL); if (r >= 0) return false; if (r != -ESRCH) @@ -399,13 +399,13 @@ static int name_is_available( if (!user_name) return -ENOMEM; - r = userdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL); + r = userdb_by_name(user_name, /* match= */ NULL, USERDB_AVOID_MULTIPLEXER, /* ret_record= */ NULL); if (r >= 0) return false; if (r != -ESRCH) return r; - r = groupdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL); + r = groupdb_by_name(user_name, /* match= */ NULL, USERDB_AVOID_MULTIPLEXER, /* ret_record= */ NULL); if (r >= 0) return false; if (r != -ESRCH) diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 683352fab9..6a13b87b02 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -619,7 +619,7 @@ enum nss_status _nss_systemd_setpwent(int stayopen) { * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the * user database. */ - r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getpwent_data.iterator); + r = userdb_all(/* match= */ NULL, nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getpwent_data.iterator); return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } @@ -639,7 +639,7 @@ enum nss_status _nss_systemd_setgrent(int stayopen) { getgrent_data.by_membership = false; /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE_INTRINSIC here */ - r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getgrent_data.iterator); + r = groupdb_all(/* match= */ NULL, nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getgrent_data.iterator); return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } @@ -659,7 +659,7 @@ enum nss_status _nss_systemd_setspent(int stayopen) { getspent_data.by_membership = false; /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE_INTRINSIC here */ - r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getspent_data.iterator); + r = userdb_all(/* match= */ NULL, nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getspent_data.iterator); return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } @@ -679,7 +679,7 @@ enum nss_status _nss_systemd_setsgent(int stayopen) { getsgent_data.by_membership = false; /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */ - r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getsgent_data.iterator); + r = groupdb_all(/* match= */ NULL, nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getsgent_data.iterator); return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } @@ -709,7 +709,7 @@ enum nss_status _nss_systemd_getpwent_r( return NSS_STATUS_UNAVAIL; } - r = userdb_iterator_get(getpwent_data.iterator, &ur); + r = userdb_iterator_get(getpwent_data.iterator, /* match= */ NULL, &ur); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -756,7 +756,7 @@ enum nss_status _nss_systemd_getgrent_r( } if (!getgrent_data.by_membership) { - r = groupdb_iterator_get(getgrent_data.iterator, &gr); + r = groupdb_iterator_get(getgrent_data.iterator, /* match= */ NULL, &gr); if (r == -ESRCH) { /* So we finished iterating native groups now. Let's now continue with iterating * native memberships, and generate additional group entries for any groups @@ -882,7 +882,7 @@ enum nss_status _nss_systemd_getspent_r( } for (;;) { - r = userdb_iterator_get(getspent_data.iterator, &ur); + r = userdb_iterator_get(getspent_data.iterator, /* match= */ NULL, &ur); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -934,7 +934,7 @@ enum nss_status _nss_systemd_getsgent_r( } for (;;) { - r = groupdb_iterator_get(getsgent_data.iterator, &gr); + r = groupdb_iterator_get(getsgent_data.iterator, /* match= */ NULL, &gr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -1014,7 +1014,7 @@ enum nss_status _nss_systemd_initgroups_dyn( /* The group might be defined via traditional NSS only, hence let's do a full look-up without * disabling NSS. This means we are operating recursively here. */ - r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g); + r = groupdb_by_name(group_name, /* match= */ NULL, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g); if (r == -ESRCH) continue; if (r < 0) { diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 61311d8db3..4f96e9b90f 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -81,7 +81,7 @@ enum nss_status userdb_getpwnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = userdb_by_name(name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); + r = userdb_by_name(name, /* match= */ NULL, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -114,7 +114,7 @@ enum nss_status userdb_getpwuid( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = userdb_by_uid(uid, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); + r = userdb_by_uid(uid, /* match= */ NULL, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -190,7 +190,7 @@ enum nss_status userdb_getspnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = userdb_by_name(name, nss_glue_userdb_flags(), &hr); + r = userdb_by_name(name, /* match= */ NULL, nss_glue_userdb_flags(), &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -290,7 +290,7 @@ enum nss_status userdb_getgrnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = groupdb_by_name(name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); + r = groupdb_by_name(name, /* match= */ NULL, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; @@ -357,7 +357,7 @@ enum nss_status userdb_getgrgid( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = groupdb_by_gid(gid, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); + r = groupdb_by_gid(gid, /* match= */ NULL, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; @@ -456,7 +456,7 @@ enum nss_status userdb_getsgnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = groupdb_by_name(name, nss_glue_userdb_flags(), &hr); + r = groupdb_by_name(name, /* match= */ NULL, nss_glue_userdb_flags(), &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index a9c635a478..af4f7cfb6c 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -221,7 +221,7 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (show_full_group_info) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; - r = groupdb_by_gid(hr->gid, 0, &gr); + r = groupdb_by_gid(hr->gid, /* match= */ NULL, /* flags= */ 0, &gr); if (r < 0) { errno = -r; printf(" GID: " GID_FMT " (unresolvable: %m)\n", hr->gid); diff --git a/src/shared/userdb.c b/src/shared/userdb.c index 32f851b0f3..ac505285bb 100644 --- a/src/shared/userdb.c +++ b/src/shared/userdb.c @@ -4,6 +4,7 @@ #include "sd-varlink.h" +#include "bitfield.h" #include "conf-files.h" #include "dirent-util.h" #include "dlfcn-util.h" @@ -35,16 +36,23 @@ struct UserDBIterator { LookupWhat what; UserDBFlags flags; Set *links; + + const char *method; /* Note, this is a const static string! */ + sd_json_variant *query; + + bool more:1; bool nss_covered:1; bool nss_iterating:1; bool dropin_covered:1; bool synthesize_root:1; bool synthesize_nobody:1; bool nss_systemd_blocked:1; + char **dropins; size_t current_dropin; int error; unsigned n_found; + sd_event *event; UserRecord *found_user; /* when .what == LOOKUP_USER */ GroupRecord *found_group; /* when .what == LOOKUP_GROUP */ @@ -55,10 +63,14 @@ struct UserDBIterator { char *filter_user_name, *filter_group_name; }; +static int userdb_connect(UserDBIterator *iterator, const char *path, const char *method, bool more, sd_json_variant *query); + UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) { if (!iterator) return NULL; + sd_json_variant_unref(iterator->query); + set_free(iterator->links); strv_free(iterator->dropins); @@ -159,6 +171,70 @@ static void membership_data_done(struct membership_data *d) { free(d->group_name); } +static int userdb_maybe_restart_query( + UserDBIterator *iterator, + sd_varlink *link, + sd_json_variant *parameters, + const char *error_id) { + + int r; + + assert(iterator); + assert(link); + assert(error_id); + + /* These fields were added in v258 and didn't exist in previous implementations. Hence, we consider + * their support optional: if any service refuses any of these fields, we'll restart the query + * without them, and apply the filtering they are supposed to do client side. */ + static const char *const fields[] = { + "fuzzyNames", + "dispositionMask", + "uidMin", + "uidMax", + "gidMin", + "gidMax", + NULL + }; + + /* Figure out if the reported error indicates any of the suppressable fields are at fault, and that + * our query actually included them */ + bool restart = false; + STRV_FOREACH(f, fields) { + if (!sd_varlink_error_is_invalid_parameter(error_id, parameters, *f)) + continue; + + if (!sd_json_variant_by_key(iterator->query, *f)) + continue; + + restart = true; + break; + } + + if (!restart) + return 0; + + /* Now patch the fields out */ + _cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query = + sd_json_variant_ref(iterator->query); + + r = sd_json_variant_filter(&patched_query, (char**const) fields); + if (r < 0) + return r; + + /* NB: we stored the socket path in the varlink connection description when we set things up here! */ + r = userdb_connect( + iterator, + ASSERT_PTR(sd_varlink_get_description(link)), + iterator->method, + iterator->more, + patched_query); + if (r < 0) + return r; + + log_debug("Restarted query to service '%s' due to missing features.", sd_varlink_get_description(link)); + return 1; +} + static int userdb_on_query_reply( sd_varlink *link, sd_json_variant *parameters, @@ -172,6 +248,14 @@ static int userdb_on_query_reply( if (error_id) { log_debug("Got lookup error: %s", error_id); + r = userdb_maybe_restart_query(iterator, link, parameters, error_id); + if (r < 0) + return r; + if (r > 0) { + r = 0; + goto finish; + } + /* Convert various forms of record not found into -ESRCH, since NSS typically doesn't care, * about the details. Note that if a userName specification is refused as invalid parameter, * we also turn this into -ESRCH following the logic that there cannot be a user record for a @@ -182,6 +266,8 @@ static int userdb_on_query_reply( sd_varlink_error_is_invalid_parameter(error_id, parameters, "userName") || sd_varlink_error_is_invalid_parameter(error_id, parameters, "groupName")) r = -ESRCH; + else if (streq(error_id, "io.systemd.UserDatabase.NonMatchingRecordFound")) + r = -ENOEXEC; else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable")) r = -EHOSTDOWN; else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported")) @@ -338,9 +424,9 @@ static int userdb_on_query_reply( } finish: - /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad - * errors if at least one connection ended cleanly */ - if (r == -ESRCH || iterator->error == 0) + /* If we got one ESRCH or ENOEXEC, let that win. This way when we do a wild dump we won't be tripped + * up by bad errors – as long as at least one connection ended somewhat cleanly */ + if (IN_SET(r, -ESRCH, -ENOEXEC) || iterator->error == 0) iterator->error = -r; assert_se(set_remove(iterator->links, link) == link); @@ -378,7 +464,12 @@ static int userdb_connect( if (r < 0) return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m"); - (void) sd_varlink_set_description(vl, path); + /* Note, this is load bearing: we store the socket path as description for the varlink + * connection. That's not just good for debugging, but we reuse this information in case we need to + * reissue the query with a reduced set of parameters. */ + r = sd_varlink_set_description(vl, path); + if (r < 0) + return log_debug_errno(r, "Failed to set varlink connection description: %m"); r = sd_varlink_bind_reply(vl, userdb_on_query_reply); if (r < 0) @@ -410,7 +501,7 @@ static int userdb_connect( static int userdb_start_query( UserDBIterator *iterator, - const char *method, + const char *method, /* must be a static string, we are not going to copy this here! */ bool more, sd_json_variant *query, UserDBFlags flags) { @@ -426,6 +517,11 @@ static int userdb_start_query( if (FLAGS_SET(flags, USERDB_EXCLUDE_VARLINK)) return -ENOLINK; + assert(!iterator->query); + iterator->method = method; /* note: we don't make a copy here! */ + iterator->query = sd_json_variant_ref(query); + iterator->more = more; + e = getenv("SYSTEMD_BYPASS_USERDB"); if (e) { r = parse_boolean(e); @@ -674,38 +770,61 @@ nomatch: return 0; } -int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) { - _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; +static int query_append_disposition_mask(sd_json_variant **query, uint64_t mask) { int r; - if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) { - uid_t uid; + assert(query); - if (parse_uid(name, &uid) >= 0) - return userdb_by_uid(uid, flags, ret); - } + if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL)) + return 0; - if (!valid_user_group_name(name, VALID_USER_RELAX)) - return -EINVAL; + _cleanup_strv_free_ char **dispositions = NULL; + for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) { + if (!BITS_SET(mask, d)) + continue; - r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(name))); - if (r < 0) - return r; - - iterator = userdb_iterator_new(LOOKUP_USER, flags); - if (!iterator) - return -ENOMEM; - - r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags); - if (r >= 0) { - r = userdb_process(iterator, ret, NULL, NULL, NULL); - if (r >= 0) + r = strv_extend(&dispositions, user_disposition_to_string(d)); + if (r < 0) return r; } + return sd_json_variant_merge_objectbo( + query, + SD_JSON_BUILD_PAIR_STRV("dispositionMask", dispositions)); +} + +static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *match) { + int r; + + assert(query); + + if (!userdb_match_is_set(match)) + return 0; + + r = sd_json_variant_merge_objectbo( + query, + SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)), + SD_JSON_BUILD_PAIR_CONDITION(match->uid_min > 0, "uidMin", SD_JSON_BUILD_UNSIGNED(match->uid_min)), + SD_JSON_BUILD_PAIR_CONDITION(match->uid_max < UID_INVALID-1, "uidMax", SD_JSON_BUILD_UNSIGNED(match->uid_max))); + if (r < 0) + return r; + + return query_append_disposition_mask(query, match->disposition_mask); +} + +static int userdb_by_name_fallbacks( + const char *name, + UserDBIterator *iterator, + UserDBFlags flags, + UserRecord **ret) { + int r; + + assert(name); + assert(iterator); + assert(ret); + if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) { - r = dropin_user_record_by_name(name, NULL, flags, ret); + r = dropin_user_record_by_name(name, /* path= */ NULL, flags, ret); if (r >= 0) return r; } @@ -737,21 +856,41 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) { return r; if (r > 0) return synthetic_foreign_user_build(foreign_uid, ret); - r = -ESRCH; } - return r; + return -ESRCH; } -int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) { +int userdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; int r; - if (!uid_is_valid(uid)) + /* Well known errors this returns: + * -EINVAL → user name is not valid + * -ESRCH → no such user + * -ENOEXEC → found a user by request UID or name, but it does not match filter + * -EHOSTDOWN → service failed for some reason + * -ETIMEDOUT → service timed out + */ + + assert(name); + + if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) { + uid_t uid; + + if (parse_uid(name, &uid) >= 0) + return userdb_by_uid(uid, match, flags, ret); + } + + if (!valid_user_group_name(name, VALID_USER_RELAX)) return -EINVAL; - r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(uid))); + r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(name))); + if (r < 0) + return r; + + r = query_append_uid_match(&query, match); if (r < 0) return r; @@ -759,12 +898,45 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) { if (!iterator) return -ENOMEM; - r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags); + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ false, query, flags); if (r >= 0) { - r = userdb_process(iterator, ret, NULL, NULL, NULL); - if (r >= 0) + r = userdb_process(iterator, &ur, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL); + if (r == -ENOEXEC) /* found a user matching UID or name, but not filter. In this case the + * fallback paths below are pointless */ return r; } + if (r < 0) { /* If the above fails for any other reason, try fallback paths */ + r = userdb_by_name_fallbacks(name, iterator, flags, &ur); + if (r < 0) + return r; + } + + /* NB: we always apply our own filtering here, explicitly, regardless if the server supported it or + * not. It's more robust this way, we never know how carefully the server is written, and whether it + * properly implements all details of the filtering logic. */ + r = user_record_match(ur, match); + if (r < 0) + return r; + if (r == 0) + return -ENOEXEC; + + if (ret) + *ret = TAKE_PTR(ur); + + return 0; +} + +static int userdb_by_uid_fallbacks( + uid_t uid, + UserDBIterator *iterator, + UserDBFlags flags, + UserRecord **ret) { + int r; + + assert(uid_is_valid(uid)); + assert(iterator); + assert(ret); if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) { r = dropin_user_record_by_uid(uid, NULL, flags, ret); @@ -793,20 +965,70 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) { if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && uid_is_foreign(uid)) return synthetic_foreign_user_build(uid - FOREIGN_UID_BASE, ret); - return r; + return -ESRCH; } -int userdb_all(UserDBFlags flags, UserDBIterator **ret) { +int userdb_by_uid(uid_t uid, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - int r, qr; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; + int r; - assert(ret); + if (!uid_is_valid(uid)) + return -EINVAL; + + r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(uid))); + if (r < 0) + return r; + + r = query_append_uid_match(&query, match); + if (r < 0) + return r; iterator = userdb_iterator_new(LOOKUP_USER, flags); if (!iterator) return -ENOMEM; - qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags); + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ false, query, flags); + if (r >= 0) { + r = userdb_process(iterator, &ur, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL); + if (r == -ENOEXEC) + return r; + } + if (r < 0) { + r = userdb_by_uid_fallbacks(uid, iterator, flags, &ur); + if (r < 0) + return r; + } + + r = user_record_match(ur, match); + if (r < 0) + return r; + if (r == 0) + return -ENOEXEC; + + if (ret) + *ret = TAKE_PTR(ur); + + return 0; +} + +int userdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret) { + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; + int r, qr; + + assert(ret); + + r = query_append_uid_match(&query, match); + if (r < 0) + return r; + + iterator = userdb_iterator_new(LOOKUP_USER, flags); + if (!iterator) + return -ENOMEM; + + qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ true, query, flags); if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) { r = userdb_iterator_block_nss_systemd(iterator); @@ -840,7 +1062,7 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) { return 0; } -int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) { +static int userdb_iterator_get_one(UserDBIterator *iterator, UserRecord **ret) { int r; assert(iterator); @@ -928,7 +1150,7 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) { } /* Then, let's return the users provided by varlink IPC */ - r = userdb_process(iterator, ret, NULL, NULL, NULL); + r = userdb_process(iterator, ret, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL); if (r < 0) { /* Finally, synthesize root + nobody if not done yet */ @@ -952,6 +1174,29 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) { return r; } +int userdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, UserRecord **ret) { + int r; + + assert(iterator); + assert(iterator->what == LOOKUP_USER); + + for (;;) { + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + + r = userdb_iterator_get_one(iterator, userdb_match_is_set(match) || ret ? &ur : NULL); + if (r < 0) + return r; + + if (ur && !user_record_match(ur, match)) + continue; + + if (ret) + *ret = TAKE_PTR(ur); + + return r; + } +} + static int synthetic_root_group_build(GroupRecord **ret) { return group_record_build( ret, @@ -993,43 +1238,44 @@ static int synthetic_foreign_group_build(gid_t foreign_gid, GroupRecord **ret) { SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign")))); } -int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) { - _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; +static int query_append_gid_match(sd_json_variant **query, const UserDBMatch *match) { int r; - if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) { - gid_t gid; + assert(query); - if (parse_gid(name, &gid) >= 0) - return groupdb_by_gid(gid, flags, ret); - } + if (!userdb_match_is_set(match)) + return 0; - if (!valid_user_group_name(name, VALID_USER_RELAX)) - return -EINVAL; - - r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(name))); + r = sd_json_variant_merge_objectbo( + query, + SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)), + SD_JSON_BUILD_PAIR_CONDITION(match->gid_min > 0, "gidMin", SD_JSON_BUILD_UNSIGNED(match->gid_min)), + SD_JSON_BUILD_PAIR_CONDITION(match->gid_max < GID_INVALID-1, "gidMax", SD_JSON_BUILD_UNSIGNED(match->gid_max))); if (r < 0) return r; - iterator = userdb_iterator_new(LOOKUP_GROUP, flags); - if (!iterator) - return -ENOMEM; + return query_append_disposition_mask(query, match->disposition_mask); +} - r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags); - if (r >= 0) { - r = userdb_process(iterator, NULL, ret, NULL, NULL); - if (r >= 0) - return r; - } +static int groupdb_by_name_fallbacks( + const char *name, + UserDBIterator *iterator, + UserDBFlags flags, + GroupRecord **ret) { - if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) { + int r; + + assert(name); + assert(iterator); + assert(ret); + + if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) { r = dropin_group_record_by_name(name, NULL, flags, ret); if (r >= 0) return r; } - if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) { + if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) { r = userdb_iterator_block_nss_systemd(iterator); if (r >= 0) { r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret); @@ -1053,21 +1299,33 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) { return r; if (r > 0) return synthetic_foreign_group_build(foreign_gid, ret); - r = -ESRCH; } - return r; + return -ESRCH; } -int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) { +int groupdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; int r; - if (!gid_is_valid(gid)) + assert(name); + + if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) { + gid_t gid; + + if (parse_gid(name, &gid) >= 0) + return groupdb_by_gid(gid, match, flags, ret); + } + + if (!valid_user_group_name(name, VALID_USER_RELAX)) return -EINVAL; - r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(gid))); + r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(name))); + if (r < 0) + return r; + + r = query_append_gid_match(&query, match); if (r < 0) return r; @@ -1075,12 +1333,42 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) { if (!iterator) return -ENOMEM; - r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags); + _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ false, query, flags); if (r >= 0) { - r = userdb_process(iterator, NULL, ret, NULL, NULL); - if (r >= 0) + r = userdb_process(iterator, /* ret_user_record= */ NULL, &gr, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL); + if (r == -ENOEXEC) return r; } + if (r < 0) { + r = groupdb_by_name_fallbacks(name, iterator, flags, &gr); + if (r < 0) + return r; + } + + /* As above, we apply our own client-side filtering even if server-side filtering worked, for robustness and simplicity reasons. */ + r = group_record_match(gr, match); + if (r < 0) + return r; + if (r == 0) + return -ENOEXEC; + + if (ret) + *ret = TAKE_PTR(gr); + + return r; +} + +static int groupdb_by_gid_fallbacks( + gid_t gid, + UserDBIterator *iterator, + UserDBFlags flags, + GroupRecord **ret) { + int r; + + assert(gid_is_valid(gid)); + assert(iterator); + assert(ret); if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) { r = dropin_group_record_by_gid(gid, NULL, flags, ret); @@ -1108,20 +1396,70 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) { if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && gid_is_foreign(gid)) return synthetic_foreign_group_build(gid - FOREIGN_UID_BASE, ret); - return r; + return -ESRCH; } -int groupdb_all(UserDBFlags flags, UserDBIterator **ret) { +int groupdb_by_gid(gid_t gid, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - int r, qr; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; + int r; - assert(ret); + if (!gid_is_valid(gid)) + return -EINVAL; + + r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(gid))); + if (r < 0) + return r; + + r = query_append_gid_match(&query, match); + if (r < 0) + return r; iterator = userdb_iterator_new(LOOKUP_GROUP, flags); if (!iterator) return -ENOMEM; - qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags); + _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ false, query, flags); + if (r >= 0) { + r = userdb_process(iterator, /* ret_user_record= */ NULL, &gr, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL); + if (r == -ENOEXEC) + return r; + } + if (r < 0) { + r = groupdb_by_gid_fallbacks(gid, iterator, flags, &gr); + if (r < 0) + return r; + } + + r = group_record_match(gr, match); + if (r < 0) + return r; + if (r == 0) + return -ENOEXEC; + + if (ret) + *ret = TAKE_PTR(gr); + + return 0; +} + +int groupdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret) { + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL; + int r, qr; + + assert(ret); + + r = query_append_gid_match(&query, match); + if (r < 0) + return r; + + iterator = userdb_iterator_new(LOOKUP_GROUP, flags); + if (!iterator) + return -ENOMEM; + + qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ true, query, flags); if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) { r = userdb_iterator_block_nss_systemd(iterator); @@ -1152,7 +1490,7 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) { return 0; } -int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) { +static int groupdb_iterator_get_one(UserDBIterator *iterator, GroupRecord **ret) { int r; assert(iterator); @@ -1254,6 +1592,29 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) { return r; } +int groupdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, GroupRecord **ret) { + int r; + + assert(iterator); + assert(iterator->what == LOOKUP_GROUP); + + for (;;) { + _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + + r = groupdb_iterator_get_one(iterator, userdb_match_is_set(match) || ret ? &gr : NULL); + if (r < 0) + return r; + + if (gr && !group_record_match(gr, match)) + continue; + + if (ret) + *ret = TAKE_PTR(gr); + + return r; + } +} + static void discover_membership_dropins(UserDBIterator *i, UserDBFlags flags) { int r; diff --git a/src/shared/userdb.h b/src/shared/userdb.h index 9bb47efbfe..783e39d591 100644 --- a/src/shared/userdb.h +++ b/src/shared/userdb.h @@ -42,15 +42,15 @@ typedef enum UserDBFlags { * -ETIMEDOUT: Time-out */ -int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret); -int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret); -int userdb_all(UserDBFlags flags, UserDBIterator **ret); -int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret); +int userdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret); +int userdb_by_uid(uid_t uid, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret); +int userdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret); +int userdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, UserRecord **ret); -int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret); -int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret); -int groupdb_all(UserDBFlags flags, UserDBIterator **ret); -int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret); +int groupdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret); +int groupdb_by_gid(gid_t gid, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret); +int groupdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret); +int groupdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, GroupRecord **ret); int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret); int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret); diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index 6730b531b7..81007c4e50 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -396,7 +396,7 @@ static int display_user(int argc, char *argv[], void *userdata) { (void) table_hide_column_from_display(table, (size_t) 0); } - UserDBMatch match = { + _cleanup_(userdb_match_done) UserDBMatch match = { .disposition_mask = arg_disposition_mask, .uid_min = arg_uid_min, .uid_max = arg_uid_max, @@ -406,19 +406,18 @@ static int display_user(int argc, char *argv[], void *userdata) { STRV_FOREACH(i, argv + 1) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; - r = userdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &ur); + r = userdb_by_name(*i, &match, arg_userdb_flags|USERDB_PARSE_NUMERIC, &ur); if (r < 0) { if (r == -ESRCH) log_error_errno(r, "User %s does not exist.", *i); else if (r == -EHOSTDOWN) log_error_errno(r, "Selected user database service is not available for this request."); + else if (r == -ENOEXEC) + log_error_errno(r, "User '%s' exists but does not match specified filter.", *i); else log_error_errno(r, "Failed to find user %s: %m", *i); RET_GATHER(ret, r); - } else if (!user_record_match(ur, &match)) { - log_error("User '%s' does not match filter.", *i); - RET_GATHER(ret, -ENOEXEC); } else { if (draw_separator && arg_output == OUTPUT_FRIENDLY) putchar('\n'); @@ -431,18 +430,15 @@ static int display_user(int argc, char *argv[], void *userdata) { } } else { - _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - _cleanup_strv_free_ char **names = NULL; - if (argc > 1) { - names = strv_copy(argv + 1); - if (!names) + /* If there are further arguments, they are the fuzzy match strings. */ + match.fuzzy_names = strv_copy(strv_skip(argv, 1)); + if (!match.fuzzy_names) return log_oom(); - - match.fuzzy_names = names; } - r = userdb_all(arg_userdb_flags, &iterator); + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + r = userdb_all(&match, arg_userdb_flags, &iterator); if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */ log_debug_errno(r, "No entries found. (Didn't check via Varlink.)"); else if (r == -ESRCH) /* ESRCH → Couldn't find any suitable entry, but we checked all sources */ @@ -453,7 +449,7 @@ static int display_user(int argc, char *argv[], void *userdata) { for (;;) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; - r = userdb_iterator_get(iterator, &ur); + r = userdb_iterator_get(iterator, &match, &ur); if (r == -ESRCH) break; if (r == -EHOSTDOWN) @@ -461,9 +457,6 @@ static int display_user(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed acquire next user: %m"); - if (!user_record_match(ur, &match)) - continue; - if (draw_separator && arg_output == OUTPUT_FRIENDLY) putchar('\n'); @@ -728,7 +721,7 @@ static int display_group(int argc, char *argv[], void *userdata) { (void) table_hide_column_from_display(table, (size_t) 0); } - UserDBMatch match = { + _cleanup_(userdb_match_done) UserDBMatch match = { .disposition_mask = arg_disposition_mask, .gid_min = arg_uid_min, .gid_max = arg_uid_max, @@ -738,19 +731,18 @@ static int display_group(int argc, char *argv[], void *userdata) { STRV_FOREACH(i, argv + 1) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; - r = groupdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &gr); + r = groupdb_by_name(*i, &match, arg_userdb_flags|USERDB_PARSE_NUMERIC, &gr); if (r < 0) { if (r == -ESRCH) log_error_errno(r, "Group %s does not exist.", *i); else if (r == -EHOSTDOWN) log_error_errno(r, "Selected group database service is not available for this request."); + else if (r == -ENOEXEC) + log_error_errno(r, "Group '%s' exists but does not match specified filter.", *i); else log_error_errno(r, "Failed to find group %s: %m", *i); RET_GATHER(ret, r); - } else if (!group_record_match(gr, &match)) { - log_error("Group '%s' does not match filter.", *i); - RET_GATHER(ret, -ENOEXEC); } else { if (draw_separator && arg_output == OUTPUT_FRIENDLY) putchar('\n'); @@ -763,18 +755,14 @@ static int display_group(int argc, char *argv[], void *userdata) { } } else { - _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; - _cleanup_strv_free_ char **names = NULL; - if (argc > 1) { - names = strv_copy(argv + 1); - if (!names) + match.fuzzy_names = strv_copy(strv_skip(argv, 1)); + if (!match.fuzzy_names) return log_oom(); - - match.fuzzy_names = names; } - r = groupdb_all(arg_userdb_flags, &iterator); + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + r = groupdb_all(&match, arg_userdb_flags, &iterator); if (r == -ENOLINK) log_debug_errno(r, "No entries found. (Didn't check via Varlink.)"); else if (r == -ESRCH) @@ -785,7 +773,7 @@ static int display_group(int argc, char *argv[], void *userdata) { for (;;) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; - r = groupdb_iterator_get(iterator, &gr); + r = groupdb_iterator_get(iterator, &match, &gr); if (r == -ESRCH) break; if (r == -EHOSTDOWN) @@ -793,9 +781,6 @@ static int display_group(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed acquire next group: %m"); - if (!group_record_match(gr, &match)) - continue; - if (draw_separator && arg_output == OUTPUT_FRIENDLY) putchar('\n'); @@ -1089,7 +1074,7 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) { chain_invocation = NULL; } - r = userdb_by_name(argv[1], arg_userdb_flags, &ur); + r = userdb_by_name(argv[1], /* match= */ NULL, arg_userdb_flags, &ur); if (r == -ESRCH) log_error_errno(r, "User %s does not exist.", argv[1]); else if (r == -EHOSTDOWN) diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c index c8fef87326..da115ec6e5 100644 --- a/src/userdb/userwork.c +++ b/src/userdb/userwork.c @@ -160,14 +160,14 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete return r; if (uid_is_valid(p.uid)) - r = userdb_by_uid(p.uid, userdb_flags, &hr); + r = userdb_by_uid(p.uid, /* match= */ NULL, userdb_flags, &hr); else if (p.name) - r = userdb_by_name(p.name, userdb_flags, &hr); + r = userdb_by_name(p.name, /* match= */ NULL, userdb_flags, &hr); else { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL; - r = userdb_all(userdb_flags, &iterator); + r = userdb_all(/* match= */ NULL, userdb_flags, &iterator); if (IN_SET(r, -ESRCH, -ENOLINK)) /* We turn off Varlink lookups in various cases (e.g. in case we only enable DropIn * backend) — this might make userdb_all return ENOLINK (which indicates that varlink @@ -182,7 +182,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete for (;;) { _cleanup_(user_record_unrefp) UserRecord *z = NULL; - r = userdb_iterator_get(iterator, &z); + r = userdb_iterator_get(iterator, /* match= */ NULL, &z); if (r == -ESRCH) break; if (r < 0) @@ -296,14 +296,14 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet return r; if (gid_is_valid(p.gid)) - r = groupdb_by_gid(p.gid, userdb_flags, &g); + r = groupdb_by_gid(p.gid, /* match= */ NULL, userdb_flags, &g); else if (p.name) - r = groupdb_by_name(p.name, userdb_flags, &g); + r = groupdb_by_name(p.name, /* match= */ NULL, userdb_flags, &g); else { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL; - r = groupdb_all(userdb_flags, &iterator); + r = groupdb_all(/* match= */ NULL, userdb_flags, &iterator); if (IN_SET(r, -ESRCH, -ENOLINK)) return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); if (r < 0) @@ -312,7 +312,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet for (;;) { _cleanup_(group_record_unrefp) GroupRecord *z = NULL; - r = groupdb_iterator_get(iterator, &z); + r = groupdb_iterator_get(iterator, /* match= */ NULL, &z); if (r == -ESRCH) break; if (r < 0)