Blame SOURCES/0041-PAM-allow-missing-logon_name-during-certificate-auth.patch

9f2ebf
From f6e57537cbeaf6e3f313e700f08e0022a32a7d6c Mon Sep 17 00:00:00 2001
9f2ebf
From: Sumit Bose <sbose@redhat.com>
9f2ebf
Date: Mon, 30 Oct 2017 17:11:56 +0100
9f2ebf
Subject: [PATCH 41/46] PAM: allow missing logon_name during certificate
9f2ebf
 authentication
9f2ebf
MIME-Version: 1.0
9f2ebf
Content-Type: text/plain; charset=UTF-8
9f2ebf
Content-Transfer-Encoding: 8bit
9f2ebf
9f2ebf
If only one certificate is available and the logon_name is the user is
9f2ebf
not given the PAM responder already tried to find the name during the
9f2ebf
pre-auth step. With multiple certificates this might cause useless extra
9f2ebf
effort and the name should be determined after the certificate is
9f2ebf
selected in the authentication step. This might currently only happen
9f2ebf
with GDM because all other PAM clients will prompt for the user name
9f2ebf
unconditionally.
9f2ebf
9f2ebf
New unit tests are added to cover this new case.
9f2ebf
9f2ebf
Related to https://pagure.io/SSSD/sssd/issue/3560
9f2ebf
9f2ebf
Reviewed-by: Fabiano FidĂȘncio <fidencio@redhat.com>
9f2ebf
Tested-by: Scott Poore <spoore@redhat.com>
9f2ebf
(cherry picked from commit fd6f4047b58686bd4057c9859c3c804a77b136d8)
9f2ebf
---
9f2ebf
 src/responder/pam/pamsrv_cmd.c  | 63 ++++++++++++++++++++++++++-----
9f2ebf
 src/tests/cmocka/test_pam_srv.c | 82 +++++++++++++++++++++++++++++++++++++++++
9f2ebf
 2 files changed, 135 insertions(+), 10 deletions(-)
9f2ebf
9f2ebf
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
9f2ebf
index 8b2c086e206796ad4c977495be957c56b3255e7f..caf6c99489b8378d2e850473191223709920cd79 100644
9f2ebf
--- a/src/responder/pam/pamsrv_cmd.c
9f2ebf
+++ b/src/responder/pam/pamsrv_cmd.c
9f2ebf
@@ -1151,6 +1151,7 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
9f2ebf
     size_t blen;
9f2ebf
     errno_t ret;
9f2ebf
     uint32_t terminator;
9f2ebf
+    const char *key_id;
9f2ebf
 
9f2ebf
     prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
9f2ebf
 
9f2ebf
@@ -1191,9 +1192,33 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
9f2ebf
                                          pd->logon_name,
9f2ebf
                                          &pd->domain, &pd->user);
9f2ebf
     } else {
9f2ebf
-        /* Only SSS_PAM_PREAUTH request may have a missing name, e.g. if the
9f2ebf
-         * name is determined with the help of a certificate */
9f2ebf
-        if (pd->cmd == SSS_PAM_PREAUTH
9f2ebf
+        /* SSS_PAM_PREAUTH request may have a missing name, e.g. if the
9f2ebf
+         * name is determined with the help of a certificate. During
9f2ebf
+         * SSS_PAM_AUTHENTICATE at least a key ID is needed to identify the
9f2ebf
+         * selected certificate. */
9f2ebf
+        if (pd->cmd == SSS_PAM_AUTHENTICATE
9f2ebf
+                && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
9f2ebf
+                                                    struct pam_ctx), pd)
9f2ebf
+                && (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
9f2ebf
+                    || sss_authtok_get_type(pd->authtok)
9f2ebf
+                                               == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
9f2ebf
+            ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, NULL, NULL, NULL,
9f2ebf
+                                     NULL, &key_id, NULL);
9f2ebf
+            if (ret != EOK) {
9f2ebf
+                DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
9f2ebf
+                goto done;
9f2ebf
+            }
9f2ebf
+
9f2ebf
+            if (key_id == NULL || *key_id == '\0') {
9f2ebf
+                DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
+                      "Missing logon and Smartcard key ID during "
9f2ebf
+                      "authentication.\n");
9f2ebf
+                ret = ERR_NO_CREDS;
9f2ebf
+                goto done;
9f2ebf
+            }
9f2ebf
+
9f2ebf
+            ret = EOK;
9f2ebf
+        } else if (pd->cmd == SSS_PAM_PREAUTH
9f2ebf
                 && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
9f2ebf
                                                     struct pam_ctx), pd)) {
9f2ebf
             ret = EOK;
9f2ebf
@@ -1375,9 +1400,12 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
9f2ebf
     /* Determine what domain type to contact */
9f2ebf
     preq->req_dom_type = get_domain_request_type(preq, pctx);
9f2ebf
 
9f2ebf
-    /* try backend first for authentication before doing local Smartcard
9f2ebf
-     * authentication */
9f2ebf
-    if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
9f2ebf
+    /* Try backend first for authentication before doing local Smartcard
9f2ebf
+     * authentication if a logon name is available. Otherwise try to derive
9f2ebf
+     * the logon name from the certificate first. */
9f2ebf
+    if ((pd->cmd != SSS_PAM_AUTHENTICATE
9f2ebf
+                || (pd->cmd == SSS_PAM_AUTHENTICATE && pd->logon_name == NULL))
9f2ebf
+            && may_do_cert_auth(pctx, pd)) {
9f2ebf
         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
9f2ebf
         /* Finish here */
9f2ebf
         goto done;
9f2ebf
@@ -1577,9 +1605,10 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
     } else {
9f2ebf
 
9f2ebf
         if (preq->pd->logon_name == NULL) {
9f2ebf
-            if (preq->pd->cmd != SSS_PAM_PREAUTH) {
9f2ebf
+            if (preq->pd->cmd != SSS_PAM_PREAUTH
9f2ebf
+                    && preq->pd->cmd != SSS_PAM_AUTHENTICATE) {
9f2ebf
                 DEBUG(SSSDBG_CRIT_FAILURE,
9f2ebf
-                      "Missing logon name only allowed during pre-auth.\n");
9f2ebf
+                      "Missing logon name only allowed during (pre-)auth.\n");
9f2ebf
                 ret = ENOENT;
9f2ebf
                 goto done;
9f2ebf
             }
9f2ebf
@@ -1641,7 +1670,8 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
                 }
9f2ebf
             }
9f2ebf
 
9f2ebf
-            if (preq->cctx->rctx->domains->user_name_hint) {
9f2ebf
+            if (preq->cctx->rctx->domains->user_name_hint
9f2ebf
+                    && preq->pd->cmd == SSS_PAM_PREAUTH) {
9f2ebf
                 ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
                                             preq->cert_list,
9f2ebf
                                             SSS_PAM_CERT_INFO_WITH_HINT);
9f2ebf
@@ -1664,6 +1694,20 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
9f2ebf
                 goto done;
9f2ebf
             }
9f2ebf
 
9f2ebf
+            /* If logon_name was not given during authentication add a
9f2ebf
+             * SSS_PAM_CERT_INFO message to send the name to the caller. */
9f2ebf
+            if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
9f2ebf
+                    && preq->pd->logon_name == NULL) {
9f2ebf
+                ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
+                                            preq->cert_list,
9f2ebf
+                                            SSS_PAM_CERT_INFO);
9f2ebf
+                if (ret != EOK) {
9f2ebf
+                    DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
9f2ebf
+                    preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
9f2ebf
+                    goto done;
9f2ebf
+                }
9f2ebf
+            }
9f2ebf
+
9f2ebf
             /* cert_user will be returned to the PAM client as user name, so
9f2ebf
              * we can use it here already e.g. to set in initgroups timeout */
9f2ebf
             preq->pd->logon_name = talloc_strdup(preq->pd, cert_user);
9f2ebf
@@ -2039,7 +2083,6 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
9f2ebf
                                   "the backend.\n");
9f2ebf
                         }
9f2ebf
 
9f2ebf
-                        /* FIXME: use the right cert info */
9f2ebf
                         ret = add_pam_cert_response(preq->pd, cert_user,
9f2ebf
                                                     preq->current_cert,
9f2ebf
                                                     SSS_PAM_CERT_INFO);
9f2ebf
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
9f2ebf
index 50d3ed005468375ff02c60bebd1c61047ca1c6d4..b6845320ca41d6933280aa2836a3d984dacfcc5e 100644
9f2ebf
--- a/src/tests/cmocka/test_pam_srv.c
9f2ebf
+++ b/src/tests/cmocka/test_pam_srv.c
9f2ebf
@@ -967,6 +967,16 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
9f2ebf
                                   NULL);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static int test_pam_cert_check_auth_success(uint32_t status, uint8_t *body,
9f2ebf
+                                            size_t blen)
9f2ebf
+{
9f2ebf
+    assert_int_equal(pam_test_ctx->exp_pam_status, PAM_BAD_ITEM);
9f2ebf
+    pam_test_ctx->exp_pam_status = PAM_SUCCESS;
9f2ebf
+    return test_pam_cert_check_ex(status, body, blen,
9f2ebf
+                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
9f2ebf
+                                  NULL);
9f2ebf
+}
9f2ebf
+
9f2ebf
 static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
9f2ebf
                                          size_t blen)
9f2ebf
 {
9f2ebf
@@ -2265,6 +2275,74 @@ void test_pam_cert_auth(void **state)
9f2ebf
     assert_int_equal(ret, EOK);
9f2ebf
 }
9f2ebf
 
9f2ebf
+void test_pam_cert_auth_no_logon_name(void **state)
9f2ebf
+{
9f2ebf
+    int ret;
9f2ebf
+
9f2ebf
+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
9f2ebf
+
9f2ebf
+    /* Here the last option must be set to true because the backend is only
9f2ebf
+     * connected once. During authentication the backend is connected first to
9f2ebf
+     * see if it can handle Smartcard authentication, but before that the user
9f2ebf
+     * is looked up. Since the first mocked reply already adds the certificate
9f2ebf
+     * to the user entry the lookup by certificate will already find the user
9f2ebf
+     * in the cache and no second request to the backend is needed. */
9f2ebf
+    mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
9f2ebf
+                        "NSS-Internal",
9f2ebf
+                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
9f2ebf
+                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, true);
9f2ebf
+
9f2ebf
+    mock_account_recv_simple();
9f2ebf
+    mock_parse_inp("pamuser", NULL, EOK);
9f2ebf
+
9f2ebf
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
9f2ebf
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
9f2ebf
+
9f2ebf
+    /* Assume backend cannot handle Smartcard credentials */
9f2ebf
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
9f2ebf
+
9f2ebf
+    set_cmd_cb(test_pam_cert_check_auth_success);
9f2ebf
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
9f2ebf
+                          pam_test_ctx->pam_cmds);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    /* Wait until the test finishes with EOK */
9f2ebf
+    ret = test_ev_loop(pam_test_ctx->tctx);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+}
9f2ebf
+
9f2ebf
+void test_pam_cert_auth_no_logon_name_no_key_id(void **state)
9f2ebf
+{
9f2ebf
+    int ret;
9f2ebf
+
9f2ebf
+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
9f2ebf
+
9f2ebf
+    /* Here the last option must be set to true because the backend is only
9f2ebf
+     * connected once. During authentication the backend is connected first to
9f2ebf
+     * see if it can handle Smartcard authentication, but before that the user
9f2ebf
+     * is looked up. Since the first mocked reply already adds the certificate
9f2ebf
+     * to the user entry the lookup by certificate will already find the user
9f2ebf
+     * in the cache and no second request to the backend is needed. */
9f2ebf
+    mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
9f2ebf
+                        "NSS-Internal", NULL, NULL,
9f2ebf
+                        NULL, NULL, false);
9f2ebf
+
9f2ebf
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
9f2ebf
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
9f2ebf
+
9f2ebf
+    /* Assume backend cannot handle Smartcard credentials */
9f2ebf
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
9f2ebf
+
9f2ebf
+    set_cmd_cb(test_pam_creds_insufficient_check);
9f2ebf
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
9f2ebf
+                          pam_test_ctx->pam_cmds);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+
9f2ebf
+    /* Wait until the test finishes with EOK */
9f2ebf
+    ret = test_ev_loop(pam_test_ctx->tctx);
9f2ebf
+    assert_int_equal(ret, EOK);
9f2ebf
+}
9f2ebf
+
9f2ebf
 void test_pam_cert_auth_double_cert(void **state)
9f2ebf
 {
9f2ebf
     int ret;
9f2ebf
@@ -2770,6 +2848,10 @@ int main(int argc, const char *argv[])
9f2ebf
                                         pam_test_setup, pam_test_teardown),
9f2ebf
         cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings,
9f2ebf
                                         pam_test_setup, pam_test_teardown),
9f2ebf
+        cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name,
9f2ebf
+                                        pam_test_setup, pam_test_teardown),
9f2ebf
+        cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
9f2ebf
+                                        pam_test_setup, pam_test_teardown),
9f2ebf
 #endif /* HAVE_NSS */
9f2ebf
 
9f2ebf
         cmocka_unit_test_setup_teardown(test_filter_response,
9f2ebf
-- 
9f2ebf
2.13.6
9f2ebf