From 85bbc387c3f7bcfd094b78e2fc2779e27aca348f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Tue, 20 Nov 2018 11:56:09 +0100 Subject: [PATCH 10/15] lib: refuse to activate profile if unsupported feature was selected Resolves: https://github.com/pbrezina/authselect/issues/107 --- include/authselect.h | 1 + src/lib/authselect.c | 44 +++++++++++++++++++++++++++++++++++++ src/lib/util/string.c | 40 +++++++++++++++++++++++++++++++++ src/lib/util/string.h | 6 +++++ src/lib/util/string_array.c | 30 +++++++++++++++++++++++++ src/lib/util/string_array.h | 15 +++++++++++++ src/tests/Makefile.am | 1 + 7 files changed, 137 insertions(+) diff --git a/include/authselect.h b/include/authselect.h index 462f54291e8283d5778005ac1747dd03258584be..110f22a864157f898541be573e800ed7d6c38e0a 100644 --- a/include/authselect.h +++ b/include/authselect.h @@ -70,6 +70,7 @@ enum authselect_profile_type { * authselect and @force_overwrite must be set to true to force * overwrite the existing files. * - ENOENT if the profile was not found. + * - EINVAL if unsupported feature was provided. * - Other errno code on generic error. */ int diff --git a/src/lib/authselect.c b/src/lib/authselect.c index 3148f63b60534480b05b1ddb5587e4ed09aaa84a..1b4337e2e0eae4f298e59f90cc9c8659891e23f3 100644 --- a/src/lib/authselect.c +++ b/src/lib/authselect.c @@ -29,6 +29,45 @@ #include "lib/files/files.h" #include "lib/profiles/profiles.h" +static bool +authselect_check_features(const struct authselect_profile *profile, + const char **features) +{ + const char *similar; + bool result = true; + char **supported; + int i; + + if (features == NULL) { + return true; + } + + supported = authselect_profile_features(profile); + if (supported == NULL) { + ERROR("Unable to obtain supported features"); + return false; + } + + for (i = 0; features[i] != NULL; i++) { + if (string_array_has_value(supported, features[i])) { + continue; + } + + result = false; + similar = string_array_find_similar(features[i], supported, 5); + if (similar != NULL) { + ERROR("Unknown profile feature [%s], did you mean [%s]?", + features[i], similar); + } else { + ERROR("Unknown profile feature [%s]", features[i]); + } + } + + string_array_free(supported); + + return result; +} + _PUBLIC_ void authselect_set_debug_fn(authselect_debug_fn fn, void *pvt) { @@ -53,6 +92,11 @@ authselect_activate(const char *profile_id, return ret; } + if (!authselect_check_features(profile, features)) { + ret = EINVAL; + goto done; + } + if (force_overwrite) { INFO("Enforcing activation!"); ret = authselect_profile_activate(profile, features); diff --git a/src/lib/util/string.c b/src/lib/util/string.c index 73ae778663bd8db024620a58987833ede60307d1..e6803bcd4e41adc05eaa623089009b17ccd4f49b 100644 --- a/src/lib/util/string.c +++ b/src/lib/util/string.c @@ -329,3 +329,43 @@ string_replace_shake(char *str, size_t original_length) str[pos] = '\0'; } } + +static int +min3(unsigned int a, unsigned int b, unsigned int c) +{ + if (a < b && a < c) { + return a; + } else if (b < a && b < c) { + return b; + } + + return c; +} + +int +string_levenshtein(const char *a, const char *b) +{ + unsigned int len_a = strlen(a); + unsigned int len_b = strlen(b); + unsigned int x; + unsigned int y; + unsigned int last_diag; + unsigned int old_diag; + unsigned int column[len_a + 1]; + + for (y = 1; y <= len_a; y++) { + column[y] = y; + } + + for (x = 1; x <= len_b; x++) { + column[0] = x; + for (y = 1, last_diag = x - 1; y <= len_a; y++) { + old_diag = column[y]; + column[y] = min3(column[y] + 1, column[y - 1] + 1, + last_diag + (a[y - 1] == b[x - 1] ? 0 : 1)); + last_diag = old_diag; + } + } + + return column[len_a]; +} diff --git a/src/lib/util/string.h b/src/lib/util/string.h index d7ca04491e5ceff20bd1d6e136ea151166156546..e550d853d3fa0699909b84cc9febdae9d5884b9f 100644 --- a/src/lib/util/string.h +++ b/src/lib/util/string.h @@ -179,4 +179,10 @@ string_remove_remainder(char *str, size_t from); void string_replace_shake(char *str, size_t original_length); +/** + * Compute Levenshtein distance of two strings. + */ +int +string_levenshtein(const char *a, const char *b); + #endif /* _STRING_H_ */ diff --git a/src/lib/util/string_array.c b/src/lib/util/string_array.c index 43e30d0008b5709f97da9c43f8f2c28ef2475df5..e56d66bdcce7c8a1cf99f9b91068614c4b8d3d81 100644 --- a/src/lib/util/string_array.c +++ b/src/lib/util/string_array.c @@ -25,6 +25,7 @@ #include "common/common.h" #include "lib/util/string_array.h" +#include "lib/util/string.h" char ** string_array_create(size_t num_items) @@ -195,3 +196,32 @@ string_array_sort(char **array) qsort(array, string_array_count(array), sizeof(char *), string_array_sort_callback); } + +const char * +string_array_find_similar(const char *value, char **array, int max_distance) +{ + const char *word = NULL; + int current; + int best; + int i; + + for (i = 0; array[i] != NULL; i++) { + current = string_levenshtein(value, array[i]); + if (word == NULL) { + best = current; + word = array[i]; + continue; + } + + if (current < best) { + best = current; + word = array[i]; + } + } + + if (best > max_distance) { + return NULL; + } + + return word; +} diff --git a/src/lib/util/string_array.h b/src/lib/util/string_array.h index 06aa46c008058163e5557d51e18258fa4e9a1523..ba9760b5d66a9619ca8edea5e3418c5cfbbec929 100644 --- a/src/lib/util/string_array.h +++ b/src/lib/util/string_array.h @@ -138,4 +138,19 @@ string_array_concat(char **to, char **items, bool unique); void string_array_sort(char **array); +/** + * Find similar word inside a NULL-terminated array, based on Levenshtein + * distance algorithm. + * + * @param value Value to search in @array. + * @param array NULL-terminated string array. + * @param max_distance Maximum distance between two strings. If the real + * distance is greater then this value, those string + * are not considered as similar. + * + * @return Most similar word that was found or NULL if non was found. + */ +const char * +string_array_find_similar(const char *value, char **array, int max_distance); + #endif /* _STRING_ARRAY_H_ */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 1cfebeab6bf3b36e760372b05482fd9f322feab1..e1505e6b5787a669065e5ea3c7acf55ea110c4da 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -23,6 +23,7 @@ check_PROGRAMS = $(TESTS) test_util_string_array_SOURCES = \ test_util_string_array.c \ ../lib/util/string_array.c \ + ../lib/util/string.c \ $(NULL) test_util_string_array_CFLAGS = \ $(AM_CFLAGS) -- 2.17.2