From a531a785f57be7ae228ca04a7af606debd66eeb1 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Wed, 3 May 2017 16:30:12 +0200 Subject: [PATCH 158/160] PAM: send user name hint response when needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the PAM client didn't send a user name and promtusername is enable the PAM responder will tell pam_sss to ask for an optional user name as well. Resolves: https://pagure.io/SSSD/sssd/issue/3395 Reviewed-by: Fabiano FidĂȘncio (cherry picked from commit 32474fa2f0a6dc09386bab405fc3461cb3dd12ac) --- src/responder/pam/pamsrv_cmd.c | 72 ++++++++++------ src/tests/cmocka/test_pam_srv.c | 180 +++++++++++++++++++++++++++++----------- 2 files changed, 177 insertions(+), 75 deletions(-) diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 080cfafa709d63542fbf57d26fab11f0a367dea7..49a05657e03feef564d6196029da4cacc2ab8eaf 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -1414,7 +1414,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) struct cache_req_result **results; struct pam_auth_req *preq = tevent_req_callback_data(req, struct pam_auth_req); - const char *cert_user; + const char *cert_user = NULL; ret = cache_req_recv(preq, req, &results); talloc_zfree(req); @@ -1439,35 +1439,55 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) goto done; } - if (preq->cert_user_objs->count != 1) { - DEBUG(SSSDBG_CRIT_FAILURE, - "More than one user mapped to certificate.\n"); - /* TODO: send pam response to ask for a user name */ - ret = ERR_NO_CREDS; - goto done; - } - cert_user = ldb_msg_find_attr_as_string( + if (preq->cert_user_objs->count == 1) { + cert_user = ldb_msg_find_attr_as_string( preq->cert_user_objs->msgs[0], SYSDB_NAME, NULL); + if (cert_user == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Certificate user object has not name.\n"); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_FUNC_DATA, + "Found certificate user [%s].\n", cert_user); + + 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, + "sss_parse_name_for_domains failed.\n"); + goto done; + } + } + + if (preq->cctx->rctx->domains->user_name_hint) { + ret = add_pam_cert_response(preq->pd, cert_user, + preq->token_name, + preq->module_name, + preq->key_id, + SSS_PAM_CERT_INFO_WITH_HINT); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n"); + preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + } + ret = EOK; + preq->pd->pam_status = PAM_SUCCESS; + pam_reply(preq); + goto done; + } + + /* Without user name hints the certificate must map to single user + * if no login name was given */ if (cert_user == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, - "Certificate user object has not name.\n"); - ret = ENOENT; - goto done; - } - - DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n", - cert_user); - - 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, - "sss_parse_name_for_domains failed.\n"); + "More than one user mapped to certificate.\n"); + ret = ERR_NO_CREDS; goto done; } diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c index 35afbdd81d004236885ee80914771ccb4b8acff4..0f92f05417025e41a702127099d1d01e269412dc 100644 --- a/src/tests/cmocka/test_pam_srv.c +++ b/src/tests/cmocka/test_pam_srv.c @@ -747,57 +747,83 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, return EOK; } +static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, + enum response_type type, const char *name) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + if (name == NULL || *name == '\0') { + assert_int_equal(val, 1); + } else { + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal(body + rp, TEST_DOM_NAME); + rp += val; + } + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, (strlen(name) + 1 + + sizeof(TEST_TOKEN_NAME) + + sizeof(TEST_MODULE_NAME) + + sizeof(TEST_KEY_ID))); + + assert_int_equal(*(body + rp + strlen(name)), 0); + assert_string_equal(body + rp, name); + rp += strlen(name) + 1; + + 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; +} + static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) { - size_t rp = 0; - uint32_t val; - - assert_int_equal(status, 0); - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - assert_int_equal(val, pam_test_ctx->exp_pam_status); - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - assert_int_equal(val, 2); - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - assert_int_equal(val, SSS_PAM_DOMAIN_NAME); - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - assert_int_equal(val, 9); - - assert_int_equal(*(body + rp + val - 1), 0); - assert_string_equal(body + rp, TEST_DOM_NAME); - rp += val; - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - assert_int_equal(val, SSS_PAM_CERT_INFO); - - SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); - 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); - rp += sizeof("pamuser@"TEST_DOM_NAME); - - 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; + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME); +} + +static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO_WITH_HINT, + "pamuser@"TEST_DOM_NAME); +} + +static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO_WITH_HINT, ""); } static int test_pam_offline_chauthtok_check(uint32_t status, @@ -1895,6 +1921,33 @@ void test_pam_preauth_cert_no_logon_name(void **state) assert_int_equal(ret, EOK); } +void test_pam_preauth_cert_no_logon_name_with_hint(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, NSS_DB); + pam_test_ctx->rctx->domains->user_name_hint = true; + + /* If no logon name is given the user is looked by certificate first. + * Since user name hint is enabled we do not have to search the user + * during pre-auth and there is no need for an extra mocked response as in + * test_pam_preauth_cert_no_logon_name. */ + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, + test_lookup_by_cert_cb, TEST_TOKEN_CERT, false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_with_hint); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + void test_pam_preauth_cert_no_logon_name_double_cert(void **state) { int ret; @@ -1917,6 +1970,29 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state) assert_int_equal(ret, EOK); } +void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, NSS_DB); + pam_test_ctx->rctx->domains->user_name_hint = true; + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, + test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_with_hint_no_user); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + void test_pam_preauth_no_cert_no_logon_name(void **state) { int ret; @@ -2426,8 +2502,14 @@ int main(int argc, const char *argv[]) cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name, pam_test_setup, pam_test_teardown), cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_with_hint, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( test_pam_preauth_cert_no_logon_name_double_cert, pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_double_cert_with_hint, + pam_test_setup, pam_test_teardown), cmocka_unit_test_setup_teardown(test_pam_preauth_no_cert_no_logon_name, pam_test_setup, pam_test_teardown), cmocka_unit_test_setup_teardown( -- 2.9.4