URL: https://github.com/SSSD/sssd/pull/137
Author: sumit-bose
 Title: #137: Initial pkinit support
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/137/head:pr137
git checkout pr137
From 0c0b2103dd5cd3bedaacbd239a6cfef66b5c75aa Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 19 Sep 2016 16:56:46 +0200
Subject: [PATCH 01/10] PAM: store user object in the preq context

---
 src/responder/pam/pamsrv.h     |  1 +
 src/responder/pam/pamsrv_cmd.c | 12 ++++++------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 75045d0..e356812 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -68,6 +68,7 @@ struct pam_auth_req {
 
     struct pam_auth_dp_req *dpreq_spy;
 
+    struct ldb_message *user_obj;
     struct ldb_message *cert_user_obj;
     char *token_name;
 };
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index e73a819..e74dd16 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1560,7 +1560,6 @@ static int pam_check_user_search(struct pam_auth_req *preq)
     struct pam_ctx *pctx =
             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     static const char *user_attrs[] = SYSDB_PW_ATTRS;
-    struct ldb_message *msg;
     struct ldb_result *res;
     const char *sysdb_name;
 
@@ -1621,11 +1620,12 @@ static int pam_check_user_search(struct pam_auth_req *preq)
         }
 
         if (preq->pd->name_is_upn) {
-            ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
+            ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs,
+                                           &preq->user_obj);
             if (ret == EOK) {
                 /* Since sysdb_search_user_by_upn() searches the whole cache we
                 * have to set the domain so that it matches the result. */
-                sysdb_name = ldb_msg_find_attr_as_string(msg,
+                sysdb_name = ldb_msg_find_attr_as_string(preq->user_obj,
                                                          SYSDB_NAME, NULL);
                 if (sysdb_name == NULL) {
                     DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n");
@@ -1654,7 +1654,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
             } else if (res->count == 0) {
                 ret = ENOENT;
             } else {
-                msg = res->msgs[0];
+                preq->user_obj = res->msgs[0];
             }
         }
         if (ret != EOK && ret != ENOENT) {
@@ -1693,7 +1693,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
 
         /* if we need to check the remote account go on */
         if (preq->check_provider) {
-            cacheExpire = ldb_msg_find_attr_as_uint64(msg,
+            cacheExpire = ldb_msg_find_attr_as_uint64(preq->user_obj,
                                                       SYSDB_CACHE_EXPIRE, 0);
             if (cacheExpire < time(NULL)) {
                 break;
@@ -1704,7 +1704,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
               "Returning info for user [%s@%s]\n", name, dom->name);
 
         /* We might have searched by alias. Pass on the primary name */
-        ret = pd_set_primary_name(msg, preq->pd);
+        ret = pd_set_primary_name(preq->user_obj, preq->pd);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
             return ret;

From 2ee4652d4c8cc687ac705d20ab81e7faf5408432 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 25 Jan 2017 17:34:54 +0100
Subject: [PATCH 02/10] PAM: fix memory leak in pam_sss

Since there can be multiple rounds trips between the PAM client and SSSD
it might be possible that the same data is send multiple times by SSSD.
So before overriding the old data it should be freed. I've seen this
with the domain name which is send both in the pre-auth and the auth
responses. To be on the safe side I added free() for some other items as
well.
---
 src/sss_client/pam_sss.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 03613b8..8f97af7 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -869,6 +869,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     break;
                 }
                 D(("domain name: [%s]", &buf[p]));
+                free(pi->domain_name);
                 pi->domain_name = strdup((char *) &buf[p]);
                 if (pi->domain_name == NULL) {
                     D(("strdup failed"));
@@ -937,6 +938,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     break;
                 }
 
+                free(pi->otp_vendor);
                 pi->otp_vendor = strdup((char *) &buf[p]);
                 if (pi->otp_vendor == NULL) {
                     D(("strdup failed"));
@@ -950,6 +952,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     pi->otp_vendor = NULL;
                     break;
                 }
+                free(pi->otp_token_id);
                 pi->otp_token_id = strdup((char *) &buf[p + offset]);
                 if (pi->otp_token_id == NULL) {
                     D(("strdup failed"));
@@ -963,6 +966,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     pi->otp_token_id = NULL;
                     break;
                 }
+                free(pi->otp_challenge);
                 pi->otp_challenge = strdup((char *) &buf[p + offset]);
                 if (pi->otp_challenge == NULL) {
                     D(("strdup failed"));
@@ -976,6 +980,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     break;
                 }
 
+                free(pi->cert_user);
                 pi->cert_user = strdup((char *) &buf[p]);
                 if (pi->cert_user == NULL) {
                     D(("strdup failed"));
@@ -1010,6 +1015,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                     pi->cert_user = NULL;
                     break;
                 }
+                free(pi->token_name);
                 pi->token_name = strdup((char *) &buf[p + offset]);
                 if (pi->token_name == NULL) {
                     D(("strdup failed"));

From 89022abcc0e4fb4d3071d3a31f74bb0583acd66e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 24 Jan 2017 18:18:23 +0100
Subject: [PATCH 03/10] PAM: use sentinel error code in PAM tests

---
 src/tests/cmocka/test_pam_srv.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 3b8327e..e6ed8f5 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -883,7 +883,9 @@ void test_pam_open_session(void **state)
     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_OPEN_SESSION);
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
 
-    pam_test_ctx->exp_pam_status = PAM_NO_MODULE_DATA;
+    /* make sure pam_status is not touched by setting it to a value which is
+     * not used by SSSD. */
+    pam_test_ctx->exp_pam_status = _PAM_RETURN_VALUES;
     set_cmd_cb(test_pam_simple_check);
     ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_OPEN_SESSION,
                           pam_test_ctx->pam_cmds);

From 0e7074359bfcfc92fbe06334c8648130e3fd502d Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 24 Jan 2017 18:04:11 +0100
Subject: [PATCH 04/10] utils: new error codes

ERR_SC_AUTH_NOT_SUPPORTED can be used by backends to indicate that
Smartcard authentication is not supported. ERR_NO_AUTH_METHOD_AVAILABLE
can be used by backends that no authentication method was found.
---
 src/util/util_errors.c | 2 ++
 src/util/util_errors.h | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 88ebf4e..17388c9 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -102,6 +102,8 @@ struct err_string error_to_str[] = {
     { "No proxy server for secrets available"}, /* ERR_SEC_NO_PROXY */
     { "The maximum number of stored secrets has been reached" }, /* ERR_SEC_INVALID_TOO_MANY_SECRETS */
     { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */
+    { "No authentication methode available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */
+    { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */
     { "ERR_LAST" } /* ERR_LAST */
 };
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 525983f..7aacad2 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -124,6 +124,8 @@ enum sssd_errors {
     ERR_SEC_NO_PROXY,
     ERR_SEC_INVALID_TOO_MANY_SECRETS,
     ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE,
+    ERR_NO_AUTH_METHOD_AVAILABLE,
+    ERR_SC_AUTH_NOT_SUPPORTED,
     ERR_LAST            /* ALWAYS LAST */
 };
 

From a21615e84914a60c5e4650c36653a77ae5e46cf8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 25 Jan 2017 20:29:43 +0100
Subject: [PATCH 05/10] LDAP/proxy: tell frontend that Smartcard auth is not
 supported

---
 src/providers/ldap/ldap_auth.c   | 11 ++++++++++-
 src/providers/proxy/proxy_auth.c |  8 ++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 00d3828..00ddd88 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -645,7 +645,13 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx,
 
     /* The token must be a password token */
     if (sss_authtok_get_type(authtok) != SSS_AUTHTOK_TYPE_PASSWORD) {
-        tevent_req_error(req, ERR_AUTH_FAILED);
+        if (sss_authtok_get_type(authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+            || sss_authtok_get_type(authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+            /* Tell frontend that we do not support Smartcard authentication */
+            tevent_req_error(req, ERR_SC_AUTH_NOT_SUPPORTED);
+        } else {
+            tevent_req_error(req, ERR_AUTH_FAILED);
+        }
         return tevent_req_post(req, ev);
     }
 
@@ -1028,6 +1034,9 @@ static void sdap_pam_auth_handler_done(struct tevent_req *subreq)
         state->pd->account_locked = true;
         state->pd->pam_status = PAM_PERM_DENIED;
         break;
+    case ERR_SC_AUTH_NOT_SUPPORTED:
+        state->pd->pam_status = PAM_BAD_ITEM;
+        break;
     default:
         state->pd->pam_status = PAM_SYSTEM_ERR;
         break;
diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c
index 2b3510c..e53b38e 100644
--- a/src/providers/proxy/proxy_auth.c
+++ b/src/providers/proxy/proxy_auth.c
@@ -737,6 +737,14 @@ proxy_pam_handler_send(TALLOC_CTX *mem_ctx,
     state->auth_ctx = proxy_auth_ctx;
     state->be_ctx = params->be_ctx;
 
+    /* Tell frontend that we do not support Smartcard authentication */
+    if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+            || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+        pd->pam_status = PAM_BAD_ITEM;
+        goto immediately;
+    }
+
+
     switch (pd->cmd) {
     case SSS_PAM_AUTHENTICATE:
     case SSS_PAM_CHAUTHTOK:

From 45be442019a8789bf0a0f36cd5424a5718aa5070 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Fri, 23 Sep 2016 17:11:35 +0200
Subject: [PATCH 06/10] authtok: enhance support for Smartcard auth blobs

The blobs contains beside the PIN the name of the PKCS#11 module and the
token name where the certificate of the user was found and the key id.
Those data will be used e.g. by the pkinit module to make sure them
right certificate is used.
---
 src/tests/cmocka/test_authtok.c |  89 ++++++++++-
 src/util/authtok-utils.c        |  91 ++++++++++++
 src/util/authtok-utils.h        |  56 +++++++
 src/util/authtok.c              | 320 ++++++++++++++++++++++++++++++++++++++--
 src/util/authtok.h              |  84 +++++++++++
 5 files changed, 625 insertions(+), 15 deletions(-)

diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c
index 30dcc9c..b2ef08a 100644
--- a/src/tests/cmocka/test_authtok.c
+++ b/src/tests/cmocka/test_authtok.c
@@ -437,6 +437,85 @@ void test_sss_authtok_2fa_blobs(void **state)
     talloc_free(fa2);
 }
 
+void test_sss_authtok_sc_blobs(void **state)
+{
+    int ret;
+    struct test_state *ts;
+    size_t needed_size;
+    uint8_t *buf;
+    const char *pin;
+    size_t pin_len;
+    const char *token_name;
+    size_t token_name_len;
+    const char *module_name;
+    size_t module_name_len;
+    const char *key_id;
+    size_t key_id_len;
+
+    ts = talloc_get_type_abort(*state, struct test_state);
+
+    ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0,
+                                NULL, 0, &needed_size);
+    assert_int_equal(ret, EAGAIN);
+
+    buf = talloc_size(ts, needed_size);
+    assert_non_null(buf);
+
+    ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0,
+                                buf, needed_size, &needed_size);
+    assert_int_equal(ret, EOK);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0abc\0defg\0hijkl\0mnopqr\0",
+                        needed_size);
+#else
+    assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7abc\0defg\0hijkl\0mnopqr\0",
+                        needed_size);
+#endif
+
+    ret = sss_authtok_set(ts->authtoken, SSS_AUTHTOK_TYPE_SC_PIN, buf,
+                          needed_size);
+    assert_int_equal(ret, EOK);
+
+    ret = sss_authtok_get_sc(ts->authtoken, &pin, &pin_len,
+                             &token_name, &token_name_len,
+                             &module_name, &module_name_len,
+                             &key_id, &key_id_len);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(pin_len, 3);
+    assert_string_equal(pin, "abc");
+    assert_int_equal(token_name_len, 4);
+    assert_string_equal(token_name, "defg");
+    assert_int_equal(module_name_len, 5);
+    assert_string_equal(module_name, "hijkl");
+    assert_int_equal(key_id_len, 6);
+    assert_string_equal(key_id, "mnopqr");
+
+    ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
+                             &token_name, &token_name_len,
+                             &module_name, &module_name_len,
+                             &key_id, &key_id_len);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(token_name_len, 4);
+    assert_string_equal(token_name, "defg");
+    assert_int_equal(module_name_len, 5);
+    assert_string_equal(module_name, "hijkl");
+    assert_int_equal(key_id_len, 6);
+    assert_string_equal(key_id, "mnopqr");
+
+    ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
+                             &token_name, NULL,
+                             &module_name, NULL,
+                             &key_id, NULL);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(token_name, "defg");
+    assert_string_equal(module_name, "hijkl");
+    assert_string_equal(key_id, "mnopqr");
+
+    sss_authtok_set_empty(ts->authtoken);
+    talloc_free(buf);
+}
+
 #define MISSING_NULL_CHECK do { \
     assert_int_equal(ret, EOK); \
     assert_int_equal(fa1_len, 3); \
@@ -524,8 +603,8 @@ void test_sss_authtok_sc_pin(void **state)
     assert_int_equal(sss_authtok_get_type(ts->authtoken),
                      SSS_AUTHTOK_TYPE_SC_PIN);
     size = sss_authtok_get_size(ts->authtoken);
-    assert_int_equal(size, 9);
-    assert_memory_equal(sss_authtok_get_data(ts->authtoken), "12345678\0",
+    assert_int_equal(size, 28);
+    assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0",
                                              size);
 
     ret = sss_authtok_set_sc_pin(ts->authtoken, "12345678", 5);
@@ -533,8 +612,8 @@ void test_sss_authtok_sc_pin(void **state)
     assert_int_equal(sss_authtok_get_type(ts->authtoken),
                      SSS_AUTHTOK_TYPE_SC_PIN);
     size = sss_authtok_get_size(ts->authtoken);
-    assert_int_equal(size, 6);
-    assert_memory_equal(sss_authtok_get_data(ts->authtoken), "12345\0",
+    assert_int_equal(size, 25);
+    assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0",
                                              size);
 
     ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len);
@@ -592,6 +671,8 @@ int main(int argc, const char *argv[])
                                         setup, teardown),
         cmocka_unit_test_setup_teardown(test_sss_authtok_sc_pin,
                                         setup, teardown),
+        cmocka_unit_test_setup_teardown(test_sss_authtok_sc_blobs,
+                                        setup, teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c
index 65fba90..e7123df 100644
--- a/src/util/authtok-utils.c
+++ b/src/util/authtok-utils.c
@@ -72,3 +72,94 @@ errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len,
 
     return 0;
 }
+
+errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+                              const char *token_name, size_t token_name_len,
+                              const char *module_name, size_t module_name_len,
+                              const char *key_id, size_t key_id_len,
+                              uint8_t *buf, size_t buf_len,
+                              size_t *_sc_blob_len)
+{
+    size_t c;
+    uint32_t tmp_uint32_t;
+
+    if (pin_len > UINT32_MAX || token_name_len > UINT32_MAX
+            || module_name_len > UINT32_MAX
+            || (pin_len != 0 && pin == NULL)
+            || (token_name_len != 0 && token_name == NULL)
+            || (module_name_len != 0 && module_name == NULL)
+            || (key_id_len != 0 && key_id == NULL)) {
+        return EINVAL;
+    }
+
+    /* A missing pin is ok in the case of a reader with a keyboard */
+    if (pin == NULL) {
+        pin = "";
+        pin_len = 0;
+    }
+
+    if (token_name == NULL) {
+        token_name = "";
+        token_name_len = 0;
+    }
+
+    if (module_name == NULL) {
+        module_name = "";
+        module_name_len = 0;
+    }
+
+    if (key_id == NULL) {
+        key_id = "";
+        key_id_len = 0;
+    }
+
+    /* len should not include the trailing \0 */
+    if (pin_len == 0 || pin[pin_len - 1] == '\0') {
+        pin_len = strlen(pin);
+    }
+
+    if (token_name_len == 0 || token_name[token_name_len - 1] == '\0') {
+        token_name_len = strlen(token_name);
+    }
+
+    if (module_name_len == 0 || module_name[module_name_len - 1] == '\0') {
+        module_name_len = strlen(module_name);
+    }
+
+    if (key_id_len == 0 || key_id[key_id_len - 1] == '\0') {
+        key_id_len = strlen(key_id);
+    }
+
+    *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len + 4
+                            + 4 * sizeof(uint32_t);
+    if (buf == NULL || buf_len < *_sc_blob_len) {
+        return EAGAIN;
+    }
+
+    c = 0;
+    tmp_uint32_t = (uint32_t) pin_len + 1;
+    SAFEALIGN_COPY_UINT32(buf, &tmp_uint32_t, &c);
+    tmp_uint32_t = (uint32_t) token_name_len + 1;
+    SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+    tmp_uint32_t = (uint32_t) module_name_len + 1;
+    SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+    tmp_uint32_t = (uint32_t) key_id_len + 1;
+    SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+
+    memcpy(buf + c, pin, pin_len);
+    buf[c + pin_len] = '\0';
+    c += pin_len + 1;
+
+    memcpy(buf + c, token_name, token_name_len);
+    buf[c + token_name_len] = '\0';
+    c += token_name_len + 1;
+
+    memcpy(buf + c, module_name, module_name_len);
+    buf[c + module_name_len] = '\0';
+    c += module_name_len + 1;
+
+    memcpy(buf + c, key_id, key_id_len);
+    buf[c + key_id_len] = '\0';
+
+    return 0;
+}
diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h
index 07aef3c..c5aace3 100644
--- a/src/util/authtok-utils.h
+++ b/src/util/authtok-utils.h
@@ -25,6 +25,37 @@
 #include "sss_client/sss_cli.h"
 
 /**
+ * @brief Fill memory buffer with Smartcard authentication blob
+ *
+ * @param[in]  pin         PIN, null terminated
+ * @param[in]  pin_len     Length of the PIN, if 0
+ *                         strlen() will be called internally
+ * @param[in]  token_name  Token name, null terminated
+ * @param[in]  token_name_len     Length of the token name, if 0
+ *                         strlen() will be called internally
+ * @param[in]  module_name Name of PKCS#11 module, null terminated
+ * @param[in]  module_name_len     Length of the module name, if 0
+ *                         strlen() will be called internally
+ * @param[in]  key_id      Key ID of the certificate
+ * @param[in]  key_id_len  Length of the key id of the certificate, if 0
+ *                         strlen() will be called internally
+ * @param[in]  buf         memory buffer of size buf_len, may be NULL
+ * @param[in]  buf_len     size of memory buffer buf
+ *
+ * @param[out] _sc_blob    len size of the Smartcard authentication blob
+ *
+ * @return     EOK         on success
+ *             EINVAL      if input data is not consistent
+ *             EAGAIN      if provided buffer is too small, _sc_blob_len
+ *                         contains the size needed to store the SC blob
+ */
+errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+                              const char *token_name, size_t token_name_len,
+                              const char *module_name, size_t module_name_len,
+                              const char *key_id, size_t key_id_len,
+                              uint8_t *buf, size_t buf_len,
+                              size_t *_sc_blob_len);
+/**
  * @brief Fill memory buffer with 2FA blob
  *
  * @param[in]  fa1       First authentication factor, null terminated
@@ -67,4 +98,29 @@ errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx,
                                  const uint8_t *blob, size_t blob_len,
                                  char **fa1, size_t *_fa1_len,
                                  char **fa2, size_t *_fa2_len);
+
+/**
+ * @brief Extract SC data from memory buffer
+ *
+ * @param[in]  mem_ctx           Talloc memory context to allocate the 2FA
+ *                               data on
+ * @param[in]  blob              Memory buffer containing the 2FA data
+ * @param[in]  blob_len          Size of the memory buffer
+ * @param[out] _pin              PIN, null terminated
+ * @param[out] _pin_len          Length of the PIN
+ * @param[out] _token_name       Token name, null terminated
+ * @param[out] _token_name_len   Length of the token name
+ * @param[out] _module_name      Name of PKCS#11 module, null terminated
+ * @param[out] _module_name_len  Length of the module name
+ *
+ * @return     EOK       on success
+ *             EINVAL    if input data is not consistent
+ *             EINVAL    if no memory can be allocated
+ */
+errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+                                 const uint8_t *blob, size_t blob_len,
+                                 char **pin, size_t *_pin_len,
+                                 char **token_name, size_t *_token_name_len,
+                                 char **module_name, size_t *_module_name_len,
+                                 char **key_id, size_t *_key_id_len);
 #endif /*  __AUTHTOK_UTILS_H__ */
diff --git a/src/util/authtok.c b/src/util/authtok.c
index 6062cd8..c2f78be 100644
--- a/src/util/authtok.c
+++ b/src/util/authtok.c
@@ -196,10 +196,9 @@ errno_t sss_authtok_set(struct sss_auth_token *tok,
     case SSS_AUTHTOK_TYPE_2FA:
         return sss_authtok_set_2fa_from_blob(tok, data, len);
     case SSS_AUTHTOK_TYPE_SC_PIN:
-        return sss_authtok_set_sc_pin(tok, (const char*)data, len);
+        return sss_authtok_set_sc_from_blob(tok, data, len);
     case SSS_AUTHTOK_TYPE_SC_KEYPAD:
-        sss_authtok_set_sc_keypad(tok);
-        return EOK;
+        return sss_authtok_set_sc_from_blob(tok, data, len);
     case SSS_AUTHTOK_TYPE_EMPTY:
         sss_authtok_set_empty(tok);
         return EOK;
@@ -425,6 +424,104 @@ errno_t sss_authtok_set_2fa(struct sss_auth_token *tok,
     return EOK;
 }
 
+errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
+                           enum sss_authtok_type type,
+                           const char *pin, size_t pin_len,
+                           const char *token_name, size_t token_name_len,
+                           const char *module_name, size_t module_name_len,
+                           const char *key_id, size_t key_id_len)
+{
+    int ret;
+    size_t needed_size;
+
+    if (type != SSS_AUTHTOK_TYPE_SC_PIN
+            && type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid type [%d].\n", type);
+        return EINVAL;
+    }
+
+    sss_authtok_set_empty(tok);
+
+    ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
+                                module_name, module_name_len,
+                                key_id, key_id_len, NULL, 0,
+                                &needed_size);
+    if (ret != EAGAIN) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+        return ret;
+    }
+
+    tok->data = talloc_size(tok, needed_size);
+    if (tok->data == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+        return ENOMEM;
+    }
+
+    ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len,
+                                module_name, module_name_len,
+                                key_id, key_id_len, tok->data,
+                                needed_size, &needed_size);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+        talloc_free(tok->data);
+        return ret;
+    }
+
+    tok->length = needed_size;
+    tok->type = type;
+
+    return EOK;
+}
+
+errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+                                     const uint8_t *data,
+                                     size_t len)
+{
+    int ret;
+    char *pin = NULL;
+    size_t pin_len;
+    char *token_name = NULL;
+    size_t token_name_len;
+    char *module_name = NULL;
+    size_t module_name_len;
+    char *key_id = NULL;
+    size_t key_id_len;
+    TALLOC_CTX *tmp_ctx;
+
+    if (tok == NULL) {
+        return EFAULT;
+    }
+    if (data == NULL || len == 0) {
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sss_auth_unpack_sc_blob(tmp_ctx, data, len, &pin, &pin_len,
+                                  &token_name, &token_name_len,
+                                  &module_name, &module_name_len,
+                                  &key_id, &key_id_len);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_sc_blob failed.\n");
+        goto done;
+    }
+
+    ret = sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, pin_len,
+                             token_name, token_name_len,
+                             module_name, module_name_len,
+                             key_id, key_id_len);
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
 errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
                                size_t len)
 {
@@ -435,15 +532,17 @@ errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
         return EINVAL;
     }
 
-    sss_authtok_set_empty(tok);
-
-    return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_SC_PIN,
-                                  "sc_pin", pin, len);
+    return sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, len,
+                              NULL, 0, NULL, 0, NULL, 0);
 }
 
-errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin,
+errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
                                size_t *len)
 {
+    int ret;
+    const char *pin = NULL;
+    size_t pin_len;
+
     if (!tok) {
         return EFAULT;
     }
@@ -451,9 +550,16 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin,
     case SSS_AUTHTOK_TYPE_EMPTY:
         return ENOENT;
     case SSS_AUTHTOK_TYPE_SC_PIN:
-        *pin = (const char *)tok->data;
+        ret = sss_authtok_get_sc(tok, &pin, &pin_len,
+                                 NULL, NULL, NULL, NULL, NULL, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+            return ret;
+        }
+
+        *_pin = pin;
         if (len) {
-            *len = tok->length - 1;
+            *len = pin_len;
         }
         return EOK;
     case SSS_AUTHTOK_TYPE_PASSWORD:
@@ -468,10 +574,202 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin,
 
 void sss_authtok_set_sc_keypad(struct sss_auth_token *tok)
 {
-    if (!tok) {
+    if (tok == NULL) {
         return;
     }
+
     sss_authtok_set_empty(tok);
 
     tok->type = SSS_AUTHTOK_TYPE_SC_KEYPAD;
 }
+
+errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+                                 const uint8_t *blob, size_t blob_len,
+                                 char **pin, size_t *_pin_len,
+                                 char **token_name, size_t *_token_name_len,
+                                 char **module_name, size_t *_module_name_len,
+                                 char **key_id, size_t *_key_id_len)
+{
+    size_t c;
+    uint32_t pin_len;
+    uint32_t token_name_len;
+    uint32_t module_name_len;
+    uint32_t key_id_len;
+
+    c = 0;
+
+    if (blob == NULL || blob_len == 0) {
+        pin_len = 0;
+        token_name_len = 0;
+        module_name_len = 0;
+        key_id_len = 0;
+    } else if (blob_len > 0
+                && strnlen((const char *) blob, blob_len) == blob_len - 1) {
+        pin_len = blob_len;
+        token_name_len = 0;
+        module_name_len = 0;
+        key_id_len = 0;
+    } else {
+        if (blob_len < 4 * sizeof(uint32_t)) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+            return EINVAL;
+        }
+
+        SAFEALIGN_COPY_UINT32(&pin_len, blob, &c);
+        SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c);
+        SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c);
+        SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c);
+
+        if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len
+                                             + module_name_len + key_id_len) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+            return EINVAL;
+        }
+    }
+
+    if (pin_len != 0) {
+        *pin = talloc_strndup(mem_ctx, (const char *) blob + c, pin_len);
+        if (*pin == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            return ENOMEM;
+        }
+    } else {
+        *pin = NULL;
+    }
+
+    if (token_name_len != 0) {
+        *token_name = talloc_strndup(mem_ctx, (const char *) blob + c + pin_len,
+                                     token_name_len);
+        if (*token_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            talloc_free(*pin);
+            return ENOMEM;
+        }
+    } else {
+        *token_name = NULL;
+    }
+
+    if (module_name_len != 0) {
+        *module_name = talloc_strndup(mem_ctx,
+                                      (const char *) blob + c + pin_len
+                                                              + token_name_len,
+                                      module_name_len);
+        if (*module_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            talloc_free(*pin);
+            talloc_free(*token_name);
+            return ENOMEM;
+        }
+    } else {
+        *module_name = NULL;
+    }
+
+    if (key_id_len != 0) {
+        *key_id = talloc_strndup(mem_ctx,
+                                      (const char *) blob + c + pin_len
+                                                              + token_name_len
+                                                              + module_name_len,
+                                      key_id_len);
+        if (*key_id == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            talloc_free(*pin);
+            talloc_free(*token_name);
+            talloc_free(*module_name);
+            return ENOMEM;
+        }
+    } else {
+        *key_id = NULL;
+    }
+
+    /* Re-calculate length for the case where \0 was missing in the blob */
+    if (_pin_len != NULL) {
+        *_pin_len = (*pin == NULL) ? 0 : strlen(*pin);
+    }
+    if (_token_name_len != NULL) {
+        *_token_name_len = (*token_name == NULL) ? 0 : strlen(*token_name);
+    }
+    if (_module_name_len != NULL) {
+        *_module_name_len = (*module_name == NULL) ? 0 : strlen(*module_name);
+    }
+
+    if (_key_id_len != NULL) {
+        *_key_id_len = (*key_id == NULL) ? 0 : strlen(*key_id);
+    }
+
+    return EOK;
+}
+
+errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+                           const char **_pin, size_t *_pin_len,
+                           const char **_token_name, size_t *_token_name_len,
+                           const char **_module_name, size_t *_module_name_len,
+                           const char **_key_id, size_t *_key_id_len)
+{
+    size_t c = 0;
+    size_t pin_len;
+    size_t token_name_len;
+    size_t module_name_len;
+    size_t key_id_len;
+    uint32_t tmp_uint32_t;
+
+    if (!tok) {
+        return EFAULT;
+    }
+
+    if (tok->type != SSS_AUTHTOK_TYPE_SC_PIN
+            && tok->type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+        return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
+    }
+
+    if (tok->length < 4 * sizeof(uint32_t)) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+        return EINVAL;
+    }
+
+    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data, &c);
+    pin_len = tmp_uint32_t - 1;
+    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+    token_name_len = tmp_uint32_t - 1;
+    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+    module_name_len = tmp_uint32_t -1;
+    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+    key_id_len = tmp_uint32_t -1;
+
+    if (tok->length != 4 * sizeof(uint32_t) +  4 + pin_len + token_name_len
+                                         + module_name_len + key_id_len) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+        return EINVAL;
+    }
+
+    if (_pin != NULL) {
+        *_pin = (const char *) tok->data + c;
+    }
+    if (_pin_len != NULL) {
+        *_pin_len = pin_len;
+    }
+
+    if (_token_name != NULL) {
+        *_token_name = (const char *) tok->data + c + pin_len + 1;
+    }
+    if (_token_name_len != NULL) {
+        *_token_name_len = token_name_len;
+    }
+
+    if (_module_name != NULL) {
+        *_module_name = (const char *) tok->data + c + pin_len + 1
+                                                     + token_name_len + 1;
+    }
+    if (_module_name_len != NULL) {
+        *_module_name_len = module_name_len;
+    }
+
+    if (_key_id != NULL) {
+        *_key_id = (const char *) tok->data + c + pin_len + 1
+                               + token_name_len + 1 + module_name_len + 1;
+    }
+    if (_key_id_len != NULL) {
+        *_key_id_len = key_id_len;
+    }
+
+    return EOK;
+}
diff --git a/src/util/authtok.h b/src/util/authtok.h
index f1a01a4..c146ba2 100644
--- a/src/util/authtok.h
+++ b/src/util/authtok.h
@@ -264,4 +264,88 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin,
  */
 void sss_authtok_set_sc_keypad(struct sss_auth_token *tok);
 
+/**
+ * @brief Set complete Smart Card authentication blob including PKCS#11 token
+ *        name, module name and key id.
+ *
+ * @param tok             A pointer to an sss_auth_token
+ * @param type            Authentication token type, may be
+ *                        SSS_AUTHTOK_TYPE_SC_PIN or SSS_AUTHTOK_TYPE_SC_KEYPAD
+ * @param pin             A pointer to a const char *, that will point to a null
+ *                        terminated string containing the pin
+ * @param pin_len         The length of the pin string, if set to 0 it will be
+ *                        calculated
+ * @param token_name      A pointer to a const char *, that will point to a null
+ *                        terminated string containing the PKCS#11 token name
+ * @param token_name_len  The length of the token name string, if set to 0 it
+ *                        will be calculated
+ * @param module_name     A pointer to a const char *, that will point to a null
+ *                        terminated string containing the PKCS#11 module name
+ * @param module_name_len The length of the module name string, if set to 0 it
+ *                        will be calculated
+ * @param key_id          A pointer to a const char *, that will point to a null
+ *                        terminated string containing the PKCS#11 key id
+ * @param key_id_len      The length of the key id string, if set to 0 it will be
+ *                        calculated
+ *
+ * @return       EOK on success
+ *               EINVAL unexpected or inval input
+ *               ENOMEM memory allocation error
+ */
+errno_t sss_authtok_set_sc(struct sss_auth_token *tok,
+                           enum sss_authtok_type type,
+                           const char *pin, size_t pin_len,
+                           const char *token_name, size_t token_name_len,
+                           const char *module_name, size_t module_name_len,
+                           const char *key_id, size_t key_id_len);
+/**
+ * @brief Set a Smart Card authentication data, replacing any previous data
+ *
+ * @param tok    A pointer to a sss_auth_token structure to change, also
+ *               used as a memory context to allocate the internal data.
+ * @param data   Smart Card authentication data blob
+ * @param len    The length of the blob
+ *
+ * @return       EOK on success
+ *               ENOMEM on error
+ */
+errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+                                     const uint8_t *data,
+                                     size_t len);
+
+/**
+ * @brief Get complete Smart Card authtoken data
+ *
+ * @param tok                   A pointer to a sss_auth_token structure
+ * @param[out] _pin             A pointer to a const char *, that will point to
+ *                              a null terminated string holding the pin,
+ *                              may not be modified or freed
+ * @param[out] _pin__len        Length of the pin
+ * @param[out] _token_name      A pointer to a const char *, that will point to
+ *                              a null terminated string holding the PKCS#11
+ *                              token name, may not be modified or freed
+ * @param[out] _token_name_len  Length of the PKCS#11 token name
+ * @param[out] _module_name     A pointer to a const char *, that will point to
+ *                              a null terminated string holding the PKCS#11
+ *                              module name, may not be modified or freed
+ * @param[out] _module_name_len Length of the PKCS#11 module name
+ * @param[out] _key_id          A pointer to a const char *, that will point to
+ *                              a null terminated string holding the PKCS#11
+ *                              key id, may not be modified or freed
+ * @param[out] _key_id_len      Length of the PKCS#11 key id
+ *
+ * Any of the output pointers may be NULL if the caller does not need the
+ * specific item.
+ *
+ * @return     EOK     on success
+ *             EFAULT  missing token
+ *             EINVAL  if input data is not consistent
+ *             ENOENT  if the token is empty
+ *             EACCESS if the token is not a Smart Card token
+ */
+errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+                           const char **_pin, size_t *_pin_len,
+                           const char **_token_name, size_t *_token_name_len,
+                           const char **_module_name, size_t *_module_name_len,
+                           const char **_key_id, size_t *_key_id_len);
 #endif /*  __AUTHTOK_H__ */

From f909dacbdeaf617a63ad45d7db6f5989d029acd1 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Thu, 8 Oct 2015 16:42:39 +0200
Subject: [PATCH 07/10] PAM: forward Smartcard credentials to backends

---
 src/responder/pam/pamsrv.h      |   3 ++
 src/responder/pam/pamsrv_cmd.c  | 102 +++++++++++++++++++++++++++++++---------
 src/tests/cmocka/test_pam_srv.c |  16 ++++++-
 3 files changed, 97 insertions(+), 24 deletions(-)

diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index e356812..389888e 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -71,6 +71,9 @@ struct pam_auth_req {
     struct ldb_message *user_obj;
     struct ldb_message *cert_user_obj;
     char *token_name;
+    char *module_name;
+    char *key_id;
+    bool cert_auth_local;
 };
 
 struct sss_cmd_table *get_pam_cmds(void);
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index e74dd16..529c74f 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -53,6 +53,14 @@ pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
 
 static void pam_reply(struct pam_auth_req *preq);
 
+static errno_t check_cert(TALLOC_CTX *mctx,
+                          struct tevent_context *ev,
+                          struct pam_ctx *pctx,
+                          struct pam_auth_req *preq,
+                          struct pam_data *pd);
+
+static int pam_check_user_done(struct pam_auth_req *preq, int ret);
+
 static errno_t pack_user_info_msg(TALLOC_CTX *mem_ctx,
                                   const char *user_error_message,
                                   size_t *resp_len,
@@ -718,6 +726,28 @@ static void pam_reply(struct pam_auth_req *preq)
     DEBUG(SSSDBG_FUNC_DATA,
           "pam_reply called with result [%d]: %s.\n",
           pd->pam_status, pam_strerror(NULL, pd->pam_status));
+
+    if (pd->cmd == SSS_PAM_AUTHENTICATE
+            && (pd->pam_status == PAM_AUTHINFO_UNAVAIL
+                || pd->pam_status == PAM_NO_MODULE_DATA
+                || pd->pam_status == PAM_BAD_ITEM)
+            && may_do_cert_auth(pctx, pd)) {
+        /* We have Smartcard credentials and the backend indicates that it is
+         * offline (PAM_AUTHINFO_UNAVAIL) or cannot handle the credentials
+         * (PAM_BAD_ITEM), so let's try authentication against the Smartcard
+         * PAM_NO_MODULE_DATA is returned by the krb5 backend if no
+         * authentication method was found at all, this might happen if the
+         * user has a Smartcard assigned but the pkint plugin is not available
+         * on the client. */
+        DEBUG(SSSDBG_IMPORTANT_INFO,
+              "Backend cannot handle Smartcard authentication, "
+              "trying local Smartcard authentication.\n");
+        preq->cert_auth_local = true;
+        ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
+        pam_check_user_done(preq, ret);
+        return;
+    }
+
     if (pd->pam_status == PAM_AUTHINFO_UNAVAIL || preq->use_cached_auth) {
 
         switch(pd->cmd) {
@@ -1019,7 +1049,6 @@ static void pam_forwarder_cert_cb(struct tevent_req *req);
 static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
                                        const char *err_msg, void *ptr);
 static int pam_check_user_search(struct pam_auth_req *preq);
-static int pam_check_user_done(struct pam_auth_req *preq, int ret);
 
 static errno_t pam_cmd_assume_upn(struct pam_auth_req *preq)
 {
@@ -1234,6 +1263,7 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
     }
     talloc_set_destructor(preq, pam_auth_req_destructor);
     preq->cctx = cctx;
+    preq->cert_auth_local = false;
 
     preq->pd = create_pam_data(preq);
     if (!preq->pd) {
@@ -1330,8 +1360,9 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
         }
     }
 
-
-    if (may_do_cert_auth(pctx, pd)) {
+    /* try backend first for authentication before doing local Smartcard
+     * authentication */
+    if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
         /* Finish here */
         goto done;
@@ -1439,6 +1470,12 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
         preq->cert_user_obj = talloc_steal(preq, result->msgs[0]);
 
         if (preq->pd->logon_name == NULL) {
+            if (preq->pd->cmd != SSS_PAM_PREAUTH) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      "Missing logon name only allowed during pre-auth.\n");
+                ret = ENOENT;
+                goto done;
+            }
             cert_user = ldb_msg_find_attr_as_string(preq->cert_user_obj,
                                                     SYSDB_NAME, NULL);
             if (cert_user == NULL) {
@@ -1451,20 +1488,17 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
             DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n",
                                     cert_user);
 
-            ret = add_pam_cert_response(preq->pd, cert_user, preq->token_name);
+            ret = sss_parse_name_for_domains(preq->pd,
+                                             preq->cctx->rctx->domains,
+                                             preq->cctx->rctx->default_domain,
+                                             cert_user,
+                                             &preq->pd->domain,
+                                             &preq->pd->user);
             if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
-            }
-
-            preq->pd->domain = talloc_strdup(preq->pd, result->domain->name);
-            if (preq->pd->domain == NULL) {
-                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-                ret = ENOMEM;
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "sss_parse_name_for_domains failed.\n");
                 goto done;
             }
-            preq->pd->pam_status = PAM_SUCCESS;
-            pam_reply(preq);
-            return;
         }
     } else {
         if (preq->pd->logon_name == NULL) {
@@ -1475,7 +1509,12 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
         }
     }
 
-    ret = pam_check_user_search(preq);
+    if (preq->user_obj == NULL) {
+        ret = pam_check_user_search(preq);
+    } else {
+        ret = EOK;
+    }
+
     if (ret == EOK) {
         pam_dom_forwarder(preq);
     }
@@ -1531,7 +1570,9 @@ static void pam_forwarder_cb(struct tevent_req *req)
         }
     }
 
-    if (may_do_cert_auth(pctx, pd)) {
+    /* try backend first for authentication before doing local Smartcard
+     * authentication */
+    if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
         /* Finish here */
         goto done;
@@ -1985,7 +2026,7 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
                                                 NULL);
         if (cert_user == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Certificate user object has not name.\n");
+                  "Certificate user object has no name.\n");
             preq->pd->pam_status = PAM_USER_UNKNOWN;
             pam_reply(preq);
             return;
@@ -1994,11 +2035,21 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
         /* pam_check_user_search() calls pd_set_primary_name() is the search
          * was successful, so pd->user contains the canonical sysdb name
          * as well */
-        if (strcmp(cert_user, preq->pd->user) == 0) {
-
-            preq->pd->pam_status = PAM_SUCCESS;
+        if (ldb_dn_compare(preq->cert_user_obj->dn, preq->user_obj->dn) == 0) {
 
             if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+                ret = sss_authtok_set_sc(preq->pd->authtok,
+                                         SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
+                                         preq->token_name, 0,
+                                         preq->module_name, 0,
+                                         preq->key_id, 0);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_sc failed, "
+                                             "Smartcard authentication "
+                                             "detection might fail in the "
+                                             "backend.\n");
+                }
+
                 ret = add_pam_cert_response(preq->pd, cert_user,
                                             preq->token_name);
                 if (ret != EOK) {
@@ -2007,9 +2058,14 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
                 }
             }
 
-            preq->callback = pam_reply;
-            pam_reply(preq);
-            return;
+            /* We are done if we do not have to call the backend */
+            if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
+                    && preq->cert_auth_local) {
+                preq->pd->pam_status = PAM_SUCCESS;
+                preq->callback = pam_reply;
+                pam_reply(preq);
+                return;
+            }
         } else {
             if (preq->pd->cmd == SSS_PAM_PREAUTH) {
                 DEBUG(SSSDBG_TRACE_FUNC,
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index e6ed8f5..6b91f49 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -285,6 +285,9 @@ static void pam_test_setup_common(void)
                                                pam_test_ctx->tctx->dom->name);
     assert_non_null(pam_test_ctx->wrong_user_fqdn);
 
+    /* integer values cannot be set by pam_params */
+    pam_test_ctx->pctx->id_timeout = 5;
+
     /* Prime the cache with a valid user */
     ret = sysdb_add_user(pam_test_ctx->tctx->dom,
                          pam_test_ctx->pam_user_fqdn,
@@ -783,6 +786,13 @@ static int test_pam_wrong_pw_offline_auth_check(uint32_t status,
     return test_pam_simple_check(status, body, blen);
 }
 
+static int test_pam_simple_check_success(uint32_t status,
+                                                uint8_t *body, size_t blen)
+{
+    pam_test_ctx->exp_pam_status = PAM_SUCCESS;
+    return test_pam_simple_check(status, body, blen);
+}
+
 static int test_pam_creds_insufficient_check(uint32_t status,
                                              uint8_t *body, size_t blen)
 {
@@ -1752,7 +1762,11 @@ void test_pam_cert_auth(void **state)
     mock_account_recv(0, 0, NULL, test_lookup_by_cert_cb,
                       discard_const(TEST_TOKEN_CERT));
 
-    set_cmd_cb(test_pam_simple_check);
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+
+    set_cmd_cb(test_pam_simple_check_success);
     ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
                           pam_test_ctx->pam_cmds);
     assert_int_equal(ret, EOK);

From 5973789dfee73eea5eeb87fa66086bc48bb5eaee Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 22 Feb 2017 17:58:15 +0100
Subject: [PATCH 08/10] p11: return name of PKCS#11 module and key id to
 pam_sss

---
 src/p11_child/p11_child_nss.c   | 55 ++++++++++++++++++++++++--
 src/responder/pam/pamsrv.h      |  6 ++-
 src/responder/pam/pamsrv_cmd.c  |  8 +++-
 src/responder/pam/pamsrv_p11.c  | 86 ++++++++++++++++++++++++++++++++++++++---
 src/sss_client/pam_message.h    |  2 +
 src/sss_client/pam_sss.c        | 49 ++++++++++++++++++++++-
 src/tests/cmocka/test_pam_srv.c | 33 +++++++++++++++-
 7 files changed, 221 insertions(+), 18 deletions(-)

diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index 8477269..f165b58 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -72,13 +72,15 @@ static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg)
 int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
             enum op_mode mode, const char *pin,
             struct cert_verify_opts *cert_verify_opts,
-            char **cert, char **token_name_out)
+            char **cert, char **token_name_out, char **module_name_out,
+            char **key_id_out)
 {
     int ret;
     SECStatus rv;
     NSSInitContext *nss_ctx;
     SECMODModuleList *mod_list;
     SECMODModuleList *mod_list_item;
+    SECMODModule *module;
     const char *slot_name;
     const char *token_name;
     uint32_t flags = NSS_INIT_READONLY
@@ -91,6 +93,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
     PK11SlotInfo *slot = NULL;
     CK_SLOT_ID slot_id;
     SECMODModuleID module_id;
+    const char *module_name;
     CERTCertList *cert_list = NULL;
     CERTCertListNode *cert_list_node;
     const PK11DefaultArrayEntry friendly_attr = { "Publicly-readable certs",
@@ -105,6 +108,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
     CERTCertificate *found_cert = NULL;
     PK11SlotList *list = NULL;
     PK11SlotListElement *le;
+    SECItem *key_id = NULL;
+    char *key_id_str = NULL;
 
 
     nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
@@ -187,8 +192,11 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
     module_id = PK11_GetModuleID(slot);
     slot_name = PK11_GetSlotName(slot);
     token_name = PK11_GetTokenName(slot);
-    DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d].\n",
-          token_name, slot_name, (int) slot_id, (int) module_id);
+    module = PK11_GetModule(slot);
+    module_name = module->dllName == NULL ? "NSS-Internal" : module->dllName;
+
+    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);
 
     if (PK11_IsFriendly(slot)) {
         DEBUG(SSSDBG_TRACE_ALL, "Token is friendly.\n");
@@ -346,6 +354,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
         DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
         *cert = NULL;
         *token_name_out = NULL;
+        *module_name_out = NULL;
         ret = EOK;
         goto done;
     }
@@ -412,6 +421,30 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
               "Certificate verified and validated.\n");
     }
 
+    key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
+    if (key_id == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
+                                 PR_GetError());
+        ret = EINVAL;
+        goto done;
+    }
+
+    key_id_str = CERT_Hexify(key_id, PR_FALSE);
+    if (key_id_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError());
+        ret = ENOMEM;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n", key_id_str);
+
+    *key_id_out = talloc_strdup(mem_ctx, key_id_str);
+    if (*key_id_out == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy key id.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
     *cert = sss_base64_encode(mem_ctx, found_cert->derCert.data,
                                        found_cert->derCert.len);
     if (*cert == NULL) {
@@ -427,6 +460,13 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
         goto done;
     }
 
+    *module_name_out = talloc_strdup(mem_ctx, module_name);
+    if (*module_name_out == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy module_name_out name.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
     ret = EOK;
 
 done:
@@ -438,6 +478,9 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
         CERT_DestroyCertList(cert_list);
     }
 
+    SECITEM_FreeItem(key_id, PR_TRUE);
+    PORT_Free(key_id_str);
+
     PORT_Free(signed_random_value.data);
 
     rv = NSS_ShutdownContext(nss_ctx);
@@ -502,6 +545,8 @@ int main(int argc, const char *argv[])
     char *pin = NULL;
     char *slot_name_in = NULL;
     char *token_name_out = NULL;
+    char *module_name_out = NULL;
+    char *key_id_out = NULL;
     char *nss_db = NULL;
     struct cert_verify_opts *cert_verify_opts;
     char *verify_opts = NULL;
@@ -665,7 +710,7 @@ int main(int argc, const char *argv[])
     }
 
     ret = do_work(main_ctx, nss_db, slot_name_in, mode, pin, cert_verify_opts,
-                  &cert, &token_name_out);
+                  &cert, &token_name_out, &module_name_out, &key_id_out);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
         goto fail;
@@ -673,6 +718,8 @@ int main(int argc, const char *argv[])
 
     if (cert != NULL) {
         fprintf(stdout, "%s\n", token_name_out);
+        fprintf(stdout, "%s\n", module_name_out);
+        fprintf(stdout, "%s\n", key_id_out);
         fprintf(stdout, "%s\n", cert);
     }
 
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 389888e..7860a99 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -92,10 +92,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
                                        const char *verify_opts,
                                        struct pam_data *pd);
 errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-                            char **cert, char **token_name);
+                            char **cert, char **token_name, char **module_name,
+                            char **key_id);
 
 errno_t add_pam_cert_response(struct pam_data *pd, const char *user,
-                              const char *token_name);
+                              const char *token_name, const char *module_name,
+                              const char *key_id);
 
 bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
 
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 529c74f..6b7a949 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1401,7 +1401,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
     struct pam_ctx *pctx =
             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
 
-    ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name);
+    ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name,
+                                                &preq->module_name,
+                                                &preq->key_id);
     talloc_free(req);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n");
@@ -2051,7 +2053,9 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
                 }
 
                 ret = add_pam_cert_response(preq->pd, cert_user,
-                                            preq->token_name);
+                                            preq->token_name,
+                                            preq->module_name,
+                                            preq->key_id);
                 if (ret != EOK) {
                     DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
                     preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 570bfe0..365300b 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -133,7 +133,8 @@ static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
 
 static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
                                         ssize_t buf_len, char **_cert,
-                                        char **_token_name)
+                                        char **_token_name, char **_module_name,
+                                        char **_key_id)
 {
     int ret;
     TALLOC_CTX *tmp_ctx = NULL;
@@ -141,6 +142,8 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
     uint8_t *pn;
     char *cert = NULL;
     char *token_name = NULL;
+    char *module_name = NULL;
+    char *key_id = NULL;
 
     if (buf_len < 0) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -187,6 +190,54 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
     }
 
     if (pn == p) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Missing module name in p11_child response.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    module_name = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
+    if (module_name == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+    DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n", module_name);
+
+    p = ++pn;
+    pn = memchr(p, '\n', buf_len - (p - buf));
+    if (pn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Missing new-line in p11_child response.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (pn == p) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Missing key id in p11_child response.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    key_id = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
+    if (key_id == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+    DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", key_id);
+
+    p = pn + 1;
+    pn = memchr(p, '\n', buf_len - (p - buf));
+    if (pn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Missing new-line in p11_child response.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (pn == p) {
         DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
         ret = EINVAL;
         goto done;
@@ -206,6 +257,8 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
     if (ret == EOK) {
         *_token_name = talloc_steal(mem_ctx, token_name);
         *_cert = talloc_steal(mem_ctx, cert);
+        *_module_name = talloc_steal(mem_ctx, module_name);
+        *_key_id = talloc_steal(mem_ctx, key_id);
     }
 
     talloc_free(tmp_ctx);
@@ -222,6 +275,8 @@ struct pam_check_cert_state {
     struct child_io_fds *io;
     char *cert;
     char *token_name;
+    char *module_name;
+    char *key_id;
 };
 
 static void p11_child_write_done(struct tevent_req *subreq);
@@ -296,6 +351,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     state->child_status = EFAULT;
     state->cert = NULL;
     state->token_name = NULL;
+    state->module_name = NULL;
     state->io = talloc(state, struct child_io_fds);
     if (state->io == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
@@ -459,7 +515,8 @@ static void p11_child_done(struct tevent_req *subreq)
     PIPE_FD_CLOSE(state->io->read_from_child_fd);
 
     ret = parse_p11_child_response(state, buf, buf_len, &state->cert,
-                                   &state->token_name);
+                                   &state->token_name, &state->module_name,
+                                   &state->key_id);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_respose failed.\n");
         tevent_req_error(req, ret);
@@ -486,7 +543,8 @@ static void p11_child_timeout(struct tevent_context *ev,
 }
 
 errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-                            char **cert, char **token_name)
+                            char **cert, char **token_name, char **module_name,
+                            char **key_id)
 {
     struct pam_check_cert_state *state =
                               tevent_req_data(req, struct pam_check_cert_state);
@@ -501,6 +559,14 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
         *token_name = talloc_steal(mem_ctx, state->token_name);
     }
 
+    if (module_name != NULL) {
+        *module_name = talloc_steal(mem_ctx, state->module_name);
+    }
+
+    if (key_id != NULL) {
+        *key_id = talloc_steal(mem_ctx, state->key_id);
+    }
+
     return EOK;
 }
 
@@ -513,23 +579,29 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
 
 errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-                              const char *token_name)
+                              const char *token_name, const char *module_name,
+                              const char *key_id)
 {
     uint8_t *msg = NULL;
     char *env = NULL;
     size_t user_len;
     size_t msg_len;
     size_t slot_len;
+    size_t module_len;
+    size_t key_id_len;
     int ret;
 
-    if (sysdb_username == NULL || token_name == NULL) {
+    if (sysdb_username == NULL || token_name == NULL || module_name == NULL
+            || key_id == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
         return EINVAL;
     }
 
     user_len = strlen(sysdb_username) + 1;
     slot_len = strlen(token_name) + 1;
-    msg_len = user_len + slot_len;
+    module_len = strlen(module_name) + 1;
+    key_id_len = strlen(key_id) + 1;
+    msg_len = user_len + slot_len + module_len + key_id_len;
 
     msg = talloc_zero_size(pd, msg_len);
     if (msg == NULL) {
@@ -546,6 +618,8 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
      * being I think using sysdb_username is fine. */
     memcpy(msg, sysdb_username, user_len);
     memcpy(msg + user_len, token_name, slot_len);
+    memcpy(msg + user_len + slot_len, module_name, module_len);
+    memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len);
 
     ret = pam_add_response(pd, SSS_PAM_CERT_INFO, msg_len, msg);
     talloc_free(msg);
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
index 34889e0..3f4a770 100644
--- a/src/sss_client/pam_message.h
+++ b/src/sss_client/pam_message.h
@@ -61,6 +61,8 @@ struct pam_items {
 
     char *cert_user;
     char *token_name;
+    char *module_name;
+    char *key_id;
 };
 
 int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer);
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 8f97af7..fa30889 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -162,6 +162,12 @@ static void overwrite_and_free_pam_items(struct pam_items *pi)
 
     free(pi->token_name);
     pi->token_name = NULL;
+
+    free(pi->module_name);
+    pi->module_name = NULL;
+
+    free(pi->key_id);
+    pi->key_id = NULL;
 }
 
 static int null_strcmp(const char *s1, const char *s2) {
@@ -1019,10 +1025,47 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
                 pi->token_name = strdup((char *) &buf[p + offset]);
                 if (pi->token_name == NULL) {
                     D(("strdup failed"));
+                    free(pi->cert_user);
+                    pi->cert_user = NULL;
+                    break;
+                }
+
+                offset += strlen(pi->token_name) + 1;
+                if (offset >= len) {
+                    D(("Cert message size mismatch"));
+                    free(pi->cert_user);
+                    pi->cert_user = NULL;
+                    free(pi->token_name);
+                    pi->token_name = NULL;
+                    break;
+                }
+                free(pi->module_name);
+                pi->module_name = strdup((char *) &buf[p + offset]);
+                if (pi->module_name == NULL) {
+                    D(("strdup failed"));
+                    break;
+                }
+
+                offset += strlen(pi->module_name) + 1;
+                if (offset >= len) {
+                    D(("Cert message size mismatch"));
+                    free(pi->cert_user);
+                    pi->cert_user = NULL;
+                    free(pi->token_name);
+                    pi->token_name = NULL;
+                    free(pi->module_name);
+                    pi->module_name = NULL;
+                    break;
+                }
+                free(pi->key_id);
+                pi->key_id = strdup((char *) &buf[p + offset]);
+                if (pi->key_id == NULL) {
+                    D(("strdup failed"));
                     break;
                 }
-                D(("cert user: [%s] token name: [%s]", pi->cert_user,
-                                                       pi->token_name));
+                D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
+                    pi->cert_user, pi->token_name, pi->module_name,
+                    pi->key_id));
                 break;
             case SSS_PASSWORD_PROMPTING:
                 D(("Password prompting available."));
@@ -1120,6 +1163,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
 
     pi->cert_user = NULL;
     pi->token_name = NULL;
+    pi->module_name = NULL;
+    pi->key_id = NULL;
 
     return PAM_SUCCESS;
 }
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 6b91f49..cbc1d03 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -48,6 +48,8 @@
 #define NSS_DB "sql:"NSS_DB_PATH
 
 #define TEST_TOKEN_NAME "SSSD Test Token"
+#define TEST_MODULE_NAME "NSS-Internal"
+#define TEST_KEY_ID "6822EDDD231DAB8DBCD83721BE16A13DD0E31C08"
 #define TEST_TOKEN_CERT \
 "MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
 "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \
@@ -668,7 +670,10 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
     assert_int_equal(val, SSS_PAM_CERT_INFO);
 
     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) + sizeof(TEST_TOKEN_NAME)));
+    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME)
+                                + sizeof(TEST_TOKEN_NAME)
+                                + sizeof(TEST_MODULE_NAME)
+                                + sizeof(TEST_KEY_ID)));
 
     assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
     assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
@@ -676,7 +681,17 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
 
     assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
     assert_string_equal(body + rp, TEST_TOKEN_NAME);
+    rp += sizeof(TEST_TOKEN_NAME);
 
+    assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
+    assert_string_equal(body + rp, TEST_MODULE_NAME);
+    rp += sizeof(TEST_MODULE_NAME);
+
+    assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
+    assert_string_equal(body + rp, TEST_KEY_ID);
+    rp += sizeof(TEST_KEY_ID);
+
+    assert_int_equal(rp, blen);
     return EOK;
 }
 
@@ -707,7 +722,10 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
     assert_int_equal(val, SSS_PAM_CERT_INFO);
 
     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) + sizeof(TEST_TOKEN_NAME)));
+    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME)
+                                + sizeof(TEST_TOKEN_NAME)
+                                + sizeof(TEST_MODULE_NAME)
+                                + sizeof(TEST_KEY_ID)));
 
     assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
     assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
@@ -715,6 +733,17 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
 
     assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
     assert_string_equal(body + rp, TEST_TOKEN_NAME);
+    rp += sizeof(TEST_TOKEN_NAME);
+
+    assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
+    assert_string_equal(body + rp, TEST_MODULE_NAME);
+    rp += sizeof(TEST_MODULE_NAME);
+
+    assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
+    assert_string_equal(body + rp, TEST_KEY_ID);
+    rp += sizeof(TEST_KEY_ID);
+
+    assert_int_equal(rp, blen);
 
     return EOK;
 }

From c3e36391fe390311ffc408fd26fb23957494d974 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 27 Sep 2016 16:03:22 +0200
Subject: [PATCH 09/10] pam: enhance Smartcard authentication token

---
 src/providers/krb5/krb5_child.c         |  4 ++-
 src/providers/krb5/krb5_child_handler.c |  2 ++
 src/responder/pam/pamsrv_cmd.c          |  9 ++-----
 src/sss_client/pam_sss.c                | 45 ++++++++++++++++++++++++++++-----
 4 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index be31ddb..0318470 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -1856,7 +1856,9 @@ static errno_t unpack_authtok(struct sss_auth_token *tok,
         ret = sss_authtok_set_ccfile(tok, (char *)(buf + *p), 0);
         break;
     case SSS_AUTHTOK_TYPE_2FA:
-        ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, (buf + *p),
+    case SSS_AUTHTOK_TYPE_SC_PIN:
+    case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+        ret = sss_authtok_set(tok, auth_token_type, (buf + *p),
                               auth_token_length);
         break;
     default:
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 69636e0..6a3dc9d 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -78,6 +78,8 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp,
         auth_token_length = len + 1;
         break;
     case SSS_AUTHTOK_TYPE_2FA:
+    case SSS_AUTHTOK_TYPE_SC_PIN:
+    case SSS_AUTHTOK_TYPE_SC_KEYPAD:
         data = (char *) sss_authtok_get_data(tok);
         auth_token_length = sss_authtok_get_size(tok);
         break;
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 6b7a949..e788a75 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -160,15 +160,10 @@ static int extract_authtok_v2(struct sss_auth_token *tok,
         }
         break;
     case SSS_AUTHTOK_TYPE_2FA:
-        ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA,
-                              auth_token_data, auth_token_length);
-        break;
     case SSS_AUTHTOK_TYPE_SC_PIN:
-        ret = sss_authtok_set_sc_pin(tok, (const char *) auth_token_data,
-                                     auth_token_length);
-        break;
     case SSS_AUTHTOK_TYPE_SC_KEYPAD:
-        sss_authtok_set_sc_keypad(tok);
+        ret = sss_authtok_set(tok, auth_token_type,
+                              auth_token_data, auth_token_length);
         break;
     default:
         return EINVAL;
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index fa30889..a3d7a8a 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -1476,6 +1476,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
     char *answer = NULL;
     char *prompt;
     size_t size;
+    size_t needed_size;
 
     if (pi->token_name == NULL || *pi->token_name == '\0'
             || pi->cert_user == NULL || *pi->cert_user == '\0') {
@@ -1509,18 +1510,48 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
         pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY;
         pi->pam_authtok_size=0;
     } else {
-        pi->pam_authtok = strdup(answer);
-        _pam_overwrite((void *)answer);
-        free(answer);
-        answer=NULL;
+
+        ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
+                                    pi->module_name, 0,
+                                    pi->key_id, 0,
+                                    NULL, 0, &needed_size);
+        if (ret != EAGAIN) {
+            D(("sss_auth_pack_sc_blob failed."));
+            ret = PAM_BUF_ERR;
+            goto done;
+        }
+
+        pi->pam_authtok = malloc(needed_size);
         if (pi->pam_authtok == NULL) {
-            return PAM_BUF_ERR;
+            D(("malloc failed."));
+            ret = PAM_BUF_ERR;
+            goto done;
         }
+
+        ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
+                                    pi->module_name, 0,
+                                    pi->key_id, 0,
+                                    (uint8_t *) pi->pam_authtok, needed_size,
+                                    &needed_size);
+        if (ret != EOK) {
+            D(("sss_auth_pack_sc_blob failed."));
+            free((void *)pi->pam_authtok);
+            ret = PAM_BUF_ERR;
+            goto done;
+        }
+
         pi->pam_authtok_type = SSS_AUTHTOK_TYPE_SC_PIN;
-        pi->pam_authtok_size=strlen(pi->pam_authtok);
+        pi->pam_authtok_size = needed_size;
     }
 
-    return PAM_SUCCESS;
+    ret = PAM_SUCCESS;
+
+done:
+    _pam_overwrite((void *)answer);
+    free(answer);
+    answer=NULL;
+
+    return ret;
 }
 
 static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi)

From 24e5ec2b3d70ad5c7d1508abe4b1ddd016b702aa Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 12 Oct 2015 11:52:56 +0200
Subject: [PATCH 10/10] KRB5: allow pkinit pre-authentication

---
 src/providers/krb5/krb5_auth.c          |  18 +-
 src/providers/krb5/krb5_child.c         | 286 ++++++++++++++++++++++++++++++--
 src/providers/krb5/krb5_child_handler.c |   6 +-
 src/sss_client/sss_cli.h                |   6 +
 4 files changed, 303 insertions(+), 13 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 0e68561..c2d6d7e 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -344,8 +344,13 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain,
                           domain->cache_credentials_min_ff_length);
                     ret = EINVAL;
                 }
-            } else {
+            } else if (sss_authtok_get_type(pd->authtok) ==
+                                                    SSS_AUTHTOK_TYPE_PASSWORD) {
                 ret = sss_authtok_get_password(pd->authtok, &password, NULL);
+            } else {
+                DEBUG(SSSDBG_MINOR_FAILURE, "Cannot cache authtok type [%d].\n",
+                      sss_authtok_get_type(pd->authtok));
+                ret = EINVAL;
             }
             break;
         case SSS_PAM_CHAUTHTOK:
@@ -466,7 +471,9 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
         case SSS_PAM_AUTHENTICATE:
         case SSS_PAM_CHAUTHTOK:
             if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD
-                    && authtok_type != SSS_AUTHTOK_TYPE_2FA) {
+                    && authtok_type != SSS_AUTHTOK_TYPE_2FA
+                    && authtok_type != SSS_AUTHTOK_TYPE_SC_PIN
+                    && authtok_type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
                 /* handle empty password gracefully */
                 if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) {
                     DEBUG(SSSDBG_CRIT_FAILURE,
@@ -1023,6 +1030,12 @@ static void krb5_auth_done(struct tevent_req *subreq)
         ret = EOK;
         goto done;
 
+    case ERR_NO_AUTH_METHOD_AVAILABLE:
+        state->pam_status = PAM_NO_MODULE_DATA;
+        state->dp_err = DP_ERR_OK;
+        ret = EOK;
+        goto done;
+
     default:
         DEBUG(SSSDBG_IMPORTANT_INFO,
               "The krb5_child process returned an error. Please inspect the "
@@ -1185,6 +1198,7 @@ krb5_pam_handler_send(TALLOC_CTX *mem_ctx,
 
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
+        case SSS_PAM_PREAUTH:
         case SSS_CMD_RENEW:
         case SSS_PAM_CHAUTHTOK_PRELIM:
         case SSS_PAM_CHAUTHTOK:
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 0318470..777a25f 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -64,6 +64,7 @@ struct krb5_req {
     krb5_creds *creds;
     bool otp;
     bool password_prompting;
+    bool pkinit_prompting;
     char *otp_vendor;
     char *otp_token_id;
     char *otp_challenge;
@@ -587,6 +588,138 @@ static krb5_error_code answer_otp(krb5_context ctx,
     return ret;
 }
 
+static bool pkinit_identity_matches(const char *identity,
+                                    const char *token_name,
+                                    const char *module_name)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *str;
+    bool res = false;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+        return false;
+    }
+
+    str = talloc_asprintf(tmp_ctx, "module_name=%s", module_name);
+    if (str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+        goto done;
+    }
+
+    if (strstr(identity, str) == NULL) {
+        DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n",
+                                identity, str);
+        goto done;
+    }
+    DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity);
+
+    str = talloc_asprintf(tmp_ctx, "token=%s", token_name);
+    if (str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+        goto done;
+    }
+
+    if (strstr(identity, str) == NULL) {
+        DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n",
+                                identity, str);
+        goto done;
+    }
+    DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity);
+
+    res = true;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return res;
+}
+
+static krb5_error_code answer_pkinit(krb5_context ctx,
+                                     struct krb5_req *kr,
+                                     krb5_responder_context rctx)
+{
+    krb5_error_code kerr;
+    const char *pin = NULL;
+    const char *token_name = NULL;
+    const char *module_name = NULL;
+    krb5_responder_pkinit_challenge *chl = NULL;
+    size_t c;
+
+    kerr = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
+    if (kerr != EOK || chl == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "krb5_responder_pkinit_get_challenge failed.\n");
+        return kerr;
+    }
+    if (chl->identities == NULL || chl->identities[0] == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No identities for pkinit!\n");
+        kerr = EINVAL;
+        goto done;
+    }
+
+    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
+        for (c = 0; chl->identities[c] != NULL; c++) {
+            DEBUG(SSSDBG_TRACE_ALL, "[%zu] Identity [%s] flags [%"PRId32"].\n",
+                                    c, chl->identities[c]->identity,
+                                    chl->identities[c]->token_flags);
+        }
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "Setting pkinit_prompting.\n");
+    kr->pkinit_prompting = true;
+
+    if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
+            && (sss_authtok_get_type(kr->pd->authtok)
+                    == SSS_AUTHTOK_TYPE_SC_PIN
+                || sss_authtok_get_type(kr->pd->authtok)
+                    == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
+        kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
+                                 &token_name, NULL,
+                                 &module_name, NULL,
+                                 NULL, NULL);
+        if (kerr != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "sss_authtok_get_sc failed.\n");
+            goto done;
+        }
+
+        for (c = 0; chl->identities[c] != NULL; c++) {
+            if (chl->identities[c]->identity != NULL
+                    && pkinit_identity_matches(chl->identities[c]->identity,
+                                               token_name, module_name)) {
+                break;
+            }
+        }
+
+        if (chl->identities[c] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "No matching identity for [%s][%s] found in pkinit challenge.\n",
+                  token_name, module_name);
+            kerr = EINVAL;
+            goto done;
+        }
+
+        kerr = krb5_responder_pkinit_set_answer(ctx, rctx,
+                                                chl->identities[c]->identity,
+                                                pin);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "krb5_responder_set_answer failed.\n");
+        }
+
+        goto done;
+    }
+
+    kerr = EOK;
+
+done:
+    krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
+
+    return kerr;
+}
+
 static krb5_error_code sss_krb5_responder(krb5_context ctx,
                                           void *data,
                                           krb5_responder_context rctx)
@@ -634,6 +767,9 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
 
                     return kerr;
                 }
+            } else if (strcmp(question_list[c],
+                       KRB5_RESPONDER_QUESTION_PKINIT) == 0) {
+                return answer_pkinit(ctx, kr, rctx);
             }
         }
     }
@@ -661,18 +797,23 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
     size_t c;
     struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
 
+    if (kr == NULL) {
+        return EINVAL;
+    }
+
     DEBUG(SSSDBG_TRACE_ALL,
           "sss_krb5_prompter name [%s] banner [%s] num_prompts [%d] EINVAL.\n",
           name, banner, num_prompts);
 
     if (num_prompts != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
         if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
             for (c = 0; c < num_prompts; c++) {
                 DEBUG(SSSDBG_TRACE_ALL, "Prompt [%zu][%s].\n", c,
                                         prompts[c].prompt);
             }
         }
+
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
         return KRB5_LIBOS_CANTREADPWD;
     }
 
@@ -1036,6 +1177,63 @@ static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error)
     return EOK;
 }
 
+static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
+                                   struct sss_auth_token *authtok,
+                                   char **_identity)
+{
+    int ret;
+    char *identity;
+    const char *token_name;
+    const char *module_name;
+    const char *key_id;
+
+    ret = sss_authtok_get_sc(authtok, NULL, NULL,
+                             &token_name, NULL,
+                             &module_name, NULL,
+                             &key_id, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+        return ret;
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "Got [%s][%s].\n", token_name, module_name);
+
+    if (module_name == NULL || *module_name == '\0') {
+        module_name = "p11-kit-proxy.so";
+    }
+
+    identity = talloc_asprintf(mem_ctx, "PKCS11:module_name=%s", module_name);
+    if (identity == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+        return ENOMEM;
+    }
+
+    if (token_name != NULL && *token_name != '\0') {
+        identity = talloc_asprintf_append(identity, ":token=%s",
+                                                    token_name);
+        if (identity == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "talloc_asprintf_append failed.\n");
+            return ENOMEM;
+        }
+    }
+
+    if (key_id != NULL && *key_id != '\0') {
+        identity = talloc_asprintf_append(identity, ":certid=%s", key_id);
+        if (identity == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "talloc_asprintf_append failed.\n");
+            return ENOMEM;
+        }
+    }
+
+    *_identity = identity;
+
+    DEBUG(SSSDBG_TRACE_ALL, "Using pkinit identity [%s].\n", identity);
+
+    return EOK;
+}
+
 static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr)
 {
     int ret;
@@ -1268,6 +1466,8 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
     int realm_length;
     krb5_error_code kerr;
     char *cc_name;
+    int ret;
+    char *identity = NULL;
 
     kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options,
                                                   sss_krb5_expire_callback_func,
@@ -1284,6 +1484,30 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         return KRB5KRB_ERR_GENERIC;
     }
 
+    if (sss_authtok_get_type(kr->pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+            || sss_authtok_get_type(kr->pd->authtok)
+                                                == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Found Smartcard credentials, trying pkinit.\n");
+
+        ret = get_pkinit_identity(kr, kr->pd->authtok, &identity);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "get_pkinit_identity failed.\n");
+            return ret;
+        }
+
+        kerr = krb5_get_init_creds_opt_set_pa(kr->ctx, kr->options,
+                                              "X509_user_identity", identity);
+        talloc_free(identity);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "krb5_get_init_creds_opt_set_pa failed.\n");
+            return kerr;
+        }
+
+        /* TODO: Maybe X509_anchors should be added here as well */
+    }
+
     DEBUG(SSSDBG_TRACE_FUNC,
           "Attempting kinit for realm [%s]\n",realm_name);
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
@@ -1294,12 +1518,25 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         /* Any errors are ignored during pre-auth, only data is collected to
          * be send back to the client.*/
         DEBUG(SSSDBG_TRACE_FUNC,
-              "krb5_get_init_creds_password returned [%d} during pre-auth.\n",
+              "krb5_get_init_creds_password returned [%d] during pre-auth.\n",
               kerr);
         return 0;
     } else {
         if (kerr != 0) {
             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+
+            /* If during authentication either the MIT Kerberos pkinit
+             * pre-auth module is missing or no Smartcard is inserted and only
+             * pkinit is available KRB5_PREAUTH_FAILED is returned.
+             * ERR_NO_AUTH_METHOD_AVAILABLE is used to indicate to the
+             * frontend that local authentication might be tried. */
+            if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
+                    && kerr == KRB5_PREAUTH_FAILED
+                    && kr->password_prompting == false
+                    && kr->otp == false
+                    && kr->pkinit_prompting == false) {
+                return ERR_NO_AUTH_METHOD_AVAILABLE;
+            }
             return kerr;
         }
     }
@@ -1367,6 +1604,12 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
 
 static errno_t map_krb5_error(krb5_error_code kerr)
 {
+    /* just pass SSSD's internal error codes */
+    if (kerr > 0 && IS_SSSD_ERROR(kerr)) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "[%d][%s].\n", kerr, sss_strerror(kerr));
+        return kerr;
+    }
+
     if (kerr != 0) {
         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     }
@@ -1615,9 +1858,12 @@ static errno_t tgt_req_child(struct krb5_req *kr)
 
     DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n");
 
-    /* No password is needed for pre-auth, or if we have 2FA */
+    /* No password is needed for pre-auth or if we have 2FA or SC */
     if (kr->pd->cmd != SSS_PAM_PREAUTH
-            && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) {
+            && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA
+            && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
+            && sss_authtok_get_type(kr->pd->authtok)
+                                                != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
         ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
         switch (ret) {
         case EOK:
@@ -1641,7 +1887,12 @@ static errno_t tgt_req_child(struct krb5_req *kr)
         if (kr->pd->cmd == SSS_PAM_PREAUTH) {
             /* add OTP tokeninfo messge if available */
             if (kr->otp) {
-                kerr = k5c_attach_otp_info_msg(kr);
+                ret = k5c_attach_otp_info_msg(kr);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "k5c_attach_otp_info_msg failed.\n");
+                    goto done;
+                }
             }
 
             if (kr->password_prompting) {
@@ -1651,6 +1902,15 @@ static errno_t tgt_req_child(struct krb5_req *kr)
                     goto done;
                 }
             }
+
+            if (kr->pkinit_prompting) {
+                ret = pam_add_response(kr->pd, SSS_CERT_AUTH_PROMPTING, 0,
+                                       NULL);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+                    goto done;
+                }
+            }
         } else {
             if (kerr == 0) {
                 kerr = k5c_attach_ccname_msg(kr);
@@ -1918,6 +2178,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
            *offline ? "true" : "false", kr->upn ? kr->upn : "none");
 
     if (pd->cmd == SSS_PAM_AUTHENTICATE ||
+        pd->cmd == SSS_PAM_PREAUTH ||
         pd->cmd == SSS_CMD_RENEW ||
         pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) {
         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
@@ -2801,11 +3062,16 @@ int main(int argc, const char *argv[])
         goto done;
     }
 
-    kerr = become_user(kr->uid, kr->gid);
-    if (kerr != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
-        ret = EFAULT;
-        goto done;
+    /* pkinit need access to pcscd */
+    if ((sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
+            && sss_authtok_get_type(kr->pd->authtok)
+                                        != SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
+        kerr = become_user(kr->uid, kr->gid);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
+            ret = EFAULT;
+            goto done;
+        }
     }
 
     DEBUG(SSSDBG_TRACE_INTERNAL,
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 6a3dc9d..680e67b 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -90,7 +90,9 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp,
     if (ret == EOK) {
         SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_type, rp);
         SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_length, rp);
-        safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp);
+        if (data != NULL) {
+            safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp);
+        }
     }
 
     return ret;
@@ -145,6 +147,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     buf->size = 8*sizeof(uint32_t) + strlen(kr->upn);
 
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+        kr->pd->cmd == SSS_PAM_PREAUTH ||
         kr->pd->cmd == SSS_CMD_RENEW ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
@@ -187,6 +190,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp);
 
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+        kr->pd->cmd == SSS_PAM_PREAUTH ||
         kr->pd->cmd == SSS_CMD_RENEW ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index b6610bc..8091e11 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -431,6 +431,12 @@ enum response_type {
                              * SSS_PAM_OTP_INFO to determine the type of
                              * prompting. There is no message.
                              * @param None. */
+    SSS_CERT_AUTH_PROMPTING, /**< Indicates that on the server side
+                              * Smartcard/certificate based authentication is
+                              * available for the selected account. This might
+                              * be used together with other prompting options
+                              * to determine the type of prompting.
+                              * @param None. */
 };
 
 /**
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to