From dc3ba26a2c9acde7ca4ed9260fdc01997511e478 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Thu, 26 Nov 2009 00:05:08 +0000 Subject: [PATCH] libkrb5 support for non-blocking AS requests Merge Luke's iakerb-libkrb5-as-only branch into trunk with several bug fixes. Adds support for the krb5_init_creds APIs (same as Heimdal's) which allow AS requests to be performed via a different transport than the blocking send_to_kdc. ticket: 6586 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23358 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/krb5/krb5.hin | 52 ++ src/lib/krb5/krb/fast.c | 2 +- src/lib/krb5/krb/gc_via_tkt.c | 300 ++++--- src/lib/krb5/krb/get_in_tkt.c | 1275 ++++++++++++++++++----------- src/lib/krb5/krb/gic_keytab.c | 12 + src/lib/krb5/krb/gic_pwd.c | 29 +- src/lib/krb5/krb/init_creds_ctx.h | 49 ++ src/lib/krb5/krb/int-proto.h | 53 ++ src/lib/krb5/krb/mk_req_ext.c | 20 +- src/lib/krb5/krb/send_tgs.c | 124 ++- src/lib/krb5/libkrb5.exports | 11 + 11 files changed, 1293 insertions(+), 634 deletions(-) create mode 100644 src/lib/krb5/krb/init_creds_ctx.h diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 3b86b2328..0f1d40007 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -2261,6 +2261,58 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *k5_gic_options); +struct _krb5_init_creds_context; +typedef struct _krb5_init_creds_context *krb5_init_creds_context; + +void KRB5_CALLCONV +krb5_init_creds_free(krb5_context context, krb5_init_creds_context ctx); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_creds(krb5_context context, krb5_init_creds_context ctx, + krb5_creds *creds); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_error(krb5_context context, krb5_init_creds_context ctx, + krb5_error **error); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_init(krb5_context context, krb5_principal client, + krb5_prompter_fct prompter, void *data, + krb5_deltat start_time, krb5_get_init_creds_opt *options, + krb5_init_creds_context *ctx); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_keyblock(krb5_context context, krb5_init_creds_context ctx, + krb5_keyblock *keyblock); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_keytab(krb5_context context, krb5_init_creds_context ctx, + krb5_keytab keytab); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_step(krb5_context context, krb5_init_creds_context ctx, + krb5_data *in, krb5_data *out, krb5_data *realm, + unsigned int *flags); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_password(krb5_context context, krb5_init_creds_context ctx, + const char *password); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_service(krb5_context context, krb5_init_creds_context ctx, + const char *service); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_store_creds(krb5_context context, krb5_init_creds_context ctx, + krb5_ccache ccache); + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_times(krb5_context context, krb5_init_creds_context ctx, + krb5_ticket_times *times); + krb5_error_code KRB5_CALLCONV krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab arg_keytab, diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c index 7d6bc274c..884a4ed82 100644 --- a/src/lib/krb5/krb/fast.c +++ b/src/lib/krb5/krb/fast.c @@ -373,7 +373,7 @@ krb5int_fast_process_error(krb5_context context, * ever changed then this will need to be a copy not a cast. */ if (retval == 0) - retval = encode_krb5_typed_data( (krb5_typed_data **) fast_response->padata, + retval = encode_krb5_typed_data( (const krb5_typed_data **)fast_response->padata, &encoded_td); if (retval == 0) { fx_error->e_data = *encoded_td; diff --git a/src/lib/krb5/krb/gc_via_tkt.c b/src/lib/krb5/krb/gc_via_tkt.c index 8b202d259..df54621a1 100644 --- a/src/lib/krb5/krb/gc_via_tkt.c +++ b/src/lib/krb5/krb/gc_via_tkt.c @@ -167,33 +167,29 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, } 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) +krb5int_make_tgs_request(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_data *request_data, + krb5_timestamp *timestamp, + krb5_int32 *nonce, + krb5_keyblock **subkey) { krb5_error_code retval; - krb5_kdc_rep *dec_rep; - krb5_error *err_reply; - krb5_response tgsrep; - krb5_enctype *enctypes = 0; - krb5_keyblock *subkey = NULL; - krb5_boolean s4u2self = FALSE, second_tkt; + krb5_enctype *enctypes = NULL; + krb5_boolean second_tkt; -#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 + request_data->data = NULL; + *timestamp = 0; + *subkey = NULL; /* tkt->client must be equal to in_cred->client */ if (!krb5_principal_compare(context, tkt->client, in_cred->client)) @@ -202,74 +198,64 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, if (!tkt->ticket.length) return KRB5_NO_TKT_SUPPLIED; - second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) != 0); - + second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | + KDC_OPT_CNAME_IN_ADDL_TKT)) != 0); if (second_tkt && !in_cred->second_ticket.length) - return(KRB5_NO_2ND_TKT); - - s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) || - krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER); - - /* check if we have the right TGT */ - /* tkt->server must be equal to */ - /* krbtgt/realmof(cred->server)@realmof(tgt->server) */ -/* - { - krb5_principal tempprinc; - if (retval = krb5_tgtname(context, - krb5_princ_realm(context, in_cred->server), - krb5_princ_realm(context, tkt->server), &tempprinc)) - return(retval); - - if (!krb5_principal_compare(context, tempprinc, tkt->server)) { - krb5_free_principal(context, tempprinc); - return (KRB5_PRINC_NOMATCH); - } - krb5_free_principal(context, tempprinc); - } -*/ + return KRB5_NO_2ND_TKT; if (in_cred->keyblock.enctype) { - enctypes = (krb5_enctype *) malloc(sizeof(krb5_enctype)*2); - if (!enctypes) + enctypes = (krb5_enctype *)malloc(sizeof(krb5_enctype)*2); + if (enctypes == NULL) return ENOMEM; enctypes[0] = in_cred->keyblock.enctype; enctypes[1] = 0; } - retval = krb5int_send_tgs(context, kdcoptions, &in_cred->times, enctypes, - in_cred->server, address, in_cred->authdata, - in_padata, - second_tkt ? &in_cred->second_ticket : NULL, - tkt, pacb_fct, pacb_data, &tgsrep, &subkey); - if (enctypes) + retval = krb5int_make_tgs_request_ext(context, kdcoptions, &in_cred->times, + enctypes, in_cred->server, address, + in_cred->authdata, in_padata, + second_tkt ? + &in_cred->second_ticket : 0, + tkt, pacb_fct, pacb_data, + request_data, + timestamp, nonce, subkey); + if (enctypes != NULL) free(enctypes); - if (retval) { -#ifdef DEBUG_REFERRALS - printf("krb5_get_cred_via_tkt ending early after send_tgs with: %s\n", - error_message(retval)); -#endif - return retval; - } - - switch (tgsrep.message_type) { - case KRB5_TGS_REP: - break; - case KRB5_ERROR: - default: - if (krb5_is_krb_error(&tgsrep.response)) - retval = decode_krb5_error(&tgsrep.response, &err_reply); - else - retval = KRB5KRB_AP_ERR_MSG_TYPE; - if (retval) /* neither proper reply nor error! */ - goto error_4; + return retval; +} +krb5_error_code +krb5int_process_tgs_reply(krb5_context context, + 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 = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5; if (err_reply->text.length > 0) { -#if 0 - const char *m; -#endif switch (err_reply->error) { case KRB_ERR_GENERIC: krb5_set_error_message(context, retval, @@ -280,7 +266,8 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, case KDC_ERR_S_PRINCIPAL_UNKNOWN: { char *s_name; - if (krb5_unparse_name(context, in_cred->server, &s_name) == 0) { + if (err_reply->server && + krb5_unparse_name(context, err_reply->server, &s_name) == 0) { krb5_set_error_message(context, retval, "Server %s not found in Kerberos database", s_name); @@ -291,45 +278,33 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, krb5_clear_error_message(context); } break; - default: -#if 0 /* We should stop the KDC from sending back this text, because - if the local language doesn't match the KDC's language, we'd - just wind up printing out the error message in two languages. - Well, when we get some localization. Which is already - happening in KfM. */ - m = error_message(retval); - /* Special case: MIT KDC may return this same string - in the e-text field. */ - if (strlen (m) == err_reply->text.length-1 - && !strcmp(m, err_reply->text.data)) - break; - krb5_set_error_message(context, retval, - "%s (KDC supplied additional data: %s)", - m, err_reply->text.data); -#endif - break; } } - krb5_free_error(context, err_reply); - goto error_4; + 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. */ - if ((retval = krb5int_decode_tgs_rep(context, &tgsrep.response, - subkey, - KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY, &dec_rep))) { - if ((krb5int_decode_tgs_rep(context, &tgsrep.response, + retval = krb5int_decode_tgs_rep(context, response_data, + subkey, + KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY, + &dec_rep); + if (retval) { + if ((krb5int_decode_tgs_rep(context, response_data, &tkt->keyblock, KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0) retval = 0; - else goto error_4; + else + goto cleanup; } if (dec_rep->msg_type != KRB5_TGS_REP) { retval = KRB5KRB_AP_ERR_MSG_TYPE; - goto error_3; + goto cleanup; } /* @@ -358,7 +333,7 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, if (retval == 0) retval = check_reply_server(context, kdcoptions, in_cred, dec_rep); - if (dec_rep->enc_part2->nonce != tgsrep.expected_nonce) + if (dec_rep->enc_part2->nonce != nonce) retval = KRB5_KDCREP_MODIFIED; if ((kdcoptions & KDC_OPT_POSTDATED) && @@ -382,13 +357,13 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, retval = KRB5_KDCREP_MODIFIED; if (retval != 0) - goto error_3; + goto cleanup; if (!in_cred->times.starttime && !in_clock_skew(dec_rep->enc_part2->times.starttime, - tgsrep.request_time)) { + timestamp)) { retval = KRB5_KDCREP_SKEW; - goto error_3; + goto cleanup; } if (out_padata != NULL) { @@ -401,9 +376,103 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, } retval = krb5_kdcrep2creds(context, dec_rep, address, - &in_cred->second_ticket, out_cred); + &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); -error_3:; if (subkey != NULL) { if (retval == 0 && out_subkey != NULL) *out_subkey = subkey; @@ -411,14 +480,5 @@ error_3:; krb5_free_keyblock(context, subkey); } - memset(dec_rep->enc_part2->session->contents, 0, - dec_rep->enc_part2->session->length); - krb5_free_kdc_rep(context, dec_rep); - -error_4:; - free(tgsrep.response.data); -#ifdef DEBUG_REFERRALS - printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error"); -#endif return retval; } diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index d8849ecf4..6ab8afad0 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -34,6 +34,7 @@ #include "int-proto.h" #include "os-proto.h" #include "fast.h" +#include "init_creds_ctx.h" #if APPLE_PKINIT #define IN_TKT_DEBUG 0 @@ -1057,462 +1058,729 @@ build_in_tkt_name(krb5_context context, return ret; } +void KRB5_CALLCONV +krb5_init_creds_free(krb5_context context, + krb5_init_creds_context ctx) +{ + if (ctx == NULL) + return; + + if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) { + krb5_get_init_creds_opt_free(context, + (krb5_get_init_creds_opt *)ctx->opte); + } + free(ctx->in_tkt_service); + zap(ctx->password.data, ctx->password.length); + krb5_free_data_contents(context, &ctx->password); + krb5_free_error(context, ctx->err_reply); + krb5_free_cred_contents(context, &ctx->cred); + krb5_free_kdc_req(context, ctx->request); + krb5_free_kdc_rep(context, ctx->reply); + krb5_free_data(context, ctx->encoded_request_body); + krb5_free_data(context, ctx->encoded_previous_request); + krb5int_fast_free_state(context, ctx->fast_state); + krb5_free_pa_data(context, ctx->preauth_to_use); + krb5_free_data_contents(context, &ctx->salt); + krb5_free_data_contents(context, &ctx->s2kparams); + krb5_free_keyblock_contents(context, &ctx->as_key); + free(ctx); +} + +static krb5_error_code +init_creds_get(krb5_context context, + krb5_init_creds_context ctx, + int *use_master) +{ + krb5_error_code code; + krb5_data request; + krb5_data reply; + krb5_data realm; + unsigned int flags = 0; + int tcp_only = 0; + + request.length = 0; + request.data = NULL; + reply.length = 0; + reply.data = NULL; + realm.length = 0; + realm.data = NULL; + + for (;;) { + code = krb5_init_creds_step(context, + ctx, + &reply, + &request, + &realm, + &flags); + if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) + tcp_only = 1; + else if (code != 0 || (flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE)) + break; + + krb5_free_data_contents(context, &reply); + + code = krb5_sendto_kdc(context, &request, &realm, + &reply, use_master, tcp_only); + if (code != 0) + break; + + krb5_free_data_contents(context, &request); + krb5_free_data_contents(context, &realm); + } + + krb5_free_data_contents(context, &request); + krb5_free_data_contents(context, &reply); + krb5_free_data_contents(context, &realm); + + return code; +} + +/* Heimdal API */ krb5_error_code KRB5_CALLCONV -krb5int_get_init_creds(krb5_context context, - krb5_creds *creds, - krb5_principal client, - krb5_prompter_fct prompter, - void *prompter_data, - krb5_deltat start_time, - char *in_tkt_service, - krb5_get_init_creds_opt *opts, - krb5_gic_get_as_key_fct gak_fct, - void *gak_data, - int *use_master, - krb5_kdc_rep **as_reply) +krb5_init_creds_get(krb5_context context, + krb5_init_creds_context ctx) { - krb5_error_code ret; - krb5_kdc_req request; - krb5_data *encoded_request_body, *encoded_previous_request; - krb5_pa_data **preauth_to_use, **kdc_padata; - int tempint; - char *tempstr; - krb5_deltat tkt_life; - krb5_deltat renew_life; - int loopcount; - krb5_data salt; - krb5_data s2kparams; - krb5_keyblock as_key, encrypting_key; - krb5_keyblock *strengthen_key = NULL; - krb5_error *err_reply; - krb5_kdc_rep *local_as_reply; - krb5_timestamp time_now; - krb5_enctype etype = 0; - krb5_preauth_client_rock get_data_rock; - int canon_flag = 0; - krb5_principal_data referred_client; - krb5_boolean retry = 0; - struct krb5int_fast_request_state *fast_state = NULL; - krb5_pa_data **out_padata = NULL; - krb5_gic_opt_ext *options = NULL; - - /* initialize everything which will be freed at cleanup */ - - s2kparams.data = NULL; - s2kparams.length = 0; - request.server = NULL; - request.ktype = NULL; - request.addresses = NULL; - request.padata = NULL; - encoded_request_body = NULL; - encoded_previous_request = NULL; - preauth_to_use = NULL; - kdc_padata = NULL; - as_key.length = 0; - encrypting_key.length = 0; - encrypting_key.contents = NULL; - salt.length = 0; - salt.data = NULL; + int use_master = 0; - local_as_reply = 0; -#if APPLE_PKINIT - inTktDebug("krb5_get_init_creds top\n"); -#endif /* APPLE_PKINIT */ + return init_creds_get(context, ctx, &use_master); +} - err_reply = NULL; +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_creds(krb5_context context, + krb5_init_creds_context ctx, + krb5_creds *creds) +{ + if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0) + return KRB5_NO_TKT_SUPPLIED; - /* referred_client is used to rewrite the client realm for referrals */ - referred_client = *client; - referred_client.realm.data = NULL; - referred_client.realm.length = 0; - ret = krb5int_fast_make_state(context, &fast_state); - if (ret) + return krb5int_copy_creds_contents(context, &ctx->cred, creds); +} + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_store_creds(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache ccache) +{ + if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0) + return KRB5_NO_TKT_SUPPLIED; + + return krb5_cc_store_cred(context, ccache, &ctx->cred); +} + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_times(krb5_context context, + krb5_init_creds_context ctx, + krb5_ticket_times *times) +{ + if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0) + return KRB5_NO_TKT_SUPPLIED; + + *times = ctx->cred.times; + + return 0; +} + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_get_error(krb5_context context, + krb5_init_creds_context ctx, + krb5_error **error) +{ + krb5_error_code code; + krb5_error *ret = NULL; + + *error = NULL; + + if (ctx->err_reply == NULL) + return 0; + + ret = k5alloc(sizeof(*ret), &code); + if (code != 0) goto cleanup; - ret = krb5int_gic_opt_to_opte(context, opts, &options, 1, - "krb5int_get_init_creds"); - if (ret) + ret->magic = KV5M_ERROR; + ret->ctime = ctx->err_reply->ctime; + ret->cusec = ctx->err_reply->cusec; + ret->susec = ctx->err_reply->susec; + ret->stime = ctx->err_reply->stime; + ret->error = ctx->err_reply->error; + + if (ctx->err_reply->client != NULL) { + code = krb5_copy_principal(context, ctx->err_reply->client, + &ret->client); + if (code != 0) + goto cleanup; + } + + code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server); + if (code != 0) goto cleanup; - /* - * Set up the basic request structure - */ - request.magic = KV5M_KDC_REQ; - request.msg_type = KRB5_AS_REQ; + code = krb5int_copy_data_contents(context, &ctx->err_reply->text, + &ret->text); + if (code != 0) + goto cleanup; - /* request.nonce is filled in when we send a request to the kdc */ - request.nonce = 0; + code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data, + &ret->e_data); + if (code != 0) + goto cleanup; + + *error = ret; + +cleanup: + if (code != 0) + krb5_free_error(context, ret); + + return code; +} + +krb5_error_code KRB5_CALLCONV +krb5_init_creds_init(krb5_context context, + krb5_principal client, + krb5_prompter_fct prompter, + void *data, + krb5_deltat start_time, + krb5_get_init_creds_opt *options, + krb5_init_creds_context *pctx) +{ + krb5_error_code code; + krb5_init_creds_context ctx; + int tmp; + char *str = NULL; + krb5_gic_opt_ext *opte; + + ctx = k5alloc(sizeof(*ctx), &code); + if (code != 0) + goto cleanup; + + ctx->request = k5alloc(sizeof(krb5_kdc_req), &code); + if (code != 0) + goto cleanup; + + code = krb5_copy_principal(context, client, &ctx->request->client); + if (code != 0) + goto cleanup; + + ctx->prompter = prompter; + ctx->prompter_data = data; + ctx->gak_fct = krb5_get_as_key_password; + ctx->gak_data = &ctx->password; + + ctx->request_time = 0; /* filled in later */ + ctx->start_time = start_time; - /* request.padata is filled in later */ + if (options == NULL) { + code = krb5_get_init_creds_opt_alloc(context, &options); + if (code != 0) + goto cleanup; + } + + code = krb5int_gic_opt_to_opte(context, options, + &ctx->opte, 1, "krb5_init_creds_init"); + if (code != 0) + goto cleanup; + + opte = ctx->opte; + + code = krb5int_fast_make_state(context, &ctx->fast_state); + if (code != 0) + goto cleanup; - request.kdc_options = context->kdc_default_options; + ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC; + ctx->get_data_rock.etype = &ctx->etype; + ctx->get_data_rock.fast_state = ctx->fast_state; - /* forwardable */ + /* Initialise request parameters as per krb5_get_init_creds() */ + ctx->request->kdc_options = context->kdc_default_options; - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)) - tempint = options->forwardable; - else if ((ret = krb5_libdefault_boolean(context, &client->realm, - KRB5_CONF_FORWARDABLE, &tempint)) == 0) + /* forwaradble */ + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) + tmp = opte->forwardable; + else if (krb5_libdefault_boolean(context, &ctx->request->client->realm, + KRB5_CONF_FORWARDABLE, &tmp) == 0) ; else - tempint = 0; - if (tempint) - request.kdc_options |= KDC_OPT_FORWARDABLE; + tmp = 0; + if (tmp) + ctx->request->kdc_options |= KDC_OPT_FORWARDABLE; /* proxiable */ - - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)) - tempint = options->proxiable; - else if ((ret = krb5_libdefault_boolean(context, &client->realm, - KRB5_CONF_PROXIABLE, &tempint)) == 0) + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) + tmp = opte->proxiable; + else if (krb5_libdefault_boolean(context, &ctx->request->client->realm, + KRB5_CONF_PROXIABLE, &tmp) == 0) ; else - tempint = 0; - if (tempint) - request.kdc_options |= KDC_OPT_PROXIABLE; + tmp = 0; + if (tmp) + ctx->request->kdc_options |= KDC_OPT_PROXIABLE; /* canonicalize */ - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)) - tempint = 1; - else if ((ret = krb5_libdefault_boolean(context, &client->realm, - KRB5_CONF_CANONICALIZE, &tempint)) == 0) + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE) + tmp = 1; + else if (krb5_libdefault_boolean(context, &ctx->request->client->realm, + KRB5_CONF_CANONICALIZE, &tmp) == 0) ; else - tempint = 0; - if (tempint) - request.kdc_options |= KDC_OPT_CANONICALIZE; + tmp = 0; + if (tmp) + ctx->request->kdc_options |= KDC_OPT_CANONICALIZE; /* allow_postdate */ - - if (start_time > 0) - request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED); + if (ctx->start_time > 0) + ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED; /* ticket lifetime */ + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) + ctx->tkt_life = options->tkt_life; + else if (krb5_libdefault_string(context, &ctx->request->client->realm, + KRB5_CONF_TICKET_LIFETIME, &str) == 0) { + code = krb5_string_to_deltat(str, &ctx->tkt_life); + if (code != 0) + goto cleanup; + free(str); + str = NULL; + } else + ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */ - if ((ret = krb5_timeofday(context, &request.from))) + /* renewable lifetime */ + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) + ctx->renew_life = options->renew_life; + else if (krb5_libdefault_string(context, &ctx->request->client->realm, + KRB5_CONF_RENEW_LIFETIME, &str) == 0) { + code = krb5_string_to_deltat(str, &ctx->renew_life); + if (code != 0) + goto cleanup; + free(str); + str = NULL; + } else + ctx->renew_life = 0; + + if (ctx->renew_life > 0) + ctx->request->kdc_options |= KDC_OPT_RENEWABLE; + + /* enctypes */ + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { + ctx->request->ktype = + k5alloc((opte->etype_list_length * sizeof(krb5_enctype)), + &code); + if (code != 0) + goto cleanup; + ctx->request->nktypes = opte->etype_list_length; + memcpy(ctx->request->ktype, opte->etype_list, + ctx->request->nktypes * sizeof(krb5_enctype)); + } else if (krb5_get_default_in_tkt_ktypes(context, + &ctx->request->ktype) == 0) { + for (ctx->request->nktypes = 0; + ctx->request->ktype[ctx->request->nktypes] != ENCTYPE_NULL; + ctx->request->nktypes++) + ; + } else { + /* there isn't any useful default here. */ + code = KRB5_CONFIG_ETYPE_NOSUPP; goto cleanup; - request.from = krb5int_addint32(request.from, start_time); - - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) { - tkt_life = options->tkt_life; - } else if ((ret = krb5_libdefault_string(context, &client->realm, - KRB5_CONF_TICKET_LIFETIME, &tempstr)) - == 0) { - ret = krb5_string_to_deltat(tempstr, &tkt_life); - free(tempstr); - if (ret) { + } + + /* addresess */ + if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { + code = krb5_copy_addresses(context, opte->address_list, + &ctx->request->addresses); + if (code != 0) goto cleanup; - } + } else if (krb5_libdefault_boolean(context, &ctx->request->client->realm, + KRB5_CONF_NOADDRESSES, &tmp) != 0 + || tmp) { + ctx->request->addresses = NULL; } else { - /* this used to be hardcoded in kinit.c */ - tkt_life = 24*60*60; + code = krb5_os_localaddr(context, &ctx->request->addresses); + if (code != 0) + goto cleanup; } - request.till = krb5int_addint32(request.from, tkt_life); - /* renewable lifetime */ + /* initial preauth state */ + krb5_preauth_request_context_init(context); - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { - renew_life = options->renew_life; - } else if ((ret = krb5_libdefault_string(context, &client->realm, - KRB5_CONF_RENEW_LIFETIME, &tempstr)) - == 0) { - ret = krb5_string_to_deltat(tempstr, &renew_life); - free(tempstr); - if (ret) { + 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) goto cleanup; - } } else { - renew_life = 0; + ctx->salt.length = SALT_TYPE_AFS_LENGTH; + ctx->salt.data = NULL; } - if (renew_life > 0) - request.kdc_options |= KDC_OPT_RENEWABLE; - - if (renew_life > 0) { - request.rtime = krb5int_addint32(request.from, renew_life); - if (request.rtime < request.till) { - /* don't ask for a smaller renewable time than the lifetime */ - request.rtime = request.till; + + /* 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; } - /* we are already asking for renewable tickets so strip this option */ - request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK); - } else { - request.rtime = 0; } - /* client */ + ctx->loopcount = 0; - request.client = client; + *pctx = ctx; - /* per referrals draft, enterprise principals imply canonicalization */ - canon_flag = ((request.kdc_options & KDC_OPT_CANONICALIZE) != 0) || - client->type == KRB5_NT_ENTERPRISE_PRINCIPAL; +cleanup: + if (code != 0) + krb5_init_creds_free(context, ctx); + if (str != NULL) + free(str); - /* service */ - if ((ret = build_in_tkt_name(context, in_tkt_service, - request.client, &request.server))) - goto cleanup; + return code; +} - krb5_preauth_request_context_init(context); +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_service(krb5_context context, + krb5_init_creds_context ctx, + const char *service) +{ + char *s; + s = strdup(service); + if (s == NULL) + return ENOMEM; - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) { - request.ktype = options->etype_list; - request.nktypes = options->etype_list_length; - } else if ((ret = krb5_get_default_in_tkt_ktypes(context, - &request.ktype)) == 0) { - for (request.nktypes = 0; - request.ktype[request.nktypes]; - request.nktypes++) - ; - } else { - /* there isn't any useful default here. ret is set from above */ - goto cleanup; + free(ctx->in_tkt_service); + ctx->in_tkt_service = s; + + return 0; +} + +static krb5_error_code +init_creds_validate_reply(krb5_context context, + krb5_init_creds_context ctx, + krb5_data *reply) +{ + krb5_error_code code; + krb5_error *error = NULL; + krb5_kdc_rep *as_reply = NULL; + + krb5_free_error(context, ctx->err_reply); + ctx->err_reply = NULL; + + krb5_free_kdc_rep(context, ctx->reply); + ctx->reply = NULL; + + if (krb5_is_krb_error(reply)) { + code = decode_krb5_error(reply, &error); + if (code != 0) + return code; + + assert(error != NULL); + + if (error->error == KRB_ERR_RESPONSE_TOO_BIG) { + krb5_free_error(context, error); + return KRB5KRB_ERR_RESPONSE_TOO_BIG; + } else { + ctx->err_reply = error; + return 0; + } } - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) { - request.addresses = options->address_list; + /* + * Check to make sure it isn't a V4 reply. + */ + if (reply->length != 0 && !krb5_is_as_rep(reply)) { +/* these are in as well but it isn't worth including. */ +#define V4_KRB_PROT_VERSION 4 +#define V4_AUTH_MSG_ERR_REPLY (5<<1) + /* check here for V4 reply */ + unsigned int t_switch; + + /* From v4 g_in_tkt.c: This used to be + switch (pkt_msg_type(rpkt) & ~1) { + but SCO 3.2v4 cc compiled that incorrectly. */ + t_switch = reply->data[1]; + t_switch &= ~1; + + if (t_switch == V4_AUTH_MSG_ERR_REPLY + && reply->data[0] == V4_KRB_PROT_VERSION) { + code = KRB5KRB_AP_ERR_V4_REPLY; + } else { + code = KRB5KRB_AP_ERR_MSG_TYPE; + } + return code; } - /* it would be nice if this parsed out an address list, but - that would be work. */ - else if (((ret = krb5_libdefault_boolean(context, &client->realm, - KRB5_CONF_NOADDRESSES, &tempint)) != 0) - || (tempint == 1)) { - ; - } else { - if ((ret = krb5_os_localaddr(context, &request.addresses))) - goto cleanup; + + /* It must be a KRB_AS_REP message, or an bad returned packet */ + code = decode_krb5_as_rep(reply, &as_reply); + if (code != 0) + return code; + + if (as_reply->msg_type != KRB5_AS_REP) { + krb5_free_kdc_rep(context, as_reply); + return KRB5KRB_AP_ERR_MSG_TYPE; } - request.authorization_data.ciphertext.length = 0; - request.authorization_data.ciphertext.data = 0; - request.unenc_authdata = 0; - request.second_ticket = 0; + ctx->reply = as_reply; + + return 0; +} + +static krb5_error_code +init_creds_step_request(krb5_context context, + krb5_init_creds_context ctx, + krb5_data *out) +{ + krb5_error_code code; - /* set up the other state. */ + krb5_free_principal(context, ctx->request->server); + ctx->request->server = NULL; - 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))) + code = build_in_tkt_name(context, ctx->in_tkt_service, + ctx->request->client, + &ctx->request->server); + if (code != 0) + goto cleanup; + + if (ctx->loopcount == 0) { + code = krb5_timeofday(context, &ctx->request_time); + if (code != 0) + goto cleanup; + + code = krb5int_fast_as_armor(context, ctx->fast_state, + ctx->opte, ctx->request); + if (code != 0) goto cleanup; - } - /* the salt is allocated from somewhere, unless it is from the caller, - then it is a reference */ + /* give the preauth plugins a chance to prep the request body */ + krb5_preauth_prepare_request(context, ctx->opte, ctx->request); + code = krb5int_fast_prep_req_body(context, ctx->fast_state, + ctx->request, + &ctx->encoded_request_body); + if (code != 0) + goto cleanup; - if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) { - salt = *options->salt; + ctx->request->from = krb5int_addint32(ctx->request_time, + ctx->start_time); + ctx->request->till = krb5int_addint32(ctx->request->from, + ctx->tkt_life); + + if (ctx->renew_life > 0) { + ctx->request->rtime = + krb5int_addint32(ctx->request->from, ctx->renew_life); + if (ctx->request->rtime < ctx->request->till) { + /* don't ask for a smaller renewable time than the lifetime */ + ctx->request->rtime = ctx->request->till; + } + ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK); + } else + ctx->request->rtime = 0; + } else if (ctx->loopcount >= MAX_IN_TKT_LOOPS) { + code = KRB5_GET_IN_TKT_LOOP; + goto cleanup; + } + + if (ctx->err_reply == NULL) { + /* either our first attempt, or retrying after PREAUTH_NEEDED */ + code = krb5_do_preauth(context, + ctx->request, + ctx->encoded_request_body, + ctx->encoded_previous_request, + ctx->preauth_to_use, + &ctx->request->padata, + &ctx->salt, + &ctx->s2kparams, + &ctx->etype, + &ctx->as_key, + ctx->prompter, + ctx->prompter_data, + ctx->gak_fct, + ctx->gak_data, + &ctx->get_data_rock, + ctx->opte); + if (code != 0) + goto cleanup; } else { - salt.length = SALT_TYPE_AFS_LENGTH; - salt.data = NULL; + if (ctx->preauth_to_use != NULL) { + /* + * Retry after an error other than PREAUTH_NEEDED, + * using e-data to figure out what to change. + */ + code = krb5_do_preauth_tryagain(context, + ctx->request, + ctx->encoded_request_body, + ctx->encoded_previous_request, + ctx->preauth_to_use, + &ctx->request->padata, + ctx->err_reply, + &ctx->salt, + &ctx->s2kparams, + &ctx->etype, + &ctx->as_key, + ctx->prompter, + ctx->prompter_data, + ctx->gak_fct, + ctx->gak_data, + &ctx->get_data_rock, + ctx->opte); + } else { + /* No preauth supplied, so can't query the plugins. */ + code = KRB5KRB_ERR_GENERIC; + } + if (code != 0) { + /* couldn't come up with anything better */ + code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5; + goto cleanup; + } } + if (ctx->encoded_previous_request != NULL) { + krb5_free_data(context, ctx->encoded_previous_request); + ctx->encoded_previous_request = NULL; + } - /* set the request nonce */ - if ((ret = krb5_timeofday(context, &time_now))) + code = krb5int_fast_prep_req(context, ctx->fast_state, + ctx->request, ctx->encoded_request_body, + encode_krb5_as_req, + &ctx->encoded_previous_request); + if (code != 0) 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) + code = krb5int_copy_data_contents(context, + ctx->encoded_previous_request, + out); + if (code != 0) goto cleanup; - get_data_rock.magic = CLIENT_ROCK_MAGIC; - get_data_rock.etype = &etype; - get_data_rock.fast_state = fast_state; +cleanup: + return code; +} - /* now, loop processing preauth data and talking to the kdc */ - for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) { - if (request.padata) { - krb5_free_pa_data(context, request.padata); - request.padata = NULL; - } - if (!err_reply) { - /* either our first attempt, or retrying after PREAUTH_NEEDED */ - if ((ret = krb5_do_preauth(context, - &request, - encoded_request_body, - encoded_previous_request, - preauth_to_use, &request.padata, - &salt, &s2kparams, &etype, &as_key, - prompter, prompter_data, - gak_fct, gak_data, - &get_data_rock, options))) - goto cleanup; - if (out_padata) { - krb5_free_pa_data(context, out_padata); - out_padata = NULL; - } - } else { - if (preauth_to_use != NULL) { - /* - * Retry after an error other than PREAUTH_NEEDED, - * using e-data to figure out what to change. - */ - ret = krb5_do_preauth_tryagain(context, - &request, - encoded_request_body, - encoded_previous_request, - preauth_to_use, &request.padata, - err_reply, - &salt, &s2kparams, &etype, - &as_key, - prompter, prompter_data, - gak_fct, gak_data, - &get_data_rock, options); - } else { - /* No preauth supplied, so can't query the plug-ins. */ - ret = KRB5KRB_ERR_GENERIC; - } - if (ret) { - /* couldn't come up with anything better */ - ret = err_reply->error + ERROR_TABLE_BASE_krb5; - } - krb5_free_error(context, err_reply); - err_reply = NULL; - if (ret) - goto cleanup; - } +static krb5_error_code +init_creds_step_reply(krb5_context context, + krb5_init_creds_context ctx, + krb5_data *in) +{ + krb5_error_code code; + krb5_pa_data **padata = NULL; + krb5_pa_data **kdc_padata = NULL; + krb5_boolean retry = FALSE; + int canon_flag = 0; + krb5_keyblock *strengthen_key = NULL; + krb5_keyblock encrypting_key; - if (encoded_previous_request != NULL) { - krb5_free_data(context, encoded_previous_request); - encoded_previous_request = NULL; - } - ret = krb5int_fast_prep_req(context, fast_state, - &request, encoded_request_body, - encode_krb5_as_req, &encoded_previous_request); - if (ret) - goto cleanup; + encrypting_key.length = 0; + encrypting_key.contents = NULL; - err_reply = 0; - local_as_reply = 0; - if ((ret = send_as_request(context, encoded_previous_request, - krb5_princ_realm(context, request.client), &err_reply, - &local_as_reply, use_master))) - goto cleanup; + /* process previous KDC response */ + code = init_creds_validate_reply(context, ctx, in); + if (code != 0) + goto cleanup; - if (err_reply) { - ret = krb5int_fast_process_error(context, fast_state, &err_reply, - &out_padata, &retry); - if (ret !=0) - goto cleanup; - if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) { - /* reset the list of preauth types to try */ - if (preauth_to_use) { - krb5_free_pa_data(context, preauth_to_use); - preauth_to_use = NULL; - } - preauth_to_use = out_padata; - out_padata = NULL; - krb5_free_error(context, err_reply); - err_reply = NULL; - ret = sort_krb5_padata_sequence(context, - &request.server->realm, - preauth_to_use); - if (ret) - goto cleanup; - /* continue to next iteration */ - } else if (canon_flag && err_reply->error == KDC_ERR_WRONG_REALM) { - if (err_reply->client == NULL || - err_reply->client->realm.length == 0) { - ret = KRB5KDC_ERR_WRONG_REALM; - krb5_free_error(context, err_reply); - goto cleanup; - } - /* Rewrite request.client with realm from error reply */ - if (referred_client.realm.data) { - krb5_free_data_contents(context, &referred_client.realm); - referred_client.realm.data = NULL; - } - ret = krb5int_copy_data_contents(context, - &err_reply->client->realm, - &referred_client.realm); - krb5_free_error(context, err_reply); - err_reply = NULL; - if (ret) - goto cleanup; - request.client = &referred_client; + /* per referrals draft, enterprise principals imply canonicalization */ + canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) || + ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL; - krb5_free_principal(context, request.server); - request.server = NULL; + if (ctx->err_reply != NULL) { + code = krb5int_fast_process_error(context, ctx->fast_state, + &ctx->err_reply, &padata, &retry); + if (code != 0) + goto cleanup; - ret = build_in_tkt_name(context, in_tkt_service, - request.client, &request.server); - if (ret) - goto cleanup; - } else { - if (retry) { - /* continue to next iteration */ - } else { - /* error + no hints = give up */ - ret = (krb5_error_code) err_reply->error - + ERROR_TABLE_BASE_krb5; - krb5_free_error(context, err_reply); - goto cleanup; - } + 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; + padata = NULL; + /* this will trigger a new call to krb5_do_preauth() */ + krb5_free_error(context, ctx->err_reply); + ctx->err_reply = NULL; + code = sort_krb5_padata_sequence(context, + &ctx->request->client->realm, + ctx->preauth_to_use); + + } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) { + if (ctx->err_reply->client == NULL || + !krb5_princ_realm(context, ctx->err_reply->client)->length) { + code = KRB5KDC_ERR_WRONG_REALM; + goto cleanup; } - } else if (local_as_reply) { - break; + /* Rewrite request.client with realm from error reply */ + krb5_free_data_contents(context, &ctx->request->client->realm); + code = krb5int_copy_data_contents(context, + &ctx->err_reply->client->realm, + &ctx->request->client->realm); + /* this will trigger a new call to krb5_do_preauth() */ + krb5_free_error(context, ctx->err_reply); + ctx->err_reply = NULL; } else { - ret = KRB5KRB_AP_ERR_MSG_TYPE; - goto cleanup; + if (retry) { + code = 0; + } else { + /* error + no hints = give up */ + code = (krb5_error_code)ctx->err_reply->error + + ERROR_TABLE_BASE_krb5; + } } - } -#if APPLE_PKINIT - inTktDebug("krb5_get_init_creds done with send_as_request loop lc %d\n", - (int)loopcount); -#endif /* APPLE_PKINIT */ - if (loopcount == MAX_IN_TKT_LOOPS) { - ret = KRB5_GET_IN_TKT_LOOP; + /* Return error code, or continue with next iteration */ goto cleanup; } + /* We have a response. Process it. */ + assert(ctx->reply != NULL); + /* 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, &strengthen_key); - if (ret) + code = krb5int_fast_process_response(context, ctx->fast_state, + ctx->reply, &strengthen_key); + if (code != 0) goto cleanup; - if ((ret = sort_krb5_padata_sequence(context, &request.server->realm, - local_as_reply->padata))) + + code = sort_krb5_padata_sequence(context, &ctx->request->client->realm, + ctx->reply->padata); + if (code != 0) goto cleanup; - etype = local_as_reply->enc_part.enctype; - if ((ret = krb5_do_preauth(context, - &request, - encoded_request_body, encoded_previous_request, - local_as_reply->padata, &kdc_padata, - &salt, &s2kparams, &etype, &as_key, prompter, - prompter_data, gak_fct, gak_data, - &get_data_rock, options))) { -#if APPLE_PKINIT - inTktDebug("krb5_get_init_creds krb5_do_preauth returned %d\n", (int)ret); -#endif /* APPLE_PKINIT */ + + ctx->etype = ctx->reply->enc_part.enctype; + + code = krb5_do_preauth(context, + ctx->request, + ctx->encoded_request_body, + ctx->encoded_previous_request, + ctx->reply->padata, + &kdc_padata, + &ctx->salt, + &ctx->s2kparams, + &ctx->etype, + &ctx->as_key, + ctx->prompter, + ctx->prompter_data, + ctx->gak_fct, + ctx->gak_data, + &ctx->get_data_rock, + ctx->opte); + if (code != 0) goto cleanup; - } /* * If we haven't gotten a salt from another source yet, set up one @@ -1524,9 +1792,9 @@ krb5int_get_init_creds(krb5_context context, * salt. local_as_reply->client will be checked later on in * verify_as_reply. */ - if (salt.length == SALT_TYPE_AFS_LENGTH && salt.data == NULL) { - ret = krb5_principal2salt(context, local_as_reply->client, &salt); - if (ret) + if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) { + code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt); + if (code != 0) goto cleanup; } @@ -1542,117 +1810,196 @@ krb5int_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) { - ret = krb5int_fast_reply_key(context, strengthen_key, &as_key, + if (ctx->as_key.length) { + code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key, &encrypting_key); - if (ret) + if (code != 0) goto cleanup; - ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, - NULL, &encrypting_key, krb5_kdc_rep_decrypt_proc, - NULL); + code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL, + &encrypting_key, krb5_kdc_rep_decrypt_proc, + NULL); } else - ret = -1; + code = -1; - if (ret) { + if (code != 0) { /* if we haven't get gotten a key, get it now */ - - if ((ret = ((*gak_fct)(context, request.client, - local_as_reply->enc_part.enctype, - prompter, prompter_data, &salt, &s2kparams, - &as_key, gak_data)))) + code = (*ctx->gak_fct)(context, ctx->request->client, + ctx->reply->enc_part.enctype, + ctx->prompter, ctx->prompter_data, + &ctx->salt, &ctx->s2kparams, + &ctx->as_key, ctx->gak_data); + if (code != 0) goto cleanup; - ret = krb5int_fast_reply_key(context, strengthen_key, &as_key, - &encrypting_key); - if (ret) + code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key, + &encrypting_key); + if (code != 0) goto cleanup; - if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, - NULL, &encrypting_key, krb5_kdc_rep_decrypt_proc, - NULL))) + + code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL, + &encrypting_key, krb5_kdc_rep_decrypt_proc, + NULL); + if (code != 0) goto cleanup; } - if ((ret = verify_as_reply(context, time_now, &request, local_as_reply))) + code = verify_as_reply(context, ctx->request_time, + ctx->request, ctx->reply); + if (code != 0) goto cleanup; - /* XXX this should be inside stash_as_reply, but as long as - get_in_tkt is still around using that arg as an in/out, I can't - do that */ - memset(creds, 0, sizeof(*creds)); - - if ((ret = stash_as_reply(context, time_now, &request, local_as_reply, - creds, NULL))) + code = stash_as_reply(context, ctx->request_time, ctx->request, + ctx->reply, &ctx->cred, NULL); + if (code != 0) goto cleanup; - /* success */ + krb5_preauth_request_context_fini(context); - ret = 0; + /* success */ + code = 0; + ctx->flags |= KRB5_INIT_CREDS_STEP_FLAG_COMPLETE; cleanup: - if (ret != 0) { - char *client_name; - /* See if we can produce a more detailed error message. */ - switch (ret) { - case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: - client_name = NULL; - if (krb5_unparse_name(context, client, &client_name) == 0) { - krb5_set_error_message(context, ret, - "Client '%s' not found in Kerberos database", - client_name); - free(client_name); + krb5_free_pa_data(context, padata); + krb5_free_pa_data(context, kdc_padata); + krb5_free_keyblock(context, strengthen_key); + krb5_free_keyblock_contents(context, &encrypting_key); + + return code; +} + +/* + * Do next step of credentials acquisition. + * + * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request + * should be sent with TCP. + */ +krb5_error_code KRB5_CALLCONV +krb5_init_creds_step(krb5_context context, + krb5_init_creds_context ctx, + krb5_data *in, + krb5_data *out, + krb5_data *realm, + unsigned int *flags) +{ + krb5_error_code code, code2; + + *flags = 0; + + out->data = NULL; + out->length = 0; + + realm->data = NULL; + realm->length = 0; + + if (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) + goto cleanup; + + if (in->length != 0) { + code = init_creds_step_reply(context, ctx, in); + if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) { + code2 = krb5int_copy_data_contents(context, + ctx->encoded_previous_request, + out); + if (code2 != 0) { + code = code2; + goto cleanup; } - break; - default: - break; + goto copy_realm; } + if (code != 0 || (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE)) + goto cleanup; } - krb5_preauth_request_context_fini(context); - krb5_free_keyblock(context, strengthen_key); - if (encrypting_key.contents) - krb5_free_keyblock_contents(context, &encrypting_key); - if (fast_state) - krb5int_fast_free_state(context, fast_state); - if (out_padata) - krb5_free_pa_data(context, out_padata); - if (encoded_previous_request != NULL) { - krb5_free_data(context, encoded_previous_request); - encoded_previous_request = NULL; + + code = init_creds_step_request(context, ctx, out); + if (code != 0) + goto cleanup; + + /* Only a new request increments the loop count, not a TCP retry */ + ctx->loopcount++; + +copy_realm: + assert(ctx->request->server != NULL); + + code2 = krb5int_copy_data_contents(context, + &ctx->request->server->realm, + realm); + if (code2 != 0) { + code = code2; + goto cleanup; + } + +cleanup: + if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) { + char *client_name; + + /* See if we can produce a more detailed error message */ + code2 = krb5_unparse_name(context, ctx->request->client, &client_name); + if (code2 == 0) { + krb5_set_error_message(context, code, + "Client '%s' not found in Kerberos database", + client_name); + krb5_free_unparsed_name(context, client_name); + } } - if (encoded_request_body != NULL) { - krb5_free_data(context, encoded_request_body); - encoded_request_body = NULL; + + *flags = (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE); + + return code; +} + +krb5_error_code KRB5_CALLCONV +krb5int_get_init_creds(krb5_context context, + krb5_creds *creds, + krb5_principal client, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_deltat start_time, + char *in_tkt_service, + krb5_get_init_creds_opt *options, + krb5_gic_get_as_key_fct gak_fct, + void *gak_data, + int *use_master, + krb5_kdc_rep **as_reply) +{ + krb5_error_code code; + krb5_init_creds_context ctx = NULL; + + code = krb5_init_creds_init(context, + client, + prompter, + prompter_data, + start_time, + options, + &ctx); + if (code != 0) + goto cleanup; + + ctx->gak_fct = gak_fct; + ctx->gak_data = gak_data; + + if (in_tkt_service) { + code = krb5_init_creds_set_service(context, ctx, in_tkt_service); + if (code != 0) + goto cleanup; } - if (request.server) - krb5_free_principal(context, request.server); - if (request.ktype && - (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)))) - free(request.ktype); - if (request.addresses && - (!(options && - (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)))) - krb5_free_addresses(context, request.addresses); - if (preauth_to_use) - krb5_free_pa_data(context, preauth_to_use); - if (kdc_padata) - krb5_free_pa_data(context, kdc_padata); - if (request.padata) - krb5_free_pa_data(context, request.padata); - if (as_key.length) - krb5_free_keyblock_contents(context, &as_key); - if (salt.data && - (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)))) - free(salt.data); - krb5_free_data_contents(context, &s2kparams); - if (as_reply) - *as_reply = local_as_reply; - else if (local_as_reply) - krb5_free_kdc_rep(context, local_as_reply); - if (referred_client.realm.data) - krb5_free_data_contents(context, &referred_client.realm); - if (krb5_gic_opt_is_shadowed(options)) { - krb5_get_init_creds_opt_free(context, - (krb5_get_init_creds_opt *)options); + + code = init_creds_get(context, ctx, use_master); + if (code != 0) + goto cleanup; + + code = krb5_init_creds_get_creds(context, ctx, creds); + if (code != 0) + goto cleanup; + + if (as_reply != NULL) { + *as_reply = ctx->reply; + ctx->reply = NULL; } - return(ret); +cleanup: + krb5_init_creds_free(context, ctx); + + return code; } + diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c index b6341778d..4bdf9ee1b 100644 --- a/src/lib/krb5/krb/gic_keytab.c +++ b/src/lib/krb5/krb/gic_keytab.c @@ -27,6 +27,7 @@ #ifndef LEAN_CLIENT #include "k5-int.h" +#include "init_creds_ctx.h" static krb5_error_code get_as_key_keytab(krb5_context context, @@ -76,6 +77,17 @@ get_as_key_keytab(krb5_context context, return(ret); } +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_keytab(krb5_context context, + krb5_init_creds_context ctx, + krb5_keytab keytab) +{ + ctx->gak_fct = get_as_key_keytab; + ctx->gak_data = keytab; + + return 0; +} + krb5_error_code KRB5_CALLCONV krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c index aad0a4162..eac2afb28 100644 --- a/src/lib/krb5/krb/gic_pwd.c +++ b/src/lib/krb5/krb/gic_pwd.c @@ -1,8 +1,9 @@ /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include "k5-int.h" #include "com_err.h" +#include "init_creds_ctx.h" -static krb5_error_code +krb5_error_code krb5_get_as_key_password( krb5_context context, krb5_principal client, @@ -39,7 +40,7 @@ krb5_get_as_key_password( } } - if (password->data[0] == '\0') { + if (password->length == 0 || password->data[0] == '\0') { if (prompter == NULL) return(EIO); @@ -82,6 +83,30 @@ krb5_get_as_key_password( return(ret); } +krb5_error_code KRB5_CALLCONV +krb5_init_creds_set_password(krb5_context context, + krb5_init_creds_context ctx, + const char *password) +{ + char *s; + + s = strdup(password); + if (s == NULL) + return ENOMEM; + + if (ctx->password.data != NULL) { + zap(ctx->password.data, ctx->password.length); + krb5_free_data_contents(context, &ctx->password); + } + + ctx->password.data = s; + ctx->password.length = strlen(s); + ctx->gak_fct = krb5_get_as_key_password; + ctx->gak_data = &ctx->password; + + return 0; +} + krb5_error_code KRB5_CALLCONV krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h new file mode 100644 index 000000000..b830684f2 --- /dev/null +++ b/src/lib/krb5/krb/init_creds_ctx.h @@ -0,0 +1,49 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ + +#ifndef KRB5_INIT_CREDS_CONTEXT +#define KRB5_INIT_CREDS_CONTEXT 1 + +struct _krb5_init_creds_context { + krb5_gic_opt_ext *opte; + char *in_tkt_service; + krb5_prompter_fct prompter; + void *prompter_data; + krb5_gic_get_as_key_fct gak_fct; + void *gak_data; + krb5_timestamp request_time; + krb5_deltat start_time; + krb5_deltat tkt_life; + krb5_deltat renew_life; + unsigned int flags; + unsigned int loopcount; + krb5_data password; + krb5_error *err_reply; + krb5_creds cred; + krb5_kdc_req *request; + krb5_kdc_rep *reply; + krb5_data *encoded_request_body; + krb5_data *encoded_previous_request; + struct krb5int_fast_request_state *fast_state; + krb5_pa_data **preauth_to_use; + krb5_data salt; + krb5_data s2kparams; + krb5_keyblock as_key; + krb5_enctype etype; + krb5_preauth_client_rock get_data_rock; +}; + +#define KRB5_INIT_CREDS_STEP_FLAG_COMPLETE 0x1 + +krb5_error_code +krb5_get_as_key_password(krb5_context context, + krb5_principal client, + krb5_enctype etype, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_data *salt, + krb5_data *params, + krb5_keyblock *as_key, + void *gak_data); + +#endif /* !KRB5_INIT_CREDS_CONTEXT */ + diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index 44ea69d4e..47555d678 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -85,6 +85,59 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, krb5_creds **out_cred, krb5_keyblock **out_subkey); +krb5_error_code +krb5int_make_tgs_request_ext(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_error_code (*pacb_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *pacb_data, + krb5_data *request_data, + krb5_timestamp *timestamp, + krb5_int32 *nonce, + krb5_keyblock **subkey); + +krb5_error_code +krb5int_make_tgs_request(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_data *request_data, + krb5_timestamp *timestamp, + krb5_int32 *nonce, + krb5_keyblock **subkey); + +krb5_error_code +krb5int_process_tgs_reply(krb5_context context, + 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 krb5int_send_tgs(krb5_context, krb5_flags, const krb5_ticket_times *, const krb5_enctype *, diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c index 95f04e9a4..c0f9f833d 100644 --- a/src/lib/krb5/krb/mk_req_ext.c +++ b/src/lib/krb5/krb/mk_req_ext.c @@ -181,17 +181,7 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context, &(*auth_context)->local_seq_number))) goto cleanup; - /* generate subkey if needed */ - if (!in_data &&(*auth_context)->checksum_func) { - retval = (*auth_context)->checksum_func( context, - *auth_context, - (*auth_context)->checksum_func_data, - &in_data); - if (retval) - goto cleanup; - } - if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) { retval = krb5int_generate_and_save_subkey (context, *auth_context, &in_creds->keyblock, @@ -201,8 +191,16 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context, } - if (in_data) { + if (!in_data && (*auth_context)->checksum_func) { + retval = (*auth_context)->checksum_func( context, + *auth_context, + (*auth_context)->checksum_func_data, + &in_data); + if (retval) + goto cleanup; + } + if (in_data) { if ((*auth_context)->req_cksumtype == 0x8003) { /* XXX Special hack for GSSAPI */ checksum.checksum_type = 0x8003; diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c index bee982b08..7980c8454 100644 --- a/src/lib/krb5/krb/send_tgs.c +++ b/src/lib/krb5/krb/send_tgs.c @@ -148,19 +148,27 @@ cleanup: * The pacb_fct callback allows the caller access to the nonce * and request subkey, for binding preauthentication data */ + krb5_error_code -krb5int_send_tgs(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_error_code (*pacb_fct)(krb5_context, - krb5_keyblock *, - krb5_kdc_req *, - void *), - void *pacb_data, - krb5_response *rep, krb5_keyblock **subkey) +krb5int_make_tgs_request_ext(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_error_code (*pacb_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *pacb_data, + krb5_data *request_data, + krb5_timestamp *timestamp, + krb5_int32 *nonce, + krb5_keyblock **subkey) { krb5_error_code retval; krb5_kdc_req tgsreq; @@ -170,7 +178,6 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, krb5_timestamp time_now; krb5_pa_data **combined_padata = NULL; krb5_pa_data ap_req_padata; - int tcp_only = 0, use_master; krb5_keyblock *local_subkey = NULL; assert (subkey != NULL); @@ -195,10 +202,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, 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; - rep->message_type = KRB5_ERROR; /*caller only uses the response - * element on successful return*/ + *nonce = tgsreq.nonce = (krb5_int32)time_now; + *timestamp = time_now; tgsreq.addresses = (krb5_address **) addrs; @@ -332,9 +337,71 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, krb5_free_pa_data(context, tgsreq.padata); tgsreq.padata = NULL; + *request_data = *scratch; + free(scratch); + scratch = NULL; + +send_tgs_error_2:; + if (tgsreq.padata) + krb5_free_pa_data(context, tgsreq.padata); + if (sec_ticket) + krb5_free_ticket(context, sec_ticket); + +send_tgs_error_1:; + if (ktypes == NULL) + free(tgsreq.ktype); + if (tgsreq.authorization_data.ciphertext.data) { + memset(tgsreq.authorization_data.ciphertext.data, 0, + tgsreq.authorization_data.ciphertext.length); + free(tgsreq.authorization_data.ciphertext.data); + } + + if (retval) + krb5_free_keyblock(context, local_subkey); + else + *subkey = local_subkey; + + return retval; + +} + +krb5_error_code +krb5int_send_tgs(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_error_code (*pacb_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *pacb_data, + krb5_response *rep, krb5_keyblock **subkey) +{ + krb5_error_code retval; + krb5_data scratch; + int tcp_only = 0, use_master; + krb5_timestamp now; + krb5_int32 nonce; + + rep->message_type = KRB5_ERROR; + + retval = krb5int_make_tgs_request_ext(context, kdcoptions, timestruct, + ktypes, sname, addrs, + authorization_data, padata, + second_ticket, in_cred, + pacb_fct, pacb_data, &scratch, &now, + &nonce, subkey); + if (retval != 0) + return retval; + + rep->expected_nonce = nonce; + rep->request_time = now; + send_again: use_master = 0; - retval = krb5_sendto_kdc(context, scratch, + retval = krb5_sendto_kdc(context, &scratch, krb5_princ_realm(context, sname), &rep->response, &use_master, tcp_only); if (retval == 0) { @@ -358,30 +425,15 @@ send_again: rep->message_type = KRB5_ERROR; } else if (krb5_is_tgs_rep(&rep->response)) { rep->message_type = KRB5_TGS_REP; - *subkey = local_subkey; } else /* XXX: assume it's an error */ rep->message_type = KRB5_ERROR; } - krb5_free_data(context, scratch); - -send_tgs_error_2:; - if (tgsreq.padata) - krb5_free_pa_data(context, tgsreq.padata); - if (sec_ticket) - krb5_free_ticket(context, sec_ticket); - -send_tgs_error_1:; - if (ktypes == NULL) - free(tgsreq.ktype); - if (tgsreq.authorization_data.ciphertext.data) { - memset(tgsreq.authorization_data.ciphertext.data, 0, - tgsreq.authorization_data.ciphertext.length); - free(tgsreq.authorization_data.ciphertext.data); - } - if (rep->message_type != KRB5_TGS_REP && local_subkey){ + if (rep->message_type != KRB5_TGS_REP && *subkey){ krb5_free_keyblock(context, *subkey); + *subkey = NULL; } + krb5_free_data_contents(context, &scratch); return retval; } diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 26b3da61d..8ea6c0223 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -354,6 +354,17 @@ krb5_get_tgs_ktypes krb5_get_time_offsets krb5_get_validated_creds krb5_init_context +krb5_init_creds_free +krb5_init_creds_get +krb5_init_creds_get_creds +krb5_init_creds_get_error +krb5_init_creds_get_times +krb5_init_creds_init +krb5_init_creds_set_keytab +krb5_init_creds_set_password +krb5_init_creds_set_service +krb5_init_creds_step +krb5_init_creds_store_creds krb5_init_keyblock krb5_init_secure_context krb5_internalize_opaque -- 2.26.2