From: Sam Hartman Date: Wed, 23 Nov 2011 01:04:38 +0000 (+0000) Subject: FAST TGS X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=09484d0e835928a48655c0650f7de97825607b2e;p=krb5.git FAST TGS Implement RFC 6113 FAST TGS support. Includes library support for a varient of explicit TGS armor that has not yet been proposed within the IETF. ticket: 7026 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25488 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/krb5/krb/decode_kdc.c b/src/lib/krb5/krb/decode_kdc.c index b149f0f01..f4d167a7d 100644 --- a/src/lib/krb5/krb/decode_kdc.c +++ b/src/lib/krb5/krb/decode_kdc.c @@ -26,6 +26,7 @@ #include "k5-int.h" #include "int-proto.h" +#include "fast.h" /* Takes a KDC_REP message and decrypts encrypted part using etype and @@ -41,11 +42,15 @@ */ krb5_error_code -krb5int_decode_tgs_rep(krb5_context context, krb5_data *enc_rep, const krb5_keyblock *key, +krb5int_decode_tgs_rep(krb5_context context, + struct krb5int_fast_request_state *fast_state, +krb5_data *enc_rep, const krb5_keyblock *key, krb5_keyusage usage, krb5_kdc_rep **dec_rep) { krb5_error_code retval; krb5_kdc_rep *local_dec_rep; + krb5_keyblock *strengthen_key = NULL, tgs_key; + tgs_key.contents = NULL; if (krb5_is_as_rep(enc_rep)) { retval = decode_krb5_as_rep(enc_rep, &local_dec_rep); @@ -58,10 +63,23 @@ krb5int_decode_tgs_rep(krb5_context context, krb5_data *enc_rep, const krb5_keyb if (retval) return retval; - if ((retval = krb5_kdc_rep_decrypt_proc(context, key, &usage, + retval = krb5int_fast_process_response(context, fast_state, + local_dec_rep, &strengthen_key); + if (retval == KRB5_ERR_FAST_REQUIRED) + retval = 0; + else if (retval) + goto cleanup; + retval = krb5int_fast_reply_key(context, strengthen_key, key, &tgs_key); + if (retval) + goto cleanup; + + if ((retval = krb5_kdc_rep_decrypt_proc(context, &tgs_key, &usage, local_dec_rep))) krb5_free_kdc_rep(context, local_dec_rep); else *dec_rep = local_dec_rep; - return(retval); -} +cleanup: + krb5_free_keyblock(context, strengthen_key); + krb5_free_keyblock_contents(context, &tgs_key); + return (retval); + } diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c index ee7534b8d..ab852a8b3 100644 --- a/src/lib/krb5/krb/fast.c +++ b/src/lib/krb5/krb/fast.c @@ -106,6 +106,37 @@ fast_armor_ap_request(krb5_context context, return retval; } +krb5_error_code krb5int_fast_tgs_armor(krb5_context context, struct krb5int_fast_request_state *state, + krb5_keyblock *subkey,krb5_keyblock *session_key, + krb5_ccache ccache, + krb5_data *target_realm) +{ + krb5_principal target_principal = NULL; + krb5_keyblock *existing_armor = NULL; + krb5_error_code retval = 0; + + if (ccache) { + retval = krb5int_tgtname(context, target_realm, target_realm, + &target_principal); + if (retval == 0) + retval = fast_armor_ap_request(context, state, ccache, target_principal); + if (retval == 0) { + existing_armor = state->armor_key; + state->armor_key = NULL; + retval = krb5_c_fx_cf2_simple(context, existing_armor, + "explicitarmor", subkey, + "tgsarmor", &state->armor_key); + } + } else retval = krb5_c_fx_cf2_simple(context, + subkey, "subkeyarmor", + session_key, "ticketarmor", &state->armor_key); + if (target_principal) + krb5_free_principal(context, target_principal); + krb5_free_keyblock(context, existing_armor); + return retval; +} + + krb5_error_code krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state, @@ -200,13 +231,15 @@ krb5int_fast_prep_req(krb5_context context, krb5_data **encoded_request) { krb5_error_code retval = 0; - krb5_pa_data *pa_array[2]; + krb5_pa_data *pa_array[3]; krb5_pa_data pa[2]; krb5_fast_req fast_req; - krb5_fast_armored_req *armored_req = NULL; + krb5_pa_data *tgs = NULL; + krb5_fast_armored_req *armored_req = NULL; krb5_data *encoded_fast_req = NULL; krb5_data *encoded_armored_req = NULL; krb5_data *local_encoded_result = NULL; + int i,j; assert(state != NULL); assert(state->fast_outer_request.padata == NULL); @@ -224,6 +257,16 @@ krb5int_fast_prep_req(krb5_context context, retval = ENOMEM; } fast_req.fast_options = state->fast_options; + if (retval == 0 + && (tgs = krb5int_find_pa_data(context,fast_req.req_body->padata, + KRB5_PADATA_AP_REQ))) { + krb5_pa_data **paptr = &fast_req.req_body->padata[0]; + for (i=0,j=0;paptr[j]; j++) + if (paptr[j]->pa_type == KRB5_PADATA_AP_REQ) + paptr[j] = NULL; + else paptr[i++] = paptr[j]; + paptr[i++] = NULL; + } if (retval == 0) retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req); if (retval == 0) { @@ -249,7 +292,10 @@ krb5int_fast_prep_req(krb5_context context, pa[0].pa_type = KRB5_PADATA_FX_FAST; pa[0].contents = (unsigned char *) encoded_armored_req->data; pa[0].length = encoded_armored_req->length; - pa_array[0] = &pa[0]; + if (tgs) { + pa_array[0] = tgs; + pa_array[1] = &pa[0]; + } else pa_array[0] = &pa[0]; } state->fast_outer_request.padata = pa_array; if(retval == 0) diff --git a/src/lib/krb5/krb/fast.h b/src/lib/krb5/krb/fast.h index 5b8e3aa5a..159e6e77f 100644 --- a/src/lib/krb5/krb/fast.h +++ b/src/lib/krb5/krb/fast.h @@ -102,5 +102,10 @@ krb5_boolean krb5int_upgrade_to_fast_p(krb5_context context, struct krb5int_fast_request_state *state, krb5_pa_data **padata); +krb5_error_code krb5int_fast_tgs_armor(krb5_context context, struct krb5int_fast_request_state *state, + krb5_keyblock *subkey, + krb5_keyblock *session_key, + krb5_ccache ccache, + krb5_data *target_realm); #endif diff --git a/src/lib/krb5/krb/gc_via_tkt.c b/src/lib/krb5/krb/gc_via_tkt.c index e25860cb6..37c6ce3f5 100644 --- a/src/lib/krb5/krb/gc_via_tkt.c +++ b/src/lib/krb5/krb/gc_via_tkt.c @@ -31,6 +31,8 @@ #include "k5-int.h" #include "int-proto.h" +#include "fast.h" + static krb5_error_code kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, @@ -171,6 +173,7 @@ krb5_get_cred_via_tkt(krb5_context context, krb5_creds *tkt, krb5_error_code krb5int_make_tgs_request(krb5_context context, + struct krb5int_fast_request_state *fast_state, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, @@ -214,7 +217,7 @@ krb5int_make_tgs_request(krb5_context context, enctypes[1] = 0; } - retval = krb5int_make_tgs_request_ext(context, kdcoptions, &in_cred->times, + retval = krb5int_make_tgs_request_ext(context, fast_state, kdcoptions, &in_cred->times, enctypes, in_cred->server, address, in_cred->authdata, in_padata, second_tkt ? @@ -230,6 +233,7 @@ krb5int_make_tgs_request(krb5_context context, 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, @@ -257,6 +261,10 @@ krb5int_process_tgs_reply(krb5_context context, 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) { @@ -292,13 +300,14 @@ krb5int_process_tgs_reply(krb5_context context, /* 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, response_data, + 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, response_data, + if ((krb5int_decode_tgs_rep(context, fast_state, response_data, &tkt->keyblock, KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0) retval = 0; @@ -416,19 +425,24 @@ krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt, 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; + #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, + retval = krb5int_make_tgs_request(context, fast_state, tkt, kdcoptions, address, in_padata, in_cred, pacb_fct, pacb_data, &request_data, ×tamp, &nonce, @@ -448,6 +462,10 @@ send_again: 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); @@ -460,7 +478,7 @@ send_again: } else goto cleanup; - retval = krb5int_process_tgs_reply(context, &response_data, + retval = krb5int_process_tgs_reply(context, fast_state, &response_data, tkt, kdcoptions, address, in_padata, in_cred, timestamp, nonce, subkey, @@ -470,6 +488,7 @@ send_again: goto cleanup; cleanup: + krb5int_fast_free_state(context, fast_state); #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error"); #endif diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c index 780e6568b..d1439586c 100644 --- a/src/lib/krb5/krb/get_creds.c +++ b/src/lib/krb5/krb/get_creds.c @@ -39,6 +39,7 @@ #include "k5-int.h" #include "int-proto.h" +#include "fast.h" /* * Set *mcreds and *fields to a matching credential and field set for @@ -151,6 +152,7 @@ struct _krb5_tkt_creds_context { krb5_flags req_options; /* Caller-requested KRB5_GC_* options */ krb5_flags req_kdcopt; /* Caller-requested options as KDC options */ krb5_authdata **authdata; /* Caller-requested authdata */ + struct krb5int_fast_request_state *fast_state; /* The following fields are used in multiple steps. */ krb5_creds *cur_tgt; /* TGT to be used for next query */ @@ -266,7 +268,8 @@ make_request(krb5_context context, krb5_tkt_creds_context ctx, if (!krb5_c_valid_enctype(ctx->cur_tgt->keyblock.enctype)) return KRB5_PROG_ETYPE_NOSUPP; - code = krb5int_make_tgs_request(context, ctx->cur_tgt, ctx->kdcopt, + code = krb5int_make_tgs_request(context, ctx->fast_state, + ctx->cur_tgt, ctx->kdcopt, ctx->cur_tgt->addresses, NULL, ctx->tgs_in_creds, NULL, NULL, &request, &ctx->timestamp, &ctx->nonce, @@ -354,7 +357,8 @@ get_creds_from_tgs_reply(krb5_context context, krb5_tkt_creds_context ctx, krb5_free_creds(context, ctx->reply_creds); ctx->reply_creds = NULL; - code = krb5int_process_tgs_reply(context, reply, ctx->cur_tgt, ctx->kdcopt, + code = krb5int_process_tgs_reply(context, ctx->fast_state, + reply, ctx->cur_tgt, ctx->kdcopt, ctx->cur_tgt->addresses, NULL, ctx->tgs_in_creds, ctx->timestamp, ctx->nonce, ctx->subkey, NULL, NULL, @@ -1043,6 +1047,9 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache, ctx = k5alloc(sizeof(*ctx), &code); if (ctx == NULL) goto cleanup; + code = krb5int_fast_make_state(context, &ctx->fast_state); + if (code) + goto cleanup; ctx->req_options = options; ctx->req_kdcopt = 0; @@ -1110,6 +1117,7 @@ krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context ctx) { if (ctx == NULL) return; + krb5int_fast_free_state(context, ctx->fast_state); krb5_free_creds(context, ctx->in_creds); krb5_cc_close(context, ctx->ccache); krb5_free_principal(context, ctx->req_server); diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index 4e2d1deb2..6b160957e 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -27,6 +27,8 @@ #ifndef KRB5_INT_FUNC_PROTO__ #define KRB5_INT_FUNC_PROTO__ +struct krb5int_fast_request_state; + krb5_error_code krb5int_tgtname(krb5_context context, const krb5_data *, const krb5_data *, krb5_principal *); @@ -89,6 +91,7 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, krb5_error_code krb5int_make_tgs_request_ext(krb5_context context, + struct krb5int_fast_request_state *, krb5_flags kdcoptions, const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, @@ -110,6 +113,7 @@ krb5int_make_tgs_request_ext(krb5_context context, krb5_error_code krb5int_make_tgs_request(krb5_context context, + struct krb5int_fast_request_state *, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, @@ -127,6 +131,7 @@ krb5int_make_tgs_request(krb5_context context, krb5_error_code krb5int_process_tgs_reply(krb5_context context, + struct krb5int_fast_request_state *, krb5_data *response_data, krb5_creds *tkt, krb5_flags kdcoptions, @@ -145,7 +150,9 @@ krb5int_process_tgs_reply(krb5_context context, * in with the subkey needed to decrypt the TGS * response. Otherwise it will be set to null. */ -krb5_error_code krb5int_decode_tgs_rep(krb5_context, krb5_data *, +krb5_error_code krb5int_decode_tgs_rep(krb5_context, + struct krb5int_fast_request_state *, + krb5_data *, const krb5_keyblock *, krb5_keyusage, krb5_kdc_rep ** ); diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c index 4f616ab28..8f6ca0bab 100644 --- a/src/lib/krb5/krb/send_tgs.c +++ b/src/lib/krb5/krb/send_tgs.c @@ -26,6 +26,7 @@ #include "k5-int.h" #include "int-proto.h" +#include "fast.h" /* Constructs a TGS request @@ -147,6 +148,7 @@ cleanup: krb5_error_code krb5int_make_tgs_request_ext(krb5_context context, + struct krb5int_fast_request_state *fast_state, krb5_flags kdcoptions, const krb5_ticket_times *timestruct, const krb5_enctype *ktypes, @@ -208,7 +210,11 @@ krb5int_make_tgs_request_ext(krb5_context context, return retval; TRACE_SEND_TGS_SUBKEY(context, local_subkey); - if (authorization_data) { + retval = krb5int_fast_tgs_armor(context, fast_state, local_subkey, + &in_cred->keyblock, NULL, NULL); + if (retval) + goto cleanup; + if (authorization_data) { /* need to encrypt it in the request */ if ((retval = encode_krb5_authdata(authorization_data, &scratch))) @@ -249,7 +255,7 @@ krb5int_make_tgs_request_ext(krb5_context context, tgsreq.second_ticket = 0; /* encode the body; then checksum it */ - if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) + if ((retval = krb5int_fast_prep_req_body(context, fast_state, &tgsreq, &scratch))) goto cleanup; /* @@ -320,7 +326,9 @@ krb5int_make_tgs_request_ext(krb5_context context, goto cleanup; } /* the TGS_REQ is assembled in tgsreq, so encode it */ - if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) + if ((retval = krb5int_fast_prep_req(context, fast_state, &tgsreq, + &scratch2, encode_krb5_tgs_req, + &scratch))) goto cleanup; *request_data = *scratch;