krb5 commit: Track first local TGT key in KDC code

Greg Hudson ghudson at mit.edu
Mon Aug 26 20:14:32 EDT 2019


https://github.com/krb5/krb5/commit/570967e11bd5ea60a82fc8157ad7d07602402ebb
commit 570967e11bd5ea60a82fc8157ad7d07602402ebb
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Aug 22 02:04:28 2019 -0400

    Track first local TGT key in KDC code
    
    Decrypt the first local TGT key in get_local_tgt() and save it in the
    AS and TGS processing functions.  (As we now sort key data by
    descending kvno, this is guaranteed to be the most recent key.)  Pass
    this key to the authdata and FAST cookie functions to simplify cookie
    encryption and authdata signing.  Decryption and verification
    functions must still sometimes decrypt earlier keys to process tickets
    predating the last local TGT key rollover.

 src/kdc/cammac.c       |   52 +++++++++++----------
 src/kdc/do_as_req.c    |   43 ++++++++---------
 src/kdc/do_tgs_req.c   |   11 +++--
 src/kdc/fast_util.c    |   82 ++++++++++++++++++--------------
 src/kdc/kdc_authdata.c |  122 +++++++++++++++++++++++-------------------------
 src/kdc/kdc_util.c     |   34 ++++++++++---
 src/kdc/kdc_util.h     |   19 +++++---
 7 files changed, 198 insertions(+), 165 deletions(-)

diff --git a/src/kdc/cammac.c b/src/kdc/cammac.c
index 8d18b16..9837463 100644
--- a/src/kdc/cammac.c
+++ b/src/kdc/cammac.c
@@ -47,21 +47,22 @@ encode_kdcver_encpart(krb5_enc_tkt_part *enc_tkt, krb5_authdata **contents,
 }
 
 /*
- * Create a CAMMAC for contents, using enc_tkt and the first key from krbtgt
- * for the KDC verifier.  Set *cammac_out to a single-element authdata list
- * containing the CAMMAC inside an IF-RELEVANT container.
+ * Create a CAMMAC for contents, using enc_tkt and tgt_key for the KDC
+ * verifier.  tgt_key must be the decrypted first key data entry in tgt.  Set
+ * *cammac_out to a single-element authdata list containing the CAMMAC inside
+ * an IF-RELEVANT container.
  */
 krb5_error_code
 cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt,
-              krb5_keyblock *server_key, krb5_db_entry *krbtgt,
-              krb5_authdata **contents, krb5_authdata ***cammac_out)
+              krb5_keyblock *server_key, krb5_db_entry *tgt,
+              krb5_keyblock *tgt_key, krb5_authdata **contents,
+              krb5_authdata ***cammac_out)
 {
     krb5_error_code ret;
     krb5_data *der_authdata = NULL, *der_enctkt = NULL, *der_cammac = NULL;
     krb5_authdata ad, *list[2];
     krb5_cammac cammac;
     krb5_verifier_mac kdc_verifier, svc_verifier;
-    krb5_key_data *kd;
     krb5_keyblock tgtkey;
     krb5_checksum kdc_cksum, svc_cksum;
 
@@ -70,24 +71,16 @@ cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt,
     memset(&kdc_cksum, 0, sizeof(kdc_cksum));
     memset(&svc_cksum, 0, sizeof(svc_cksum));
 
-    /* Fetch the first krbtgt key for the KDC verifier. */
-    ret = krb5_dbe_find_enctype(context, krbtgt, -1, -1, 0, &kd);
-    if (ret)
-        goto cleanup;
-    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL);
-    if (ret)
-        goto cleanup;
-
     /* Checksum the reply with contents as authdata for the KDC verifier. */
     ret = encode_kdcver_encpart(enc_tkt, contents, &der_enctkt);
     if (ret)
         goto cleanup;
-    ret = krb5_c_make_checksum(context, 0, &tgtkey, KRB5_KEYUSAGE_CAMMAC,
+    ret = krb5_c_make_checksum(context, 0, tgt_key, KRB5_KEYUSAGE_CAMMAC,
                                der_enctkt, &kdc_cksum);
     if (ret)
         goto cleanup;
     kdc_verifier.princ = NULL;
-    kdc_verifier.kvno = kd->key_data_kvno;
+    kdc_verifier.kvno = tgt->key_data[0].key_data_kvno;
     kdc_verifier.enctype = ENCTYPE_NULL;
     kdc_verifier.checksum = kdc_cksum;
 
@@ -133,15 +126,19 @@ cleanup:
     return ret;
 }
 
-/* Return true if cammac's KDC verifier is valid for enc_tkt, using krbtgt to
- * retrieve the TGT key indicated by the verifier. */
+/*
+ * Return true if cammac's KDC verifier is valid for enc_tkt, using tgt to
+ * retrieve the TGT key indicated by the verifier.  tgt_key must be the
+ * decrypted first key data entry in tgt.
+ */
 krb5_boolean
 cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
-                    krb5_enc_tkt_part *enc_tkt, krb5_db_entry *krbtgt)
+                    krb5_enc_tkt_part *enc_tkt, krb5_db_entry *tgt,
+                    krb5_keyblock *tgt_key)
 {
     krb5_verifier_mac *ver = cammac->kdc_verifier;
     krb5_key_data *kd;
-    krb5_keyblock tgtkey;
+    krb5_keyblock tgtkey, *key;
     krb5_boolean valid = FALSE;
     krb5_data *der_enctkt = NULL;
 
@@ -152,10 +149,15 @@ cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
 
     /* Fetch the krbtgt key indicated by the KDC verifier.  Only allow the
      * first krbtgt key of the specified kvno. */
-    if (krb5_dbe_find_enctype(context, krbtgt, -1, -1, ver->kvno, &kd) != 0)
-        goto cleanup;
-    if (krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL) != 0)
-        goto cleanup;
+    if (ver->kvno == tgt->key_data[0].key_data_kvno) {
+        key = tgt_key;
+    } else {
+        if (krb5_dbe_find_enctype(context, tgt, -1, -1, ver->kvno, &kd) != 0)
+            goto cleanup;
+        if (krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL) != 0)
+            goto cleanup;
+        key = &tgtkey;
+    }
     if (ver->enctype != ENCTYPE_NULL && tgtkey.enctype != ver->enctype)
         goto cleanup;
 
@@ -163,7 +165,7 @@ cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
      * elements as authdata. */
     if (encode_kdcver_encpart(enc_tkt, cammac->elements, &der_enctkt) != 0)
         goto cleanup;
-    (void)krb5_c_verify_checksum(context, &tgtkey, KRB5_KEYUSAGE_CAMMAC,
+    (void)krb5_c_verify_checksum(context, key, KRB5_KEYUSAGE_CAMMAC,
                                  der_enctkt, &ver->checksum, &valid);
 
 cleanup:
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 37961d1..a14e64f 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -76,8 +76,8 @@
 
 static krb5_error_code
 prepare_error_as(struct kdc_request_state *, krb5_kdc_req *, krb5_db_entry *,
-                 int, krb5_pa_data **, krb5_boolean, krb5_principal,
-                 krb5_data **, const char *);
+                 krb5_keyblock *, int, krb5_pa_data **, krb5_boolean,
+                 krb5_principal, krb5_data **, const char *);
 
 /* Determine the key-expiration value according to RFC 4120 section 5.4.2. */
 static krb5_timestamp
@@ -157,6 +157,7 @@ struct as_req_state {
     krb5_enc_tkt_part enc_tkt_reply;
     krb5_enc_kdc_rep_part reply_encpart;
     krb5_ticket ticket_reply;
+    krb5_keyblock local_tgt_key;
     krb5_keyblock server_keyblock;
     krb5_keyblock client_keyblock;
     krb5_db_entry *client;
@@ -294,21 +295,12 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
         goto egress;
     }
 
-    errcode = handle_authdata(kdc_context,
-                              state->c_flags,
-                              state->client,
-                              state->server,
-                              NULL,
-                              state->local_tgt,
-                              &state->client_keyblock,
-                              &state->server_keyblock,
-                              NULL,
-                              state->req_pkt,
-                              state->request,
-                              NULL, /* for_user_princ */
-                              NULL, /* enc_tkt_request */
-                              state->auth_indicators,
-                              &state->enc_tkt_reply);
+    errcode = handle_authdata(kdc_context, state->c_flags, state->client,
+                              state->server, NULL, state->local_tgt,
+                              &state->local_tgt_key, &state->client_keyblock,
+                              &state->server_keyblock, NULL, state->req_pkt,
+                              state->request, NULL, NULL,
+                              state->auth_indicators, &state->enc_tkt_reply);
     if (errcode) {
         krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"),
                          errcode);
@@ -405,8 +397,9 @@ egress:
                 errcode = KRB_ERR_GENERIC;
 
             errcode = prepare_error_as(state->rstate, state->request,
-                                       state->local_tgt, errcode,
-                                       state->e_data, state->typed_e_data,
+                                       state->local_tgt, &state->local_tgt_key,
+                                       errcode, state->e_data,
+                                       state->typed_e_data,
                                        ((state->client != NULL) ?
                                         state->client->princ : NULL),
                                        &response, state->status);
@@ -419,6 +412,8 @@ egress:
     if (state->enc_tkt_reply.authorization_data != NULL)
         krb5_free_authdata(kdc_context,
                            state->enc_tkt_reply.authorization_data);
+    if (state->local_tgt_key.contents != NULL)
+        krb5_free_keyblock_contents(kdc_context, &state->local_tgt_key);
     if (state->server_keyblock.contents != NULL)
         krb5_free_keyblock_contents(kdc_context, &state->server_keyblock);
     if (state->client_keyblock.contents != NULL)
@@ -667,7 +662,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
 
     errcode = get_local_tgt(kdc_context, &state->request->server->realm,
                             state->server, &state->local_tgt,
-                            &state->local_tgt_storage);
+                            &state->local_tgt_storage, &state->local_tgt_key);
     if (errcode) {
         state->status = "GET_LOCAL_TGT";
         goto errout;
@@ -802,7 +797,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     }
 
     errcode = kdc_fast_read_cookie(kdc_context, state->rstate, state->request,
-                                   state->local_tgt);
+                                   state->local_tgt, &state->local_tgt_key);
     if (errcode) {
         state->status = "READ_COOKIE";
         goto errout;
@@ -826,7 +821,8 @@ errout:
 
 static krb5_error_code
 prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request,
-                 krb5_db_entry *local_tgt, int error, krb5_pa_data **e_data_in,
+                 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
+                 int error, krb5_pa_data **e_data_in,
                  krb5_boolean typed_e_data, krb5_principal canon_client,
                  krb5_data **response, const char *status)
 {
@@ -848,7 +844,8 @@ prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request,
             return ENOMEM;
         memcpy(e_data, e_data_in, count * sizeof(*e_data));
         retval = kdc_fast_make_cookie(kdc_context, rstate, local_tgt,
-                                      request->client, &cookie);
+                                      local_tgt_key, request->client,
+                                      &cookie);
         e_data[count] = cookie;
     }
 
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index a7a01c2..98f4cc6 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -115,7 +115,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     krb5_error_code retval = 0;
     krb5_keyblock server_keyblock, *encrypting_key;
     krb5_timestamp kdc_time, authtime = 0;
-    krb5_keyblock session_key;
+    krb5_keyblock session_key, local_tgt_key;
     krb5_keyblock *reply_key = NULL;
     krb5_key_data  *server_key;
     krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL;
@@ -144,6 +144,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     memset(&ticket_reply, 0, sizeof(ticket_reply));
     memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply));
     memset(&server_keyblock, 0, sizeof(server_keyblock));
+    memset(&local_tgt_key, 0, sizeof(local_tgt_key));
     session_key.contents = NULL;
 
     /* Save pointer to client-requested service principal, in case of
@@ -203,7 +204,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     }
 
     errcode = get_local_tgt(kdc_context, &sprinc->realm, header_server,
-                            &local_tgt, &local_tgt_storage);
+                            &local_tgt, &local_tgt_storage, &local_tgt_key);
     if (errcode) {
         status = "GET_LOCAL_TGT";
         goto cleanup;
@@ -361,7 +362,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
      * requests (where the client didn't authenticate). */
     if (s4u_x509_user == NULL) {
         errcode = get_auth_indicators(kdc_context, subject_tkt, local_tgt,
-                                      &auth_indicators);
+                                      &local_tgt_key, &auth_indicators);
         if (errcode) {
             status = "GET_AUTH_INDICATORS";
             goto cleanup;
@@ -606,7 +607,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     }
 
     errcode = handle_authdata(kdc_context, c_flags, client, server,
-                              header_server, local_tgt,
+                              header_server, local_tgt, &local_tgt_key,
                               subkey != NULL ? subkey :
                               header_ticket->enc_part2->session,
                               encrypting_key, /* U2U or server key */
@@ -798,6 +799,8 @@ cleanup:
     krb5_db_free_principal(kdc_context, header_server);
     krb5_db_free_principal(kdc_context, client);
     krb5_db_free_principal(kdc_context, local_tgt_storage);
+    if (local_tgt_key.contents != NULL)
+        krb5_free_keyblock_contents(kdc_context, &local_tgt_key);
     if (session_key.contents != NULL)
         krb5_free_keyblock_contents(kdc_context, &session_key);
     if (newtransited)
diff --git a/src/kdc/fast_util.c b/src/kdc/fast_util.c
index a55fc1b..e4bb3ce 100644
--- a/src/kdc/fast_util.c
+++ b/src/kdc/fast_util.c
@@ -463,37 +463,19 @@ make_padata(krb5_preauthtype pa_type, const void *contents, size_t len,
 }
 
 /*
- * Construct the secure cookie encryption key for the given local-realm TGT
- * entry, kvno, and client principal.  The cookie key is derived from the first
- * TGT key for the given kvno, using the concatenation of "COOKIE" and the
- * unparsed client principal name as input.  If kvno is 0, the highest current
- * kvno of the TGT is used.  If kvno_out is not null, *kvno_out is set to the
- * kvno used.
+ * Derive the secure cookie encryption key from tgt_key and client_princ.  The
+ * cookie key is derived with PRF+ using the concatenation of "COOKIE" and the
+ * unparsed client principal name as input.
  */
 static krb5_error_code
-get_cookie_key(krb5_context context, krb5_db_entry *tgt, krb5_kvno kvno,
-               krb5_const_principal client_princ, krb5_keyblock **key_out,
-               krb5_kvno *kvno_out)
+derive_cookie_key(krb5_context context, krb5_keyblock *tgt_key,
+                  krb5_const_principal client_princ, krb5_keyblock **key_out)
 {
     krb5_error_code ret;
-    krb5_key_data *kd;
-    krb5_keyblock kb;
     krb5_data d;
-    krb5_int32 start = 0;
     char *princstr = NULL, *derive_input = NULL;
 
     *key_out = NULL;
-    memset(&kb, 0, sizeof(kb));
-
-    /* Find the first krbtgt key with the specified kvno. */
-    ret = krb5_dbe_search_enctype(context, tgt, &start, -1, -1, kvno, &kd);
-    if (ret)
-        goto cleanup;
-
-    /* Decrypt the key. */
-    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL);
-    if (ret)
-        goto cleanup;
 
     /* Construct the input string and derive the cookie key. */
     ret = krb5_unparse_name(context, client_princ, &princstr);
@@ -504,18 +486,47 @@ get_cookie_key(krb5_context context, krb5_db_entry *tgt, krb5_kvno kvno,
         goto cleanup;
     }
     d = string2data(derive_input);
-    ret = krb5_c_derive_prfplus(context, &kb, &d, ENCTYPE_NULL, key_out);
-
-    if (kvno_out != NULL)
-        *kvno_out = kd->key_data_kvno;
+    ret = krb5_c_derive_prfplus(context, tgt_key, &d, ENCTYPE_NULL, key_out);
 
 cleanup:
-    krb5_free_keyblock_contents(context, &kb);
     krb5_free_unparsed_name(context, princstr);
     free(derive_input);
     return ret;
 }
 
+/* Derive the cookie key for the specified kvno in tgt.  tgt_key must be the
+ * decrypted first key data entry in tgt. */
+static krb5_error_code
+get_cookie_key(krb5_context context, krb5_db_entry *tgt,
+               krb5_keyblock *tgt_key, krb5_kvno kvno,
+               krb5_const_principal client_princ, krb5_keyblock **key_out)
+{
+    krb5_error_code ret;
+    krb5_keyblock storage, *key;
+    krb5_key_data *kd;
+
+    *key_out = NULL;
+    memset(&storage, 0, sizeof(storage));
+
+    if (kvno == tgt->key_data[0].key_data_kvno) {
+        /* Use the already-decrypted first key. */
+        key = tgt_key;
+    } else {
+        /* The cookie used an older TGT key; find and decrypt it. */
+        ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
+        if (ret)
+            return ret;
+        ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &storage, NULL);
+        if (ret)
+            return ret;
+        key = &storage;
+    }
+
+    ret = derive_cookie_key(context, key, client_princ, key_out);
+    krb5_free_keyblock_contents(context, &storage);
+    return ret;
+}
+
 /* Return true if there is any overlap between padata types in cpadata
  * (from the cookie) and rpadata (from the request). */
 static krb5_boolean
@@ -538,7 +549,8 @@ is_relevant(krb5_pa_data *const *cpadata, krb5_pa_data *const *rpadata)
  */
 krb5_error_code
 kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
-                     krb5_kdc_req *req, krb5_db_entry *local_tgt)
+                     krb5_kdc_req *req, krb5_db_entry *local_tgt,
+                     krb5_keyblock *local_tgt_key)
 {
     krb5_error_code ret;
     krb5_secure_cookie *cookie = NULL;
@@ -560,7 +572,8 @@ kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
 
     /* Extract the kvno and generate the corresponding cookie key. */
     kvno = load_32_be(pa->contents + 4);
-    ret = get_cookie_key(context, local_tgt, kvno, req->client, &key, NULL);
+    ret = get_cookie_key(context, local_tgt, local_tgt_key, kvno, req->client,
+                         &key);
     if (ret)
         goto cleanup;
 
@@ -646,7 +659,7 @@ kdc_fast_set_cookie(struct kdc_request_state *state, krb5_preauthtype pa_type,
  * trivial "MIT" cookie if no values are set. */
 krb5_error_code
 kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
-                     krb5_db_entry *local_tgt,
+                     krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
                      krb5_const_principal client_princ,
                      krb5_pa_data **cookie_out)
 {
@@ -657,7 +670,6 @@ kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
     krb5_timestamp now;
     krb5_enc_data enc;
     krb5_data *der_cookie = NULL;
-    krb5_kvno kvno;
     size_t ctlen;
 
     *cookie_out = NULL;
@@ -665,10 +677,10 @@ kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
 
     /* Make a trivial cookie if there are no contents to marshal or we don't
      * have a TGT entry to encrypt them. */
-    if (contents == NULL || *contents == NULL || local_tgt == NULL)
+    if (contents == NULL || *contents == NULL || local_tgt_key == NULL)
         return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out);
 
-    ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno);
+    ret = derive_cookie_key(context, local_tgt_key, client_princ, &key);
     if (ret)
         goto cleanup;
 
@@ -699,7 +711,7 @@ kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
     ret = k5_alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length,
                            &pa);
     memcpy(pa->contents, "MIT1", 4);
-    store_32_be(kvno, pa->contents + 4);
+    store_32_be(local_tgt->key_data[0].key_data_kvno, pa->contents + 4);
     memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
     *cookie_out = pa;
 
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 1b067cb..df670e8 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -438,6 +438,7 @@ make_signedpath_data(krb5_context context, krb5_const_principal client,
 
 static krb5_error_code
 verify_signedpath_checksum(krb5_context context, krb5_db_entry *local_tgt,
+                           krb5_keyblock *local_tgt_key,
                            krb5_enc_tkt_part *enc_tkt_part,
                            krb5_principal *deleg_path,
                            krb5_pa_data **method_data, krb5_checksum *cksum,
@@ -464,31 +465,31 @@ verify_signedpath_checksum(krb5_context context, krb5_db_entry *local_tgt,
     if (ret)
         return ret;
 
-    /* There is no kvno in AD-SIGNTICKET, so try the last three versions. */
-    kvno = 0;
-    tries = 3;
-    do {
-        /* Get the first local tgt key of this kvno (highest kvno for the first
-         * iteration). */
-        ret = krb5_dbe_find_enctype(context, local_tgt, -1, -1, kvno, &kd);
-        if (ret) {
-            ret = 0;
-            break;
+    ret = krb5_c_verify_checksum(context, local_tgt_key,
+                                 KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum,
+                                 &valid);
+    if (ret || !valid) {
+        /* There is no kvno in AD-SIGNTICKET, so try two previous versions. */
+        kvno = local_tgt->key_data[0].key_data_kvno - 1;
+        for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
+            /* Get the first local tgt key of this kvno. */
+            ret = krb5_dbe_find_enctype(context, local_tgt, -1, -1, kvno, &kd);
+            if (ret) {
+                ret = 0;
+                break;
+            }
+            ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL);
+            if (ret)
+                break;
+
+            ret = krb5_c_verify_checksum(context, &tgtkey,
+                                         KRB5_KEYUSAGE_AD_SIGNEDPATH, data,
+                                         cksum, &valid);
+            krb5_free_keyblock_contents(context, &tgtkey);
+            if (!ret && valid)
+                break;
         }
-        ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL);
-        if (ret)
-            break;
-
-        ret = krb5_c_verify_checksum(context, &tgtkey,
-                                     KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum,
-                                     &valid);
-        krb5_free_keyblock_contents(context, &tgtkey);
-        if (!ret && valid)
-            break;
-
-        /* Try the next lower kvno on the next iteration. */
-        kvno = kd->key_data_kvno - 1;
-    } while (--tries > 0 && kvno > 0);
+    }
 
     *valid_out = valid;
     krb5_free_data(context, data);
@@ -498,6 +499,7 @@ verify_signedpath_checksum(krb5_context context, krb5_db_entry *local_tgt,
 
 static krb5_error_code
 verify_signedpath(krb5_context context, krb5_db_entry *local_tgt,
+                  krb5_keyblock *local_tgt_key,
                   krb5_enc_tkt_part *enc_tkt_part,
                   krb5_principal **delegated_out, krb5_boolean *pathsigned_out)
 {
@@ -530,9 +532,10 @@ verify_signedpath(krb5_context context, krb5_db_entry *local_tgt,
         goto cleanup;
     }
 
-    ret = verify_signedpath_checksum(context, local_tgt, enc_tkt_part,
-                                     sp->delegated, sp->method_data,
-                                     &sp->checksum, pathsigned_out);
+    ret = verify_signedpath_checksum(context, local_tgt, local_tgt_key,
+                                     enc_tkt_part, sp->delegated,
+                                     sp->method_data, &sp->checksum,
+                                     pathsigned_out);
     if (ret)
         goto cleanup;
 
@@ -550,7 +553,7 @@ cleanup:
 static krb5_error_code
 make_signedpath_checksum(krb5_context context,
                          krb5_const_principal for_user_princ,
-                         krb5_db_entry *local_tgt,
+                         krb5_keyblock *local_tgt_key,
                          krb5_enc_tkt_part *enc_tkt_part,
                          krb5_principal *deleg_path,
                          krb5_pa_data **method_data, krb5_checksum *cksum_out,
@@ -559,42 +562,31 @@ make_signedpath_checksum(krb5_context context,
     krb5_error_code ret;
     krb5_data *data = NULL;
     krb5_const_principal client;
-    krb5_key_data *kd;
-    krb5_keyblock tgtkey;
 
-    memset(&tgtkey, 0, sizeof(tgtkey));
     memset(cksum_out, 0, sizeof(*cksum_out));
     *enctype_out = ENCTYPE_NULL;
 
     client = (for_user_princ != NULL) ? for_user_princ : enc_tkt_part->client;
 
-    /* Get the first local tgt key of the highest kvno. */
-    ret = krb5_dbe_find_enctype(context, local_tgt, -1, -1, 0, &kd);
-    if (ret)
-        goto cleanup;
-    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL);
-    if (ret)
-        goto cleanup;
-
     ret = make_signedpath_data(context, client, enc_tkt_part->times.authtime,
                                deleg_path, method_data,
                                enc_tkt_part->authorization_data, &data);
     if (ret)
-        goto cleanup;
+        return ret;
 
-    ret = krb5_c_make_checksum(context, 0, &tgtkey,
+    ret = krb5_c_make_checksum(context, 0, local_tgt_key,
                                KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum_out);
-    *enctype_out = tgtkey.enctype;
-
-cleanup:
     krb5_free_data(context, data);
-    krb5_free_keyblock_contents(context, &tgtkey);
-    return ret;
+    if (ret)
+        return ret;
+
+    *enctype_out = local_tgt_key->enctype;
+    return 0;
 }
 
 static krb5_error_code
 make_signedpath(krb5_context context, krb5_const_principal for_user_princ,
-                krb5_principal server, krb5_db_entry *local_tgt,
+                krb5_principal server, krb5_keyblock *local_tgt_key,
                 krb5_principal *deleg_path, krb5_enc_tkt_part *enc_tkt_reply)
 {
     krb5_error_code ret;
@@ -620,7 +612,7 @@ make_signedpath(krb5_context context, krb5_const_principal for_user_princ,
     sp.delegated[count] = NULL;
     sp.method_data = NULL;
 
-    ret = make_signedpath_checksum(context, for_user_princ, local_tgt,
+    ret = make_signedpath_checksum(context, for_user_princ, local_tgt_key,
                                    enc_tkt_reply, sp.delegated, sp.method_data,
                                    &sp.checksum, &sp.enctype);
     if (ret) {
@@ -692,8 +684,8 @@ only_pac_p(krb5_context context, krb5_authdata **authdata)
 static krb5_error_code
 handle_signticket(krb5_context context, unsigned int flags,
                   krb5_db_entry *client, krb5_db_entry *server,
-                  krb5_db_entry *local_tgt, krb5_kdc_req *req,
-                  krb5_const_principal for_user_princ,
+                  krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
+                  krb5_kdc_req *req, krb5_const_principal for_user_princ,
                   krb5_enc_tkt_part *enc_tkt_req,
                   krb5_enc_tkt_part *enc_tkt_reply)
 {
@@ -710,8 +702,8 @@ handle_signticket(krb5_context context, unsigned int flags,
      */
     if (req->msg_type == KRB5_TGS_REQ &&
         !only_pac_p(context, enc_tkt_req->authorization_data)) {
-        ret = verify_signedpath(context, local_tgt, enc_tkt_req, &deleg_path,
-                                &signed_path);
+        ret = verify_signedpath(context, local_tgt, local_tgt_key, enc_tkt_req,
+                                &deleg_path, &signed_path);
         if (ret)
             goto cleanup;
 
@@ -727,7 +719,7 @@ handle_signticket(krb5_context context, unsigned int flags,
         !is_cross_tgs_principal(server->princ) &&
         !only_pac_p(context, enc_tkt_reply->authorization_data)) {
         ret = make_signedpath(context, for_user_princ,
-                              s4u2proxy ? client->princ : NULL, local_tgt,
+                              s4u2proxy ? client->princ : NULL, local_tgt_key,
                               deleg_path, enc_tkt_reply);
         if (ret)
             goto cleanup;
@@ -743,6 +735,7 @@ cleanup:
 static krb5_error_code
 add_auth_indicators(krb5_context context, krb5_data *const *auth_indicators,
                     krb5_keyblock *server_key, krb5_db_entry *krbtgt,
+                    krb5_keyblock *krbtgt_key,
                     krb5_enc_tkt_part *enc_tkt_reply)
 {
     krb5_error_code ret;
@@ -760,8 +753,8 @@ add_auth_indicators(krb5_context context, krb5_data *const *auth_indicators,
     list[1] = NULL;
 
     /* Wrap the list in CAMMAC and IF-RELEVANT containers. */
-    ret = cammac_create(context, enc_tkt_reply, server_key, krbtgt, list,
-                        &cammac);
+    ret = cammac_create(context, enc_tkt_reply, server_key, krbtgt, krbtgt_key,
+                        list, &cammac);
     if (ret)
         goto cleanup;
 
@@ -782,7 +775,8 @@ cleanup:
  * enc_tkt. */
 krb5_error_code
 get_auth_indicators(krb5_context context, krb5_enc_tkt_part *enc_tkt,
-                    krb5_db_entry *local_tgt, krb5_data ***indicators_out)
+                    krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
+                    krb5_data ***indicators_out)
 {
     krb5_error_code ret;
     krb5_authdata **cammacs = NULL, **adp;
@@ -801,7 +795,8 @@ get_auth_indicators(krb5_context context, krb5_enc_tkt_part *enc_tkt,
         ret = decode_krb5_cammac(&der_cammac, &cammac);
         if (ret)
             goto cleanup;
-        if (cammac_check_kdcver(context, cammac, enc_tkt, local_tgt)) {
+        if (cammac_check_kdcver(context, cammac, enc_tkt, local_tgt,
+                                local_tgt_key)) {
             ret = authind_extract(context, cammac->elements, &indicators);
             if (ret)
                 goto cleanup;
@@ -824,9 +819,10 @@ krb5_error_code
 handle_authdata(krb5_context context, unsigned int flags,
                 krb5_db_entry *client, krb5_db_entry *server,
                 krb5_db_entry *header_server, krb5_db_entry *local_tgt,
-                krb5_keyblock *client_key, krb5_keyblock *server_key,
-                krb5_keyblock *header_key, krb5_data *req_pkt,
-                krb5_kdc_req *req, krb5_const_principal for_user_princ,
+                krb5_keyblock *local_tgt_key, krb5_keyblock *client_key,
+                krb5_keyblock *server_key, krb5_keyblock *header_key,
+                krb5_data *req_pkt, krb5_kdc_req *req,
+                krb5_const_principal for_user_princ,
                 krb5_enc_tkt_part *enc_tkt_req,
                 krb5_data *const *auth_indicators,
                 krb5_enc_tkt_part *enc_tkt_reply)
@@ -870,7 +866,7 @@ handle_authdata(krb5_context context, unsigned int flags,
     if (auth_indicators != NULL && *auth_indicators != NULL &&
         !isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED)) {
         ret = add_auth_indicators(context, auth_indicators, server_key,
-                                  local_tgt, enc_tkt_reply);
+                                  local_tgt, local_tgt_key, enc_tkt_reply);
         if (ret)
             return ret;
     }
@@ -886,8 +882,8 @@ handle_authdata(krb5_context context, unsigned int flags,
         /* Validate and insert AD-SIGNTICKET authdata.  This must happen last
          * since it contains a signature over the other authdata. */
         ret = handle_signticket(context, flags, client, server, local_tgt,
-                                req, for_user_princ, enc_tkt_req,
-                                enc_tkt_reply);
+                                local_tgt_key, req, for_user_princ,
+                                enc_tkt_req, enc_tkt_reply);
         if (ret)
             return ret;
     }
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 05684d2..db5a9ed 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -550,7 +550,8 @@ errout:
 /*
  * If candidate is the local TGT for realm, set *alias_out to candidate and
  * *storage_out to NULL.  Otherwise, load the local TGT into *storage_out and
- * set *alias_out to *storage_out.
+ * set *alias_out to *storage_out.  In either case, set *key_out to the
+ * decrypted first key of the local TGT.
  *
  * In the future we might generalize this to a small per-request principal
  * cache.  For now, it saves a load operation in the common case where the AS
@@ -559,29 +560,46 @@ errout:
 krb5_error_code
 get_local_tgt(krb5_context context, const krb5_data *realm,
               krb5_db_entry *candidate, krb5_db_entry **alias_out,
-              krb5_db_entry **storage_out)
+              krb5_db_entry **storage_out, krb5_keyblock *key_out)
 {
     krb5_error_code ret;
     krb5_principal princ;
-    krb5_db_entry *tgt;
+    krb5_db_entry *storage = NULL, *tgt;
 
     *alias_out = NULL;
     *storage_out = NULL;
+    memset(key_out, 0, sizeof(*key_out));
 
     ret = krb5_build_principal_ext(context, &princ, realm->length, realm->data,
                                    KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
                                    realm->length, realm->data, 0);
     if (ret)
-        return ret;
+        goto cleanup;
 
     if (!krb5_principal_compare(context, candidate->princ, princ)) {
-        ret = krb5_db_get_principal(context, princ, 0, &tgt);
-        if (!ret)
-            *storage_out = *alias_out = tgt;
+        ret = krb5_db_get_principal(context, princ, 0, &storage);
+        if (ret)
+            goto cleanup;
+        tgt = storage;
     } else {
-        *alias_out = candidate;
+        tgt = candidate;
     }
 
+    if (tgt->n_key_data == 0) {
+        ret = KRB5_KDB_NO_MATCHING_KEY;
+        goto cleanup;
+    }
+    ret = krb5_dbe_decrypt_key_data(context, NULL, &tgt->key_data[0], key_out,
+                                    NULL);
+    if (ret)
+        goto cleanup;
+
+    *alias_out = tgt;
+    *storage_out = storage;
+    storage = NULL;
+
+cleanup:
+    krb5_db_free_principal(context, storage);
     krb5_free_principal(context, princ);
     return ret;
 }
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index 8d4d3f1..f41c0d9 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -78,7 +78,7 @@ kdc_get_server_key (krb5_context, krb5_ticket *, unsigned int,
 krb5_error_code
 get_local_tgt(krb5_context context, const krb5_data *realm,
               krb5_db_entry *candidate, krb5_db_entry **alias_out,
-              krb5_db_entry **storage_out);
+              krb5_db_entry **storage_out, krb5_keyblock *kb_out);
 
 int
 validate_as_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry,
@@ -127,12 +127,14 @@ authind_extract(krb5_context context, krb5_authdata **authdata,
 /* cammac.c */
 krb5_error_code
 cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt_reply,
-              krb5_keyblock *server_key, krb5_db_entry *krbtgt,
-              krb5_authdata **contents, krb5_authdata ***cammac_out);
+              krb5_keyblock *server_key, krb5_db_entry *tgt,
+              krb5_keyblock *tgt_key, krb5_authdata **contents,
+              krb5_authdata ***cammac_out);
 
 krb5_boolean
 cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
-                    krb5_enc_tkt_part *enc_tkt, krb5_db_entry *krbtgt);
+                    krb5_enc_tkt_part *enc_tkt, krb5_db_entry *tgt,
+                    krb5_keyblock *tgt_key);
 
 /* do_as_req.c */
 void
@@ -216,7 +218,8 @@ unload_authdata_plugins(krb5_context context);
 
 krb5_error_code
 get_auth_indicators(krb5_context context, krb5_enc_tkt_part *enc_tkt,
-                    krb5_db_entry *local_tgt, krb5_data ***indicators_out);
+                    krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
+                    krb5_data ***indicators_out);
 
 krb5_error_code
 handle_authdata (krb5_context context,
@@ -225,6 +228,7 @@ handle_authdata (krb5_context context,
                  krb5_db_entry *server,
                  krb5_db_entry *header_server,
                  krb5_db_entry *local_tgt,
+                 krb5_keyblock *local_tgt_key,
                  krb5_keyblock *client_key,
                  krb5_keyblock *server_key,
                  krb5_keyblock *header_key,
@@ -393,7 +397,8 @@ kdc_handle_protected_negotiation( krb5_context context,
 
 krb5_error_code
 kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
-                     krb5_kdc_req *req, krb5_db_entry *local_tgt);
+                     krb5_kdc_req *req, krb5_db_entry *local_tgt,
+                     krb5_keyblock *local_tgt_key);
 
 krb5_boolean kdc_fast_search_cookie(struct kdc_request_state *state,
                                     krb5_preauthtype pa_type, krb5_data *out);
@@ -404,7 +409,7 @@ krb5_error_code kdc_fast_set_cookie(struct kdc_request_state *state,
 
 krb5_error_code
 kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
-                     krb5_db_entry *local_tgt,
+                     krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
                      krb5_const_principal client_princ,
                      krb5_pa_data **cookie_out);
 


More information about the cvs-krb5 mailing list