URL: https://github.com/SSSD/sssd/pull/671
Author: sumit-bose
 Title: #671: PAM: use PKCS#11 URIs to restrict certificate selection
Action: opened

PR body:
"""
With the new option 'p11_uri' to the PAM responder can be used to restrict the
selection of certificates in p11_child with the help of a PKCS#11 URI.

The implementation of for the NSS version of p11_child is not available in this
PR. As you can see in the first patch the support for PKCS#11 URIs in NSS is
limited and I have to talk to NSS developers first if this will change of if it
would make more sense to use the PKCS#11 URI calls form libp11kit for the NSS
version as well.

To avoid rebase issues this PR is already on top of PR#668.

Related to https://pagure.io/SSSD/sssd/issue/3814
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/671/head:pr671
git checkout pr671
From c3b18db91f9a9b3101f62084f55b2e0fc770883d Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Fri, 14 Sep 2018 12:47:00 +0200
Subject: [PATCH 01/10] p11_child: add --wait_for_card option

The --wait_for_card option will let the p11_child wait until a
Smartcard/token is available in a slot with the removable flag.

Related to  https://pagure.io/SSSD/sssd/issue/3650
---
 src/p11_child/p11_child.h         |   5 +-
 src/p11_child/p11_child_common.c  |  12 ++-
 src/p11_child/p11_child_nss.c     | 105 ++++++++++++++++-------
 src/p11_child/p11_child_openssl.c | 136 ++++++++++++++++++++++++------
 4 files changed, 196 insertions(+), 62 deletions(-)

diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h
index 1e9fc3d1c..dd8fdeafb 100644
--- a/src/p11_child/p11_child.h
+++ b/src/p11_child/p11_child.h
@@ -25,6 +25,9 @@
 #ifndef __P11_CHILD_H__
 #define __P11_CHILD_H__
 
+/* Time to wait during a C_Finalize C_Initialize cycle to discover
+ * new slots. */
+#define PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME 3
 struct p11_ctx;
 
 enum op_mode {
@@ -41,7 +44,7 @@ enum pin_mode {
 };
 
 errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *nss_db,
-                     struct p11_ctx **p11_ctx);
+                     bool wait_for_card, struct p11_ctx **p11_ctx);
 
 errno_t init_verification(struct p11_ctx *p11_ctx,
                           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 125430d13..bc5f6b09b 100644
--- a/src/p11_child/p11_child_common.c
+++ b/src/p11_child/p11_child_common.c
@@ -57,6 +57,7 @@ static const char *op_mode_str(enum op_mode mode)
 
 static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
                    struct cert_verify_opts *cert_verify_opts,
+                   bool wait_for_card,
                    const char *cert_b64, const char *pin,
                    const char *module_name, const char *token_name,
                    const char *key_id, char **multi)
@@ -64,7 +65,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
     int ret;
     struct p11_ctx *p11_ctx;
 
-    ret = init_p11_ctx(mem_ctx, ca_db, &p11_ctx);
+    ret = init_p11_ctx(mem_ctx, ca_db, wait_for_card, &p11_ctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "init_p11_ctx failed.\n");
         return ret;
@@ -157,6 +158,7 @@ int main(int argc, const char *argv[])
     char *token_name = NULL;
     char *key_id = NULL;
     char *cert_b64 = NULL;
+    bool wait_for_card = false;
 
     struct poptOption long_options[] = {
         POPT_AUTOHELP
@@ -174,6 +176,7 @@ int main(int argc, const char *argv[])
         SSSD_LOGGER_OPTS
         {"auth", 0, POPT_ARG_NONE, NULL, 'a', _("Run in auth mode"), NULL},
         {"pre", 0, POPT_ARG_NONE, NULL, 'p', _("Run in pre-auth mode"), NULL},
+        {"wait_for_card", 0, POPT_ARG_NONE, NULL, 'w', _("Wait until card is available"), NULL},
         {"verification", 0, POPT_ARG_NONE, NULL, 'v', _("Run in verification mode"),
          NULL},
         {"pin", 0, POPT_ARG_NONE, NULL, 'i', _("Expect PIN on stdin"), NULL},
@@ -258,6 +261,9 @@ int main(int argc, const char *argv[])
             }
             pin_mode = PIN_KEYPAD;
             break;
+        case 'w':
+            wait_for_card = true;
+            break;
         default:
             fprintf(stderr, "\nInvalid option %s: %s\n\n",
                   poptBadOption(pc, 0), poptStrerror(opt));
@@ -360,8 +366,8 @@ int main(int argc, const char *argv[])
         }
     }
 
-    ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, cert_b64,
-                 pin, module_name, token_name, key_id, &multi);
+    ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, wait_for_card,
+                  cert_b64, pin, module_name, token_name, key_id, &multi);
     if (ret != 0) {
         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
         goto fail;
diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index d6a0b804a..b2777d1d2 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -51,6 +51,7 @@ struct p11_ctx {
     CERTCertDBHandle *handle;
     struct cert_verify_opts *cert_verify_opts;
     const char *nss_db;
+    bool wait_for_card;
 };
 
 #define EXP_USAGES (  certificateUsageSSLClient \
@@ -141,6 +142,19 @@ static int talloc_free_handle(struct p11_ctx *p11_ctx)
     return 0;
 }
 
+static NSSInitContext *get_nss_ctx(const char *nss_db)
+{
+    uint32_t flags = NSS_INIT_READONLY
+                                   | NSS_INIT_FORCEOPEN
+                                   | NSS_INIT_NOROOTINIT
+                                   | NSS_INIT_OPTIMIZESPACE
+                                   | NSS_INIT_PK11RELOAD;
+    NSSInitParameters parameters = { 0 };
+    parameters.length =  sizeof (parameters);
+
+    return NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
+}
+
 errno_t init_verification(struct p11_ctx *p11_ctx,
                           struct cert_verify_opts *cert_verify_opts)
 {
@@ -256,14 +270,15 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     SECItem signed_random_value = {0};
     SECKEYPublicKey *pub_key;
     CERTCertificate *found_cert = NULL;
-    PK11SlotList *list = NULL;
-    PK11SlotListElement *le;
     const char *label;
     char *key_id_str = NULL;
     CERTCertList *valid_certs = NULL;
     char *cert_b64 = NULL;
     char *multi = NULL;
     PRCList *node;
+    CK_SLOT_INFO slInfo;
+    PK11TokenStatus token_status;
+    size_t s;
 
     PK11_SetPasswordFunc(password_passthrough);
 
@@ -297,28 +312,50 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
                                 mod_list_item->module->dllName);
     }
 
-    list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE,
-                             NULL);
-    if (list == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetAllTokens failed.\n");
-        ret = EIO;
-        goto done;
-    }
+    for (;;)  {
+        mod_list = SECMOD_GetDefaultModuleList();
+        for (mod_list_item = mod_list; mod_list_item != NULL;
+                                       mod_list_item = mod_list_item->next) {
+            for (s = 0; s < mod_list_item->module->slotCount; s++) {
+                slInfo.flags = 0;
+                rv = PK11_GetSlotInfo(mod_list_item->module->slots[s], &slInfo);
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "Description [%s] Manufacturer [%s] flags [%lu] "
+                      "removable [%s] token present [%s].\n",
+                      slInfo.slotDescription, slInfo.manufacturerID,
+                      slInfo.flags,
+                      (slInfo.flags & CKF_REMOVABLE_DEVICE) ? "true": "false",
+                      (slInfo.flags & CKF_TOKEN_PRESENT) ? "true": "false");
+
+                if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) {
+                    slot = PK11_ReferenceSlot(mod_list_item->module->slots[s]);
+                    break;
+                }
+            }
+        }
 
-    for (le = list->head; le; le = le->next) {
-        CK_SLOT_INFO slInfo;
+        /* When e.g. using Yubikeys the slot isn't present until the device is
+         * inserted, so we should wait for a slot as well. */
+        if (p11_ctx->wait_for_card && slot == NULL) {
+            rv = NSS_ShutdownContext(p11_ctx->nss_ctx);
+            if (rv != SECSuccess) {
+                DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d][%s].\n",
+                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+            }
 
-        slInfo.flags = 0;
-        rv = PK11_GetSlotInfo(le->slot, &slInfo);
-        DEBUG(SSSDBG_TRACE_ALL,
-              "Description [%s] Manufacturer [%s] flags [%lu].\n",
-              slInfo.slotDescription, slInfo.manufacturerID, slInfo.flags);
-        if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) {
-            slot = PK11_ReferenceSlot(le->slot);
+            sleep(PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME);
+
+            p11_ctx->nss_ctx = get_nss_ctx(p11_ctx->nss_db);
+            if (p11_ctx->nss_ctx == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d][%s].\n",
+                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                return EIO;
+            }
+        } else {
             break;
         }
     }
-    PK11_FreeSlotList(list);
+
     if (slot == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "No removable slots found.\n");
         ret = EIO;
@@ -332,6 +369,22 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     module = PK11_GetModule(slot);
     module_name = module->dllName == NULL ? "NSS-Internal" : module->dllName;
 
+    if (!(slInfo.flags & CKF_TOKEN_PRESENT)) {
+        DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n");
+        if (p11_ctx->wait_for_card) {
+            token_status = PK11_WaitForTokenEvent(slot, PK11TokenPresentEvent,
+                                                 PR_INTERVAL_NO_TIMEOUT, 0, 0);
+            if (token_status != PK11TokenPresent) {
+                DEBUG(SSSDBG_OP_FAILURE, "PK11_WaitForTokenEvent failed.\n");
+                ret = EIO;
+                goto done;
+            }
+        } else {
+            ret = EIO;
+            goto done;
+        }
+    }
+
     DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d][%s].\n",
           token_name, slot_name, (int) slot_id, (int) module_id, module_name);
 
@@ -651,26 +704,18 @@ static int talloc_nss_shutdown(struct p11_ctx *p11_ctx)
 }
 
 errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *nss_db,
-                     struct p11_ctx **p11_ctx)
+                     bool wait_for_card, struct p11_ctx **p11_ctx)
 {
     struct p11_ctx *ctx;
-    uint32_t flags = NSS_INIT_READONLY
-                                   | NSS_INIT_FORCEOPEN
-                                   | NSS_INIT_NOROOTINIT
-                                   | NSS_INIT_OPTIMIZESPACE
-                                   | NSS_INIT_PK11RELOAD;
-    NSSInitParameters parameters = { 0 };
-    parameters.length =  sizeof (parameters);
-
     ctx = talloc_zero(mem_ctx, struct p11_ctx);
     if (ctx == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
         return ENOMEM;
     }
     ctx->nss_db = nss_db;
+    ctx->wait_for_card = wait_for_card;
 
-    ctx->nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters,
-                                    flags);
+    ctx->nss_ctx = get_nss_ctx(nss_db);
     if (ctx->nss_ctx == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d][%s].\n",
               PR_GetError(), PORT_ErrorToString(PR_GetError()));
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index bf4418f86..d4572d99c 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -40,6 +40,7 @@
 struct p11_ctx {
     X509_STORE *x509_store;
     const char *ca_db;
+    bool wait_for_card;
 };
 
 static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx)
@@ -48,8 +49,9 @@ static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx)
 
     return 0;
 }
+
 errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *ca_db,
-                     struct p11_ctx **p11_ctx)
+                     bool wait_for_card, struct p11_ctx **p11_ctx)
 {
     int ret;
     struct p11_ctx *ctx;
@@ -73,6 +75,7 @@ errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *ca_db,
     }
 
     ctx->ca_db = ca_db;
+    ctx->wait_for_card = wait_for_card;
     talloc_set_destructor(ctx, talloc_cleanup_openssl);
 
     *p11_ctx = ctx;
@@ -547,6 +550,45 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
     return ret;
 }
 
+static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id)
+{
+    CK_FLAGS wait_flags = 0;
+    CK_RV rv;
+    CK_SLOT_INFO info;
+
+    rv = module->C_WaitForSlotEvent(wait_flags, slot_id, NULL);
+    if (rv != CKR_OK) {
+        if (rv != CKR_FUNCTION_NOT_SUPPORTED) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "C_WaitForSlotEvent failed [%lu][%s].\n",
+                  rv, p11_kit_strerror(rv));
+            return EIO;
+        }
+
+        /* Poor man's wait */
+        do {
+            sleep(10);
+            rv = module->C_GetSlotInfo(*slot_id, &info);
+            if (rv != CKR_OK) {
+                DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n");
+                return EIO;
+            }
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "Description [%s] Manufacturer [%s] flags [%lu] "
+                  "removable [%s] token present [%s].\n",
+                  info.slotDescription, info.manufacturerID, info.flags,
+                  (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false",
+                  (info.flags & CKF_TOKEN_PRESENT) ? "true": "false");
+            if ((info.flags & CKF_REMOVABLE_DEVICE)
+                    && (info.flags & CKF_TOKEN_PRESENT)) {
+                break;
+            }
+        } while (true);
+    }
+
+    return EOK;
+}
+
 #define MAX_SLOTS 64
 
 errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
@@ -588,39 +630,62 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
         return EIO;
     }
 
-    DEBUG(SSSDBG_TRACE_ALL, "Module List:\n");
-    for (c = 0; modules[c] != NULL; c++) {
-        mod_name = p11_kit_module_get_name(modules[c]);
-        mod_file_name = p11_kit_module_get_filename(modules[c]);
-        DEBUG(SSSDBG_TRACE_ALL, "common name: [%s].\n", mod_name);
-        DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name);
-        free(mod_name);
-        free(mod_file_name);
+    for (;;) {
+        DEBUG(SSSDBG_TRACE_ALL, "Module List:\n");
+        for (c = 0; modules[c] != NULL; c++) {
+            mod_name = p11_kit_module_get_name(modules[c]);
+            mod_file_name = p11_kit_module_get_filename(modules[c]);
+            DEBUG(SSSDBG_TRACE_ALL, "common name: [%s].\n", mod_name);
+            DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name);
+            free(mod_name);
+            free(mod_file_name);
 
-        num_slots = MAX_SLOTS;
-        rv = modules[c]->C_GetSlotList(CK_TRUE, slots, &num_slots);
-        if (rv != CKR_OK) {
-            DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed.\n");
-            ret = EIO;
-            goto done;
-        }
-
-        for (s = 0; s < num_slots; s++) {
-            rv = modules[c]->C_GetSlotInfo(slots[s], &info);
+            num_slots = MAX_SLOTS;
+            rv = modules[c]->C_GetSlotList(CK_FALSE, slots, &num_slots);
             if (rv != CKR_OK) {
-                DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n");
+                DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed.\n");
                 ret = EIO;
                 goto done;
             }
-            DEBUG(SSSDBG_TRACE_ALL,
-                  "Description [%s] Manufacturer [%s] flags [%lu] removable [%s].\n",
-                  info.slotDescription, info.manufacturerID, info.flags,
-                  (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false");
-            if ((info.flags & CKF_REMOVABLE_DEVICE)) {
+
+            for (s = 0; s < num_slots; s++) {
+                rv = modules[c]->C_GetSlotInfo(slots[s], &info);
+                if (rv != CKR_OK) {
+                    DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n");
+                    ret = EIO;
+                    goto done;
+                }
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "Description [%s] Manufacturer [%s] flags [%lu] "
+                      "removable [%s] token present [%s].\n",
+                      info.slotDescription, info.manufacturerID, info.flags,
+                      (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false",
+                      (info.flags & CKF_TOKEN_PRESENT) ? "true": "false");
+                if ((info.flags & CKF_REMOVABLE_DEVICE)) {
+                    break;
+                }
+            }
+            if (s != num_slots) {
                 break;
             }
         }
-        if (s != num_slots) {
+
+        /* When e.g. using Yubikeys the slot isn't present until the device is
+         * inserted, so we should wait for a slot as well. */
+        if (p11_ctx->wait_for_card && modules[c] == NULL) {
+            p11_kit_modules_finalize_and_release(modules);
+
+            sleep(PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME);
+
+            modules = p11_kit_modules_load_and_initialize(0);
+            if (modules == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "p11_kit_modules_load_and_initialize failed.\n");
+                ret = EIO;
+                goto done;
+            }
+
+        } else {
             break;
         }
     }
@@ -631,14 +696,29 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
         goto done;
     }
 
-    rv = modules[c]->C_GetTokenInfo(slots[s], &token_info);
+    slot_id = slots[s];
+
+    if (!(info.flags & CKF_TOKEN_PRESENT)) {
+        DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n");
+        if (p11_ctx->wait_for_card) {
+            ret = wait_for_card(modules[c], &slot_id);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "wait_for_card failed.\n");
+                goto done;
+            }
+        } else {
+            ret = EIO;
+            goto done;
+        }
+    }
+
+    rv = modules[c]->C_GetTokenInfo(slot_id, &token_info);
     if (rv != CKR_OK) {
         DEBUG(SSSDBG_OP_FAILURE, "C_GetTokenInfo failed.\n");
         ret = EIO;
         goto done;
     }
 
-    slot_id = slots[s];
     module_id = c;
     slot_name = p11_kit_space_strdup(info.slotDescription,
                                      sizeof(info.slotDescription));

From 7ad34cfcfb7a1f80cfd37dc7e137eb7b2911a246 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Sep 2018 18:15:02 +0200
Subject: [PATCH 02/10] PAM: add p11_wait_for_card_timeout option

If the --wait_for_card is used to call p11_child the PAM responder
should be prepared to wait longer until p11_child can return
successfully.

Related to https://pagure.io/SSSD/sssd/issue/3650
---
 src/confdb/confdb.h                  |  1 +
 src/config/SSSDConfig/__init__.py.in |  1 +
 src/config/cfg_rules.ini             |  1 +
 src/config/etc/sssd.api.conf         |  1 +
 src/man/sssd.conf.5.xml              | 14 ++++++++++++++
 src/responder/pam/pamsrv_cmd.c       | 15 +++++++++++++++
 src/util/util.h                      |  1 +
 7 files changed, 34 insertions(+)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 625d15626..87904c214 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -130,6 +130,7 @@
 #define CONFDB_PAM_CERT_AUTH "pam_cert_auth"
 #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path"
 #define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout"
+#define CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT "p11_wait_for_card_timeout"
 #define CONFDB_PAM_APP_SERVICES "pam_app_services"
 #define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services"
 
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 81a03adfe..4d1dba2d2 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -104,6 +104,7 @@ option_strings = {
     'p11_child_timeout' : _('How many seconds will pam_sss wait for p11_child to finish'),
     'pam_app_services' : _('Which PAM services are permitted to contact application domains'),
     'pam_p11_allowed_services' : _('Allowed services for using smartcards'),
+    'p11_wait_for_card_timeout' : _('Additional timeout to wait for a card if requested'),
 
     # [sudo]
     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 78f215ef9..50a8f1dfc 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -127,6 +127,7 @@ option = pam_cert_db_path
 option = p11_child_timeout
 option = pam_app_services
 option = pam_p11_allowed_services
+option = p11_wait_for_card_timeout
 
 [rule/allowed_sudo_options]
 validator = ini_allowed_options
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 52494c0e6..bb686c344 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -76,6 +76,7 @@ pam_cert_db_path = str, None, false
 p11_child_timeout = int, None, false
 pam_app_services = str, None, false
 pam_p11_allowed_services = str, None, false
+p11_wait_for_card_timeout = int, None, false
 
 [sudo]
 # sudo service
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index c1e38950f..4df016331 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -1464,6 +1464,20 @@ pam_p11_allowed_services = +my_pam_service, -login
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>p11_wait_for_card_timeout (integer)</term>
+                    <listitem>
+                        <para>
+                            If Smartcard authentication is required how many
+                            extra seconds in addition to p11_child_timeout
+                            should the PAM responder wait until a Smartcard is
+                            inserted.
+                        </para>
+                        <para>
+                            Default: 60
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
 
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 817f3c513..c8df32de9 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1297,6 +1297,7 @@ static errno_t check_cert(TALLOC_CTX *mctx,
                           struct pam_data *pd)
 {
     int p11_child_timeout;
+    int wait_for_card_timeout;
     char *cert_verification_opts;
     errno_t ret;
     struct tevent_req *req;
@@ -1311,6 +1312,20 @@ static errno_t check_cert(TALLOC_CTX *mctx,
               ret, sss_strerror(ret));
         return ret;
     }
+    if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
+        ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+                             CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT,
+                             P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT,
+                             &wait_for_card_timeout);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Failed to read wait_for_card_timeout from confdb: [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            return ret;
+        }
+
+        p11_child_timeout += wait_for_card_timeout;
+    }
 
     ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_MONITOR_CONF_ENTRY,
                             CONFDB_MONITOR_CERT_VERIFICATION, NULL,
diff --git a/src/util/util.h b/src/util/util.h
index 59e7a96ba..e3e910097 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -724,6 +724,7 @@ errno_t create_preauth_indicator(void);
 #define P11_CHILD_LOG_FILE "p11_child"
 #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
 #define P11_CHILD_TIMEOUT_DEFAULT 10
+#define P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT 60
 #endif  /* SSSD_LIBEXEC_PATH */
 
 #endif /* __SSSD_UTIL_H__ */

From 9d0df8607900506e903d525a36a3fe14b81cb9f4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Sep 2018 10:11:02 +0200
Subject: [PATCH 03/10] pam_sss: make flags public

To allow the PAM responder to act on the config flags set for pam_sss
the flags have to be made public first.

Related to https://pagure.io/SSSD/sssd/issue/3650
---
 src/sss_client/pam_sss.c | 71 ++++++++++++++++++----------------------
 src/sss_client/sss_cli.h |  9 +++++
 2 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 59081cc67..b336d1f61 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -52,15 +52,6 @@
 #include <libintl.h>
 #define _(STRING) dgettext (PACKAGE, STRING)
 
-#define FLAGS_USE_FIRST_PASS (1 << 0)
-#define FLAGS_FORWARD_PASS   (1 << 1)
-#define FLAGS_USE_AUTHTOK    (1 << 2)
-#define FLAGS_IGNORE_UNKNOWN_USER (1 << 3)
-#define FLAGS_IGNORE_AUTHINFO_UNAVAIL (1 << 4)
-#define FLAGS_USE_2FA (1 << 5)
-#define FLAGS_ALLOW_MISSING_NAME (1 << 6)
-#define FLAGS_PROMPT_ALWAYS (1 << 7)
-
 #define PWEXP_FLAG "pam_sss:password_expired_flag"
 #define FD_DESTRUCTOR "pam_sss:fd_destructor"
 #define PAM_SSS_AUTHOK_TYPE "pam_sss:authtok_type"
@@ -1193,13 +1184,13 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
     pi->pam_service_size=strlen(pi->pam_service)+1;
 
     ret = pam_get_item(pamh, PAM_USER, (const void **) &(pi->pam_user));
-    if (ret == PAM_PERM_DENIED && (flags & FLAGS_ALLOW_MISSING_NAME)) {
+    if (ret == PAM_PERM_DENIED && (flags & PAM_CLI_FLAGS_ALLOW_MISSING_NAME)) {
         pi->pam_user = "";
         ret = PAM_SUCCESS;
     }
     if (ret != PAM_SUCCESS) return ret;
     if (pi->pam_user == NULL) {
-        if (flags & FLAGS_ALLOW_MISSING_NAME) {
+        if (flags & PAM_CLI_FLAGS_ALLOW_MISSING_NAME) {
             pi->pam_user = "";
         } else {
             D(("No user found, aborting."));
@@ -1959,11 +1950,11 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
 
     for (; argc-- > 0; ++argv) {
         if (strcmp(*argv, "forward_pass") == 0) {
-            *flags |= FLAGS_FORWARD_PASS;
+            *flags |= PAM_CLI_FLAGS_FORWARD_PASS;
         } else if (strcmp(*argv, "use_first_pass") == 0) {
-            *flags |= FLAGS_USE_FIRST_PASS;
+            *flags |= PAM_CLI_FLAGS_USE_FIRST_PASS;
         } else if (strcmp(*argv, "use_authtok") == 0) {
-            *flags |= FLAGS_USE_AUTHTOK;
+            *flags |= PAM_CLI_FLAGS_USE_AUTHTOK;
         } else if (strncmp(*argv, OPT_DOMAINS_KEY, strlen(OPT_DOMAINS_KEY)) == 0) {
             if (*(*argv+strlen(OPT_DOMAINS_KEY)) == '\0') {
                 logger(pamh, LOG_ERR, "Missing argument to option domains.");
@@ -1997,15 +1988,15 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
         } else if (strcmp(*argv, "quiet") == 0) {
             *quiet_mode = true;
         } else if (strcmp(*argv, "ignore_unknown_user") == 0) {
-            *flags |= FLAGS_IGNORE_UNKNOWN_USER;
+            *flags |= PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER;
         } else if (strcmp(*argv, "ignore_authinfo_unavail") == 0) {
-            *flags |= FLAGS_IGNORE_AUTHINFO_UNAVAIL;
+            *flags |= PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL;
         } else if (strcmp(*argv, "use_2fa") == 0) {
-            *flags |= FLAGS_USE_2FA;
+            *flags |= PAM_CLI_FLAGS_USE_2FA;
         } else if (strcmp(*argv, "allow_missing_name") == 0) {
-            *flags |= FLAGS_ALLOW_MISSING_NAME;
+            *flags |= PAM_CLI_FLAGS_ALLOW_MISSING_NAME;
         } else if (strcmp(*argv, "prompt_always") == 0) {
-            *flags |= FLAGS_PROMPT_ALWAYS;
+            *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS;
         } else {
             logger(pamh, LOG_WARNING, "unknown option: %s", *argv);
         }
@@ -2020,10 +2011,10 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
 {
     int ret;
 
-    if ((flags & FLAGS_USE_FIRST_PASS)
+    if ((flags & PAM_CLI_FLAGS_USE_FIRST_PASS)
             || ( pi->pamstack_authtok != NULL
                     && *(pi->pamstack_authtok) != '\0'
-                    && !(flags & FLAGS_PROMPT_ALWAYS))) {
+                    && !(flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))) {
         pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD;
         pi->pam_authtok = strdup(pi->pamstack_authtok);
         if (pi->pam_authtok == NULL) {
@@ -2032,7 +2023,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
         }
         pi->pam_authtok_size = strlen(pi->pam_authtok);
     } else {
-        if (flags & FLAGS_USE_2FA
+        if (flags & PAM_CLI_FLAGS_USE_2FA
                 || (pi->otp_vendor != NULL && pi->otp_token_id != NULL
                         && pi->otp_challenge != NULL)) {
             if (pi->password_prompting) {
@@ -2062,7 +2053,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
             return ret;
         }
 
-        if (flags & FLAGS_FORWARD_PASS) {
+        if (flags & PAM_CLI_FLAGS_FORWARD_PASS) {
             if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_PASSWORD) {
                 ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok);
             } else if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_2FA
@@ -2193,8 +2184,8 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
     /* we query for the old password during PAM_PRELIM_CHECK to make
      * pam_sss work e.g. with pam_cracklib */
     if (pam_flags & PAM_PRELIM_CHECK) {
-        if ( (getuid() != 0 || exp_data ) && !(flags & FLAGS_USE_FIRST_PASS)) {
-            if (flags & FLAGS_USE_2FA
+        if ( (getuid() != 0 || exp_data ) && !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS)) {
+            if (flags & PAM_CLI_FLAGS_USE_2FA
                     || (pi->otp_vendor != NULL && pi->otp_token_id != NULL
                             && pi->otp_challenge != NULL)) {
                 if (pi->password_prompting) {
@@ -2253,7 +2244,7 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
         }
     }
 
-    if (flags & FLAGS_USE_AUTHTOK) {
+    if (flags & PAM_CLI_FLAGS_USE_AUTHTOK) {
         pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD;
         pi->pam_newauthtok =  strdup(pi->pamstack_authtok);
         if (pi->pam_newauthtok == NULL) {
@@ -2268,7 +2259,7 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
             return ret;
         }
 
-        if (flags & FLAGS_FORWARD_PASS) {
+        if (flags & PAM_CLI_FLAGS_FORWARD_PASS) {
             ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_newauthtok);
             if (ret != PAM_SUCCESS) {
                 D(("Failed to set PAM_AUTHTOK [%s], "
@@ -2376,10 +2367,10 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
     ret = get_pam_items(pamh, flags, &pi);
     if (ret != PAM_SUCCESS) {
         D(("get items returned error: %s", pam_strerror(pamh,ret)));
-        if (flags & FLAGS_IGNORE_UNKNOWN_USER && ret == PAM_USER_UNKNOWN) {
+        if (flags & PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER && ret == PAM_USER_UNKNOWN) {
             ret = PAM_IGNORE;
         }
-        if (flags & FLAGS_IGNORE_AUTHINFO_UNAVAIL
+        if (flags & PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL
                 && ret == PAM_AUTHINFO_UNAVAIL) {
             ret = PAM_IGNORE;
         }
@@ -2393,13 +2384,13 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
             case SSS_PAM_AUTHENTICATE:
                 /*
                  * Only do preauth if
-                 * - FLAGS_USE_FIRST_PASS is not set
-                 * - no password is on the stack or FLAGS_PROMPT_ALWAYS is set
+                 * - PAM_CLI_FLAGS_USE_FIRST_PASS is not set
+                 * - no password is on the stack or PAM_CLI_FLAGS_PROMPT_ALWAYS is set
                  * - preauth indicator file exists.
                  */
-                if ( !(flags & FLAGS_USE_FIRST_PASS)
+                if ( !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS)
                         && (pi.pam_authtok == NULL
-                                || (flags & FLAGS_PROMPT_ALWAYS))
+                                || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))
                         && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) {
                     pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH,
                                                   quiet_mode);
@@ -2443,14 +2434,14 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
                  * The means the preauth step has to be done here as well but
                  * only if
                  * - PAM_PRELIM_CHECK is set
-                 * - FLAGS_USE_FIRST_PASS is not set
-                 * - no password is on the stack or FLAGS_PROMPT_ALWAYS is set
+                 * - PAM_CLI_FLAGS_USE_FIRST_PASS is not set
+                 * - no password is on the stack or PAM_CLI_FLAGS_PROMPT_ALWAYS is set
                  * - preauth indicator file exists.
                  */
                 if ( (pam_flags & PAM_PRELIM_CHECK)
-                        && !(flags & FLAGS_USE_FIRST_PASS)
+                        && !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS)
                         && (pi.pam_authtok == NULL
-                                || (flags & FLAGS_PROMPT_ALWAYS))
+                                || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))
                         && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) {
                     pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH,
                                                   quiet_mode);
@@ -2497,11 +2488,11 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
 
         pam_status = send_and_receive(pamh, &pi, task, quiet_mode);
 
-        if (flags & FLAGS_IGNORE_UNKNOWN_USER
+        if (flags & PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER
                 && pam_status == PAM_USER_UNKNOWN) {
             pam_status = PAM_IGNORE;
         }
-        if (flags & FLAGS_IGNORE_AUTHINFO_UNAVAIL
+        if (flags & PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL
                 && pam_status == PAM_AUTHINFO_UNAVAIL) {
             pam_status = PAM_IGNORE;
         }
@@ -2581,7 +2572,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
             retry = true;
             retries--;
 
-            flags &= ~FLAGS_USE_FIRST_PASS;
+            flags &= ~PAM_CLI_FLAGS_USE_FIRST_PASS;
             ret = pam_set_item(pamh, PAM_AUTHTOK, NULL);
             if (ret != PAM_SUCCESS) {
                 D(("Failed to unset PAM_AUTHTOK [%s]",
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 24d28ed4b..3404715d8 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -365,6 +365,15 @@ enum pam_item_type {
     SSS_PAM_ITEM_REQUESTED_DOMAINS,
 };
 
+#define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0)
+#define PAM_CLI_FLAGS_FORWARD_PASS   (1 << 1)
+#define PAM_CLI_FLAGS_USE_AUTHTOK    (1 << 2)
+#define PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER (1 << 3)
+#define PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL (1 << 4)
+#define PAM_CLI_FLAGS_USE_2FA (1 << 5)
+#define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6)
+#define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7)
+
 #define SSS_NSS_MAX_ENTRIES 256
 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)
 struct sss_cli_req_data {

From fcc6ed90b7471fb1c79deb4631c95ee42538cdbf Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 17 Sep 2018 17:54:26 +0200
Subject: [PATCH 04/10] pam_sss: add try_cert_auth option

With this new option pam_sss can be configured to only do Smartcard
authentication or return an error if this is not possible.

Related to https://pagure.io/SSSD/sssd/issue/3650
---
 src/man/pam_sss.8.xml    | 23 +++++++++++++++++++++++
 src/sss_client/pam_sss.c |  9 +++++++++
 src/sss_client/sss_cli.h |  1 +
 3 files changed, 33 insertions(+)

diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml
index d8e6a2041..ca2e8e206 100644
--- a/src/man/pam_sss.8.xml
+++ b/src/man/pam_sss.8.xml
@@ -50,6 +50,9 @@
             <arg choice='opt'>
                 <replaceable>prompt_always</replaceable>
             </arg>
+            <arg choice='opt'>
+                <replaceable>try_cert_auth</replaceable>
+            </arg>
         </cmdsynopsis>
     </refsynopsisdiv>
 
@@ -200,6 +203,26 @@ auth sufficient pam_sss.so allow_missing_name
                     </para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>try_cert_auth</option>
+                </term>
+                <listitem>
+                    <para>
+                        Try to use certificate based authentication, i.e.
+                        authentication with a Smartcard or similar devices. If a
+                        Smartcard is available and the service is allowed for
+                        Smartcard authentication the use will be prompted for a
+                        PIN and the certificate based authentication will
+                        continue
+                    </para>
+                    <para>
+                        If no Smartcard is available or certificate based
+                        authentication is not allowed for the current service
+                        PAM_AUTHINFO_UNAVAIL is returned.
+                    </para>
+                </listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
 
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index b336d1f61..96ff15ada 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -1997,6 +1997,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
             *flags |= PAM_CLI_FLAGS_ALLOW_MISSING_NAME;
         } else if (strcmp(*argv, "prompt_always") == 0) {
             *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS;
+        } else if (strcmp(*argv, "try_cert_auth") == 0) {
+            *flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH;
         } else {
             logger(pamh, LOG_WARNING, "unknown option: %s", *argv);
         }
@@ -2405,6 +2407,13 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
                     }
                 }
 
+                if (flags & PAM_CLI_FLAGS_TRY_CERT_AUTH
+                        && pi.cert_list == NULL) {
+                    D(("No certificates for authentication available."));
+                    overwrite_and_free_pam_items(&pi);
+                    return PAM_AUTHINFO_UNAVAIL;
+                }
+
                 if (strcmp(pi.pam_service, "gdm-smartcard") == 0) {
                     ret = check_login_token_name(pamh, &pi, quiet_mode);
                     if (ret != PAM_SUCCESS) {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 3404715d8..38e3f999d 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -373,6 +373,7 @@ enum pam_item_type {
 #define PAM_CLI_FLAGS_USE_2FA (1 << 5)
 #define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6)
 #define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7)
+#define PAM_CLI_FLAGS_TRY_CERT_AUTH (1 << 8)
 
 #define SSS_NSS_MAX_ENTRIES 256
 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)

From e256d9c3be1114a478a297ac05b5304ccddf39d1 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Sep 2018 09:53:37 +0200
Subject: [PATCH 05/10] pam_sss: add option require_cert_auth

With this new option pam_sss will wait until a Smartcard is available
and then try to authenticate with the help of the Smartcard.

Related https://pagure.io/SSSD/sssd/issue/3650
---
 src/responder/pam/pamsrv_cmd.c | 12 +++++
 src/responder/pam/pamsrv_p11.c |  5 +-
 src/sss_client/pam_message.c   |  4 ++
 src/sss_client/pam_message.h   |  1 +
 src/sss_client/pam_sss.c       | 90 +++++++++++++++++++++-------------
 src/sss_client/sss_cli.h       |  2 +
 src/util/sss_pam_data.c        |  1 +
 src/util/sss_pam_data.h        |  1 +
 8 files changed, 81 insertions(+), 35 deletions(-)

diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index c8df32de9..6e37f8316 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -317,6 +317,11 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
                                              size, body, blen, &c);
                     if (ret != EOK) return ret;
                     break;
+                case SSS_PAM_ITEM_FLAGS:
+                    ret = extract_uint32_t(&pd->cli_flags, size,
+                                           body, blen, &c);
+                    if (ret != EOK) return ret;
+                    break;
                 default:
                     DEBUG(SSSDBG_CRIT_FAILURE,
                           "Ignoring unknown data type [%d].\n", type);
@@ -1447,6 +1452,13 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
                   "No certificate found and no logon name given, " \
                   "authentication not possible.\n");
             ret = ENOENT;
+        } else if (pd->cli_flags & PAM_CLI_FLAGS_TRY_CERT_AUTH) {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "try_cert_auth flag set but no certificate available, "
+                  "request finished.\n");
+            preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+            pam_reply(preq);
+            return;
         } else {
             if (pd->cmd == SSS_PAM_AUTHENTICATE) {
                 DEBUG(SSSDBG_CRIT_FAILURE,
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index ffa6787e9..8b8859d9d 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -721,7 +721,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     struct timeval tv;
     int pipefd_to_child[2] = PIPE_INIT;
     int pipefd_from_child[2] = PIPE_INIT;
-    const char *extra_args[13] = { NULL };
+    const char *extra_args[14] = { NULL };
     uint8_t *write_buf = NULL;
     size_t write_buf_len = 0;
     size_t arg_c;
@@ -748,6 +748,9 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
 
     /* extra_args are added in revers order */
     arg_c = 0;
+    if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
+        extra_args[arg_c++] = "--wait_for_card";
+    }
     extra_args[arg_c++] = nss_db;
     extra_args[arg_c++] = "--nssdb";
     if (verify_opts != NULL) {
diff --git a/src/sss_client/pam_message.c b/src/sss_client/pam_message.c
index b239f6f53..036ae2ad1 100644
--- a/src/sss_client/pam_message.c
+++ b/src/sss_client/pam_message.c
@@ -126,6 +126,7 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
     len += 3*sizeof(uint32_t); /* cli_pid */
     len += *pi->requested_domains != '\0' ?
                 2*sizeof(uint32_t) + pi->requested_domains_size : 0;
+    len += 3*sizeof(uint32_t); /* flags */
 
     buf = malloc(len);
     if (buf == NULL) {
@@ -164,6 +165,9 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
                            pi->pam_newauthtok, pi->pam_newauthtok_size,
                            &buf[rp]);
 
+    rp += add_uint32_t_item(SSS_PAM_ITEM_FLAGS, (uint32_t) pi->flags,
+                            &buf[rp]);
+
     SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp);
 
     if (rp != len) {
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
index 11526a80a..50fedcd82 100644
--- a/src/sss_client/pam_message.h
+++ b/src/sss_client/pam_message.h
@@ -51,6 +51,7 @@ struct pam_items {
     enum sss_authtok_type pam_newauthtok_type;
     size_t pam_newauthtok_size;
     pid_t cli_pid;
+    uint32_t flags;
     const char *login_name;
     char *domain_name;
     const char *requested_domains;
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 96ff15ada..b4c1036ad 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -134,6 +134,7 @@ static void free_cai(struct cert_auth_info *cai)
         free(cai->cert_user);
         free(cai->cert);
         free(cai->token_name);
+        free(cai->module_name);
         free(cai->key_id);
         free(cai->prompt_str);
         free(cai);
@@ -1247,6 +1248,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
     pi->cert_list = NULL;
     pi->selected_cert = NULL;
 
+    pi->flags = flags;
+
     return PAM_SUCCESS;
 }
 
@@ -1267,6 +1270,7 @@ static void print_pam_items(struct pam_items *pi)
     D(("Newauthtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_newauthtok)));
     D(("Cli_PID: %d", pi->cli_pid));
     D(("Requested domains: %s", pi->requested_domains));
+    D(("Flags: %d", pi->flags));
 }
 
 static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi,
@@ -1999,6 +2003,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
             *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS;
         } else if (strcmp(*argv, "try_cert_auth") == 0) {
             *flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH;
+        } else if (strcmp(*argv, "require_cert_auth") == 0) {
+            *flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
         } else {
             logger(pamh, LOG_WARNING, "unknown option: %s", *argv);
         }
@@ -2274,55 +2280,51 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
     return PAM_SUCCESS;
 }
 
-#define SC_ENTER_FMT "Please enter smart card labeled\n %s\nand press enter"
+#define SC_ENTER_LABEL_FMT "Please enter smart card labeled\n %s"
+#define SC_ENTER_FMT "Please enter smart card"
 
 static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
-                                  bool quiet_mode)
+                                  int retries, bool quiet_mode)
 {
     int ret;
     int pam_status;
     char *login_token_name;
     char *prompt = NULL;
-    size_t size;
-    char *answer = NULL;
-    /* TODO: check multiple cert case */
-    struct cert_auth_info *cai = pi->cert_list;
+    uint32_t orig_flags = pi->flags;
 
-    if (cai == NULL) {
-        D(("No certificate information available"));
-        return EINVAL;
+    login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
+    if (login_token_name == NULL
+            && !(pi->flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) {
+        return PAM_SUCCESS;
     }
 
-    login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
     if (login_token_name == NULL) {
-        return PAM_SUCCESS;
+        ret = asprintf(&prompt, SC_ENTER_FMT);
+    } else {
+        ret = asprintf(&prompt, SC_ENTER_LABEL_FMT, login_token_name);
+    }
+    if (ret == -1) {
+        return ENOMEM;
     }
 
-    while (cai->token_name == NULL
-               || strcmp(login_token_name, cai->token_name) != 0) {
-        size = sizeof(SC_ENTER_FMT) + strlen(login_token_name);
-        prompt = malloc(size);
-        if (prompt == NULL) {
-            D(("malloc failed."));
-            return ENOMEM;
-        }
+    pi->flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
 
-        ret = snprintf(prompt, size, SC_ENTER_FMT,
-                       login_token_name);
-        if (ret < 0 || ret >= size) {
-            D(("snprintf failed."));
-            free(prompt);
-            return EFAULT;
+    /* TODO: check multiple cert case */
+    while (pi->cert_list == NULL || pi->cert_list->token_name == NULL
+                || (login_token_name != NULL
+                        && strcmp(login_token_name,
+                                  pi->cert_list->token_name) != 0)) {
+
+        if (retries < 0) {
+            ret = PAM_AUTHINFO_UNAVAIL;
+            goto done;
         }
+        retries--;
 
-        ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt,
-                                  NULL, &answer);
-        free(prompt);
+        ret = do_pam_conversation(pamh, PAM_TEXT_INFO, prompt, NULL, NULL);
         if (ret != PAM_SUCCESS) {
             D(("do_pam_conversation failed."));
-            return ret;
-        } else {
-            free(answer);
+            goto done;
         }
 
         pam_status = send_and_receive(pamh, pi, SSS_PAM_PREAUTH, quiet_mode);
@@ -2335,7 +2337,14 @@ static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
         }
     }
 
-    return PAM_SUCCESS;
+    ret = PAM_SUCCESS;
+
+done:
+
+    pi->flags = orig_flags;
+    free(prompt);
+
+    return ret;
 }
 
 static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
@@ -2394,8 +2403,19 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
                         && (pi.pam_authtok == NULL
                                 || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))
                         && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) {
+
+                    if (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) {
+                        /* Do not use PAM_CLI_FLAGS_REQUIRE_CERT_AUTH in the first
+                         * SSS_PAM_PREAUTH run. In case a card is already inserted
+                         * we do not have to prompt to insert a card. */
+                        pi.flags &= ~PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
+                        pi.flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH;
+                    }
+
                     pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH,
                                                   quiet_mode);
+
+                    pi.flags = flags;
                     if (pam_status != PAM_SUCCESS) {
                         D(("send_and_receive returned [%d] during pre-auth",
                            pam_status));
@@ -2414,8 +2434,10 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
                     return PAM_AUTHINFO_UNAVAIL;
                 }
 
-                if (strcmp(pi.pam_service, "gdm-smartcard") == 0) {
-                    ret = check_login_token_name(pamh, &pi, quiet_mode);
+                if (strcmp(pi.pam_service, "gdm-smartcard") == 0
+                        || (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) {
+                    ret = check_login_token_name(pamh, &pi, retries,
+                                                 quiet_mode);
                     if (ret != PAM_SUCCESS) {
                         D(("check_login_token_name failed.\n"));
                         return ret;
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 38e3f999d..af8a43916 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -363,6 +363,7 @@ enum pam_item_type {
     SSS_PAM_ITEM_CLI_LOCALE,
     SSS_PAM_ITEM_CLI_PID,
     SSS_PAM_ITEM_REQUESTED_DOMAINS,
+    SSS_PAM_ITEM_FLAGS,
 };
 
 #define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0)
@@ -374,6 +375,7 @@ enum pam_item_type {
 #define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6)
 #define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7)
 #define PAM_CLI_FLAGS_TRY_CERT_AUTH (1 << 8)
+#define PAM_CLI_FLAGS_REQUIRE_CERT_AUTH (1 << 9)
 
 #define SSS_NSS_MAX_ENTRIES 256
 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)
diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c
index 5e41349b9..cb8779c1d 100644
--- a/src/util/sss_pam_data.c
+++ b/src/util/sss_pam_data.c
@@ -176,6 +176,7 @@ void pam_print_data(int l, struct pam_data *pd)
     DEBUG(l, "priv: %d\n", pd->priv);
     DEBUG(l, "cli_pid: %d\n", pd->cli_pid);
     DEBUG(l, "logon name: %s\n", PAM_SAFE_ITEM(pd->logon_name));
+    DEBUG(l, "flags: %d\n", pd->cli_flags);
 }
 
 int pam_add_response(struct pam_data *pd, enum response_type type,
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
index 7d74fa6a0..c98981054 100644
--- a/src/util/sss_pam_data.h
+++ b/src/util/sss_pam_data.h
@@ -58,6 +58,7 @@ struct pam_data {
     struct sss_auth_token *newauthtok;
     uint32_t cli_pid;
     char *logon_name;
+    uint32_t cli_flags;
 
     int pam_status;
     int response_delay;

From ccbada8aa9bac020c2767c0f5ce9735ca7f19bc5 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 26 Sep 2018 11:48:37 +0200
Subject: [PATCH 06/10] intg: require SC tests

Integration test for the new try_cert_auth and require_cert_auth option
for pam_sss.

Related to https://pagure.io/SSSD/sssd/issue/3650
---
 src/tests/intg/Makefile.am           |  16 ++-
 src/tests/intg/test_pam_responder.py | 188 ++++++++++++++++++++++++---
 2 files changed, 182 insertions(+), 22 deletions(-)

diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index bb3a7f01a..44fb6353a 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -113,6 +113,20 @@ pam_sss_service:
 	echo "password required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
 	echo "session  required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
 
+pam_sss_sc_required:
+	$(MKDIR_P) $(PAM_SERVICE_DIR)
+	echo "auth     required       $(DESTDIR)$(pammoddir)/pam_sss.so require_cert_auth retry=1"  > $(PAM_SERVICE_DIR)/$@
+	echo "account  required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+	echo "password required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+	echo "session  required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+
+pam_sss_try_sc:
+	$(MKDIR_P) $(PAM_SERVICE_DIR)
+	echo "auth     required       $(DESTDIR)$(pammoddir)/pam_sss.so try_cert_auth"  > $(PAM_SERVICE_DIR)/$@
+	echo "account  required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+	echo "password required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+	echo "session  required       $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@
+
 CLEANFILES=config.py config.pyc passwd group
 
 clean-local:
@@ -127,7 +141,7 @@ PAM_CERT_DB_PATH="$(abs_builddir)/../test_CA/SSSD_test_CA.pem"
 SOFTHSM2_CONF="$(abs_builddir)/../test_CA/softhsm2_one.conf"
 endif
 
-intgcheck-installed: config.py passwd group pam_sss_service
+intgcheck-installed: config.py passwd group pam_sss_service pam_sss_sc_required pam_sss_try_sc
 	pipepath="$(DESTDIR)$(pipepath)"; \
 	if test $${#pipepath} -gt 80; then \
 	    echo "error: Pipe directory path too long," \
diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py
index c6d048cd3..06f69a3d8 100644
--- a/src/tests/intg/test_pam_responder.py
+++ b/src/tests/intg/test_pam_responder.py
@@ -41,6 +41,11 @@
              dir='/home/user1',
              shell='/bin/bash')
 
+USER2 = dict(name='user2', passwd='x', uid=10002, gid=20002,
+             gecos='User with no Smartcard mapping',
+             dir='/home/user2',
+             shell='/bin/bash')
+
 
 def format_pam_cert_auth_conf(config):
     """Format a basic SSSD configuration"""
@@ -55,8 +60,11 @@ def format_pam_cert_auth_conf(config):
 
         [pam]
         pam_cert_auth = True
-        pam_p11_allowed_services = +pam_sss_service
+        pam_p11_allowed_services = +pam_sss_service, +pam_sss_sc_required, \
+                                   +pam_sss_try_sc
         pam_cert_db_path = {config.PAM_CERT_DB_PATH}
+        p11_child_timeout = 5
+        p11_wait_for_card_timeout = 5
         debug_level = 10
 
         [domain/auth_only]
@@ -149,6 +157,15 @@ def create_nssdb():
     pkcs11_txt.close()
 
 
+def create_nssdb_no_cert():
+    os.mkdir(config.SYSCONFDIR + "/pki")
+    os.mkdir(config.SYSCONFDIR + "/pki/nssdb")
+    if subprocess.call(["certutil", "-N", "-d",
+                        "sql:" + config.SYSCONFDIR + "/pki/nssdb/",
+                        "--empty-password"]) != 0:
+        raise Exception("certutil failed")
+
+
 def cleanup_nssdb():
     shutil.rmtree(config.SYSCONFDIR + "/pki")
 
@@ -158,14 +175,42 @@ def create_nssdb_fixture(request):
     request.addfinalizer(cleanup_nssdb)
 
 
+def create_nssdb_no_cert_fixture(request):
+    create_nssdb_no_cert()
+    request.addfinalizer(cleanup_nssdb)
+
+
 @pytest.fixture
-def simple_pam_cert_auth(request):
+def simple_pam_cert_auth(request, passwd_ops_setup):
     """Setup SSSD with pam_cert_auth=True"""
     config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH']
     conf = format_pam_cert_auth_conf(config)
     create_conf_fixture(request, conf)
     create_sssd_fixture(request)
     create_nssdb_fixture(request)
+    passwd_ops_setup.useradd(**USER1)
+    passwd_ops_setup.useradd(**USER2)
+    return None
+
+
+@pytest.fixture
+def simple_pam_cert_auth_no_cert(request, passwd_ops_setup):
+    """Setup SSSD with pam_cert_auth=True"""
+    config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH']
+
+    old_softhsm2_conf = os.environ['SOFTHSM2_CONF']
+    del os.environ['SOFTHSM2_CONF']
+
+    conf = format_pam_cert_auth_conf(config)
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    create_nssdb_no_cert_fixture(request)
+
+    os.environ['SOFTHSM2_CONF'] = old_softhsm2_conf
+
+    passwd_ops_setup.useradd(**USER1)
+    passwd_ops_setup.useradd(**USER2)
+
     return None
 
 
@@ -176,26 +221,26 @@ def test_preauth_indicator(simple_pam_cert_auth):
 
 
 @pytest.fixture
-def pam_wrapper_setup(request):
+def env_for_sssctl(request):
     pwrap_runtimedir = os.getenv("PAM_WRAPPER_SERVICE_DIR")
     if pwrap_runtimedir is None:
         raise ValueError("The PAM_WRAPPER_SERVICE_DIR variable is unset\n")
 
+    env_for_sssctl = os.environ.copy()
+    env_for_sssctl['PAM_WRAPPER'] = "1"
+    env_for_sssctl['SSSD_INTG_PEER_UID'] = "0"
+    env_for_sssctl['SSSD_INTG_PEER_GID'] = "0"
+    env_for_sssctl['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH']
 
-def test_sc_auth_wrong_pin(simple_pam_cert_auth, pam_wrapper_setup,
-                           passwd_ops_setup):
+    return env_for_sssctl
 
-    passwd_ops_setup.useradd(**USER1)
-    current_env = os.environ.copy()
-    current_env['PAM_WRAPPER'] = "1"
-    current_env['SSSD_INTG_PEER_UID'] = "0"
-    current_env['SSSD_INTG_PEER_GID'] = "0"
-    current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH']
+
+def test_sc_auth_wrong_pin(simple_pam_cert_auth, env_for_sssctl):
 
     sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
                                "--action=auth", "--service=pam_sss_service"],
                               universal_newlines=True,
-                              env=current_env, stdin=subprocess.PIPE,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
     try:
@@ -214,19 +259,120 @@ def test_sc_auth_wrong_pin(simple_pam_cert_auth, pam_wrapper_setup,
                     "Authentication failure") != -1
 
 
-def test_sc_auth(simple_pam_cert_auth, pam_wrapper_setup, passwd_ops_setup):
-
-    passwd_ops_setup.useradd(**USER1)
-    current_env = os.environ.copy()
-    current_env['PAM_WRAPPER'] = "1"
-    current_env['SSSD_INTG_PEER_UID'] = "0"
-    current_env['SSSD_INTG_PEER_GID'] = "0"
-    current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH']
+def test_sc_auth(simple_pam_cert_auth, env_for_sssctl):
 
     sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
                                "--action=auth", "--service=pam_sss_service"],
                               universal_newlines=True,
-                              env=current_env, stdin=subprocess.PIPE,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
+                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+    try:
+        out, err = sssctl.communicate(input="123456")
+    except:
+        sssctl.kill()
+        out, err = sssctl.communicate()
+
+    sssctl.stdin.close()
+    sssctl.stdout.close()
+
+    if sssctl.wait() != 0:
+        raise Exception("sssctl failed")
+
+    assert err.find("pam_authenticate for user [user1]: Success") != -1
+
+
+def test_require_sc_auth(simple_pam_cert_auth, env_for_sssctl):
+
+    sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
+                               "--action=auth",
+                               "--service=pam_sss_sc_required"],
+                              universal_newlines=True,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
+                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+    try:
+        out, err = sssctl.communicate(input="123456")
+    except:
+        sssctl.kill()
+        out, err = sssctl.communicate()
+
+    sssctl.stdin.close()
+    sssctl.stdout.close()
+
+    if sssctl.wait() != 0:
+        raise Exception("sssctl failed")
+
+    assert err.find("pam_authenticate for user [user1]: Success") != -1
+
+
+def test_require_sc_auth_no_cert(simple_pam_cert_auth_no_cert, env_for_sssctl):
+
+    # We have to wait about 20s before the command returns because there will
+    # be 2 run since retry=1 in the PAM configuration and both
+    # p11_child_timeout and p11_wait_for_card_timeout are 5s in sssd.conf,
+    # so 2*(5+5)=20. */
+    start_time = time.time()
+    sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
+                               "--action=auth",
+                               "--service=pam_sss_sc_required"],
+                              universal_newlines=True,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
+                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+    try:
+        out, err = sssctl.communicate(input="123456")
+    except:
+        sssctl.kill()
+        out, err = sssctl.communicate()
+
+    sssctl.stdin.close()
+    sssctl.stdout.close()
+
+    if sssctl.wait() != 0:
+        raise Exception("sssctl failed")
+
+    end_time = time.time()
+    assert end_time > start_time and \
+        (end_time - start_time) >= 20 and \
+        (end_time - start_time) < 40
+    assert out.find("Please enter smart card\nPlease enter smart card") != -1
+    assert err.find("pam_authenticate for user [user1]: Authentication " +
+                    "service cannot retrieve authentication info") != -1
+
+
+def test_try_sc_auth_no_map(simple_pam_cert_auth, env_for_sssctl):
+
+    sssctl = subprocess.Popen(["sssctl", "user-checks", "user2",
+                               "--action=auth",
+                               "--service=pam_sss_try_sc"],
+                              universal_newlines=True,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
+                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+    try:
+        out, err = sssctl.communicate(input="123456")
+    except:
+        sssctl.kill()
+        out, err = sssctl.communicate()
+
+    sssctl.stdin.close()
+    sssctl.stdout.close()
+
+    if sssctl.wait() != 0:
+        raise Exception("sssctl failed")
+
+    assert err.find("pam_authenticate for user [user2]: Authentication " +
+                    "service cannot retrieve authentication info") != -1
+
+
+def test_try_sc_auth(simple_pam_cert_auth, env_for_sssctl):
+
+    sssctl = subprocess.Popen(["sssctl", "user-checks", "user1",
+                               "--action=auth",
+                               "--service=pam_sss_try_sc"],
+                              universal_newlines=True,
+                              env=env_for_sssctl, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
     try:

From fe93f552bd457140788c7ab8136be0d82e201627 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 8 Oct 2018 10:45:28 +0200
Subject: [PATCH 07/10] p11_child: show PKCS#11 URI in debug output

The patch only adds debug messages where the PKCS#11 URI of the selected
certificates is shown. The output should help to create suitable URIs to
restrict the selection.

Related to https://pagure.io/SSSD/sssd/issue/3814
---
 src/p11_child/p11_child_nss.c     | 240 ++++++++++++++++++++++++++++++
 src/p11_child/p11_child_openssl.c |  80 ++++++++++
 2 files changed, 320 insertions(+)

diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index b2777d1d2..fff1f2525 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -39,6 +39,7 @@
 #include <pk11pub.h>
 #include <prerror.h>
 #include <ocsp.h>
+#include <pkcs11uri.h>
 
 #include "util/child_common.h"
 #include "providers/backend.h"
@@ -63,6 +64,239 @@ struct p11_ctx {
                     | certificateUsageStatusResponder \
                     | certificateUsageSSLCA )
 
+
+static char *get_pkcs11_string(TALLOC_CTX *mem_ctx, const char *in, size_t len)
+{
+    size_t c = len;
+
+    if (in == NULL || len == 0) {
+        return NULL;
+    }
+
+    while(c > 0 && in[c - 1] == ' ') {
+        c--;
+    }
+
+    return talloc_strndup(mem_ctx, in, c);
+}
+
+static char *pct_encode(TALLOC_CTX *mem_ctx, SECItem *data)
+{
+    char *pct;
+    size_t c;
+    int ret;
+
+    pct = talloc_zero_size(mem_ctx, sizeof(char) * (3*data->len + 1));
+    if (pct == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
+        return NULL;
+    }
+
+    for (c = 0; c < data->len; c++) {
+        ret = snprintf(pct + 3*c, 4, "%%%02X", data->data[c]);
+        if (ret != 3) {
+            DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
+            talloc_free(pct);
+            return NULL;
+        }
+    }
+
+    return pct;
+}
+
+static char *get_key_id_pct(TALLOC_CTX *mem_ctx, PK11SlotInfo *slot,
+                            CERTCertificate *cert)
+{
+    SECItem *key_id = NULL;
+    char *key_id_str = NULL;
+
+    key_id = PK11_GetLowLevelKeyIDForCert(slot, cert, NULL);
+    if (key_id == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "PK11_GetLowLevelKeyIDForCert failed [%d][%s].\n",
+              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+        return NULL;
+    }
+
+    key_id_str = pct_encode(mem_ctx, key_id);
+    SECITEM_FreeItem(key_id, PR_TRUE);
+    if (key_id_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "pct_encode failed.\n");
+        return NULL;
+    }
+
+    return key_id_str;
+}
+
+static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, SECMODModule *mod,
+                            PK11SlotInfo *slot,
+                            const char *label, CERTCertificate *cert)
+{
+    CK_INFO module_info;
+    CK_SLOT_INFO slot_info;
+    CK_TOKEN_INFO token_info;
+    char *values[13];
+    PK11URIAttribute attrs[13];
+    size_t nattrs = 0;
+    SECStatus rv;
+    char *tmp_str;
+    char *uri_str;
+    PK11URI *uri;
+    CK_SLOT_ID slot_id;
+    char *id_pct;
+
+    rv = PK11_GetModInfo(mod, &module_info);
+    if (rv != SECSuccess) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetModInfo failed.\n");
+        return NULL;
+    }
+
+    rv = PK11_GetSlotInfo(slot, &slot_info);
+    if (rv != SECSuccess) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetSlotInfo failed.\n");
+        return NULL;
+    }
+
+    rv = PK11_GetTokenInfo(slot, &token_info);
+    if (rv != SECSuccess) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetTokenInfo failed.\n");
+        return NULL;
+    }
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)module_info.libraryDescription,
+                                       sizeof(module_info.libraryDescription));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_LIBRARY_DESCRIPTION;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)module_info.manufacturerID,
+                                       sizeof(module_info.manufacturerID));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_LIBRARY_MANUFACTURER;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = talloc_asprintf(mem_ctx, "%d.%d",
+                                     module_info.libraryVersion.major,
+                                     module_info.libraryVersion.minor);
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_LIBRARY_VERSION;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)slot_info.slotDescription,
+                                       sizeof(slot_info.slotDescription));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_SLOT_DESCRIPTION;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)slot_info.manufacturerID,
+                                       sizeof(slot_info.manufacturerID));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_SLOT_MANUFACTURER;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    slot_id = PK11_GetSlotID(slot);
+    values[nattrs] = talloc_asprintf(mem_ctx, "%d", (int) slot_id);
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_SLOT_ID;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx, (char *)token_info.model,
+                                       sizeof(token_info.model));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_MODEL;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)token_info.manufacturerID,
+                                       sizeof(token_info.manufacturerID));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_MANUFACTURER;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx,
+                                       (char *)token_info.serialNumber,
+                                       sizeof(token_info.serialNumber));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_SERIAL;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    values[nattrs] = get_pkcs11_string(mem_ctx, (char *)token_info.label,
+                                       sizeof(token_info.label));
+    if (values[nattrs] != NULL && *values[nattrs] != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_TOKEN;
+        attrs[nattrs].value = values[nattrs];
+        nattrs++;
+    }
+
+    if (label != NULL && *label != '\0') {
+        attrs[nattrs].name = PK11URI_PATTR_OBJECT;
+        attrs[nattrs].value = label;
+        nattrs++;
+    }
+
+    attrs[nattrs].name = PK11URI_PATTR_TYPE;
+    attrs[nattrs].value = "cert";
+    nattrs++;
+
+    uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0);
+    if (uri == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11URI_CreateURI failed.\n");
+        return NULL;
+    }
+
+    tmp_str = PK11URI_FormatURI(NULL, uri);
+    PK11URI_DestroyURI(uri);
+    if (tmp_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11URI_FormatURI failed.\n");
+        return NULL;
+    }
+
+    /* Currently I have no idea how to get the ID properly formatted with the
+     * NSS  PK11 calls. Since all attribute values are treated as strings zeros
+     * in the IDs cannot be handled. And the IDs cannot be set percent-encoded
+     * since all attribute values will be escaped which means the '%' sign
+     * will be escaped to '%25'. Hence for the time being the ID is added
+     * manually to the end of the URI. */
+    id_pct = get_key_id_pct(mem_ctx, slot, cert);
+    if (id_pct == NULL || *id_pct == '\0') {
+        DEBUG(SSSDBG_OP_FAILURE, "get_key_id_pct failed.\n");
+        PORT_Free(tmp_str);
+        return NULL;
+    }
+
+    uri_str = talloc_asprintf(mem_ctx, "%s;%s=%s", tmp_str,
+                                                   PK11URI_PATTR_ID, id_pct);
+    talloc_free(id_pct);
+    if (uri_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+        return NULL;
+    }
+
+    return uri_str;
+
+}
+
 static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg)
 {
   /* give up if 1) no password was supplied, or 2) the password has already
@@ -465,6 +699,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
               cert_list_node->cert->nickname,
               cert_list_node->cert->subjectName);
 
+        DEBUG(SSSDBG_TRACE_ALL, "module uri: %s.\n", PK11_GetModuleURI(module));
+        DEBUG(SSSDBG_TRACE_ALL, "token uri: %s.\n", PK11_GetTokenURI(slot));
+
         if (p11_ctx->handle != NULL) {
             if (!do_verification(p11_ctx, cert_list_node->cert)) {
                 DEBUG(SSSDBG_OP_FAILURE,
@@ -651,6 +888,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
 
         DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
               key_id_str);
+        DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", get_pkcs11_uri(mem_ctx, module,
+                                                             slot, label,
+                                                             found_cert));
 
         multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n%s\n",
                                        token_name, module_name, key_id_str,
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index d4572d99c..09edeefbd 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -29,6 +29,7 @@
 #include <openssl/err.h>
 #include <openssl/rand.h>
 #include <p11-kit/p11-kit.h>
+#include <p11-kit/uri.h>
 
 #include <popt.h>
 
@@ -43,6 +44,72 @@ struct p11_ctx {
     bool wait_for_card;
 };
 
+
+static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, CK_INFO *module_info,
+                            CK_SLOT_INFO *slot_info, CK_SLOT_ID slot_id,
+                            CK_TOKEN_INFO *token_info, CK_ATTRIBUTE *label,
+                            CK_ATTRIBUTE *id)
+{
+    P11KitUri *uri;
+    char *uri_str = NULL;
+    char *tmp_str = NULL;
+    int ret;
+    CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE;
+    CK_ATTRIBUTE class_attr = {CKA_CLASS, &cert_class, sizeof(CK_OBJECT_CLASS)};
+
+    uri = p11_kit_uri_new();
+    if (uri == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_new failed.\n");
+        return NULL;
+    }
+
+    ret = p11_kit_uri_set_attribute(uri, label);
+    if (ret != P11_KIT_URI_OK) {
+        DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n");
+        goto done;
+    }
+
+    ret = p11_kit_uri_set_attribute(uri, id);
+    if (ret != P11_KIT_URI_OK) {
+        DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n");
+        goto done;
+    }
+
+    ret = p11_kit_uri_set_attribute(uri, &class_attr);
+    if (ret != P11_KIT_URI_OK) {
+        DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n");
+        goto done;
+    }
+
+
+    memcpy(p11_kit_uri_get_token_info(uri), token_info, sizeof(CK_TOKEN_INFO));
+
+    memcpy(p11_kit_uri_get_slot_info(uri), slot_info, sizeof(CK_SLOT_INFO));
+    ret = p11_kit_uri_set_slot_id(uri, slot_id);
+
+    memcpy(p11_kit_uri_get_module_info(uri), module_info, sizeof(CK_INFO));
+
+    ret = p11_kit_uri_format(uri, P11_KIT_URI_FOR_ANY, &tmp_str);
+    if (ret != P11_KIT_URI_OK) {
+        DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_format failed [%s].\n",
+                                 p11_kit_uri_message(ret));
+        goto done;
+    }
+
+    if (tmp_str != NULL) {
+        uri_str = talloc_strdup(mem_ctx, tmp_str);
+        free(tmp_str);
+        if (uri_str == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+        }
+    }
+
+done:
+    p11_kit_uri_free(uri);
+
+    return uri_str;
+}
+
 static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx)
 {
     CRYPTO_cleanup_all_ex_data();
@@ -234,6 +301,7 @@ struct cert_list {
     X509 *cert;
     char *subject_dn;
     char *cert_b64;
+    char *uri;
     CK_KEY_TYPE key_type;
     CK_OBJECT_HANDLE private_key;
 };
@@ -608,6 +676,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     CK_SLOT_ID slot_id;
     CK_SLOT_INFO info;
     CK_TOKEN_INFO token_info;
+    CK_INFO module_info;
     CK_RV rv;
     size_t module_id;
     char *module_file_name = NULL;
@@ -821,6 +890,17 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
         }
     }
 
+    memset(&module_info, 0, sizeof(CK_INFO));
+    module->C_GetInfo(&module_info);
+
+    DLIST_FOR_EACH(item, cert_list) {
+        item->uri = get_pkcs11_uri(mem_ctx, &module_info, &info, slot_id,
+                                   &token_info,
+                                   &item->attributes[1] /* label */,
+                                   &item->attributes[0] /* id */);
+        DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", item->uri);
+    }
+
     /* TODO: check module_name_in, token_name_in, key_id_in */
 
     if (cert_list == NULL) {

From 9f664bbd10614c57a77e2957ce38735bc6fb7a27 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 8 Oct 2018 12:47:25 +0200
Subject: [PATCH 08/10] p11_child: add PKCS#11 uri to restrict selection

p11_child gets a new option to restrict the selection of certificates
with the help of a PKCS#11 URI.

Related to https://pagure.io/SSSD/sssd/issue/3814
---
 src/p11_child/p11_child.h         |  2 +-
 src/p11_child/p11_child_common.c  |  9 ++--
 src/p11_child/p11_child_nss.c     |  2 +-
 src/p11_child/p11_child_openssl.c | 69 ++++++++++++++++++++++++++++++-
 4 files changed, 75 insertions(+), 7 deletions(-)

diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h
index dd8fdeafb..92ecf74a8 100644
--- a/src/p11_child/p11_child.h
+++ b/src/p11_child/p11_child.h
@@ -54,5 +54,5 @@ 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, char **_multi);
+                const char *key_id_in, const char *uri, char **_multi);
 #endif /* __P11_CHILD_H__ */
diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c
index bc5f6b09b..097e7fa07 100644
--- a/src/p11_child/p11_child_common.c
+++ b/src/p11_child/p11_child_common.c
@@ -60,7 +60,7 @@ 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, char **multi)
+                   const char *key_id, const char *uri, char **multi)
 {
     int ret;
     struct p11_ctx *p11_ctx;
@@ -90,7 +90,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, multi);
+                      module_name, token_name, key_id, uri, multi);
     }
 
 done:
@@ -159,6 +159,7 @@ int main(int argc, const char *argv[])
     char *key_id = NULL;
     char *cert_b64 = NULL;
     bool wait_for_card = false;
+    char *uri = NULL;
 
     struct poptOption long_options[] = {
         POPT_AUTOHELP
@@ -194,6 +195,8 @@ int main(int argc, const char *argv[])
          _("Key ID for authentication"), NULL},
         {"certificate", 0, POPT_ARG_STRING, &cert_b64, 0,
          _("certificate to verify, base64 encoded"), NULL},
+        {"uri", 0, POPT_ARG_STRING, &uri, 0,
+         _("PKCS#11 URI to restrict selection"), NULL},
         POPT_TABLEEND
     };
 
@@ -367,7 +370,7 @@ int main(int argc, const char *argv[])
     }
 
     ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, wait_for_card,
-                  cert_b64, pin, module_name, token_name, key_id, &multi);
+                  cert_b64, pin, module_name, token_name, key_id, uri, &multi);
     if (ret != 0) {
         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
         goto fail;
diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index fff1f2525..f9cbf3f37 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -480,7 +480,7 @@ 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, char **_multi)
+                const char *key_id_in, const char *uri, char **_multi)
 {
     int ret;
     SECStatus rv;
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index 09edeefbd..e5d7c20c8 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -85,7 +85,7 @@ static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, CK_INFO *module_info,
     memcpy(p11_kit_uri_get_token_info(uri), token_info, sizeof(CK_TOKEN_INFO));
 
     memcpy(p11_kit_uri_get_slot_info(uri), slot_info, sizeof(CK_SLOT_INFO));
-    ret = p11_kit_uri_set_slot_id(uri, slot_id);
+    p11_kit_uri_set_slot_id(uri, slot_id);
 
     memcpy(p11_kit_uri_get_module_info(uri), module_info, sizeof(CK_INFO));
 
@@ -662,7 +662,7 @@ 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, char **_multi)
+                const char *key_id_in, const char *uri_str, char **_multi)
 {
     int ret;
     size_t c;
@@ -674,6 +674,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     CK_ULONG num_slots;
     CK_SLOT_ID slots[MAX_SLOTS];
     CK_SLOT_ID slot_id;
+    CK_SLOT_ID uri_slot_id;
     CK_SLOT_INFO info;
     CK_TOKEN_INFO token_info;
     CK_INFO module_info;
@@ -690,6 +691,19 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     char *multi = NULL;
     bool pkcs11_session = false;
     bool pkcs11_login = false;
+    P11KitUri *uri = NULL;
+
+    if (uri_str != NULL) {
+        uri = p11_kit_uri_new();
+        ret = p11_kit_uri_parse(uri_str, P11_KIT_URI_FOR_ANY, uri);
+        if (ret != P11_KIT_URI_OK) {
+            DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_parse failed [%d][%s].\n",
+                                     ret, p11_kit_uri_message(ret));
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
 
     /* Maybe use P11_KIT_MODULE_TRUSTED ? */
     modules = p11_kit_modules_load_and_initialize(0);
@@ -709,6 +723,18 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
             free(mod_name);
             free(mod_file_name);
 
+            if (uri != NULL) {
+                memset(&module_info, 0, sizeof(CK_INFO));
+                modules[c]->C_GetInfo(&module_info);
+
+                /* Skip modules which do not match the PKCS#11 URI */
+                if (p11_kit_uri_match_module_info(uri, &module_info) != 1) {
+                    DEBUG(SSSDBG_TRACE_ALL,
+                          "Not matching URI [%s], skipping.\n", uri_str);
+                    continue;
+                }
+            }
+
             num_slots = MAX_SLOTS;
             rv = modules[c]->C_GetSlotList(CK_FALSE, slots, &num_slots);
             if (rv != CKR_OK) {
@@ -730,6 +756,37 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
                       info.slotDescription, info.manufacturerID, info.flags,
                       (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false",
                       (info.flags & CKF_TOKEN_PRESENT) ? "true": "false");
+
+                /* Skip slots which do not match the PKCS#11 URI */
+                if (uri != NULL) {
+                    uri_slot_id = p11_kit_uri_get_slot_id(uri);
+                    if ((uri_slot_id != (CK_SLOT_ID)-1
+                                && uri_slot_id != slots[s])
+                            || p11_kit_uri_match_slot_info(uri, &info) != 1) {
+                        DEBUG(SSSDBG_TRACE_ALL,
+                              "Not matching URI [%s], skipping.\n", uri_str);
+                        continue;
+                    }
+                }
+
+                if ((info.flags & CKF_TOKEN_PRESENT) && uri != NULL) {
+                    rv = modules[c]->C_GetTokenInfo(slots[s], &token_info);
+                    if (rv != CKR_OK) {
+                        DEBUG(SSSDBG_OP_FAILURE, "C_GetTokenInfo failed.\n");
+                        ret = EIO;
+                        goto done;
+                    }
+                    DEBUG(SSSDBG_TRACE_ALL, "Token label [%s].\n",
+                          token_info.label);
+
+                    if (p11_kit_uri_match_token_info(uri, &token_info) != 1) {
+                        DEBUG(SSSDBG_CONF_SETTINGS,
+                              "No matching uri [%s], skipping.\n", uri_str);
+                        continue;
+                    }
+
+                }
+
                 if ((info.flags & CKF_REMOVABLE_DEVICE)) {
                     break;
                 }
@@ -788,6 +845,13 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
         goto done;
     }
 
+    if (uri != NULL && p11_kit_uri_match_token_info(uri, &token_info) != 1) {
+        DEBUG(SSSDBG_CONF_SETTINGS, "No token matching uri [%s] found.",
+                                    uri_str);
+        ret = ENOENT;
+        goto done;
+    }
+
     module_id = c;
     slot_name = p11_kit_space_strdup(info.slotDescription,
                                      sizeof(info.slotDescription));
@@ -970,6 +1034,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
     free(token_name);
     free(module_file_name);
     p11_kit_modules_finalize_and_release(modules);
+    p11_kit_uri_free(uri);
 
     return ret;
 }

From 1e84906827f84f9af03981ba0e2e68507b1b3d1a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 9 Oct 2018 10:47:04 +0200
Subject: [PATCH 09/10] PAM: add p11_uri option

This patch adds a new option 'p11_uri' to the PAM responder to restrict
the selection of certificates in p11_child with the help of a PKCS#11
URI.

Related to https://pagure.io/SSSD/sssd/issue/3814
---
 src/confdb/confdb.h                  |  1 +
 src/config/SSSDConfig/__init__.py.in |  1 +
 src/config/cfg_rules.ini             |  1 +
 src/config/etc/sssd.api.conf         |  1 +
 src/man/sssd.conf.5.xml              | 33 ++++++++++++++++++++++++++++
 src/responder/pam/pamsrv.h           |  1 +
 src/responder/pam/pamsrv_cmd.c       | 12 +++++++++-
 src/responder/pam/pamsrv_p11.c       |  9 +++++++-
 8 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 87904c214..741d4bc47 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -133,6 +133,7 @@
 #define CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT "p11_wait_for_card_timeout"
 #define CONFDB_PAM_APP_SERVICES "pam_app_services"
 #define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services"
+#define CONFDB_PAM_P11_URI "p11_uri"
 
 /* SUDO */
 #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 4d1dba2d2..a20157c71 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -105,6 +105,7 @@ option_strings = {
     'pam_app_services' : _('Which PAM services are permitted to contact application domains'),
     'pam_p11_allowed_services' : _('Allowed services for using smartcards'),
     'p11_wait_for_card_timeout' : _('Additional timeout to wait for a card if requested'),
+    'p11_uri' : _('PKCS#11 URI to restrict the selection of devices for Smartcard authentication'),
 
     # [sudo]
     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 50a8f1dfc..09a52df4e 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -128,6 +128,7 @@ option = p11_child_timeout
 option = pam_app_services
 option = pam_p11_allowed_services
 option = p11_wait_for_card_timeout
+option = p11_uri
 
 [rule/allowed_sudo_options]
 validator = ini_allowed_options
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index bb686c344..c6d6690fb 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -77,6 +77,7 @@ p11_child_timeout = int, None, false
 pam_app_services = str, None, false
 pam_p11_allowed_services = str, None, false
 p11_wait_for_card_timeout = int, None, false
+p11_uri = str, None, false
 
 [sudo]
 # sudo service
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 4df016331..c8d53f01f 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -1478,6 +1478,39 @@ pam_p11_allowed_services = +my_pam_service, -login
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>p11_uri (string)</term>
+                    <listitem>
+                        <para>
+                            PKCS#11 URI (see RFC-7512 for details) which can be
+                            used to restrict the selection of devices used for
+                            Smartcard authentication. By default SSSD's
+                            p11_child will search for a PKCS#11 slot (reader)
+                            where the 'removable' flags is set and read the
+                            certificates from the inserted token from the first
+                            slot found. If multiple readers are connected
+                            p11_uri can be use to tell p11_child to use a
+                            specific reader.
+                        </para>
+                        <para>
+                            Example:
+                            <programlisting>
+p11_uri = slot-description=My%20Smartcar%20Reader
+                            </programlisting>
+                            or
+                            <programlisting>
+p11_uri = library-description=OpenSC%20smartcard%20framework;slot-id=2
+                            </programlisting>
+                            To find suitable URI please check the debug output
+                            of p11_child. As an alternative the GnuTLS utility
+                            'p11tool' with e.g. the '--list-all' will show
+                            PKCS#11 URIs as well.
+                        </para>
+                        <para>
+                            Default: none
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
 
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 5d877566f..60aa97967 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -103,6 +103,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
                                        time_t timeout,
                                        const char *verify_opts,
                                        struct sss_certmap_ctx *sss_certmap_ctx,
+                                       const char *uri,
                                        struct pam_data *pd);
 errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                             struct cert_auth_info **cert_list);
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 6e37f8316..a22afd225 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1306,6 +1306,7 @@ static errno_t check_cert(TALLOC_CTX *mctx,
     char *cert_verification_opts;
     errno_t ret;
     struct tevent_req *req;
+    char *uri = NULL;
 
     ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
                          CONFDB_PAM_P11_CHILD_TIMEOUT,
@@ -1342,10 +1343,19 @@ static errno_t check_cert(TALLOC_CTX *mctx,
         return ret;
     }
 
+    ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY,
+                            CONFDB_PAM_P11_URI, NULL, &uri);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to read certificate_verification from confdb: [%d]: %s\n",
+              ret, sss_strerror(ret));
+        return ret;
+    }
+
     req = pam_check_cert_send(mctx, ev, pctx->p11_child_debug_fd,
                               pctx->nss_db, p11_child_timeout,
                               cert_verification_opts, pctx->sss_certmap_ctx,
-                              pd);
+                              uri, pd);
     if (req == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n");
         return ENOMEM;
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 8b8859d9d..491bd2b01 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -711,6 +711,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
                                        time_t timeout,
                                        const char *verify_opts,
                                        struct sss_certmap_ctx *sss_certmap_ctx,
+                                       const char *uri,
                                        struct pam_data *pd)
 {
     errno_t ret;
@@ -721,7 +722,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     struct timeval tv;
     int pipefd_to_child[2] = PIPE_INIT;
     int pipefd_from_child[2] = PIPE_INIT;
-    const char *extra_args[14] = { NULL };
+    const char *extra_args[16] = { NULL };
     uint8_t *write_buf = NULL;
     size_t write_buf_len = 0;
     size_t arg_c;
@@ -748,6 +749,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
 
     /* extra_args are added in revers order */
     arg_c = 0;
+    if (uri != NULL) {
+        DEBUG(SSSDBG_TRACE_ALL, "Adding PKCS#11 URI [%s].\n", uri);
+        extra_args[arg_c++] = uri;
+        extra_args[arg_c++] = "--uri";
+    }
+
     if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
         extra_args[arg_c++] = "--wait_for_card";
     }

From 93b45fcdddee66b246bce9d5a263eafe13f5248f Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 9 Oct 2018 10:46:43 +0200
Subject: [PATCH 10/10] tests: add PKCS#11 URI tests

New PAM responder unit test to test the selection of certificates with
the help of PKCS#11 URIs. For this a new SoftHSM2 configuration with 2
slots is created. The new tests will try to access the certificates
stored in the slot individually.

Related to https://pagure.io/SSSD/sssd/issue/3814
---
 src/tests/cmocka/test_pam_srv.c | 120 ++++++++++++++++++++++++++++++++
 src/tests/test_CA/Makefile.am   |  16 ++++-
 2 files changed, 135 insertions(+), 1 deletion(-)

diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 2b02ac27b..7fc9224e1 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -65,6 +65,7 @@
 #endif
 
 #define TEST_TOKEN_NAME "SSSD Test Token"
+#define TEST_TOKEN2_NAME "SSSD Test Token Number 2"
 #define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17"
 #ifdef HAVE_NSS
 #define TEST_MODULE_NAME "NSS-Internal"
@@ -961,6 +962,54 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
     return EOK;
 }
 
+static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body,
+                                          size_t blen, enum response_type type,
+                                          const char *name)
+{
+    size_t rp = 0;
+    uint32_t val;
+    size_t check2_len = 0;
+    char const *check2_strings[] = { NULL,
+                                     TEST_TOKEN2_NAME,
+                                     TEST_MODULE_NAME,
+                                     TEST2_KEY_ID,
+                                     TEST2_PROMPT,
+                                     NULL };
+
+    assert_int_equal(status, 0);
+
+    check2_strings[0] = name;
+    check2_len = check_string_array_len(check2_strings);
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, pam_test_ctx->exp_pam_status);
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, 2);
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, 9);
+
+    assert_int_equal(*(body + rp + val - 1), 0);
+    assert_string_equal(body + rp, TEST_DOM_NAME);
+    rp += val;
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, type);
+
+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+    assert_int_equal(val, check2_len);
+
+    check_string_array(check2_strings, body, &rp);
+
+    assert_int_equal(rp, blen);
+
+    return EOK;
+}
+
 static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
 {
     return test_pam_cert_check_ex(status, body, blen,
@@ -968,6 +1017,12 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
                                   NULL);
 }
 
+static int test_pam_cert2_check(uint32_t status, uint8_t *body, size_t blen)
+{
+    return test_pam_cert2_token2_check_ex(status, body, blen, SSS_PAM_CERT_INFO,
+                                          "pamuser@"TEST_DOM_NAME);
+}
+
 static int test_pam_cert_check_auth_success(uint32_t status, uint8_t *body,
                                             size_t blen)
 {
@@ -2476,6 +2531,65 @@ void test_pam_cert_auth_2certs_one_mapping(void **state)
     assert_int_equal(ret, EOK);
 }
 
+void test_pam_cert_preauth_uri_token1(void **state)
+{
+    int ret;
+
+    struct sss_test_conf_param pam_params[] = {
+        { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token" },
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
+    assert_int_equal(ret, EOK);
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+    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, false);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    set_cmd_cb(test_pam_cert_check);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
+                          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_token2(void **state)
+{
+    int ret;
+
+    struct sss_test_conf_param pam_params[] = {
+        { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token%20Number%202" },
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
+    assert_int_equal(ret, EOK);
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+    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, false);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    set_cmd_cb(test_pam_cert2_check);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
+                          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_filter_response(void **state)
 {
@@ -2915,6 +3029,12 @@ int main(int argc, const char *argv[])
                                         pam_test_setup, pam_test_teardown),
         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
                                         pam_test_setup, pam_test_teardown),
+#ifndef HAVE_NSS
+        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token1,
+                                        pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token2,
+                                        pam_test_setup, pam_test_teardown),
+#endif /* ! HAVE_NSS */
 #endif /* HAVE_TEST_CA */
 
         cmocka_unit_test_setup_teardown(test_filter_response,
diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am
index 1bce2c366..b574c7611 100644
--- a/src/tests/test_CA/Makefile.am
+++ b/src/tests/test_CA/Makefile.am
@@ -24,7 +24,7 @@ pkcs12 = $(addprefix SSSD_test_cert_pkcs12_,$(addsuffix .pem,$(ids)))
 if HAVE_NSS
 extra = p11_nssdb p11_nssdb_2certs
 else
-extra = softhsm2_none softhsm2_one softhsm2_two
+extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens
 endif
 
 # If openssl is run in parallel there might be conflicts with the serial
@@ -114,6 +114,20 @@ softhsm2_two.conf:
 	@echo "objectstore.backend = file" >> $@
 	@echo "slots.removable = true" >> $@
 
+softhsm2_2tokens: softhsm2_2tokens.conf
+	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_0001.pem --login  --label 'SSSD test cert 0001' --id 'C554C9F82C2A9D58B70921C143304153A8A42F17' pkcs11:token=SSSD%20Test%20Token
+	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 'C554C9F82C2A9D58B70921C143304153A8A42F17' pkcs11:token=SSSD%20Test%20Token
+	SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token  --label "SSSD Test Token Number 2" --pin 654321 --so-pin 654321 --free
+	GNUTLS_PIN=654321 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0002.pem --login  --label 'SSSD test cert 0002' --id '5405842D56CF31F0BB025A695C5F3E907051C5B9' pkcs11:token=SSSD%20Test%20Token%20Number%202
+	GNUTLS_PIN=654321 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_cert_key_0002.pem --login  --label 'SSSD test cert 0002' --id '5405842D56CF31F0BB025A695C5F3E907051C5B9' pkcs11:token=SSSD%20Test%20Token%20Number%202
+
+softhsm2_2tokens.conf:
+	@echo "directories.tokendir = "$(abs_top_builddir)"/src/tests/test_CA/softhsm2_2tokens" > $@
+	@echo "objectstore.backend = file" >> $@
+	@echo "slots.removable = true" >> $@
+
 CLEANFILES = \
     index.txt  index.txt.attr \
     index.txt.attr.old  index.txt.old \
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to