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); assert(state->armor_key); fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST); if (fx_reply == NULL) retval = KRB5_ERR_FAST_REQUIRED; TRACE_FAST_DECODE(context); 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) k5_prependmsg(context, retval, _("Failed to decrypt FAST reply")); if (retval == 0) retval = decode_krb5_fast_response(&scratch, &local_resp); if (retval == 0) { if (local_resp->nonce != state->nonce) { retval = KRB5_KDCREP_MODIFIED; k5_setmsg(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); if (local_resp) krb5_free_fast_response(context, local_resp); return retval; }
krb5_error_code krb5int_fast_process_response(krb5_context context, struct krb5int_fast_request_state *state, krb5_kdc_rep *resp, krb5_keyblock **strengthen_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); *strengthen_key = NULL; if (state->armor_key == 0) return 0; 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; *strengthen_key = fast_response->strengthen_key; fast_response->strengthen_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; }
/* * If state contains an armor key and *err_replyptr contains a FAST error, * decode it and set *err_replyptr to the inner error and *out_padata to the * padata in the FAST response. Otherwise, leave *err_replyptr alone and set * *out_padata to the error e_data decoded as pa-data or typed-data, or to NULL * if it doesn't decode as either. In either case, set *retry to indicate * whether the client should try to make a follow-up request. */ krb5_error_code krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_state *state, krb5_error **err_replyptr, krb5_pa_data ***out_padata, krb5_boolean *retry) { krb5_error_code retval = 0; krb5_error *err_reply = *err_replyptr; krb5_pa_data *fx_error_pa; krb5_pa_data **result = NULL; krb5_data scratch = empty_data(); krb5_error *fx_error = NULL; krb5_fast_response *fast_response = NULL; if (out_padata) *out_padata = NULL; if (retry) *retry = 0; if (state->armor_key) { retval = decode_krb5_padata_sequence(&err_reply->e_data, &result); if (retval == 0) 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. */ if (retry != NULL) *retry = 0; krb5_free_pa_data(context, result); return 0; } if (retval == 0) { fx_error_pa = krb5int_find_pa_data(context, fast_response->padata, KRB5_PADATA_FX_ERROR); if (fx_error_pa == NULL) { k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED, _("Expecting FX_ERROR pa-data inside FAST " "container")); retval = KRB5KDC_ERR_PREAUTH_FAILED; } } if (retval == 0) { scratch = make_data(fx_error_pa->contents, fx_error_pa->length); retval = decode_krb5_error(&scratch, &fx_error); } if (retval == 0) { krb5_free_error(context, err_reply); *err_replyptr = fx_error; fx_error = NULL; if (out_padata) { *out_padata = fast_response->padata; fast_response->padata = NULL; } /* * If there is more than the fx_error padata, then we want * to retry the error if a cookie is present */ if (retry != NULL) { *retry = (*out_padata)[1] != NULL; if (krb5int_find_pa_data(context, *out_padata, KRB5_PADATA_FX_COOKIE) == NULL) *retry = 0; } } } else { /*not FAST*/ /* Possibly retry if there's any e_data to process. */ if (retry) *retry = (err_reply->e_data.length > 0); /* Try to decode e_data as pa-data or typed-data for out_padata. */ if (out_padata) { retval = decode_krb5_padata_sequence(&err_reply->e_data, out_padata); if (retval != 0) { (void)decode_krb5_typed_data(&err_reply->e_data, out_padata); retval = 0; } } } krb5_free_pa_data(context, result); krb5_free_fast_response(context, fast_response); if (fx_error) krb5_free_error(context, fx_error); 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 * making available to mechanisms in order for them to respond to an * error. The plugin interface in March 2009 does not permit * separating these concepts for the plugins. This function makes * both available for future revisions to the plugin interface. It * also re-encodes the padata from the current error as a encoded * typed-data and puts that in the e_data field. That will allow * existing plugins with the old interface to find the error data. * The output parameter out_padata contains the padata from the error * whenever padata is available (all the time with fast). */ krb5_error_code krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_state *state, krb5_error **err_replyptr, krb5_pa_data ***out_padata, krb5_boolean *retry) { krb5_error_code retval = 0; krb5_error *err_reply = *err_replyptr; *out_padata = NULL; *retry = 0; if (state->armor_key) { 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) 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. */ *retry = 0; krb5_free_pa_data(context, result); return 0; } krb5_free_pa_data(context, result); result = NULL; if (retval == 0) { fx_error_pa = krb5int_find_pa_data(context, fast_response->padata, KRB5_PADATA_FX_ERROR); if (fx_error_pa == NULL) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "Expecting FX_ERROR pa-data inside " "FAST container"); retval = KRB5KDC_ERR_PREAUTH_FAILED; } } if (retval == 0) { scratch.data = (char *) fx_error_pa->contents; scratch.length = fx_error_pa->length; retval = decode_krb5_error(&scratch, &fx_error); } /* * krb5_pa_data and krb5_typed_data are safe to cast between: * they have the same type fields in the same order. * (krb5_preauthtype is a krb5_int32). If krb5_typed_data is * ever changed then this will need to be a copy not a cast. */ if (retval == 0) retval = encode_krb5_typed_data((const krb5_typed_data **) fast_response->padata, &encoded_td); if (retval == 0) { fx_error->e_data = *encoded_td; free(encoded_td); /*contents owned by fx_error*/ encoded_td = NULL; krb5_free_error(context, err_reply); *err_replyptr = fx_error; fx_error = NULL; *out_padata = fast_response->padata; fast_response->padata = NULL; /* * If there is more than the fx_error padata, then we want * to retry the error if a cookie is present */ *retry = (*out_padata)[1] != NULL; if (krb5int_find_pa_data(context, *out_padata, KRB5_PADATA_FX_COOKIE) == NULL) *retry = 0; } if (fx_error) krb5_free_error(context, fx_error); krb5_free_fast_response(context, fast_response); } else { /*not FAST*/ *retry = (err_reply->e_data.length > 0); if ((err_reply->error == KDC_ERR_PREAUTH_REQUIRED || err_reply->error == KDC_ERR_PREAUTH_FAILED) && err_reply->e_data.length) { krb5_pa_data **result = NULL; retval = decode_krb5_padata_sequence(&err_reply->e_data, &result); if (retval == 0) { *out_padata = result; return 0; } krb5_free_pa_data(context, result); krb5_set_error_message(context, retval, "Error decoding padata in error reply"); return retval; } } return retval; }