179 lines
8.3 KiB
Diff
179 lines
8.3 KiB
Diff
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
|
|
|