FAST TGS
authorSam Hartman <hartmans@mit.edu>
Wed, 23 Nov 2011 01:04:38 +0000 (01:04 +0000)
committerSam Hartman <hartmans@mit.edu>
Wed, 23 Nov 2011 01:04:38 +0000 (01:04 +0000)
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

src/lib/krb5/krb/decode_kdc.c
src/lib/krb5/krb/fast.c
src/lib/krb5/krb/fast.h
src/lib/krb5/krb/gc_via_tkt.c
src/lib/krb5/krb/get_creds.c
src/lib/krb5/krb/int-proto.h
src/lib/krb5/krb/send_tgs.c

index b149f0f016ce5d730fb493f2d1a8c6a6186a3454..f4d167a7d9250386714b7563e7826667c16b107d 100644 (file)
@@ -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
 */
 
 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);
+    }
index ee7534b8d925dcd17f8197a814eedf593bd9ce57..ab852a8b37b97cfe437d982ef935e94b608fd6ed 100644 (file)
@@ -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)
index 5b8e3aa5a0487e46713a9dc79cd4e6aa92a7b573..159e6e77f7f051bbc23b9d48e9ca8d8ecbf3de16 100644 (file)
@@ -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
index e25860cb66be227c5a64142d2e7e2e6c170e83da..37c6ce3f5a7286dbf6df71165d23fb2ae58581f1 100644 (file)
@@ -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, &timestamp, &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
index 780e6568b00cd4c6d6e88dffccdb996ba1df2abe..d1439586cb83492a03162c1f1be238c24ce8ac73 100644 (file)
@@ -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);
index 4e2d1deb21da603f3dfb82f70e2e4f6c032bf25c..6b160957eb6d925c3295239d1f17e9dfd971b3e6 100644 (file)
@@ -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 ** );
 
index 4f616ab28f52f4302f7f8066bf1c0733bcfe327e..8f6ca0baba539645ed6d8c0ca5cbb6155abfedf0 100644 (file)
@@ -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;