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