From 6b7ce87976ebba7b3c1aea24dbf91486ec5de2ed Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Wed, 27 Mar 2019 09:48:42 +0100 Subject: [PATCH 18/21] pam_sss: use configured prompting If the responds of SSSD's PAM responder contains a prompt_config structure use the content to prompt the user for credentials. Related to https://pagure.io/SSSD/sssd/issue/3264 Reviewed-by: Jakub Hrozek (cherry picked with fixes from commit fc26b4a82d4a92b29cf321fba8dbec52c3bff8d6) --- src/sss_client/pam_message.h | 2 + src/sss_client/pam_sss.c | 136 +++++++++++++++++++++++++++++------ src/sss_client/sss_cli.h | 3 + 3 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h index 11526a80a..c87162479 100644 --- a/src/sss_client/pam_message.h +++ b/src/sss_client/pam_message.h @@ -64,6 +64,8 @@ struct pam_items { bool user_name_hint; struct cert_auth_info *cert_list; struct cert_auth_info *selected_cert; + + struct prompt_config **pc; }; 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 59081cc67..ab9b7478e 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -205,6 +205,9 @@ static void overwrite_and_free_pam_items(struct pam_items *pi) free_cert_list(pi->cert_list); pi->cert_list = NULL; pi->selected_cert = NULL; + + pc_list_free(pi->pc); + pi->pc = NULL; } static int null_strcmp(const char *s1, const char *s2) { @@ -1163,6 +1166,16 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, D(("Password prompting available.")); pi->password_prompting = true; break; + case SSS_PAM_PROMPT_CONFIG: + if (pi->pc == NULL) { + ret = pc_list_from_response(len, &buf[p], &pi->pc); + if (ret != EOK) { + D(("Failed to parse prompting data, using defaults")); + pc_list_free(pi->pc); + pi->pc = NULL; + } + } + break; default: D(("Unknown response type [%d]", type)); } @@ -1256,6 +1269,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags, pi->cert_list = NULL; pi->selected_cert = NULL; + pi->pc = NULL; + return PAM_SUCCESS; } @@ -1558,6 +1573,37 @@ done: return ret; } +static int prompt_2fa_single(pam_handle_t *pamh, struct pam_items *pi, + const char *prompt) +{ + int ret; + char *answer = NULL; + + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return ret; + } + + if (answer == NULL) { + pi->pam_authtok = NULL; + 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; + if (pi->pam_authtok == NULL) { + return PAM_BUF_ERR; + } + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_2FA_SINGLE; + pi->pam_authtok_size=strlen(pi->pam_authtok); + } + + return PAM_SUCCESS; +} + #define SC_PROMPT_FMT "PIN for %s" #ifndef discard_const @@ -2014,6 +2060,48 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, return; } +static int prompt_by_config(pam_handle_t *pamh, struct pam_items *pi) +{ + size_t c; + int ret; + + if (pi->pc == NULL || *pi->pc == NULL) { + return EINVAL; + } + + for (c = 0; pi->pc[c] != NULL; c++) { + switch (pc_get_type(pi->pc[c])) { + case PC_TYPE_PASSWORD: + ret = prompt_password(pamh, pi, pc_get_password_prompt(pi->pc[c])); + break; + case PC_TYPE_2FA: + ret = prompt_2fa(pamh, pi, pc_get_2fa_1st_prompt(pi->pc[c]), + pc_get_2fa_2nd_prompt(pi->pc[c])); + break; + case PC_TYPE_2FA_SINGLE: + ret = prompt_2fa_single(pamh, pi, + pc_get_2fa_single_prompt(pi->pc[c])); + break; + case PC_TYPE_SC_PIN: + ret = prompt_sc_pin(pamh, pi); + /* Todo: add extra string option */ + break; + default: + ret = EINVAL; + } + + /* If not credential where given try the next type otherwise we are + * done. */ + if (ret == PAM_SUCCESS && pi->pam_authtok_size == 0) { + continue; + } + + break; + } + + return ret; +} + static int get_authtok_for_authentication(pam_handle_t *pamh, struct pam_items *pi, uint32_t flags) @@ -2032,30 +2120,34 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, } pi->pam_authtok_size = strlen(pi->pam_authtok); } else { - if (flags & FLAGS_USE_2FA - || (pi->otp_vendor != NULL && pi->otp_token_id != NULL - && pi->otp_challenge != NULL)) { - if (pi->password_prompting) { - ret = prompt_2fa(pamh, pi, _("First Factor: "), - _("Second Factor (optional): ")); - } else { - ret = prompt_2fa(pamh, pi, _("First Factor: "), - _("Second Factor: ")); - } - } else if (pi->cert_list != NULL) { - if (pi->cert_list->next == NULL) { - /* Only one certificate */ - pi->selected_cert = pi->cert_list; - } else { - ret = prompt_multi_cert(pamh, pi); - if (ret != 0) { - D(("Failed to select certificate")); - return PAM_AUTHTOK_ERR; + if (pi->pc != NULL) { + ret = prompt_by_config(pamh, pi); + } else { + if (flags & FLAGS_USE_2FA + || (pi->otp_vendor != NULL && pi->otp_token_id != NULL + && pi->otp_challenge != NULL)) { + if (pi->password_prompting) { + ret = prompt_2fa(pamh, pi, _("First Factor: "), + _("Second Factor (optional): ")); + } else { + ret = prompt_2fa(pamh, pi, _("First Factor: "), + _("Second Factor: ")); } + } else if (pi->cert_list != NULL) { + if (pi->cert_list->next == NULL) { + /* Only one certificate */ + pi->selected_cert = pi->cert_list; + } else { + ret = prompt_multi_cert(pamh, pi); + if (ret != 0) { + D(("Failed to select certificate")); + return PAM_AUTHTOK_ERR; + } + } + ret = prompt_sc_pin(pamh, pi); + } else { + ret = prompt_password(pamh, pi, _("Password: ")); } - ret = prompt_sc_pin(pamh, pi); - } else { - ret = prompt_password(pamh, pi, _("Password: ")); } if (ret != PAM_SUCCESS) { D(("failed to get password from user")); diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 23ef21608..24b24a91b 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -469,6 +469,9 @@ enum response_type { SSS_PAM_CERT_INFO_WITH_HINT, /**< Same as SSS_PAM_CERT_INFO but user name * might be missing and should be prompted * for. */ + SSS_PAM_PROMPT_CONFIG, /**< Contains data which controls which credentials + * are expected and how the user is prompted for + * them. */ }; /** -- 2.19.1