From 101bc4672c1fcffac398d49318c9935988cd8b41 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 13 Nov 2020 12:59:39 +0100
Subject: [PATCH 1/4] pam_sss: use unique id for gdm choice list

Currently the key-id read from the Smartcard is used as key value for
the gdm choice list dialog. Since it might be possible that multiple
certificates use the same key and hence the same key-id this is not a
suitable value.

With this patch the string representation of a numerial counter is used.

Resolves: https://github.com/SSSD/sssd/issues/5400
---
 src/sss_client/pam_sss.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index b844d257e1..ce08f0399f 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -128,6 +128,7 @@ struct cert_auth_info {
     char *key_id;
     char *prompt_str;
     char *pam_cert_user;
+    char *choice_list_id;
     struct cert_auth_info *prev;
     struct cert_auth_info *next;
 };
@@ -141,6 +142,7 @@ static void free_cai(struct cert_auth_info *cai)
         free(cai->module_name);
         free(cai->key_id);
         free(cai->prompt_str);
+        free(cai->choice_list_id);
         free(cai);
     }
 }
@@ -1698,7 +1700,13 @@ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
             ret = ENOMEM;
             goto done;
         }
-        request->list.items[c].key = cai->key_id;
+        ret = asprintf(&cai->choice_list_id, "%zu", c);
+        if (ret == -1) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        request->list.items[c].key = cai->choice_list_id;
         request->list.items[c++].text = prompt;
     }
 
@@ -1719,7 +1727,7 @@ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
     }
 
     DLIST_FOR_EACH(cai, pi->cert_list) {
-        if (strcmp(response->key, cai->key_id) == 0) {
+        if (strcmp(response->key, cai->choice_list_id) == 0) {
             pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
             pi->selected_cert = cai;
             ret = 0;

From e47e9445e6d3348ad669c33d8bda761d165a9837 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 13 Nov 2020 18:05:14 +0100
Subject: [PATCH 2/4] authtok: add label to Smartcard token

The key-id might not be sufficient to identify a certificate on a
Smartcard since it is possible that multiple certificates will use the
same key.

This patch adds the certificate label to the Smartcard authtok item to
about the ambiguity if the key-id is used for multiple certificates.

Resolves: https://github.com/SSSD/sssd/issues/5400
---
 src/p11_child/p11_child.h         |  3 +-
 src/p11_child/p11_child_common.c  | 12 +++--
 src/p11_child/p11_child_openssl.c | 10 ++--
 src/providers/krb5/krb5_child.c   | 14 +++++-
 src/responder/pam/pamsrv_cmd.c    |  5 +-
 src/responder/pam/pamsrv_p11.c    |  8 +++-
 src/sss_client/pam_sss.c          |  3 ++
 src/tests/cmocka/test_authtok.c   | 36 +++++++++------
 src/tests/cmocka/test_pam_srv.c   | 65 ++++++++++++++------------
 src/util/authtok-utils.c          | 30 ++++++++++--
 src/util/authtok-utils.h          | 11 ++++-
 src/util/authtok.c                | 77 +++++++++++++++++++++++++------
 src/util/authtok.h                | 14 +++++-
 13 files changed, 210 insertions(+), 78 deletions(-)

diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h
index 0b53e70c5c..9c0cefe053 100644
--- a/src/p11_child/p11_child.h
+++ b/src/p11_child/p11_child.h
@@ -68,7 +68,8 @@ bool do_verification_b64(struct p11_ctx *p11_ctx, const char *cert_b64);
 errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
                 enum op_mode mode, const char *pin,
                 const char *module_name_in, const char *token_name_in,
-                const char *key_id_in, const char *uri, char **_multi);
+                const char *key_id_in, const char *label,
+                const char *uri, char **_multi);
 
 errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
                                struct cert_verify_opts **cert_verify_opts);
diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c
index 236d7dac45..f17de1a9e9 100644
--- a/src/p11_child/p11_child_common.c
+++ b/src/p11_child/p11_child_common.c
@@ -60,7 +60,8 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
                    bool wait_for_card,
                    const char *cert_b64, const char *pin,
                    const char *module_name, const char *token_name,
-                   const char *key_id, const char *uri, char **multi)
+                   const char *key_id, const char *label, const char *uri,
+                   char **multi)
 {
     int ret;
     struct p11_ctx *p11_ctx;
@@ -91,7 +92,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
         }
     } else {
         ret = do_card(mem_ctx, p11_ctx, mode, pin,
-                      module_name, token_name, key_id, uri, multi);
+                      module_name, token_name, key_id, label, uri, multi);
     }
 
 done:
@@ -158,6 +159,7 @@ int main(int argc, const char *argv[])
     char *module_name = NULL;
     char *token_name = NULL;
     char *key_id = NULL;
+    char *label = NULL;
     char *cert_b64 = NULL;
     bool wait_for_card = false;
     char *uri = NULL;
@@ -194,6 +196,8 @@ int main(int argc, const char *argv[])
          _("Token name for authentication"), NULL},
         {"key_id", 0, POPT_ARG_STRING, &key_id, 0,
          _("Key ID for authentication"), NULL},
+        {"label", 0, POPT_ARG_STRING, &label, 0,
+         _("Label for authentication"), NULL},
         {"certificate", 0, POPT_ARG_STRING, &cert_b64, 0,
          _("certificate to verify, base64 encoded"), NULL},
         {"uri", 0, POPT_ARG_STRING, &uri, 0,
@@ -340,6 +344,7 @@ int main(int argc, const char *argv[])
     }
     talloc_steal(main_ctx, debug_prg_name);
 
+    /* We do not require the label, but it is recommended */
     if (mode == OP_AUTH && (module_name == NULL || token_name == NULL
                                 || key_id == NULL)) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -369,7 +374,8 @@ int main(int argc, const char *argv[])
     }
 
     ret = do_work(main_ctx, mode, ca_db, cert_verify_opts, wait_for_card,
-                  cert_b64, pin, module_name, token_name, key_id, uri, &multi);
+                  cert_b64, pin, module_name, token_name, key_id, label, uri,
+                  &multi);
     if (ret != 0) {
         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
         goto fail;
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index 04b3e1467b..f1e471aa5a 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -1587,7 +1587,8 @@ static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id)
 errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
                 enum op_mode mode, const char *pin,
                 const char *module_name_in, const char *token_name_in,
-                const char *key_id_in, const char *uri_str, char **_multi)
+                const char *key_id_in, const char *label_in,
+                const char *uri_str, char **_multi)
 {
     int ret;
     size_t c;
@@ -1847,9 +1848,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
          * the requested ones for pre-auth. For authentication all attributes
          * must be given and match, for pre-auth only the given ones must
          * match. */
-        DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s.\n",
+        DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s %s.\n",
               module_name_in, module_file_name, token_name_in, token_name,
-              key_id_in, item->id);
+              key_id_in, label_in, item->id);
 
         if ((mode == OP_AUTH
                 && module_name_in != NULL
@@ -1857,6 +1858,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
                 && key_id_in != NULL
                 && item->id != NULL
                 && strcmp(key_id_in, item->id) == 0
+                && (label_in == NULL
+                    || (label_in != NULL && item->label != NULL
+                        && strcmp(label_in, item->label) == 0))
                 && strcmp(token_name_in, token_name) == 0
                 && strcmp(module_name_in, module_file_name) == 0)
             || (mode == OP_PREAUTH
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 6e2bf6d759..cab7b27a28 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -714,7 +714,7 @@ static krb5_error_code answer_pkinit(krb5_context ctx,
         kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
                                  &token_name, NULL,
                                  &module_name, NULL,
-                                 NULL, NULL);
+                                 NULL, NULL, NULL, NULL);
         if (kerr != EOK) {
             DEBUG(SSSDBG_OP_FAILURE,
                   "sss_authtok_get_sc failed.\n");
@@ -1226,11 +1226,12 @@ static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
     const char *token_name;
     const char *module_name;
     const char *key_id;
+    const char *label;
 
     ret = sss_authtok_get_sc(authtok, NULL, NULL,
                              &token_name, NULL,
                              &module_name, NULL,
-                             &key_id, NULL);
+                             &key_id, NULL, &label, NULL);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
         return ret;
@@ -1267,6 +1268,15 @@ static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
         }
     }
 
+    if (label != NULL && *label != '\0') {
+        identity = talloc_asprintf_append(identity, ":certlabel=%s", label);
+        if (identity == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "talloc_asprintf_append failed.\n");
+            return ENOMEM;
+        }
+    }
+
     *_identity = identity;
 
     DEBUG(SSSDBG_TRACE_ALL, "Using pkinit identity [%s].\n", identity);
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 1d02514979..f5606f1e4c 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1258,7 +1258,7 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
                     || sss_authtok_get_type(pd->authtok)
                                                == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
             ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, &key_id, NULL);
+                                     NULL, &key_id, NULL, NULL, NULL);
             if (ret != EOK) {
                 DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
                 goto done;
@@ -2274,7 +2274,8 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
                                  SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
                                  sss_cai_get_token_name(preq->current_cert), 0,
                                  sss_cai_get_module_name(preq->current_cert), 0,
-                                 sss_cai_get_key_id(preq->current_cert), 0);
+                                 sss_cai_get_key_id(preq->current_cert), 0,
+                                 sss_cai_get_label(preq->current_cert), 0);
                         if (ret != EOK) {
                             DEBUG(SSSDBG_OP_FAILURE,
                                   "sss_authtok_set_sc failed, Smartcard "
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index abc9878049..23f94927a0 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -727,6 +727,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     const char *module_name = NULL;
     const char *token_name = NULL;
     const char *key_id = NULL;
+    const char *label = NULL;
 
     req = tevent_req_create(mem_ctx, &state, struct pam_check_cert_state);
     if (req == NULL) {
@@ -766,7 +767,8 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
             || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
         ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, &token_name, NULL,
-                                 &module_name, NULL, &key_id, NULL);
+                                 &module_name, NULL, &key_id, NULL,
+                                 &label, NULL);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
             goto done;
@@ -784,6 +786,10 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
             extra_args[arg_c++] = key_id;
             extra_args[arg_c++] = "--key_id";
         }
+        if (label != NULL && *label != '\0') {
+            extra_args[arg_c++] = label;
+            extra_args[arg_c++] = "--label";
+        }
     }
 
     if (pd->cmd == SSS_PAM_AUTHENTICATE) {
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index ce08f0399f..a247500beb 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -126,6 +126,7 @@ struct cert_auth_info {
     char *token_name;
     char *module_name;
     char *key_id;
+    char *label;
     char *prompt_str;
     char *pam_cert_user;
     char *choice_list_id;
@@ -1960,6 +1961,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
         ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
                                     cai->module_name, 0,
                                     cai->key_id, 0,
+                                    cai->label, 0,
                                     NULL, 0, &needed_size);
         if (ret != EAGAIN) {
             D(("sss_auth_pack_sc_blob failed."));
@@ -1977,6 +1979,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
         ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
                                     cai->module_name, 0,
                                     cai->key_id, 0,
+                                    cai->label, 0,
                                     (uint8_t *) pi->pam_authtok, needed_size,
                                     &needed_size);
         if (ret != EOK) {
diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c
index a8f5bdee7f..a31014eb6b 100644
--- a/src/tests/cmocka/test_authtok.c
+++ b/src/tests/cmocka/test_authtok.c
@@ -451,25 +451,27 @@ void test_sss_authtok_sc_blobs(void **state)
     size_t module_name_len;
     const char *key_id;
     size_t key_id_len;
+    const char *label;
+    size_t label_len;
 
     ts = talloc_get_type_abort(*state, struct test_state);
 
     ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0,
-                                NULL, 0, &needed_size);
+                                "stuvw", 0, NULL, 0, &needed_size);
     assert_int_equal(ret, EAGAIN);
 
     buf = talloc_size(ts, needed_size);
     assert_non_null(buf);
 
     ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0,
-                                buf, needed_size, &needed_size);
+                                "stuvw", 0, buf, needed_size, &needed_size);
     assert_int_equal(ret, EOK);
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-    assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0abc\0defg\0hijkl\0mnopqr\0",
+    assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6\0\0\0abc\0defg\0hijkl\0mnopqr\0stuvw\0",
                         needed_size);
 #else
-    assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7abc\0defg\0hijkl\0mnopqr\0",
+    assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6abc\0defg\0hijkl\0mnopqr\0stuvw\0",
                         needed_size);
 #endif
 
@@ -485,7 +487,8 @@ void test_sss_authtok_sc_blobs(void **state)
     ret = sss_authtok_get_sc(ts->authtoken, &pin, &pin_len,
                              &token_name, &token_name_len,
                              &module_name, &module_name_len,
-                             &key_id, &key_id_len);
+                             &key_id, &key_id_len,
+                             &label, &label_len);
     assert_int_equal(ret, EOK);
     assert_int_equal(pin_len, 3);
     assert_string_equal(pin, "abc");
@@ -495,11 +498,14 @@ void test_sss_authtok_sc_blobs(void **state)
     assert_string_equal(module_name, "hijkl");
     assert_int_equal(key_id_len, 6);
     assert_string_equal(key_id, "mnopqr");
+    assert_int_equal(label_len, 5);
+    assert_string_equal(label, "stuvw");
 
     ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
                              &token_name, &token_name_len,
                              &module_name, &module_name_len,
-                             &key_id, &key_id_len);
+                             &key_id, &key_id_len,
+                             &label, &label_len);
     assert_int_equal(ret, EOK);
     assert_int_equal(token_name_len, 4);
     assert_string_equal(token_name, "defg");
@@ -507,15 +513,19 @@ void test_sss_authtok_sc_blobs(void **state)
     assert_string_equal(module_name, "hijkl");
     assert_int_equal(key_id_len, 6);
     assert_string_equal(key_id, "mnopqr");
+    assert_int_equal(label_len, 5);
+    assert_string_equal(label, "stuvw");
 
     ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
                              &token_name, NULL,
                              &module_name, NULL,
-                             &key_id, NULL);
+                             &key_id, NULL,
+                             &label, NULL);
     assert_int_equal(ret, EOK);
     assert_string_equal(token_name, "defg");
     assert_string_equal(module_name, "hijkl");
     assert_string_equal(key_id, "mnopqr");
+    assert_string_equal(label, "stuvw");
 
     sss_authtok_set_empty(ts->authtoken);
     talloc_free(buf);
@@ -608,14 +618,14 @@ void test_sss_authtok_sc_pin(void **state)
     assert_int_equal(sss_authtok_get_type(ts->authtoken),
                      SSS_AUTHTOK_TYPE_SC_PIN);
     size = sss_authtok_get_size(ts->authtoken);
-    assert_int_equal(size, 28);
+    assert_int_equal(size, 33);
 #if __BYTE_ORDER == __LITTLE_ENDIAN
     assert_memory_equal(sss_authtok_get_data(ts->authtoken),
-                        "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0",
+                        "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0\0",
                         size);
 #else
     assert_memory_equal(sss_authtok_get_data(ts->authtoken),
-                        "\0\0\0\11\0\0\0\1\0\0\0\1\0\0\0\1" "12345678\0\0\0\0",
+                        "\0\0\0\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345678\0\0\0\0\0",
                         size);
 #endif
 
@@ -624,14 +634,14 @@ void test_sss_authtok_sc_pin(void **state)
     assert_int_equal(sss_authtok_get_type(ts->authtoken),
                      SSS_AUTHTOK_TYPE_SC_PIN);
     size = sss_authtok_get_size(ts->authtoken);
-    assert_int_equal(size, 25);
+    assert_int_equal(size, 30);
 #if __BYTE_ORDER == __LITTLE_ENDIAN
     assert_memory_equal(sss_authtok_get_data(ts->authtoken),
-                        "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0",
+                        "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0\0",
                         size);
 #else
     assert_memory_equal(sss_authtok_get_data(ts->authtoken),
-                        "\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\1" "12345\0\0\0\0",
+                        "\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345\0\0\0\0\0",
                         size);
 #endif
 
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 326deaf1fc..cb05042dea 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -536,7 +536,7 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx,
 static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
                                 const char *pin, const char *token_name,
                                 const char *module_name, const char *key_id,
-                                const char *service,
+                                const char *label, const char *service,
                                 acct_cb_t acct_cb, const char *cert)
 {
     size_t buf_size;
@@ -556,14 +556,14 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
 
     if (pin != NULL) {
         ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
-                                    key_id, 0, NULL, 0, &needed_size);
+                                    key_id, 0, label, 0, NULL, 0, &needed_size);
         assert_int_equal(ret, EAGAIN);
 
         pi.pam_authtok = malloc(needed_size);
         assert_non_null(pi.pam_authtok);
 
         ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
-                                    key_id, 0,
+                                    key_id, 0, label, 0,
                                     (uint8_t *)pi.pam_authtok, needed_size,
                                     &needed_size);
         assert_int_equal(ret, EOK);
@@ -1766,7 +1766,7 @@ void test_pam_preauth_no_logon_name(void **state)
     int ret;
 
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                        NULL);
+                        NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -1862,7 +1862,7 @@ void test_pam_preauth_cert_nocert(void **state)
     unsetenv("SOFTHSM2_CONF");
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        NULL, NULL);
+                        NULL, NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2004,7 +2004,7 @@ void test_pam_preauth_cert_nomatch(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, NULL);
+                        NULL, test_lookup_by_cert_cb, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2026,7 +2026,7 @@ void test_pam_preauth_cert_match(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2048,7 +2048,7 @@ void test_pam_preauth_cert_match_gdm_smartcard(void **state)
 
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL,
+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
                         "gdm-smartcard", test_lookup_by_cert_cb,
                         SSSD_TEST_CERT_0001);
 
@@ -2072,7 +2072,7 @@ void test_pam_preauth_cert_match_wrong_user(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_wrong_user_cb,
+                        NULL, test_lookup_by_cert_wrong_user_cb,
                         SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
@@ -2104,7 +2104,7 @@ void test_pam_preauth_cert_no_logon_name(void **state)
      * request will be done with the username found by the certificate
      * lookup. */
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
     mock_account_recv_simple();
     mock_parse_inp("pamuser", NULL, EOK);
     mock_parse_inp("pamuser", NULL, EOK);
@@ -2134,7 +2134,7 @@ void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
      * during pre-auth and there is no need for an extra mocked response as in
      * test_pam_preauth_cert_no_logon_name. */
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2155,7 +2155,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
 
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
@@ -2178,7 +2178,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
     pam_test_ctx->rctx->domains->user_name_hint = true;
 
-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
@@ -2201,7 +2201,7 @@ void test_pam_preauth_no_cert_no_logon_name(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, "/no/path");
 
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                        NULL);
+                        NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2223,7 +2223,7 @@ void test_pam_preauth_cert_no_logon_name_no_match(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, NULL);
+                        NULL, test_lookup_by_cert_cb, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2252,7 +2252,8 @@ void test_pam_cert_auth(void **state)
      * in the cache and no second request to the backend is needed. */
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
                         TEST_MODULE_NAME,
-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
+                        "SSSD test cert 0001", NULL,
                         test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
@@ -2289,7 +2290,8 @@ void test_pam_ecc_cert_auth(void **state)
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
                         "SSSD Test ECC Token",
                         TEST_MODULE_NAME,
-                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", NULL,
+                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB",
+                        "SSSD test ECC cert 0001", NULL,
                         test_lookup_by_cert_cb, SSSD_TEST_ECC_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
@@ -2324,7 +2326,8 @@ void test_pam_cert_auth_no_logon_name(void **state)
      * in the cache and no second request to the backend is needed. */
     mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
                         TEST_MODULE_NAME,
-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
+                        "SSSD test cert 0001", NULL,
                         test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     mock_account_recv_simple();
@@ -2360,7 +2363,7 @@ void test_pam_cert_auth_no_logon_name_no_key_id(void **state)
      * to the user entry the lookup by certificate will already find the user
      * in the cache and no second request to the backend is needed. */
     mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
-                        TEST_MODULE_NAME, NULL, NULL,
+                        TEST_MODULE_NAME, NULL, NULL, NULL,
                         NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
@@ -2387,7 +2390,8 @@ void test_pam_cert_auth_double_cert(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
                         TEST_MODULE_NAME,
-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
+                        "SSSD test cert 0001", NULL,
                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
@@ -2416,7 +2420,7 @@ void test_pam_cert_preauth_2certs_one_mapping(void **state)
     ret = test_lookup_by_cert_cb(discard_const(SSSD_TEST_CERT_0001));
     assert_int_equal(ret, EOK);
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, NULL);
+                        NULL, test_lookup_by_cert_cb, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2439,7 +2443,7 @@ void test_pam_cert_preauth_2certs_two_mappings(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_two.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb_2nd_cert_same_user,
+                        NULL, test_lookup_by_cert_cb_2nd_cert_same_user,
                         SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
@@ -2464,7 +2468,8 @@ void test_pam_cert_auth_2certs_one_mapping(void **state)
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
                         TEST_MODULE_NAME,
-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
+                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
+                        "SSSD test cert 0001", NULL,
                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
@@ -2498,7 +2503,7 @@ void test_pam_cert_preauth_uri_token1(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2528,7 +2533,7 @@ void test_pam_cert_preauth_uri_token2(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0002);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0002);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2567,7 +2572,7 @@ void test_pam_preauth_expired_crl_file(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        NULL, NULL);
+                        NULL, NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2599,7 +2604,7 @@ void test_pam_preauth_expired_crl_file_soft(void **state)
     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2632,7 +2637,7 @@ void test_pam_preauth_ocsp(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        NULL, NULL);
+                        NULL, NULL, NULL);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2674,7 +2679,7 @@ void test_pam_preauth_ocsp_no_ocsp(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
@@ -2708,7 +2713,7 @@ void test_pam_preauth_ocsp_soft_ocsp(void **state)
     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
 
     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
+                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
 
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c
index e50f867410..e76bd17c5f 100644
--- a/src/util/authtok-utils.c
+++ b/src/util/authtok-utils.c
@@ -77,6 +77,7 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
                               const char *token_name, size_t token_name_len,
                               const char *module_name, size_t module_name_len,
                               const char *key_id, size_t key_id_len,
+                              const char *label, size_t label_len,
                               uint8_t *buf, size_t buf_len,
                               size_t *_sc_blob_len)
 {
@@ -88,7 +89,8 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
             || (pin_len != 0 && pin == NULL)
             || (token_name_len != 0 && token_name == NULL)
             || (module_name_len != 0 && module_name == NULL)
-            || (key_id_len != 0 && key_id == NULL)) {
+            || (key_id_len != 0 && key_id == NULL)
+            || (label_len != 0 && label == NULL)) {
         return EINVAL;
     }
 
@@ -113,6 +115,11 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
         key_id_len = 0;
     }
 
+    if (label == NULL) {
+        label = "";
+        label_len = 0;
+    }
+
     /* len should not include the trailing \0 */
     if (pin_len == 0 || pin[pin_len - 1] == '\0') {
         pin_len = strlen(pin);
@@ -130,8 +137,12 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
         key_id_len = strlen(key_id);
     }
 
-    *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len + 4
-                            + 4 * sizeof(uint32_t);
+    if (label_len == 0 || label[label_len - 1] == '\0') {
+        label_len = strlen(label);
+    }
+
+    *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len
+                            + label_len + 5 + 5 * sizeof(uint32_t);
     if (buf == NULL || buf_len < *_sc_blob_len) {
         return EAGAIN;
     }
@@ -145,6 +156,8 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
     SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
     tmp_uint32_t = (uint32_t) key_id_len + 1;
     SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+    tmp_uint32_t = (uint32_t) label_len + 1;
+    SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
 
     memcpy(buf + c, pin, pin_len);
     buf[c + pin_len] = '\0';
@@ -160,6 +173,10 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
 
     memcpy(buf + c, key_id, key_id_len);
     buf[c + key_id_len] = '\0';
+    c += key_id_len +1;
+
+    memcpy(buf + c, label, label_len);
+    buf[c + label_len] = '\0';
 
     return 0;
 }
@@ -171,6 +188,7 @@ const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len)
     uint32_t token_name_len;
     uint32_t module_name_len;
     uint32_t key_id_len;
+    uint32_t label_len;
 
     if (blob == NULL || blob_len == 0) {
         return NULL;
@@ -184,9 +202,11 @@ const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len)
     SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c);
     SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c);
     SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c);
+    SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
 
-    if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len
-                                         + module_name_len + key_id_len) {
+    if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
+                                         + module_name_len + key_id_len
+                                         + label_len) {
         return NULL;
     }
 
diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h
index 714c8187e1..f3b268f780 100644
--- a/src/util/authtok-utils.h
+++ b/src/util/authtok-utils.h
@@ -39,6 +39,9 @@
  * @param[in]  key_id      Key ID of the certificate
  * @param[in]  key_id_len  Length of the key id of the certificate, if 0
  *                         strlen() will be called internally
+ * @param[in]  label       Label of the certificate
+ * @param[in]  label_len   Length of the label of the certificate, if 0
+ *                         strlen() will be called internally
  * @param[in]  buf         memory buffer of size buf_len, may be NULL
  * @param[in]  buf_len     size of memory buffer buf
  *
@@ -53,6 +56,7 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
                               const char *token_name, size_t token_name_len,
                               const char *module_name, size_t module_name_len,
                               const char *key_id, size_t key_id_len,
+                              const char *label, size_t label_len,
                               uint8_t *buf, size_t buf_len,
                               size_t *_sc_blob_len);
 /**
@@ -112,6 +116,10 @@ errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx,
  * @param[out] _token_name_len   Length of the token name
  * @param[out] _module_name      Name of PKCS#11 module, null terminated
  * @param[out] _module_name_len  Length of the module name
+ * @param[out] _key_id           Key ID of the certificate, null terminated
+ * @param[out] _key_id_len       Length of the key ID
+ * @param[out] _labe l           Label of the certificate, null terminated
+ * @param[out] _label_len        Length of the label
  *
  * @return     EOK       on success
  *             EINVAL    if input data is not consistent
@@ -122,7 +130,8 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
                                  char **pin, size_t *_pin_len,
                                  char **token_name, size_t *_token_name_len,
                                  char **module_name, size_t *_module_name_len,
-                                 char **key_id, size_t *_key_id_len);
+                                 char **key_id, size_t *_key_id_len,
+                                 char **label, size_t *_label_len);
 
 /**
  * @brief Return a pointer to the PIN string in the memory buffer
diff --git a/src/util/authtok.c b/src/util/authtok.c
index f8b44d6d65..7254ed1da1 100644
--- a/src/util/authtok.c
+++ b/src/util/authtok.c
@@ -503,7 +503,8 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
                            const char *pin, size_t pin_len,
                            const char *token_name, size_t token_name_len,
                            const char *module_name, size_t module_name_len,
-                           const char *key_id, size_t key_id_len)
+                           const char *key_id, size_t key_id_len,
+                           const char *label, size_t label_len)
 {
     int ret;
     size_t needed_size;
@@ -518,7 +519,7 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
 
     ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
                                 module_name, module_name_len,
-                                key_id, key_id_len, NULL, 0,
+                                key_id, key_id_len, label, label_len, NULL, 0,
                                 &needed_size);
     if (ret != EAGAIN) {
         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
@@ -533,7 +534,7 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
 
     ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
                                 module_name, module_name_len,
-                                key_id, key_id_len, tok->data,
+                                key_id, key_id_len, label, label_len, tok->data,
                                 needed_size, &needed_size);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
@@ -560,6 +561,8 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
     size_t module_name_len;
     char *key_id = NULL;
     size_t key_id_len;
+    char *label = NULL;
+    size_t label_len;
     TALLOC_CTX *tmp_ctx;
 
     if (tok == NULL) {
@@ -579,7 +582,7 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
     ret = sss_auth_unpack_sc_blob(tmp_ctx, data, len, &pin, &pin_len,
                                   &token_name, &token_name_len,
                                   &module_name, &module_name_len,
-                                  &key_id, &key_id_len);
+                                  &key_id, &key_id_len, &label, &label_len);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_sc_blob failed.\n");
         goto done;
@@ -588,7 +591,7 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
     ret = sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, pin_len,
                              token_name, token_name_len,
                              module_name, module_name_len,
-                             key_id, key_id_len);
+                             key_id, key_id_len, label, label_len);
 
 done:
     talloc_free(tmp_ctx);
@@ -607,7 +610,7 @@ errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
     }
 
     return sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, len,
-                              NULL, 0, NULL, 0, NULL, 0);
+                              NULL, 0, NULL, 0, NULL, 0, NULL, 0);
 }
 
 errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
@@ -625,7 +628,8 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
         return ENOENT;
     case SSS_AUTHTOK_TYPE_SC_PIN:
         ret = sss_authtok_get_sc(tok, &pin, &pin_len,
-                                 NULL, NULL, NULL, NULL, NULL, NULL);
+                                 NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, NULL);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
             return ret;
@@ -663,13 +667,15 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
                                  char **pin, size_t *_pin_len,
                                  char **token_name, size_t *_token_name_len,
                                  char **module_name, size_t *_module_name_len,
-                                 char **key_id, size_t *_key_id_len)
+                                 char **key_id, size_t *_key_id_len,
+                                 char **label, size_t *_label_len)
 {
     size_t c;
     uint32_t pin_len;
     uint32_t token_name_len;
     uint32_t module_name_len;
     uint32_t key_id_len;
+    uint32_t label_len;
 
     c = 0;
 
@@ -678,14 +684,16 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
         token_name_len = 0;
         module_name_len = 0;
         key_id_len = 0;
+        label_len = 0;
     } else if (blob_len > 0
                 && strnlen((const char *) blob, blob_len) == blob_len - 1) {
         pin_len = blob_len;
         token_name_len = 0;
         module_name_len = 0;
         key_id_len = 0;
+        label_len = 0;
     } else {
-        if (blob_len < 4 * sizeof(uint32_t)) {
+        if (blob_len < 5 * sizeof(uint32_t)) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
             return EINVAL;
         }
@@ -694,9 +702,11 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
         SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c);
         SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c);
         SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c);
+        SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
 
-        if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len
-                                             + module_name_len + key_id_len) {
+        if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
+                                             + module_name_len + key_id_len
+                                             + label_len) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
             return EINVAL;
         }
@@ -756,6 +766,25 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
         *key_id = NULL;
     }
 
+    if (label_len != 0) {
+        *label = talloc_strndup(mem_ctx,
+                                      (const char *) blob + c + pin_len
+                                                              + token_name_len
+                                                              + module_name_len
+                                                              + key_id_len,
+                                      label_len);
+        if (*label == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            talloc_free(*pin);
+            talloc_free(*token_name);
+            talloc_free(*module_name);
+            talloc_free(*key_id);
+            return ENOMEM;
+        }
+    } else {
+        *label = NULL;
+    }
+
     /* Re-calculate length for the case where \0 was missing in the blob */
     if (_pin_len != NULL) {
         *_pin_len = (*pin == NULL) ? 0 : strlen(*pin);
@@ -771,6 +800,10 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
         *_key_id_len = (*key_id == NULL) ? 0 : strlen(*key_id);
     }
 
+    if (_label_len != NULL) {
+        *_label_len = (*label == NULL) ? 0 : strlen(*label);
+    }
+
     return EOK;
 }
 
@@ -778,13 +811,15 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
                            const char **_pin, size_t *_pin_len,
                            const char **_token_name, size_t *_token_name_len,
                            const char **_module_name, size_t *_module_name_len,
-                           const char **_key_id, size_t *_key_id_len)
+                           const char **_key_id, size_t *_key_id_len,
+                           const char **_label, size_t *_label_len)
 {
     size_t c = 0;
     size_t pin_len;
     size_t token_name_len;
     size_t module_name_len;
     size_t key_id_len;
+    size_t label_len;
     uint32_t tmp_uint32_t;
 
     if (!tok) {
@@ -796,7 +831,7 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
         return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
     }
 
-    if (tok->length < 4 * sizeof(uint32_t)) {
+    if (tok->length < 5 * sizeof(uint32_t)) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
         return EINVAL;
     }
@@ -809,9 +844,12 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
     module_name_len = tmp_uint32_t -1;
     SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
     key_id_len = tmp_uint32_t -1;
+    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+    label_len = tmp_uint32_t -1;
 
-    if (tok->length != 4 * sizeof(uint32_t) +  4 + pin_len + token_name_len
-                                         + module_name_len + key_id_len) {
+    if (tok->length != 5 * sizeof(uint32_t) +  5 + pin_len + token_name_len
+                                         + module_name_len + key_id_len
+                                         + label_len) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
         return EINVAL;
     }
@@ -846,5 +884,14 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
         *_key_id_len = key_id_len;
     }
 
+    if (_label != NULL) {
+        *_label = (const char *) tok->data + c + pin_len + 1
+                               + token_name_len + 1 + module_name_len + 1
+                               + key_id_len + 1;
+    }
+    if (_label_len != NULL) {
+        *_label_len = label_len;
+    }
+
     return EOK;
 }
diff --git a/src/util/authtok.h b/src/util/authtok.h
index f70c9da135..6fd3e9ef03 100644
--- a/src/util/authtok.h
+++ b/src/util/authtok.h
@@ -296,6 +296,10 @@ void sss_authtok_set_sc_keypad(struct sss_auth_token *tok);
  *                        terminated string containing the PKCS#11 key id
  * @param key_id_len      The length of the key id string, if set to 0 it will be
  *                        calculated
+ * @param label           A pointer to a const char *, that will point to a null
+ *                        terminated string containing the PKCS#11 label
+ * @param label_len       The length of the label string, if set to 0 it will be
+ *                        calculated
  *
  * @return       EOK on success
  *               EINVAL unexpected or inval input
@@ -306,7 +310,8 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
                            const char *pin, size_t pin_len,
                            const char *token_name, size_t token_name_len,
                            const char *module_name, size_t module_name_len,
-                           const char *key_id, size_t key_id_len);
+                           const char *key_id, size_t key_id_len,
+                           const char *label, size_t label_len);
 /**
  * @brief Set a Smart Card authentication data, replacing any previous data
  *
@@ -342,6 +347,10 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
  *                              a null terminated string holding the PKCS#11
  *                              key id, may not be modified or freed
  * @param[out] _key_id_len      Length of the PKCS#11 key id
+ * @param[out] _label           A pointer to a const char *, that will point to
+ *                              a null terminated string holding the PKCS#11
+ *                              label, may not be modified or freed
+ * @param[out] _label_len       Length of the PKCS#11 label
  *
  * Any of the output pointers may be NULL if the caller does not need the
  * specific item.
@@ -356,7 +365,8 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
                            const char **_pin, size_t *_pin_len,
                            const char **_token_name, size_t *_token_name_len,
                            const char **_module_name, size_t *_module_name_len,
-                           const char **_key_id, size_t *_key_id_len);
+                           const char **_key_id, size_t *_key_id_len,
+                           const char **_label, size_t *_label_len);
 
 
 /**

From e64b2356aa93b6b0a1bbd29957e76ec1102723df Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Sat, 14 Nov 2020 17:52:35 +0100
Subject: [PATCH 3/4] pam_sss: add certificate label to reply to pam_sss

Add the certificate label to the data send back and forth to the pam
module to avoid the ambiguity if two certificate use the same key.

Resolves: https://github.com/SSSD/sssd/issues/5400
---
 src/responder/pam/pamsrv_p11.c  | 13 ++++++++++---
 src/sss_client/pam_sss.c        | 15 +++++++++++++++
 src/tests/cmocka/test_pam_srv.c | 20 ++++++++++++++++----
 3 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 23f94927a0..e1fd72e642 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -1086,11 +1086,13 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
     const char *token_name;
     const char *module_name;
     const char *key_id;
+    const char *label;
     char *prompt;
     size_t user_len;
     size_t token_len;
     size_t module_len;
     size_t key_id_len;
+    size_t label_len;
     size_t prompt_len;
     size_t nss_name_len;
     const char *username = "";
@@ -1113,16 +1115,18 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
     token_name = sss_cai_get_token_name(cert_info);
     module_name = sss_cai_get_module_name(cert_info);
     key_id = sss_cai_get_key_id(cert_info);
+    label = sss_cai_get_label(cert_info);
 
     user_len = strlen(username) + 1;
     token_len = strlen(token_name) + 1;
     module_len = strlen(module_name) + 1;
     key_id_len = strlen(key_id) + 1;
+    label_len = strlen(label) + 1;
     prompt_len = strlen(prompt) + 1;
     nss_name_len = strlen(nss_username) +1;
 
-    msg_len = user_len + token_len + module_len + key_id_len + prompt_len
-                       + nss_name_len;
+    msg_len = user_len + token_len + module_len + key_id_len + label_len
+                       + prompt_len + nss_name_len;
 
     msg = talloc_zero_size(mem_ctx, msg_len);
     if (msg == NULL) {
@@ -1136,8 +1140,11 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
     memcpy(msg + user_len + token_len, module_name, module_len);
     memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
     memcpy(msg + user_len + token_len + module_len + key_id_len,
+           label, label_len);
+    memcpy(msg + user_len + token_len + module_len + key_id_len + label_len,
            prompt, prompt_len);
-    memcpy(msg + user_len + token_len + module_len + key_id_len + prompt_len,
+    memcpy(msg + user_len + token_len + module_len + key_id_len + label_len
+               + prompt_len,
            nss_username, nss_name_len);
     talloc_free(prompt);
 
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index a247500beb..2bf6244b8f 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -142,6 +142,7 @@ static void free_cai(struct cert_auth_info *cai)
         free(cai->token_name);
         free(cai->module_name);
         free(cai->key_id);
+        free(cai->label);
         free(cai->prompt_str);
         free(cai->choice_list_id);
         free(cai);
@@ -936,6 +937,20 @@ static int parse_cert_info(struct pam_items *pi, uint8_t *buf, size_t len,
         goto done;
     }
 
+    cai->label = strdup((char *) &buf[*p + offset]);
+    if (cai->label == NULL) {
+        D(("strdup failed"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    offset += strlen(cai->label) + 1;
+    if (offset >= len) {
+        D(("Cert message size mismatch"));
+        ret = EINVAL;
+        goto done;
+    }
+
     cai->prompt_str = strdup((char *) &buf[*p + offset]);
     if (cai->prompt_str == NULL) {
         D(("strdup failed"));
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index cb05042dea..5506fbf346 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -62,13 +62,16 @@
 #define TEST_TOKEN_NAME "SSSD Test Token"
 #define TEST_TOKEN2_NAME "SSSD Test Token Number 2"
 #define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17"
+#define TEST_LABEL "SSSD test cert 0001"
 #define TEST_MODULE_NAME SOFTHSM2_PATH
 #define TEST_PROMPT "SSSD test cert 0001\nCN=SSSD test cert 0001,OU=SSSD test,O=SSSD"
 #define TEST2_PROMPT "SSSD test cert 0002\nCN=SSSD test cert 0002,OU=SSSD test,O=SSSD"
 #define TEST5_PROMPT "SSSD test cert 0005\nCN=SSSD test cert 0005,OU=SSSD test,O=SSSD"
 
 #define TEST2_KEY_ID "5405842D56CF31F0BB025A695C5F3E907051C5B9"
+#define TEST2_LABEL "SSSD test cert 0002"
 #define TEST5_KEY_ID "1195833C424AB00297F582FC43FFFFAB47A64CC9"
+#define TEST5_LABEL "SSSD test cert 0005"
 
 static char CACHED_AUTH_TIMEOUT_STR[] = "4";
 static const int CACHED_AUTH_TIMEOUT = 4;
@@ -673,6 +676,7 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
                                 + sizeof(TEST_TOKEN_NAME)
                                 + sizeof(TEST_MODULE_NAME)
                                 + sizeof(TEST_KEY_ID)
+                                + sizeof(TEST_LABEL)
                                 + sizeof(TEST_PROMPT)
                                 + sizeof("pamuser")));
 
@@ -692,6 +696,10 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
     assert_string_equal(body + rp, TEST_KEY_ID);
     rp += sizeof(TEST_KEY_ID);
 
+    assert_int_equal(*(body + rp + sizeof(TEST_LABEL) - 1), 0);
+    assert_string_equal(body + rp, TEST_LABEL);
+    rp += sizeof(TEST_LABEL);
+
     assert_int_equal(*(body + rp + sizeof(TEST_PROMPT) - 1), 0);
     assert_string_equal(body + rp, TEST_PROMPT);
     rp += sizeof(TEST_PROMPT);
@@ -740,6 +748,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
                                     TEST_TOKEN_NAME,
                                     TEST_MODULE_NAME,
                                     TEST_KEY_ID,
+                                    TEST_LABEL,
                                     TEST_PROMPT,
                                     NULL,
                                     NULL };
@@ -749,6 +758,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
                                      TEST_TOKEN_NAME,
                                      TEST_MODULE_NAME,
                                      TEST2_KEY_ID,
+                                     TEST2_LABEL,
                                      TEST2_PROMPT,
                                      NULL,
                                      NULL };
@@ -756,10 +766,10 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
     assert_int_equal(status, 0);
 
     check_strings[0] = name;
-    check_strings[5] = nss_name;
+    check_strings[6] = nss_name;
     check_len = check_string_array_len(check_strings);
     check2_strings[0] = name;
-    check2_strings[5] = nss_name;
+    check2_strings[6] = nss_name;
     check2_len = check_string_array_len(check2_strings);
 
 
@@ -843,6 +853,7 @@ static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body,
                                      TEST_TOKEN2_NAME,
                                      TEST_MODULE_NAME,
                                      TEST2_KEY_ID,
+                                     TEST2_LABEL,
                                      TEST2_PROMPT,
                                      NULL,
                                      NULL };
@@ -850,7 +861,7 @@ static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body,
     assert_int_equal(status, 0);
 
     check2_strings[0] = name;
-    check2_strings[5] = nss_name;
+    check2_strings[6] = nss_name;
     check2_len = check_string_array_len(check2_strings);
 
     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
@@ -895,7 +906,7 @@ static int test_pam_cert_X_token_X_check_ex(uint32_t status, uint8_t *body,
     assert_int_equal(status, 0);
 
     check_strings[0] = name;
-    check_strings[5] = nss_name;
+    check_strings[6] = nss_name;
     check_len = check_string_array_len(check_strings);
 
     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
@@ -946,6 +957,7 @@ static int test_pam_cert5_check(uint32_t status, uint8_t *body, size_t blen)
                                      TEST_TOKEN_NAME,
                                      TEST_MODULE_NAME,
                                      TEST5_KEY_ID,
+                                     TEST5_LABEL,
                                      TEST5_PROMPT,
                                      NULL,
                                      NULL };

From 19d816d1ebe4bb6ceed228f3d04c0bc15f6335dc Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 3 Nov 2020 09:58:52 +0100
Subject: [PATCH 4/4] add tests multiple certs same id

Add unit test for the case that two certificates use the same key.

Resolves: https://github.com/SSSD/sssd/issues/5400
---
 src/tests/cmocka/test_pam_srv.c | 116 ++++++++++++++++++++++++++++++++
 src/tests/test_CA/Makefile.am   |  24 ++++++-
 2 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 5506fbf346..8ca5abd432 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -40,12 +40,14 @@
 #include "tests/test_CA/SSSD_test_cert_x509_0001.h"
 #include "tests/test_CA/SSSD_test_cert_x509_0002.h"
 #include "tests/test_CA/SSSD_test_cert_x509_0005.h"
+#include "tests/test_CA/SSSD_test_cert_x509_0006.h"
 
 #include "tests/test_ECC_CA/SSSD_test_ECC_cert_x509_0001.h"
 #else
 #define SSSD_TEST_CERT_0001 ""
 #define SSSD_TEST_CERT_0002 ""
 #define SSSD_TEST_CERT_0005 ""
+#define SSSD_TEST_CERT_0006 ""
 
 #define SSSD_TEST_ECC_CERT_0001 ""
 #endif
@@ -1093,6 +1095,13 @@ static int test_pam_creds_insufficient_check(uint32_t status,
     return EOK;
 }
 
+static int test_pam_auth_err_check(uint32_t status, uint8_t *body, size_t blen)
+{
+    /* PAM_AUTH_ERR is returned for different types of error, we use different
+     * names for the check functions to make the purpose more clear. */
+    return test_pam_wrong_pw_offline_auth_check(status, body, blen);
+}
+
 static int test_pam_user_unknown_check(uint32_t status,
                                        uint8_t *body, size_t blen)
 {
@@ -2500,6 +2509,107 @@ void test_pam_cert_auth_2certs_one_mapping(void **state)
     assert_int_equal(ret, EOK);
 }
 
+/* The following three tests cover a use case where multiple certificates are
+ * using the same key-pair. According to PKCS#11 specs "The CKA_ID field is
+ * intended to distinguish among multiple keys. In the case of public and
+ * private keys, this field assists in handling multiple keys held by the same
+ * subject; the key identifier for a public key and its corresponding private
+ * key should be the same. The key identifier should also be the same as for
+ * the corresponding certificate, if one exists. Cryptoki does not enforce
+ * these associations, however." As a result certificates sharing the same
+ * key-pair will have the same id on the Smartcard. This means a second
+ * parameter is needed to distinguish them. We use the label here.
+ *
+ * The first test makes sure authentication fails is the label is missing, the
+ * second and third test make sure that each certificate can be selected with
+ * the proper label. */
+void test_pam_cert_auth_2certs_same_id_no_label(void **state)
+{
+    int ret;
+
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
+
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                        TEST_MODULE_NAME,
+                        "11111111",
+                        NULL, NULL,
+                        NULL, SSSD_TEST_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_auth_err_check);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          pam_test_ctx->pam_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(pam_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_pam_cert_auth_2certs_same_id_with_label_1(void **state)
+{
+    int ret;
+
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
+
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                        TEST_MODULE_NAME,
+                        "11111111",
+                        "SSSD test cert 0001", NULL,
+                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_simple_check_success);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          pam_test_ctx->pam_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(pam_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_pam_cert_auth_2certs_same_id_with_label_6(void **state)
+{
+    int ret;
+
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
+
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                        TEST_MODULE_NAME,
+                        "11111111",
+                        "SSSD test cert 0006", NULL,
+                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0006);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_simple_check_success);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          pam_test_ctx->pam_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(pam_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
 void test_pam_cert_preauth_uri_token1(void **state)
 {
     int ret;
@@ -3179,6 +3289,12 @@ int main(int argc, const char *argv[])
                                         pam_test_setup, pam_test_teardown),
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_one_mapping,
                                         pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_no_label,
+                                        pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_1,
+                                        pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_6,
+                                        pam_test_setup, pam_test_teardown),
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name,
                                         pam_test_setup, pam_test_teardown),
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am
index 0e0122737b..3b80c97ba4 100644
--- a/src/tests/test_CA/Makefile.am
+++ b/src/tests/test_CA/Makefile.am
@@ -25,7 +25,7 @@ pubkeys = $(addprefix SSSD_test_cert_pubsshkey_,$(addsuffix .pub,$(ids)))
 pubkeys_h = $(addprefix SSSD_test_cert_pubsshkey_,$(addsuffix .h,$(ids)))
 pkcs12 = $(addprefix SSSD_test_cert_pkcs12_,$(addsuffix .pem,$(ids)))
 
-extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens softhsm2_ocsp
+extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens softhsm2_ocsp softhsm2_2certs_same_id
 if HAVE_FAKETIME
 extra += SSSD_test_CA_expired_crl.pem
 endif
@@ -41,6 +41,13 @@ $(pwdfile):
 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_0006 should use the same key as SSSD_test_cert_0001
+SSSD_test_cert_req_0006.pem: $(srcdir)/SSSD_test_cert_key_0001.pem $(srcdir)/SSSD_test_cert_0006.config
+	if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_0006.config) -eq 0 ]; then \
+		$(OPENSSL) req -new -nodes -key $< -config $(srcdir)/SSSD_test_cert_0006.config -out $@ ; \
+	else \
+		$(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_0006.config -out $@ ; \
+	fi
 
 SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test_cert_%.config
 	if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_$*.config) -eq 0 ]; then \
@@ -52,6 +59,9 @@ SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test
 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 $@
 
+SSSD_test_cert_pkcs12_0006.pem: SSSD_test_cert_x509_0006.pem $(srcdir)/SSSD_test_cert_key_0001.pem $(pwdfile)
+	$(OPENSSL) pkcs12 -export -in SSSD_test_cert_x509_0006.pem -inkey $(srcdir)/SSSD_test_cert_key_0001.pem -nodes -passout file:$(pwdfile) -out $@
+
 SSSD_test_cert_pkcs12_%.pem: SSSD_test_cert_x509_%.pem $(srcdir)/SSSD_test_cert_key_%.pem $(pwdfile)
 	$(OPENSSL) pkcs12 -export -in SSSD_test_cert_x509_$*.pem -inkey $(srcdir)/SSSD_test_cert_key_$*.pem -nodes -passout file:$(pwdfile) -out $@
 
@@ -130,6 +140,18 @@ softhsm2_ocsp.conf:
 	@echo "objectstore.backend = file" >> $@
 	@echo "slots.removable = true" >> $@
 
+softhsm2_2certs_same_id: softhsm2_2certs_same_id.conf SSSD_test_cert_x509_0001.pem SSSD_test_cert_x509_0002.pem
+	mkdir $@
+	SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token  --label "SSSD Test Token" --pin 123456 --so-pin 123456 --free
+	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0006.pem --login  --label 'SSSD test cert 0006' --id '11111111'
+	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0001.pem --login  --label 'SSSD test cert 0001' --id '11111111'
+	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_cert_key_0001.pem --login  --label 'SSSD test cert 0001' --id '11111111'
+
+softhsm2_2certs_same_id.conf:
+	@echo "directories.tokendir = "$(abs_top_builddir)"/src/tests/test_CA/softhsm2_2certs_same_id" > $@
+	@echo "objectstore.backend = file" >> $@
+	@echo "slots.removable = true" >> $@
+
 CLEANFILES = \
     index.txt  index.txt.attr \
     index.txt.attr.old  index.txt.old \
