krb5 commit: Modernize send_tgs.c

Greg Hudson ghudson at MIT.EDU
Sat Feb 9 18:44:45 EST 2013


https://github.com/krb5/krb5/commit/8510f8949a7b92fa8c1b0e3b5c0764ea27eaba6f
commit 8510f8949a7b92fa8c1b0e3b5c0764ea27eaba6f
Author: Greg Hudson <ghudson at mit.edu>
Date:   Fri Feb 8 00:18:50 2013 -0500

    Modernize send_tgs.c
    
    Bring send_tgs.c up to date with current coding practices.  No
    functional changes.

 src/lib/krb5/krb/send_tgs.c |  435 +++++++++++++++++++------------------------
 1 files changed, 190 insertions(+), 245 deletions(-)

diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c
index b215acf..89ac529 100644
--- a/src/lib/krb5/krb/send_tgs.c
+++ b/src/lib/krb5/krb/send_tgs.c
@@ -1,7 +1,7 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/krb/send_tgs.c */
+/* lib/krb5/krb/send_tgs.c - Construct a TGS request */
 /*
- * Copyright 1990,1991,2009 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2009,2013 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -28,42 +28,28 @@
 #include "int-proto.h"
 #include "fast.h"
 
-/*
-  Constructs a TGS request
-  options is used for the options in the KRB_TGS_REQ.
-  timestruct values are used for from, till, rtime " " "
-  enctype is used for enctype " " ", and to encrypt the authorization data,
-  sname is used for sname " " "
-  addrs, if non-NULL, is used for addresses " " "
-  authorization_dat, if non-NULL, is used for authorization_dat " " "
-  second_ticket, if required by options, is used for the 2nd ticket in the req.
-  in_cred is used for the ticket & session key in the KRB_AP_REQ header " " "
-  (the KDC realm is extracted from in_cred->server's realm)
-
-  The response is placed into *rep.
-  rep->response.data is set to point at allocated storage which should be
-  freed by the caller when finished.
-
-  returns system errors
-*/
+/* Construct an AP-REQ message for a TGS request. */
 static krb5_error_code
-tgs_construct_tgsreq(krb5_context context, krb5_data *in_data,
-                     krb5_creds *in_cred, krb5_data *outbuf, krb5_keyblock *subkey)
+tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data,
+                     krb5_creds *tgt, krb5_keyblock *subkey,
+                     krb5_data **ap_req_asn1_out)
 {
     krb5_cksumtype cksumtype;
-    krb5_error_code       retval;
-    krb5_checksum         checksum;
-    krb5_authenticator    authent;
-    krb5_ap_req           request;
-    krb5_data           * scratch = NULL;
-    krb5_data           * toutbuf = NULL;
-
-    checksum.contents = NULL;
-    request.authenticator.ciphertext.data = NULL;
-    request.authenticator.kvno = 0;
-    request.ap_options = 0;
-    request.ticket = 0;
-    switch (in_cred->keyblock.enctype) {
+    krb5_error_code ret;
+    krb5_checksum checksum;
+    krb5_authenticator authent;
+    krb5_ap_req ap_req;
+    krb5_data *authent_asn1 = NULL;
+    krb5_ticket *ticket = NULL;
+    krb5_enc_data authent_enc;
+
+    *ap_req_asn1_out = NULL;
+    memset(&checksum, 0, sizeof(checksum));
+    memset(&ap_req, 0, sizeof(ap_req));
+    memset(&authent_enc, 0, sizeof(authent_enc));
+
+    /* Determine the authenticator checksum type. */
+    switch (tgt->keyblock.enctype) {
     case ENCTYPE_DES_CBC_CRC:
     case ENCTYPE_DES_CBC_MD4:
     case ENCTYPE_DES_CBC_MD5:
@@ -72,80 +58,61 @@ tgs_construct_tgsreq(krb5_context context, krb5_data *in_data,
         cksumtype = context->kdc_req_sumtype;
         break;
     default:
-        retval = krb5int_c_mandatory_cksumtype(context, in_cred->keyblock.enctype, &cksumtype);
-        if (retval)
+        ret = krb5int_c_mandatory_cksumtype(context, tgt->keyblock.enctype,
+                                            &cksumtype);
+        if (ret)
             goto cleanup;
     }
 
-    /* Generate checksum */
-    if ((retval = krb5_c_make_checksum(context, cksumtype,
-                                       &in_cred->keyblock,
-                                       KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
-                                       in_data, &checksum))) {
-        free(checksum.contents);
+    /* Generate checksum. */
+    ret = krb5_c_make_checksum(context, cksumtype, &tgt->keyblock,
+                               KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data,
+                               &checksum);
+    if (ret)
         goto cleanup;
-    }
 
-    /* gen authenticator */
-    authent.subkey = subkey; /*owned by caller*/
+    /* Construct, encode, and encrypt an authenticator. */
+    authent.subkey = subkey;
     authent.seq_number = 0;
     authent.checksum = &checksum;
-    authent.client = in_cred->client;
-    authent.authorization_data = in_cred->authdata;
-    if ((retval = krb5_us_timeofday(context, &authent.ctime,
-                                    &authent.cusec)))
+    authent.client = tgt->client;
+    authent.authorization_data = tgt->authdata;
+    ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec);
+    if (ret)
         goto cleanup;
-
-
-    /* encode the authenticator */
-    if ((retval = encode_krb5_authenticator(&authent, &scratch)))
+    ret = encode_krb5_authenticator(&authent, &authent_asn1);
+    if (ret)
         goto cleanup;
-
-    free(checksum.contents);
-    checksum.contents = NULL;
-
-
-    if ((retval = decode_krb5_ticket(&(in_cred)->ticket, &request.ticket)))
-        /* Cleanup scratch and scratch data */
+    ret = krb5_encrypt_helper(context, &tgt->keyblock,
+                              KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1,
+                              &authent_enc);
+    if (ret)
         goto cleanup;
 
-    /* call the encryption routine */
-    if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock,
-                                      KRB5_KEYUSAGE_TGS_REQ_AUTH,
-                                      scratch, &request.authenticator)))
+    ret = decode_krb5_ticket(&tgt->ticket, &ticket);
+    if (ret)
         goto cleanup;
 
-    if (!(retval = encode_krb5_ap_req(&request, &toutbuf))) {
-        *outbuf = *toutbuf;
-        free(toutbuf);
-    }
-
-    memset(request.authenticator.ciphertext.data, 0,
-           request.authenticator.ciphertext.length);
-    free(request.authenticator.ciphertext.data);
-    request.authenticator.ciphertext.length = 0;
-    request.authenticator.ciphertext.data = 0;
-
+    /* Encode the AP-REQ. */
+    ap_req.authenticator = authent_enc;
+    ap_req.ticket = ticket;
+    ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out);
 
 cleanup:
-    if (request.ticket)
-        krb5_free_ticket(context, request.ticket);
-
-    if (scratch != NULL && scratch->data != NULL) {
-        zap(scratch->data,  scratch->length);
-        free(scratch->data);
-    }
-    free(scratch);
-
-    return retval;
+    free(checksum.contents);
+    krb5_free_ticket(context, ticket);
+    krb5_free_data_contents(context, &authent_enc.ciphertext);
+    if (authent_asn1 != NULL)
+        zapfree(authent_asn1->data, authent_asn1->length);
+    free(authent_asn1);
+    return ret;
 }
+
 /*
- * Note that this function fills in part of rep even on failure.
- *
- * The pacb_fct callback allows the caller access to the nonce
- * and request subkey, for binding preauthentication data
+ * Construct a TGS request and return its ASN.1 encoding as well as the
+ * timestamp, nonce, and subkey used.  The pacb_fn callback allows the caller
+ * to amend the request padata after the nonce and subkey are determined.
  */
-
 krb5_error_code
 krb5int_make_tgs_request_ext(krb5_context context,
                              struct krb5int_fast_request_state *fast_state,
@@ -155,198 +122,176 @@ krb5int_make_tgs_request_ext(krb5_context context,
                              krb5_const_principal sname,
                              krb5_address *const *addrs,
                              krb5_authdata *const *authorization_data,
-                             krb5_pa_data *const *padata,
+                             krb5_pa_data *const *in_padata,
                              const krb5_data *second_ticket,
-                             krb5_creds *in_cred,
-                             krb5_error_code (*pacb_fct)(krb5_context,
-                                                         krb5_keyblock *,
-                                                         krb5_kdc_req *,
-                                                         void *),
+                             krb5_creds *tgt,
+                             krb5_error_code (*pacb_fn)(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_data *req_asn1_out,
+                             krb5_timestamp *timestamp_out,
+                             krb5_int32 *nonce_out,
+                             krb5_keyblock **subkey_out)
 {
-    krb5_error_code retval;
-    krb5_kdc_req tgsreq;
-    krb5_data *scratch, scratch2 = empty_data();
+    krb5_error_code ret;
+    krb5_kdc_req req;
+    krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL;
+    krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL;
     krb5_ticket *sec_ticket = NULL;
     krb5_ticket *sec_ticket_arr[2];
     krb5_timestamp time_now;
-    krb5_pa_data **combined_padata = NULL;
-    krb5_keyblock *local_subkey = NULL;
-
-    assert (subkey != NULL);
-    *subkey  = NULL;
-
-    /*
-     * in_creds MUST be a valid credential NOT just a partially filled in
-     * place holder for us to get credentials for the caller.
-     */
-    if (!in_cred->ticket.length)
+    krb5_pa_data **padata = NULL, *pa;
+    krb5_keyblock *subkey = NULL;
+    krb5_enc_data authdata_enc;
+    krb5_enctype *defenctypes = NULL;
+    size_t count, i;
+
+    *req_asn1_out = empty_data();
+    *timestamp_out = 0;
+    *nonce_out = 0;
+    *subkey_out = NULL;
+    memset(&req, 0, sizeof(req));
+    memset(&authdata_enc, 0, sizeof(authdata_enc));
+
+    /* tgt must be an actual credential, not a template. */
+    if (!tgt->ticket.length)
         return KRB5_NO_TKT_SUPPLIED;
 
-    memset(&tgsreq, 0, sizeof(tgsreq));
-
-    tgsreq.kdc_options = kdcoptions;
-    tgsreq.server = (krb5_principal) sname;
-
-    tgsreq.from = timestruct->starttime;
-    tgsreq.till = timestruct->endtime ? timestruct->endtime :    in_cred->times.endtime;
-    tgsreq.authorization_data.ciphertext.data = NULL;
-    tgsreq.rtime = timestruct->renew_till;
-    if ((retval = krb5_timeofday(context, &time_now)))
-        return retval;
-    /* XXX we know they are the same size... */
-    *nonce = tgsreq.nonce = (krb5_int32)time_now;
-    *timestamp = time_now;
-
-    tgsreq.addresses = (krb5_address **) addrs;
-
-    /* Generate subkey*/
-    if ((retval = krb5_generate_subkey( context, &in_cred->keyblock,
-                                        &local_subkey)) != 0)
-        return retval;
-    TRACE_SEND_TGS_SUBKEY(context, local_subkey);
-
-    retval = krb5int_fast_tgs_armor(context, fast_state, local_subkey,
-                                    &in_cred->keyblock, NULL, NULL);
-    if (retval)
+    req.kdc_options = kdcoptions;
+    req.server = (krb5_principal)sname;
+    req.from = timestruct->starttime;
+    req.till = timestruct->endtime ? timestruct->endtime : tgt->times.endtime;
+    req.authorization_data.ciphertext.data = NULL;
+    req.rtime = timestruct->renew_till;
+    ret = krb5_timeofday(context, &time_now);
+    if (ret)
+        return ret;
+    *nonce_out = req.nonce = (krb5_int32)time_now;
+    *timestamp_out = time_now;
+
+    req.addresses = (krb5_address **)addrs;
+
+    /* Generate subkey. */
+    ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey);
+    if (ret)
+        return ret;
+    TRACE_SEND_TGS_SUBKEY(context, subkey);
+
+    ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock,
+                                 NULL, NULL);
+    if (ret)
         goto cleanup;
-    if (authorization_data) {
-        /* need to encrypt it in the request */
 
-        if ((retval = encode_krb5_authdata(authorization_data, &scratch)))
+    if (authorization_data != NULL) {
+        ret = encode_krb5_authdata(authorization_data, &authdata_asn1);
+        if (ret)
             goto cleanup;
-
-        retval = krb5_encrypt_helper(context, local_subkey,
-                                     KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
-                                     scratch, &tgsreq.authorization_data);
-        krb5_free_data(context, scratch);
-        if (retval)
+        ret = krb5_encrypt_helper(context, subkey,
+                                  KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
+                                  authdata_asn1, &authdata_enc);
+        if (ret)
             goto cleanup;
+        req.authorization_data = authdata_enc;
     }
 
-    /* Get the encryption types list */
-    if (ktypes) {
-        /* Check passed ktypes and make sure they're valid. */
-        for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) {
-            if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) {
-                retval = KRB5_PROG_ETYPE_NOSUPP;
+    /* Get the encryption types list. */
+    if (ktypes != NULL) {
+        /* Check passed enctypes and make sure they're valid. */
+        for (req.nktypes = 0; ktypes[req.nktypes]; req.nktypes++) {
+            if (!krb5_c_valid_enctype(ktypes[req.nktypes])) {
+                ret = KRB5_PROG_ETYPE_NOSUPP;
                 goto cleanup;
             }
         }
-        tgsreq.ktype = (krb5_enctype *)ktypes;
+        req.ktype = (krb5_enctype *)ktypes;
     } else {
-        /* Get the default ktypes */
-        krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype));
-        for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++);
+        /* Get the default TGS enctypes. */
+        krb5_get_tgs_ktypes(context, sname, &defenctypes);
+        for (count = 0; defenctypes[count]; count++);
+        req.ktype = defenctypes;
+        req.nktypes = count;
     }
-    TRACE_SEND_TGS_ETYPES(context, tgsreq.ktype);
+    TRACE_SEND_TGS_ETYPES(context, req.ktype);
 
-    if (second_ticket) {
-        if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket)))
+    if (second_ticket != NULL) {
+        ret = decode_krb5_ticket(second_ticket, &sec_ticket);
+        if (ret)
             goto cleanup;
         sec_ticket_arr[0] = sec_ticket;
-        sec_ticket_arr[1] = 0;
-        tgsreq.second_ticket = sec_ticket_arr;
-    } else
-        tgsreq.second_ticket = 0;
+        sec_ticket_arr[1] = NULL;
+        req.second_ticket = sec_ticket_arr;
+    }
 
-    /* encode the body; then checksum it */
-    retval = krb5int_fast_prep_req_body(context, fast_state, &tgsreq,
-                                        &scratch);
-    if (retval)
+    /* Encode the request body. */
+    ret = krb5int_fast_prep_req_body(context, fast_state, &req,
+                                     &req_body_asn1);
+    if (ret)
         goto cleanup;
 
-    /*
-     * Get an ap_req.
-     */
-    if ((retval = tgs_construct_tgsreq(context, scratch, in_cred,
-                                       &scratch2, local_subkey))) {
-        krb5_free_data(context, scratch);
+    ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey,
+                               &ap_req_asn1);
+    if (ret)
         goto cleanup;
-    }
-    krb5_free_data(context, scratch);
 
-    tgsreq.padata = k5alloc(2 * sizeof(krb5_pa_data *), &retval);
-    if (tgsreq.padata == NULL) {
-        free(scratch2.data);
+    for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++);
+
+    /* Construct a padata array for the request, beginning with the ap-req. */
+    padata = k5alloc((count + 2) * sizeof(krb5_pa_data *), &ret);
+    if (padata == NULL)
         goto cleanup;
-    }
-    tgsreq.padata[0] = k5alloc(sizeof(krb5_pa_data), &retval);
-    if (tgsreq.padata[0] == NULL) {
-        free(scratch2.data);
+    padata[0] = k5alloc(sizeof(krb5_pa_data), &ret);
+    if (padata[0] == NULL)
         goto cleanup;
-    }
-    tgsreq.padata[0]->pa_type = KRB5_PADATA_AP_REQ;
-    tgsreq.padata[0]->length = scratch2.length;
-    tgsreq.padata[0]->contents = (krb5_octet *)scratch2.data;
-    tgsreq.padata[1] = NULL;
-
-    /* combine in any other supplied padata, unfortunately now it is
-     * necessary to copy it as the callback function might modify the
-     * padata, and having a separate path for the non-callback case,
-     * or attempting to determine which elements were changed by the
-     * callback, would have complicated the code significantly.
-     */
-    if (padata) {
-        krb5_pa_data **tmp;
-        int i;
-
-        for (i = 0; padata[i]; i++)
-            ;
+    padata[0]->pa_type = KRB5_PADATA_AP_REQ;
+    padata[0]->contents = k5alloc(ap_req_asn1->length, &ret);
+    if (padata[0] == NULL)
+        goto cleanup;
+    memcpy(padata[0]->contents, ap_req_asn1->data, ap_req_asn1->length);
+    padata[0]->length = ap_req_asn1->length;
 
-        tmp = realloc(tgsreq.padata, (i + 2) * sizeof(*combined_padata));
-        if (tmp == NULL) {
-            retval = ENOMEM;
+    /* Append copies of any other supplied padata. */
+    for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) {
+        pa = k5alloc(sizeof(krb5_pa_data), &ret);
+        if (pa == NULL)
             goto cleanup;
-        }
-
-        tgsreq.padata = tmp;
-
-        for (i = 0; padata[i]; i++) {
-            krb5_pa_data *pa;
-
-            pa = tgsreq.padata[1 + i] = k5alloc(sizeof(krb5_pa_data), &retval);
-            if (tgsreq.padata == NULL)
-                goto cleanup;
-
-            pa->pa_type = padata[i]->pa_type;
-            pa->length = padata[i]->length;
-            pa->contents = k5alloc(padata[i]->length, &retval);
-            if (pa->contents == NULL)
-                goto cleanup;
-            memcpy(pa->contents, padata[i]->contents, padata[i]->length);
-        }
-        tgsreq.padata[1 + i] = NULL;
+        pa->pa_type = in_padata[i]->pa_type;
+        pa->length = in_padata[i]->length;
+        pa->contents = k5alloc(in_padata[i]->length, &ret);
+        if (pa->contents == NULL)
+            goto cleanup;
+        memcpy(pa->contents, in_padata[i]->contents, in_padata[i]->length);
+        padata[i + 1] = pa;
     }
+    req.padata = padata;
 
-    if (pacb_fct != NULL) {
-        if ((retval = (*pacb_fct)(context, local_subkey, &tgsreq, pacb_data)))
+    if (pacb_fn != NULL) {
+        ret = (*pacb_fn)(context, subkey, &req, pacb_data);
+        if (ret)
             goto cleanup;
     }
-    /* the TGS_REQ is assembled in tgsreq, so encode it */
-    retval = krb5int_fast_prep_req(context, fast_state, &tgsreq, &scratch2,
-                                   encode_krb5_tgs_req, &scratch);
-    if (retval)
-        goto cleanup;
 
-    *request_data = *scratch;
-    free(scratch);
-    scratch = NULL;
+    /* Encode the TGS-REQ.  Discard the krb5_data container. */
+    ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1,
+                                encode_krb5_tgs_req, &tgs_req_asn1);
+    if (ret)
+        goto cleanup;
+    *req_asn1_out = *tgs_req_asn1;
+    free(tgs_req_asn1);
+    tgs_req_asn1 = NULL;
 
-    *subkey = local_subkey;
-    local_subkey = NULL;
+    *subkey_out = subkey;
+    subkey = NULL;
 
 cleanup:
-    krb5_free_pa_data(context, tgsreq.padata);
+    krb5_free_data(context, authdata_asn1);
+    krb5_free_data(context, req_body_asn1);
+    krb5_free_data(context, ap_req_asn1);
+    krb5_free_pa_data(context, req.padata);
     krb5_free_ticket(context, sec_ticket);
-    if (ktypes == NULL)
-        free(tgsreq.ktype);
-    zapfree(tgsreq.authorization_data.ciphertext.data,
-            tgsreq.authorization_data.ciphertext.length);
-    krb5_free_keyblock(context, local_subkey);
-    return retval;
+    krb5_free_data_contents(context, &authdata_enc.ciphertext);
+    krb5_free_keyblock(context, subkey);
+    free(defenctypes);
+    return ret;
 }


More information about the cvs-krb5 mailing list