From: Marco Bettini Date: Thu, 11 May 2023 09:24:37 +0000 (+0000) Subject: auth: db_ldap_set_attrs() - Handle braces nesting while splitting on commas X-Git-Tag: 2.4.0~2719 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=308227d2c1a77d06092ce7ab30c14480e445ca4f;p=thirdparty%2Fdovecot%2Fcore.git auth: db_ldap_set_attrs() - Handle braces nesting while splitting on commas --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index c2ee34d003..80a9c61ec5 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -228,6 +228,7 @@ test_auth_SOURCES = \ test-auth-request-fields.c \ test-username-filter.c \ test-db-dict.c \ + test-db-ldap.c \ test-lua.c \ test-mock.c \ test-main.c diff --git a/src/auth/db-ldap.c b/src/auth/db-ldap.c index 2050c4df28..d636673e9c 100644 --- a/src/auth/db-ldap.c +++ b/src/auth/db-ldap.c @@ -1381,6 +1381,48 @@ db_ldap_field_find(const char *data, void *context, return 1; } +const char *const *db_ldap_parse_attrs(const char *cstr) +{ + ARRAY_TYPE(const_string) entries; + t_array_init(&entries, 32); + + char *ptr = t_strdup_noconst(cstr); + const char *start = ptr; + unsigned int nesting = 0; + while (*ptr != '\0') { + switch (*ptr) { + case '{': + nesting++; + ptr++; + break; + case '}': + if (nesting > 0) + nesting--; + ptr++; + break; + case ',': + if (nesting > 0) + ptr++; + else { + *ptr = '\0'; + if (*start != '\0') + array_push_back(&entries, &start); + start = ++ptr; + } + break; + default: + ptr++; + break; + } + } + if (*start != '\0') + array_push_back(&entries, &start); + + unsigned int count ATTR_UNUSED; + array_append_zero(&entries); + return array_get(&entries, &count); +} + void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char ***attr_names_r, ARRAY_TYPE(ldap_field) *attr_map, const char *skip_attr) @@ -1397,11 +1439,10 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char *ldap_attr, *name, *templ; unsigned int i; - if (*attrlist == '\0') + attr = db_ldap_parse_attrs(attrlist); + if (*attr == NULL) return; - attr = t_strsplit_spaces(attrlist, ","); - tmp_str = t_str_new(128); ctx.pool = conn->pool; p_array_init(&ctx.attr_names, conn->pool, 16); diff --git a/src/auth/db-ldap.h b/src/auth/db-ldap.h index 82dc31b6dc..1ccc47e76a 100644 --- a/src/auth/db-ldap.h +++ b/src/auth/db-ldap.h @@ -219,4 +219,8 @@ bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx, const char *const **values_r); void db_ldap_result_iterate_deinit(struct db_ldap_result_iterate_context **ctx); +/* exposed only for unit tests */ + +const char *const *db_ldap_parse_attrs(const char *cstr); + #endif diff --git a/src/auth/test-auth.h b/src/auth/test-auth.h index 4641fe3561..195ff9c279 100644 --- a/src/auth/test-auth.h +++ b/src/auth/test-auth.h @@ -21,5 +21,7 @@ struct auth_passdb *passdb_mock(void); void passdb_mock_mod_init(void); void passdb_mock_mod_deinit(void); +void test_db_ldap_parse_attrs(void); + #endif diff --git a/src/auth/test-db-ldap.c b/src/auth/test-db-ldap.c new file mode 100644 index 0000000000..35bc416847 --- /dev/null +++ b/src/auth/test-db-ldap.c @@ -0,0 +1,60 @@ +/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "test-auth.h" +#include "db-ldap.h" +#include + +void test_db_ldap_parse_attrs(void) +{ + struct vectors { + const char *inp; + const char *out; + } vectors[] = { + { .inp = "", .out = ""}, + { .inp = "a", .out = "a"}, + + /* tests with leading/trailing/no spaces*/ + { .inp = "a,b,c", .out = "a|b|c"}, + { .inp = "a, b, c", .out = "a| b| c"}, + { .inp = "a ,b ,c", .out = "a |b |c"}, + + /* leading empty field */ + { .inp = ",a,b", .out = "a|b"}, + /* trailing empty field */ + { .inp = "a,b,", .out = "a|b"}, + /* middle empty field */ + { .inp = "a,,b", .out = "a|b"}, + + + /* simple nesting at begining/end of field */ + { .inp = "a,{b,c},d", .out = "a|{b,c}|d"}, + + /* simple nesting in the middle of the field */ + { .inp = "a,b{c,d}e,f", .out = "a|b{c,d}e|f"}, + + /* multiple nesting, balanced, prefixed and suffixed */ + { .inp = "a, {{b, c}, d}, e", .out = "a| {{b, c}, d}| e"}, + { .inp = "a, {b, {c, d}}, e", .out = "a| {b, {c, d}}| e"}, + + /* unbalanced nesting, excess of {s */ + { .inp = "{", .out = "{"}, + { .inp = "a, {b, {c, d}, e", .out = "a| {b, {c, d}, e"}, + + /* unbalanced nesting, excess of }s */ + { .inp = "}", .out = "}"}, + { .inp = "a, {b, {c, d}}}, e", .out = "a| {b, {c, d}}}| e"}, + + {} + }; + + test_begin("db ldap parse attrs"); + unsigned int index = 0; + for (struct vectors *vector = vectors; vector->inp != NULL; vector++, index++) { + const char *const *array = db_ldap_parse_attrs(vector->inp); + const char *out = t_strarray_join(array, "|"); + test_assert_strcmp_idx(vector->out, out, index); + } + test_end(); +} diff --git a/src/auth/test-main.c b/src/auth/test-main.c index 762c34bc35..f149eaa56a 100644 --- a/src/auth/test-main.c +++ b/src/auth/test-main.c @@ -12,6 +12,7 @@ int main(int argc, const char *argv[]) const char *match = ""; int ret; static const struct named_test test_functions[] = { + TEST_NAMED(test_db_ldap_parse_attrs) TEST_NAMED(test_auth_request_var_expand) TEST_NAMED(test_auth_request_fields) TEST_NAMED(test_db_dict_parse_cache_key)