Be more careful asking for AS key in SPAKE client
This commit is contained in:
parent
091dcbf794
commit
9f52d3d29f
229
Be-more-careful-asking-for-AS-key-in-SPAKE-client.patch
Normal file
229
Be-more-careful-asking-for-AS-key-in-SPAKE-client.patch
Normal file
@ -0,0 +1,229 @@
|
||||
From 864f90dcc860997189679b980f52de41ef10a238 Mon Sep 17 00:00:00 2001
|
||||
From: Greg Hudson <ghudson@mit.edu>
|
||||
Date: Sat, 31 Mar 2018 10:43:49 -0400
|
||||
Subject: [PATCH] Be more careful asking for AS key in SPAKE client
|
||||
|
||||
Asking for the AS key too early can result in password prompts in
|
||||
situations where SPAKE won't proceed, such as when the KDC offers only
|
||||
second factor types not supported by the client.
|
||||
|
||||
In spake_prep_questions(), decode the received message and make sure
|
||||
it's a challenge with a supported group and second factor type
|
||||
(SF-NONE at the moment). Save the decoded message and use it in
|
||||
spake_process(). Do not retrieve the AS key at the beginning of
|
||||
spake_process(); instead do so in process_challenge() after checking
|
||||
the challenge group and factor types.
|
||||
|
||||
Move contains_sf_none() earlier in the file so that it can be used by
|
||||
spake_prep_questions() without a prototype.
|
||||
|
||||
ticket: 8659
|
||||
(cherry picked from commit f240f1b0d324312be8aa59ead7cfbe0c329ed064)
|
||||
---
|
||||
src/plugins/preauth/spake/spake_client.c | 111 ++++++++++++++++++-------------
|
||||
1 file changed, 66 insertions(+), 45 deletions(-)
|
||||
|
||||
diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
|
||||
index d72bd64aa..47a6ba26c 100644
|
||||
--- a/src/plugins/preauth/spake/spake_client.c
|
||||
+++ b/src/plugins/preauth/spake/spake_client.c
|
||||
@@ -39,12 +39,26 @@
|
||||
#include <krb5/clpreauth_plugin.h>
|
||||
|
||||
typedef struct reqstate_st {
|
||||
+ krb5_pa_spake *msg; /* set in prep_questions, used in process */
|
||||
krb5_keyblock *initial_key;
|
||||
krb5_data *support;
|
||||
krb5_data thash;
|
||||
krb5_data spakeresult;
|
||||
} reqstate;
|
||||
|
||||
+/* Return true if SF-NONE is present in factors. */
|
||||
+static krb5_boolean
|
||||
+contains_sf_none(krb5_spake_factor **factors)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; factors != NULL && factors[i] != NULL; i++) {
|
||||
+ if (factors[i]->type == SPAKE_SF_NONE)
|
||||
+ return TRUE;
|
||||
+ }
|
||||
+ return FALSE;
|
||||
+}
|
||||
+
|
||||
static krb5_error_code
|
||||
spake_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
|
||||
{
|
||||
@@ -77,6 +91,7 @@ spake_request_fini(krb5_context context, krb5_clpreauth_moddata moddata,
|
||||
{
|
||||
reqstate *st = (reqstate *)modreq;
|
||||
|
||||
+ k5_free_pa_spake(context, st->msg);
|
||||
krb5_free_keyblock(context, st->initial_key);
|
||||
krb5_free_data(context, st->support);
|
||||
krb5_free_data_contents(context, &st->thash);
|
||||
@@ -92,16 +107,42 @@ spake_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
|
||||
krb5_data *enc_req, krb5_data *enc_prev_req,
|
||||
krb5_pa_data *pa_data)
|
||||
{
|
||||
+ krb5_error_code ret;
|
||||
+ groupstate *gstate = (groupstate *)moddata;
|
||||
reqstate *st = (reqstate *)modreq;
|
||||
+ krb5_data in_data;
|
||||
+ krb5_spake_challenge *ch;
|
||||
|
||||
if (st == NULL)
|
||||
return ENOMEM;
|
||||
- if (st->initial_key == NULL && pa_data->length > 0)
|
||||
+
|
||||
+ /* We don't need to ask any questions to send a support message. */
|
||||
+ if (pa_data->length == 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Decode the incoming message, replacing any previous one in the request
|
||||
+ * state. If we can't decode it, we have no questions to ask. */
|
||||
+ k5_free_pa_spake(context, st->msg);
|
||||
+ st->msg = NULL;
|
||||
+ in_data = make_data(pa_data->contents, pa_data->length);
|
||||
+ ret = decode_krb5_pa_spake(&in_data, &st->msg);
|
||||
+ if (ret)
|
||||
+ return (ret == ENOMEM) ? ENOMEM : 0;
|
||||
+
|
||||
+ if (st->msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
|
||||
+ ch = &st->msg->u.challenge;
|
||||
+ if (!group_is_permitted(gstate, ch->group))
|
||||
+ return 0;
|
||||
+ /* When second factor support is implemented, we should ask questions
|
||||
+ * based on the factors in the challenge. */
|
||||
+ if (!contains_sf_none(ch->factors))
|
||||
+ return 0;
|
||||
+ /* We will need the AS key to respond to the challenge. */
|
||||
cb->need_as_key(context, rock);
|
||||
-
|
||||
- /* When second-factor is implemented, we should ask questions based on the
|
||||
- * factors in the challenge. */
|
||||
-
|
||||
+ } else if (st->msg->choice == SPAKE_MSGTYPE_ENCDATA) {
|
||||
+ /* When second factor support is implemented, we should decrypt the
|
||||
+ * encdata message and ask questions based on the factor data. */
|
||||
+ }
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -136,19 +177,6 @@ send_support(krb5_context context, groupstate *gstate, reqstate *st,
|
||||
return convert_to_padata(support, pa_out);
|
||||
}
|
||||
|
||||
-/* Return true if SF-NONE is present in factors. */
|
||||
-static krb5_boolean
|
||||
-contains_sf_none(krb5_spake_factor **factors)
|
||||
-{
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; factors != NULL && factors[i] != NULL; i++) {
|
||||
- if (factors[i]->type == SPAKE_SF_NONE)
|
||||
- return TRUE;
|
||||
- }
|
||||
- return FALSE;
|
||||
-}
|
||||
-
|
||||
static krb5_error_code
|
||||
process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
|
||||
krb5_spake_challenge *ch, const krb5_data *der_msg,
|
||||
@@ -157,7 +185,7 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
|
||||
const krb5_data *der_req, krb5_pa_data ***pa_out)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
- krb5_keyblock *k0 = NULL, *k1 = NULL;
|
||||
+ krb5_keyblock *k0 = NULL, *k1 = NULL, *as_key;
|
||||
krb5_spake_factor factor;
|
||||
krb5_pa_spake msg;
|
||||
krb5_data *der_factor = NULL, *response;
|
||||
@@ -167,8 +195,8 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
|
||||
|
||||
enc_factor.ciphertext = empty_data();
|
||||
|
||||
- /* Not expected if we already computed the SPAKE result. */
|
||||
- if (st->spakeresult.length != 0)
|
||||
+ /* Not expected if we processed a challenge and didn't reject it. */
|
||||
+ if (st->initial_key != NULL)
|
||||
return KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
|
||||
if (!group_is_permitted(gstate, ch->group)) {
|
||||
@@ -193,6 +221,12 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
|
||||
if (!contains_sf_none(ch->factors))
|
||||
return KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
|
||||
+ ret = cb->get_as_key(context, rock, &as_key);
|
||||
+ if (ret)
|
||||
+ goto cleanup;
|
||||
+ ret = krb5_copy_keyblock(context, as_key, &st->initial_key);
|
||||
+ if (ret)
|
||||
+ goto cleanup;
|
||||
ret = derive_wbytes(context, ch->group, st->initial_key, &wbytes);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
@@ -267,7 +301,7 @@ process_encdata(krb5_context context, reqstate *st, krb5_enc_data *enc,
|
||||
krb5_pa_data ***pa_out)
|
||||
{
|
||||
/* Not expected if we haven't sent a response yet. */
|
||||
- if (st->spakeresult.length == 0)
|
||||
+ if (st->initial_key == NULL || st->spakeresult.length == 0)
|
||||
return KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
|
||||
/*
|
||||
@@ -292,9 +326,7 @@ spake_process(krb5_context context, krb5_clpreauth_moddata moddata,
|
||||
krb5_error_code ret;
|
||||
groupstate *gstate = (groupstate *)moddata;
|
||||
reqstate *st = (reqstate *)modreq;
|
||||
- krb5_pa_spake *msg;
|
||||
krb5_data in_data;
|
||||
- krb5_keyblock *as_key;
|
||||
|
||||
if (st == NULL)
|
||||
return ENOMEM;
|
||||
@@ -306,34 +338,23 @@ spake_process(krb5_context context, krb5_clpreauth_moddata moddata,
|
||||
return send_support(context, gstate, st, pa_out);
|
||||
}
|
||||
|
||||
- /* We need the initial reply key to process any non-trivial message. */
|
||||
- if (st->initial_key == NULL) {
|
||||
- ret = cb->get_as_key(context, rock, &as_key);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- ret = krb5_copy_keyblock(context, as_key, &st->initial_key);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- in_data = make_data(pa_in->contents, pa_in->length);
|
||||
- ret = decode_krb5_pa_spake(&in_data, &msg);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- if (msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
|
||||
- ret = process_challenge(context, gstate, st, &msg->u.challenge,
|
||||
+ if (st->msg == NULL) {
|
||||
+ /* The message failed to decode in spake_prep_questions(). */
|
||||
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
+ } else if (st->msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
|
||||
+ in_data = make_data(pa_in->contents, pa_in->length);
|
||||
+ ret = process_challenge(context, gstate, st, &st->msg->u.challenge,
|
||||
&in_data, cb, rock, prompter, prompter_data,
|
||||
der_req, pa_out);
|
||||
- } else if (msg->choice == SPAKE_MSGTYPE_ENCDATA) {
|
||||
- ret = process_encdata(context, st, &msg->u.encdata, cb, rock, prompter,
|
||||
- prompter_data, der_prev_req, der_req, pa_out);
|
||||
+ } else if (st->msg->choice == SPAKE_MSGTYPE_ENCDATA) {
|
||||
+ ret = process_encdata(context, st, &st->msg->u.encdata, cb, rock,
|
||||
+ prompter, prompter_data, der_prev_req, der_req,
|
||||
+ pa_out);
|
||||
} else {
|
||||
/* Unexpected message type */
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
}
|
||||
|
||||
- k5_free_pa_spake(context, msg);
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system
|
||||
Name: krb5
|
||||
Version: 1.16
|
||||
# for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
|
||||
Release: 19%{?dist}
|
||||
Release: 20%{?dist}
|
||||
|
||||
# lookaside-cached sources; two downloads and a build artifact
|
||||
Source0: https://web.mit.edu/kerberos/dist/krb5/1.16/krb5-%{version}%{prerelease}.tar.gz
|
||||
@ -88,6 +88,7 @@ Patch61: Add-doc-index-entries-for-SPAKE-constants.patch
|
||||
Patch62: Fix-SPAKE-memory-leak.patch
|
||||
Patch63: Continue-after-KRB5_CC_END-in-KCM-cache-iteration.patch
|
||||
Patch64: Zap-data-when-freeing-krb5_spake_factor.patch
|
||||
Patch65: Be-more-careful-asking-for-AS-key-in-SPAKE-client.patch
|
||||
|
||||
License: MIT
|
||||
URL: http://web.mit.edu/kerberos/www/
|
||||
@ -738,6 +739,9 @@ exit 0
|
||||
%{_libdir}/libkadm5srv_mit.so.*
|
||||
|
||||
%changelog
|
||||
* Tue Apr 03 2018 Robbie Harwood <rharwood@redhat.com> - 1.16-20
|
||||
- Be more careful asking for AS key in SPAKE client
|
||||
|
||||
* Mon Apr 02 2018 Robbie Harwood <rharwood@redhat.com> - 1.16-19
|
||||
- Zap data when freeing krb5_spake_factor
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user