From: Sam Hartman Date: Thu, 26 Mar 2009 05:37:25 +0000 (+0000) Subject: FAST encrypted response for client X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c992aaaf9cefe23252157e85738fa06e2b2571e;p=thirdparty%2Fkrb5.git FAST encrypted response for client Implement routine to decrypt FAST response. Use this in process_error. Implement new krb5int_fast_process_response to process FAST in an AS-REP or TGS-rep. Call that routine from krb5_get_init_creds. Add a new error code for FAST required but not supported. git-svn-id: svn://anonsvn.mit.edu/krb5/branches/fast@22140 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et index 02351e8737..5698f1e4a7 100644 --- a/src/lib/krb5/error_tables/krb5_err.et +++ b/src/lib/krb5/error_tables/krb5_err.et @@ -347,4 +347,5 @@ error_code KRB5_PLUGIN_NO_HANDLE, "Supplied data not handled by this plugin" error_code KRB5_PLUGIN_OP_NOTSUPP, "Plugin does not support the operaton" error_code KRB5_ERR_INVALID_UTF8, "Invalid UTF-8 string" +error_code KRB5_ERR_FAST_REQUIRED, "FAST protected pre-authentication required but not supported by KDC" end diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c index 5eac7477f7..51dfe04da7 100644 --- a/src/lib/krb5/krb/fast.c +++ b/src/lib/krb5/krb/fast.c @@ -246,6 +246,63 @@ krb5int_fast_prep_req (krb5_context context, struct krb5int_fast_request_state * return retval; } +static krb5_error_code decrypt_fast_reply +(krb5_context context, struct krb5int_fast_request_state *state, + krb5_pa_data **in_padata, + krb5_fast_response **response) +{ + krb5_error_code retval = 0; + krb5_data scratch; + krb5_enc_data *encrypted_response = NULL; + krb5_pa_data *fx_reply = NULL; + krb5_fast_response *local_resp = NULL; + assert(state != NULL); + if (state->armor_key == NULL) + return 0; + fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST); + if (fx_reply == NULL) + retval = KRB5_ERR_FAST_REQUIRED; + if (retval == 0) { + scratch.data = (char *) fx_reply->contents; + scratch.length = fx_reply->length; + retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response); + } + scratch.data = NULL; + if (retval == 0) { + scratch.data = malloc(encrypted_response->ciphertext.length); + if (scratch.data == NULL) + retval = ENOMEM; + scratch.length = encrypted_response->ciphertext.length; + } + if (retval == 0) + retval = krb5_c_decrypt(context, state->armor_key, + KRB5_KEYUSAGE_FAST_REP, NULL, + encrypted_response, &scratch); + if (retval != 0) { + const char * errmsg; + errmsg = krb5_get_error_message(context, retval); + krb5_set_error_message(context, retval, "%s while decrypting FAST reply", errmsg); + krb5_free_error_message(context, errmsg); + } + if (retval == 0) + retval = decode_krb5_fast_response(&scratch, &local_resp); + if (retval == 0) { + if (local_resp->nonce != state->nonce) { + retval = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, retval, "nonce modified in FAST response: KDC response modified"); + } + } + if (retval == 0) { + *response = local_resp; + local_resp = NULL; + } + if (scratch.data) + free(scratch.data); + if (encrypted_response) + krb5_free_enc_data(context, encrypted_response); + return retval; +} + /* * FAST separates two concepts: the set of padata we're using to * decide what pre-auth mechanisms to use and the set of padata we're @@ -269,15 +326,15 @@ krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_sta *out_padata = NULL; *retry = 0; if (state->armor_key) { - krb5_pa_data *fast_pa, *fx_error_pa; + krb5_pa_data *fx_error_pa; krb5_pa_data **result = NULL; krb5_data scratch, *encoded_td = NULL; krb5_error *fx_error = NULL; krb5_fast_response *fast_response = NULL; retval = decode_krb5_padata_sequence(&err_reply->e_data, &result); if (retval == 0) - fast_pa = krb5int_find_pa_data(context, result, KRB5_PADATA_FX_FAST); - if (retval || fast_pa == NULL) { + retval = decrypt_fast_reply(context, state, result, &fast_response); + if (retval) { /*This can happen if the KDC does not understand FAST. We * don't expect that, but treating it as the fatal error * indicated by the KDC seems reasonable. @@ -286,17 +343,8 @@ krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_sta krb5_free_pa_data(context, result); return 0; } - scratch.data = (char *) fast_pa->contents; - scratch.length = fast_pa->length; - retval = decode_krb5_fast_response(&scratch, &fast_response); krb5_free_pa_data(context, result); result = NULL; - if (retval == 0) { - if (fast_response->nonce != state->nonce) { - krb5_set_error_message(context, KRB5_KDCREP_MODIFIED, "Nonce in reply did not match expected value"); - retval = KRB5_KDCREP_MODIFIED; - } - } if (retval == 0) { fx_error_pa = krb5int_find_pa_data(context, fast_response->padata, KRB5_PADATA_FX_ERROR); if (fx_error_pa == NULL) { @@ -317,7 +365,7 @@ krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_sta */ if (retval == 0) retval = encode_krb5_typed_data( (krb5_typed_data **) fast_response->padata, - &encoded_td); + &encoded_td); if (retval == 0) { fx_error->e_data = *encoded_td; free(encoded_td); /*contents owned by fx_error*/ @@ -353,10 +401,58 @@ krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_sta "Error decoding padata in error reply"); return retval; } - } + } return retval; } + +krb5_error_code krb5int_fast_process_response +(krb5_context context, struct krb5int_fast_request_state *state, + krb5_kdc_rep *resp, + krb5_keyblock **as_key) +{ + krb5_error_code retval = 0; + krb5_fast_response *fast_response = NULL; + krb5_data *encoded_ticket = NULL; + krb5_boolean cksum_valid; + krb5_clear_error_message(context); + *as_key = NULL; + retval = decrypt_fast_reply(context, state, resp->padata, + &fast_response); + if (retval == 0) { + if (fast_response->finished == 0) { + retval = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, retval, "FAST response missing finish message in KDC reply"); + } + } + if (retval == 0) + retval = encode_krb5_ticket(resp->ticket, &encoded_ticket); + if (retval == 0) + retval = krb5_c_verify_checksum(context, state->armor_key, + KRB5_KEYUSAGE_FAST_FINISHED, + encoded_ticket, + &fast_response->finished->ticket_checksum, + &cksum_valid); + if (retval == 0 && cksum_valid == 0) { + retval = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, retval, "ticket modified in KDC reply"); + } + if (retval == 0) { + krb5_free_principal(context, resp->client); + resp->client = fast_response->finished->client; + fast_response->finished->client = NULL; + *as_key = fast_response->rep_key; + fast_response->rep_key = NULL; + krb5_free_pa_data(context, resp->padata); + resp->padata = fast_response->padata; + fast_response->padata = NULL; + } + if (fast_response) + krb5_free_fast_response(context, fast_response); + if (encoded_ticket) + krb5_free_data(context, encoded_ticket); + return retval; +} krb5_error_code krb5int_fast_make_state( krb5_context context, struct krb5int_fast_request_state **state) { @@ -381,6 +477,7 @@ krb5int_fast_free_state( krb5_context context, struct krb5int_fast_request_state free(state->cookie); state->cookie = NULL; } + free(state); } krb5_pa_data * krb5int_find_pa_data diff --git a/src/lib/krb5/krb/fast.h b/src/lib/krb5/krb/fast.h index 29c099de27..e21df6504b 100644 --- a/src/lib/krb5/krb/fast.h +++ b/src/lib/krb5/krb/fast.h @@ -58,6 +58,11 @@ krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_sta krb5_error **err_replyptr , krb5_pa_data ***out_padata, krb5_boolean *retry); +krb5_error_code krb5int_fast_process_response +(krb5_context context, struct krb5int_fast_request_state *state, + krb5_kdc_rep *resp, + krb5_keyblock **as_key); + krb5_error_code krb5int_fast_make_state( krb5_context context, struct krb5int_fast_request_state **state); diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index eb23e544a6..be955ae203 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -968,6 +968,7 @@ krb5_get_init_creds(krb5_context context, krb5_data salt; krb5_data s2kparams; krb5_keyblock as_key; + krb5_keyblock *fast_as_key = NULL; krb5_error *err_reply; krb5_kdc_rep *local_as_reply; krb5_timestamp time_now; @@ -993,7 +994,7 @@ krb5_get_init_creds(krb5_context context, preauth_to_use = NULL; kdc_padata = NULL; as_key.length = 0; - salt.length = 0; + salt.length = 0; salt.data = NULL; local_as_reply = 0; @@ -1396,6 +1397,10 @@ krb5_get_init_creds(krb5_context context, /* process any preauth data in the as_reply */ krb5_clear_preauth_context_use_counts(context); + ret = krb5int_fast_process_response(context, fast_state, + local_as_reply, &fast_as_key); + if (ret) + goto cleanup; if ((ret = sort_krb5_padata_sequence(context, &request.server->realm, local_as_reply->padata))) goto cleanup; @@ -1441,8 +1446,14 @@ krb5_get_init_creds(krb5_context context, it. If decrypting the as_rep fails, or if there isn't an as_key at all yet, then use the gak_fct to get one, and try again. */ - - if (as_key.length) + if (fast_as_key) { + if (as_key.length) + krb5_free_keyblock_contents(context, &as_key); + as_key = *fast_as_key; + free(fast_as_key); + fast_as_key = NULL; + } + if (as_key.length) ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, NULL, &as_key, krb5_kdc_rep_decrypt_proc, NULL); @@ -1499,6 +1510,7 @@ cleanup: } } krb5_preauth_request_context_fini(context); + krb5_free_keyblock(context, fast_as_key); if (fast_state) krb5int_fast_free_state(context, fast_state); if (out_padata)