krb5_error_code KRB5_CALLCONV krb5_rd_error(krb5_context context, const krb5_data *enc_errbuf, krb5_error **dec_error) { if (!krb5_is_krb_error(enc_errbuf)) return KRB5KRB_AP_ERR_MSG_TYPE; return(decode_krb5_error(enc_errbuf, dec_error)); }
static int check_for_svc_unavailable (krb5_context context, const krb5_data *reply, void *msg_handler_data) { krb5_error_code *retval = (krb5_error_code *)msg_handler_data; *retval = 0; if (krb5_is_krb_error(reply)) { krb5_error *err_reply; if (decode_krb5_error(reply, &err_reply) == 0) { *retval = err_reply->error; krb5_free_error(context, err_reply); /* Returning 0 means continue to next KDC */ return (*retval != KDC_ERR_SVC_UNAVAILABLE); } } return 1; }
/* * 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; }
/* * Solaris Kerberos * Same as krb5_send_tgs plus an extra arg to return the FQDN * of the KDC sent the request. */ krb5_error_code krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions, const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, krb5_const_principal sname, krb5_address *const *addrs, krb5_authdata *const *authorization_data, krb5_pa_data *const *padata, const krb5_data *second_ticket, krb5_creds *in_cred, krb5_response *rep, char **hostname_used) { krb5_error_code retval; krb5_kdc_req tgsreq; krb5_data *scratch, scratch2; krb5_ticket *sec_ticket = 0; krb5_ticket *sec_ticket_arr[2]; krb5_timestamp time_now; krb5_pa_data **combined_padata; krb5_pa_data ap_req_padata; int tcp_only = 0, use_master; /* * in_creds MUST be a valid credential NOT just a partially filled in * place holder for us to get credentials for the caller. */ if (!in_cred->ticket.length) return(KRB5_NO_TKT_SUPPLIED); memset((char *)&tgsreq, 0, sizeof(tgsreq)); tgsreq.kdc_options = kdcoptions; tgsreq.server = (krb5_principal) sname; tgsreq.from = timestruct->starttime; tgsreq.till = timestruct->endtime ? timestruct->endtime : in_cred->times.endtime; tgsreq.rtime = timestruct->renew_till; if ((retval = krb5_timeofday(context, &time_now))) return(retval); /* XXX we know they are the same size... */ rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now; rep->request_time = time_now; tgsreq.addresses = (krb5_address **) addrs; if (authorization_data) { /* need to encrypt it in the request */ if ((retval = encode_krb5_authdata(authorization_data, &scratch))) return(retval); if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, scratch, &tgsreq.authorization_data))) { krb5_xfree(tgsreq.authorization_data.ciphertext.data); krb5_free_data(context, scratch); return retval; } krb5_free_data(context, scratch); } /* Get the encryption types list */ if (ktypes) { /* Check passed ktypes and make sure they're valid. */ for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) return KRB5_PROG_ETYPE_NOSUPP; } tgsreq.ktype = (krb5_enctype *)ktypes; } else { /* Get the default ktypes */ /* Solaris Kerberos */ if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype)))) goto send_tgs_error_2; for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++); } if (second_ticket) { if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket))) goto send_tgs_error_1; sec_ticket_arr[0] = sec_ticket; sec_ticket_arr[1] = 0; tgsreq.second_ticket = sec_ticket_arr; } else tgsreq.second_ticket = 0; /* encode the body; then checksum it */ if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) goto send_tgs_error_2; /* * Get an ap_req. */ if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) { krb5_free_data(context, scratch); goto send_tgs_error_2; } krb5_free_data(context, scratch); ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; ap_req_padata.length = scratch2.length; ap_req_padata.contents = (krb5_octet *)scratch2.data; /* combine in any other supplied padata */ if (padata) { krb5_pa_data * const * counter; register unsigned int i = 0; for (counter = padata; *counter; counter++, i++); combined_padata = malloc((i+2) * sizeof(*combined_padata)); if (!combined_padata) { krb5_xfree(ap_req_padata.contents); retval = ENOMEM; goto send_tgs_error_2; } combined_padata[0] = &ap_req_padata; for (i = 1, counter = padata; *counter; counter++, i++) combined_padata[i] = (krb5_pa_data *) *counter; combined_padata[i] = 0; } else { combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); if (!combined_padata) { krb5_xfree(ap_req_padata.contents); retval = ENOMEM; goto send_tgs_error_2; } combined_padata[0] = &ap_req_padata; combined_padata[1] = 0; } tgsreq.padata = combined_padata; /* the TGS_REQ is assembled in tgsreq, so encode it */ if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { krb5_xfree(ap_req_padata.contents); krb5_xfree(combined_padata); goto send_tgs_error_2; } krb5_xfree(ap_req_padata.contents); krb5_xfree(combined_padata); /* now send request & get response from KDC */ send_again: use_master = 0; retval = krb5_sendto_kdc2(context, scratch, krb5_princ_realm(context, sname), &rep->response, &use_master, tcp_only, hostname_used); if (retval == 0) { if (krb5_is_krb_error(&rep->response)) { if (!tcp_only) { krb5_error *err_reply; retval = decode_krb5_error(&rep->response, &err_reply); /* Solaris Kerberos */ if (retval == 0) { if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); free(rep->response.data); rep->response.data = 0; goto send_again; } krb5_free_error(context, err_reply); } } } else if (krb5_is_tgs_rep(&rep->response)) rep->message_type = KRB5_TGS_REP; else /* XXX: assume it's an error */ rep->message_type = KRB5_ERROR; } krb5_free_data(context, scratch); send_tgs_error_2:; if (sec_ticket) krb5_free_ticket(context, sec_ticket); send_tgs_error_1:; if (ktypes == NULL) krb5_xfree(tgsreq.ktype); if (tgsreq.authorization_data.ciphertext.data) { memset(tgsreq.authorization_data.ciphertext.data, 0, tgsreq.authorization_data.ciphertext.length); krb5_xfree(tgsreq.authorization_data.ciphertext.data); } return retval; }
/* * Parse the IAKERB token in input_token and send the contained KDC * request to the KDC for the realm. * * Wrap the KDC reply in output_token. */ static krb5_error_code iakerb_acceptor_step(iakerb_ctx_id_t ctx, int initialContextToken, const gss_buffer_t input_token, gss_buffer_t output_token) { krb5_error_code code; krb5_data request = empty_data(), reply = empty_data(); krb5_data realm = empty_data(); OM_uint32 tmp; int tcp_only, use_master; krb5_ui_4 kdc_code; output_token->length = 0; output_token->value = NULL; if (ctx->count >= IAKERB_MAX_HOPS) { code = KRB5_KDC_UNREACH; goto cleanup; } code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, NULL, &request); if (code != 0) goto cleanup; if (realm.length == 0 || request.length == 0) { code = KRB5_BAD_MSIZE; goto cleanup; } code = iakerb_save_token(ctx, input_token); if (code != 0) goto cleanup; for (tcp_only = 0; tcp_only <= 1; tcp_only++) { use_master = 0; code = krb5_sendto_kdc(ctx->k5c, &request, &realm, &reply, &use_master, tcp_only); if (code == 0 && krb5_is_krb_error(&reply)) { krb5_error *error; code = decode_krb5_error(&reply, &error); if (code != 0) goto cleanup; kdc_code = error->error; krb5_free_error(ctx->k5c, error); if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { krb5_free_data_contents(ctx->k5c, &reply); reply = empty_data(); continue; } } break; } if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { krb5_error error; memset(&error, 0, sizeof(error)); if (code == KRB5_KDC_UNREACH) error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; else if (code == KRB5_REALM_UNKNOWN) error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; code = krb5_mk_error(ctx->k5c, &error, &reply); if (code != 0) goto cleanup; } else if (code != 0) goto cleanup; code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); if (code != 0) goto cleanup; code = iakerb_save_token(ctx, output_token); if (code != 0) goto cleanup; ctx->count++; cleanup: if (code != 0) gss_release_buffer(&tmp, output_token); /* request is a pointer into input_token, no need to free */ krb5_free_data_contents(ctx->k5c, &realm); krb5_free_data_contents(ctx->k5c, &reply); return code; }
krb5_error_code krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, k5_pacb_fn pacb_fn, void *pacb_data, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred, krb5_keyblock **out_subkey) { krb5_error_code retval; krb5_data request_data; krb5_data response_data; krb5_timestamp timestamp; krb5_int32 nonce; krb5_keyblock *subkey = NULL; int tcp_only = 0, use_master = 0; struct krb5int_fast_request_state *fast_state = NULL; request_data.data = NULL; request_data.length = 0; response_data.data = NULL; response_data.length = 0; retval = krb5int_fast_make_state(context, &fast_state); if (retval) goto cleanup; TRACE_GET_CRED_VIA_TKT_EXT(context, in_cred->server, tkt->server, kdcoptions); retval = k5_make_tgs_req(context, fast_state, tkt, kdcoptions, address, in_padata, in_cred, pacb_fn, pacb_data, &request_data, ×tamp, &nonce, &subkey); if (retval != 0) goto cleanup; send_again: use_master = 0; retval = krb5_sendto_kdc(context, &request_data, &in_cred->server->realm, &response_data, &use_master, tcp_only); if (retval == 0) { if (krb5_is_krb_error(&response_data)) { if (!tcp_only) { krb5_error *err_reply; retval = decode_krb5_error(&response_data, &err_reply); if (retval != 0) goto cleanup; retval = krb5int_fast_process_error(context, fast_state, &err_reply, NULL, NULL); if (retval) goto cleanup; if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); krb5_free_data_contents(context, &response_data); goto send_again; } krb5_free_error(context, err_reply); } } } else goto cleanup; retval = krb5int_process_tgs_reply(context, fast_state, &response_data, tkt, kdcoptions, address, in_padata, in_cred, timestamp, nonce, subkey, out_padata, out_enc_padata, out_cred); if (retval != 0) goto cleanup; cleanup: krb5int_fast_free_state(context, fast_state); TRACE_GET_CRED_VIA_TKT_EXT_RETURN(context, retval); krb5_free_data_contents(context, &request_data); krb5_free_data_contents(context, &response_data); if (subkey != NULL) { if (retval == 0 && out_subkey != NULL) *out_subkey = subkey; else krb5_free_keyblock(context, subkey); } return retval; }
krb5_error_code krb5int_process_tgs_reply(krb5_context context, struct krb5int_fast_request_state *fast_state, krb5_data *response_data, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, krb5_timestamp timestamp, krb5_int32 nonce, krb5_keyblock *subkey, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred) { krb5_error_code retval; krb5_kdc_rep *dec_rep = NULL; krb5_error *err_reply = NULL; krb5_boolean s4u2self; s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) || krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER); if (krb5_is_krb_error(response_data)) { retval = decode_krb5_error(response_data, &err_reply); if (retval != 0) goto cleanup; retval = krb5int_fast_process_error(context, fast_state, &err_reply, NULL, NULL); if (retval) goto cleanup; retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5; if (err_reply->text.length > 0) { switch (err_reply->error) { case KRB_ERR_GENERIC: k5_setmsg(context, retval, _("KDC returned error string: %.*s"), err_reply->text.length, err_reply->text.data); break; case KDC_ERR_S_PRINCIPAL_UNKNOWN: { char *s_name; if (err_reply->server && krb5_unparse_name(context, err_reply->server, &s_name) == 0) { k5_setmsg(context, retval, _("Server %s not found in Kerberos database"), s_name); krb5_free_unparsed_name(context, s_name); } else /* In case there's a stale S_PRINCIPAL_UNKNOWN report already noted. */ krb5_clear_error_message(context); } break; } } krb5_free_error(context, err_reply); goto cleanup; } else if (!krb5_is_tgs_rep(response_data)) { retval = KRB5KRB_AP_ERR_MSG_TYPE; goto cleanup; } /* Unfortunately, Heimdal at least up through 1.2 encrypts using the session key not the subsession key. So we try both. */ retval = krb5int_decode_tgs_rep(context, fast_state, response_data, subkey, KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY, &dec_rep); if (retval) { TRACE_TGS_REPLY_DECODE_SESSION(context, &tkt->keyblock); if ((krb5int_decode_tgs_rep(context, fast_state, response_data, &tkt->keyblock, KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0) retval = 0; else goto cleanup; } if (dec_rep->msg_type != KRB5_TGS_REP) { retval = KRB5KRB_AP_ERR_MSG_TYPE; goto cleanup; } /* * Don't trust the ok-as-delegate flag from foreign KDCs unless the * cross-realm TGT also had the ok-as-delegate flag set. */ if (!tgt_is_local_realm(tkt) && !(tkt->ticket_flags & TKT_FLG_OK_AS_DELEGATE)) dec_rep->enc_part2->flags &= ~TKT_FLG_OK_AS_DELEGATE; /* make sure the response hasn't been tampered with..... */ retval = 0; if (s4u2self && !IS_TGS_PRINC(dec_rep->ticket->server)) { /* Final hop, check whether KDC supports S4U2Self */ if (krb5_principal_compare(context, dec_rep->client, in_cred->server)) retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) { /* XXX for constrained delegation this check must be performed by caller * as we don't have access to the key to decrypt the evidence ticket. */ if (!krb5_principal_compare(context, dec_rep->client, tkt->client)) retval = KRB5_KDCREP_MODIFIED; } if (retval == 0) retval = check_reply_server(context, kdcoptions, in_cred, dec_rep); if (dec_rep->enc_part2->nonce != nonce) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_POSTDATED) && (in_cred->times.starttime != 0) && (in_cred->times.starttime != dec_rep->enc_part2->times.starttime)) retval = KRB5_KDCREP_MODIFIED; if ((in_cred->times.endtime != 0) && (dec_rep->enc_part2->times.endtime > in_cred->times.endtime)) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_RENEWABLE) && (in_cred->times.renew_till != 0) && (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till)) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_RENEWABLE_OK) && (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) && (in_cred->times.endtime != 0) && (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime)) retval = KRB5_KDCREP_MODIFIED; if (retval != 0) goto cleanup; if (!in_cred->times.starttime && !in_clock_skew(dec_rep->enc_part2->times.starttime, timestamp)) { retval = KRB5_KDCREP_SKEW; goto cleanup; } if (out_padata != NULL) { *out_padata = dec_rep->padata; dec_rep->padata = NULL; } if (out_enc_padata != NULL) { *out_enc_padata = dec_rep->enc_part2->enc_padata; dec_rep->enc_part2->enc_padata = NULL; } retval = kdcrep2creds(context, dec_rep, address, &in_cred->second_ticket, out_cred); if (retval != 0) goto cleanup; cleanup: if (dec_rep != NULL) { memset(dec_rep->enc_part2->session->contents, 0, dec_rep->enc_part2->session->length); krb5_free_kdc_rep(context, dec_rep); } return retval; }
krb5_error_code krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_pa_data **in_padata, krb5_creds *in_cred, krb5_error_code (*pacb_fct)(krb5_context, krb5_keyblock *, krb5_kdc_req *, void *), void *pacb_data, krb5_pa_data ***out_padata, krb5_pa_data ***out_enc_padata, krb5_creds **out_cred, krb5_keyblock **out_subkey) { krb5_error_code retval; krb5_data request_data; krb5_data response_data; krb5_timestamp timestamp; krb5_int32 nonce; krb5_keyblock *subkey = NULL; int tcp_only = 0, use_master = 0; request_data.data = NULL; request_data.length = 0; response_data.data = NULL; response_data.length = 0; #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off"); krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server); krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server); #endif retval = krb5int_make_tgs_request(context, tkt, kdcoptions, address, in_padata, in_cred, pacb_fct, pacb_data, &request_data, ×tamp, &nonce, &subkey); if (retval != 0) goto cleanup; send_again: use_master = 0; retval = krb5_sendto_kdc(context, &request_data, krb5_princ_realm(context, in_cred->server), &response_data, &use_master, tcp_only); if (retval == 0) { if (krb5_is_krb_error(&response_data)) { if (!tcp_only) { krb5_error *err_reply; retval = decode_krb5_error(&response_data, &err_reply); if (retval != 0) goto cleanup; if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) { tcp_only = 1; krb5_free_error(context, err_reply); krb5_free_data_contents(context, &response_data); goto send_again; } krb5_free_error(context, err_reply); } } } else goto cleanup; retval = krb5int_process_tgs_reply(context, &response_data, tkt, kdcoptions, address, in_padata, in_cred, timestamp, nonce, subkey, out_padata, out_enc_padata, out_cred); if (retval != 0) goto cleanup; cleanup: #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error"); #endif krb5_free_data_contents(context, &request_data); krb5_free_data_contents(context, &response_data); if (subkey != NULL) { if (retval == 0 && out_subkey != NULL) *out_subkey = subkey; else krb5_free_keyblock(context, subkey); } return retval; }