From ef375cdd67b51d8fb63cae4d3cd40f3a5c2bc173 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 1 Jul 2024 20:40:30 +0200 Subject: [PATCH 7/8] krb5_child: do not try passwords with OTP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During two-factor authentication (OTP) krb5_child should use use the dedicated OTP auth types SSS_AUTHTOK_TYPE_2FA and SSS_AUTHTOK_TYPE_2FA_SINGLE exclusively and should not try password or other types. The special handling needed of ssh under certain conditions are documented in the code and the man page. Resolves: https://github.com/SSSD/sssd/issues/7456 Reviewed-by: Justin Stephenson Reviewed-by: Tomáš Halman (cherry picked from commit af799964e5fa1264467b49988021c054586eff27) Reviewed-by: Sumit Bose --- src/man/sssd.conf.5.xml | 11 +++++++++ src/providers/krb5/krb5_child.c | 11 +-------- src/sss_client/pam_sss.c | 44 ++++++++++++++++++++++++--------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index fb86a4e41..8ac1a4418 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -4559,6 +4559,17 @@ ldap_user_extra_attrs = phone:telephoneNumber to log in either only with the password or with both factors two-step prompting has to be used. + + Some clients, such as SSH with + 'PasswordAuthentication yes', generate their own prompts + and do not use prompts provided by SSSD or other PAM + modules. Additionally, for SSH with + PasswordAuthentication, if two-factor authentication is + available, SSSD expects that the + credentials entered by the user at the SSH password prompt + will always be the two factors in a single string, even if + two-factor authentication is optional. + diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 494711de9..cb9a9ce73 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -536,15 +536,6 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx, size_t fa2_len; switch (sss_authtok_get_type(auth_tok)) { - case SSS_AUTHTOK_TYPE_PASSWORD: - ret = sss_authtok_get_password(auth_tok, &pwd, &len); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_password failed.\n"); - return ret; - } - - return tokeninfo_matches_pwd(mem_ctx, ti, pwd, len, out_token, out_pin); - break; case SSS_AUTHTOK_TYPE_2FA_SINGLE: ret = sss_authtok_get_2fa_single(auth_tok, &pwd, &len); if (ret != EOK) { @@ -569,7 +560,7 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx, "Unsupported authtok type %d\n", sss_authtok_get_type(auth_tok)); } - return EINVAL; + return EAGAIN; } static krb5_error_code answer_otp(krb5_context ctx, diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 5171e58ec..d43bd0f55 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -1656,6 +1656,7 @@ static int prompt_password(pam_handle_t *pamh, struct pam_items *pi, } static int prompt_2fa(pam_handle_t *pamh, struct pam_items *pi, + bool second_factor_optional, const char *prompt_fa1, const char *prompt_fa2) { int ret; @@ -1706,13 +1707,30 @@ static int prompt_2fa(pam_handle_t *pamh, struct pam_items *pi, goto done; } - if (resp[1].resp == NULL || *(resp[1].resp) == '\0' - || (pi->pam_service != NULL && strcmp(pi->pam_service, "sshd") == 0 - && strcmp(resp[0].resp, resp[1].resp) == 0)) { + if (resp[1].resp == NULL || *(resp[1].resp) == '\0') { /* Missing second factor, assume first factor contains combined 2FA - * credentials. - * Special handling for SSH with password authentication. Combined - * 2FA credentials are used but SSH puts them in both responses. */ + * credentials if the second factor is not optional. If it is optional + * then it is assumed that the first factor contain the password. */ + pi->pam_authtok = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); + if (pi->pam_authtok == NULL) { + D(("strndup failed.")); + ret = PAM_BUF_ERR; + goto done; + } + pi->pam_authtok_size = strlen(pi->pam_authtok) + 1; + pi->pam_authtok_type = second_factor_optional + ? SSS_AUTHTOK_TYPE_PASSWORD + : SSS_AUTHTOK_TYPE_2FA_SINGLE; + } else if (pi->pam_service != NULL && strcmp(pi->pam_service, "sshd") == 0 + && strcmp(resp[0].resp, resp[1].resp) == 0) { + /* Special handling for SSH with password authentication (ssh's + * 'PasswordAuthentication' option. In this mode the ssh client + * directly prompts the user for a password and the prompts we are + * sending are ignored. Since we send two prompts ssh * will create two + * response as well with the same content. We assume that the combined + * 2FA credentials are used even if the second factor is optional + * because there is no indication about the intention of the user. As a + * result we prefer the more secure variant. */ pi->pam_authtok = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); if (pi->pam_authtok == NULL) { @@ -1721,7 +1739,7 @@ static int prompt_2fa(pam_handle_t *pamh, struct pam_items *pi, goto done; } pi->pam_authtok_size = strlen(pi->pam_authtok) + 1; - pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_2FA_SINGLE; } else { ret = sss_auth_pack_2fa_blob(resp[0].resp, 0, resp[1].resp, 0, NULL, 0, @@ -2487,7 +2505,7 @@ static int prompt_by_config(pam_handle_t *pamh, struct pam_items *pi) 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]), + ret = prompt_2fa(pamh, pi, false, pc_get_2fa_1st_prompt(pi->pc[c]), pc_get_2fa_2nd_prompt(pi->pc[c])); break; case PC_TYPE_2FA_SINGLE: @@ -2564,10 +2582,10 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, || (pi->otp_vendor != NULL && pi->otp_token_id != NULL && pi->otp_challenge != NULL)) { if (pi->password_prompting) { - ret = prompt_2fa(pamh, pi, _("First Factor: "), + ret = prompt_2fa(pamh, pi, true, _("First Factor: "), _("Second Factor (optional): ")); } else { - ret = prompt_2fa(pamh, pi, _("First Factor: "), + ret = prompt_2fa(pamh, pi, false, _("First Factor: "), _("Second Factor: ")); } } else if (pi->passkey_prompt_pin) { @@ -2736,10 +2754,12 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, || (pi->otp_vendor != NULL && pi->otp_token_id != NULL && pi->otp_challenge != NULL)) { if (pi->password_prompting) { - ret = prompt_2fa(pamh, pi, _("First Factor (Current Password): "), + ret = prompt_2fa(pamh, pi, true, + _("First Factor (Current Password): "), _("Second Factor (optional): ")); } else { - ret = prompt_2fa(pamh, pi, _("First Factor (Current Password): "), + ret = prompt_2fa(pamh, pi, false, + _("First Factor (Current Password): "), _("Second Factor: ")); } } else if ((flags & PAM_CLI_FLAGS_USE_FIRST_PASS) -- 2.45.2