/* SSSD Library for rule based certificate to user mapping Authors: Sumit Bose Copyright (C) 2017 Red Hat This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "util/util.h" #include "util/cert.h" #include "util/crypto/sss_crypto.h" #include "lib/certmap/sss_certmap.h" #include "lib/certmap/sss_certmap_int.h" #include "util/crypto/nss/nss_util.h" int debug_level; void sss_debug_fn(const char *file, long line, const char *function, int level, const char *format, ...) { return; } //#define ERRMSG(text) text "("__FILE__":"__LINE__" "__FUNCTION__")" #define ERRMSG(text) text int sss_certmap_init(TALLOC_CTX *mem_ctx, sss_certmap_ext_debug *debug, void *debug_priv, struct sss_certmap_ctx **ctx) { if (ctx == NULL) { return EINVAL; } *ctx = talloc_zero(mem_ctx, struct sss_certmap_ctx); if (*ctx == NULL) { return ENOMEM; } (*ctx)->debug = debug; (*ctx)->debug_priv = debug_priv; CM_DEBUG((*ctx), "sss_certmap initialized.\n"); return EOK; } void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx) { talloc_free(ctx); } void sss_certmap_free_filter_and_domains(char *filter, char **domains) { talloc_free(filter); talloc_free(domains); } const char *sss_certmap_err_msg(struct sss_certmap_ctx *ctx) { if (ctx->err_msg != NULL) { return ctx->err_msg; } return "(no error messages available)"; } static int get_type_prefix(TALLOC_CTX *mem_ctx, const char *match_rule, char **type, const char **rule_start) { const char *c; char *delim; *type = NULL; *rule_start = match_rule; delim = strchr(match_rule, ':'); if (delim == NULL) { /* no type prefix found */ return 0; } /* rule starts with ':', empty type */ if (delim == match_rule) { *rule_start = delim + 1; return EOK; } for (c = match_rule; c < delim; c++) { /* type prefix may only contain digits and upper-case ASCII characters */ if (!(isascii(*c) && (isdigit(*c) || isupper(*c)))) { /* no type prefix found */ return 0; } } *rule_start = delim + 1; *type = talloc_strndup(mem_ctx, match_rule, (delim - match_rule)); if (*type == NULL) { return ENOMEM; } return 0; } static int component_list_destructor(void *data) { struct component_list *comp = talloc_get_type(data, struct component_list); if (comp != NULL) { regfree(&(comp->regexp)); } return 0; } /* * The syntax of the MIT Kerberos style matching rules is: * [KRB5:][relation-operator]component-rule ... * * where: * * relation-operator * can be either &&, meaning all component rules must match, or ||, * meaning only one component rule must match. The default is &&. * * component-rule * can be one of the following. Note that there is no punctuation or whitespace between component rules. * regular-expression * regular-expression * regular-expression * extended-key-usage * key-usage * * TODO: * generic extension handling for e.g. MSFT 1.3.6.1.4.1.311.20.2 */ static int get_comp_value(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char **cur, struct component_list **_comp) { struct component_list *comp = NULL; const char *end; int ret; comp = talloc_zero(mem_ctx, struct component_list); if (comp == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } talloc_set_destructor((TALLOC_CTX *) comp, component_list_destructor); end = strchr(*cur, '<'); if (end == NULL) { comp->val = talloc_strdup(comp, *cur); } else { comp->val = talloc_strndup(comp, *cur, end - *cur); } if (comp->val == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } if (*(comp->val) == '\0') { ctx->err_msg = ERRMSG("Missing component value."); ret = EINVAL; goto done; } *cur += strlen(comp->val); *_comp = comp; ret = 0; done: if (ret != 0) { talloc_free(comp); } return ret; } static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char **cur, struct component_list **_comp) { struct component_list *comp = NULL; int ret; char **eku_list; size_t c; size_t k; const char *o; size_t e = 0; int eku_list_size; struct ext_key_usage { const char *name; const char *oid; } ext_key_usage[] = { /* RFC 3280 section 4.2.1.13 */ {"serverAuth", "1.3.6.1.5.5.7.3.1"}, {"clientAuth", "1.3.6.1.5.5.7.3.2"}, {"codeSigning", "1.3.6.1.5.5.7.3.3"}, {"emailProtection", "1.3.6.1.5.5.7.3.4"}, {"timeStamping", "1.3.6.1.5.5.7.3.8"}, {"OCSPSigning", "1.3.6.1.5.5.7.3.9"}, /* RFC 4556 section 3.2.2 */ {"KPClientAuth", "1.3.6.1.5.2.3.4"}, {"pkinit", "1.3.6.1.5.2.3.4"}, /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography*/ {"msScLogin", "1.3.6.1.4.1.311.20.2.2"}, {NULL ,0} }; ret = get_comp_value(mem_ctx, ctx, cur, &comp); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to parse regexp."); goto done; } ret = split_on_separator(mem_ctx, comp->val, ',', true, true, &eku_list, &eku_list_size); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to split list."); CM_DEBUG(ctx, "Failed to split list."); goto done; } for (c = 0; eku_list[c] != NULL; c++) { for (k = 0; ext_key_usage[k].name != NULL; k++) { CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name); if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) { if (comp->eku_oid_list == NULL) { comp->eku_oid_list = talloc_zero_array(comp, const char *, eku_list_size + 1); if (comp->eku_oid_list == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } } comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, ext_key_usage[k].oid); if (comp->eku_oid_list[e] == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } e++; break; } } if (ext_key_usage[k].name == NULL) { /* check for an dotted-decimal OID */ if (*(eku_list[c]) != '.') { o = eku_list[c]; while (*o != '\0') { if (*o != '.' && !isdigit(*o)) { break; } o++; } if (*o == '\0' && *(o - 1) != '.') { /* looks like a OID, only '.' and digits */ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, eku_list[c]); if (comp->eku_oid_list[e] == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } e++; continue; } } CM_DEBUG(ctx, "No matching extended key usage found."); ret = EINVAL; goto done; } } ret = 0; done: if (ret == 0) { *_comp = comp; } else { talloc_free(comp); } return ret; } static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char **cur, struct component_list **_comp) { struct component_list *comp = NULL; int ret; char **ku_list; size_t c; size_t k; struct key_usage { const char *name; uint32_t flag; } key_usage[] = { {"digitalSignature" , SSS_KU_DIGITAL_SIGNATURE}, {"nonRepudiation" , SSS_KU_NON_REPUDIATION}, {"keyEncipherment" , SSS_KU_KEY_ENCIPHERMENT}, {"dataEncipherment" , SSS_KU_DATA_ENCIPHERMENT}, {"keyAgreement" , SSS_KU_KEY_AGREEMENT}, {"keyCertSign" , SSS_KU_KEY_CERT_SIGN}, {"cRLSign" , SSS_KU_CRL_SIGN}, {"encipherOnly" , SSS_KU_ENCIPHER_ONLY}, {"decipherOnly" , SSS_KU_DECIPHER_ONLY}, {NULL ,0} }; ret = get_comp_value(mem_ctx, ctx, cur, &comp); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to get value."); CM_DEBUG(ctx, "Failed to get value."); goto done; } ret = split_on_separator(mem_ctx, comp->val, ',', true, true, &ku_list, NULL); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to split list."); CM_DEBUG(ctx, "Failed to split list."); goto done; } for (c = 0; ku_list[c] != NULL; c++) { for (k = 0; key_usage[k].name != NULL; k++) { if (strcasecmp(ku_list[c], key_usage[k].name) == 0) { comp->ku |= key_usage[k].flag; break; } } if (key_usage[k].name == NULL) { /* FIXME: add check for numerical ku */ CM_DEBUG(ctx, "No matching key usage found."); ret = EINVAL; goto done; } } ret = 0; done: if (ret == 0) { *_comp = comp; } else { talloc_free(comp); } return ret; } static int parse_krb5_get_component_value(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char **cur, struct component_list **_comp) { struct component_list *comp = NULL; int ret; ret = get_comp_value(mem_ctx, ctx, cur, &comp); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to parse regexp."); goto done; } ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to parse regexp."); goto done; } ret = 0; done: if (ret == 0) { *_comp = comp; } else { talloc_free(comp); } return ret; } static int parse_krb5_match_rule(struct sss_certmap_ctx *ctx, const char *rule_start, struct krb5_match_rule **match_rule) { const char *cur; struct krb5_match_rule *rule; struct component_list *comp; int ret; rule = talloc_zero(ctx, struct krb5_match_rule); if (rule == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } cur = rule_start; /* check relation */ if (strncmp(cur, "&&", 2) == 0) { rule->r = relation_and; cur += 2; } else if (strncmp(cur, "||", 2) == 0) { rule->r = relation_or; cur += 2; } else { rule->r = relation_and; } while (*cur != '\0') { /* new component must start with '<' */ if (*cur != '<') { ctx->err_msg = ERRMSG("Invalid KRB5 matching rule."); ret = EINVAL; goto done; } cur++; if (strncmp(cur, "ISSUER>", 7) == 0) { cur += 7; ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp); if (ret != 0) { goto done; } DLIST_ADD(rule->issuer, comp); } else if (strncmp(cur, "SUBJECT>", 8) == 0) { cur += 8; ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp); if (ret != 0) { goto done; } DLIST_ADD(rule->subject, comp); } else if (strncmp(cur, "KU>", 3) == 0) { cur += 3; ret = parse_krb5_get_ku_value(rule, ctx, &cur, &comp); if (ret != 0) { goto done; } DLIST_ADD(rule->ku, comp); } else if (strncmp(cur, "EKU>", 4) == 0) { cur += 4; ret = parse_krb5_get_eku_value(rule, ctx, &cur, &comp); if (ret != 0) { goto done; } DLIST_ADD(rule->eku, comp); } else { ctx->err_msg = ERRMSG("Invalid KRB5 matching rule."); ret = EINVAL; goto done; } } ret = 0; done: if (ret == 0) { *match_rule = rule; } else { talloc_free(rule); } return ret; } static int parse_match_rule(struct sss_certmap_ctx *ctx, const char *match_rule, struct krb5_match_rule **parsed_match_rule) { int ret; char *type; const char *rule_start; ret = get_type_prefix(ctx, match_rule, &type, &rule_start); if (ret != EOK) { ctx->err_msg = ERRMSG("Failed to read rule type."); goto done; } if (type == NULL || strcmp(type, "KRB5") == 0) { ret = parse_krb5_match_rule(ctx, rule_start, parsed_match_rule); if (ret != EOK) { ctx->err_msg = ERRMSG("Failed to parse KRB5 matching rule.\n"); goto done; } } else { ctx->err_msg = ERRMSG("Unsupported matching rule type.\n"); ret = ESRCH; goto done; } ret = EOK; done: talloc_free(type); return ret; } struct template_table { const char *name; const char **attr_name; const char **conversion; }; const char *empty[] = {NULL}; const char *name_attr[] = {"name", NULL}; const char *x500_conv[] = {"x500", "x500ad", NULL}; const char *bin_conv[] = {"bin", "base64", NULL}; struct template_table template_table[] = { {"issuer_dn", empty, x500_conv}, {"subject_dn", empty, x500_conv}, {"subject_nt_principal", name_attr, empty}, {"cert", empty, bin_conv}, {NULL, NULL, NULL}}; static int check_parsed_template(struct sss_certmap_ctx *ctx, struct parsed_template *parsed) { size_t n; size_t a; size_t c; bool attr_name_valid = false; bool conversion_valid = false; for (n = 0; template_table[n].name != NULL; n++) { if (strcmp(template_table[n].name, parsed->name) != 0) { continue; } if (parsed->attr_name != NULL) { for (a = 0; template_table[n].attr_name[a] != NULL; a++) { if (strcmp(template_table[n].attr_name[a], parsed->attr_name) == 0) { attr_name_valid = true; break; } } } else { attr_name_valid = true; } if (parsed->conversion != NULL) { for (c = 0; template_table[n].conversion[c] != NULL; c++) { if (strcmp(template_table[n].conversion[c], parsed->conversion) == 0) { conversion_valid = true; break; } } } else { conversion_valid = true; } if (attr_name_valid && conversion_valid) { return 0; } } return EINVAL; } static int parse_template(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char *template, struct parsed_template **parsed_template) { int ret; struct parsed_template *parsed = NULL; const char *dot; const char *excl; const char *p; parsed = talloc_zero(mem_ctx, struct parsed_template); if (parsed == NULL) { //ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } dot = strchr(template, '.'); if (dot != NULL) { p = strchr(dot + 1, '.'); if (p != NULL) { //ctx->err_msg = ERRMSG("Only one '.' allowed in template."); CM_DEBUG(ctx, "Only one '.' allowed in template."); ret = EINVAL; goto done; } if (dot == template) { //ctx->err_msg = ERRMSG("Missing name in template."); CM_DEBUG(ctx, "Missing name in template."); ret = EINVAL; goto done; } } excl = strchr(template, '!'); if (excl != NULL) { p = strchr(excl + 1, '!'); if (p != NULL) { //ctx->err_msg = ERRMSG("Only one '!' allowed in template."); CM_DEBUG(ctx, "Only one '!' allowed in template."); ret = EINVAL; goto done; } if (excl == template) { //ctx->err_msg = ERRMSG("Missing name in template."); CM_DEBUG(ctx, "Missing name in template."); ret = EINVAL; goto done; } } if (excl != NULL && excl[1] != '\0') { parsed->conversion = talloc_strdup(parsed, excl + 1); if (parsed->conversion == NULL) { //ctx->err_msg = ERRMSG("Memory allocation failed."); CM_DEBUG(ctx, "Memory allocation failed."); ret = ENOMEM; goto done; } } if (dot != NULL && dot[1] != '\0' && dot[1] != '!') { if (excl == NULL) { parsed->attr_name = talloc_strdup(parsed, dot + 1); } else { parsed->attr_name = talloc_strndup(parsed, dot + 1, (excl - dot - 1)); } if (parsed->attr_name == NULL) { CM_DEBUG(ctx, "Memory allocation failed."); ret = ENOMEM; goto done; } } if (dot != NULL) { parsed->name = talloc_strndup(parsed, template, (dot - template)); } else if (excl != NULL) { parsed->name = talloc_strndup(parsed, template, (excl - template)); } else { parsed->name = talloc_strdup(parsed, template); } if (parsed->name == NULL) { //ctx->err_msg = ERRMSG("Memory allocation failed."); CM_DEBUG(ctx, "Memory allocation failed."); ret = ENOMEM; goto done; } ret = check_parsed_template(ctx, parsed); if (ret != 0) { CM_DEBUG(ctx, "Parse template invalid."); goto done; } ret = 0; done: if (ret == 0) { *parsed_template = parsed; } else { talloc_free(parsed); } return ret; } static int add_comp(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule, const char *string, enum comp_type type) { int ret; struct ldap_mapping_rule_comp *comp; comp = talloc_zero(rule, struct ldap_mapping_rule_comp); if (comp == NULL) { return ENOMEM; } comp->type = type; comp->val = talloc_strdup(comp, string); if (comp->val == NULL) { talloc_free(comp); return ENOMEM; } if (type == comp_template) { ret = parse_template(comp, ctx, string, &comp->parsed_template); if (ret != 0) { talloc_free(comp); return ret; } } DLIST_ADD_END(rule->list, comp, struct ldap_mapping_rule_comp *); return 0; } static int add_string(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule, const char *string) { return add_comp(ctx, rule, string, comp_string); } static int add_template(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule, const char *string) { return add_comp(ctx, rule, string, comp_template); } static int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx, const char *rule_start, struct ldap_mapping_rule **mapping_rule) { size_t c; const char *cur; char *tmp_string = NULL; size_t tmp_string_size; struct ldap_mapping_rule *rule = NULL; int ret; bool in_template = false; rule = talloc_zero(ctx, struct ldap_mapping_rule); if (rule == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } tmp_string_size = strlen(rule_start) + 1; tmp_string = talloc_zero_size(ctx, tmp_string_size); if (tmp_string == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } cur = rule_start; c = 0; while (*cur != '\0') { if (c > tmp_string_size) { ctx->err_msg = ERRMSG("Cannot parse mapping rule."); ret = EIO; goto done; } switch (*cur) { case '{': if (in_template) { ctx->err_msg = ERRMSG("'{' not allowed in templates."); ret = EINVAL; goto done; } if (cur[1] == '{') { /* Add only a single '{' to the output */ tmp_string[c] = '{'; c++; cur += 2; } else { if (c != 0) { ret = add_string(ctx, rule, tmp_string); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to add string."); ret = EINVAL; goto done; } memset(tmp_string, 0, tmp_string_size); c = 0; } cur++; in_template = true; } break; case '}': if (cur[1] == '}') { if (in_template) { ctx->err_msg = ERRMSG("'}}' not allowed in templates."); ret = EINVAL; goto done; } else { /* Add only a single '}' to the output */ tmp_string[c] = '}'; c++; cur += 2; } } else { ret = add_template(ctx, rule, tmp_string); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to add template."); ret = EINVAL; goto done; } memset(tmp_string, 0, tmp_string_size); c = 0; cur++; in_template = false; } break; default: tmp_string[c] = *cur; c++; cur++; } } if (in_template) { ctx->err_msg = ERRMSG("Rule ended inside template."); ret = EINVAL; goto done; } if (c != 0) { ret = add_string(ctx, rule, tmp_string); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to add string."); ret = EINVAL; goto done; } } ret = 0; done: if (ret == 0) { *mapping_rule = rule; } else { talloc_free(rule); } talloc_free(tmp_string); return ret; } static int parse_mapping_rule(struct sss_certmap_ctx *ctx, const char *mapping_rule, struct ldap_mapping_rule **parsed_mapping_rule) { int ret; char *type; const char *rule_start; ret = get_type_prefix(ctx, mapping_rule, &type, &rule_start); if (ret != EOK) { ctx->err_msg = ERRMSG("Failed to read rule type."); goto done; } if (type == NULL || strcmp(type, "LDAP") == 0) { ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule); if (ret != EOK) { ctx->err_msg = ERRMSG("Failed to parse LDAP mapping rule.\n"); goto done; } } else { ctx->err_msg = ERRMSG("Unsupported mapping rule type.\n"); ret = ESRCH; goto done; } ret = EOK; done: talloc_free(type); return ret; } int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, unsigned int priority, const char *match_rule, const char *map_rule, const char **domains) { size_t c; int ret; struct match_map_rule *rule; struct TALLOC_CTX *tmp_ctx; struct priority_list *p; struct priority_list *p_new; struct krb5_match_rule *parsed_match_rule; struct ldap_mapping_rule *parsed_mapping_rule; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); return ENOMEM; } rule = talloc_zero(tmp_ctx, struct match_map_rule); if (rule == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } rule->priority = priority; if (match_rule == NULL) { match_rule = DEFAULT_MATCH_RULE; } ret = parse_match_rule(ctx, match_rule, &parsed_match_rule); if (ret == 0) { rule->parsed_match_rule = talloc_steal(rule, parsed_match_rule); rule->match_rule = talloc_strdup(rule, match_rule); if (rule->match_rule == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } } else if (ret == ESRCH) { /* report unsupported rules */ goto done; } else { goto done; } if (map_rule == NULL) { map_rule = DEFAULT_MAP_RULE; } ret = parse_mapping_rule(ctx, map_rule, &parsed_mapping_rule); if (ret == 0) { rule->parsed_mapping_rule = talloc_steal(rule, parsed_mapping_rule); rule->map_rule = talloc_strdup(rule, map_rule); if (rule->map_rule == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } } else if (ret == ESRCH) { /* report unsupported rules */ goto done; } else { goto done; } if (domains != NULL && *domains != NULL) { for (c = 0; domains[c] != NULL; c++); rule->domains = talloc_zero_array(rule, char *, c + 1); if (rule->domains == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } for (c = 0; domains[c] != NULL; c++) { rule->domains[c] = talloc_strdup(rule->domains, domains[c]); if (rule->domains[c] == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } } } if (ctx->prio_list == NULL) { ctx->prio_list = talloc_zero(ctx, struct priority_list); if (ctx->prio_list == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } ctx->prio_list->priority = rule->priority; ctx->prio_list->rule_list = rule; } else { for (p = ctx->prio_list; p != NULL && p->priority < rule->priority; p = p->next); if (p != NULL && p->priority == priority) { DLIST_ADD(p->rule_list, rule); } else { p_new = talloc_zero(ctx, struct priority_list); if (p_new == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } p_new->priority = rule->priority; p_new->rule_list = rule; if (p == NULL) { DLIST_ADD_END(ctx->prio_list, p_new, struct priority_list *); } else if (p->prev == NULL) { DLIST_ADD(ctx->prio_list, p_new); } else { DLIST_ADD_AFTER(ctx->prio_list, p_new, p->prev); } } } talloc_steal(ctx, rule); ret = EOK; done: talloc_free(tmp_ctx); return ret; } static int expand_cert(struct sss_certmap_ctx *ctx, struct parsed_template *parsed_template, struct sss_cert_content *cert_content, char **expanded) { int ret; char *tmp_str = NULL; if (parsed_template->conversion == NULL || strcmp(parsed_template->conversion, "bin") == 0) { ret = bin_to_ldap_filter_value(ctx, cert_content->cert_der, cert_content->cert_der_size, &tmp_str); if (ret != 0) { ctx->err_msg = ERRMSG("bin conversion failed."); CM_DEBUG(ctx, "bin conversion failed."); goto done; } } else if (strcmp(parsed_template->conversion, "base64") == 0) { tmp_str = sss_base64_encode(ctx, cert_content->cert_der, cert_content->cert_der_size); if (tmp_str == NULL) { ctx->err_msg = ERRMSG("base64 conversion failed."); CM_DEBUG(ctx, "base64 conversion failed."); ret = ENOMEM; goto done; } } else { ctx->err_msg = ERRMSG("Unsupported conversion."); CM_DEBUG(ctx, "Unsupported conversion."); ret = EINVAL; goto done; } ret = 0; done: if (ret == 0) { *expanded = tmp_str; } else { talloc_free(tmp_str); } return ret; } static int get_dn_str(struct sss_certmap_ctx *ctx, const char *conversion, const char **rdn_list, char **result) { char *str = NULL; size_t c; int ret; str = talloc_strdup(ctx, ""); if (str == NULL) { ret = ENOMEM; goto done; } if (conversion == NULL || strcmp(conversion, "ldap") == 0) { for (c = 0; rdn_list[c] != NULL; c++); while (c != 0) { c--; str = talloc_asprintf_append(str, "%s%s", (rdn_list[c + 1] == NULL) ? "" : ",", rdn_list[c]); if (str == NULL) { ret = ENOMEM; goto done; } }; } else if (strcmp(conversion, "x500") == 0) { for (c = 0; rdn_list[c] != NULL; c++) { str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",", rdn_list[c]); if (str == NULL) { ret = ENOMEM; goto done; } } } else { ret = EINVAL; goto done; } ret = 0; done: if (ret == 0) { *result = str; } else { talloc_free(str); } return ret; } static int expand_template(struct sss_certmap_ctx *ctx, struct parsed_template *parsed_template, struct sss_cert_content *cert_content, char **expanded) { int ret; char *exp; if (strcmp("issuer_dn", parsed_template->name) == 0) { //exp = talloc_strdup(ctx, cert_content->issuer_str); ret = get_dn_str(ctx, parsed_template->conversion, cert_content->issuer_rdn_list, &exp); } else if (strcmp("subject_dn", parsed_template->name) == 0) { //exp = talloc_strdup(ctx, cert_content->subject_str); ret = get_dn_str(ctx, parsed_template->conversion, cert_content->subject_rdn_list, &exp); } else if (strcmp("cert", parsed_template->name) == 0) { ret = expand_cert(ctx, parsed_template, cert_content, &exp); } else { ctx->err_msg = ERRMSG("Unsupported template name."); CM_DEBUG(ctx, "Unsupported template name."); ret = EINVAL; goto done; } if (ret != 0) { ctx->err_msg = ERRMSG("Failed to expand template."); CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name); goto done; } if (exp == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } ret = 0; done: if (ret == 0) { *expanded = exp; } else { talloc_free(exp); } return ret; } static int get_filter(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *parsed_mapping_rule, struct sss_cert_content *cert_content, char **filter) { struct ldap_mapping_rule_comp *comp; char *result = NULL; char *expanded = NULL; int ret; result = talloc_strdup(ctx, ""); if (result == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); return ENOMEM; } for (comp = parsed_mapping_rule->list; comp != NULL; comp = comp->next) { if (comp->type == comp_string) { result = talloc_strdup_append(result, comp->val); } else if (comp->type == comp_template) { ret = expand_template(ctx, comp->parsed_template, cert_content, &expanded); if (ret != 0) { CM_DEBUG(ctx, "Failed to expanded template."); ctx->err_msg = ERRMSG("Failed to expand template."); goto done; } result = talloc_strdup_append(result, expanded); talloc_free(expanded); expanded = NULL; if (result == NULL) { ctx->err_msg = ERRMSG("Memory allocation failed."); ret = ENOMEM; goto done; } } else { ret = EINVAL; CM_DEBUG(ctx, "Unsupported component type."); ctx->err_msg = ERRMSG("Unsupported component type."); goto done; } } ret = 0; done: talloc_free(expanded); if (ret == 0) { *filter = result; } else { talloc_free(result); } return ret; } static int do_match(struct krb5_match_rule *parsed_match_rule, struct sss_cert_content *cert_content) { struct component_list *comp; bool match = false; if (parsed_match_rule == NULL || cert_content == NULL) { return EINVAL; } /* Issuer */ if (cert_content->issuer_str != NULL) { for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) { match = (regexec(&(comp->regexp), cert_content->issuer_str, 0, NULL, 0) == 0); if (match && parsed_match_rule->r == relation_or) { /* match */ return 0; } else if (!match && parsed_match_rule->r == relation_and) { /* no match */ return ENOENT; } } } if (cert_content->subject_str != NULL) { for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) { if (match && parsed_match_rule->r == relation_or) { match = (regexec(&(comp->regexp), cert_content->issuer_str, 0, NULL, 0) == 0); /* match */ return 0; } else if (!match && parsed_match_rule->r == relation_and) { /* no match */ return ENOENT; } } } if (match) { /* match */ return 0; } /* no match */ return ENOENT; } int sss_certmap_match_cert(struct sss_certmap_ctx *ctx, uint8_t *der_cert, size_t der_size) { int ret; struct match_map_rule *r; struct priority_list *p; struct sss_cert_content *cert_content = NULL; ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content); if (ret != 0) { ctx->err_msg = ERRMSG("Failed to get certificate content."); return ret; } for (p = ctx->prio_list; p != NULL; p = p->next) { for (r = p->rule_list; r != NULL; r = r->next) { ret = do_match(r->parsed_match_rule, cert_content); if (ret == 0) { /* match */ goto done; } } } ret = ENOENT; done: talloc_free(cert_content); return ret; } int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, uint8_t *der_cert, size_t der_size, char **_filter, char ***_domains) { int ret; struct match_map_rule *r; struct priority_list *p; struct sss_cert_content *cert_content = NULL; char *filter = NULL; char **domains = NULL; size_t c; if (_filter == NULL || _domains == NULL) { return EINVAL; } ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content); if (ret != 0) { CM_DEBUG(ctx, "Failed to get certificate content [%d].", ret); ctx->err_msg = ERRMSG("Failed to get certificate content."); return ret; } for (p = ctx->prio_list; p != NULL; p = p->next) { for (r = p->rule_list; r != NULL; r = r->next) { ret = do_match(r->parsed_match_rule, cert_content); if (ret == 0) { /* match */ ret = get_filter(ctx, r->parsed_mapping_rule, cert_content, &filter); if (ret != 0) { CM_DEBUG(ctx, "Failed to get filter"); ctx->err_msg = ERRMSG("Failed to get filter."); goto done; } if (r->domains != NULL) { for (c = 0; r->domains[c] != NULL; c++); domains = talloc_zero_array(ctx, char *, c + 1); if (domains == NULL) { ctx->err_msg = ERRMSG("Memory allocation false."); ret = ENOMEM; goto done; } for (c = 0; r->domains[c] != NULL; c++) { domains[c] = talloc_strdup(domains, r->domains[c]); if (domains[c] == NULL) { ctx->err_msg = ERRMSG("Memory allocation false."); ret = ENOMEM; goto done; } } } ret = 0; goto done; } } } ret = ENOENT; done: talloc_free(cert_content); if (ret == 0) { *_filter = filter; *_domains = domains; } else { talloc_free(filter); talloc_free(domains); } return ret; }