From 045a27604d0df6e6215776c4b3a27eac2906dc4e Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 27 Nov 2018 16:40:01 +0100 Subject: [PATCH 1/2] certmap: add sss_certmap_display_cert_content() To make debugging and writing certificate mapping and matching rules more easy a new function is added to libsss_certmap to display the certificate content as seen by libsss_certmap. Please note that the actual output might change in future. Reviewed-by: Jakub Hrozek (cherry picked from commit 1c40208aa1e0f9a17cc4f336c99bcaa6977592d3) Reviewed-by: Alexey Tikhonov Reviewed-by: Justin Stephenson --- Makefile.am | 2 +- src/lib/certmap/sss_certmap.c | 142 ++++++++++++++++++++++ src/lib/certmap/sss_certmap.exports | 5 + src/lib/certmap/sss_certmap.h | 18 +++ src/lib/certmap/sss_certmap_int.h | 31 ++++- src/lib/certmap/sss_certmap_krb5_match.c | 145 +++++++++++------------ 6 files changed, 261 insertions(+), 82 deletions(-) diff --git a/Makefile.am b/Makefile.am index ac234ed..87eb571 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1862,7 +1862,7 @@ libsss_certmap_la_LIBADD = \ $(NULL) libsss_certmap_la_LDFLAGS = \ -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \ - -version-info 0:0:0 + -version-info 1:0:1 if HAVE_NSS libsss_certmap_la_SOURCES += \ diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c index f6f6f98..c60ac24 100644 --- a/src/lib/certmap/sss_certmap.c +++ b/src/lib/certmap/sss_certmap.c @@ -914,3 +914,145 @@ void sss_certmap_free_filter_and_domains(char *filter, char **domains) talloc_free(filter); talloc_free(domains); } + +static const char *sss_eku_oid2name(const char *oid) +{ + size_t c; + + for (c = 0; sss_ext_key_usage[c].name != NULL; c++) { + if (strcmp(sss_ext_key_usage[c].oid, oid) == 0) { + return sss_ext_key_usage[c].name; + } + } + + return NULL; +} + +struct parsed_template san_parsed_template[] = { + { NULL, NULL, NULL }, /* SAN_OTHER_NAME handled separately */ + { "subject_rfc822_name", NULL, NULL}, + { "subject_dns_name", NULL, NULL}, + { "subject_x400_address", NULL, NULL}, + { "subject_directory_name", NULL, NULL}, + { "subject_ediparty_name", NULL, NULL}, + { "subject_uri", NULL, NULL}, + { "subject_ip_address", NULL, NULL}, + { "subject_registered_id", NULL, NULL}, + { "subject_pkinit_principal", NULL, NULL}, + { "subject_nt_principal", NULL, NULL}, + { "subject_principal", NULL, NULL}, + { NULL, NULL, NULL }, /* SAN_STRING_OTHER_NAME handled separately */ + { NULL, NULL, NULL } /* SAN_END */ +}; + +int sss_cert_dump_content(TALLOC_CTX *mem_ctx, struct sss_cert_content *c, + char **content_str) +{ + char *out = NULL; + size_t o; + struct san_list *s; + struct sss_certmap_ctx *ctx = NULL; + char *expanded = NULL; + int ret; + char *b64 = NULL; + const char *eku_str = NULL; + + ret = sss_certmap_init(mem_ctx, NULL, NULL, &ctx); + if (ret != EOK) { + return ret; + } + + out = talloc_strdup(mem_ctx, "sss cert content (format might change):\n"); + if (out == NULL) return ENOMEM; + + out = talloc_asprintf_append(out, "Issuer: %s\n", c->issuer_str != NULL + ? c->issuer_str + : "- not available -"); + if (out == NULL) return ENOMEM; + out = talloc_asprintf_append(out, "Subject: %s\n", c->subject_str != NULL + ? c->subject_str + : "- not available -"); + if (out == NULL) return ENOMEM; + + out = talloc_asprintf_append(out, "Key Usage: %u(0x%04x)", c->key_usage, + c->key_usage); + if (out == NULL) return ENOMEM; + + if (c->key_usage != 0) { + out = talloc_asprintf_append(out, " ("); + if (out == NULL) return ENOMEM; + for (o = 0; sss_key_usage[o].name != NULL; o++) { + if ((c->key_usage & sss_key_usage[o].flag) != 0) { + out = talloc_asprintf_append(out, "%s%s", + o == 0 ? "" : ",", + sss_key_usage[o].name); + if (out == NULL) return ENOMEM; + } + } + out = talloc_asprintf_append(out, ")"); + if (out == NULL) return ENOMEM; + } + out = talloc_asprintf_append(out, "\n"); + if (out == NULL) return ENOMEM; + + for (o = 0; c->extended_key_usage_oids[o] != NULL; o++) { + eku_str = sss_eku_oid2name(c->extended_key_usage_oids[o]); + out = talloc_asprintf_append(out, "Extended Key Usage #%zu: %s%s%s%s\n", + o, c->extended_key_usage_oids[o], + eku_str == NULL ? "" : " (", + eku_str == NULL ? "" : eku_str, + eku_str == NULL ? "" : ")"); + if (out == NULL) return ENOMEM; + } + + DLIST_FOR_EACH(s, c->san_list) { + out = talloc_asprintf_append(out, "SAN type: %s\n", + s->san_opt < SAN_END + ? sss_san_names[s->san_opt].name + : "- unsupported -"); + if (out == NULL) return ENOMEM; + + if (san_parsed_template[s->san_opt].name != NULL) { + ret = expand_san(ctx, &san_parsed_template[s->san_opt], c->san_list, + &expanded); + if (ret != EOK) { + return ret; + } + out = talloc_asprintf_append(out, " %s=%s\n\n", + san_parsed_template[s->san_opt].name, + expanded); + talloc_free(expanded); + if (out == NULL) return ENOMEM; + } else if (s->san_opt == SAN_STRING_OTHER_NAME) { + b64 = sss_base64_encode(mem_ctx, s->bin_val, s->bin_val_len); + out = talloc_asprintf_append(out, " %s=%s\n\n", s->other_name_oid, + b64 != NULL ? b64 + : "- cannot encode -"); + talloc_free(b64); + } + } + + *content_str = out; + + return EOK; +} + +int sss_certmap_display_cert_content(TALLOC_CTX *mem_cxt, + const uint8_t *der_cert, size_t der_size, + char **desc) +{ + int ret; + struct sss_cert_content *content; + + ret = sss_cert_get_content(mem_cxt, der_cert, der_size, &content); + if (ret != EOK) { + return ret; + } + + ret = sss_cert_dump_content(mem_cxt, content, desc); + if (ret != EOK) { + return ret; + } + + return 0; +} diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports index 8b5d536..a9e48d6 100644 --- a/src/lib/certmap/sss_certmap.exports +++ b/src/lib/certmap/sss_certmap.exports @@ -11,3 +11,8 @@ SSS_CERTMAP_0.0 { local: *; }; + +SSS_CERTMAP_0.1 { + global: + sss_certmap_display_cert_content; +} SSS_CERTMAP_0.0; diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h index 646e0f3..7da2d1c 100644 --- a/src/lib/certmap/sss_certmap.h +++ b/src/lib/certmap/sss_certmap.h @@ -146,6 +146,24 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, */ void sss_certmap_free_filter_and_domains(char *filter, char **domains); +/** + * @brief Get a string with the content of the certificate used by the library + * + * @param[in] mem_ctx Talloc memory context, may be NULL + * @param[in] der_cert binary blog with the DER encoded certificate + * @param[in] der_size size of the certificate blob + * @param[out] desc Multiline string showing the certificate content + * which is used by libsss_certmap + * + * @return + * - 0: success + * - EINVAL: certificate cannot be parsed + * - ENOMEM: memory allocation failure + */ +int sss_certmap_display_cert_content(TALLOC_CTX *mem_cxt, + const uint8_t *der_cert, size_t der_size, + char **desc); + /** * @} */ diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h index 479cc16..b1155e2 100644 --- a/src/lib/certmap/sss_certmap_int.h +++ b/src/lib/certmap/sss_certmap_int.h @@ -101,9 +101,9 @@ enum comp_type { }; struct parsed_template { - char *name; - char *attr_name; - char *conversion; + const char *name; + const char *attr_name; + const char *conversion; }; struct ldap_mapping_rule_comp { @@ -166,6 +166,28 @@ struct san_list { #define SSS_KU_ENCIPHER_ONLY 0x0001 #define SSS_KU_DECIPHER_ONLY 0x8000 +struct sss_key_usage { + const char *name; + uint32_t flag; +}; + +extern const struct sss_key_usage sss_key_usage[]; + +struct sss_ext_key_usage { + const char *name; + const char *oid; +}; + +extern const struct sss_ext_key_usage sss_ext_key_usage[]; + +struct sss_san_name { + const char *name; + enum san_opt san_opt; + bool is_string; +}; + +extern const struct sss_san_name sss_san_names[]; + struct sss_cert_content { char *issuer_str; const char **issuer_rdn_list; @@ -183,6 +205,9 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx, const uint8_t *der_blob, size_t der_size, struct sss_cert_content **content); +int sss_cert_dump_content(TALLOC_CTX *mem_ctx, struct sss_cert_content *c, + char **content_str); + char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn); char *openssl_2_nss_attr_name(const char *attr); diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c index 125e925..398d3d2 100644 --- a/src/lib/certmap/sss_certmap_krb5_match.c +++ b/src/lib/certmap/sss_certmap_krb5_match.c @@ -29,6 +29,59 @@ #include "lib/certmap/sss_certmap.h" #include "lib/certmap/sss_certmap_int.h" +const struct sss_key_usage sss_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} +}; + +const struct sss_ext_key_usage sss_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} +}; + +const struct sss_san_name sss_san_names[] = { + /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */ + {"otherName", SAN_OTHER_NAME, false}, + {"rfc822Name", SAN_RFC822_NAME, true}, + {"dNSName", SAN_DNS_NAME, true}, + {"x400Address", SAN_X400_ADDRESS, false}, + {"directoryName", SAN_DIRECTORY_NAME, true}, + {"ediPartyName", SAN_EDIPART_NAME, false}, + {"uniformResourceIdentifier", SAN_URI, true}, + {"iPAddress", SAN_IP_ADDRESS, true}, + {"registeredID", SAN_REGISTERED_ID, true}, + /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */ + {"pkinitSAN", SAN_PKINIT, true}, + /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */ + {"ntPrincipalName", SAN_NT, true}, + /* both previous principal types */ + {"Principal", SAN_PRINCIPAL, true}, + {"stringOtherName", SAN_STRING_OTHER_NAME, true}, + {NULL, SAN_END, false} +}; + static bool is_dotted_decimal(const char *s, size_t len) { size_t c = 0; @@ -145,28 +198,6 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx, 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) { CM_DEBUG(ctx, "Failed to parse regexp."); @@ -188,11 +219,11 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx, } 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) { + for (k = 0; sss_ext_key_usage[k].name != NULL; k++) { +CM_DEBUG(ctx, "[%s][%s].", eku_list[c], sss_ext_key_usage[k].name); + if (strcasecmp(eku_list[c], sss_ext_key_usage[k].name) == 0) { comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, - ext_key_usage[k].oid); + sss_ext_key_usage[k].oid); if (comp->eku_oid_list[e] == NULL) { ret = ENOMEM; goto done; @@ -202,7 +233,7 @@ CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name); } } - if (ext_key_usage[k].name == NULL) { + if (sss_ext_key_usage[k].name == NULL) { /* check for an dotted-decimal OID */ if (*(eku_list[c]) != '.') { o = eku_list[c]; @@ -252,23 +283,6 @@ static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx, 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) { CM_DEBUG(ctx, "Failed to get value."); @@ -283,14 +297,14 @@ static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx, } 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; + for (k = 0; sss_key_usage[k].name != NULL; k++) { + if (strcasecmp(ku_list[c], sss_key_usage[k].name) == 0) { + comp->ku |= sss_key_usage[k].flag; break; } } - if (key_usage[k].name == NULL) { + if (sss_key_usage[k].name == NULL) { /* FIXME: add check for numerical ku */ CM_DEBUG(ctx, "No matching key usage found."); ret = EINVAL; @@ -342,31 +356,6 @@ done: return ret; } -struct san_name { - const char *name; - enum san_opt san_opt; - bool is_string; -} san_names[] = { - /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */ - {"otherName", SAN_OTHER_NAME, false}, - {"rfc822Name", SAN_RFC822_NAME,true}, - {"dNSName", SAN_DNS_NAME, true}, - {"x400Address", SAN_X400_ADDRESS, false}, - {"directoryName", SAN_DIRECTORY_NAME, true}, - {"ediPartyName", SAN_EDIPART_NAME, false}, - {"uniformResourceIdentifier", SAN_URI, true}, - {"iPAddress", SAN_IP_ADDRESS, true}, - {"registeredID", SAN_REGISTERED_ID, true}, - /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */ - {"pkinitSAN", SAN_PKINIT, true}, - /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */ - {"ntPrincipalName", SAN_NT, true}, - /* both previous principal types */ - {"Principal", SAN_PRINCIPAL, true}, - {"stringOtherName", SAN_STRING_OTHER_NAME, true}, - {NULL, SAN_END, false} -}; - static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, const char **cur, @@ -388,12 +377,12 @@ static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx, if (len == 0) { c= SAN_PRINCIPAL; } else { - for (c = 0; san_names[c].name != NULL; c++) { - if (strncasecmp(*cur, san_names[c].name, len) == 0) { + for (c = 0; sss_san_names[c].name != NULL; c++) { + if (strncasecmp(*cur, sss_san_names[c].name, len) == 0) { break; } } - if (san_names[c].name == NULL) { + if (sss_san_names[c].name == NULL) { if (is_dotted_decimal(*cur, len)) { c = SAN_STRING_OTHER_NAME; *str_other_name_oid = talloc_strndup(mem_ctx, *cur, len); @@ -408,7 +397,7 @@ static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx, } } - *option = san_names[c].san_opt; + *option = sss_san_names[c].san_opt; *cur = end + 1; return 0; @@ -432,7 +421,7 @@ static int parse_krb5_get_san_value(TALLOC_CTX *mem_ctx, } } - if (san_names[san_opt].is_string) { + if (sss_san_names[san_opt].is_string) { ret = parse_krb5_get_component_value(mem_ctx, ctx, cur, &comp); if (ret != 0) { goto done; -- 2.37.3 From 136858b1a5f7ed011287293fdf35cc7142ee3cf1 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 9 Dec 2019 11:31:14 +0100 Subject: [PATCH 2/2] certmap: sanitize LDAP search filter The sss_certmap_get_search_filter() will now sanitize the values read from the certificates before adding them to a search filter. To be able to get the plain values as well sss_certmap_expand_mapping_rule() is added. Resolves: https://github.com/SSSD/sssd/issues/5135 Reviewed-by: Alexey Tikhonov (cherry picked from commit a2b9a84460429181f2a4fa7e2bb5ab49fd561274) Reviewed-by: Alexey Tikhonov Reviewed-by: Justin Stephenson --- Makefile.am | 2 +- src/lib/certmap/sss_certmap.c | 42 ++++++++++-- src/lib/certmap/sss_certmap.exports | 5 ++ src/lib/certmap/sss_certmap.h | 35 ++++++++-- src/responder/pam/pamsrv_p11.c | 5 +- src/tests/cmocka/test_certmap.c | 98 +++++++++++++++++++++++++++- src/util/util.c | 95 --------------------------- src/util/util_ext.c | 99 +++++++++++++++++++++++++++++ 8 files changed, 272 insertions(+), 109 deletions(-) diff --git a/Makefile.am b/Makefile.am index 87eb571..d1568fb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1862,7 +1862,7 @@ libsss_certmap_la_LIBADD = \ $(NULL) libsss_certmap_la_LDFLAGS = \ -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \ - -version-info 1:0:1 + -version-info 2:0:2 if HAVE_NSS libsss_certmap_la_SOURCES += \ diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c index c60ac24..d7bc992 100644 --- a/src/lib/certmap/sss_certmap.c +++ b/src/lib/certmap/sss_certmap.c @@ -441,10 +441,12 @@ static int expand_san(struct sss_certmap_ctx *ctx, static int expand_template(struct sss_certmap_ctx *ctx, struct parsed_template *parsed_template, struct sss_cert_content *cert_content, + bool sanitize, char **expanded) { int ret; char *exp = NULL; + char *exp_sanitized = NULL; if (strcmp("issuer_dn", parsed_template->name) == 0) { ret = rdn_list_2_dn_str(ctx, parsed_template->conversion, @@ -455,6 +457,8 @@ static int expand_template(struct sss_certmap_ctx *ctx, } else if (strncmp("subject_", parsed_template->name, 8) == 0) { ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp); } else if (strcmp("cert", parsed_template->name) == 0) { + /* cert blob is already sanitized */ + sanitize = false; ret = expand_cert(ctx, parsed_template, cert_content, &exp); } else { CM_DEBUG(ctx, "Unsupported template name."); @@ -471,6 +475,16 @@ static int expand_template(struct sss_certmap_ctx *ctx, goto done; } + if (sanitize) { + ret = sss_filter_sanitize(ctx, exp, &exp_sanitized); + if (ret != EOK) { + CM_DEBUG(ctx, "Failed to sanitize expanded template."); + goto done; + } + talloc_free(exp); + exp = exp_sanitized; + } + ret = 0; done: @@ -485,7 +499,7 @@ done: static int get_filter(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *parsed_mapping_rule, - struct sss_cert_content *cert_content, + struct sss_cert_content *cert_content, bool sanitize, char **filter) { struct ldap_mapping_rule_comp *comp; @@ -503,7 +517,7 @@ static int get_filter(struct sss_certmap_ctx *ctx, result = talloc_strdup_append(result, comp->val); } else if (comp->type == comp_template) { ret = expand_template(ctx, comp->parsed_template, cert_content, - &expanded); + sanitize, &expanded); if (ret != 0) { CM_DEBUG(ctx, "Failed to expanded template."); goto done; @@ -791,8 +805,9 @@ done: return ret; } -int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, +static int expand_mapping_rule_ex(struct sss_certmap_ctx *ctx, const uint8_t *der_cert, size_t der_size, + bool sanitize, char **_filter, char ***_domains) { int ret; @@ -819,7 +834,8 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, return EINVAL; } - ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, &filter); + ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, sanitize, + &filter); goto done; } @@ -829,7 +845,7 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, if (ret == 0) { /* match */ ret = get_filter(ctx, r->parsed_mapping_rule, cert_content, - &filter); + sanitize, &filter); if (ret != 0) { CM_DEBUG(ctx, "Failed to get filter"); goto done; @@ -873,6 +889,22 @@ done: return ret; } +int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, + const uint8_t *der_cert, size_t der_size, + char **_filter, char ***_domains) +{ + return expand_mapping_rule_ex(ctx, der_cert, der_size, true, + _filter, _domains); +} + +int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx, + const uint8_t *der_cert, size_t der_size, + char **_expanded, char ***_domains) +{ + return expand_mapping_rule_ex(ctx, der_cert, der_size, false, + _expanded, _domains); +} + int sss_certmap_init(TALLOC_CTX *mem_ctx, sss_certmap_ext_debug *debug, void *debug_priv, struct sss_certmap_ctx **ctx) diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports index a9e48d6..7d76677 100644 --- a/src/lib/certmap/sss_certmap.exports +++ b/src/lib/certmap/sss_certmap.exports @@ -16,3 +16,8 @@ SSS_CERTMAP_0.1 { global: sss_certmap_display_cert_content; } SSS_CERTMAP_0.0; + +SSS_CERTMAP_0.2 { + global: + sss_certmap_expand_mapping_rule; +} SSS_CERTMAP_0.1; diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h index 7da2d1c..058d4f9 100644 --- a/src/lib/certmap/sss_certmap.h +++ b/src/lib/certmap/sss_certmap.h @@ -103,7 +103,7 @@ int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, * * @param[in] ctx certmap context previously initialized with * @ref sss_certmap_init - * @param[in] der_cert binary blog with the DER encoded certificate + * @param[in] der_cert binary blob with the DER encoded certificate * @param[in] der_size size of the certificate blob * * @return @@ -119,10 +119,11 @@ int sss_certmap_match_cert(struct sss_certmap_ctx *ctx, * * @param[in] ctx certmap context previously initialized with * @ref sss_certmap_init - * @param[in] der_cert binary blog with the DER encoded certificate + * @param[in] der_cert binary blob with the DER encoded certificate * @param[in] der_size size of the certificate blob - * @param[out] filter LDAP filter string, caller should free the data by - * calling sss_certmap_free_filter_and_domains + * @param[out] filter LDAP filter string, expanded templates are sanitized, + * caller should free the data by calling + * sss_certmap_free_filter_and_domains * @param[out] domains NULL-terminated array of strings with the domains the * rule applies, caller should free the data by calling * sss_certmap_free_filter_and_domains @@ -136,8 +137,32 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, const uint8_t *der_cert, size_t der_size, char **filter, char ***domains); +/** + * @brief Expand the mapping rule by replacing the templates + * + * @param[in] ctx certmap context previously initialized with + * @ref sss_certmap_init + * @param[in] der_cert binary blob with the DER encoded certificate + * @param[in] der_size size of the certificate blob + * @param[out] expanded expanded mapping rule, templates are filled in + * verbatim in contrast to sss_certmap_get_search_filter, + * caller should free the data by + * calling sss_certmap_free_filter_and_domains + * @param[out] domains NULL-terminated array of strings with the domains the + * rule applies, caller should free the data by calling + * sss_certmap_free_filter_and_domains + * + * @return + * - 0: certificate matches a rule + * - ENOENT: certificate does not match + * - EINVAL: internal error + */ +int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx, + const uint8_t *der_cert, size_t der_size, + char **_expanded, char ***_domains); /** * @brief Free data returned by @ref sss_certmap_get_search_filter + * and @ref sss_certmap_expand_mapping_rule * * @param[in] filter LDAP filter strings returned by * sss_certmap_get_search_filter @@ -150,7 +175,7 @@ void sss_certmap_free_filter_and_domains(char *filter, char **domains); * @brief Get a string with the content of the certificate used by the library * * @param[in] mem_ctx Talloc memory context, may be NULL - * @param[in] der_cert binary blog with the DER encoded certificate + * @param[in] der_cert binary blob with the DER encoded certificate * @param[in] der_size size of the certificate blob * @param[out] desc Multiline string showing the certificate content * which is used by libsss_certmap diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c index c58a8be..f01566e 100644 --- a/src/responder/pam/pamsrv_p11.c +++ b/src/responder/pam/pamsrv_p11.c @@ -1044,9 +1044,10 @@ static char *get_cert_prompt(TALLOC_CTX *mem_ctx, goto done; } - ret = sss_certmap_get_search_filter(ctx, der, der_size, &filter, &domains); + ret = sss_certmap_expand_mapping_rule(ctx, der, der_size, + &filter, &domains); if (ret != 0) { - DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_get_search_filter failed.\n"); + DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_expand_mapping_rule failed.\n"); goto done; } diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c index c882202..232ff78 100644 --- a/src/tests/cmocka/test_certmap.c +++ b/src/tests/cmocka/test_certmap.c @@ -1431,6 +1431,15 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule100=CN=Certificate\\20Authority,O=IPA.DEVEL" + "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule100=CN=Certificate Authority,O=IPA.DEVEL" "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); assert_null(domains); @@ -1445,6 +1454,17 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule99=CN=Certificate\\20Authority,O=IPA.DEVEL" + "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule99=CN=Certificate Authority,O=IPA.DEVEL" "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); assert_non_null(domains); @@ -1466,6 +1486,16 @@ static void test_sss_certmap_get_search_filter(void **state) assert_string_equal(domains[0], "test.dom"); assert_null(domains[1]); + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + ret = sss_certmap_add_rule(ctx, 97, "KRB5:CN=Certificate Authority,O=IPA.DEVEL", "LDAP:rule97={issuer_dn!nss_x500}{subject_dn}", @@ -1476,6 +1506,17 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule97=O=IPA.DEVEL,CN=Certificate\\20Authority" + "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule97=O=IPA.DEVEL,CN=Certificate Authority" "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); assert_non_null(domains); @@ -1492,6 +1533,17 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule96=O=IPA.DEVEL,CN=Certificate\\20Authority" + "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule96=O=IPA.DEVEL,CN=Certificate Authority" "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); assert_non_null(domains); @@ -1510,6 +1562,14 @@ static void test_sss_certmap_get_search_filter(void **state) assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")"); assert_null(domains); + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")"); + assert_null(domains); + ret = sss_certmap_add_rule(ctx, 94, "KRB5:CN=Certificate Authority,O=IPA.DEVEL", "LDAP:rule94={issuer_dn!ad_x500}{subject_dn!ad_x500}", @@ -1520,12 +1580,22 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); - assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate Authority" + assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate\\20Authority" "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); assert_non_null(domains); assert_string_equal(domains[0], "test.dom"); assert_null(domains[1]); + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate Authority" + "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); ret = sss_certmap_add_rule(ctx, 89, NULL, "(rule89={subject_nt_principal})", @@ -1539,6 +1609,14 @@ static void test_sss_certmap_get_search_filter(void **state) assert_string_equal(filter, "(rule89=tu1@ad.devel)"); assert_null(domains); + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(rule89=tu1@ad.devel)"); + assert_null(domains); + ret = sss_certmap_add_rule(ctx, 88, NULL, "(rule88={subject_nt_principal.short_name})", NULL); @@ -1560,6 +1638,15 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule87=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule87=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" "DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); assert_null(domains); @@ -1573,6 +1660,15 @@ static void test_sss_certmap_get_search_filter(void **state) &filter, &domains); assert_int_equal(ret, 0); assert_non_null(filter); + assert_string_equal(filter, "rule86=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); assert_string_equal(filter, "rule86=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" "DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); assert_null(domains); diff --git a/src/util/util.c b/src/util/util.c index 4051c1f..9327f97 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -436,101 +436,6 @@ errno_t sss_hash_create(TALLOC_CTX *mem_ctx, unsigned long count, return sss_hash_create_ex(mem_ctx, count, tbl, 0, 0, 0, 0, NULL, NULL); } -errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx, - const char *input, - char **sanitized, - const char *ignore) -{ - char *output; - size_t i = 0; - size_t j = 0; - char *allowed; - - /* Assume the worst-case. We'll resize it later, once */ - output = talloc_array(mem_ctx, char, strlen(input) * 3 + 1); - if (!output) { - return ENOMEM; - } - - while (input[i]) { - /* Even though this character might have a special meaning, if it's - * expliticly allowed, just copy it and move on - */ - if (ignore == NULL) { - allowed = NULL; - } else { - allowed = strchr(ignore, input[i]); - } - if (allowed) { - output[j++] = input[i++]; - continue; - } - - switch(input[i]) { - case '\t': - output[j++] = '\\'; - output[j++] = '0'; - output[j++] = '9'; - break; - case ' ': - output[j++] = '\\'; - output[j++] = '2'; - output[j++] = '0'; - break; - case '*': - output[j++] = '\\'; - output[j++] = '2'; - output[j++] = 'a'; - break; - case '(': - output[j++] = '\\'; - output[j++] = '2'; - output[j++] = '8'; - break; - case ')': - output[j++] = '\\'; - output[j++] = '2'; - output[j++] = '9'; - break; - case '\\': - output[j++] = '\\'; - output[j++] = '5'; - output[j++] = 'c'; - break; - case '\r': - output[j++] = '\\'; - output[j++] = '0'; - output[j++] = 'd'; - break; - case '\n': - output[j++] = '\\'; - output[j++] = '0'; - output[j++] = 'a'; - break; - default: - output[j++] = input[i]; - } - - i++; - } - output[j] = '\0'; - *sanitized = talloc_realloc(mem_ctx, output, char, j+1); - if (!*sanitized) { - talloc_free(output); - return ENOMEM; - } - - return EOK; -} - -errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx, - const char *input, - char **sanitized) -{ - return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL); -} - - /* There is similar function ldap_dn_normalize in openldap. * To avoid dependecies across project we have this own func. * Also ldb can do this but doesn't handle all the cases diff --git a/src/util/util_ext.c b/src/util/util_ext.c index 04dc02a..a89b60f 100644 --- a/src/util/util_ext.c +++ b/src/util/util_ext.c @@ -29,6 +29,11 @@ #define EOK 0 +#ifndef HAVE_ERRNO_T +#define HAVE_ERRNO_T +typedef int errno_t; +#endif + int split_on_separator(TALLOC_CTX *mem_ctx, const char *str, const char sep, bool trim, bool skip_empty, char ***_list, int *size) @@ -141,3 +146,97 @@ bool string_in_list(const char *string, char **list, bool case_sensitive) return false; } + +errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx, + const char *input, + char **sanitized, + const char *ignore) +{ + char *output; + size_t i = 0; + size_t j = 0; + char *allowed; + + /* Assume the worst-case. We'll resize it later, once */ + output = talloc_array(mem_ctx, char, strlen(input) * 3 + 1); + if (!output) { + return ENOMEM; + } + + while (input[i]) { + /* Even though this character might have a special meaning, if it's + * explicitly allowed, just copy it and move on + */ + if (ignore == NULL) { + allowed = NULL; + } else { + allowed = strchr(ignore, input[i]); + } + if (allowed) { + output[j++] = input[i++]; + continue; + } + + switch(input[i]) { + case '\t': + output[j++] = '\\'; + output[j++] = '0'; + output[j++] = '9'; + break; + case ' ': + output[j++] = '\\'; + output[j++] = '2'; + output[j++] = '0'; + break; + case '*': + output[j++] = '\\'; + output[j++] = '2'; + output[j++] = 'a'; + break; + case '(': + output[j++] = '\\'; + output[j++] = '2'; + output[j++] = '8'; + break; + case ')': + output[j++] = '\\'; + output[j++] = '2'; + output[j++] = '9'; + break; + case '\\': + output[j++] = '\\'; + output[j++] = '5'; + output[j++] = 'c'; + break; + case '\r': + output[j++] = '\\'; + output[j++] = '0'; + output[j++] = 'd'; + break; + case '\n': + output[j++] = '\\'; + output[j++] = '0'; + output[j++] = 'a'; + break; + default: + output[j++] = input[i]; + } + + i++; + } + output[j] = '\0'; + *sanitized = talloc_realloc(mem_ctx, output, char, j+1); + if (!*sanitized) { + talloc_free(output); + return ENOMEM; + } + + return EOK; +} + +errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx, + const char *input, + char **sanitized) +{ + return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL); +} -- 2.37.3