diff options
author | Sam Hartman <hartmans@mit.edu> | 2009-12-02 16:16:47 +0000 |
---|---|---|
committer | Sam Hartman <hartmans@mit.edu> | 2009-12-02 16:16:47 +0000 |
commit | fcb0229ae9f6256fd15ee858e2830f88f2bcdd34 (patch) | |
tree | 13cd845c03b8959fa7879547a249cddaf3800479 | |
parent | ac54e362341b21452fe2bd1a28f2b851b11aed88 (diff) | |
download | krb5-fcb0229ae9f6256fd15ee858e2830f88f2bcdd34.zip krb5-fcb0229ae9f6256fd15ee858e2830f88f2bcdd34.tar.gz krb5-fcb0229ae9f6256fd15ee858e2830f88f2bcdd34.tar.bz2 |
While developing the fast negotiation changes, the get_in_tkt code was
reorganized to be more maintainable and asynchronous. This commit
forward ports old changes found in users/hartmans/fast-negotiate to
the new architecture. All the changes were ported at once rather than
porting each individual change.
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/fast-negotiate@23421 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r-- | src/lib/krb5/krb/get_in_tkt.c | 205 | ||||
-rw-r--r-- | src/lib/krb5/krb/init_creds_ctx.h | 1 |
2 files changed, 157 insertions, 49 deletions
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 4728015..679f86f 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -545,6 +545,29 @@ tgt_is_local_realm(krb5_creds *tgt) && data_eq(tgt->server->realm, tgt->client->realm)); } +static krb5_error_code request_enc_pa_rep(krb5_pa_data ***padptr) +{ + size_t size = 0; + krb5_pa_data **pad = *padptr; + krb5_pa_data *pa= NULL; + if (pad) + for (size=0; pad[size]; size++); + pad = realloc(pad, sizeof(*pad)*(size+2)); + + if (pad == NULL) + return ENOMEM; + pad[size+1] = NULL; + pa = malloc(sizeof(krb5_pa_data)); + if (pa == NULL) + return ENOMEM; + pa->contents = NULL; + pa->length = 0; + pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP; + pad[size] = pa; + *padptr = pad; + return 0; +} + krb5_error_code KRB5_CALLCONV krb5_get_in_tkt(krb5_context context, krb5_flags options, @@ -1234,6 +1257,44 @@ cleanup: return code; } +static krb5_error_code +restart_init_creds_loop(krb5_context context, + krb5_init_creds_context *ctx) +{ + krb5_error_code ret = 0; + unsigned char random_buf[4]; + krb5_data random_data; + if (ctx->preauth_to_use) { + krb5_free_pa_data(context, ctx->preauth_to_use); + ctx->preauth_to_use = NULL; + } + krb5_preauth_request_context_init(context); + if (ctx->encoded_request_body) { + krb5_free_data(context, ctx->encoded_request_body); + ctx->encoded_request_body = NULL; + } + ctx->loop_count = 0; + if (ctx->opte && (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) { + if ((ret = make_preauth_list(context, ctx->opte->preauth_list, + ctx->opte->preauth_list_length, + &ctx->preauth_to_use))) + goto cleanup; + } + + /* set the request nonce */ + random_data.length = 4; + random_data.data = (char *)random_buf; + ret = krb5_c_random_make_octets(context, &random_data); + if (ret !=0) + goto cleanup; +/* 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. */ + ctx->request->nonce = 0x7fffffff & load_32_n(random_buf); +cleanup: + return ret; +} + krb5_error_code KRB5_CALLCONV krb5_init_creds_init(krb5_context context, krb5_principal client, @@ -1256,7 +1317,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; code = krb5_copy_principal(context, client, &ctx->request->client); if (code != 0) goto cleanup; @@ -1388,8 +1449,8 @@ krb5_init_creds_init(krb5_context context, if (code != 0) goto cleanup; } else if (krb5_libdefault_boolean(context, &ctx->request->client->realm, - KRB5_CONF_NOADDRESSES, &tmp) != 0 - || tmp) { + KRB5_CONF_NOADDRESSES, &tmp) != 0 + || tmp) { ctx->request->addresses = NULL; } else { code = krb5_os_localaddr(context, &ctx->request->addresses); @@ -1397,18 +1458,6 @@ krb5_init_creds_init(krb5_context context, goto cleanup; } - /* initial preauth state */ - krb5_preauth_request_context_init(context); - - if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { - code = make_preauth_list(context, - opte->preauth_list, - opte->preauth_list_length, - &ctx->preauth_to_use); - if (code != 0) - goto cleanup; - } - if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) { code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt); if (code != 0) @@ -1418,33 +1467,7 @@ krb5_init_creds_init(krb5_context context, ctx->salt.data = NULL; } - /* nonce */ - { - unsigned char random_buf[4]; - krb5_data random_data; - - random_data.length = sizeof(random_buf); - random_data.data = (char *)random_buf; - - /* - * 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. - */ - if (krb5_c_random_make_octets(context, &random_data) == 0) - ctx->request->nonce = 0x7fffffff & load_32_n(random_buf); - else { - krb5_timestamp now; - - code = krb5_timeofday(context, &now); - if (code != 0) - goto cleanup; - - ctx->request->nonce = (krb5_int32)now; - } - } - - ctx->loopcount = 0; + code = restart_init_creds_loop(context, ctx); *pctx = ctx; @@ -1657,7 +1680,10 @@ init_creds_step_request(krb5_context context, krb5_free_data(context, ctx->encoded_previous_request); ctx->encoded_previous_request = NULL; } - + if (ctx->enc_pa_rep_permitted) + code = request_enc_pa_rep(&ctx->request->padata); + if (code) + goto cleanup; code = krb5int_fast_prep_req(context, ctx->fast_state, ctx->request, ctx->encoded_request_body, encode_krb5_as_req, @@ -1675,6 +1701,54 @@ 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->loopcount == 0) &&(!ctx->have_restarted) && ( + krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata) + || (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED && + (!ctx->sent_nontrivial_preauth)))) + return 1; + return 0; +} + static krb5_error_code init_creds_step_reply(krb5_context context, krb5_init_creds_context ctx, @@ -1687,6 +1761,7 @@ init_creds_step_reply(krb5_context context, int canon_flag = 0; krb5_keyblock *strengthen_key = NULL; krb5_keyblock encrypting_key; + krb5_boolean fast_avail; encrypting_key.length = 0; encrypting_key.contents = NULL; @@ -1705,8 +1780,15 @@ init_creds_step_reply(krb5_context context, &ctx->err_reply, &padata, &retry); if (code != 0) goto cleanup; - - if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) { + if (negotiation_requests_restart(context, ctx, out_padata)) { + ctx->have_restarted = 1; + krb5_preauth_request_context_fini(context); + krb5_free_error(context, ctx->err_reply); + ctx->err_reply = NULL; + 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); + }else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) { /* reset the list of preauth types to try */ krb5_free_pa_data(context, ctx->preauth_to_use); ctx->preauth_to_use = padata; @@ -1738,7 +1820,7 @@ init_creds_step_reply(krb5_context context, } else { /* error + no hints = give up */ code = (krb5_error_code)ctx->err_reply->error + - ERROR_TABLE_BASE_krb5; + ERROR_TABLE_BASE_krb5; } } @@ -1812,7 +1894,7 @@ init_creds_step_reply(krb5_context context, again. */ if (ctx->as_key.length) { code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key, - &encrypting_key); + &encrypting_key); if (code != 0) goto cleanup; code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL, @@ -1843,6 +1925,11 @@ init_creds_step_reply(krb5_context context, goto cleanup; } + code = krb5int_fast_verify_nego(context, ctx->fast_state, + ctx->reply, ctx->encoded_previous_request, + &encrypting_key, &fast_avail); + if (code) + goto cleanup; code = verify_as_reply(context, ctx->request_time, ctx->request, ctx->reply); if (code != 0) @@ -1852,6 +1939,29 @@ init_creds_step_reply(krb5_context context, ctx->reply, &ctx->cred, NULL); if (code != 0) goto cleanup; + if (ctx->opte&&ctx->opte->opt_private->out_ccache) { + krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache; + krb5_data config_data; + code = krb5_cc_initialize(context, out_ccache, ctx->cred->client); + if (code != 0) + goto cc_cleanup; + code = krb5_cc_store_cred(context, out_ccache, &ctx->cred); + if (code != 0) + goto cc_cleanup; + if (fast_avail) { + config_data.data = "yes"; + config_data.length = strlen(config_data.data); + code = krb5_cc_set_config(context, out_ccache, ctx->cred.server, + KRB5_CCCONF_FAST_AVAIL, &config_data); + } + cc_cleanup: + if (code !=0) { + const char *msg; + msg = krb5_get_error_message(context, code); + krb5_set_error_message(context, code, "%s while storing credentials", msg); + krb5_free_error_message(context, msg); + } + } krb5_preauth_request_context_fini(context); @@ -2002,4 +2112,3 @@ cleanup: return code; } - diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h index 5712d94..1d41a44 100644 --- a/src/lib/krb5/krb/init_creds_ctx.h +++ b/src/lib/krb5/krb/init_creds_ctx.h @@ -49,4 +49,3 @@ krb5_get_as_key_password(krb5_context context, void *gak_data); #endif /* !KRB5_INIT_CREDS_CONTEXT */ - |