diff options
author | Lennart Poettering <lennart@poettering.net> | 2024-10-24 22:36:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-24 22:36:22 +0200 |
commit | 210fb8626fe205c417403523596ebbc3c3a16da4 (patch) | |
tree | 0056fe9baa0a95e4f2a534f5cac17c7df2dde94e /src/shared | |
parent | user-util: tighten shell validation a tiny bit (diff) | |
parent | ci: give new userdbctl some CI exposure (diff) | |
download | systemd-210fb8626fe205c417403523596ebbc3c3a16da4.tar.xz systemd-210fb8626fe205c417403523596ebbc3c3a16da4.zip |
Merge pull request #34875 from poettering/userdbctl-filter
userdbctl: add some basic client-side filtering
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/group-record.c | 25 | ||||
-rw-r--r-- | src/shared/group-record.h | 2 | ||||
-rw-r--r-- | src/shared/user-record-nss.c | 48 | ||||
-rw-r--r-- | src/shared/user-record.c | 66 | ||||
-rw-r--r-- | src/shared/user-record.h | 18 |
5 files changed, 135 insertions, 24 deletions
diff --git a/src/shared/group-record.c b/src/shared/group-record.c index a297272fab..7b401bf064 100644 --- a/src/shared/group-record.c +++ b/src/shared/group-record.c @@ -326,3 +326,28 @@ int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord ** *ret = TAKE_PTR(c); return 0; } + +int group_record_match(GroupRecord *h, const UserDBMatch *match) { + assert(h); + assert(match); + + if (h->gid < match->gid_min || h->gid > match->gid_max) + return false; + + if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << group_record_disposition(h))) + return false; + + if (!strv_isempty(match->fuzzy_names)) { + const char* names[] = { + h->group_name, + group_record_group_name_and_realm(h), + h->description, + }; + + if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names)) + return false; + } + + return true; + +} diff --git a/src/shared/group-record.h b/src/shared/group-record.h index 054849b409..a2cef81c8a 100644 --- a/src/shared/group-record.h +++ b/src/shared/group-record.h @@ -43,5 +43,7 @@ int group_record_load(GroupRecord *h, sd_json_variant *v, UserRecordLoadFlags fl int group_record_build(GroupRecord **ret, ...); int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret); +int group_record_match(GroupRecord *h, const UserDBMatch *match); + const char* group_record_group_name_and_realm(GroupRecord *h); UserDisposition group_record_disposition(GroupRecord *h); diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c index a37957ee96..9223a2e6ca 100644 --- a/src/shared/user-record-nss.c +++ b/src/shared/user-record-nss.c @@ -104,37 +104,37 @@ int nss_passwd_to_user_record( * just a password instead of the whole account, but that's mostly pointless in times of * password-less authorization, hence let's not bother. */ - SET_IF(hr->locked, - spwd && spwd->sp_expire >= 0, - spwd->sp_expire <= 1, -1); + SET_IF(hr->locked, + spwd && spwd->sp_expire >= 0, + spwd->sp_expire <= 1, -1); - SET_IF(hr->not_after_usec, - spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_expire * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->not_after_usec, + spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_expire * USEC_PER_DAY, UINT64_MAX); - SET_IF(hr->password_change_now, - spwd && spwd->sp_lstchg >= 0, - spwd->sp_lstchg == 0, -1); + SET_IF(hr->password_change_now, + spwd && spwd->sp_lstchg >= 0, + spwd->sp_lstchg == 0, -1); - SET_IF(hr->last_password_change_usec, - spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->last_password_change_usec, + spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX); - SET_IF(hr->password_change_min_usec, - spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_min * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->password_change_min_usec, + spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_min * USEC_PER_DAY, UINT64_MAX); - SET_IF(hr->password_change_max_usec, - spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_max * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->password_change_max_usec, + spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_max * USEC_PER_DAY, UINT64_MAX); - SET_IF(hr->password_change_warn_usec, - spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_warn * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->password_change_warn_usec, + spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_warn * USEC_PER_DAY, UINT64_MAX); - SET_IF(hr->password_change_inactive_usec, - spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY, - spwd->sp_inact * USEC_PER_DAY, UINT64_MAX); + SET_IF(hr->password_change_inactive_usec, + spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY, + spwd->sp_inact * USEC_PER_DAY, UINT64_MAX); hr->json = sd_json_variant_unref(hr->json); r = sd_json_buildo( diff --git a/src/shared/user-record.c b/src/shared/user-record.c index f14a38e03b..12447a9337 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -2401,6 +2401,72 @@ int suitable_blob_filename(const char *name) { name[0] != '.'; } +bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) { + assert(names || n_names == 0); + + /* Checks if any of the user record strings in the names[] array matches any of the search strings in + * the matches** strv fuzzily. */ + + FOREACH_ARRAY(n, names, n_names) { + if (!*n) + continue; + + _cleanup_free_ char *lcn = strdup(*n); + if (!lcn) + return -ENOMEM; + + ascii_strlower(lcn); + + STRV_FOREACH(i, matches) { + _cleanup_free_ char *lc = strdup(*i); + if (!lc) + return -ENOMEM; + + ascii_strlower(lc); + + /* First do substring check */ + if (strstr(lcn, lc)) + return true; + + /* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */ + if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3) + return true; + } + } + + return false; +} + +int user_record_match(UserRecord *u, const UserDBMatch *match) { + assert(u); + assert(match); + + if (u->uid < match->uid_min || u->uid > match->uid_max) + return false; + + if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << user_record_disposition(u))) + return false; + + if (!strv_isempty(match->fuzzy_names)) { + + /* Note this array of names is sparse, i.e. various entries listed in it will be + * NULL. Because of that we are not using a NULL terminated strv here, but a regular + * array. */ + const char* names[] = { + u->user_name, + user_record_user_name_and_realm(u), + u->real_name, + u->email_address, + u->cifs_user_name, + }; + + if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names)) + return false; + } + + return true; +} + static const char* const user_storage_table[_USER_STORAGE_MAX] = { [USER_CLASSIC] = "classic", [USER_LUKS] = "luks", diff --git a/src/shared/user-record.h b/src/shared/user-record.h index 2a0e92d69a..0443820890 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -462,6 +462,24 @@ int user_group_record_mangle(sd_json_variant *v, UserRecordLoadFlags load_flags, #define BLOB_DIR_MAX_SIZE (UINT64_C(64) * U64_MB) int suitable_blob_filename(const char *name); +typedef struct UserDBMatch { + char **fuzzy_names; + uint64_t disposition_mask; + union { + uid_t uid_min; + gid_t gid_min; + }; + union { + uid_t uid_max; + gid_t gid_max; + }; +} UserDBMatch; + +#define USER_DISPOSITION_MASK_MAX ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1)) + +bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches); +int user_record_match(UserRecord *u, const UserDBMatch *match); + const char* user_storage_to_string(UserStorage t) _const_; UserStorage user_storage_from_string(const char *s) _pure_; |