Blame SOURCES/0039-certmap-sanitize-LDAP-search-filter.patch

1bb595
From a2b9a84460429181f2a4fa7e2bb5ab49fd561274 Mon Sep 17 00:00:00 2001
1bb595
From: Sumit Bose <sbose@redhat.com>
1bb595
Date: Mon, 9 Dec 2019 11:31:14 +0100
1bb595
Subject: [PATCH] certmap: sanitize LDAP search filter
1bb595
1bb595
The sss_certmap_get_search_filter() will now sanitize the values read
1bb595
from the certificates before adding them to a search filter. To be able
1bb595
to get the plain values as well sss_certmap_expand_mapping_rule() is
1bb595
added.
1bb595
1bb595
Resolves:
1bb595
https://github.com/SSSD/sssd/issues/5135
1bb595
1bb595
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
1bb595
---
1bb595
 Makefile.am                         |  2 +-
1bb595
 src/lib/certmap/sss_certmap.c       | 42 ++++++++++--
1bb595
 src/lib/certmap/sss_certmap.exports |  5 ++
1bb595
 src/lib/certmap/sss_certmap.h       | 35 ++++++++--
1bb595
 src/responder/pam/pamsrv_p11.c      |  5 +-
1bb595
 src/tests/cmocka/test_certmap.c     | 98 +++++++++++++++++++++++++++-
1bb595
 src/util/util.c                     | 94 ---------------------------
1bb595
 src/util/util_ext.c                 | 99 +++++++++++++++++++++++++++++
1bb595
 8 files changed, 272 insertions(+), 108 deletions(-)
1bb595
1bb595
diff --git a/Makefile.am b/Makefile.am
1bb595
index 059e1eaf6..4bacabdda 100644
1bb595
--- a/Makefile.am
1bb595
+++ b/Makefile.am
1bb595
@@ -2163,7 +2163,7 @@ libsss_certmap_la_LIBADD = \
1bb595
     $(NULL)
1bb595
 libsss_certmap_la_LDFLAGS = \
1bb595
     -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \
1bb595
-    -version-info 1:0:1
1bb595
+    -version-info 2:0:2
1bb595
 
1bb595
 if HAVE_NSS
1bb595
 libsss_certmap_la_SOURCES += \
1bb595
diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c
1bb595
index 703782b53..f19e57732 100644
1bb595
--- a/src/lib/certmap/sss_certmap.c
1bb595
+++ b/src/lib/certmap/sss_certmap.c
1bb595
@@ -441,10 +441,12 @@ static int expand_san(struct sss_certmap_ctx *ctx,
1bb595
 static int expand_template(struct sss_certmap_ctx *ctx,
1bb595
                            struct parsed_template *parsed_template,
1bb595
                            struct sss_cert_content *cert_content,
1bb595
+                           bool sanitize,
1bb595
                            char **expanded)
1bb595
 {
1bb595
     int ret;
1bb595
     char *exp = NULL;
1bb595
+    char *exp_sanitized = NULL;
1bb595
 
1bb595
     if (strcmp("issuer_dn", parsed_template->name) == 0) {
1bb595
         ret = rdn_list_2_dn_str(ctx, parsed_template->conversion,
1bb595
@@ -455,6 +457,8 @@ static int expand_template(struct sss_certmap_ctx *ctx,
1bb595
     } else if (strncmp("subject_", parsed_template->name, 8) == 0) {
1bb595
         ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp);
1bb595
     } else if (strcmp("cert", parsed_template->name) == 0) {
1bb595
+        /* cert blob is already sanitized */
1bb595
+        sanitize = false;
1bb595
         ret = expand_cert(ctx, parsed_template, cert_content, &exp);
1bb595
     } else {
1bb595
         CM_DEBUG(ctx, "Unsupported template name.");
1bb595
@@ -471,6 +475,16 @@ static int expand_template(struct sss_certmap_ctx *ctx,
1bb595
         goto done;
1bb595
     }
1bb595
 
1bb595
+    if (sanitize) {
1bb595
+        ret = sss_filter_sanitize(ctx, exp, &exp_sanitized);
1bb595
+        if (ret != EOK) {
1bb595
+            CM_DEBUG(ctx, "Failed to sanitize expanded template.");
1bb595
+            goto done;
1bb595
+        }
1bb595
+        talloc_free(exp);
1bb595
+        exp = exp_sanitized;
1bb595
+    }
1bb595
+
1bb595
     ret = 0;
1bb595
 
1bb595
 done:
1bb595
@@ -485,7 +499,7 @@ done:
1bb595
 
1bb595
 static int get_filter(struct sss_certmap_ctx *ctx,
1bb595
                       struct ldap_mapping_rule *parsed_mapping_rule,
1bb595
-                      struct sss_cert_content *cert_content,
1bb595
+                      struct sss_cert_content *cert_content, bool sanitize,
1bb595
                       char **filter)
1bb595
 {
1bb595
     struct ldap_mapping_rule_comp *comp;
1bb595
@@ -503,7 +517,7 @@ static int get_filter(struct sss_certmap_ctx *ctx,
1bb595
             result = talloc_strdup_append(result, comp->val);
1bb595
         } else if (comp->type == comp_template) {
1bb595
             ret = expand_template(ctx, comp->parsed_template, cert_content,
1bb595
-                                  &expanded);
1bb595
+                                  sanitize, &expanded);
1bb595
             if (ret != 0) {
1bb595
                 CM_DEBUG(ctx, "Failed to expanded template.");
1bb595
                 goto done;
1bb595
@@ -791,8 +805,9 @@ done:
1bb595
     return ret;
1bb595
 }
1bb595
 
1bb595
-int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
1bb595
+static int expand_mapping_rule_ex(struct sss_certmap_ctx *ctx,
1bb595
                                   const uint8_t *der_cert, size_t der_size,
1bb595
+                                  bool sanitize,
1bb595
                                   char **_filter, char ***_domains)
1bb595
 {
1bb595
     int ret;
1bb595
@@ -819,7 +834,8 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
1bb595
             return EINVAL;
1bb595
         }
1bb595
 
1bb595
-        ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, &filter);
1bb595
+        ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, sanitize,
1bb595
+                         &filter);
1bb595
         goto done;
1bb595
     }
1bb595
 
1bb595
@@ -829,7 +845,7 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
1bb595
             if (ret == 0) {
1bb595
                 /* match */
1bb595
                 ret = get_filter(ctx, r->parsed_mapping_rule, cert_content,
1bb595
-                                 &filter);
1bb595
+                                 sanitize, &filter);
1bb595
                 if (ret != 0) {
1bb595
                     CM_DEBUG(ctx, "Failed to get filter");
1bb595
                     goto done;
1bb595
@@ -873,6 +889,22 @@ done:
1bb595
     return ret;
1bb595
 }
1bb595
 
1bb595
+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
1bb595
+                                  const uint8_t *der_cert, size_t der_size,
1bb595
+                                  char **_filter, char ***_domains)
1bb595
+{
1bb595
+    return expand_mapping_rule_ex(ctx, der_cert, der_size, true,
1bb595
+                                  _filter, _domains);
1bb595
+}
1bb595
+
1bb595
+int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx,
1bb595
+                                    const uint8_t *der_cert, size_t der_size,
1bb595
+                                    char **_expanded, char ***_domains)
1bb595
+{
1bb595
+    return expand_mapping_rule_ex(ctx, der_cert, der_size, false,
1bb595
+                                  _expanded, _domains);
1bb595
+}
1bb595
+
1bb595
 int sss_certmap_init(TALLOC_CTX *mem_ctx,
1bb595
                      sss_certmap_ext_debug *debug, void *debug_priv,
1bb595
                      struct sss_certmap_ctx **ctx)
1bb595
diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports
1bb595
index a9e48d6d0..7d7667738 100644
1bb595
--- a/src/lib/certmap/sss_certmap.exports
1bb595
+++ b/src/lib/certmap/sss_certmap.exports
1bb595
@@ -16,3 +16,8 @@ SSS_CERTMAP_0.1 {
1bb595
     global:
1bb595
         sss_certmap_display_cert_content;
1bb595
 } SSS_CERTMAP_0.0;
1bb595
+
1bb595
+SSS_CERTMAP_0.2 {
1bb595
+    global:
1bb595
+        sss_certmap_expand_mapping_rule;
1bb595
+} SSS_CERTMAP_0.1;
1bb595
diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h
1bb595
index 7da2d1c58..058d4f9e4 100644
1bb595
--- a/src/lib/certmap/sss_certmap.h
1bb595
+++ b/src/lib/certmap/sss_certmap.h
1bb595
@@ -103,7 +103,7 @@ int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
1bb595
  *
1bb595
  * @param[in] ctx      certmap context previously initialized with
1bb595
  *                     @ref sss_certmap_init
1bb595
- * @param[in] der_cert binary blog with the DER encoded certificate
1bb595
+ * @param[in] der_cert binary blob with the DER encoded certificate
1bb595
  * @param[in] der_size size of the certificate blob
1bb595
  *
1bb595
  * @return
1bb595
@@ -119,10 +119,11 @@ int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
1bb595
  *
1bb595
  * @param[in] ctx      certmap context previously initialized with
1bb595
  *                     @ref sss_certmap_init
1bb595
- * @param[in] der_cert binary blog with the DER encoded certificate
1bb595
+ * @param[in] der_cert binary blob with the DER encoded certificate
1bb595
  * @param[in] der_size size of the certificate blob
1bb595
- * @param[out] filter  LDAP filter string, caller should free the data by
1bb595
- *                     calling sss_certmap_free_filter_and_domains
1bb595
+ * @param[out] filter  LDAP filter string, expanded templates are sanitized,
1bb595
+ *                     caller should free the data by calling
1bb595
+ *                     sss_certmap_free_filter_and_domains
1bb595
  * @param[out] domains NULL-terminated array of strings with the domains the
1bb595
  *                     rule applies, caller should free the data by calling
1bb595
  *                     sss_certmap_free_filter_and_domains
1bb595
@@ -136,8 +137,32 @@ int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
1bb595
                                   const uint8_t *der_cert, size_t der_size,
1bb595
                                   char **filter, char ***domains);
1bb595
 
1bb595
+/**
1bb595
+ * @brief Expand the mapping rule by replacing the templates
1bb595
+ *
1bb595
+ * @param[in] ctx        certmap context previously initialized with
1bb595
+ *                       @ref sss_certmap_init
1bb595
+ * @param[in] der_cert   binary blob with the DER encoded certificate
1bb595
+ * @param[in] der_size   size of the certificate blob
1bb595
+ * @param[out] expanded  expanded mapping rule, templates are filled in
1bb595
+ *                       verbatim in contrast to sss_certmap_get_search_filter,
1bb595
+ *                       caller should free the data by
1bb595
+ *                       calling sss_certmap_free_filter_and_domains
1bb595
+ * @param[out] domains   NULL-terminated array of strings with the domains the
1bb595
+ *                       rule applies, caller should free the data by calling
1bb595
+ *                       sss_certmap_free_filter_and_domains
1bb595
+ *
1bb595
+ * @return
1bb595
+ *  - 0:      certificate matches a rule
1bb595
+ *  - ENOENT: certificate does not match
1bb595
+ *  - EINVAL: internal error
1bb595
+ */
1bb595
+int sss_certmap_expand_mapping_rule(struct sss_certmap_ctx *ctx,
1bb595
+                                    const uint8_t *der_cert, size_t der_size,
1bb595
+                                    char **_expanded, char ***_domains);
1bb595
 /**
1bb595
  * @brief Free data returned by @ref sss_certmap_get_search_filter
1bb595
+ *        and @ref sss_certmap_expand_mapping_rule
1bb595
  *
1bb595
  * @param[in] filter  LDAP filter strings returned by
1bb595
  *                    sss_certmap_get_search_filter
1bb595
@@ -150,7 +175,7 @@ void sss_certmap_free_filter_and_domains(char *filter, char **domains);
1bb595
  * @brief Get a string with the content of the certificate used by the library
1bb595
  *
1bb595
  * @param[in]  mem_ctx    Talloc memory context, may be NULL
1bb595
- * @param[in]  der_cert   binary blog with the DER encoded certificate
1bb595
+ * @param[in]  der_cert   binary blob with the DER encoded certificate
1bb595
  * @param[in]  der_size   size of the certificate blob
1bb595
  * @param[out] desc       Multiline string showing the certificate content
1bb595
  *                        which is used by libsss_certmap
1bb595
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
1bb595
index 3f0afaeff..cdf239e07 100644
1bb595
--- a/src/responder/pam/pamsrv_p11.c
1bb595
+++ b/src/responder/pam/pamsrv_p11.c
1bb595
@@ -1049,9 +1049,10 @@ static char *get_cert_prompt(TALLOC_CTX *mem_ctx,
1bb595
         goto done;
1bb595
     }
1bb595
 
1bb595
-    ret = sss_certmap_get_search_filter(ctx, der, der_size, &filter, &domains);
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, der, der_size,
1bb595
+                                          &filter, &domains);
1bb595
     if (ret != 0) {
1bb595
-        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_get_search_filter failed.\n");
1bb595
+        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_expand_mapping_rule failed.\n");
1bb595
         goto done;
1bb595
     }
1bb595
 
1bb595
diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
1bb595
index c882202a0..232ff7878 100644
1bb595
--- a/src/tests/cmocka/test_certmap.c
1bb595
+++ b/src/tests/cmocka/test_certmap.c
1bb595
@@ -1431,6 +1431,15 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule100=CN=Certificate\\20Authority,O=IPA.DEVEL"
1bb595
+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
+    assert_null(domains);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule100=CN=Certificate Authority,O=IPA.DEVEL"
1bb595
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
     assert_null(domains);
1bb595
@@ -1445,6 +1454,17 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule99=CN=Certificate\\20Authority,O=IPA.DEVEL"
1bb595
+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
+    assert_non_null(domains);
1bb595
+    assert_string_equal(domains[0], "test.dom");
1bb595
+    assert_null(domains[1]);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule99=CN=Certificate Authority,O=IPA.DEVEL"
1bb595
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
     assert_non_null(domains);
1bb595
@@ -1466,6 +1486,16 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
     assert_string_equal(domains[0], "test.dom");
1bb595
     assert_null(domains[1]);
1bb595
 
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN);
1bb595
+    assert_non_null(domains);
1bb595
+    assert_string_equal(domains[0], "test.dom");
1bb595
+    assert_null(domains[1]);
1bb595
+
1bb595
     ret = sss_certmap_add_rule(ctx, 97,
1bb595
                             "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
1bb595
                             "LDAP:rule97={issuer_dn!nss_x500}<S>{subject_dn}",
1bb595
@@ -1476,6 +1506,17 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule97=O=IPA.DEVEL,CN=Certificate\\20Authority"
1bb595
+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
+    assert_non_null(domains);
1bb595
+    assert_string_equal(domains[0], "test.dom");
1bb595
+    assert_null(domains[1]);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule97=O=IPA.DEVEL,CN=Certificate Authority"
1bb595
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
1bb595
     assert_non_null(domains);
1bb595
@@ -1492,6 +1533,17 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule96=O=IPA.DEVEL,CN=Certificate\\20Authority"
1bb595
+                                "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
1bb595
+    assert_non_null(domains);
1bb595
+    assert_string_equal(domains[0], "test.dom");
1bb595
+    assert_null(domains[1]);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule96=O=IPA.DEVEL,CN=Certificate Authority"
1bb595
                                 "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
1bb595
     assert_non_null(domains);
1bb595
@@ -1510,6 +1562,14 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
     assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")");
1bb595
     assert_null(domains);
1bb595
 
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")");
1bb595
+    assert_null(domains);
1bb595
+
1bb595
     ret = sss_certmap_add_rule(ctx, 94,
1bb595
                       "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
1bb595
                       "LDAP:rule94={issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
1bb595
@@ -1520,12 +1580,22 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
-    assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate Authority"
1bb595
+    assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate\\20Authority"
1bb595
                                 "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
1bb595
     assert_non_null(domains);
1bb595
     assert_string_equal(domains[0], "test.dom");
1bb595
     assert_null(domains[1]);
1bb595
 
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der),
1bb595
+                                          sizeof(test_cert_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate Authority"
1bb595
+                                "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
1bb595
+    assert_non_null(domains);
1bb595
+    assert_string_equal(domains[0], "test.dom");
1bb595
+    assert_null(domains[1]);
1bb595
 
1bb595
     ret = sss_certmap_add_rule(ctx, 89, NULL,
1bb595
                             "(rule89={subject_nt_principal})",
1bb595
@@ -1539,6 +1609,14 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
     assert_string_equal(filter, "(rule89=tu1@ad.devel)");
1bb595
     assert_null(domains);
1bb595
 
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der),
1bb595
+                                          sizeof(test_cert2_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "(rule89=tu1@ad.devel)");
1bb595
+    assert_null(domains);
1bb595
+
1bb595
     ret = sss_certmap_add_rule(ctx, 88, NULL,
1bb595
                             "(rule88={subject_nt_principal.short_name})",
1bb595
                             NULL);
1bb595
@@ -1560,6 +1638,15 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule87=DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
1bb595
+                  "<S>DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain");
1bb595
+    assert_null(domains);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der),
1bb595
+                                          sizeof(test_cert2_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule87=DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
1bb595
                   "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
1bb595
     assert_null(domains);
1bb595
@@ -1573,6 +1660,15 @@ static void test_sss_certmap_get_search_filter(void **state)
1bb595
                                         &filter, &domains);
1bb595
     assert_int_equal(ret, 0);
1bb595
     assert_non_null(filter);
1bb595
+    assert_string_equal(filter, "rule86=DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
1bb595
+                  "<S>DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain");
1bb595
+    assert_null(domains);
1bb595
+
1bb595
+    ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der),
1bb595
+                                          sizeof(test_cert2_der),
1bb595
+                                          &filter, &domains);
1bb595
+    assert_int_equal(ret, 0);
1bb595
+    assert_non_null(filter);
1bb595
     assert_string_equal(filter, "rule86=DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
1bb595
                   "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
1bb595
     assert_null(domains);
1bb595
diff --git a/src/util/util.c b/src/util/util.c
1bb595
index d9bd3cb59..19d447328 100644
1bb595
--- a/src/util/util.c
1bb595
+++ b/src/util/util.c
1bb595
@@ -436,100 +436,6 @@ errno_t sss_hash_create(TALLOC_CTX *mem_ctx, unsigned long count,
1bb595
     return sss_hash_create_ex(mem_ctx, count, tbl, 0, 0, 0, 0, NULL, NULL);
1bb595
 }
1bb595
 
1bb595
-errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx,
1bb595
-                               const char *input,
1bb595
-                               char **sanitized,
1bb595
-                               const char *ignore)
1bb595
-{
1bb595
-    char *output;
1bb595
-    size_t i = 0;
1bb595
-    size_t j = 0;
1bb595
-    char *allowed;
1bb595
-
1bb595
-    /* Assume the worst-case. We'll resize it later, once */
1bb595
-    output = talloc_array(mem_ctx, char, strlen(input) * 3 + 1);
1bb595
-    if (!output) {
1bb595
-        return ENOMEM;
1bb595
-    }
1bb595
-
1bb595
-    while (input[i]) {
1bb595
-        /* Even though this character might have a special meaning, if it's
1bb595
-         * explicitly allowed, just copy it and move on
1bb595
-         */
1bb595
-        if (ignore == NULL) {
1bb595
-            allowed = NULL;
1bb595
-        } else {
1bb595
-            allowed = strchr(ignore, input[i]);
1bb595
-        }
1bb595
-        if (allowed) {
1bb595
-            output[j++] = input[i++];
1bb595
-            continue;
1bb595
-        }
1bb595
-
1bb595
-        switch(input[i]) {
1bb595
-        case '\t':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '0';
1bb595
-            output[j++] = '9';
1bb595
-            break;
1bb595
-        case ' ':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '2';
1bb595
-            output[j++] = '0';
1bb595
-            break;
1bb595
-        case '*':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '2';
1bb595
-            output[j++] = 'a';
1bb595
-            break;
1bb595
-        case '(':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '2';
1bb595
-            output[j++] = '8';
1bb595
-            break;
1bb595
-        case ')':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '2';
1bb595
-            output[j++] = '9';
1bb595
-            break;
1bb595
-        case '\\':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '5';
1bb595
-            output[j++] = 'c';
1bb595
-            break;
1bb595
-        case '\r':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '0';
1bb595
-            output[j++] = 'd';
1bb595
-            break;
1bb595
-        case '\n':
1bb595
-            output[j++] = '\\';
1bb595
-            output[j++] = '0';
1bb595
-            output[j++] = 'a';
1bb595
-            break;
1bb595
-        default:
1bb595
-            output[j++] = input[i];
1bb595
-        }
1bb595
-
1bb595
-        i++;
1bb595
-    }
1bb595
-    output[j] = '\0';
1bb595
-    *sanitized = talloc_realloc(mem_ctx, output, char, j+1);
1bb595
-    if (!*sanitized) {
1bb595
-        talloc_free(output);
1bb595
-        return ENOMEM;
1bb595
-    }
1bb595
-
1bb595
-    return EOK;
1bb595
-}
1bb595
-
1bb595
-errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
1bb595
-                            const char *input,
1bb595
-                            char **sanitized)
1bb595
-{
1bb595
-    return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL);
1bb595
-}
1bb595
-
1bb595
 char *
1bb595
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr)
1bb595
 {
1bb595
diff --git a/src/util/util_ext.c b/src/util/util_ext.c
1bb595
index 04dc02a8a..a89b60f76 100644
1bb595
--- a/src/util/util_ext.c
1bb595
+++ b/src/util/util_ext.c
1bb595
@@ -29,6 +29,11 @@
1bb595
 
1bb595
 #define EOK 0
1bb595
 
1bb595
+#ifndef HAVE_ERRNO_T
1bb595
+#define HAVE_ERRNO_T
1bb595
+typedef int errno_t;
1bb595
+#endif
1bb595
+
1bb595
 int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
1bb595
                        const char sep, bool trim, bool skip_empty,
1bb595
                        char ***_list, int *size)
1bb595
@@ -141,3 +146,97 @@ bool string_in_list(const char *string, char **list, bool case_sensitive)
1bb595
 
1bb595
     return false;
1bb595
 }
1bb595
+
1bb595
+errno_t sss_filter_sanitize_ex(TALLOC_CTX *mem_ctx,
1bb595
+                               const char *input,
1bb595
+                               char **sanitized,
1bb595
+                               const char *ignore)
1bb595
+{
1bb595
+    char *output;
1bb595
+    size_t i = 0;
1bb595
+    size_t j = 0;
1bb595
+    char *allowed;
1bb595
+
1bb595
+    /* Assume the worst-case. We'll resize it later, once */
1bb595
+    output = talloc_array(mem_ctx, char, strlen(input) * 3 + 1);
1bb595
+    if (!output) {
1bb595
+        return ENOMEM;
1bb595
+    }
1bb595
+
1bb595
+    while (input[i]) {
1bb595
+        /* Even though this character might have a special meaning, if it's
1bb595
+         * explicitly allowed, just copy it and move on
1bb595
+         */
1bb595
+        if (ignore == NULL) {
1bb595
+            allowed = NULL;
1bb595
+        } else {
1bb595
+            allowed = strchr(ignore, input[i]);
1bb595
+        }
1bb595
+        if (allowed) {
1bb595
+            output[j++] = input[i++];
1bb595
+            continue;
1bb595
+        }
1bb595
+
1bb595
+        switch(input[i]) {
1bb595
+        case '\t':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '0';
1bb595
+            output[j++] = '9';
1bb595
+            break;
1bb595
+        case ' ':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '2';
1bb595
+            output[j++] = '0';
1bb595
+            break;
1bb595
+        case '*':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '2';
1bb595
+            output[j++] = 'a';
1bb595
+            break;
1bb595
+        case '(':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '2';
1bb595
+            output[j++] = '8';
1bb595
+            break;
1bb595
+        case ')':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '2';
1bb595
+            output[j++] = '9';
1bb595
+            break;
1bb595
+        case '\\':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '5';
1bb595
+            output[j++] = 'c';
1bb595
+            break;
1bb595
+        case '\r':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '0';
1bb595
+            output[j++] = 'd';
1bb595
+            break;
1bb595
+        case '\n':
1bb595
+            output[j++] = '\\';
1bb595
+            output[j++] = '0';
1bb595
+            output[j++] = 'a';
1bb595
+            break;
1bb595
+        default:
1bb595
+            output[j++] = input[i];
1bb595
+        }
1bb595
+
1bb595
+        i++;
1bb595
+    }
1bb595
+    output[j] = '\0';
1bb595
+    *sanitized = talloc_realloc(mem_ctx, output, char, j+1);
1bb595
+    if (!*sanitized) {
1bb595
+        talloc_free(output);
1bb595
+        return ENOMEM;
1bb595
+    }
1bb595
+
1bb595
+    return EOK;
1bb595
+}
1bb595
+
1bb595
+errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
1bb595
+                            const char *input,
1bb595
+                            char **sanitized)
1bb595
+{
1bb595
+    return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL);
1bb595
+}
1bb595
-- 
1bb595
2.21.3
1bb595