From 6a935cfa0f43ce845e73e9d1a5f7dc823a0d4b8a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Thu, 14 Feb 2019 18:35:49 +0100
Subject: [PATCH 1/8] TESTS: replace hardcoded certificates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Since the hardcoded certificates have a limited lifetime they are
replaces by certificates from the test CA.

Related to https://pagure.io/SSSD/sssd/issue/3436

Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
---
 src/tests/cmocka/test_cert_utils.c |  41 ++++--------
 src/tests/cmocka/test_pam_srv.c    | 104 +++++++++++------------------
 2 files changed, 50 insertions(+), 95 deletions(-)

diff --git a/src/tests/cmocka/test_cert_utils.c b/src/tests/cmocka/test_cert_utils.c
index f50030e49..dd58b73a7 100644
--- a/src/tests/cmocka/test_cert_utils.c
+++ b/src/tests/cmocka/test_cert_utils.c
@@ -34,6 +34,13 @@
 #include "util/crypto/nss/nss_util.h"
 #include "util/crypto/sss_crypto.h"
 
+#ifdef HAVE_TEST_CA
+#include "tests/test_CA/SSSD_test_cert_pubsshkey_0001.h"
+#include "tests/test_CA/SSSD_test_cert_x509_0001.h"
+#else
+#define SSSD_TEST_CERT_0001 ""
+#define SSSD_TEST_CERT_SSH_KEY_0001 ""
+#endif
 
 /* TODO: create a certificate for this test */
 const uint8_t test_cert_der[] = {
@@ -325,32 +332,6 @@ void test_sss_cert_derb64_to_ldap_filter(void **state)
     talloc_free(filter);
 }
 
-#define SSH_TEST_CERT \
-"MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
-"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
-"NDEzNDlaFw0xODA1MjQxNDEzNDlaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \
-"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \
-"ADCCAQoCggEBALfEAE0IUlOAgDTdZQGcYA03IPooixNnkUQruh0eU3uw+KYGQoS1" \
-"YCdCHJzRc+IfuqdNntgtGDIpWADRwB4h963pBImpMSU5L1T4uiHNCpvl9eMt4ynk" \
-"xduOa+JmJUvqvwe7Gj9iDql4lWmJcXvq74/yOc3MBSPQCdg/pHZU65+NjSZmZzlN" \
-"eNV3tQKrhMe6tM00pai2igXilfUpzOU2v+AX69oOesrqTUl9i2eCUirGanR9l95d" \
-"yVCcmIDJd2P2NLIkhbHGRitfTC/tQZ4G+Edg9STw8Y+4ljp2rTHs59dWRBe2Gn8Z" \
-"Zt8zZ5WuNxARVF1THI9X6ydX/uoaz8R7pfkCAwEAAaOCASYwggEiMB8GA1UdIwQY" \
-"MBaAFPci/0Km5D/L5z7YqwEc7E1/GwgcMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \
-"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \
-"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \
-"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \
-"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \
-"IEF1dGhvcml0eTAdBgNVHQ4EFgQUMydoshxYXhDXOMo/EETvrZaQuBwwDQYJKoZI" \
-"hvcNAQELBQADggEBADIrTFNvEdZGna7jD1xpiLGGUwCi11GQT+Txg5B7dydUn5U5" \
-"32zSBBZV6bsy0E+PiiAgehJObv9hBaOWnhp7ltNyQod1OLdI1t988ow2wxHvUEEi" \
-"MhRF0h2RJwdYIUIIF7XC01mKBOFj/84vvMOgLToZnGqVzArkzpr1aCaHI7EoTkpb" \
-"V16v+drZkXc47JuHg5CRjTHV/kFPm63gQ8Fstmw/dQZBzbCiVzmcG0Xm9r4jMOOf" \
-"YjVueMt/jk1LP4KoSCBY6kLMcpL5rQm53hO82rPAgV695rjdPlIUm09dvkCl28ZD" \
-"109Ju18eAaaVFewK82NDg9rsNraBKxMCBSgg0es="
-
-#define SSH_PUB_KEY "AAAAB3NzaC1yc2EAAAADAQABAAABAQC3xABNCFJTgIA03WUBnGANNyD6KIsTZ5FEK7odHlN7sPimBkKEtWAnQhyc0XPiH7qnTZ7YLRgyKVgA0cAeIfet6QSJqTElOS9U+LohzQqb5fXjLeMp5MXbjmviZiVL6r8Huxo/Yg6peJVpiXF76u+P8jnNzAUj0AnYP6R2VOufjY0mZmc5TXjVd7UCq4THurTNNKWotooF4pX1KczlNr/gF+vaDnrK6k1JfYtnglIqxmp0fZfeXclQnJiAyXdj9jSyJIWxxkYrX0wv7UGeBvhHYPUk8PGPuJY6dq0x7OfXVkQXthp/GWbfM2eVrjcQEVRdUxyPV+snV/7qGs/Ee6X5"
-
 void test_cert_to_ssh_key(void **state)
 {
     int ret;
@@ -366,13 +347,13 @@ void test_cert_to_ssh_key(void **state)
     struct test_state *ts = talloc_get_type_abort(*state, struct test_state);
     assert_non_null(ts);
 
-    der = sss_base64_decode(ts, SSH_TEST_CERT, &der_size);
+    der = sss_base64_decode(ts, SSSD_TEST_CERT_0001, &der_size);
     assert_non_null(der);
 
-    exp_key = sss_base64_decode(ts, SSH_PUB_KEY, &exp_key_size);
+    exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0001, &exp_key_size);
     assert_non_null(exp_key);
 
-    ret = cert_to_ssh_key(ts, "sql:" ABS_SRC_DIR "/src/tests/cmocka/p11_nssdb",
+    ret = cert_to_ssh_key(ts, "sql:" ABS_BUILD_DIR "/src/tests/test_CA/p11_nssdb",
                           der, der_size, &cert_verify_opts, &key, &key_size);
     assert_int_equal(ret, EOK);
     assert_int_equal(key_size, exp_key_size);
@@ -407,8 +388,10 @@ int main(int argc, const char *argv[])
                                         setup, teardown),
         cmocka_unit_test_setup_teardown(test_sss_cert_derb64_to_ldap_filter,
                                         setup, teardown),
+#ifdef HAVE_TEST_CA
         cmocka_unit_test_setup_teardown(test_cert_to_ssh_key,
                                         setup, teardown),
+#endif
     };
 
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index c510c2d3b..e68e81f97 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -38,6 +38,14 @@
 #include "util/crypto/nss/nss_util.h"
 #endif
 
+#ifdef HAVE_TEST_CA
+#include "tests/test_CA/SSSD_test_cert_x509_0001.h"
+#include "tests/test_CA/SSSD_test_cert_x509_0002.h"
+#else
+#define SSSD_TEST_CERT_0001 ""
+#define SSSD_TEST_CERT_0002 ""
+#endif
+
 #define TESTS_PATH "tp_" BASE_FILE_STEM
 #define TEST_CONF_DB "test_pam_conf.ldb"
 #define TEST_DOM_NAME "pam_test"
@@ -52,55 +60,11 @@
 
 #define TEST_TOKEN_NAME "SSSD Test Token"
 #define TEST_MODULE_NAME "NSS-Internal"
-#define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7"
-#define TEST_PROMPT "Server-Cert\nCN=ipa-devel.ipa.devel,O=IPA.DEVEL"
-#define TEST_TOKEN_CERT \
-"MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
-"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
-"NDE0MTVaFw0xODA1MjQxNDE0MTVaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \
-"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \
-"ADCCAQoCggEBALHvOzZy/3llvoAYxrtOpux0gDVvSuSRpTGOW/bjpgdTowvXoOb5" \
-"G9Cy/9S6be7ZJ9D95lc/J9W8tX+ShKN8Q4b74l4WjmILQJ4dUsJ/BXfvoMPR8tw/" \
-"G47dGbLZanMXdWGBSTuXhoiogZWib2DhSwrX2DbEH5L3OWooeAVU5ZWOw55/HD7O" \
-"Q/7Of7H3tf4bvxNTFkxh39KQMG28wjPZSv+SZWNHMB+rj2yZgyeHBMkoPOPesAEi" \
-"7KKHxw1MHSv2xBI1AiV+aMdKfYUMy0Rq3PrRU4274i3eaBX4Q9GnDi36K/7bHjbt" \
-"LW0YTIW/L5/cH/BO88BREjxS3bEXAQqlKOcCAwEAAaOCASYwggEiMB8GA1UdIwQY" \
-"MBaAFPci/0Km5D/L5z7YqwEc7E1/GwgcMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \
-"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \
-"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \
-"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \
-"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \
-"IEF1dGhvcml0eTAdBgNVHQ4EFgQUIJuWIts3m3uEYqJ9pUL0y7utTiEwDQYJKoZI" \
-"hvcNAQELBQADggEBAB0GyqGxtZ99fsXA1+fHfAwKOwznT7Hh8hN9efEMBJICVud+" \
-"ivUBOH6JpSTWgNLuBhrpebV/b/DSjhn+ayuvoPWng3hjwMbSEIe0euzCEdwVcokt" \
-"bwNMMSeTxSg6wbJnEyZqQEIr2h/TR9dRNxE+RbQXyamW0fUxSVT16iueL0hMwszT" \
-"jCfI/UZv3tDMHbh6D4811A0HO8daW7ufMGb/M+kDxYigJiL2gllMZ+6xba1RRgzF" \
-"8Z+9gqZhCa7FEKJOPNR9RVtJs0qUUutMZrp1zpyx0GTmXQBA7LbgPxy8L68uymEQ" \
-"XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g="
-
-#define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C"
-#define TEST2_PROMPT "ipaCert\nCN=IPA RA,O=IPA.DEVEL"
-#define TEST_TOKEN_2ND_CERT \
-"MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
-"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
-"NDEzMDFaFw0xODA1MTMxNDEzMDFaMCUxEjAQBgNVBAoMCUlQQS5ERVZFTDEPMA0G" \
-"A1UEAwwGSVBBIFJBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3abE" \
-"8LmIc6QN16VVxsMlN/rrCOoZKyyJolSzpP4+K66t+KZUiW/1j1MZogjyYyD39U1F" \
-"zpa2H+pID74XYrdiqP7sp+uE9/k2XOv/nN3FobXDt+fSINLDriCmxNhUZqpgo2uq" \
-"Mmka+yx2iJZwkntEoJTcd3aynoa2Sa2ZZbkMBy5p6/pUQKwnD6scOwe6mUDppIBK" \
-"+ZZRm+u/NDdIRFI5wfKLRR1r/ONaJA9nz1TxSEsgLsjG/1m+Zbb6lGG4pePIFkQ9" \
-"Iotpi64obBh93oIxzQR29lBG/FMjQVHlPIbx+xuGx11Vtp5pAomgFz0HRrj0leI7" \
-"bROE+jnC/VGPLQD2aQIDAQABo4GWMIGTMB8GA1UdIwQYMBaAFPci/0Km5D/L5z7Y" \
-"qwEc7E1/GwgcMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAYYlaHR0cDovL2lw" \
-"YS1kZXZlbC5pcGEuZGV2ZWw6ODAvY2Evb2NzcDAOBgNVHQ8BAf8EBAMCBPAwHQYD" \
-"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBg" \
-"4Sppx2C3eXPJ4Pd9XElkQPOaBReXf1vV0uk/GlK+rG+aAqAkA2Lryx5PK/iAuzAU" \
-"M6JUpELuQYgqugoCgBXMgsMlpAO/0C3CFq4ZH3KgIsRlRngKPrt6RG0UPMRD1CE2" \
-"tSVkwUWvyK83lDiu2BbWDXyMyz5eZOlp7uHusf5BKvob8jEndHj1YzaNTmVSsDM5" \
-"kiIwf8qgFhsO1HCq08PtAnbVHhqkcvnmIJN98eNWNfTKodDmFVbN8gB0wK+WB5ii" \
-"WVOw7+3/zF1QgqnYX3t+kPLRryip/wvTZkzXWwMNj/W6UHgjNF/4gWGoBgCHu+u3" \
-"EvjMmbVSrEkesibpGQS5"
+#define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17"
+#define TEST_PROMPT "SSSD test cert 0001 - SSSD\nCN=SSSD test cert 0001,OU=SSSD test,O=SSSD"
 
+#define TEST2_KEY_ID "5405842D56CF31F0BB025A695C5F3E907051C5B9"
+#define TEST2_PROMPT "SSSD test cert 0002 - SSSD\nCN=SSSD test cert 0002,OU=SSSD test,O=SSSD"
 
 static char CACHED_AUTH_TIMEOUT_STR[] = "4";
 static const int CACHED_AUTH_TIMEOUT = 4;
@@ -187,7 +151,7 @@ static errno_t setup_nss_db(void)
         DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
         return ret;
     }
-    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/cmocka/p11_nssdb' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_SRC_DIR);
+    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/test_CA/p11_nssdb' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_BUILD_DIR);
     if (ret < 0) {
         DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
         return ret;
@@ -208,7 +172,7 @@ static errno_t setup_nss_db(void)
         DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
         return ret;
     }
-    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/cmocka/p11_nssdb_2certs' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_SRC_DIR);
+    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/test_CA/p11_nssdb_2certs' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_BUILD_DIR);
     if (ret < 0) {
         DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
         return ret;
@@ -451,6 +415,7 @@ static int pam_test_setup(void **state)
     return 0;
 }
 
+#ifdef HAVE_TEST_CA
 #ifdef HAVE_NSS
 static int pam_test_setup_no_verification(void **state)
 {
@@ -476,6 +441,7 @@ static int pam_test_setup_no_verification(void **state)
     return 0;
 }
 #endif /* HAVE_NSS */
+#endif /* HAVE_TEST_CA */
 
 static int pam_cached_test_setup(void **state)
 {
@@ -1915,6 +1881,7 @@ static int test_lookup_by_cert_cb(void *pvt)
 
     return EOK;
 }
+
 static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt)
 {
     int ret;
@@ -1927,7 +1894,7 @@ static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt)
     attrs = sysdb_new_attrs(pam_test_ctx);
     assert_non_null(attrs);
 
-    der = sss_base64_decode(pam_test_ctx, TEST_TOKEN_2ND_CERT, &der_size);
+    der = sss_base64_decode(pam_test_ctx, SSSD_TEST_CERT_0002, &der_size);
     assert_non_null(der);
 
     ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
@@ -2033,7 +2000,7 @@ void test_pam_preauth_cert_match(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2057,7 +2024,7 @@ void test_pam_preauth_cert_match_gdm_smartcard(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL,
                         "gdm-smartcard", test_lookup_by_cert_cb,
-                        TEST_TOKEN_CERT, false);
+                        SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2080,7 +2047,7 @@ void test_pam_preauth_cert_match_wrong_user(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
                         test_lookup_by_cert_wrong_user_cb,
-                        TEST_TOKEN_CERT, false);
+                        SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2111,7 +2078,7 @@ void test_pam_preauth_cert_no_logon_name(void **state)
      * request will be done with the username found by the certificate
      * lookup. */
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, false);
     mock_account_recv_simple();
     mock_parse_inp("pamuser", NULL, EOK);
 
@@ -2140,7 +2107,7 @@ void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
      * during pre-auth and there is no need for an extra mocked response as in
      * test_pam_preauth_cert_no_logon_name. */
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2162,7 +2129,8 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
 
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001,
+                        false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2185,7 +2153,8 @@ void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
     pam_test_ctx->rctx->domains->user_name_hint = true;
 
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001,
+                        false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2258,8 +2227,8 @@ void test_pam_cert_auth(void **state)
      * in the cache and no second request to the backend is needed. */
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
                         "NSS-Internal",
-                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, true);
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, true);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2292,8 +2261,8 @@ void test_pam_cert_auth_no_logon_name(void **state)
      * in the cache and no second request to the backend is needed. */
     mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
                         "NSS-Internal",
-                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, true);
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, true);
 
     mock_account_recv_simple();
     mock_parse_inp("pamuser", NULL, EOK);
@@ -2354,8 +2323,9 @@ void test_pam_cert_auth_double_cert(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
                         "NSS-Internal",
-                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
-                        test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, true);
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001,
+                        true);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2380,7 +2350,7 @@ void test_pam_cert_preauth_2certs_one_mapping(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2403,7 +2373,7 @@ void test_pam_cert_preauth_2certs_two_mappings(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
                         test_lookup_by_cert_cb_2nd_cert_same_user,
-                        TEST_TOKEN_CERT, false);
+                        SSSD_TEST_CERT_0001, false);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2812,6 +2782,7 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_pam_cached_auth_failed_combined_pw_with_cached_2fa,
                                         pam_cached_test_setup,
                                         pam_test_teardown),
+#ifdef HAVE_TEST_CA
 /* p11_child is not built without NSS */
 #ifdef HAVE_NSS
         cmocka_unit_test_setup_teardown(test_pam_preauth_cert_nocert,
@@ -2856,6 +2827,7 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
                                         pam_test_setup, pam_test_teardown),
 #endif /* HAVE_NSS */
+#endif /* HAVE_TEST_CA */
 
         cmocka_unit_test_setup_teardown(test_filter_response,
                                         pam_test_setup, pam_test_teardown),
-- 
2.39.1


From 08958d81f17569e900cad0489862e7c5fca01092 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thalman@redhat.com>
Date: Fri, 31 Jul 2020 11:12:02 +0200
Subject: [PATCH 2/8] UTIL: DN sanitization

Some of the ldap servers returns DN in attributes such as isMemberOf
with spaces like dc=example, dc=com. That should be fine and we
should ignore them (cut them out) instead of escaping.

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
(cherry picked from commit 093061f553ab0a2c316794221e79779fb1bd40d2)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
---
 src/tests/cmocka/test_utils.c |  69 +++++++++++++++++++
 src/util/util.c               | 126 ++++++++++++++++++++++++++++++++++
 src/util/util.h               |  20 ++++++
 3 files changed, 215 insertions(+)

diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 1a8699a2a..998f50d8a 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -1849,6 +1849,72 @@ static void test_sss_get_domain_mappings_content(void **state)
      * capaths might not be as expected. */
 }
 
+static void test_sss_filter_sanitize_dn(void **state)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *trimmed;
+    int ret;
+    const char *DN = "cn=user,ou=people,dc=example,dc=com";
+
+    tmp_ctx = talloc_new(NULL);
+    assert_non_null(tmp_ctx);
+
+    /* test that we remove spaces around '=' and ','*/
+    ret = sss_filter_sanitize_dn(tmp_ctx, DN, &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,ou=people,dc=example,dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user,ou =people,dc = example,dc  =  com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,  ou=people  ,dc=example  ,   dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user, ou =people ,dc = example  ,  dc  = com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user,ou=people,dc=example,dc=com ", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "  cn=user, ou=people, dc=example, dc=com  ", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    /* test that we keep spaces inside a value */
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=people  branch, dc=example, dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal("cn=user\\20one,ou=people\\20\\20branch,dc=example,dc=com", trimmed);
+    talloc_free(trimmed);
+
+    /* test that we keep escape special chars like () */
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=p(e)ople, dc=example, dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal("cn=user\\20one,ou=p\\28e\\29ople,dc=example,dc=com", trimmed);
+    talloc_free(trimmed);
+
+    talloc_free(tmp_ctx);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -1945,6 +2011,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_sss_get_domain_mappings_content,
                                         setup_dom_list_with_subdomains,
                                         teardown_dom_list),
+        cmocka_unit_test_setup_teardown(test_sss_filter_sanitize_dn,
+                                        setup_leak_tests,
+                                        teardown_leak_tests),
     };
 
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
diff --git a/src/util/util.c b/src/util/util.c
index e3efa7fef..aaf3609c3 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -530,6 +530,132 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
     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
+ */
+static errno_t sss_trim_dn(TALLOC_CTX *mem_ctx,
+                           const char *input,
+                           char **trimmed)
+{
+    int i = 0;
+    int o = 0;
+    int s;
+    char *output;
+    enum sss_trim_dn_state {
+        SSS_TRIM_DN_STATE_READING_NAME,
+        SSS_TRIM_DN_STATE_READING_VALUE
+    } state = SSS_TRIM_DN_STATE_READING_NAME;
+
+    *trimmed = NULL;
+
+    output = talloc_array(mem_ctx, char, strlen(input) + 1);
+    if (!output) {
+        return ENOMEM;
+    }
+
+    /* skip leading spaces */
+    while(isspace(input[i])) {
+        ++i;
+    }
+
+    while(input[i] != '\0') {
+        if (!isspace(input[i])) {
+            switch (input[i]) {
+            case '=':
+                output[o++] = input[i++];
+                if (state == SSS_TRIM_DN_STATE_READING_NAME) {
+                    while (isspace(input[i])) {
+                        ++i;
+                    }
+                    state = SSS_TRIM_DN_STATE_READING_VALUE;
+                }
+                break;
+            case ',':
+                output[o++] = input[i++];
+                if (state == SSS_TRIM_DN_STATE_READING_VALUE) {
+                    while (isspace(input[i])) {
+                        ++i;
+                    }
+                    state = SSS_TRIM_DN_STATE_READING_NAME;
+                }
+                break;
+            case '\\':
+                output[o++] = input[i++];
+                if (input[i] != '\0') {
+                    output[o++] = input[i++];
+                }
+                break;
+            default:
+                if (input[i] != '\0') {
+                    output[o++] = input[i++];
+                }
+                break;
+            }
+
+            continue;
+        }
+
+        /* non escaped space found */
+        s = 1;
+        while (isspace(input[i + s])) {
+            ++s;
+        }
+
+        switch (state) {
+        case SSS_TRIM_DN_STATE_READING_NAME:
+            if (input[i + s] != '=') {
+                /* this is not trailing space - should not be removed */
+                while (isspace(input[i])) {
+                    output[o++] = input[i++];
+                }
+            } else {
+                i += s;
+            }
+            break;
+        case SSS_TRIM_DN_STATE_READING_VALUE:
+            if (input[i + s] != ',') {
+                /* this is not trailing space - should not be removed */
+                while (isspace(input[i])) {
+                    output[o++] = input[i++];
+                }
+            } else {
+                i += s;
+            }
+            break;
+        }
+    }
+
+    output[o--] = '\0';
+
+    /* trim trailing space */
+    while (o >= 0 && isspace(output[o])) {
+        output[o--] = '\0';
+    }
+
+    *trimmed = output;
+    return EOK;
+}
+
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+                               const char *input,
+                               char **sanitized)
+{
+    errno_t ret;
+    char *trimmed_dn = NULL;
+
+    ret = sss_trim_dn(mem_ctx, input, &trimmed_dn);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sss_filter_sanitize_ex(mem_ctx, trimmed_dn, sanitized, NULL);
+
+ done:
+    talloc_free(trimmed_dn);
+    return ret;
+}
+
 char *
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr)
 {
diff --git a/src/util/util.h b/src/util/util.h
index 3bd4bd696..6a976ab2c 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -468,6 +468,26 @@ errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
                                     char **sanitized,
                                     char **lc_sanitized);
 
+/* Sanitize an input string (e.g. a DN) for use in
+ * an LDAP/LDB filter
+ *
+ * It is basically the same as sss_filter_sanitize(_ex),
+ * just extra spaces inside DN around '=' and ',' are removed
+ * before sanitizing other characters . According the documentation
+ * spaces in DN are allowed and some ldap servers can return them
+ * in isMemberOf or member attributes.
+ *
+ * (dc = my example, dc = com => dc=my\20example,dc=com)
+ *
+ * Returns a newly-constructed string attached to mem_ctx
+ * It will fail only on an out of memory condition, where it
+ * will return ENOMEM.
+ *
+ */
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+                               const char *input,
+                               char **sanitized);
+
 char *
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
 
-- 
2.39.1


From c55dfba0d98d7934c1bba88e4c489555edcf517a Mon Sep 17 00:00:00 2001
From: Tomas Halman <thalman@redhat.com>
Date: Fri, 31 Jul 2020 11:21:44 +0200
Subject: [PATCH 3/8] UTIL: Use sss_sanitize_dn where we deal with DN

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
(cherry picked from commit 21b9417e14ce35a2548c309642325ac43103d51e)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
---
 src/db/sysdb_ops.c                         | 2 +-
 src/providers/ipa/ipa_deskprofile_rules.c  | 2 +-
 src/providers/ipa/ipa_hbac_rules.c         | 2 +-
 src/providers/ipa/ipa_netgroups.c          | 2 +-
 src/providers/ldap/sdap_async_groups.c     | 2 +-
 src/providers/ldap/sdap_async_groups_ad.c  | 2 +-
 src/providers/ldap/sdap_async_initgroups.c | 4 ++--
 7 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 7427c8b2e..f7c899080 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -3329,7 +3329,7 @@ errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn);
+    ret = sss_filter_sanitize_dn(tmp_ctx, member_dn, &sanitized_dn);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/providers/ipa/ipa_deskprofile_rules.c b/src/providers/ipa/ipa_deskprofile_rules.c
index 65994356e..cce6184db 100644
--- a/src/providers/ipa/ipa_deskprofile_rules.c
+++ b/src/providers/ipa/ipa_deskprofile_rules.c
@@ -91,7 +91,7 @@ ipa_deskprofile_rule_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
-    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean);
     if (ret != EOK) {
         goto immediate;
     }
diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 0634a277e..e2c97ae3d 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -84,7 +84,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
-    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean);
     if (ret != EOK) goto immediate;
 
     state->ev = ev;
diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c
index 5c929a485..66342edd7 100644
--- a/src/providers/ipa/ipa_netgroups.c
+++ b/src/providers/ipa/ipa_netgroups.c
@@ -376,7 +376,7 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
             continue;
         }
 
-        ret = sss_filter_sanitize(state, orig_dn, &dn);
+        ret = sss_filter_sanitize_dn(state, orig_dn, &dn);
         if (ret != EOK) {
             goto done;
         }
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 77acded7a..0a55795f0 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -53,7 +53,7 @@ static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
         return ENOMEM;
     }
 
-    ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
+    ret = sss_filter_sanitize_dn(tmpctx, orig_dn, &sanitized_dn);
     if (ret != EOK) {
         ret = ENOMEM;
         goto done;
diff --git a/src/providers/ldap/sdap_async_groups_ad.c b/src/providers/ldap/sdap_async_groups_ad.c
index 3f842b26d..c954398bb 100644
--- a/src/providers/ldap/sdap_async_groups_ad.c
+++ b/src/providers/ldap/sdap_async_groups_ad.c
@@ -91,7 +91,7 @@ sdap_get_ad_match_rule_members_send(TALLOC_CTX *mem_ctx,
     }
 
     /* Sanitize it in case we have special characters in DN */
-    ret = sss_filter_sanitize(state, group_dn, &sanitized_group_dn);
+    ret = sss_filter_sanitize_dn(state, group_dn, &sanitized_group_dn);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE,
               "Could not sanitize group DN: %s\n",
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 326294a1c..6e0f42277 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -1634,7 +1634,7 @@ static struct tevent_req *sdap_initgr_rfc2307bis_send(
                                attr_filter, &state->attrs, NULL);
     if (ret != EOK) goto done;
 
-    ret = sss_filter_sanitize(state, orig_dn, &clean_orig_dn);
+    ret = sss_filter_sanitize_dn(state, orig_dn, &clean_orig_dn);
     if (ret != EOK) goto done;
 
     use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
@@ -2416,7 +2416,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
         goto done;
     }
 
-    ret = sss_filter_sanitize(tmp_ctx, state->orig_dn, &clean_orig_dn);
+    ret = sss_filter_sanitize_dn(tmp_ctx, state->orig_dn, &clean_orig_dn);
     if (ret != EOK) {
         goto done;
     }
-- 
2.39.1


From 50bfa6f0f995fa78e63237487a4f76ca67164a37 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thalman@redhat.com>
Date: Wed, 19 Aug 2020 15:17:44 +0200
Subject: [PATCH 4/8] UTIL: Use sss_sanitize_dn where we deal with DN 2

Tests show that also ldb_dn_get_linearized can
return DN with extra spaces. We have to trim that too.

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
(cherry picked from commit fe0f1e64e8a77dadde699495c7eb368ce61ac992)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
---
 src/ldb_modules/memberof.c           | 6 +++---
 src/providers/ldap/ldap_id_cleanup.c | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c
index 5e1ff95a8..7651fe09f 100644
--- a/src/ldb_modules/memberof.c
+++ b/src/ldb_modules/memberof.c
@@ -1364,7 +1364,7 @@ static int memberof_del(struct ldb_module *module, struct ldb_request *req)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    sret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
+    sret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn);
     if (sret != 0) {
         talloc_free(ctx);
         return LDB_ERR_OPERATIONS_ERROR;
@@ -1781,7 +1781,7 @@ static int mbof_del_execute_op(struct mbof_del_operation *delop)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    ret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
+    ret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn);
     if (ret != 0) {
         return LDB_ERR_OPERATIONS_ERROR;
     }
@@ -3058,7 +3058,7 @@ static int mbof_get_ghost_from_parent(struct mbof_mod_del_op *igh)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    ret = sss_filter_sanitize(igh, dn, &clean_dn);
+    ret = sss_filter_sanitize_dn(igh, dn, &clean_dn);
     if (ret != 0) {
         return LDB_ERR_OPERATIONS_ERROR;
     }
diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
index 8c0f0c18b..cd10126f4 100644
--- a/src/providers/ldap/ldap_id_cleanup.c
+++ b/src/providers/ldap/ldap_id_cleanup.c
@@ -422,7 +422,7 @@ static int cleanup_groups(TALLOC_CTX *memctx,
         }
 
         /* sanitize dn */
-        ret = sss_filter_sanitize(tmpctx, dn, &sanitized_dn);
+        ret = sss_filter_sanitize_dn(tmpctx, dn, &sanitized_dn);
         if (ret != EOK) {
             DEBUG(SSSDBG_MINOR_FAILURE,
                   "sss_filter_sanitize failed: %s:[%d]\n",
-- 
2.39.1


From 171b20abfb328c20b5c61763a8f107943441a07e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 26 Aug 2020 15:40:53 +0200
Subject: [PATCH 5/8] ldap: use member DN to create ghost user hash table

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
(cherry picked from commit 50d0d154cedb6915ab321b47c40851c40e91cf41)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
---
 src/db/sysdb.h                                |  1 +
 src/providers/ldap/sdap.c                     | 10 ++++++++++
 src/providers/ldap/sdap_async_groups.c        | 17 +++++++++++++++-
 src/providers/ldap/sdap_async_nested_groups.c | 20 +++++++++++++++++--
 4 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 2660314a7..fcfb81dfe 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -128,6 +128,7 @@
 #define SYSDB_UPN "userPrincipalName"
 #define SYSDB_CANONICAL_UPN "canonicalUserPrincipalName"
 #define SYSDB_CCACHE_FILE "ccacheFile"
+#define SYSDB_DN_FOR_MEMBER_HASH_TABLE "dnForMemberHashTable"
 
 #define SYSDB_ORIG_DN "originalDN"
 #define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp"
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
index 0241a99e4..9d4649514 100644
--- a/src/providers/ldap/sdap.c
+++ b/src/providers/ldap/sdap.c
@@ -771,6 +771,16 @@ errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        /* The dereference control seems to return the DN from the dereference
+         * attribute (e.g. member) so we can use it as key for the hash table
+         * later. */
+        ret = sysdb_attrs_add_string(res[mi]->attrs,
+                                     SYSDB_DN_FOR_MEMBER_HASH_TABLE, orig_dn);
+        if (ret) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+            goto done;
+        }
+
         for (dval = dref->attrVals; dval != NULL; dval = dval->next) {
             DEBUG(SSSDBG_TRACE_INTERNAL,
                   "Dereferenced attribute: %s\n", dval->type);
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 0a55795f0..4fbd8cf7b 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -2510,6 +2510,7 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
     struct ldb_message_element *el;
     const char *username;
     const char *original_dn;
+    const char *hash_key_dn;
     struct sss_domain_info *user_dom;
     struct sdap_domain *sdap_dom;
 
@@ -2608,8 +2609,22 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
                                        SYSDB_MOD_REP);
             if (ret != EOK) goto done;
         } else {
+            /* The DN of the user object and the DN in the member attribute
+             * might differ, e.g. in case. Since we later search the hash with
+             * DNs from the member attribute we should try to use DN from the
+             * member attribute here as well. This should be added earlier in
+             * the SYSDB_DN_FOR_MEMBER_HASH_TABLE attribute. If this does not
+             * exists we fall-back to original_dn which should work in the
+             * most cases as well. */
+            ret = sysdb_attrs_get_string(users[i],
+                                         SYSDB_DN_FOR_MEMBER_HASH_TABLE,
+                                         &hash_key_dn);
+            if (ret != EOK) {
+                hash_key_dn = original_dn;
+            }
+
             key.type = HASH_KEY_STRING;
-            key.str = talloc_steal(ghosts, discard_const(original_dn));
+            key.str = talloc_steal(ghosts, discard_const(hash_key_dn));
             value.type = HASH_VALUE_PTR;
             /* Already qualified from sdap_get_user_primary_name() */
             value.ptr = talloc_steal(ghosts, discard_const(username));
diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
index 0a69e96f4..056ee2da0 100644
--- a/src/providers/ldap/sdap_async_nested_groups.c
+++ b/src/providers/ldap/sdap_async_nested_groups.c
@@ -241,9 +241,12 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
     const char *name = NULL;
     errno_t ret;
 
-    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+    ret = sysdb_attrs_get_string(entry, SYSDB_DN_FOR_MEMBER_HASH_TABLE, &name);
     if (ret != EOK) {
-        return ret;
+        ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+        if (ret != EOK) {
+            return ret;
+        }
     }
 
     return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
@@ -1498,6 +1501,19 @@ sdap_nested_group_single_step_process(struct tevent_req *subreq)
             }
         }
 
+        /* The original DN of the user object itself might differ from the one
+         * used in the member attribute, e.g. different case. To make sure if
+         * can be found in a hash table when iterating over group members the
+         * DN from the member attribute used for the search as saved as well.
+         */
+        ret = sysdb_attrs_add_string(entry,
+                                     SYSDB_DN_FOR_MEMBER_HASH_TABLE,
+                                     state->current_member->dn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+            goto done;
+        }
+
         /* save user in hash table */
         ret = sdap_nested_group_hash_user(state->group_ctx, entry);
         if (ret == EEXIST) {
-- 
2.39.1


From 26e6e2fe4fb2f7a1e36e72c9ff6ba477b3347531 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 2 Sep 2020 12:37:27 +0200
Subject: [PATCH 6/8] intg: allow member DN to have a different case

Make sure that group members are added properly to the group even if the
user DN in the RFC2307bis member attribute differs in case from the
original DN of the user object.

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
(cherry picked from commit 88631392e9172ae4fa3e411398516a2f39f0060e)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
---
 src/tests/intg/test_ldap.py | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py
index a6659b1b7..352875291 100644
--- a/src/tests/intg/test_ldap.py
+++ b/src/tests/intg/test_ldap.py
@@ -299,6 +299,25 @@ def sanity_rfc2307_bis(request, ldap_conn):
     return None
 
 
+@pytest.fixture
+def member_with_different_cases_rfc2307_bis(request, ldap_conn):
+    """
+    Create a group where the user DN values of the RFC2307bis member attribute
+    differ in case from the original DN of the user object.
+    """
+    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+    ent_list.add_user("user1", 1001, 2001)
+    ent_list.add_user("user2", 1002, 2002)
+
+    ent_list.add_group_bis("two_user_group", 2012, ["USER1", "uSeR2"])
+
+    create_ldap_fixture(request, ldap_conn, ent_list)
+    conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS)
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
 def expected_list_to_name_dict(entries):
     return dict((u["name"], u) for u in entries)
 
@@ -390,6 +409,22 @@ def test_sanity_rfc2307_bis(ldap_conn, sanity_rfc2307_bis):
         grp.getgrgid(1)
 
 
+def test_member_with_different_cases_rfc2307_bis(
+                                      ldap_conn,
+                                      member_with_different_cases_rfc2307_bis):
+    """
+    Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=1817122
+    Make sure that group members are added properly to the group even if the
+    user DN in the RFC2307bis member attribute differs in case from the
+    original DN of the user object.
+    """
+    group_pattern = expected_list_to_name_dict([
+        dict(name='two_user_group', passwd='*', gid=2012,
+             mem=ent.contains_only("user1", "user2")),
+    ])
+    ent.assert_each_group_by_name(group_pattern)
+
+
 @pytest.fixture
 def refresh_after_cleanup_task(request, ldap_conn):
     ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
-- 
2.39.1


From 26591d613b8ba8257657e158a623c31e1dc7f942 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 27 Nov 2018 16:40:01 +0100
Subject: [PATCH 7/8] 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 <jhrozek@redhat.com>
(cherry picked from commit 1c40208aa1e0f9a17cc4f336c99bcaa6977592d3)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
Reviewed-by: Justin Stephenson <jstephen@redhat.com>
---
 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 8c64ed845..36ce2e8b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1833,7 +1833,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 f6f6f9804..c60ac24c6 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 8b5d53666..a9e48d6d0 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 646e0f393..7da2d1c58 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 479cc1606..b1155e220 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 125e925d9..398d3d283 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.39.1


From 590547153a4fb98b4641881644b743a541a29beb Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Mon, 9 Dec 2019 11:31:14 +0100
Subject: [PATCH 8/8] 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 <atikhono@redhat.com>
(cherry picked from commit a2b9a84460429181f2a4fa7e2bb5ab49fd561274)

Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
Reviewed-by: Justin Stephenson <jstephen@redhat.com>
---
 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                     | 94 ---------------------------
 src/util/util_ext.c                 | 99 +++++++++++++++++++++++++++++
 8 files changed, 272 insertions(+), 108 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 36ce2e8b7..5751bfe09 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1833,7 +1833,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 c60ac24c6..d7bc99286 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 a9e48d6d0..7d7667738 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 7da2d1c58..058d4f9e4 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 b6f4ba4ad..2a7cab28e 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -879,9 +879,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 6b5a31c3d..13e39dbc8 100644
--- a/src/tests/cmocka/test_certmap.c
+++ b/src/tests/cmocka/test_certmap.c
@@ -1336,6 +1336,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=<I>CN=Certificate\\20Authority,O=IPA.DEVEL"
+                                "<S>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=<I>CN=Certificate Authority,O=IPA.DEVEL"
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
     assert_null(domains);
@@ -1350,6 +1359,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=<I>CN=Certificate\\20Authority,O=IPA.DEVEL"
+                                "<S>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=<I>CN=Certificate Authority,O=IPA.DEVEL"
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
     assert_non_null(domains);
@@ -1371,6 +1391,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:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
                             "LDAP:rule97=<I>{issuer_dn!nss_x500}<S>{subject_dn}",
@@ -1381,6 +1411,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=<I>O=IPA.DEVEL,CN=Certificate\\20Authority"
+                                "<S>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=<I>O=IPA.DEVEL,CN=Certificate Authority"
                                 "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
     assert_non_null(domains);
@@ -1397,6 +1438,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=<I>O=IPA.DEVEL,CN=Certificate\\20Authority"
+                                "<S>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=<I>O=IPA.DEVEL,CN=Certificate Authority"
                                 "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
     assert_non_null(domains);
@@ -1415,6 +1467,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:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
                       "LDAP:rule94=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
@@ -1425,12 +1485,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=<I>O=IPA.DEVEL,CN=Certificate Authority"
+    assert_string_equal(filter, "rule94=<I>O=IPA.DEVEL,CN=Certificate\\20Authority"
                                 "<S>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=<I>O=IPA.DEVEL,CN=Certificate Authority"
+                                "<S>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})",
@@ -1444,6 +1514,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);
@@ -1465,6 +1543,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=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
+                  "<S>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=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
                   "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
     assert_null(domains);
@@ -1478,6 +1565,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=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
+                  "<S>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=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
                   "<S>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 aaf3609c3..9327f971c 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -436,100 +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 04dc02a8a..a89b60f76 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.39.1

