krb5 commit: Simplify get_in_tkt.c restart handling
Greg Hudson
ghudson at mit.edu
Tue Aug 11 22:40:49 EDT 2015
https://github.com/krb5/krb5/commit/9914d38658e5612db5b2847892b5ddce2b73c344
commit 9914d38658e5612db5b2847892b5ddce2b73c344
Author: Greg Hudson <ghudson at mit.edu>
Date: Mon Jul 27 10:30:30 2015 -0400
Simplify get_in_tkt.c restart handling
To simplify callers, make restart_init_creds_loop() reset the
err_reply and err_padata fields and free per-request preauth moddata.
Change its padata argument to a boolean argument for FAST upgrades,
instead of sometimes passing in ctx->err_padata (which would become
invalid partway through the function now that we're freeing it).
Split up the upgrade-to-FAST and downgrade-to-no-padata cases in
init_creds_step_reply(), and eliminate negotiation_requests_restart().
For brevity, rename the krb5_init_creds_context have_restarted field
to restarted. Rename krb5int_upgrade_to_fast_p() to
k5_upgrade_to_fast_p() and make it a true predicate. Change some flag
field assignments to use TRUE/FALSE instead of 1/0. Reset
enc_pa_rep_permitted after a client realm referral, since we don't
know that the new realm's KDCs will fail on informational padata.
src/lib/krb5/krb/fast.c | 25 +++++--
src/lib/krb5/krb/fast.h | 6 +-
src/lib/krb5/krb/get_in_tkt.c | 142 ++++++++++--------------------------
src/lib/krb5/krb/init_creds_ctx.h | 2 +-
4 files changed, 61 insertions(+), 114 deletions(-)
diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c
index dde7006..4f3e27e 100644
--- a/src/lib/krb5/krb/fast.c
+++ b/src/lib/krb5/krb/fast.c
@@ -635,7 +635,19 @@ krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
return *tmppa;
}
-
+/*
+ * Implement FAST negotiation as specified in RFC 6806 section 11. If
+ * the encrypted part of rep sets the enc-pa-rep flag, look for and
+ * verify a PA-REQ-ENC-PA-REP entry in the encrypted padata. If a
+ * PA-FX-FAST entry is also present in the encrypted padata, set
+ * *fast_avail to true. This will result in a fast_avail config entry
+ * being written to the credential cache, if an output ccache was
+ * specified using krb5_get_init_creds_opt_set_out_ccache(). That
+ * entry will be detected in the armor ccache by
+ * krb5int_fast_as_armor(), allowing us to use FAST without a
+ * round-trip for the KDC to indicate support, and without a downgrade
+ * attack.
+ */
krb5_error_code
krb5int_fast_verify_nego(krb5_context context,
struct krb5int_fast_request_state *state,
@@ -680,18 +692,15 @@ krb5int_fast_verify_nego(krb5_context context,
}
krb5_boolean
-krb5int_upgrade_to_fast_p(krb5_context context,
- struct krb5int_fast_request_state *state,
- krb5_pa_data **padata)
+k5_upgrade_to_fast_p(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_pa_data **padata)
{
if (state->armor_key != NULL)
return FALSE; /* Already using FAST. */
if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
return FALSE;
- if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
- TRACE_FAST_PADATA_UPGRADE(context);
- state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL)
return TRUE;
- }
return FALSE;
}
diff --git a/src/lib/krb5/krb/fast.h b/src/lib/krb5/krb/fast.h
index 684e25d..7156ea2 100644
--- a/src/lib/krb5/krb/fast.h
+++ b/src/lib/krb5/krb/fast.h
@@ -99,9 +99,9 @@ krb5int_fast_verify_nego(krb5_context context,
krb5_boolean *fast_avail);
krb5_boolean
-krb5int_upgrade_to_fast_p(krb5_context context,
- struct krb5int_fast_request_state *state,
- krb5_pa_data **padata);
+k5_upgrade_to_fast_p(krb5_context context,
+ struct krb5int_fast_request_state *state,
+ krb5_pa_data **padata);
krb5_error_code
krb5int_fast_tgs_armor(krb5_context context,
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 4343a4b..7ddc80a 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -713,37 +713,34 @@ set_request_times(krb5_context context, krb5_init_creds_context ctx)
}
/**
- * Throw away any state related to specific realm either at the beginning of a
- * request, or when a realm changes, or when we start to use FAST after
- * assuming we would not do so.
- *
- * @param padata padata from an error if an error from the realm we now expect
- * to talk to caused the restart. Used to infer negotiation characteristics
- * such as whether FAST is used.
+ * Throw away any pre-authentication realm state and begin with a
+ * unauthenticated or optimistically authenticated request. If fast_upgrade is
+ * set, use FAST for this request.
*/
static krb5_error_code
restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
- krb5_pa_data **padata)
+ krb5_boolean fast_upgrade)
{
krb5_error_code code = 0;
- if (ctx->preauth_to_use) {
- krb5_free_pa_data(context, ctx->preauth_to_use);
- ctx->preauth_to_use = NULL;
- }
+ krb5_free_pa_data(context, ctx->preauth_to_use);
+ krb5_free_pa_data(context, ctx->err_padata);
+ krb5_free_error(context, ctx->err_reply);
+ ctx->preauth_to_use = ctx->err_padata = NULL;
+ ctx->err_reply = NULL;
- if (ctx->fast_state) {
- krb5int_fast_free_state(context, ctx->fast_state);
- ctx->fast_state = NULL;
- }
+ krb5int_fast_free_state(context, ctx->fast_state);
+ ctx->fast_state = NULL;
code = krb5int_fast_make_state(context, &ctx->fast_state);
if (code != 0)
goto cleanup;
+ if (fast_upgrade)
+ ctx->fast_state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+
+ k5_preauth_request_context_fini(context);
k5_preauth_request_context_init(context);
- if (ctx->outer_request_body) {
- krb5_free_data(context, ctx->outer_request_body);
- ctx->outer_request_body = NULL;
- }
+ krb5_free_data(context, ctx->outer_request_body);
+ ctx->outer_request_body = NULL;
if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
code = make_preauth_list(context, ctx->opt->preauth_list,
ctx->opt->preauth_list_length,
@@ -765,12 +762,6 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
ctx->request);
if (code != 0)
goto cleanup;
- if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
- code = krb5int_fast_as_armor(context, ctx->fast_state, ctx->opt,
- ctx->request);
- if (code != 0)
- goto cleanup;
- }
/* give the preauth plugins a chance to prep the request body */
k5_preauth_prepare_request(context, ctx->opt, ctx->request);
@@ -806,7 +797,7 @@ krb5_init_creds_init(krb5_context context,
ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
if (code != 0)
goto cleanup;
- ctx->enc_pa_rep_permitted = 1;
+ ctx->enc_pa_rep_permitted = TRUE;
code = krb5_copy_principal(context, client, &ctx->request->client);
if (code != 0)
goto cleanup;
@@ -978,7 +969,7 @@ krb5_init_creds_init(krb5_context context,
ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
ctx->request->client->type = KRB5_NT_WELLKNOWN;
}
- code = restart_init_creds_loop(context, ctx, NULL);
+ code = restart_init_creds_loop(context, ctx, FALSE);
if (code)
goto cleanup;
@@ -1008,8 +999,7 @@ krb5_init_creds_set_service(krb5_context context,
free(ctx->in_tkt_service);
ctx->in_tkt_service = s;
- k5_preauth_request_context_fini(context);
- return restart_init_creds_loop(context, ctx, NULL);
+ return restart_init_creds_loop(context, ctx, FALSE);
}
static krb5_error_code
@@ -1273,7 +1263,7 @@ init_creds_step_request(krb5_context context,
ctx->encoded_previous_request = NULL;
}
if (ctx->request->padata)
- ctx->sent_nontrivial_preauth = 1;
+ ctx->sent_nontrivial_preauth = TRUE;
if (ctx->enc_pa_rep_permitted)
code = request_enc_pa_rep(&ctx->request->padata);
if (code)
@@ -1297,59 +1287,6 @@ cleanup:
return code;
}
-/*
- * The control flow is complicated. In order to switch from non-FAST mode to
- * FAST mode, we need to reset our pre-authentication state. FAST negotiation
- * attempts to make sure we rarely have to do this. When FAST negotiation is
- * working, we record whether FAST is available when we obtain an armor ticket;
- * if so, we start out with FAST enabled . There are two complicated
- * situations.
- *
- * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
- * a KDC in a case where we were not expecting to use FAST, and we have an
- * armor ticket available, then we want to use FAST. That involves clearing
- * out the pre-auth state, reinitializing the plugins and trying again with an
- * armor key.
- *
- * Secondly, using the negotiation can cause problems with some older KDCs.
- * Negotiation involves including a special padata item. Some KDCs, including
- * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
- * pre-authentication is required and unknown padata are included in the
- * request. To make matters worse, these KDCs typically do not include a list
- * of padata in PREAUTH_FAILED errors. So, if we get PREAUTH_FAILED and we
- * generated no pre-authentication other than the negotiation then we want to
- * retry without negotiation. In this case it is probably also desirable to
- * retry with the preauth plugin state cleared.
- *
- * In all these cases we should not start over more than once. Control flow is
- * managed by several variables.
- *
- * sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
- * no restart on PREAUTH_FAILED
- *
- * KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
- * armor; if not set, then we can't use FAST even if the KDC wants to.
- *
- * have_restarted: true if we've already restarted
- */
-static krb5_boolean
-negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
- krb5_pa_data **padata)
-{
- if (ctx->have_restarted)
- return FALSE;
- if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
- TRACE_INIT_CREDS_RESTART_FAST(context);
- return TRUE;
- }
- if (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
- !ctx->sent_nontrivial_preauth) {
- TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(context);
- return TRUE;
- }
- return FALSE;
-}
-
/* Ensure that the reply enctype was among the requested enctypes. */
static krb5_error_code
check_reply_enctype(krb5_init_creds_context ctx)
@@ -1436,16 +1373,20 @@ init_creds_step_reply(krb5_context context,
if (code != 0)
goto cleanup;
reply_code = ctx->err_reply->error;
- if (negotiation_requests_restart(context, ctx, ctx->err_padata)) {
- ctx->have_restarted = 1;
- k5_preauth_request_context_fini(context);
- if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
- ctx->enc_pa_rep_permitted = 0;
- code = restart_init_creds_loop(context, ctx, ctx->err_padata);
- krb5_free_error(context, ctx->err_reply);
- ctx->err_reply = NULL;
- krb5_free_pa_data(context, ctx->err_padata);
- ctx->err_padata = NULL;
+ if (!ctx->restarted &&
+ k5_upgrade_to_fast_p(context, ctx->fast_state, ctx->err_padata)) {
+ /* Retry with FAST after discovering that the KDC supports
+ * it. (FAST negotiation usually avoids this restart.) */
+ TRACE_FAST_PADATA_UPGRADE(context);
+ ctx->restarted = TRUE;
+ code = restart_init_creds_loop(context, ctx, TRUE);
+ } else if (!ctx->restarted && reply_code == KDC_ERR_PREAUTH_FAILED &&
+ !ctx->sent_nontrivial_preauth) {
+ /* The KDC didn't like our informational padata (probably a pre-1.7
+ * MIT krb5 KDC). Retry without it. */
+ ctx->enc_pa_rep_permitted = FALSE;
+ ctx->restarted = TRUE;
+ code = restart_init_creds_loop(context, ctx, FALSE);
} else if ((reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED ||
reply_code == KDC_ERR_PREAUTH_REQUIRED) && retry) {
/* reset the list of preauth types to try */
@@ -1471,16 +1412,13 @@ init_creds_step_reply(krb5_context context,
code = krb5int_copy_data_contents(context,
&ctx->err_reply->client->realm,
&ctx->request->client->realm);
- /* This will trigger a new call to k5_preauth(). */
- krb5_free_error(context, ctx->err_reply);
- ctx->err_reply = NULL;
- k5_preauth_request_context_fini(context);
- /* Permit another negotiation based restart. */
- ctx->have_restarted = 0;
- ctx->sent_nontrivial_preauth = 0;
- code = restart_init_creds_loop(context, ctx, NULL);
if (code != 0)
goto cleanup;
+ /* Reset per-realm negotiation state. */
+ ctx->restarted = FALSE;
+ ctx->sent_nontrivial_preauth = FALSE;
+ ctx->enc_pa_rep_permitted = TRUE;
+ code = restart_init_creds_loop(context, ctx, FALSE);
} else {
if (retry) {
code = 0;
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index 6ecf096..38c01c7 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -55,7 +55,7 @@ struct _krb5_init_creds_context {
krb5_keyblock as_key;
krb5_enctype etype;
krb5_boolean enc_pa_rep_permitted;
- krb5_boolean have_restarted;
+ krb5_boolean restarted;
krb5_boolean sent_nontrivial_preauth;
krb5_boolean preauth_required;
struct krb5_responder_context_st rctx;
More information about the cvs-krb5
mailing list