Blame SOURCES/0037-PAM-handled-multiple-certs-in-the-responder.patch

9f2ebf
From f4cac6544b5b6fb094d2088bf75f443fb74028bc Mon Sep 17 00:00:00 2001
9f2ebf
From: Sumit Bose <sbose@redhat.com>
9f2ebf
Date: Fri, 25 Aug 2017 12:51:09 +0200
9f2ebf
Subject: [PATCH 37/47] PAM: handled multiple certs in the responder
9f2ebf
MIME-Version: 1.0
9f2ebf
Content-Type: text/plain; charset=UTF-8
9f2ebf
Content-Transfer-Encoding: 8bit
9f2ebf
9f2ebf
This patch refactors the handling of the certificate and the attributes
9f2ebf
to address the certificate on the Smartcard (module name, token name and
9f2ebf
key id). Instead of using individual variables the values are put into a
9f2ebf
new struct cert_auth_info. Since the new struct can be used as a list
9f2ebf
the PAM responder can now handle multiple certificates on the Smartcard
9f2ebf
and can send the needed data to pam_sss with multiple SSS_PAM_CERT_INFO
9f2ebf
messages.
9f2ebf
9f2ebf
Unit tests are added to confirm the expected behavior.
9f2ebf
9f2ebf
Related to https://pagure.io/SSSD/sssd/issue/3560
9f2ebf
9f2ebf
Reviewed-by: Fabiano FidĂȘncio <fidencio@redhat.com>
9f2ebf
Tested-by: Scott Poore <spoore@redhat.com>
9f2ebf
(cherry picked from commit 0bdd8800c16f39b8fe308d20694ad905c669dff3)
9f2ebf
9f2ebf
The following binaries have been removed from the original patch:
9f2ebf
- src/tests/cmocka/p11_nssdb_2certs/cert9.db
9f2ebf
- src/tests/cmocka/p11_nssdb_2certs/key4.db
9f2ebf
9f2ebf
The reason for that is that we can't apply a patch which is a binary
9f2ebf
file using rpm, thus removing the patches here and adding them as source
9f2ebf
files in the sssd.spec seems to be the best solution.
9f2ebf
---
9f2ebf
 Makefile.am                                  |   2 +
9f2ebf
 src/responder/pam/pamsrv.h                   |  25 ++-
9f2ebf
 src/responder/pam/pamsrv_cmd.c               | 257 ++++++++++++++++++---------
9f2ebf
 src/responder/pam/pamsrv_p11.c               | 181 ++++++++++++++-----
9f2ebf
 src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt |   4 +
9f2ebf
 src/tests/cmocka/test_pam_srv.c              | 216 +++++++++++++++++++++-
9f2ebf
 src/tests/whitespace_test                    |   2 +-
9f2ebf
 7 files changed, 538 insertions(+), 149 deletions(-)
9f2ebf
 create mode 100644 src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
9f2ebf
9f2ebf
diff --git a/Makefile.am b/Makefile.am
9f2ebf
index bbc90d9bad4d22ca0284ea95281a487d42399c05..4ed872a532daf9b934537cc5f64ce77778121e2a 100644
9f2ebf
--- a/Makefile.am
9f2ebf
+++ b/Makefile.am
9f2ebf
@@ -481,6 +481,8 @@ dist_noinst_DATA = \
9f2ebf
     contrib/ci/sssd.supp \
9f2ebf
     src/tests/cmocka/p11_nssdb/cert9.db \
9f2ebf
     src/tests/cmocka/p11_nssdb/key4.db \
9f2ebf
+    src/tests/cmocka/p11_nssdb_2certs/cert9.db \
9f2ebf
+    src/tests/cmocka/p11_nssdb_2certs/key4.db \
9f2ebf
     $(SYSTEMTAP_PROBES) \
9f2ebf
     $(NULL)
9f2ebf
 
9f2ebf
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
9f2ebf
index 896f71befbc9947a53b5eb20cba0bb3d104c4cf2..f15f7f19f1f38626288416c9f2038371c6f58b47 100644
9f2ebf
--- a/src/responder/pam/pamsrv.h
9f2ebf
+++ b/src/responder/pam/pamsrv.h
9f2ebf
@@ -73,10 +73,8 @@ struct pam_auth_req {
9f2ebf
     struct pam_auth_dp_req *dpreq_spy;
9f2ebf
 
9f2ebf
     struct ldb_message *user_obj;
9f2ebf
-    struct ldb_result *cert_user_objs;
9f2ebf
-    char *token_name;
9f2ebf
-    char *module_name;
9f2ebf
-    char *key_id;
9f2ebf
+    struct cert_auth_info *cert_list;
9f2ebf
+    struct cert_auth_info *current_cert;
9f2ebf
     bool cert_auth_local;
9f2ebf
 };
9f2ebf
 
9f2ebf
@@ -89,6 +87,16 @@ int LOCAL_pam_handler(struct pam_auth_req *preq);
9f2ebf
 errno_t p11_child_init(struct pam_ctx *pctx);
9f2ebf
 
9f2ebf
 struct cert_auth_info;
9f2ebf
+const char *sss_cai_get_cert(struct cert_auth_info *i);
9f2ebf
+const char *sss_cai_get_token_name(struct cert_auth_info *i);
9f2ebf
+const char *sss_cai_get_module_name(struct cert_auth_info *i);
9f2ebf
+const char *sss_cai_get_key_id(struct cert_auth_info *i);
9f2ebf
+struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i);
9f2ebf
+struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i);
9f2ebf
+void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
9f2ebf
+                                struct ldb_result *cert_user_objs);
9f2ebf
+void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
9f2ebf
+                         size_t *_cert_user_count);
9f2ebf
 
9f2ebf
 struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
9f2ebf
                                        struct tevent_context *ev,
9f2ebf
@@ -98,12 +106,11 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
9f2ebf
                                        const char *verify_opts,
9f2ebf
                                        struct pam_data *pd);
9f2ebf
 errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
9f2ebf
-                            char **cert, char **token_name, char **module_name,
9f2ebf
-                            char **key_id);
9f2ebf
+                            struct cert_auth_info **cert_list);
9f2ebf
 
9f2ebf
-errno_t add_pam_cert_response(struct pam_data *pd, const char *user,
9f2ebf
-                              const char *token_name, const char *module_name,
9f2ebf
-                              const char *key_id, enum response_type type);
9f2ebf
+errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
9f2ebf
+                              struct cert_auth_info *cert_info,
9f2ebf
+                              enum response_type type);
9f2ebf
 
9f2ebf
 bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
9f2ebf
 
9f2ebf
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
9f2ebf
index 51d8185650cf823da289a3398b10133065d82ae4..8b2c086e206796ad4c977495be957c56b3255e7f 100644
9f2ebf
--- a/src/responder/pam/pamsrv_cmd.c
9f2ebf
+++ b/src/responder/pam/pamsrv_cmd.c
9f2ebf
@@ -1389,21 +1389,17 @@ done:
9f2ebf
     return pam_check_user_done(preq, ret);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static errno_t pam_user_by_cert_step(struct pam_auth_req *preq);
9f2ebf
 static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req);
9f2ebf
 static void pam_forwarder_cert_cb(struct tevent_req *req)
9f2ebf
 {
9f2ebf
     struct pam_auth_req *preq = tevent_req_callback_data(req,
9f2ebf
                                                          struct pam_auth_req);
9f2ebf
-    struct cli_ctx *cctx = preq->cctx;
9f2ebf
     struct pam_data *pd;
9f2ebf
     errno_t ret = EOK;
9f2ebf
-    char *cert;
9f2ebf
-    struct pam_ctx *pctx =
9f2ebf
-            talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
9f2ebf
+    const char *cert;
9f2ebf
 
9f2ebf
-    ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name,
9f2ebf
-                                                &preq->module_name,
9f2ebf
-                                                &preq->key_id);
9f2ebf
+    ret = pam_check_cert_recv(req, preq, &preq->cert_list);
9f2ebf
     talloc_free(req);
9f2ebf
     if (ret != EOK) {
9f2ebf
         DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n");
9f2ebf
@@ -1412,6 +1408,8 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
9f2ebf
 
9f2ebf
     pd = preq->pd;
9f2ebf
 
9f2ebf
+    cert = sss_cai_get_cert(preq->cert_list);
9f2ebf
+
9f2ebf
     if (cert == NULL) {
9f2ebf
         if (pd->logon_name == NULL) {
9f2ebf
             DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
@@ -1431,21 +1429,42 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
9f2ebf
         goto done;
9f2ebf
     }
9f2ebf
 
9f2ebf
+    preq->current_cert = preq->cert_list;
9f2ebf
+    ret = pam_user_by_cert_step(preq);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
9f2ebf
+        goto done;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return;
9f2ebf
+
9f2ebf
+done:
9f2ebf
+    pam_check_user_done(preq, ret);
9f2ebf
+}
9f2ebf
+
9f2ebf
+static errno_t pam_user_by_cert_step(struct pam_auth_req *preq)
9f2ebf
+{
9f2ebf
+    struct cli_ctx *cctx = preq->cctx;
9f2ebf
+    struct tevent_req *req;
9f2ebf
+    struct pam_ctx *pctx =
9f2ebf
+            talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
9f2ebf
+
9f2ebf
+    if (preq->current_cert == NULL) {
9f2ebf
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate data.\n");
9f2ebf
+        return EINVAL;
9f2ebf
+    }
9f2ebf
 
9f2ebf
     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
9f2ebf
                                       pctx->rctx->ncache, 0,
9f2ebf
                                       preq->req_dom_type, NULL,
9f2ebf
-                                      cert);
9f2ebf
+                                      sss_cai_get_cert(preq->current_cert));
9f2ebf
     if (req == NULL) {
9f2ebf
         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
9f2ebf
-        ret = ENOMEM;
9f2ebf
-        goto done;
9f2ebf
+        return ENOMEM;
9f2ebf
     }
9f2ebf
+
9f2ebf
     tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq);
9f2ebf
-    return;
9f2ebf
-
9f2ebf
-done:
9f2ebf
-    pam_check_user_done(preq, ret);
9f2ebf
+    return EOK;
9f2ebf
 }
9f2ebf
 
9f2ebf
 static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx,
9f2ebf
@@ -1511,6 +1530,9 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
     struct pam_auth_req *preq = tevent_req_callback_data(req,
9f2ebf
                                                          struct pam_auth_req);
9f2ebf
     const char *cert_user = NULL;
9f2ebf
+    size_t cert_count = 0;
9f2ebf
+    size_t cert_user_count = 0;
9f2ebf
+    struct ldb_result *cert_user_objs;
9f2ebf
 
9f2ebf
     ret = cache_req_recv(preq, req, &results);
9f2ebf
     talloc_zfree(req);
9f2ebf
@@ -1521,12 +1543,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
 
9f2ebf
     if (ret == EOK) {
9f2ebf
         ret = get_results_from_all_domains(preq, results,
9f2ebf
-                                           &preq->cert_user_objs);
9f2ebf
+                                           &cert_user_objs);
9f2ebf
         if (ret != EOK) {
9f2ebf
             DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n");
9f2ebf
             goto done;
9f2ebf
         }
9f2ebf
 
9f2ebf
+        sss_cai_set_cert_user_objs(preq->current_cert, cert_user_objs);
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    preq->current_cert = sss_cai_get_next(preq->current_cert);
9f2ebf
+    if (preq->current_cert != NULL) {
9f2ebf
+        ret = pam_user_by_cert_step(preq);
9f2ebf
+        if (ret != EOK) {
9f2ebf
+            DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
9f2ebf
+            goto done;
9f2ebf
+        }
9f2ebf
+        return;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    sss_cai_check_users(&preq->cert_list, &cert_count, &cert_user_count);
9f2ebf
+    DEBUG(SSSDBG_TRACE_ALL,
9f2ebf
+          "Found [%zu] certificates and [%zu] related users.\n",
9f2ebf
+          cert_count, cert_user_count);
9f2ebf
+
9f2ebf
+    if (cert_user_count == 0) {
9f2ebf
+        if (preq->pd->logon_name == NULL) {
9f2ebf
+            DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
+                  "Missing logon name and no certificate user found.\n");
9f2ebf
+            ret = ENOENT;
9f2ebf
+            goto done;
9f2ebf
+        }
9f2ebf
+    } else {
9f2ebf
+
9f2ebf
         if (preq->pd->logon_name == NULL) {
9f2ebf
             if (preq->pd->cmd != SSS_PAM_PREAUTH) {
9f2ebf
                 DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
@@ -1535,9 +1584,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
                 goto done;
9f2ebf
             }
9f2ebf
 
9f2ebf
-            if (preq->cert_user_objs->count == 1) {
9f2ebf
+            if (cert_count > 1) {
9f2ebf
+                for (preq->current_cert = preq->cert_list;
9f2ebf
+                     preq->current_cert != NULL;
9f2ebf
+                     preq->current_cert = sss_cai_get_next(preq->current_cert)) {
9f2ebf
+
9f2ebf
+                    ret = add_pam_cert_response(preq->pd, "",
9f2ebf
+                                       preq->current_cert,
9f2ebf
+                                       preq->cctx->rctx->domains->user_name_hint
9f2ebf
+                                            ? SSS_PAM_CERT_INFO_WITH_HINT
9f2ebf
+                                            : SSS_PAM_CERT_INFO);
9f2ebf
+                    if (ret != EOK) {
9f2ebf
+                        DEBUG(SSSDBG_OP_FAILURE,
9f2ebf
+                              "add_pam_cert_response failed.\n");
9f2ebf
+                        preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
9f2ebf
+                    }
9f2ebf
+                }
9f2ebf
+
9f2ebf
+                ret = EOK;
9f2ebf
+                preq->pd->pam_status = PAM_SUCCESS;
9f2ebf
+                pam_reply(preq);
9f2ebf
+                goto done;
9f2ebf
+            }
9f2ebf
+
9f2ebf
+            if (cert_user_count == 1) {
9f2ebf
+                cert_user_objs = sss_cai_get_cert_user_objs(preq->cert_list);
9f2ebf
+                if (cert_user_objs == NULL) {
9f2ebf
+                    DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate user.\n");
9f2ebf
+                    ret = ENOENT;
9f2ebf
+                    goto done;
9f2ebf
+                }
9f2ebf
+
9f2ebf
                 cert_user = ldb_msg_find_attr_as_string(
9f2ebf
-                                                  preq->cert_user_objs->msgs[0],
9f2ebf
+                                                  cert_user_objs->msgs[0],
9f2ebf
                                                   SYSDB_NAME, NULL);
9f2ebf
                 if (cert_user == NULL) {
9f2ebf
                     DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
@@ -1564,9 +1643,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
 
9f2ebf
             if (preq->cctx->rctx->domains->user_name_hint) {
9f2ebf
                 ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
-                                            preq->token_name,
9f2ebf
-                                            preq->module_name,
9f2ebf
-                                            preq->key_id,
9f2ebf
+                                            preq->cert_list,
9f2ebf
                                             SSS_PAM_CERT_INFO_WITH_HINT);
9f2ebf
                 preq->pd->pam_status = PAM_SUCCESS;
9f2ebf
                 if (ret != EOK) {
9f2ebf
@@ -1596,13 +1673,6 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
                 goto done;
9f2ebf
             }
9f2ebf
         }
9f2ebf
-    } else {
9f2ebf
-        if (preq->pd->logon_name == NULL) {
9f2ebf
-            DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
-                  "Missing logon name and no certificate user found.\n");
9f2ebf
-            ret = ENOENT;
9f2ebf
-            goto done;
9f2ebf
-        }
9f2ebf
     }
9f2ebf
 
9f2ebf
     if (preq->user_obj == NULL) {
9f2ebf
@@ -1884,7 +1954,9 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
9f2ebf
     struct pam_ctx *pctx =
9f2ebf
             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
9f2ebf
     const char *cert_user;
9f2ebf
+    struct ldb_result *cert_user_objs;
9f2ebf
     size_t c;
9f2ebf
+    bool found = false;
9f2ebf
 
9f2ebf
     if (!preq->pd->domain) {
9f2ebf
         preq->pd->domain = preq->domain->name;
9f2ebf
@@ -1921,76 +1993,87 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
9f2ebf
         return;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (may_do_cert_auth(pctx, preq->pd) && preq->cert_user_objs != NULL) {
9f2ebf
+    if (may_do_cert_auth(pctx, preq->pd) && preq->cert_list != NULL) {
9f2ebf
         /* Check if user matches certificate user */
9f2ebf
-        for (c = 0; c < preq->cert_user_objs->count; c++) {
9f2ebf
-            cert_user = ldb_msg_find_attr_as_string(
9f2ebf
-                                                  preq->cert_user_objs->msgs[c],
9f2ebf
-                                                  SYSDB_NAME,
9f2ebf
-                                                  NULL);
9f2ebf
-            if (cert_user == NULL) {
9f2ebf
-                /* Even if there might be other users mapped to the
9f2ebf
-                 * certificate a missing SYSDB_NAME indicates some critical
9f2ebf
-                 * condition which justifies that the whole request is aborted
9f2ebf
-                 * */
9f2ebf
-                DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
-                      "Certificate user object has no name.\n");
9f2ebf
-                preq->pd->pam_status = PAM_USER_UNKNOWN;
9f2ebf
-                pam_reply(preq);
9f2ebf
-                return;
9f2ebf
+        found = false;
9f2ebf
+        for (preq->current_cert = preq->cert_list;
9f2ebf
+             preq->current_cert != NULL;
9f2ebf
+             preq->current_cert = sss_cai_get_next(preq->current_cert)) {
9f2ebf
+
9f2ebf
+            cert_user_objs = sss_cai_get_cert_user_objs(preq->current_cert);
9f2ebf
+            if (cert_user_objs == NULL) {
9f2ebf
+                DEBUG(SSSDBG_OP_FAILURE,
9f2ebf
+                      "Unexpteced missing certificate user, "
9f2ebf
+                      "trying next certificate.\n");
9f2ebf
+                continue;
9f2ebf
             }
9f2ebf
 
9f2ebf
-            /* pam_check_user_search() calls pd_set_primary_name() is the search
9f2ebf
-             * was successful, so pd->user contains the canonical sysdb name
9f2ebf
-             * as well */
9f2ebf
-            if (ldb_dn_compare(preq->cert_user_objs->msgs[c]->dn,
9f2ebf
-                               preq->user_obj->dn) == 0) {
9f2ebf
-
9f2ebf
-                if (preq->pd->cmd == SSS_PAM_PREAUTH) {
9f2ebf
-                    ret = sss_authtok_set_sc(preq->pd->authtok,
9f2ebf
-                                             SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
9f2ebf
-                                             preq->token_name, 0,
9f2ebf
-                                             preq->module_name, 0,
9f2ebf
-                                             preq->key_id, 0);
9f2ebf
-                    if (ret != EOK) {
9f2ebf
-                        DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_sc failed, "
9f2ebf
-                                                 "Smartcard authentication "
9f2ebf
-                                                 "detection might fail in the "
9f2ebf
-                                                 "backend.\n");
9f2ebf
-                    }
9f2ebf
-
9f2ebf
-                    ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
-                                                preq->token_name,
9f2ebf
-                                                preq->module_name,
9f2ebf
-                                                preq->key_id,
9f2ebf
-                                                SSS_PAM_CERT_INFO);
9f2ebf
-                    if (ret != EOK) {
9f2ebf
-                        DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
9f2ebf
-                        preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
9f2ebf
-                    }
9f2ebf
-                }
9f2ebf
-
9f2ebf
-                /* We are done if we do not have to call the backend */
9f2ebf
-                if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
9f2ebf
-                        && preq->cert_auth_local) {
9f2ebf
-                    preq->pd->pam_status = PAM_SUCCESS;
9f2ebf
-                    preq->callback = pam_reply;
9f2ebf
+            for (c = 0; c < cert_user_objs->count; c++) {
9f2ebf
+                cert_user = ldb_msg_find_attr_as_string(cert_user_objs->msgs[c],
9f2ebf
+                                                        SYSDB_NAME, NULL);
9f2ebf
+                if (cert_user == NULL) {
9f2ebf
+                    /* Even if there might be other users mapped to the
9f2ebf
+                     * certificate a missing SYSDB_NAME indicates some critical
9f2ebf
+                     * condition which justifies that the whole request is aborted
9f2ebf
+                     * */
9f2ebf
+                    DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
+                          "Certificate user object has no name.\n");
9f2ebf
+                    preq->pd->pam_status = PAM_USER_UNKNOWN;
9f2ebf
                     pam_reply(preq);
9f2ebf
                     return;
9f2ebf
                 }
9f2ebf
+
9f2ebf
+                if (ldb_dn_compare(cert_user_objs->msgs[c]->dn,
9f2ebf
+                                   preq->user_obj->dn) == 0) {
9f2ebf
+                    found = true;
9f2ebf
+                    if (preq->pd->cmd == SSS_PAM_PREAUTH) {
9f2ebf
+                        ret = sss_authtok_set_sc(preq->pd->authtok,
9f2ebf
+                                 SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
9f2ebf
+                                 sss_cai_get_token_name(preq->current_cert), 0,
9f2ebf
+                                 sss_cai_get_module_name(preq->current_cert), 0,
9f2ebf
+                                 sss_cai_get_key_id(preq->current_cert), 0);
9f2ebf
+                        if (ret != EOK) {
9f2ebf
+                            DEBUG(SSSDBG_OP_FAILURE,
9f2ebf
+                                  "sss_authtok_set_sc failed, Smartcard "
9f2ebf
+                                  "authentication detection might fail in "
9f2ebf
+                                  "the backend.\n");
9f2ebf
+                        }
9f2ebf
+
9f2ebf
+                        /* FIXME: use the right cert info */
9f2ebf
+                        ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
+                                                    preq->current_cert,
9f2ebf
+                                                    SSS_PAM_CERT_INFO);
9f2ebf
+                        if (ret != EOK) {
9f2ebf
+                            DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
9f2ebf
+                            preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
9f2ebf
+                        }
9f2ebf
+                    }
9f2ebf
+
9f2ebf
+                }
9f2ebf
             }
9f2ebf
         }
9f2ebf
 
9f2ebf
-        if (preq->pd->cmd == SSS_PAM_PREAUTH) {
9f2ebf
-            DEBUG(SSSDBG_TRACE_FUNC,
9f2ebf
-                  "User and certificate user do not match, "
9f2ebf
-                  "continue with other authentication methods.\n");
9f2ebf
+        if (found) {
9f2ebf
+            /* We are done if we do not have to call the backend */
9f2ebf
+            if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
9f2ebf
+                    && preq->cert_auth_local) {
9f2ebf
+                preq->pd->pam_status = PAM_SUCCESS;
9f2ebf
+                preq->callback = pam_reply;
9f2ebf
+                pam_reply(preq);
9f2ebf
+                return;
9f2ebf
+            }
9f2ebf
         } else {
9f2ebf
-            DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
-                  "User and certificate user do not match.\n");
9f2ebf
-            preq->pd->pam_status = PAM_AUTH_ERR;
9f2ebf
-            pam_reply(preq);
9f2ebf
-            return;
9f2ebf
+            if (preq->pd->cmd == SSS_PAM_PREAUTH) {
9f2ebf
+                DEBUG(SSSDBG_TRACE_FUNC,
9f2ebf
+                      "User and certificate user do not match, "
9f2ebf
+                      "continue with other authentication methods.\n");
9f2ebf
+            } else {
9f2ebf
+                DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
+                      "User and certificate user do not match.\n");
9f2ebf
+                preq->pd->pam_status = PAM_AUTH_ERR;
9f2ebf
+                pam_reply(preq);
9f2ebf
+                return;
9f2ebf
+            }
9f2ebf
         }
9f2ebf
     }
9f2ebf
 
9f2ebf
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
9f2ebf
index ff32d1e726808caa36ca7cca557220866ef1a9ab..57c8e1e464f4262f2d78f869c52ca48bd469d90a 100644
9f2ebf
--- a/src/responder/pam/pamsrv_p11.c
9f2ebf
+++ b/src/responder/pam/pamsrv_p11.c
9f2ebf
@@ -40,10 +40,80 @@ struct cert_auth_info {
9f2ebf
     char *token_name;
9f2ebf
     char *module_name;
9f2ebf
     char *key_id;
9f2ebf
+    struct ldb_result *cert_user_objs;
9f2ebf
     struct cert_auth_info *prev;
9f2ebf
     struct cert_auth_info *next;
9f2ebf
 };
9f2ebf
 
9f2ebf
+const char *sss_cai_get_cert(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->cert : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+const char *sss_cai_get_token_name(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->token_name : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+const char *sss_cai_get_module_name(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->module_name : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+const char *sss_cai_get_key_id(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->key_id : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->next : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i)
9f2ebf
+{
9f2ebf
+    return i != NULL ? i->cert_user_objs : NULL;
9f2ebf
+}
9f2ebf
+
9f2ebf
+void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
9f2ebf
+                                struct ldb_result *cert_user_objs)
9f2ebf
+{
9f2ebf
+    if (i->cert_user_objs != NULL) {
9f2ebf
+        talloc_free(i->cert_user_objs);
9f2ebf
+    }
9f2ebf
+    i->cert_user_objs = talloc_steal(i, cert_user_objs);
9f2ebf
+}
9f2ebf
+
9f2ebf
+void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
9f2ebf
+                         size_t *_cert_user_count)
9f2ebf
+{
9f2ebf
+    struct cert_auth_info *c;
9f2ebf
+    struct cert_auth_info *tmp;
9f2ebf
+    size_t cert_count = 0;
9f2ebf
+    size_t cert_user_count = 0;
9f2ebf
+    struct ldb_result *user_objs;
9f2ebf
+
9f2ebf
+    DLIST_FOR_EACH_SAFE(c, tmp, *list) {
9f2ebf
+        user_objs = sss_cai_get_cert_user_objs(c);
9f2ebf
+        if (user_objs != NULL) {
9f2ebf
+            cert_count++;
9f2ebf
+            cert_user_count += user_objs->count;
9f2ebf
+        } else {
9f2ebf
+            DLIST_REMOVE(*list, c);
9f2ebf
+        }
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (_cert_count != NULL) {
9f2ebf
+        *_cert_count = cert_count;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    if (_cert_user_count != NULL) {
9f2ebf
+        *_cert_user_count = cert_user_count;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return;
9f2ebf
+}
9f2ebf
+
9f2ebf
 errno_t p11_child_init(struct pam_ctx *pctx)
9f2ebf
 {
9f2ebf
     return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
9f2ebf
@@ -566,39 +636,71 @@ static void p11_child_timeout(struct tevent_context *ev,
9f2ebf
 }
9f2ebf
 
9f2ebf
 errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
9f2ebf
-                            char **cert, char **token_name, char **module_name,
9f2ebf
-                            char **key_id)
9f2ebf
+                            struct cert_auth_info **cert_list)
9f2ebf
 {
9f2ebf
+    struct cert_auth_info *tmp_cert_auth_info;
9f2ebf
     struct pam_check_cert_state *state =
9f2ebf
                               tevent_req_data(req, struct pam_check_cert_state);
9f2ebf
 
9f2ebf
     TEVENT_REQ_RETURN_ON_ERROR(req);
9f2ebf
 
9f2ebf
-    if (state->cert_list == NULL) {
9f2ebf
-        *token_name = NULL;
9f2ebf
-        *cert = NULL;
9f2ebf
-        *module_name = NULL;
9f2ebf
-        *key_id = NULL;
9f2ebf
+    if (cert_list != NULL) {
9f2ebf
+        DLIST_FOR_EACH(tmp_cert_auth_info, state->cert_list) {
9f2ebf
+            talloc_steal(mem_ctx, tmp_cert_auth_info);
9f2ebf
+        }
9f2ebf
+
9f2ebf
+        *cert_list = state->cert_list;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (cert != NULL) {
9f2ebf
-        *cert = (state->cert_list == NULL) ? NULL
9f2ebf
-                                : talloc_steal(mem_ctx, state->cert_list->cert);
9f2ebf
+    return EOK;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
9f2ebf
+                              struct cert_auth_info *cert_info,
9f2ebf
+                              uint8_t **_msg, size_t *_msg_len)
9f2ebf
+{
9f2ebf
+    uint8_t *msg = NULL;
9f2ebf
+    size_t msg_len;
9f2ebf
+    const char *token_name;
9f2ebf
+    const char *module_name;
9f2ebf
+    const char *key_id;
9f2ebf
+    size_t user_len;
9f2ebf
+    size_t token_len;
9f2ebf
+    size_t module_len;
9f2ebf
+    size_t key_id_len;
9f2ebf
+    const char *username = "";
9f2ebf
+
9f2ebf
+    if (sysdb_username != NULL) {
9f2ebf
+        username = sysdb_username;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (token_name != NULL) {
9f2ebf
-        *token_name = (state->cert_list == NULL) ? NULL
9f2ebf
-                          : talloc_steal(mem_ctx, state->cert_list->token_name);
9f2ebf
+    token_name = sss_cai_get_token_name(cert_info);
9f2ebf
+    module_name = sss_cai_get_module_name(cert_info);
9f2ebf
+    key_id = sss_cai_get_key_id(cert_info);
9f2ebf
+
9f2ebf
+    user_len = strlen(username) + 1;
9f2ebf
+    token_len = strlen(token_name) + 1;
9f2ebf
+    module_len = strlen(module_name) + 1;
9f2ebf
+    key_id_len = strlen(key_id) + 1;
9f2ebf
+    msg_len = user_len + token_len + module_len + key_id_len;
9f2ebf
+
9f2ebf
+    msg = talloc_zero_size(mem_ctx, msg_len);
9f2ebf
+    if (msg == NULL) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
9f2ebf
+        return ENOMEM;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (module_name != NULL) {
9f2ebf
-        *module_name = (state->cert_list == NULL) ? NULL
9f2ebf
-                         : talloc_steal(mem_ctx, state->cert_list->module_name);
9f2ebf
+    memcpy(msg, username, user_len);
9f2ebf
+    memcpy(msg + user_len, token_name, token_len);
9f2ebf
+    memcpy(msg + user_len + token_len, module_name, module_len);
9f2ebf
+    memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
9f2ebf
+
9f2ebf
+    if (_msg != NULL) {
9f2ebf
+        *_msg = msg;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (key_id != NULL) {
9f2ebf
-        *key_id = (state->cert_list == NULL) ? NULL
9f2ebf
-                              : talloc_steal(mem_ctx, state->cert_list->key_id);
9f2ebf
+    if (_msg_len != NULL) {
9f2ebf
+        *_msg_len = msg_len;
9f2ebf
     }
9f2ebf
 
9f2ebf
     return EOK;
9f2ebf
@@ -613,18 +715,13 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
9f2ebf
 #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
9f2ebf
 
9f2ebf
 errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
9f2ebf
-                              const char *token_name, const char *module_name,
9f2ebf
-                              const char *key_id, enum response_type type)
9f2ebf
+                              struct cert_auth_info *cert_info,
9f2ebf
+                              enum response_type type)
9f2ebf
 {
9f2ebf
     uint8_t *msg = NULL;
9f2ebf
     char *env = NULL;
9f2ebf
-    size_t user_len;
9f2ebf
     size_t msg_len;
9f2ebf
-    size_t slot_len;
9f2ebf
-    size_t module_len;
9f2ebf
-    size_t key_id_len;
9f2ebf
     int ret;
9f2ebf
-    const char *username = "";
9f2ebf
 
9f2ebf
     if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) {
9f2ebf
         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type);
9f2ebf
@@ -632,26 +729,14 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
9f2ebf
     }
9f2ebf
 
9f2ebf
     if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL)
9f2ebf
-            || token_name == NULL || module_name == NULL || key_id == NULL) {
9f2ebf
+            || cert_info == NULL
9f2ebf
+            || sss_cai_get_token_name(cert_info) == NULL
9f2ebf
+            || sss_cai_get_module_name(cert_info) == NULL
9f2ebf
+            || sss_cai_get_key_id(cert_info) == NULL) {
9f2ebf
         DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
9f2ebf
         return EINVAL;
9f2ebf
     }
9f2ebf
 
9f2ebf
-    if (sysdb_username != NULL) {
9f2ebf
-        username = sysdb_username;
9f2ebf
-    }
9f2ebf
-    user_len = strlen(username) + 1;
9f2ebf
-    slot_len = strlen(token_name) + 1;
9f2ebf
-    module_len = strlen(module_name) + 1;
9f2ebf
-    key_id_len = strlen(key_id) + 1;
9f2ebf
-    msg_len = user_len + slot_len + module_len + key_id_len;
9f2ebf
-
9f2ebf
-    msg = talloc_zero_size(pd, msg_len);
9f2ebf
-    if (msg == NULL) {
9f2ebf
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
9f2ebf
-        return ENOMEM;
9f2ebf
-    }
9f2ebf
-
9f2ebf
     /* sysdb_username is a fully-qualified name which is used by pam_sss when
9f2ebf
      * prompting the user for the PIN and as login name if it wasn't set by
9f2ebf
      * the PAM caller but has to be determined based on the inserted
9f2ebf
@@ -659,10 +744,12 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
9f2ebf
      * re_expression config option was set in a way that user@domain cannot be
9f2ebf
      * handled anymore some more logic has to be added here. But for the time
9f2ebf
      * being I think using sysdb_username is fine. */
9f2ebf
-    memcpy(msg, username, user_len);
9f2ebf
-    memcpy(msg + user_len, token_name, slot_len);
9f2ebf
-    memcpy(msg + user_len + slot_len, module_name, module_len);
9f2ebf
-    memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len);
9f2ebf
+
9f2ebf
+    ret = pack_cert_data(pd, sysdb_username, cert_info, &msg, &msg_len);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "pack_cert_data failed.\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
 
9f2ebf
     ret = pam_add_response(pd, type, msg_len, msg);
9f2ebf
     talloc_free(msg);
9f2ebf
@@ -674,7 +761,7 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
9f2ebf
 
9f2ebf
     if (strcmp(pd->service, "gdm-smartcard") == 0) {
9f2ebf
         env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME,
9f2ebf
-                              token_name);
9f2ebf
+                              sss_cai_get_token_name(cert_info));
9f2ebf
         if (env == NULL) {
9f2ebf
             DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
9f2ebf
             return ENOMEM;
9f2ebf
diff --git a/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
9f2ebf
new file mode 100644
9f2ebf
index 0000000000000000000000000000000000000000..73f5279c338dffe25ad2fad8c9cafae2f3c4cdfe
9f2ebf
--- /dev/null
9f2ebf
+++ b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
9f2ebf
@@ -0,0 +1,4 @@
9f2ebf
+library=
9f2ebf
+name=NSS Internal PKCS #11 Module
9f2ebf
+parameters=configdir='sql:../src/tests/cmocka/p11_nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
9f2ebf
+NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
9f2ebf
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
9f2ebf
index 351067eb664431cda159f73590de772920504380..7f0ed706512ffe0866c0e1fb7e6baa16bec942d8 100644
9f2ebf
--- a/src/tests/cmocka/test_pam_srv.c
9f2ebf
+++ b/src/tests/cmocka/test_pam_srv.c
9f2ebf
@@ -47,6 +47,9 @@
9f2ebf
 #define NSS_DB_PATH TESTS_PATH
9f2ebf
 #define NSS_DB "sql:"NSS_DB_PATH
9f2ebf
 
9f2ebf
+#define NSS_DB_PATH_2CERTS TESTS_PATH "_2certs"
9f2ebf
+#define NSS_DB_2CERTS "sql:"NSS_DB_PATH_2CERTS
9f2ebf
+
9f2ebf
 #define TEST_TOKEN_NAME "SSSD Test Token"
9f2ebf
 #define TEST_MODULE_NAME "NSS-Internal"
9f2ebf
 #define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7"
9f2ebf
@@ -74,6 +77,28 @@
9f2ebf
 "8Z+9gqZhCa7FEKJOPNR9RVtJs0qUUutMZrp1zpyx0GTmXQBA7LbgPxy8L68uymEQ" \
9f2ebf
 "XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g="
9f2ebf
 
9f2ebf
+#define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C"
9f2ebf
+#define TEST_TOKEN_2ND_CERT \
9f2ebf
+"MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
9f2ebf
+"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
9f2ebf
+"NDEzMDFaFw0xODA1MTMxNDEzMDFaMCUxEjAQBgNVBAoMCUlQQS5ERVZFTDEPMA0G" \
9f2ebf
+"A1UEAwwGSVBBIFJBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3abE" \
9f2ebf
+"8LmIc6QN16VVxsMlN/rrCOoZKyyJolSzpP4+K66t+KZUiW/1j1MZogjyYyD39U1F" \
9f2ebf
+"zpa2H+pID74XYrdiqP7sp+uE9/k2XOv/nN3FobXDt+fSINLDriCmxNhUZqpgo2uq" \
9f2ebf
+"Mmka+yx2iJZwkntEoJTcd3aynoa2Sa2ZZbkMBy5p6/pUQKwnD6scOwe6mUDppIBK" \
9f2ebf
+"+ZZRm+u/NDdIRFI5wfKLRR1r/ONaJA9nz1TxSEsgLsjG/1m+Zbb6lGG4pePIFkQ9" \
9f2ebf
+"Iotpi64obBh93oIxzQR29lBG/FMjQVHlPIbx+xuGx11Vtp5pAomgFz0HRrj0leI7" \
9f2ebf
+"bROE+jnC/VGPLQD2aQIDAQABo4GWMIGTMB8GA1UdIwQYMBaAFPci/0Km5D/L5z7Y" \
9f2ebf
+"qwEc7E1/GwgcMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAYYlaHR0cDovL2lw" \
9f2ebf
+"YS1kZXZlbC5pcGEuZGV2ZWw6ODAvY2Evb2NzcDAOBgNVHQ8BAf8EBAMCBPAwHQYD" \
9f2ebf
+"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBg" \
9f2ebf
+"4Sppx2C3eXPJ4Pd9XElkQPOaBReXf1vV0uk/GlK+rG+aAqAkA2Lryx5PK/iAuzAU" \
9f2ebf
+"M6JUpELuQYgqugoCgBXMgsMlpAO/0C3CFq4ZH3KgIsRlRngKPrt6RG0UPMRD1CE2" \
9f2ebf
+"tSVkwUWvyK83lDiu2BbWDXyMyz5eZOlp7uHusf5BKvob8jEndHj1YzaNTmVSsDM5" \
9f2ebf
+"kiIwf8qgFhsO1HCq08PtAnbVHhqkcvnmIJN98eNWNfTKodDmFVbN8gB0wK+WB5ii" \
9f2ebf
+"WVOw7+3/zF1QgqnYX3t+kPLRryip/wvTZkzXWwMNj/W6UHgjNF/4gWGoBgCHu+u3" \
9f2ebf
+"EvjMmbVSrEkesibpGQS5"
9f2ebf
+
9f2ebf
 
9f2ebf
 static char CACHED_AUTH_TIMEOUT_STR[] = "4";
9f2ebf
 static const int CACHED_AUTH_TIMEOUT = 4;
9f2ebf
@@ -111,6 +136,13 @@ static errno_t setup_nss_db(void)
9f2ebf
         return ret;
9f2ebf
     }
9f2ebf
 
9f2ebf
+    ret = mkdir(NSS_DB_PATH_2CERTS, 0775);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE,
9f2ebf
+              "Failed to create " NSS_DB_PATH_2CERTS ".\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
     child_pid = fork();
9f2ebf
     if (child_pid == 0) { /* child */
9f2ebf
         ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d",
9f2ebf
@@ -127,6 +159,22 @@ static errno_t setup_nss_db(void)
9f2ebf
         return ret;
9f2ebf
     }
9f2ebf
 
9f2ebf
+    child_pid = fork();
9f2ebf
+    if (child_pid == 0) { /* child */
9f2ebf
+        ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d",
9f2ebf
+                     NSS_DB_2CERTS, NULL);
9f2ebf
+        if (ret == -1) {
9f2ebf
+            DEBUG(SSSDBG_FATAL_FAILURE, "execl() failed.\n");
9f2ebf
+            exit(-1);
9f2ebf
+        }
9f2ebf
+    } else if (child_pid > 0) {
9f2ebf
+        wait(&status);
9f2ebf
+    } else {
9f2ebf
+        ret = errno;
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE, "fork() failed\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
     fp = fopen(NSS_DB_PATH"/pkcs11.txt", "w");
9f2ebf
     if (fp == NULL) {
9f2ebf
         DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n");
9f2ebf
@@ -148,6 +196,27 @@ static errno_t setup_nss_db(void)
9f2ebf
         return ret;
9f2ebf
     }
9f2ebf
 
9f2ebf
+    fp = fopen(NSS_DB_PATH_2CERTS"/pkcs11.txt", "w");
9f2ebf
+    if (fp == NULL) {
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+    ret = fprintf(fp, "library=libsoftokn3.so\nname=soft\n");
9f2ebf
+    if (ret < 0) {
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/cmocka/p11_nssdb_2certs' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_SRC_DIR);
9f2ebf
+    if (ret < 0) {
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+    ret = fclose(fp);
9f2ebf
+    if (ret != 0) {
9f2ebf
+        DEBUG(SSSDBG_FATAL_FAILURE, "fclose() failed.\n");
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
     return EOK;
9f2ebf
 }
9f2ebf
 
9f2ebf
@@ -174,6 +243,26 @@ static void cleanup_nss_db(void)
9f2ebf
     if (ret != EOK) {
9f2ebf
         DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n");
9f2ebf
     }
9f2ebf
+
9f2ebf
+    ret = unlink(NSS_DB_PATH_2CERTS"/cert9.db");
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove cert9.db.\n");
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = unlink(NSS_DB_PATH_2CERTS"/key4.db");
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove key4.db.\n");
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = unlink(NSS_DB_PATH_2CERTS"/pkcs11.txt");
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove pkcs11.db.\n");
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = rmdir(NSS_DB_PATH_2CERTS);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n");
9f2ebf
+    }
9f2ebf
 }
9f2ebf
 
9f2ebf
 struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
9f2ebf
@@ -749,7 +838,8 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
9f2ebf
 }
9f2ebf
 
9f2ebf
 static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
9f2ebf
-                                  enum response_type type, const char *name)
9f2ebf
+                                  enum response_type type, const char *name,
9f2ebf
+                                  const char *name2)
9f2ebf
 {
9f2ebf
     size_t rp = 0;
9f2ebf
     uint32_t val;
9f2ebf
@@ -763,7 +853,11 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
9f2ebf
     if (name == NULL || *name == '\0') {
9f2ebf
         assert_int_equal(val, 1);
9f2ebf
     } else {
9f2ebf
-        assert_int_equal(val, 2);
9f2ebf
+        if (name2 == NULL || *name2 == '\0') {
9f2ebf
+            assert_int_equal(val, 2);
9f2ebf
+        } else {
9f2ebf
+            assert_int_equal(val, 3);
9f2ebf
+        }
9f2ebf
 
9f2ebf
         SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
9f2ebf
         assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
9f2ebf
@@ -801,6 +895,33 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
9f2ebf
     assert_string_equal(body + rp, TEST_KEY_ID);
9f2ebf
     rp += sizeof(TEST_KEY_ID);
9f2ebf
 
9f2ebf
+    if (name2 != NULL && *name2 != '\0') {
9f2ebf
+        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
9f2ebf
+        assert_int_equal(val, type);
9f2ebf
+
9f2ebf
+        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
9f2ebf
+        assert_int_equal(val, (strlen(name) + 1
9f2ebf
+                                    + sizeof(TEST_TOKEN_NAME)
9f2ebf
+                                    + sizeof(TEST_MODULE_NAME)
9f2ebf
+                                    + sizeof(TEST2_KEY_ID)));
9f2ebf
+
9f2ebf
+        assert_int_equal(*(body + rp + strlen(name)), 0);
9f2ebf
+        assert_string_equal(body + rp, name);
9f2ebf
+        rp += strlen(name) + 1;
9f2ebf
+
9f2ebf
+        assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
9f2ebf
+        assert_string_equal(body + rp, TEST_TOKEN_NAME);
9f2ebf
+        rp += sizeof(TEST_TOKEN_NAME);
9f2ebf
+
9f2ebf
+        assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
9f2ebf
+        assert_string_equal(body + rp, TEST_MODULE_NAME);
9f2ebf
+        rp += sizeof(TEST_MODULE_NAME);
9f2ebf
+
9f2ebf
+        assert_int_equal(*(body + rp + sizeof(TEST2_KEY_ID) - 1), 0);
9f2ebf
+        assert_string_equal(body + rp, TEST2_KEY_ID);
9f2ebf
+        rp += sizeof(TEST2_KEY_ID);
9f2ebf
+    }
9f2ebf
+
9f2ebf
     assert_int_equal(rp, blen);
9f2ebf
 
9f2ebf
     return EOK;
9f2ebf
@@ -809,7 +930,8 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
9f2ebf
 static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
9f2ebf
 {
9f2ebf
     return test_pam_cert_check_ex(status, body, blen,
9f2ebf
-                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME);
9f2ebf
+                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
9f2ebf
+                                  NULL);
9f2ebf
 }
9f2ebf
 
9f2ebf
 static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
9f2ebf
@@ -817,14 +939,22 @@ static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
9f2ebf
 {
9f2ebf
     return test_pam_cert_check_ex(status, body, blen,
9f2ebf
                                   SSS_PAM_CERT_INFO_WITH_HINT,
9f2ebf
-                                  "pamuser@"TEST_DOM_NAME);
9f2ebf
+                                  "pamuser@"TEST_DOM_NAME, NULL);
9f2ebf
 }
9f2ebf
 
9f2ebf
 static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body,
9f2ebf
                                                  size_t blen)
9f2ebf
 {
9f2ebf
     return test_pam_cert_check_ex(status, body, blen,
9f2ebf
-                                  SSS_PAM_CERT_INFO_WITH_HINT, "");
9f2ebf
+                                  SSS_PAM_CERT_INFO_WITH_HINT, "", NULL);
9f2ebf
+}
9f2ebf
+
9f2ebf
+static int test_pam_cert_check_2certs(uint32_t status, uint8_t *body,
9f2ebf
+                                      size_t blen)
9f2ebf
+{
9f2ebf
+    return test_pam_cert_check_ex(status, body, blen,
9f2ebf
+                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
9f2ebf
+                                  "pamuser@"TEST_DOM_NAME);
9f2ebf
 }
9f2ebf
 
9f2ebf
 static int test_pam_offline_chauthtok_check(uint32_t status,
9f2ebf
@@ -1737,6 +1867,33 @@ static int test_lookup_by_cert_cb(void *pvt)
9f2ebf
 
9f2ebf
     return EOK;
9f2ebf
 }
9f2ebf
+static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt)
9f2ebf
+{
9f2ebf
+    int ret;
9f2ebf
+    struct sysdb_attrs *attrs;
9f2ebf
+    unsigned char *der = NULL;
9f2ebf
+    size_t der_size;
9f2ebf
+
9f2ebf
+    test_lookup_by_cert_cb(pvt);
9f2ebf
+
9f2ebf
+    attrs = sysdb_new_attrs(pam_test_ctx);
9f2ebf
+    assert_non_null(attrs);
9f2ebf
+
9f2ebf
+    der = sss_base64_decode(pam_test_ctx, TEST_TOKEN_2ND_CERT, &der_size);
9f2ebf
+    assert_non_null(der);
9f2ebf
+
9f2ebf
+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
9f2ebf
+    talloc_free(der);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom,
9f2ebf
+                              pam_test_ctx->pam_user_fqdn,
9f2ebf
+                              attrs,
9f2ebf
+                              LDB_FLAG_MOD_ADD);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    return EOK;
9f2ebf
+}
9f2ebf
 
9f2ebf
 static int test_lookup_by_cert_double_cb(void *pvt)
9f2ebf
 {
9f2ebf
@@ -2094,6 +2251,51 @@ void test_pam_cert_auth_double_cert(void **state)
9f2ebf
     assert_int_equal(ret, EOK);
9f2ebf
 }
9f2ebf
 
9f2ebf
+void test_pam_cert_preauth_2certs_one_mapping(void **state)
9f2ebf
+{
9f2ebf
+    int ret;
9f2ebf
+
9f2ebf
+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
9f2ebf
+
9f2ebf
+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
9f2ebf
+                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
9f2ebf
+
9f2ebf
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
9f2ebf
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
9f2ebf
+
9f2ebf
+    set_cmd_cb(test_pam_cert_check);
9f2ebf
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
9f2ebf
+                          pam_test_ctx->pam_cmds);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    /* Wait until the test finishes with EOK */
9f2ebf
+    ret = test_ev_loop(pam_test_ctx->tctx);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+}
9f2ebf
+
9f2ebf
+void test_pam_cert_preauth_2certs_two_mappings(void **state)
9f2ebf
+{
9f2ebf
+    int ret;
9f2ebf
+
9f2ebf
+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
9f2ebf
+
9f2ebf
+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
9f2ebf
+                        test_lookup_by_cert_cb_2nd_cert_same_user,
9f2ebf
+                        TEST_TOKEN_CERT, false);
9f2ebf
+
9f2ebf
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
9f2ebf
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
9f2ebf
+
9f2ebf
+    set_cmd_cb(test_pam_cert_check_2certs);
9f2ebf
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
9f2ebf
+                          pam_test_ctx->pam_cmds);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    /* Wait until the test finishes with EOK */
9f2ebf
+    ret = test_ev_loop(pam_test_ctx->tctx);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+}
9f2ebf
+
9f2ebf
 void test_filter_response(void **state)
9f2ebf
 {
9f2ebf
     int ret;
9f2ebf
@@ -2523,6 +2725,10 @@ int main(int argc, const char *argv[])
9f2ebf
                                         pam_test_teardown),
9f2ebf
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_double_cert,
9f2ebf
                                         pam_test_setup, pam_test_teardown),
9f2ebf
+        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_one_mapping,
9f2ebf
+                                        pam_test_setup, pam_test_teardown),
9f2ebf
+        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings,
9f2ebf
+                                        pam_test_setup, pam_test_teardown),
9f2ebf
 #endif /* HAVE_NSS */
9f2ebf
 
9f2ebf
         cmocka_unit_test_setup_teardown(test_filter_response,
9f2ebf
diff --git a/src/tests/whitespace_test b/src/tests/whitespace_test
9f2ebf
index 799e35358b1d5ae4b10c4405068fb507cb234b6f..f055ed4c255db4001194844f45a9df7cda774b38 100755
9f2ebf
--- a/src/tests/whitespace_test
9f2ebf
+++ b/src/tests/whitespace_test
9f2ebf
@@ -39,7 +39,7 @@ fi
9f2ebf
 declare found_file=false
9f2ebf
 while read file; do
9f2ebf
     [[ $file == "src/config/testconfigs/noparse.api.conf" ]] && continue
9f2ebf
-    [[ $file =~ ^src/tests/cmocka/p11_nssdb/.*db ]] && continue
9f2ebf
+    [[ $file =~ ^src/tests/cmocka/p11_nssdb.*/.*db ]] && continue
9f2ebf
     test `tail -c 1 $ABS_TOP_SRCDIR/$file` && \
9f2ebf
         echo "Missing new line at the eof: $file" && \
9f2ebf
         found_file=true
9f2ebf
-- 
9f2ebf
2.13.6
9f2ebf