From f724123e20f8d4a1c85473d917da6c65a10d6d62 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 18 Sep 2018 09:53:37 +0200 Subject: [PATCH 41/47] pam_sss: add option require_cert_auth With this new option pam_sss will wait until a Smartcard is available and then try to authenticate with the help of the Smartcard. Related https://pagure.io/SSSD/sssd/issue/3650 Reviewed-by: Jakub Hrozek (cherry picked from commit 49be8974b490c368d349752f3196af0c9ed28dd5) --- src/man/pam_sss.8.xml | 25 ++++++++++++ src/responder/pam/pamsrv_cmd.c | 12 ++++++ src/responder/pam/pamsrv_p11.c | 5 ++- src/sss_client/pam_message.c | 4 ++ src/sss_client/pam_message.h | 1 + src/sss_client/pam_sss.c | 92 ++++++++++++++++++++++++++---------------- src/sss_client/sss_cli.h | 2 + src/util/sss_pam_data.c | 1 + src/util/sss_pam_data.h | 1 + 9 files changed, 107 insertions(+), 36 deletions(-) diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml index ca2e8e20678d102525a9252678dd83459c3338ac..9998519f16c934e0d578760a57cc0908db760bfb 100644 --- a/src/man/pam_sss.8.xml +++ b/src/man/pam_sss.8.xml @@ -53,6 +53,9 @@ try_cert_auth + + require_cert_auth + @@ -223,6 +226,28 @@ auth sufficient pam_sss.so allow_missing_name + + + + + + + Do certificate based authentication, i.e. + authentication with a Smartcard or similar devices. If a + Smartcard is not available the user will be prompted to + insert one. SSSD will wait for a Smartcard until the + timeout defined by p11_wait_for_card_timeout passed, + please see + sssd.conf + 5 for details. + + + If no Smartcard is available after the timeout or + certificate based authentication is not allowed for the + current service PAM_AUTHINFO_UNAVAIL is returned. + + + diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index c8df32de9e72e9f5ce33e26f0a13101a99f01d5f..6e37f831602e4c367176cc14126dbbec72c858cd 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -317,6 +317,11 @@ static int pam_parse_in_data_v2(struct pam_data *pd, size, body, blen, &c); if (ret != EOK) return ret; break; + case SSS_PAM_ITEM_FLAGS: + ret = extract_uint32_t(&pd->cli_flags, size, + body, blen, &c); + if (ret != EOK) return ret; + break; default: DEBUG(SSSDBG_CRIT_FAILURE, "Ignoring unknown data type [%d].\n", type); @@ -1447,6 +1452,13 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) "No certificate found and no logon name given, " \ "authentication not possible.\n"); ret = ENOENT; + } else if (pd->cli_flags & PAM_CLI_FLAGS_TRY_CERT_AUTH) { + DEBUG(SSSDBG_TRACE_ALL, + "try_cert_auth flag set but no certificate available, " + "request finished.\n"); + preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + pam_reply(preq); + return; } else { if (pd->cmd == SSS_PAM_AUTHENTICATE) { DEBUG(SSSDBG_CRIT_FAILURE, diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c index ffa6787e967488ac408ce0f0a11b96066c29b630..8b8859d9d335aec6d310201256522fa8afdd3694 100644 --- a/src/responder/pam/pamsrv_p11.c +++ b/src/responder/pam/pamsrv_p11.c @@ -721,7 +721,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, struct timeval tv; int pipefd_to_child[2] = PIPE_INIT; int pipefd_from_child[2] = PIPE_INIT; - const char *extra_args[13] = { NULL }; + const char *extra_args[14] = { NULL }; uint8_t *write_buf = NULL; size_t write_buf_len = 0; size_t arg_c; @@ -748,6 +748,9 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, /* extra_args are added in revers order */ arg_c = 0; + if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) { + extra_args[arg_c++] = "--wait_for_card"; + } extra_args[arg_c++] = nss_db; extra_args[arg_c++] = "--nssdb"; if (verify_opts != NULL) { diff --git a/src/sss_client/pam_message.c b/src/sss_client/pam_message.c index b239f6f53da54054c52e484bdd076193709cb003..036ae2ad17742c123ba59e39a122ea605b7b95a6 100644 --- a/src/sss_client/pam_message.c +++ b/src/sss_client/pam_message.c @@ -126,6 +126,7 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer) len += 3*sizeof(uint32_t); /* cli_pid */ len += *pi->requested_domains != '\0' ? 2*sizeof(uint32_t) + pi->requested_domains_size : 0; + len += 3*sizeof(uint32_t); /* flags */ buf = malloc(len); if (buf == NULL) { @@ -164,6 +165,9 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer) pi->pam_newauthtok, pi->pam_newauthtok_size, &buf[rp]); + rp += add_uint32_t_item(SSS_PAM_ITEM_FLAGS, (uint32_t) pi->flags, + &buf[rp]); + SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp); if (rp != len) { diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h index 11526a80a767ff5602b194d14765ff261e8f9707..50fedcd82d8ace520d0360d85d163f91df0cb100 100644 --- a/src/sss_client/pam_message.h +++ b/src/sss_client/pam_message.h @@ -51,6 +51,7 @@ struct pam_items { enum sss_authtok_type pam_newauthtok_type; size_t pam_newauthtok_size; pid_t cli_pid; + uint32_t flags; const char *login_name; char *domain_name; const char *requested_domains; diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 96ff15adad867aceae17431cd5256ae52e4b9306..b4c1036ad68a97821f5d0aee873fa18fe5e72683 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -134,6 +134,7 @@ static void free_cai(struct cert_auth_info *cai) free(cai->cert_user); free(cai->cert); free(cai->token_name); + free(cai->module_name); free(cai->key_id); free(cai->prompt_str); free(cai); @@ -1247,6 +1248,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags, pi->cert_list = NULL; pi->selected_cert = NULL; + pi->flags = flags; + return PAM_SUCCESS; } @@ -1267,6 +1270,7 @@ static void print_pam_items(struct pam_items *pi) D(("Newauthtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_newauthtok))); D(("Cli_PID: %d", pi->cli_pid)); D(("Requested domains: %s", pi->requested_domains)); + D(("Flags: %d", pi->flags)); } static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi, @@ -1999,6 +2003,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS; } else if (strcmp(*argv, "try_cert_auth") == 0) { *flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH; + } else if (strcmp(*argv, "require_cert_auth") == 0) { + *flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH; } else { logger(pamh, LOG_WARNING, "unknown option: %s", *argv); } @@ -2274,55 +2280,51 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, return PAM_SUCCESS; } -#define SC_ENTER_FMT "Please enter smart card labeled\n %s\nand press enter" +#define SC_ENTER_LABEL_FMT "Please enter smart card labeled\n %s" +#define SC_ENTER_FMT "Please enter smart card" static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi, - bool quiet_mode) + int retries, bool quiet_mode) { int ret; int pam_status; char *login_token_name; char *prompt = NULL; - size_t size; - char *answer = NULL; - /* TODO: check multiple cert case */ - struct cert_auth_info *cai = pi->cert_list; - - if (cai == NULL) { - D(("No certificate information available")); - return EINVAL; - } + uint32_t orig_flags = pi->flags; login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME"); + if (login_token_name == NULL + && !(pi->flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) { + return PAM_SUCCESS; + } + if (login_token_name == NULL) { - return PAM_SUCCESS; + ret = asprintf(&prompt, SC_ENTER_FMT); + } else { + ret = asprintf(&prompt, SC_ENTER_LABEL_FMT, login_token_name); + } + if (ret == -1) { + return ENOMEM; } - while (cai->token_name == NULL - || strcmp(login_token_name, cai->token_name) != 0) { - size = sizeof(SC_ENTER_FMT) + strlen(login_token_name); - prompt = malloc(size); - if (prompt == NULL) { - D(("malloc failed.")); - return ENOMEM; - } + pi->flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH; + + /* TODO: check multiple cert case */ + while (pi->cert_list == NULL || pi->cert_list->token_name == NULL + || (login_token_name != NULL + && strcmp(login_token_name, + pi->cert_list->token_name) != 0)) { - ret = snprintf(prompt, size, SC_ENTER_FMT, - login_token_name); - if (ret < 0 || ret >= size) { - D(("snprintf failed.")); - free(prompt); - return EFAULT; + if (retries < 0) { + ret = PAM_AUTHINFO_UNAVAIL; + goto done; } + retries--; - ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, - NULL, &answer); - free(prompt); + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, prompt, NULL, NULL); if (ret != PAM_SUCCESS) { D(("do_pam_conversation failed.")); - return ret; - } else { - free(answer); + goto done; } pam_status = send_and_receive(pamh, pi, SSS_PAM_PREAUTH, quiet_mode); @@ -2335,7 +2337,14 @@ static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi, } } - return PAM_SUCCESS; + ret = PAM_SUCCESS; + +done: + + pi->flags = orig_flags; + free(prompt); + + return ret; } static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, @@ -2394,8 +2403,19 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, && (pi.pam_authtok == NULL || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS)) && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) { + + if (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) { + /* Do not use PAM_CLI_FLAGS_REQUIRE_CERT_AUTH in the first + * SSS_PAM_PREAUTH run. In case a card is already inserted + * we do not have to prompt to insert a card. */ + pi.flags &= ~PAM_CLI_FLAGS_REQUIRE_CERT_AUTH; + pi.flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH; + } + pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH, quiet_mode); + + pi.flags = flags; if (pam_status != PAM_SUCCESS) { D(("send_and_receive returned [%d] during pre-auth", pam_status)); @@ -2414,8 +2434,10 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, return PAM_AUTHINFO_UNAVAIL; } - if (strcmp(pi.pam_service, "gdm-smartcard") == 0) { - ret = check_login_token_name(pamh, &pi, quiet_mode); + if (strcmp(pi.pam_service, "gdm-smartcard") == 0 + || (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) { + ret = check_login_token_name(pamh, &pi, retries, + quiet_mode); if (ret != PAM_SUCCESS) { D(("check_login_token_name failed.\n")); return ret; diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 38e3f999d799556a56ac08f0f3a6b538b8cde9f3..af8a43916d43b631092941fed13c520273a1acc5 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -363,6 +363,7 @@ enum pam_item_type { SSS_PAM_ITEM_CLI_LOCALE, SSS_PAM_ITEM_CLI_PID, SSS_PAM_ITEM_REQUESTED_DOMAINS, + SSS_PAM_ITEM_FLAGS, }; #define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0) @@ -374,6 +375,7 @@ enum pam_item_type { #define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6) #define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7) #define PAM_CLI_FLAGS_TRY_CERT_AUTH (1 << 8) +#define PAM_CLI_FLAGS_REQUIRE_CERT_AUTH (1 << 9) #define SSS_NSS_MAX_ENTRIES 256 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4) diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c index 5e41349b9e98974563bf55c41ce36c26b897ac99..cb8779c1dff04832f623eb518d2b010107d4b045 100644 --- a/src/util/sss_pam_data.c +++ b/src/util/sss_pam_data.c @@ -176,6 +176,7 @@ void pam_print_data(int l, struct pam_data *pd) DEBUG(l, "priv: %d\n", pd->priv); DEBUG(l, "cli_pid: %d\n", pd->cli_pid); DEBUG(l, "logon name: %s\n", PAM_SAFE_ITEM(pd->logon_name)); + DEBUG(l, "flags: %d\n", pd->cli_flags); } int pam_add_response(struct pam_data *pd, enum response_type type, diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h index 7d74fa6a0026d3964f33c8529063b1dceae45688..c9898105418fc76b45d78883a0520f37d0ae1c05 100644 --- a/src/util/sss_pam_data.h +++ b/src/util/sss_pam_data.h @@ -58,6 +58,7 @@ struct pam_data { struct sss_auth_token *newauthtok; uint32_t cli_pid; char *logon_name; + uint32_t cli_flags; int pam_status; int response_delay; -- 2.14.4