]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Track first local TGT key in KDC code 971/head
authorGreg Hudson <ghudson@mit.edu>
Thu, 22 Aug 2019 06:04:28 +0000 (02:04 -0400)
committerGreg Hudson <ghudson@mit.edu>
Tue, 27 Aug 2019 00:04:01 +0000 (20:04 -0400)
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
src/kdc/do_as_req.c
src/kdc/do_tgs_req.c
src/kdc/fast_util.c
src/kdc/kdc_authdata.c
src/kdc/kdc_util.c
src/kdc/kdc_util.h

index 8d18b16e3d6e0378d5f9090afb31587d7f1101e5..9837463c40412039f9d9ca410a2f9bae8b9a2d8f 100644 (file)
@@ -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:
index 37961d1bccec8b4de511b281ac33de374f3d412c..a14e64fda90584c1a096134c570a5a81a1d78fe5 100644 (file)
@@ -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;
     }
 
index a7a01c2699230baa22574a7c62f4b8d498497c52..98f4cc6a4d3b241b32c2f5ea42fd9b87fb19123e 100644 (file)
@@ -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)
index a55fc1ba948b876c3f502e5d6c77b57c7eed0745..e4bb3cedbb0ec76fb36b9f9c656a3a6aa6a0fcdb 100644 (file)
@@ -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;
 
index 1b067cb0ba893e1bff12816ec0e59c55f8d9a3dd..df670e837b7657876fe130aef774430b07eb3928 100644 (file)
@@ -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;
     }
index 05684d28544c8a24d9c2ecbd3993c145b6e18f1c..db5a9edecb995315bf671296149d65ba0a82df93 100644 (file)
@@ -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;
 }
index 8d4d3f12c4927a4ae64de2fa1faa4f14f7e3504e..f41c0d94da9d8643edc838846d1f6a1e1e578745 100644 (file)
@@ -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);