diff options
Diffstat (limited to 'src/lib-dict-backend/dict-ldap-settings.c')
-rw-r--r-- | src/lib-dict-backend/dict-ldap-settings.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/lib-dict-backend/dict-ldap-settings.c b/src/lib-dict-backend/dict-ldap-settings.c new file mode 100644 index 0000000..01205eb --- /dev/null +++ b/src/lib-dict-backend/dict-ldap-settings.c @@ -0,0 +1,313 @@ +/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD) + +#include "array.h" +#include "str.h" +#include "settings.h" +#include "dict-ldap-settings.h" + +#include <ctype.h> + +static const char *dict_ldap_commonName = "cn"; +static const char *dict_ldap_empty_filter = ""; + +enum section_type { + SECTION_ROOT = 0, + SECTION_MAP, + SECTION_FIELDS +}; + +struct dict_ldap_map_attribute { + const char *name; + const char *variable; +}; + +struct setting_parser_ctx { + pool_t pool; + struct dict_ldap_settings *set; + enum section_type type; + + struct dict_ldap_map cur_map; + ARRAY(struct dict_ldap_map_attribute) cur_attributes; +}; + +#undef DEF_STR +#undef DEF_BOOL +#undef DEF_UINT + +#define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map) +#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map) +#define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map) + +static const struct setting_def dict_ldap_map_setting_defs[] = { + DEF_STR(pattern), + DEF_STR(filter), + DEF_STR(filter_iter), + DEF_STR(username_attribute), + DEF_STR(value_attribute), + DEF_STR(base_dn), + DEF_STR(scope), + { 0, NULL, 0 } +}; + +static const char *pattern_read_name(const char **pattern) +{ + const char *p = *pattern, *name; + + if (*p == '{') { + /* ${name} */ + name = ++p; + p = strchr(p, '}'); + if (p == NULL) { + /* error, but allow anyway */ + *pattern += strlen(*pattern); + return ""; + } + *pattern = p + 1; + } else { + /* $name - ends at the first non-alnum_ character */ + name = p; + for (; *p != '\0'; p++) { + if (!i_isalnum(*p) && *p != '_') + break; + } + *pattern = p; + } + name = t_strdup_until(name, p); + return name; +} + +static const char *dict_ldap_attributes_map(struct setting_parser_ctx *ctx) +{ + struct dict_ldap_map_attribute *attributes; + string_t *pattern; + const char *p, *name; + unsigned int i, count; + + /* go through the variables in the pattern, replace them with plain + '$' character and add its ldap attribute */ + pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1); + attributes = array_get_modifiable(&ctx->cur_attributes, &count); + + p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, count); + for (p = ctx->cur_map.pattern; *p != '\0';) { + if (*p != '$') { + str_append_c(pattern, *p); + p++; + continue; + } + p++; + str_append_c(pattern, '$'); + + name = pattern_read_name(&p); + for (i = 0; i < count; i++) { + if (attributes[i].variable != NULL && + strcmp(attributes[i].variable, name) == 0) + break; + } + if (i == count) { + return t_strconcat("Missing LDAP attribute for variable: ", + name, NULL); + } + + /* mark this attribute as used */ + attributes[i].variable = NULL; + array_push_back(&ctx->cur_map.ldap_attributes, + &attributes[i].name); + } + + /* make sure there aren't any unused attributes */ + for (i = 0; i < count; i++) { + if (attributes[i].variable != NULL) { + return t_strconcat("Unused variable: ", + attributes[i].variable, NULL); + } + } + + if (ctx->set->max_attribute_count < count) + ctx->set->max_attribute_count = count; + ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern)); + return NULL; +} + +static const char *dict_ldap_map_finish(struct setting_parser_ctx *ctx) +{ + if (ctx->cur_map.pattern == NULL) + return "Missing setting: pattern"; + if (ctx->cur_map.filter == NULL) + ctx->cur_map.filter = dict_ldap_empty_filter; + if (*ctx->cur_map.filter != '\0') { + const char *ptr = ctx->cur_map.filter; + if (*ptr != '(') + return "Filter must start with ("; + while(*ptr != '\0') ptr++; + ptr--; + if (*ptr != ')') + return "Filter must end with )"; + } + if (ctx->cur_map.value_attribute == NULL) + return "Missing setting: value_attribute"; + + if (ctx->cur_map.username_attribute == NULL) { + /* default to commonName */ + ctx->cur_map.username_attribute = dict_ldap_commonName; + } + if (ctx->cur_map.scope == NULL) { + ctx->cur_map.scope_val = 2; /* subtree */ + } else { + if (strcasecmp(ctx->cur_map.scope, "one") == 0) ctx->cur_map.scope_val = 1; + else if (strcasecmp(ctx->cur_map.scope, "base") == 0) ctx->cur_map.scope_val = 0; + else if (strcasecmp(ctx->cur_map.scope, "subtree") == 0) ctx->cur_map.scope_val = 2; + else return "Scope must be one, base or subtree"; + } + if (!array_is_created(&ctx->cur_map.ldap_attributes)) { + /* no attributes besides value. allocate the array anyway. */ + p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, 1); + if (strchr(ctx->cur_map.pattern, '$') != NULL) + return "Missing attributes for pattern variables"; + } + array_push_back(&ctx->set->maps, &ctx->cur_map); + i_zero(&ctx->cur_map); + return NULL; +} + +static const char * +parse_setting(const char *key, const char *value, + struct setting_parser_ctx *ctx) +{ + struct dict_ldap_map_attribute *attribute; + + switch (ctx->type) { + case SECTION_ROOT: + if (strcmp(key, "uri") == 0) { + ctx->set->uri = p_strdup(ctx->pool, value); + return NULL; + } + if (strcmp(key, "bind_dn") == 0) { + ctx->set->bind_dn = p_strdup(ctx->pool, value); + return NULL; + } + if (strcmp(key, "password") == 0) { + ctx->set->password = p_strdup(ctx->pool, value); + return NULL; + } + if (strcmp(key, "timeout") == 0) { + if (str_to_uint(value, &ctx->set->timeout) != 0) { + return "Invalid timeout value"; + } + return NULL; + } + if (strcmp(key, "max_idle_time") == 0) { + if (str_to_uint(value, &ctx->set->max_idle_time) != 0) { + return "Invalid max_idle_time value"; + } + return NULL; + } + if (strcmp(key, "debug") == 0) { + if (str_to_uint(value, &ctx->set->debug) != 0) { + return "invalid debug value"; + } + return NULL; + } + if (strcmp(key, "tls") == 0) { + if (strcasecmp(value, "yes") == 0) { + ctx->set->require_ssl = TRUE; + ctx->set->start_tls = TRUE; + } else if (strcasecmp(value, "no") == 0) { + ctx->set->require_ssl = FALSE; + ctx->set->start_tls = FALSE; + } else if (strcasecmp(value, "try") == 0) { + ctx->set->require_ssl = FALSE; + ctx->set->start_tls = TRUE; + } else { + return "tls must be yes, try or no"; + } + return NULL; + } + break; + case SECTION_MAP: + return parse_setting_from_defs(ctx->pool, + dict_ldap_map_setting_defs, + &ctx->cur_map, key, value); + case SECTION_FIELDS: + if (*value != '$') { + return t_strconcat("Value is missing '$' for attribute: ", + key, NULL); + } + attribute = array_append_space(&ctx->cur_attributes); + attribute->name = p_strdup(ctx->pool, key); + attribute->variable = p_strdup(ctx->pool, value + 1); + return NULL; + } + return t_strconcat("Unknown setting: ", key, NULL); +} + +static bool +parse_section(const char *type, const char *name ATTR_UNUSED, + struct setting_parser_ctx *ctx, const char **error_r) +{ + switch (ctx->type) { + case SECTION_ROOT: + if (type == NULL) + return FALSE; + if (strcmp(type, "map") == 0) { + array_clear(&ctx->cur_attributes); + ctx->type = SECTION_MAP; + return TRUE; + } + break; + case SECTION_MAP: + if (type == NULL) { + ctx->type = SECTION_ROOT; + *error_r = dict_ldap_map_finish(ctx); + return FALSE; + } + if (strcmp(type, "fields") == 0) { + ctx->type = SECTION_FIELDS; + return TRUE; + } + break; + case SECTION_FIELDS: + if (type == NULL) { + ctx->type = SECTION_MAP; + *error_r = dict_ldap_attributes_map(ctx); + return FALSE; + } + break; + } + *error_r = t_strconcat("Unknown section: ", type, NULL); + return FALSE; +} + +struct dict_ldap_settings * +dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r) +{ + struct setting_parser_ctx ctx; + + i_zero(&ctx); + ctx.pool = pool; + ctx.set = p_new(pool, struct dict_ldap_settings, 1); + t_array_init(&ctx.cur_attributes, 16); + p_array_init(&ctx.set->maps, pool, 8); + + ctx.set->timeout = 30; /* default timeout */ + ctx.set->require_ssl = FALSE; /* try to start SSL */ + ctx.set->start_tls = TRUE; + + if (!settings_read(path, NULL, parse_setting, parse_section, + &ctx, error_r)) + return NULL; + + if (ctx.set->uri == NULL) { + *error_r = t_strdup_printf("Error in configuration file %s: " + "Missing ldap uri", path); + return NULL; + } + + return ctx.set; +} + +#endif |