aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Hartman <hartmans@mit.edu>2009-11-24 01:14:07 +0000
committerSam Hartman <hartmans@mit.edu>2009-11-24 01:14:07 +0000
commit95656e7f86c1970112621e214599dc90433c208b (patch)
tree9b4bbfcc8affebdb62a236a3182707583890bb0b /src
parentd0a1138ac5479a7d197a9b778d62a3928292ecbb (diff)
downloadkrb5-95656e7f86c1970112621e214599dc90433c208b.zip
krb5-95656e7f86c1970112621e214599dc90433c208b.tar.gz
krb5-95656e7f86c1970112621e214599dc90433c208b.tar.bz2
Implement upgrade to FAST when the KDC supports FAST. Implement fall
back to no negotiation when the KDC doesn't appear to support it. In order to do this control flow for get_init_creds is changed significantly. A comment in the diff explains the logic. * Move preauth_request_init into loop * move preauth gic option handling into loop * New function krb5int_upgrade_to_fast_p * New fast state flag: KRB5INT_FAST_ARMOR_AVAIL git-svn-id: svn://anonsvn.mit.edu/krb5/users/hartmans/fast-negotiate@23338 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/lib/krb5/krb/fast.c18
-rw-r--r--src/lib/krb5/krb/fast.h6
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c151
3 files changed, 127 insertions, 48 deletions
diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c
index 9e250d4..8562e90 100644
--- a/src/lib/krb5/krb/fast.c
+++ b/src/lib/krb5/krb/fast.c
@@ -144,6 +144,7 @@ krb5int_fast_as_armor(krb5_context context,
krb5_clear_error_message(context);
target_realm = krb5_princ_realm(context, request->server);
if (opte->opt_private->fast_ccache_name) {
+ state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
&ccache);
if (retval == 0)
@@ -155,11 +156,13 @@ krb5int_fast_as_armor(krb5_context context,
target_principal, KRB5_CCCONF_FAST_AVAIL,
&config_data);
if ((retval == 0) && config_data.data )
- opte->opt_private->fast_flags |= KRB5_FAST_REQUIRED;
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
krb5_free_data_contents(context, &config_data);
retval = 0;
}
- if (retval==0 && (opte->opt_private->fast_flags &KRB5_FAST_REQUIRED))
+ if (opte->opt_private->fast_flags& KRB5_FAST_REQUIRED)
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ if (retval==0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST))
retval = fast_armor_ap_request(context, state, ccache,
target_principal);
if (retval != 0) {
@@ -587,3 +590,14 @@ krb5_error_code krb5int_fast_verify_nego
krb5_free_checksum(context, checksum);
return retval;
}
+krb5_boolean krb5int_upgrade_to_fast_p
+(krb5_context context, struct krb5int_fast_request_state *state, krb5_pa_data **padata)
+{
+ if (! (state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
+ return 0;
+ if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
+ state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/lib/krb5/krb/fast.h b/src/lib/krb5/krb/fast.h
index 74b4136..db0aa84 100644
--- a/src/lib/krb5/krb/fast.h
+++ b/src/lib/krb5/krb/fast.h
@@ -41,6 +41,9 @@ struct krb5int_fast_request_state {
krb5_ui_4 fast_options;
krb5_int32 nonce;
};
+#define KRB5INT_FAST_DO_FAST (1l<<0) /*perform FAST*/
+#define KRB5INT_FAST_ARMOR_AVAIL (1l<<1)
+
krb5_error_code
krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state,
@@ -84,6 +87,9 @@ krb5_error_code krb5int_fast_verify_nego
krb5_kdc_rep *rep, krb5_data *request,
krb5_keyblock *decrypting_key, krb5_boolean *fast_avail);
+krb5_boolean krb5int_upgrade_to_fast_p
+(krb5_context context, struct krb5int_fast_request_state *state, krb5_pa_data **padata);
+
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 6d310cb..268a959 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1116,7 +1116,11 @@ krb5_get_init_creds(krb5_context context,
int canon_flag = 0;
krb5_principal_data referred_client;
krb5_boolean retry = 0;
- krb5_boolean fast_avail = 0; /*The KDC for this realm supports fast; output of negotiation*/
+ krb5_boolean enc_pa_rep_permitted = 1;
+ krb5_boolean fast_avail = 0; /*The KDC for this realm supports fast; output
+ * of negotiation*/
+ krb5_boolean sent_nontrivial_preauth = 0;
+ krb5_boolean have_restarted = 0;
struct krb5int_fast_request_state *fast_state = NULL;
krb5_pa_data **out_padata = NULL;
@@ -1272,9 +1276,6 @@ krb5_get_init_creds(krb5_context context,
request.client, &request.server)))
goto cleanup;
- krb5_preauth_request_context_init(context);
-
-
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
request.ktype = options->etype_list;
request.nktypes = options->etype_list_length;
@@ -1310,12 +1311,6 @@ krb5_get_init_creds(krb5_context context,
/* set up the other state. */
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
- if ((ret = make_preauth_list(context, options->preauth_list,
- options->preauth_list_length,
- &preauth_to_use)))
- goto cleanup;
- }
/* the salt is allocated from somewhere, unless it is from the caller,
then it is a reference */
@@ -1326,46 +1321,90 @@ krb5_get_init_creds(krb5_context context,
salt.length = SALT_TYPE_AFS_LENGTH;
salt.data = NULL;
}
-
-
- /* set the request nonce */
- if ((ret = krb5_timeofday(context, &time_now)))
- goto cleanup;
- /*
- * XXX we know they are the same size... and we should do
- * something better than just the current time
- */
- {
- unsigned char random_buf[4];
- krb5_data random_data;
-
- random_data.length = 4;
- random_data.data = (char *)random_buf;
- if (krb5_c_random_make_octets(context, &random_data) == 0)
- /* See RT ticket 3196 at MIT. If we set the high bit, we
- may have compatibility problems with Heimdal, because
- we (incorrectly) encode this value as signed. */
- request.nonce = 0x7fffffff & load_32_n(random_buf);
- else
- /* XXX Yuck. Old version. */
- request.nonce = (krb5_int32) time_now;
- }
- ret = krb5int_fast_as_armor(context, fast_state, options, &request);
- if (ret != 0)
- goto cleanup;
- /* give the preauth plugins a chance to prep the request body */
- krb5_preauth_prepare_request(context, options, &request);
- ret = krb5int_fast_prep_req_body(context, fast_state,
- &request, &encoded_request_body);
- if (ret)
- goto cleanup;
-
get_data_rock.magic = CLIENT_ROCK_MAGIC;
get_data_rock.etype = &etype;
get_data_rock.fast_state = fast_state;
/* now, loop processing preauth data and talking to the kdc */
+ /* 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
+ */
for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
+ start_of_loop: if (loopcount == 0) {
+ krb5_preauth_request_context_init(context);
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
+ if ((ret = make_preauth_list(context, options->preauth_list,
+ options->preauth_list_length,
+ &preauth_to_use)))
+ goto cleanup;
+ }
+
+
+ /* set the request nonce */
+ if ((ret = krb5_timeofday(context, &time_now)))
+ goto cleanup;
+ /*
+ * XXX we know they are the same size... and we should do
+ * something better than just the current time
+ */
+ {
+ unsigned char random_buf[4];
+ krb5_data random_data;
+
+ random_data.length = 4;
+ random_data.data = (char *)random_buf;
+ if (krb5_c_random_make_octets(context, &random_data) == 0)
+ /* See RT ticket 3196 at MIT. If we set the high bit, we
+ may have compatibility problems with Heimdal, because
+ we (incorrectly) encode this value as signed. */
+ request.nonce = 0x7fffffff & load_32_n(random_buf);
+ else
+ /* XXX Yuck. Old version. */
+ request.nonce = (krb5_int32) time_now;
+ }
+ ret = krb5int_fast_as_armor(context, fast_state, options, &request);
+ if (ret != 0)
+ goto cleanup;
+ /* give the preauth plugins a chance to prep the request body */
+ krb5_preauth_prepare_request(context, options, &request);
+ ret = krb5int_fast_prep_req_body(context, fast_state,
+ &request, &encoded_request_body);
+ if (ret)
+ goto cleanup;
+ }
+
if (request.padata) {
krb5_free_pa_data(context, request.padata);
request.padata = NULL;
@@ -1421,7 +1460,10 @@ krb5_get_init_creds(krb5_context context,
krb5_free_data(context, encoded_previous_request);
encoded_previous_request = NULL;
}
- ret = request_enc_pa_rep(&request.padata);
+ if (request.padata)
+ sent_nontrivial_preauth = 1;
+ if (enc_pa_rep_permitted)
+ ret = request_enc_pa_rep(&request.padata);
if (ret)
goto cleanup;
ret = krb5int_fast_prep_req(context, fast_state,
@@ -1438,10 +1480,27 @@ krb5_get_init_creds(krb5_context context,
goto cleanup;
if (err_reply) {
+ krb5_boolean upgrade_to_fast;
ret = krb5int_fast_process_error(context, fast_state, &err_reply,
&out_padata, &retry);
- if (ret !=0)
+ if (ret !=0)
goto cleanup;
+ upgrade_to_fast = krb5int_upgrade_to_fast_p(context, fast_state, out_padata);
+ if ((loopcount == 0) &&(!have_restarted) && (
+ upgrade_to_fast
+ || (err_reply->error == KDC_ERR_PREAUTH_FAILED && (!sent_nontrivial_preauth)))) {
+ loopcount = 0;
+ krb5_preauth_request_context_fini(context);
+ have_restarted = 1;
+ if (!upgrade_to_fast)
+ enc_pa_rep_permitted = 0;
+ krb5_free_error(context, err_reply);
+ err_reply = 0;
+ if (preauth_to_use)
+ krb5_free_pa_data(context, preauth_to_use);
+ preauth_to_use = NULL;
+ goto start_of_loop;
+ }
if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) {
/* reset the list of preauth types to try */
if (preauth_to_use) {