diff --git a/SOURCES/0058-nss-check-if-groups-are-filtered-during-initgroups.patch b/SOURCES/0058-nss-check-if-groups-are-filtered-during-initgroups.patch
new file mode 100644
index 0000000..b0dc81c
--- /dev/null
+++ b/SOURCES/0058-nss-check-if-groups-are-filtered-during-initgroups.patch
@@ -0,0 +1,113 @@
+From bb736d3f02861366d11d2f03314295bd1c1be209 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 17 Nov 2020 12:59:23 +0100
+Subject: [PATCH] nss: check if groups are filtered during initgroups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If groups are filtered, i.e. SSSD should not handle them, they should
+not appear in the group list returned by an initgroups request.
+
+Resolves: https://github.com/SSSD/sssd/issues/5403
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit c87b2208b9a58c12eeceb5b8ccf9c34dcd835b8d)
+---
+ src/responder/nss/nss_protocol_grent.c | 35 ++++++++++++++++++++++++++
+ src/tests/intg/test_ldap.py            | 12 +++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
+index 2367d9ecd..4c7ea9aed 100644
+--- a/src/responder/nss/nss_protocol_grent.c
++++ b/src/responder/nss/nss_protocol_grent.c
+@@ -308,6 +308,34 @@ done:
+     return EOK;
+ }
+ 
++static bool is_group_filtered(struct sss_nc_ctx *ncache,
++                              struct sss_domain_info *domain,
++                              const char *grp_name, gid_t gid)
++{
++    int ret;
++
++    if (grp_name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Group with gid [%"SPRIgid"] has no name, this should never "
++              "happen, trying to continue without.\n", gid);
++    } else {
++        ret = sss_ncache_check_group(ncache, domain, grp_name);
++        if (ret == EEXIST) {
++            DEBUG(SSSDBG_TRACE_FUNC, "Group [%s] is filtered out! "
++                                     "(negative cache)", grp_name);
++            return true;
++        }
++    }
++    ret = sss_ncache_check_gid(ncache, domain, gid);
++    if (ret == EEXIST) {
++        DEBUG(SSSDBG_TRACE_FUNC, "Group [%"SPRIgid"] is filtered out! "
++                                 "(negative cache)", gid);
++        return true;
++    }
++
++    return false;
++}
++
+ errno_t
+ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+                          struct nss_cmd_ctx *cmd_ctx,
+@@ -326,6 +354,7 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+     size_t body_len;
+     size_t rp;
+     gid_t gid;
++    const char *grp_name;
+     gid_t orig_gid;
+     errno_t ret;
+     int i;
+@@ -374,6 +403,8 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+         gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM,
+                                                    0);
+         posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL);
++        grp_name = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME,
++                                                        NULL);
+ 
+         if (gid == 0) {
+             if (posix != NULL && strcmp(posix, "FALSE") == 0) {
+@@ -386,6 +417,10 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+             }
+         }
+ 
++        if (is_group_filtered(nss_ctx->rctx->ncache, domain, grp_name, gid)) {
++            continue;
++        }
++
+         SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp);
+         num_results++;
+ 
+diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py
+index 2e88ac7d1..afd23c71e 100644
+--- a/src/tests/intg/test_ldap.py
++++ b/src/tests/intg/test_ldap.py
+@@ -1190,6 +1190,18 @@ def test_nss_filters(ldap_conn, sanity_nss_filter):
+     with pytest.raises(KeyError):
+         grp.getgrgid(14)
+ 
++    # test initgroups - user1 is member of group_two_one_user_groups (2019)
++    # which is filtered out
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user1", 2001)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    user_with_group_ids = [2001, 2012, 2015, 2017, 2018]
++    assert sorted(gids) == sorted(user_with_group_ids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user_with_group_ids)])
++        )
++
+ 
+ @pytest.fixture
+ def sanity_nss_filter_cached(request, ldap_conn):
+-- 
+2.21.3
+
diff --git a/SOURCES/0059-CACHE-Create-timestamp-if-missing.patch b/SOURCES/0059-CACHE-Create-timestamp-if-missing.patch
new file mode 100644
index 0000000..1b910be
--- /dev/null
+++ b/SOURCES/0059-CACHE-Create-timestamp-if-missing.patch
@@ -0,0 +1,50 @@
+From ca8cdbde6285d14181372f1ce8e5f6faf72e1d80 Mon Sep 17 00:00:00 2001
+From: Tomas Halman <thalman@redhat.com>
+Date: Mon, 16 Nov 2020 17:28:19 +0100
+Subject: [PATCH 59/60] CACHE: Create timestamp if missing
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In some cases, object is stored in cache but the corresponding
+record in timestamp cache is missing (for example when timestamp
+cache file is deleted). The timestamp is never created in such
+case.
+
+With this patch we create new timestamp object if update doesn't
+work for this particular reason (missing object).
+
+Resolves: https://github.com/SSSD/sssd/issues/5121
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 37761b42f570e9019f9dd850912a29385e80a14c)
+---
+ src/db/sysdb_ops.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index d4ad69e39..0e0a95961 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -1371,9 +1371,17 @@ int sysdb_set_entry_attr(struct sysdb_ctx *sysdb,
+ 
+     if (ret == EOK && is_ts_ldb_dn(entry_dn)) {
+         tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs, mod_op);
++        if (tret == ENOENT && mod_op == SYSDB_MOD_REP) {
++            /* Update failed because TS does non exist. Create missing TS */
++            tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs,
++                                           SYSDB_MOD_ADD);
++            DEBUG(SSSDBG_TRACE_FUNC,
++                  "The TS value for %s does not exist, trying to create it\n",
++                  ldb_dn_get_linearized(entry_dn));
++        }
+         if (tret != EOK) {
+             DEBUG(SSSDBG_MINOR_FAILURE,
+-                "Cannot set ts attrs for %s\n", ldb_dn_get_linearized(entry_dn));
++                "Cannot set TS attrs for %s\n", ldb_dn_get_linearized(entry_dn));
+             /* Not fatal */
+         } else {
+             state_mask |= SSS_SYSDB_TS_CACHE;
+-- 
+2.21.3
+
diff --git a/SOURCES/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch b/SOURCES/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch
new file mode 100644
index 0000000..4b240a5
--- /dev/null
+++ b/SOURCES/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch
@@ -0,0 +1,142 @@
+From dc0fb9bc5e1b186b274739389437d68c7b257e1f Mon Sep 17 00:00:00 2001
+From: Tomas Halman <thalman@redhat.com>
+Date: Wed, 18 Nov 2020 13:32:28 +0100
+Subject: [PATCH 60/60] TESTS: Add test for recreating cache timestamp
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 62b2b4972e41393cd43b58d9e6451a2c58942cb2)
+---
+ src/tests/cmocka/test_sysdb_ts_cache.c | 107 +++++++++++++++++++++++++
+ 1 file changed, 107 insertions(+)
+
+diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c
+index ae8b1b16c..24b26d950 100644
+--- a/src/tests/cmocka/test_sysdb_ts_cache.c
++++ b/src/tests/cmocka/test_sysdb_ts_cache.c
+@@ -1606,6 +1606,107 @@ static void test_sysdb_search_with_ts(void **state)
+     talloc_free(res);
+ }
+ 
++static void test_sysdb_user_missing_ts(void **state)
++{
++    int ret;
++    struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state,
++                                                               struct sysdb_ts_test_ctx);
++    struct ldb_result *res = NULL;
++    struct sysdb_attrs *attrs = NULL;
++
++    /* Nothing must be stored in either cache at the beginning of the test */
++    res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME);
++    assert_int_equal(res->count, 0);
++    talloc_free(res);
++
++    /* add user to cache */
++    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
++    assert_non_null(attrs);
++    ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL,
++                           TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME,
++                           "/home/"TEST_USER_NAME, "/bin/bash", NULL,
++                           attrs, NULL, TEST_CACHE_TIMEOUT,
++                           TEST_NOW_1);
++    assert_int_equal(ret, EOK);
++    talloc_zfree(attrs);
++
++    /* remove timestamp */
++    struct ldb_dn *userdn = sysdb_user_dn(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME);
++    ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, userdn);
++    assert_int_equal(ret, EOK);
++
++    /* update user */
++    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2);
++    assert_non_null(attrs);
++    ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL,
++                           TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME,
++                           "/home/"TEST_USER_NAME, "/bin/bash", NULL,
++                           attrs, NULL, TEST_CACHE_TIMEOUT,
++                           TEST_NOW_2);
++    assert_int_equal(ret, EOK);
++    talloc_zfree(attrs);
++
++    /* check that ts is back */
++    SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, userdn,
++                   LDB_SCOPE_BASE, NULL, NULL);
++    assert_int_equal(ret, EOK);
++    assert_int_equal(res->count, 1);
++    talloc_zfree(res);
++    talloc_zfree(userdn);
++}
++
++static void test_sysdb_group_missing_ts(void **state)
++{
++    int ret;
++    struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state,
++                                                               struct sysdb_ts_test_ctx);
++    struct ldb_result *res = NULL;
++    struct sysdb_attrs *group_attrs = NULL;
++    struct ldb_dn *groupdn = NULL;
++
++    /* Nothing must be stored in either cache at the beginning of the test */
++    res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME);
++    assert_int_equal(res->count, 0);
++    talloc_free(res);
++
++    /* add group to cache */
++    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
++    assert_non_null(group_attrs);
++    ret = sysdb_store_group(test_ctx->tctx->dom,
++                            TEST_GROUP_NAME,
++                            TEST_GROUP_GID,
++                            group_attrs,
++                            TEST_CACHE_TIMEOUT,
++                            TEST_NOW_1);
++    assert_int_equal(ret, EOK);
++    talloc_zfree(group_attrs);
++
++    /* remove timestamp */
++    groupdn = sysdb_group_dn(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME);
++    ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, groupdn);
++    assert_int_equal(ret, EOK);
++
++    /* update group */
++    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2);
++    assert_non_null(group_attrs);
++    ret = sysdb_store_group(test_ctx->tctx->dom,
++                            TEST_GROUP_NAME,
++                            TEST_GROUP_GID,
++                            group_attrs,
++                            TEST_CACHE_TIMEOUT,
++                            TEST_NOW_2);
++    assert_int_equal(ret, EOK);
++    talloc_zfree(group_attrs);
++
++    /* check that ts is back */
++    SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, groupdn,
++                   LDB_SCOPE_BASE, NULL, NULL);
++    assert_int_equal(ret, EOK);
++    assert_int_equal(res->count, 1);
++    talloc_zfree(res);
++    talloc_zfree(groupdn);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     int rv;
+@@ -1660,6 +1761,12 @@ int main(int argc, const char *argv[])
+         cmocka_unit_test_setup_teardown(test_sysdb_search_with_ts,
+                                         test_sysdb_ts_setup,
+                                         test_sysdb_ts_teardown),
++        cmocka_unit_test_setup_teardown(test_sysdb_user_missing_ts,
++                                        test_sysdb_ts_setup,
++                                        test_sysdb_ts_teardown),
++        cmocka_unit_test_setup_teardown(test_sysdb_group_missing_ts,
++                                        test_sysdb_ts_setup,
++                                        test_sysdb_ts_teardown),
+     };
+ 
+     /* Set debug level to invalid value so we can decide if -d 0 was used. */
+-- 
+2.21.3
+
diff --git a/SOURCES/0061-cert-matching.patch b/SOURCES/0061-cert-matching.patch
new file mode 100644
index 0000000..cac94eb
--- /dev/null
+++ b/SOURCES/0061-cert-matching.patch
@@ -0,0 +1,2180 @@
+From e13d8a12513bfba1531cc867fbf687ff8673241f Mon Sep 17 00:00:00 2001
+From: Alexey Tikhonov <atikhono@redhat.com>
+Date: Thu, 10 Dec 2020 19:45:08 +0100
+Subject: [PATCH] Squashed commit of the following:
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 6b3b4b0bf945814e8886b900dcda18de25f38bb4
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Thu Dec 12 13:10:16 2019 +0100
+
+    certmap: mention special regex characters in man page
+
+    Since some of the matching rules use regular expressions some characters
+    must be escaped so that they can be used a ordinary characters in the
+    rules.
+
+    Related to https://pagure.io/SSSD/sssd/issue/4127
+
+    Reviewed-by: Michal Židek <mzidek@redhat.com>
+    (cherry picked from commit 21cb9fb28db1f2eb4ee770eb029bfe20233e4392)
+
+commit 451410e72514bd68e4b56b1a42c97ade6783e74b
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Tue Nov 27 16:42:38 2018 +0100
+
+    test: add certificate without KU to certmap tests
+
+    Make sure there is a test for a certificate without key-usage (KU)
+
+    Related to https://bugzilla.redhat.com/show_bug.cgi?id=1660899
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit e1734ba828470d00370c44c95da56822fdcc104d)
+
+commit e7966dfa40b9a7fcde79a07f146ae5283a7bc8e5
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Nov 26 12:03:13 2018 +0100
+
+    certmap: allow missing KU in OpenSSL version
+
+    Make sure a missing key-usage (KU) is not treated as an error and is
+    handled equally in the NSS and OpenSSL implementation
+
+    Related to https://bugzilla.redhat.com/show_bug.cgi?id=1660899
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit aef8e49b7ee2e7743d6981070d61bc89b7c8fcfb)
+
+commit 6e9e6673916b61197df8a809f56c73d8bdbb868c
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Jan 7 17:04:34 2019 +0100
+
+    CONFIG: validator rules & test
+
+    Add support of 'certmap' config section to validator rules
+
+    Resolves:
+    https://pagure.io/SSSD/sssd/issue/3845
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 8e9e8011ce17860bec67a572e4c11a9178c03b8e)
+
+commit eec9d72a242b2b05369f0eb89c4ebcda26d59802
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Sep 7 22:26:21 2018 +0200
+
+    intg: add Smartcard authentication tests
+
+    Two test for Smartcard authentication of a local user, i.e. a user
+    managed by the files provider, are added. One for a successful
+    authentication, the other for a failed authentication with a wrong PIN.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked with fixes from commit 657f3b89bca9adfb13f0867c91f1d76845d2d6dd)
+
+commit cc2840fbb494ac686e9a3ae0016827a44d14769f
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Sep 7 22:17:47 2018 +0200
+
+    test_ca: set a password/PIN to nss databases
+
+    To make sure the PIN is properly checked during tests the NSS databases
+    need a password.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit a45a410dc7fa7cf84bcac541e693ee8781e25431)
+
+commit 0a989c62b4a3b73f23d9b6956ac81afaed9901f7
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Sep 10 22:03:55 2018 +0200
+
+    test_ca: test library only for readable
+
+    On Debian libraries typically do not have the execute-bit set so it is
+    better to only check for readability.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 91aea762d02731193eb66a00b930ff1fe8bc5ab8)
+
+commit 5a47b213b11cbf74dad47594d1826985f6b68f22
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Sep 7 22:16:50 2018 +0200
+
+    PAM: use better PAM error code for failed Smartcard authentication
+
+    If the user enters a wrong PIN the PAM responder currently returns
+    PAM_USER_UNKNOWN better is PAM_AUTH_ERR.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 442ae7b1d0704cdd667d4f1ba4c165ce3f3ffed4)
+
+commit b6907d7cd5ab7568971ddb48f3932f106e86fe06
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Sep 3 18:38:42 2018 +0200
+
+    doc: add certificate mapping section to man page
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked with fixes from commit 0c739e969a617bdb4c06cdfd63772bf6d283c518)
+
+commit d75b196312c4cec767c196c663ff969b6aebcd6b
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Jul 9 18:56:26 2018 +0200
+
+    PAM: add certificate matching rules from all domains
+
+    Currently the PAM responder only reads the certificate mapping and
+    matching rules from the first domain. To support Smartcard
+    authentication for local and remote users all configured domains must be
+    taken into account.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit d42f44d54453d3ddb54875374c1b61dc1e7cd821)
+
+commit 167ab7206913c17617a8e5ada7567d91f8ed6e11
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Jul 9 18:45:21 2018 +0200
+
+    responder: make sure SSS_DP_CERT is passed to files provider
+
+    Currently the files provider is only contacted once in a while to update
+    the full cache with fresh data from the passwd file. To allow rule based
+    certificate mapping the lookup by certificate request must be always
+    send to the file provider so that it can evaluate the rules and add the
+    certificate to cached entry of the matching user.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 9fdc5f1d87a133885e6a22810a7eb980c60dcb55)
+
+commit 69def7a3e81313a30ceae937f9cde5d62e999c3d
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Jul 9 18:37:46 2018 +0200
+
+    files: add support for Smartcard authentication
+
+    To support certificate based authentication the files provider must be
+    able to map a certificate to a user during a BE_REQ_BY_CERT request.
+
+    Additionally the authentication request should be handled by the PAM
+    responder code which is responsible for the local Smartcard
+    authentication. To be consistent with the other backend an authentication
+    handler is added to the files provider which unconditionally returns the
+    offline error code telling the PAM responder to handle the
+    authentication if it has access to the needed credentials.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 275eeed24adc31f3df51cf278f509a4be76a3a3c)
+
+commit e96ba56ea8037d58e1335f7dacd3b19919bc4135
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Jul 6 15:17:10 2018 +0200
+
+    confdb: add special handling for rules for the files provider
+
+    To make the configuration more simple there are some special assumption
+    for local users, i.e. user managed by the files provider.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 9386ef605ffbc03abe2bc273efddbc099441fe3b)
+
+commit d304f5a9e60f7f6eb915a10067ee2e5e5f14c369
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Tue Jul 3 11:31:12 2018 +0200
+
+    sysdb: sysdb_certmap_add() handle domains more flexible
+
+    sysdb_ldb_msg_attr_to_certmap_info() creates an empty list if there are
+    no domains defined, sysdb_certmap_add() should be able to handle both a
+    missing or an empty domains list.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 06f7005d38d164879b727708feff80004b422f91)
+
+commit 53befb320c2b60a420a2588425fd5004ceec791a
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Jul 2 12:20:53 2018 +0200
+
+    AD/LDAP: read certificate mapping rules from config file
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 15301db1dc1e5e2aafc1805a30e3b28756218c9b)
+
+commit 670a1ca6b7b22bb3a1079111528ee7e4aafd97e5
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Mon Jul 2 10:38:54 2018 +0200
+
+    confdb: add confdb_certmap_to_sysdb()
+
+    Add a function to write certificate mapping and matching rules from the
+    config database to the cache of a domain.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked with fixes from commit d9cc38008a51a8a5189904f175e4d10cbde4a974)
+
+commit 14c15cc6db16726419fbf6df76b5c83aec49192a
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Jun 29 18:13:59 2018 +0200
+
+    sysdb: add attr_map attribute to sysdb_ldb_msg_attr_to_certmap_info()
+
+    Allow more flexible attribute mapping in
+    sysdb_ldb_msg_attr_to_certmap_info()
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 0bf709ad348ca115443bd21e4e369abd5d7698c4)
+
+commit f867c2a293651043072afe1dd7a8a78a05e5fe4d
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Tue Jul 3 11:30:07 2018 +0200
+
+    sysdb_ldb_msg_attr_to_certmap_info: set SSS_CERTMAP_MIN_PRIO
+
+    Make sure that priority is always set.
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit d1dd7f7703b4f40d2fbb830e28969b31b8a1673e)
+
+commit 8ef2cc11008ef86f4dfcbc267c797bf8ee265455
+Author: Sumit Bose <sbose@redhat.com>
+Date:   Fri Jun 29 17:49:50 2018 +0200
+
+    sysdb: extract sysdb_ldb_msg_attr_to_certmap_info() call
+
+    Related to https://pagure.io/SSSD/sssd/issue/3500
+
+    Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+    (cherry picked from commit 7c619ae08f05a7595d15cf11b64461a7d19cfaa7)
+---
+ Makefile.am                                   |   2 +
+ configure.ac                                  |   1 +
+ contrib/sssd.spec.in                          |   1 +
+ src/confdb/confdb.c                           | 158 +++++++++++++
+ src/confdb/confdb.h                           |  23 ++
+ src/config/cfg_rules.ini                      |  10 +
+ src/db/sysdb.h                                |   5 +
+ src/db/sysdb_certmap.c                        | 223 +++++++++++-------
+ src/external/cwrap.m4                         |   5 +
+ src/external/intgcheck.m4                     |   1 +
+ src/external/test_ca.m4                       |   2 +-
+ src/lib/certmap/sss_cert_content_crypto.c     |  22 +-
+ src/lib/certmap/sss_cert_content_nss.c        |  21 +-
+ src/man/sss-certmap.5.xml                     |   9 +
+ src/man/sssd.conf.5.xml                       | 149 ++++++++++++
+ src/providers/ad/ad_init.c                    |  16 ++
+ src/providers/files/files_auth.c              |  69 ++++++
+ src/providers/files/files_certmap.c           | 186 +++++++++++++++
+ src/providers/files/files_id.c                |  20 ++
+ src/providers/files/files_init.c              |  29 +++
+ src/providers/files/files_private.h           |  17 ++
+ src/providers/ldap/ldap_init.c                |  16 ++
+ src/responder/common/responder_dp.c           |  20 +-
+ src/responder/pam/pamsrv.h                    |   2 +-
+ src/responder/pam/pamsrv_cmd.c                |   6 +-
+ src/responder/pam/pamsrv_p11.c                |  77 +++---
+ src/tests/cmocka/test_certmap.c               |  45 ++++
+ src/tests/cmocka/test_config_check.c          |  16 +-
+ src/tests/intg/test_pam_responder.py          | 109 ++++++++-
+ src/tests/test_CA/Makefile.am                 |  24 +-
+ src/tests/test_CA/SSSD_test_cert_0004.config  |  11 +
+ src/tests/test_CA/SSSD_test_cert_key_0004.pem |  28 +++
+ 32 files changed, 1170 insertions(+), 153 deletions(-)
+ create mode 100644 src/providers/files/files_auth.c
+ create mode 100644 src/providers/files/files_certmap.c
+ create mode 100644 src/tests/test_CA/SSSD_test_cert_0004.config
+ create mode 100644 src/tests/test_CA/SSSD_test_cert_key_0004.pem
+
+diff --git a/Makefile.am b/Makefile.am
+index 718041634..77f5faf6b 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -4094,6 +4094,8 @@ libsss_proxy_la_LDFLAGS = \
+ libsss_files_la_SOURCES = \
+     src/providers/files/files_init.c \
+     src/providers/files/files_id.c \
++    src/providers/files/files_auth.c \
++    src/providers/files/files_certmap.c \
+     src/providers/files/files_ops.c \
+     src/util/inotify.c \
+     $(NULL)
+diff --git a/configure.ac b/configure.ac
+index 5154918b1..89abddef4 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -488,6 +488,7 @@ AM_CONDITIONAL([HAVE_CHECK], [test x$have_check != x])
+ AM_CHECK_CMOCKA
+ AM_CHECK_UID_WRAPPER
+ AM_CHECK_NSS_WRAPPER
++AM_CHECK_PAM_WRAPPER
+ AM_CHECK_TEST_CA
+ 
+ # Check if the user wants SSSD to be compiled with systemtap probes
+diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
+index 10b5bd56c..52a77ae7a 100644
+--- a/contrib/sssd.spec.in
++++ b/contrib/sssd.spec.in
+@@ -236,6 +236,7 @@ BuildRequires: selinux-policy-targeted
+ BuildRequires: libcmocka-devel >= 1.0.0
+ BuildRequires: uid_wrapper
+ BuildRequires: nss_wrapper
++BuildRequires: pam_wrapper
+ 
+ # Test CA requires openssl independent if SSSD is build with NSS or openssl,
+ # openssh is needed for ssh-keygen and NSS builds need nss-tools for certutil.
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index e55f88e4e..97de6d3b1 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -2209,3 +2209,161 @@ done:
+     talloc_free(tmp_ctx);
+     return ret;
+ }
++
++static errno_t certmap_local_check(struct ldb_message *msg)
++{
++    const char *rule_name;
++    const char *tmp_str;
++    int ret;
++
++    rule_name = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_NAME, NULL);
++    if (rule_name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Certficate mapping rule [%s] has no name.",
++                                   ldb_dn_get_linearized(msg->dn));
++        return EINVAL;
++    }
++
++    tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_DOMAINS, NULL);
++    if (tmp_str != NULL) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "Option [%s] is ignored for local certmap rules.\n",
++              CONFDB_CERTMAP_DOMAINS);
++    }
++
++    tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_MAPRULE, NULL);
++    if (tmp_str != NULL) {
++        if (tmp_str[0] != '(' || tmp_str[strlen(tmp_str) - 1] != ')') {
++            DEBUG(SSSDBG_CONF_SETTINGS,
++                  "Mapping rule must be in braces (...).\n");
++            return EINVAL;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Using [%s] mapping rule of [%s].\n",
++                                tmp_str, ldb_dn_get_linearized(msg->dn));
++        return EOK;
++    }
++
++    tmp_str = talloc_asprintf(msg, "(%s)", rule_name);
++    if (tmp_str == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
++        return ENOMEM;
++    }
++    ret = ldb_msg_add_string(msg, CONFDB_CERTMAP_MAPRULE, tmp_str);
++    if (ret != LDB_SUCCESS) {
++        talloc_free(discard_const(tmp_str));
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
++        return EIO;
++    }
++
++    DEBUG(SSSDBG_TRACE_ALL, "Using [%s] as mapping rule for [%s].\n",
++                            tmp_str, ldb_dn_get_linearized(msg->dn));
++
++    return EOK;
++}
++
++static errno_t confdb_get_all_certmaps(TALLOC_CTX *mem_ctx,
++                                       struct confdb_ctx *cdb,
++                                       struct sss_domain_info *dom,
++                                       struct certmap_info ***_certmap_list)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    struct ldb_dn *dn = NULL;
++    struct ldb_result *res = NULL;
++    /* The attributte order is important, because it is used in
++     * sysdb_ldb_msg_attr_to_certmap_info and must match
++     * enum certmap_info_member. */
++    static const char *attrs[] = { CONFDB_CERTMAP_NAME,
++                                   CONFDB_CERTMAP_MAPRULE,
++                                   CONFDB_CERTMAP_MATCHRULE,
++                                   CONFDB_CERTMAP_PRIORITY,
++                                   CONFDB_CERTMAP_DOMAINS,
++                                   NULL};
++    struct certmap_info **certmap_list = NULL;
++    size_t c;
++    int ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", dom->name,
++                                                       CONFDB_CERTMAP_BASEDN);
++    if (dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL,
++                     attrs, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = EIO;
++        goto done;
++    }
++
++    certmap_list = talloc_zero_array(tmp_ctx, struct certmap_info *,
++                                     res->count + 1);
++    if (certmap_list == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    for (c = 0; c < res->count; c++) {
++        if (is_files_provider(dom)) {
++            ret = certmap_local_check(res->msgs[c]);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_CONF_SETTINGS,
++                      "Invalid certificate mapping [%s] for local user, "
++                      "ignored.\n", ldb_dn_get_linearized(res->msgs[c]->dn));
++                continue;
++            }
++        }
++        ret = sysdb_ldb_msg_attr_to_certmap_info(certmap_list, res->msgs[c],
++                                                 attrs, &certmap_list[c]);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "sysdb_ldb_msg_attr_to_certmap_info failed.\n");
++            goto done;
++        }
++    }
++
++    *_certmap_list = talloc_steal(mem_ctx, certmap_list);
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++int confdb_certmap_to_sysdb(struct confdb_ctx *cdb,
++                            struct sss_domain_info *dom)
++{
++    int ret;
++    TALLOC_CTX *tmp_ctx;
++    struct certmap_info **certmap_list;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
++        return ENOMEM;
++    }
++
++    ret = confdb_get_all_certmaps(tmp_ctx, cdb, dom, &certmap_list);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "confdb_get_all_certmaps failed.\n");
++        goto done;
++    }
++
++    ret = sysdb_update_certmap(dom->sysdb, certmap_list, false /* TODO */);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed.\n");
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++
++    return ret;
++}
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index d3e71be86..b0d52ba49 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -267,6 +267,14 @@
+ #define CONFDB_KCM_SOCKET "socket_path"
+ #define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */
+ 
++/* Certificate mapping rules */
++#define CONFDB_CERTMAP_BASEDN "cn=certmap,cn=config"
++#define CONFDB_CERTMAP_NAME "cn"
++#define CONFDB_CERTMAP_MAPRULE "maprule"
++#define CONFDB_CERTMAP_MATCHRULE "matchrule"
++#define CONFDB_CERTMAP_DOMAINS "domains"
++#define CONFDB_CERTMAP_PRIORITY "priority"
++
+ /* Prompting */
+ #define CONFDB_PC_CONF_ENTRY "config/prompting"
+ #define CONFDB_PC_TYPE_PASSWORD "password"
+@@ -681,6 +689,21 @@ int confdb_get_sub_sections(TALLOC_CTX *mem_ctx,
+                             const char *section,
+                             char ***sections,
+                             int *num_sections);
++
++/**
++ * @brief Convenience function to write the certificate mapping and matching
++ * rules from the configuration database to the cache of a domain
++ *
++ * @param[in] cdb The connection object to the confdb
++ * @param[in] dom Target domain where to rules should be written to
++ *
++ * @return 0 - Successfully retrieved the entry (or used the default)
++ * @return ENOMEM - There was insufficient memory to complete the operation
++ * @return EINVAL - Typically internal processing error
++ */
++int confdb_certmap_to_sysdb(struct confdb_ctx *cdb,
++                            struct sss_domain_info *dom);
++
+ /**
+  * @}
+  */
+diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
+index 997ba5aec..79e366875 100644
+--- a/src/config/cfg_rules.ini
++++ b/src/config/cfg_rules.ini
+@@ -17,6 +17,7 @@ section_re = ^secrets/kcm$
+ section_re = ^domain/[^/\@]\+$
+ section_re = ^domain/[^/\@]\+/[^/\@]\+$
+ section_re = ^application/[^/\@]\+$
++section_re = ^certmap/[^/\@]\+/[^/\@]\+$
+ 
+ 
+ [rule/allowed_sssd_options]
+@@ -765,3 +766,12 @@ option = use_fully_qualified_names
+ 
+ [rule/sssd_checks]
+ validator = sssd_checks
++
++[rule/allowed_certmap_options]
++validator = ini_allowed_options
++section_re = ^certmap/[^/\@]\+/[^/\@]\+$
++
++option = matchrule
++option = maprule
++option = priority
++option = domains
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 018ec22ab..a2bc8ed3b 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -737,6 +737,11 @@ errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb,
+                              struct certmap_info **certmaps,
+                              bool user_name_hint);
+ 
++errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx,
++                                           struct ldb_message *msg,
++                                           const char **attr_map,
++                                           struct certmap_info **certmap);
++
+ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                           struct certmap_info ***certmaps,
+                           bool *user_name_hint);
+diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c
+index eda20f5a7..4cc5b5b41 100644
+--- a/src/db/sysdb_certmap.c
++++ b/src/db/sysdb_certmap.c
+@@ -22,6 +22,7 @@
+ 
+ #include "util/util.h"
+ #include "db/sysdb_private.h"
++#include "lib/certmap/sss_certmap.h"
+ 
+ static errno_t sysdb_create_certmap_container(struct sysdb_ctx *sysdb,
+                                               bool user_name_hint)
+@@ -153,7 +154,7 @@ static errno_t sysdb_certmap_add(struct sysdb_ctx *sysdb,
+         }
+     }
+ 
+-    if (certmap->domains != NULL) {
++    if (certmap->domains != NULL && certmap->domains[0] != NULL) {
+         for (c = 0; certmap->domains[c] != NULL; c++);
+         el = talloc_zero(tmp_ctx, struct ldb_message_element);
+         if (el == NULL) {
+@@ -285,28 +286,151 @@ done:
+     return ret;
+ }
+ 
++enum certmap_info_member {
++    SSS_CMIM_NAME = 0,
++    SSS_CMIM_MAPPING_RULE,
++    SSS_CMIM_MATCHING_RULE,
++    SSS_CMIM_PRIORITY,
++    SSS_CMIM_DOMAINS,
++
++    SSS_CMIM_SENTINEL
++};
++
++errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx,
++                                           struct ldb_message *msg,
++                                           const char **attr_map,
++                                           struct certmap_info **certmap)
++{
++    int ret;
++    size_t d;
++    size_t num_values;
++    struct certmap_info *map = NULL;
++    const char *tmp_str;
++    uint64_t tmp_uint;
++    struct ldb_message_element *tmp_el;
++
++    if (msg == NULL || attr_map == NULL || certmap == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input.\n");
++        return EINVAL;
++    }
++
++    for (d = 0; d < SSS_CMIM_SENTINEL; d++) {
++        if (attr_map[d] == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid attribute map");
++            return EINVAL;
++        }
++    }
++
++    map = talloc_zero(mem_ctx, struct certmap_info);
++    if (map == NULL) {
++        return ENOMEM;
++    }
++
++    tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_NAME], NULL);
++    if (tmp_str == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n",
++                                    ldb_dn_get_linearized(msg->dn));
++        ret = EINVAL;
++        goto done;
++    }
++
++    map->name = talloc_strdup(map, tmp_str);
++    if (map->name == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MAPPING_RULE],
++                                          NULL);
++    if (tmp_str != NULL) {
++        map->map_rule = talloc_strdup(map, tmp_str);
++        if (map->map_rule == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
++    tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MATCHING_RULE],
++                                          NULL);
++    if (tmp_str != NULL) {
++        map->match_rule = talloc_strdup(map, tmp_str);
++        if (map->match_rule == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
++    tmp_uint = ldb_msg_find_attr_as_uint64(msg, attr_map[SSS_CMIM_PRIORITY],
++                                           (uint64_t) -1);
++    if (tmp_uint != (uint64_t) -1) {
++        if (tmp_uint > UINT32_MAX) {
++            DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n",
++                                     (unsigned long) tmp_uint);
++            ret = EINVAL;
++            goto done;
++        }
++
++        map->priority = (uint32_t) tmp_uint;
++    } else {
++        map->priority = SSS_CERTMAP_MIN_PRIO;
++    }
++
++    tmp_el = ldb_msg_find_element(msg, attr_map[SSS_CMIM_DOMAINS]);
++    if (tmp_el != NULL) {
++        num_values = tmp_el->num_values;
++    } else {
++        num_values = 0;
++    }
++
++    map->domains = talloc_zero_array(map, const char *, num_values + 1);
++    if (map->domains == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
++    for (d = 0; d < num_values; d++) {
++        map->domains[d] = talloc_strndup(map->domains,
++                                         (char *) tmp_el->values[d].data,
++                                         tmp_el->values[d].length);
++        if (map->domains[d] == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
++    *certmap = map;
++
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        talloc_free(map);
++    }
++
++    return ret;
++}
++
+ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                           struct certmap_info ***certmaps, bool *user_name_hint)
+ {
+     size_t c;
+-    size_t d;
+     struct ldb_dn *container_dn = NULL;
+     int ret;
+     struct certmap_info **maps = NULL;
+     TALLOC_CTX *tmp_ctx = NULL;
+     struct ldb_result *res;
+-    const char *tmp_str;
+-    uint64_t tmp_uint;
+-    struct ldb_message_element *tmp_el;
+     const char *attrs[] = {SYSDB_NAME,
+-                           SYSDB_CERTMAP_PRIORITY,
+-                           SYSDB_CERTMAP_MATCHING_RULE,
+                            SYSDB_CERTMAP_MAPPING_RULE,
++                           SYSDB_CERTMAP_MATCHING_RULE,
++                           SYSDB_CERTMAP_PRIORITY,
+                            SYSDB_CERTMAP_DOMAINS,
+                            NULL};
+     const char *config_attrs[] = {SYSDB_CERTMAP_USER_NAME_HINT,
+                                   NULL};
+-    size_t num_values;
+     bool hint = false;
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -355,86 +479,13 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+     }
+ 
+     for (c = 0; c < res->count; c++) {
+-        maps[c] = talloc_zero(maps, struct certmap_info);
+-        if (maps[c] == NULL) {
+-            ret = ENOMEM;
+-            goto done;
+-        }
+-        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL);
+-        if (tmp_str == NULL) {
+-            DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n",
+-                                       ldb_dn_get_linearized(res->msgs[c]->dn));
+-            ret = EINVAL;
+-            goto done;
+-        }
+-
+-        maps[c]->name = talloc_strdup(maps, tmp_str);
+-        if (maps[c]->name == NULL) {
+-            ret = ENOMEM;
+-            goto done;
+-        }
+-
+-        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c],
+-                                              SYSDB_CERTMAP_MAPPING_RULE, NULL);
+-        if (tmp_str != NULL) {
+-            maps[c]->map_rule = talloc_strdup(maps, tmp_str);
+-            if (maps[c]->map_rule == NULL) {
+-                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-        }
+-
+-        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c],
+-                                              SYSDB_CERTMAP_MATCHING_RULE, NULL);
+-        if (tmp_str != NULL) {
+-            maps[c]->match_rule = talloc_strdup(maps, tmp_str);
+-            if (maps[c]->match_rule == NULL) {
+-                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-        }
+-
+-        tmp_uint = ldb_msg_find_attr_as_uint64(res->msgs[c],
+-                                               SYSDB_CERTMAP_PRIORITY,
+-                                               (uint64_t) -1);
+-        if (tmp_uint != (uint64_t) -1) {
+-            if (tmp_uint > UINT32_MAX) {
+-                DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n",
+-                                         (unsigned long) tmp_uint);
+-                ret = EINVAL;
+-                goto done;
+-            }
+-
+-            maps[c]->priority = (uint32_t) tmp_uint;
+-        }
+-
+-        tmp_el = ldb_msg_find_element(res->msgs[c], SYSDB_CERTMAP_DOMAINS);
+-        if (tmp_el != NULL) {
+-            num_values = tmp_el->num_values;
+-        } else {
+-            num_values = 0;
+-        }
+-
+-        maps[c]->domains = talloc_zero_array(maps[c], const char *,
+-                                             num_values + 1);
+-        if (maps[c]->domains == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+-            ret = ENOMEM;
++        ret = sysdb_ldb_msg_attr_to_certmap_info(maps, res->msgs[c], attrs,
++                                                 &maps[c]);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "sysdb_ldb_msg_attr_to_certmap_info failed.\n");
+             goto done;
+         }
+-
+-        for (d = 0; d < num_values; d++) {
+-            maps[c]->domains[d] = talloc_strndup(maps[c]->domains,
+-                                            (char *) tmp_el->values[d].data,
+-                                            tmp_el->values[d].length);
+-            if (maps[c]->domains[d] == NULL) {
+-                DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-        }
+     }
+ 
+     ret = EOK;
+diff --git a/src/external/cwrap.m4 b/src/external/cwrap.m4
+index b8489cc76..6e3487c13 100644
+--- a/src/external/cwrap.m4
++++ b/src/external/cwrap.m4
+@@ -28,3 +28,8 @@ AC_DEFUN([AM_CHECK_NSS_WRAPPER],
+ [
+     AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER)
+ ])
++
++AC_DEFUN([AM_CHECK_PAM_WRAPPER],
++[
++    AM_CHECK_WRAPPER(pam_wrapper, HAVE_PAM_WRAPPER)
++])
+diff --git a/src/external/intgcheck.m4 b/src/external/intgcheck.m4
+index ef81ec718..d73c5dfb1 100644
+--- a/src/external/intgcheck.m4
++++ b/src/external/intgcheck.m4
+@@ -75,6 +75,7 @@ AC_DEFUN([SSS_ENABLE_INTGCHECK_REQS], [
+     if test x"$enable_intgcheck_reqs" = xyes; then
+         SSS_INTGCHECK_REQ([HAVE_UID_WRAPPER], [uid_wrapper])
+         SSS_INTGCHECK_REQ([HAVE_NSS_WRAPPER], [nss_wrapper])
++        SSS_INTGCHECK_REQ([HAVE_PAM_WRAPPER], [pam_wrapper])
+         SSS_INTGCHECK_REQ([HAVE_SLAPD], [slapd])
+         SSS_INTGCHECK_REQ([HAVE_LDAPMODIFY], [ldapmodify])
+         SSS_INTGCHECK_REQ([HAVE_FAKEROOT], [fakeroot])
+diff --git a/src/external/test_ca.m4 b/src/external/test_ca.m4
+index 2cdb3c750..bb4872698 100644
+--- a/src/external/test_ca.m4
++++ b/src/external/test_ca.m4
+@@ -58,7 +58,7 @@ AC_DEFUN([AM_CHECK_TEST_CA],
+             AC_MSG_NOTICE([Could not find p11tool])
+         fi
+ 
+-        AM_CONDITIONAL([BUILD_TEST_CA], [test -x "$OPENSSL" -a -x "$SSH_KEYGEN" -a -x "$SOFTHSM2_PATH" -a -x "$SOFTHSM2_UTIL" -a -x "$P11TOOL"])
++        AM_CONDITIONAL([BUILD_TEST_CA], [test -x "$OPENSSL" -a -x "$SSH_KEYGEN" -a -r "$SOFTHSM2_PATH" -a -x "$SOFTHSM2_UTIL" -a -x "$P11TOOL"])
+     fi
+ 
+     AM_COND_IF([BUILD_TEST_CA],
+diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c
+index ee9aec208..b01830aec 100644
+--- a/src/lib/certmap/sss_cert_content_crypto.c
++++ b/src/lib/certmap/sss_cert_content_crypto.c
+@@ -771,11 +771,25 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+         ret = EIO;
+         goto done;
+     }
+-    if (!(X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) {
+-        ret = EINVAL;
+-        goto done;
++    if ((X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) {
++        cont->key_usage = X509_get_key_usage(cert);
++    } else {
++        /* According to X.509 https://www.itu.int/rec/T-REC-X.509-201610-I
++         * section 13.3.2 "Certificate match" "keyUsage matches if all of the
++         * bits set in the presented value are also set in the key usage
++         * extension in the stored attribute value, or if there is no key
++         * usage extension in the stored attribute value;". So we set all bits
++         * in our key_usage to make sure everything matches is keyUsage is not
++         * set in the certificate.
++         *
++         * Please note that NSS currently
++         * (https://bugzilla.mozilla.org/show_bug.cgi?id=549952) does not
++         * support 'decipherOnly' and will only use 0xff in this case. To have
++         * a consistent behavior with both libraries we will use UINT32_MAX
++         * for NSS as well. Since comparisons should be always done with a
++         * bit-wise and-operation the difference should not matter. */
++        cont->key_usage = UINT32_MAX;
+     }
+-    cont->key_usage = X509_get_key_usage(cert);
+ 
+     ret = get_extended_key_usage_oids(cont, cert,
+                                       &(cont->extended_key_usage_oids));
+diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c
+index ed7ce24c4..cef1efc26 100644
+--- a/src/lib/certmap/sss_cert_content_nss.c
++++ b/src/lib/certmap/sss_cert_content_nss.c
+@@ -887,8 +887,25 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-
+-    cont->key_usage = cert->keyUsage;
++    /* According to X.509 https://www.itu.int/rec/T-REC-X.509-201610-I
++     * section 13.3.2 "Certificate match" "keyUsage matches if all of the
++     * bits set in the presented value are also set in the key usage
++     * extension in the stored attribute value, or if there is no key
++     * usage extension in the stored attribute value;". So we set all bits
++     * in our key_usage to make sure everything matches is keyUsage is not
++     * set in the certificate.
++     *
++     * Please note that NSS currently
++     * (https://bugzilla.mozilla.org/show_bug.cgi?id=549952) does not
++     * support 'decipherOnly' and will only use 0xff in this case. To have
++     * a consistent behavior with both libraries we will use UINT32_MAX
++     * for NSS as well. Since comparisons should be always done with a
++     * bit-wise and-operation the difference should not matter. */
++    if (cert->keyUsagePresent == PR_FALSE) {
++        cont->key_usage = UINT32_MAX;
++    } else {
++        cont->key_usage = cert->keyUsage;
++    }
+ 
+     ret = get_extended_key_usage_oids(cont, cert,
+                                       &(cont->extended_key_usage_oids));
+diff --git a/src/man/sss-certmap.5.xml b/src/man/sss-certmap.5.xml
+index db258d14a..10343625e 100644
+--- a/src/man/sss-certmap.5.xml
++++ b/src/man/sss-certmap.5.xml
+@@ -92,6 +92,15 @@
+                     <para>
+                         Example: &lt;SUBJECT&gt;.*,DC=MY,DC=DOMAIN
+                     </para>
++                    <para>
++                        Please note that the characters "^.[$()|*+?{\" have a
++                        special meaning in regular expressions and must be
++                        escaped with the help of the '\' character so that they
++                        are matched as ordinary characters.
++                    </para>
++                    <para>
++                        Example: &lt;SUBJECT&gt;^CN=.* \(Admin\),DC=MY,DC=DOMAIN$
++                    </para>
+                     </listitem>
+                 </varlistentry>
+                 <varlistentry>
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 0e1a97a31..8adbb8de9 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -3452,6 +3452,135 @@ ldap_user_extra_attrs = phone:telephoneNumber
+         </para>
+     </refsect1>
+ 
++    <refsect1 id='certmap'>
++        <title>CERTIFICATE MAPPING SECTION</title>
++        <para>
++            To allow authentication with Smartcards and certificates SSSD must
++            be able to map certificates to users. This can be done by adding the
++            full certificate to the LDAP object of the user or to a local
++            override. While using the full certificate is required to use the
++            Smartcard authentication feature of SSH (see
++                <citerefentry>
++                    <refentrytitle>sss_ssh_authorizedkeys</refentrytitle>
++                    <manvolnum>8</manvolnum>
++                </citerefentry>
++            for details) it might be cumbersome or not even possible to do this
++            for the general case where local services use PAM for
++            authentication.
++        </para>
++        <para>
++            To make the mapping more flexible mapping and matching rules were
++            added to SSSD (see
++                <citerefentry>
++                    <refentrytitle>sss-certmap</refentrytitle>
++                    <manvolnum>5</manvolnum>
++                </citerefentry>
++            for details).
++        </para>
++        <para>
++            A mapping and matching rule can be added to the SSSD configuration
++            in a section on its own with a name like
++            <quote>[certmap/<replaceable>DOMAIN_NAME</replaceable>/<replaceable>RULE_NAME</replaceable>]</quote>.
++            In this section the following options are allowed:
++        </para>
++        <variablelist>
++            <varlistentry>
++                <term>matchrule (string)</term>
++                <listitem>
++                    <para>
++                        Only certificates from the Smartcard which matches this
++                        rule will be processed, all others are ignored.
++                    </para>
++                    <para>
++                        Default: KRB5:&lt;EKU&gt;clientAuth, i.e. only
++                        certificates which have the Extended Key Usage
++                        <quote>clientAuth</quote>
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>maprule (string)</term>
++                <listitem>
++                    <para>
++                        Defines how the user is found for a given certificate.
++                    </para>
++                    <para>
++                        Default:
++                        <itemizedlist>
++                            <listitem>
++                                <para>LDAP:(userCertificate;binary={cert!bin})
++                                for LDAP based providers like
++                                <quote>ldap</quote>, <quote>AD</quote> or
++                                <quote>ipa</quote>.</para>
++                            </listitem>
++                            <listitem>
++                                <para>The RULE_NAME for the <quote>files</quote>
++                                provider which tries to find a user with the
++                                same name.</para>
++                            </listitem>
++                        </itemizedlist>
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>domains (string)</term>
++                <listitem>
++                    <para>
++                        Comma separated list of domain names the rule should be
++                        applied. By default a rule is only valid in the domain
++                        configured in sssd.conf. If the provider supports
++                        subdomains this option can be used to add the rule to
++                        subdomains as well.
++                    </para>
++                    <para>
++                        Default: the configured domain in sssd.conf
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>priority (integer)</term>
++                <listitem>
++                    <para>
++                        Unsigned integer value defining the priority of the
++                        rule. The higher the number the lower the priority.
++                        <quote>0</quote> stands for the highest priority while
++                        <quote>4294967295</quote> is the lowest.
++                    </para>
++                    <para>
++                        Default: the lowest priority
++                    </para>
++                </listitem>
++            </varlistentry>
++        </variablelist>
++        <para>
++            To make the configuration simple and reduce the amount of
++            configuration options the <quote>files</quote> provider has some
++            special properties:
++            <itemizedlist>
++                <listitem>
++                    <para>
++                        if maprule is not set the RULE_NAME name is assumed to
++                        be the name of the matching user
++                    </para>
++                </listitem>
++                <listitem>
++                    <para>
++                        if a maprule is used both a single user name or a
++                        template like
++                        <quote>{subject_rfc822_name.short_name}</quote> must
++                        be in braces like e.g. <quote>(username)</quote> or
++                        <quote>({subject_rfc822_name.short_name})</quote>
++                    </para>
++                </listitem>
++                <listitem>
++                    <para>
++                        the <quote>domains</quote> option is ignored
++                    </para>
++                </listitem>
++            </itemizedlist>
++        </para>
++    </refsect1>
++
+     <refsect1 id='example'>
+         <title>EXAMPLES</title>
+         <para>
+@@ -3494,6 +3623,26 @@ enumerate = False
+ <programlisting>
+ [domain/ipa.com/child.ad.com]
+ use_fully_qualified_names = false
++</programlisting>
++        </para>
++        <para>
++            3. The following example shows the configuration for two certificate
++            mapping rules. The first is valid for the configured domain
++            <quote>my.domain</quote> and additionally for the subdomains
++            <quote>your.domain</quote> and uses the full certificate in the
++            search filter. The second example is valid for the domain
++            <quote>files</quote> where it is assumed the files provider is used
++            for this domain and contains a matching rule for the local user
++            <quote>myname</quote>.
++<programlisting>
++[certmap/my.domain/rule_name]
++matchrule = &lt;ISSUER&gt;^CN=My-CA,DC=MY,DC=DOMAIN$
++maprule = (userCertificate;binary={cert!bin})
++domains = my.domain, your.domain
++priority = 10
++
++[certmap/files/myname]
++matchrule = &lt;ISSUER&gt;^CN=My-CA,DC=MY,DC=DOMAIN$&lt;SUBJECT&gt;^CN=User.Name,DC=MY,DC=DOMAIN$
+ </programlisting>
+         </para>
+     </refsect1>
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index 09397852b..fb24a28e1 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -427,6 +427,22 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx,
+         return ret;
+     }
+ 
++    ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialize certificate mapping rules. "
++              "Authentication with certificates/Smartcards might not work "
++              "as expected.\n");
++        /* not fatal, ignored */
++    }
++
++    ret = sdap_init_certmap(sdap_id_ctx, sdap_id_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialized certificate mapping.\n");
++        return ret;
++    }
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/files/files_auth.c b/src/providers/files/files_auth.c
+new file mode 100644
+index 000000000..b71de6971
+--- /dev/null
++++ b/src/providers/files/files_auth.c
+@@ -0,0 +1,69 @@
++/*
++    SSSD
++
++    files_auth.c - PAM operations on the files provider
++
++    Copyright (C) 2018 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <security/pam_modules.h>
++
++#include "providers/data_provider/dp.h"
++#include "providers/data_provider.h"
++#include "providers/files/files_private.h"
++#include "util/cert.h"
++
++struct files_auth_ctx {
++    struct pam_data *pd;
++};
++
++struct tevent_req *
++files_auth_handler_send(TALLOC_CTX *mem_ctx,
++                        void *unused,
++                        struct pam_data *pd,
++                        struct dp_req_params *params)
++{
++    struct files_auth_ctx *state;
++    struct tevent_req *req;
++
++    req = tevent_req_create(mem_ctx, &state, struct files_auth_ctx);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++
++    state->pd = pd;
++    state->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
++
++    tevent_req_done(req);
++    tevent_req_post(req, params->ev);
++    return req;
++}
++
++errno_t files_auth_handler_recv(TALLOC_CTX *mem_ctx,
++                                struct tevent_req *req,
++                                struct pam_data **_data)
++{
++    struct files_auth_ctx *state = NULL;
++
++    state = tevent_req_data(req, struct files_auth_ctx);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    *_data = talloc_steal(mem_ctx, state->pd);
++
++    return EOK;
++}
+diff --git a/src/providers/files/files_certmap.c b/src/providers/files/files_certmap.c
+new file mode 100644
+index 000000000..7d90a1fec
+--- /dev/null
++++ b/src/providers/files/files_certmap.c
+@@ -0,0 +1,186 @@
++/*
++    SSSD
++
++    files_init.c - Initialization of the files provider
++
++    Copyright (C) 2018 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "providers/files/files_private.h"
++#include "util/util.h"
++#include "util/cert.h"
++#include "lib/certmap/sss_certmap.h"
++
++struct priv_sss_debug {
++    int level;
++};
++
++static void ext_debug(void *private, const char *file, long line,
++                      const char *function, const char *format, ...)
++{
++    va_list ap;
++    struct priv_sss_debug *data = private;
++    int level = SSSDBG_OP_FAILURE;
++
++    if (data != NULL) {
++        level = data->level;
++    }
++
++    if (DEBUG_IS_SET(level)) {
++        va_start(ap, format);
++        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
++                      format, ap);
++        va_end(ap);
++    }
++}
++
++errno_t files_init_certmap(TALLOC_CTX *mem_ctx, struct files_id_ctx *id_ctx)
++{
++    int ret;
++    bool hint;
++    struct certmap_info **certmap_list = NULL;
++    size_t c;
++
++    ret = sysdb_get_certmap(mem_ctx, id_ctx->be->domain->sysdb,
++                            &certmap_list, &hint);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
++        goto done;
++    }
++
++    if (certmap_list == NULL || *certmap_list == NULL) {
++        DEBUG(SSSDBG_TRACE_ALL, "No certmap data, nothing to do.\n");
++        ret = EOK;
++        goto done;
++    }
++
++    ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &id_ctx->sss_certmap_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
++        goto done;
++    }
++
++    for (c = 0; certmap_list[c] != NULL; c++) {
++        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
++                                certmap_list[c]->name,
++                                certmap_list[c]->priority,
++                                certmap_list[c]->match_rule,
++                                certmap_list[c]->map_rule);
++
++        ret = sss_certmap_add_rule(id_ctx->sss_certmap_ctx,
++                                   certmap_list[c]->priority,
++                                   certmap_list[c]->match_rule,
++                                   certmap_list[c]->map_rule,
++                                   certmap_list[c]->domains);
++        if (ret != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "sss_certmap_add_rule failed for rule [%s] "
++                  "with error [%d][%s], skipping. "
++                  "Please check for typos and if rule syntax is supported.\n",
++                  certmap_list[c]->name, ret, sss_strerror(ret));
++            continue;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(certmap_list);
++
++    return ret;
++}
++
++errno_t files_map_cert_to_user(struct files_id_ctx *id_ctx,
++                               struct dp_id_data *data)
++{
++    errno_t ret;
++    char *filter;
++    char *user;
++    struct ldb_message *msg = NULL;
++    struct sysdb_attrs *attrs = NULL;
++    TALLOC_CTX *tmp_ctx;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
++        return ENOMEM;
++    }
++
++    ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, data->filter_value, "",
++                                         id_ctx->sss_certmap_ctx,
++                                         id_ctx->domain, &filter);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "sss_cert_derb64_to_ldap_filter failed.\n");
++        goto done;
++    }
++    if (filter == NULL || filter[0] != '('
++                       || filter[strlen(filter) - 1] != ')') {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "sss_cert_derb64_to_ldap_filter returned bad filter [%s].\n",
++              filter);
++        ret = EINVAL;
++        goto done;
++    }
++
++    filter[strlen(filter) - 1] = '\0';
++    user = sss_create_internal_fqname(tmp_ctx, &filter[1],
++                                      id_ctx->domain->name);
++    if (user == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++    DEBUG(SSSDBG_TRACE_ALL, "Certificate mapped to user: [%s].\n", user);
++
++    ret = sysdb_search_user_by_name(tmp_ctx, id_ctx->domain, user, NULL, &msg);
++    if (ret == EOK) {
++        attrs = sysdb_new_attrs(tmp_ctx);
++        if (attrs == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++
++        ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_MAPPED_CERT,
++                                          data->filter_value);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n");
++            goto done;
++        }
++
++        ret = sysdb_set_entry_attr(id_ctx->domain->sysdb, msg->dn, attrs,
++                                   SYSDB_MOD_ADD);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
++            goto done;
++        }
++    } else if (ret == ENOENT) {
++        DEBUG(SSSDBG_TRACE_ALL, "Mapped user [%s] not found.\n", user);
++        ret = EOK;
++        goto done;
++    } else {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n");
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++
++    return ret;
++}
+diff --git a/src/providers/files/files_id.c b/src/providers/files/files_id.c
+index 41314c66b..f6f8c7311 100644
+--- a/src/providers/files/files_id.c
++++ b/src/providers/files/files_id.c
+@@ -87,6 +87,26 @@ files_account_info_handler_send(TALLOC_CTX *mem_ctx,
+                        ? true \
+                        : false;
+         break;
++    case BE_REQ_BY_CERT:
++        if (data->filter_type != BE_FILTER_CERT) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Unexpected filter type for lookup by cert: %d\n",
++                  data->filter_type);
++            ret = EINVAL;
++            goto immediate;
++        }
++        if (id_ctx->sss_certmap_ctx == NULL) {
++            DEBUG(SSSDBG_TRACE_ALL, "Certificate mapping not configured.\n");
++            ret = EOK;
++            goto immediate;
++        }
++
++        ret = files_map_cert_to_user(id_ctx, data);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "files_map_cert_to_user failed");
++        }
++        goto immediate;
++        break;
+     default:
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               "Unexpected entry type: %d\n", data->entry_type & BE_REQ_TYPE_MASK);
+diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c
+index 746c04af1..1ce4bcf27 100644
+--- a/src/providers/files/files_init.c
++++ b/src/providers/files/files_init.c
+@@ -189,6 +189,23 @@ int sssm_files_init(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialize certificate mapping rules. "
++              "Authentication with certificates/Smartcards might not work "
++              "as expected.\n");
++        /* not fatal, ignored */
++    } else {
++        ret = files_init_certmap(ctx, ctx);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "files_init_certmap failed. "
++                  "Authentication with certificates/Smartcards might not work "
++                  "as expected.\n");
++            /* not fatal, ignored */
++        }
++    }
++
+     *_module_data = ctx;
+     ret = EOK;
+ done:
+@@ -224,3 +241,15 @@ int sssm_files_id_init(TALLOC_CTX *mem_ctx,
+ 
+     return EOK;
+ }
++
++int sssm_files_auth_init(TALLOC_CTX *mem_ctx,
++                         struct be_ctx *be_ctx,
++                         void *module_data,
++                         struct dp_method *dp_methods)
++{
++    dp_set_method(dp_methods, DPM_AUTH_HANDLER,
++                  files_auth_handler_send, files_auth_handler_recv, NULL, void,
++                  struct pam_data, struct pam_data *);
++
++    return EOK;
++}
+diff --git a/src/providers/files/files_private.h b/src/providers/files/files_private.h
+index f44e6d458..fd1781930 100644
+--- a/src/providers/files/files_private.h
++++ b/src/providers/files/files_private.h
+@@ -38,6 +38,7 @@ struct files_id_ctx {
+     struct be_ctx *be;
+     struct sss_domain_info *domain;
+     struct files_ctx *fctx;
++    struct sss_certmap_ctx *sss_certmap_ctx;
+ 
+     const char **passwd_files;
+     const char **group_files;
+@@ -71,4 +72,20 @@ errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx,
+ void files_account_info_finished(struct files_id_ctx *id_ctx,
+                                  int req_type,
+                                  errno_t ret);
++
++/* files_auth.c */
++struct tevent_req *files_auth_handler_send(TALLOC_CTX *mem_ctx,
++                                           void *unused,
++                                           struct pam_data *pd,
++                                           struct dp_req_params *params);
++
++errno_t files_auth_handler_recv(TALLOC_CTX *mem_ctx,
++                                struct tevent_req *req,
++                                struct pam_data **_data);
++
++/* files_certmap.c */
++errno_t files_init_certmap(TALLOC_CTX *mem_ctx, struct files_id_ctx *id_ctx);
++
++errno_t files_map_cert_to_user(struct files_id_ctx *id_ctx,
++                               struct dp_id_data *data);
+ #endif /* __FILES_PRIVATE_H_ */
+diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
+index 3714d5d79..7998cc8ff 100644
+--- a/src/providers/ldap/ldap_init.c
++++ b/src/providers/ldap/ldap_init.c
+@@ -439,6 +439,22 @@ static errno_t ldap_init_misc(struct be_ctx *be_ctx,
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
++    ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialize certificate mapping rules. "
++              "Authentication with certificates/Smartcards might not work "
++              "as expected.\n");
++        /* not fatal, ignored */
++    }
++
++    ret = sdap_init_certmap(id_ctx, id_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialized certificate mapping.\n");
++        return ret;
++    }
++
+     return EOK;
+ }
+ 
+diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c
+index 208c415ac..a49b31528 100644
+--- a/src/responder/common/responder_dp.c
++++ b/src/responder/common/responder_dp.c
+@@ -598,15 +598,17 @@ static int sss_dp_account_files_params(struct sss_domain_info *dom,
+                                        enum sss_dp_acct_type *_type_out,
+                                        const char **_opt_name_out)
+ {
+-    if (sss_domain_get_state(dom) != DOM_INCONSISTENT) {
++    if (type_in != SSS_DP_CERT) {
++        if (sss_domain_get_state(dom) != DOM_INCONSISTENT) {
++            DEBUG(SSSDBG_TRACE_INTERNAL,
++                  "The entries in the files domain are up-to-date\n");
++            return EOK;
++        }
++
+         DEBUG(SSSDBG_TRACE_INTERNAL,
+-              "The entries in the files domain are up-to-date\n");
+-        return EOK;
++              "Domain files is not consistent, issuing update\n");
+     }
+ 
+-    DEBUG(SSSDBG_TRACE_INTERNAL,
+-          "Domain files is not consistent, issuing update\n");
+-
+     switch(type_in) {
+     case SSS_DP_USER:
+     case SSS_DP_GROUP:
+@@ -620,12 +622,16 @@ static int sss_dp_account_files_params(struct sss_domain_info *dom,
+         *_type_out = type_in;
+         *_opt_name_out = DP_REQ_OPT_FILES_INITGR;
+         return EAGAIN;
++    case SSS_DP_CERT:
++        /* Let the backend handle certificate mapping for local users */
++        *_type_out = type_in;
++        *_opt_name_out = opt_name_in;
++        return EAGAIN;
+     /* These are not handled by the files provider, just fall back */
+     case SSS_DP_NETGR:
+     case SSS_DP_SERVICES:
+     case SSS_DP_SECID:
+     case SSS_DP_USER_AND_GROUP:
+-    case SSS_DP_CERT:
+     case SSS_DP_WILDCARD_USER:
+     case SSS_DP_WILDCARD_GROUP:
+         return EOK;
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 319362a95..ccb2037b1 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -123,7 +123,7 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+ bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
+ 
+ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+-                                struct certmap_info **certmap_list);
++                                struct sss_domain_info *domains);
+ 
+ errno_t
+ pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index 00302be75..139f4260e 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1461,7 +1461,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
+             if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+                       "No certificate returned, authentication failed.\n");
+-                ret = ENOENT;
++                preq->pd->pam_status = PAM_AUTH_ERR;
++                pam_reply(preq);
++                return;
+             } else {
+                 ret = pam_check_user_search(preq);
+             }
+@@ -1762,7 +1764,7 @@ static void pam_forwarder_cb(struct tevent_req *req)
+         goto done;
+     }
+ 
+-    ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains->certmaps);
++    ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE,
+               "p11_refresh_certmap_ctx failed, "
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index c7e57be17..c58a8bedd 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -141,11 +141,14 @@ static void ext_debug(void *private, const char *file, long line,
+ }
+ 
+ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+-                                struct certmap_info **certmap_list)
++                                struct sss_domain_info *domains)
+ {
+     int ret;
+     struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+     size_t c;
++    struct sss_domain_info *dom;
++    bool certmap_found = false;
++    struct certmap_info **certmap_list;
+ 
+     ret = sss_certmap_init(pctx, ext_debug, NULL, &sss_certmap_ctx);
+     if (ret != EOK) {
+@@ -153,7 +156,15 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+         goto done;
+     }
+ 
+-    if (certmap_list == NULL || *certmap_list == NULL) {
++    DLIST_FOR_EACH(dom, domains) {
++        certmap_list = dom->certmaps;
++        if (certmap_list != NULL && *certmap_list != NULL) {
++            certmap_found = true;
++            break;
++        }
++    }
++
++    if (!certmap_found) {
+         /* Try to add default matching rule */
+         ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
+                                    CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL);
+@@ -165,24 +176,32 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+         goto done;
+     }
+ 
+-    for (c = 0; certmap_list[c] != NULL; c++) {
+-        DEBUG(SSSDBG_TRACE_ALL,
+-              "Trying to add rule [%s][%d][%s][%s].\n",
+-              certmap_list[c]->name, certmap_list[c]->priority,
+-              certmap_list[c]->match_rule, certmap_list[c]->map_rule);
+-
+-        ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority,
+-                                   certmap_list[c]->match_rule,
+-                                   certmap_list[c]->map_rule,
+-                                   certmap_list[c]->domains);
+-        if (ret != 0) {
+-            DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "sss_certmap_add_rule failed for rule [%s] "
+-                  "with error [%d][%s], skipping. "
+-                  "Please check for typos and if rule syntax is supported.\n",
+-                  certmap_list[c]->name, ret, sss_strerror(ret));
++    DLIST_FOR_EACH(dom, domains) {
++        certmap_list = dom->certmaps;
++        if (certmap_list == NULL || *certmap_list == NULL) {
+             continue;
+         }
++
++        for (c = 0; certmap_list[c] != NULL; c++) {
++            DEBUG(SSSDBG_TRACE_ALL,
++                  "Trying to add rule [%s][%d][%s][%s].\n",
++                  certmap_list[c]->name, certmap_list[c]->priority,
++                  certmap_list[c]->match_rule, certmap_list[c]->map_rule);
++
++            ret = sss_certmap_add_rule(sss_certmap_ctx,
++                                       certmap_list[c]->priority,
++                                       certmap_list[c]->match_rule,
++                                       certmap_list[c]->map_rule,
++                                       certmap_list[c]->domains);
++            if (ret != 0) {
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      "sss_certmap_add_rule failed for rule [%s] "
++                      "with error [%d][%s], skipping. "
++                      "Please check for typos and if rule syntax is supported.\n",
++                      certmap_list[c]->name, ret, sss_strerror(ret));
++                continue;
++            }
++        }
+     }
+ 
+     ret = EOK;
+@@ -203,19 +222,21 @@ errno_t p11_child_init(struct pam_ctx *pctx)
+     int ret;
+     struct certmap_info **certmaps;
+     bool user_name_hint;
+-    struct sss_domain_info *dom = pctx->rctx->domains;
++    struct sss_domain_info *dom;
+ 
+-    ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
+-        return ret;
+-    }
++    DLIST_FOR_EACH(dom, pctx->rctx->domains) {
++        ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
++            return ret;
++        }
+ 
+-    dom->user_name_hint = user_name_hint;
+-    talloc_free(dom->certmaps);
+-    dom->certmaps = certmaps;
++        dom->user_name_hint = user_name_hint;
++        talloc_free(dom->certmaps);
++        dom->certmaps = certmaps;
++    }
+ 
+-    ret = p11_refresh_certmap_ctx(pctx, dom->certmaps);
++    ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n");
+         return ret;
+diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
+index 3091e1abe..c882202a0 100644
+--- a/src/tests/cmocka/test_certmap.c
++++ b/src/tests/cmocka/test_certmap.c
+@@ -46,8 +46,10 @@
+ 
+ #ifdef HAVE_TEST_CA
+ #include "tests/test_CA/SSSD_test_cert_x509_0003.h"
++#include "tests/test_CA/SSSD_test_cert_x509_0004.h"
+ #else
+ #define SSSD_TEST_CERT_0003 ""
++#define SSSD_TEST_CERT_0004 ""
+ #endif
+ 
+ struct priv_sss_debug {
+@@ -960,6 +962,48 @@ void test_sss_cert_get_content_test_cert_0003(void **state)
+     talloc_free(content);
+ }
+ 
++void test_sss_cert_get_content_test_cert_0004(void **state)
++{
++    int ret;
++    uint8_t *der;
++    size_t der_size;
++    struct sss_cert_content *content;
++
++    der = sss_base64_decode(NULL, SSSD_TEST_CERT_0004, &der_size);
++    assert_non_null(der);
++
++    ret = sss_cert_get_content(NULL, der, der_size, &content);
++    assert_int_equal(ret, 0);
++    assert_non_null(content);
++    assert_non_null(content->issuer_str);
++    assert_string_equal(content->issuer_str,
++                        "CN=SSSD test CA,OU=SSSD test,O=SSSD");
++
++    assert_non_null(content->issuer_rdn_list);
++    assert_string_equal(content->issuer_rdn_list[0], "O=SSSD");
++    assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test");
++    assert_string_equal(content->issuer_rdn_list[2], "CN=SSSD test CA");
++    assert_null(content->issuer_rdn_list[3]);
++
++    assert_non_null(content->subject_str);
++    assert_string_equal(content->subject_str,
++                        "CN=SSSD test cert 0004,OU=SSSD test,O=SSSD");
++
++    assert_non_null(content->subject_rdn_list);
++    assert_string_equal(content->issuer_rdn_list[0], "O=SSSD");
++    assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test");
++    assert_string_equal(content->subject_rdn_list[2], "CN=SSSD test cert 0004");
++    assert_null(content->subject_rdn_list[3]);
++
++    assert_int_equal(content->key_usage, UINT32_MAX);
++
++    assert_non_null(content->extended_key_usage_oids);
++    assert_null(content->extended_key_usage_oids[0]);
++
++    assert_null(content->san_list);
++
++    talloc_free(content);
++}
+ 
+ static void test_sss_certmap_match_cert(void **state)
+ {
+@@ -1572,6 +1616,7 @@ int main(int argc, const char *argv[])
+         cmocka_unit_test(test_sss_cert_get_content_2),
+ #ifdef HAVE_TEST_CA
+         cmocka_unit_test(test_sss_cert_get_content_test_cert_0003),
++        cmocka_unit_test(test_sss_cert_get_content_test_cert_0004),
+ #endif
+         cmocka_unit_test(test_sss_certmap_match_cert),
+         cmocka_unit_test(test_sss_certmap_add_mapping_rule),
+diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c
+index a2958de63..f1fdbc8eb 100644
+--- a/src/tests/cmocka/test_config_check.c
++++ b/src/tests/cmocka/test_config_check.c
+@@ -213,6 +213,18 @@ void config_check_test_bad_subdom_option_name(void **state)
+     config_check_test_common(cfg_str, 1, expected_errors);
+ }
+ 
++void config_check_test_bad_certmap_option_name(void **state)
++{
++    char cfg_str[] = "[certmap/files/testuser]\n"
++                     "debug_level = 10\n";
++    const char *expected_errors[] = {
++        "[rule/allowed_certmap_options]: Attribute 'debug_level' is not "
++        "allowed in section 'certmap/files/testuser'. Check for typos.",
++    };
++
++    config_check_test_common(cfg_str, 1, expected_errors);
++}
++
+ void config_check_test_good_sections(void **state)
+ {
+     char cfg_str[] = "[sssd]\n"
+@@ -225,7 +237,8 @@ void config_check_test_good_sections(void **state)
+                      "[secrets/users/1000]\n"
+                      "[ssh]\n"
+                      "[ifp]\n"
+-                     "[pac]\n";
++                     "[pac]\n"
++                     "[certmap/files/testuser]\n";
+     const char *expected_errors[] = { NULL };
+ 
+     config_check_test_common(cfg_str, 0, expected_errors);
+@@ -272,6 +285,7 @@ int main(int argc, const char *argv[])
+         cmocka_unit_test(config_check_test_bad_ifp_option_name),
+         cmocka_unit_test(config_check_test_bad_appdomain_option_name),
+         cmocka_unit_test(config_check_test_bad_subdom_option_name),
++        cmocka_unit_test(config_check_test_bad_certmap_option_name),
+         cmocka_unit_test(config_check_test_good_sections),
+         cmocka_unit_test(config_check_test_inherit_from_in_normal_dom),
+         cmocka_unit_test(config_check_test_inherit_from_in_app_dom),
+diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py
+index 7e5828dde..e97bd409b 100644
+--- a/src/tests/intg/test_pam_responder.py
++++ b/src/tests/intg/test_pam_responder.py
+@@ -27,12 +27,9 @@ import signal
+ import errno
+ import subprocess
+ import time
+-import pytest
+-
+-import config
+ import shutil
+-from util import unindent
+ 
++import config
+ import intg.ds_openldap
+ 
+ import pytest
+@@ -109,24 +106,35 @@ def format_basic_conf(ldap_conn):
+     """).format(**locals())
+ 
+ 
+-def format_pam_cert_auth_conf():
++USER1 = dict(name='user1', passwd='x', uid=10001, gid=20001,
++             gecos='User for tests',
++             dir='/home/user1',
++             shell='/bin/bash')
++
++
++def format_pam_cert_auth_conf(config):
+     """Format a basic SSSD configuration"""
+     return unindent("""\
+         [sssd]
++        debug_level = 10
+         domains = auth_only
+-        services = pam
++        services = pam, nss
+ 
+         [nss]
++        debug_level = 10
+ 
+         [pam]
+         pam_cert_auth = True
++        pam_p11_allowed_services = +pam_sss_service
++        pam_cert_db_path = {config.PAM_CERT_DB_PATH}
+         debug_level = 10
+ 
+         [domain/auth_only]
+-        id_provider = ldap
+-        auth_provider = ldap
+-        chpass_provider = ldap
+-        access_provider = ldap
++        debug_level = 10
++        id_provider = files
++
++        [certmap/auth_only/user1]
++        matchrule = <SUBJECT>.*CN=SSSD test cert 0001.*
+     """).format(**locals())
+ 
+ 
+@@ -193,12 +201,42 @@ def create_sssd_fixture(request):
+     request.addfinalizer(cleanup_sssd_process)
+ 
+ 
++def create_nssdb():
++    os.mkdir(config.SYSCONFDIR + "/pki")
++    os.mkdir(config.SYSCONFDIR + "/pki/nssdb")
++    if subprocess.call(["certutil", "-N", "-d",
++                        "sql:" + config.SYSCONFDIR + "/pki/nssdb/",
++                        "--empty-password"]) != 0:
++        raise Exception("certutil failed")
++
++    pkcs11_txt = open(config.SYSCONFDIR + "/pki/nssdb/pkcs11.txt", "w")
++    pkcs11_txt.write("library=libsoftokn3.so\nname=soft\n" +
++                     "parameters=configdir='sql:" + config.ABS_BUILDDIR +
++                     "/../test_CA/p11_nssdb' " +
++                     "dbSlotDescription='SSSD Test Slot' " +
++                     "dbTokenDescription='SSSD Test Token' " +
++                     "secmod='secmod.db' flags=readOnly)\n\n")
++    pkcs11_txt.close()
++
++
++def cleanup_nssdb():
++    shutil.rmtree(config.SYSCONFDIR + "/pki")
++
++
++def create_nssdb_fixture(request):
++    create_nssdb()
++    request.addfinalizer(cleanup_nssdb)
++
++
+ @pytest.fixture
+-def simple_pam_cert_auth(request):
++def simple_pam_cert_auth(request, passwd_ops_setup):
+     """Setup SSSD with pam_cert_auth=True"""
+-    conf = format_pam_cert_auth_conf()
++    config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH']
++    conf = format_pam_cert_auth_conf(config)
+     create_conf_fixture(request, conf)
+     create_sssd_fixture(request)
++    create_nssdb_fixture(request)
++    passwd_ops_setup.useradd(**USER1)
+     return None
+ 
+ 
+@@ -281,3 +319,50 @@ def env_for_sssctl(request):
+     env_for_sssctl['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH']
+ 
+     return env_for_sssctl
++
++
++def test_sc_auth_wrong_pin(simple_pam_cert_auth, env_for_sssctl):
++
++    sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
++                               "--action=auth", "--service=pam_sss_service"],
++                              universal_newlines=True,
++                              env=env_for_sssctl, stdin=subprocess.PIPE,
++                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
++
++    try:
++        out, err = sssctl.communicate(input="111")
++    except:
++        sssctl.kill()
++        out, err = sssctl.communicate()
++
++    sssctl.stdin.close()
++    sssctl.stdout.close()
++
++    if sssctl.wait() != 0:
++        raise Exception("sssctl failed")
++
++    assert err.find("pam_authenticate for user [user1]: " +
++                    "Authentication failure") != -1
++
++
++def test_sc_auth(simple_pam_cert_auth, env_for_sssctl):
++
++    sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
++                               "--action=auth", "--service=pam_sss_service"],
++                              universal_newlines=True,
++                              env=env_for_sssctl, stdin=subprocess.PIPE,
++                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
++
++    try:
++        out, err = sssctl.communicate(input="123456")
++    except:
++        sssctl.kill()
++        out, err = sssctl.communicate()
++
++    sssctl.stdin.close()
++    sssctl.stdout.close()
++
++    if sssctl.wait() != 0:
++        raise Exception("sssctl failed")
++
++    assert err.find("pam_authenticate for user [user1]: Success") != -1
+diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am
+index 0c7099390..19772d150 100644
+--- a/src/tests/test_CA/Makefile.am
++++ b/src/tests/test_CA/Makefile.am
+@@ -4,9 +4,11 @@ dist_noinst_DATA = \
+     SSSD_test_cert_0001.config \
+     SSSD_test_cert_0002.config \
+     SSSD_test_cert_0003.config \
++    SSSD_test_cert_0004.config \
+     SSSD_test_cert_key_0001.pem \
+     SSSD_test_cert_key_0002.pem \
+     SSSD_test_cert_key_0003.pem \
++    SSSD_test_cert_key_0004.pem \
+     $(NULL)
+ 
+ openssl_ca_config = $(srcdir)/SSSD_test_CA.config
+@@ -33,14 +35,18 @@ endif
+ ca_all: clean serial SSSD_test_CA.pem $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) $(extra)
+ 
+ $(pwdfile):
+-	@echo "12345678" > $@
++	@echo "123456" > $@
+ 
+ SSSD_test_CA.pem: $(openssl_ca_key) $(openssl_ca_config) serial
+ 	$(OPENSSL) req -batch -config ${openssl_ca_config} -x509 -new -nodes -key $< -sha256 -days 1024 -set_serial 0 -extensions v3_ca -out $@
+ 
+ 
+ SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test_cert_%.config
+-	$(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_$*.config -out $@
++	if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_$*.config) -eq 0 ]; then \
++		$(OPENSSL) req -new -nodes -key $< -config $(srcdir)/SSSD_test_cert_$*.config -out $@ ; \
++	else \
++		$(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_$*.config -out $@ ; \
++	fi
+ 
+ SSSD_test_cert_x509_%.pem: SSSD_test_cert_req_%.pem $(openssl_ca_config) SSSD_test_CA.pem
+ 	$(OPENSSL) ca -config ${openssl_ca_config} -batch -notext -keyfile $(openssl_ca_key) -in $< -days 200 -extensions usr_cert -out $@
+@@ -65,18 +71,18 @@ SSSD_test_cert_pubsshkey_%.h: SSSD_test_cert_pubsshkey_%.pub
+ # - src/tests/cmocka/test_pam_srv.c
+ p11_nssdb: SSSD_test_cert_pkcs12_0001.pem SSSD_test_CA.pem $(pwdfile)
+ 	mkdir $@
+-	$(CERTUTIL) -d sql:./$@ -N --empty-password
+-	$(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem
+-	$(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile)
++	$(CERTUTIL) -d sql:./$@ -N -f $(pwdfile)
++	$(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem -f $(pwdfile)
++	$(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) -k $(pwdfile)
+ 
+ # This nss db is used in
+ # - src/tests/cmocka/test_pam_srv.c
+ p11_nssdb_2certs: SSSD_test_cert_pkcs12_0001.pem SSSD_test_cert_pkcs12_0002.pem SSSD_test_CA.pem $(pwdfile)
+ 	mkdir $@
+-	$(CERTUTIL) -d sql:./$@ -N --empty-password
+-	$(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem
+-	$(PK12UTIL) -d sql:./$@ p11_nssdb -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile)
+-	$(PK12UTIL) -d sql:./$@ p11_nssdb -i SSSD_test_cert_pkcs12_0002.pem -w $(pwdfile)
++	$(CERTUTIL) -d sql:./$@ -N -f $(pwdfile)
++	$(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem -f $(pwdfile)
++	$(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) -k $(pwdfile)
++	$(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0002.pem -w $(pwdfile) -k $(pwdfile)
+ 
+ # The softhsm2 PKCS#11 setups are used in
+ # - src/tests/cmocka/test_pam_srv.c
+diff --git a/src/tests/test_CA/SSSD_test_cert_0004.config b/src/tests/test_CA/SSSD_test_cert_0004.config
+new file mode 100644
+index 000000000..cb61b43ce
+--- /dev/null
++++ b/src/tests/test_CA/SSSD_test_cert_0004.config
+@@ -0,0 +1,11 @@
++# This certificate is used in
++# - test_sss_cert_get_content_test_cert_0004
++# as an example for a simple certificate without KU, EKU and SAN extensions
++[ req ]
++distinguished_name = req_distinguished_name
++prompt = no
++
++[ req_distinguished_name ]
++O = SSSD
++OU = SSSD test
++CN = SSSD test cert 0004
+diff --git a/src/tests/test_CA/SSSD_test_cert_key_0004.pem b/src/tests/test_CA/SSSD_test_cert_key_0004.pem
+new file mode 100644
+index 000000000..e7e1b1de7
+--- /dev/null
++++ b/src/tests/test_CA/SSSD_test_cert_key_0004.pem
+@@ -0,0 +1,28 @@
++-----BEGIN PRIVATE KEY-----
++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdue/OaH/8xyzm
++PUXFJeVJe0GOLZv3Pv/wPuIlNjAU1JNSDQUUKBlv7SOJr7KZ+4se6RyTU22G0KMN
++0qswY5hlpOtCbRlH2fp5zaYakDVbAv00UBvllPuLetQA9hjCxvz3DZLfLC/N954N
++ZKJIrO2fqTDvJwKqhw7gvp4p1vZcpAcsDf/AYzPgw1oX3yZyzQhfQwQ5Gu3U0Fwc
++nQL/l++mDFD2faDBfn9CFu7hPHKMQvjITsK/RwuFqZa1BzU80x0iyRMhYyDuUDWD
++2tDgzgt4qlMIHJb0ACHuSZFNIiqCF2xkM63PP3is2w7DUpRSu26FR4JacoKq7v5g
++yhtw9y9HAgMBAAECggEAVpNKKy03G4QkhBib5HRBoAz01dr5IkTFbZTGwxA0Yiqw
++1rfo0sCT/djXyerUCSuGmKfyFHgVxYteBOdfKgdxDlHxBJwn5UWj9BnKlAgWEWfZ
++nk5eka0uSchY+FIdE0Twc5dSyAdUEiVZ7xYO8f9hy2KuRodOMlZB92EKJgMlZYGS
++/hYOfZYmz3c4LFWO+UEiXyKKjENtnp5CpOw+Vcrwlu77PbFiT4Y12dOOwDRP4a/a
++ddXQBUaApOMDBA6gpB4jaysq5EBnrLTL5fzHNpATVKFnAL7icPuIfefjU2kxQMoo
++siUL7RzZnLlx0mN37DIzTv4uGltvGzzqhkIA5X/TsQKBgQDNBt3+dih7YbDt/4cC
++HtuApUAbwYDYhETzU8Zt+WRiFZdOgBcm/bacxOqBWGJ1WCYnegPYmk4WVThH1zrF
++Pr2EN2sOn2KJzQlLIOR7hvtTXgLx1hqc9XVBq/8JKhvCPfk/RoCjt+GmkoLHdrWI
++w1kd2milRcFs09UCV8LGa/vYSQKBgQDE8JUXD8x77IP/CBXLBmFq6tSwYkRWh4jC
++l8HB75VWXrknzgjv4Iqx4FEm2T0Mp0QFZLF6WCoclUcsGiCTz7jm20eoRL+5dX1d
++yASi00GSpS1p2Q9eTTU4FHVg1nD1B5F1kQB8uqBj0oSjeQLLPngaIftxTGNrkw3J
++4mk5kVdrDwKBgF0iA3F1pwn05HQYIPHbpoYXirmQ+sBfxRprMbX/FZRgjmzATsQN
++eAhagtPinEcFlb9U865O2a3XZEtt/2peB6Spr93ilNZX5yLTfDaIqF3EVL4aLdii
++v3LneGBnWliv4irWEdVM0Bnkb7e/utK3OiIPdn2s5CJVT2tTBk0v/CTRAoGAIe04
++IeLs3SRfkN25s2IEAkE2JrSnBSkQHEW8cUZuuZRT3VGXJIvQGNiF4mVmKPnfs/Ym
++xObPSmFFA4n0tsIAHnUEIS7GwJJG6JL+iXZPQ44FBskH5rzyQBj2J5qJlwyYuGIk
++bVhRLSElDGxaWN0IH6hfAqOgNPX+WBsS+YHaR20CgYAVUwTRA9kQgPZJfg+mepFG
++zw9Tx7/TSwILZDlL0AU/i12xn0RA7sweLW8cPEDx1OnTbv+/pqSZ46eeZDzTrlu7
++ASy844law96NdhpKuTyz/jEl6aj0RLp1wzQZLSQkV0nv3f2Qlknhz83uShhxmxJv
++FqS4fShRFJNoQDwEUvE7ZA==
++-----END PRIVATE KEY-----
+-- 
+2.21.3
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index ff965ad..18f2e38 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -50,7 +50,7 @@
 
 Name: sssd
 Version: 1.16.5
-Release: 10%{?dist}.6
+Release: 10%{?dist}.7
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -116,6 +116,10 @@ Patch0054: 0054-ad-add-ad_allow_remote_domain_local_groups.patch
 Patch0055: 0055-man-fix-description-of-dns_resolver_op_timeout.patch
 Patch0056: 0056-man-fix-description-of-dns_resolver_timeout.patch
 Patch0057: 0057-failover-add-dns_resolver_server_timeout-option.patch
+Patch0058: 0058-nss-check-if-groups-are-filtered-during-initgroups.patch
+Patch0059: 0059-CACHE-Create-timestamp-if-missing.patch
+Patch0060: 0060-TESTS-Add-test-for-recreating-cache-timestamp.patch
+Patch0061: 0061-cert-matching.patch
 
 #Those patches should not be removed in RHEL-7
 Patch0999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -1291,6 +1295,11 @@ systemctl try-restart sssd >/dev/null 2>&1 || :
 }
 
 %changelog
+* Thu Dec 10 2020 Alexey Tikhonov <atikhono@redhat.com> 1.16.5-10.7
+- Resolves: rhbz#1875514 - filter_groups option partially filters the group from 'id' output of the user because gidNumber still appears in 'id' output [rhel-7.9.z]
+- Resolves: rhbz#1772513 - SSSD is generating lot of LDAP queries in a very large environment [rhel-7.9.z]
+- Resolves: rhbz#1736845 - [RFE] Backporting certificate matching rules for files, AD and LDAP provider [rhel-7.9.z]
+
 * Wed Nov 25 2020 Alexey Tikhonov <atikhono@redhat.com> 1.16.5-10.6
 - Resolves: rhbz#1899593 - sssd_be segfaults at be_refresh_get_values_ex() due to NULL ptrs in results of sysdb_search_with_ts_attr() [rhel-7.9.z]
 - Resolves: rhbz#1888409 - sssd component logging is now too generic in syslog/journal [rhel-7.9.z]