summaryrefslogtreecommitdiffstats
path: root/src/lib/certmap/sss_certmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/certmap/sss_certmap.c')
-rw-r--r--src/lib/certmap/sss_certmap.c1398
1 files changed, 1398 insertions, 0 deletions
diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c
new file mode 100644
index 000000000..0028a120c
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.c
@@ -0,0 +1,1398 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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.
+ * <SUBJECT>regular-expression
+ * <ISSUER>regular-expression
+ * <SAN>regular-expression
+ * <EKU>extended-key-usage
+ * <KU>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) {
+ 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;
+}