sssd/SOURCES/0007-krb5_child-do-not-try-passwords-with-OTP.patch

179 lines
8.3 KiB
Diff
Raw Permalink Normal View History

2024-09-30 16:41:07 +00:00
From ef375cdd67b51d8fb63cae4d3cd40f3a5c2bc173 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
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 <jstephen@redhat.com>
Reviewed-by: Tomáš Halman <thalman@redhat.com>
(cherry picked from commit af799964e5fa1264467b49988021c054586eff27)
Reviewed-by: Sumit Bose <sbose@redhat.com>
---
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.
</para>
+ <para>
+ 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.
+ </para>
</listitem>
</varlistentry>
</variablelist>
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