krb5 commit: Add KDC helpers for current key and kvno

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/42e21c57261f78cf0c68cbef16d58ca4a106f0e0
commit 42e21c57261f78cf0c68cbef16d58ca4a106f0e0
Author: Greg Hudson <[email protected]>
Date:   Wed May 13 13:01:31 2020 -0400

    Add KDC helpers for current key and kvno
    
    Add a simple static inline function current_kvno() to safely fetch the
    current kvno of a principal entry, and use it where we currently write
    entry->key_data[0].key_data_kvno.
    
    Add a function get_first_current_key() to find and decrypt the first
    valid current key from an entry.  Use it in get_local_tgt() and when
    selecting a ticket encryption key during AS and TGS processing.
    
    Add a local_tgt_key field to krb5_kdcpreauth_rock_st and use it in
    add_freshness_token() so we don't have to decrypt it again.

 src/kdc/cammac.c      |    4 ++--
 src/kdc/do_as_req.c   |   29 +++++------------------------
 src/kdc/do_tgs_req.c  |   24 +++---------------------
 src/kdc/fast_util.c   |    4 ++--
 src/kdc/kdc_preauth.c |   15 +++------------
 src/kdc/kdc_util.c    |   23 +++++++++++++++++------
 src/kdc/kdc_util.h    |   12 ++++++++++++
 7 files changed, 44 insertions(+), 67 deletions(-)

diff --git a/src/kdc/cammac.c b/src/kdc/cammac.c
index 9837463..df5fe96 100644
--- a/src/kdc/cammac.c
+++ b/src/kdc/cammac.c
@@ -80,7 +80,7 @@ cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt,
     if (ret)
         goto cleanup;
     kdc_verifier.princ = NULL;
-    kdc_verifier.kvno = tgt->key_data[0].key_data_kvno;
+    kdc_verifier.kvno = current_kvno(tgt);
     kdc_verifier.enctype = ENCTYPE_NULL;
     kdc_verifier.checksum = kdc_cksum;
 
@@ -149,7 +149,7 @@ 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 (ver->kvno == tgt->key_data[0].key_data_kvno) {
+    if (ver->kvno == current_kvno(tgt)) {
         key = tgt_key;
     } else {
         if (krb5_dbe_find_enctype(context, tgt, -1, -1, ver->kvno, &kd) != 0)
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 9ae7b0a..a893010 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -192,7 +192,6 @@ struct as_req_state {
 static void
 finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
 {
-    krb5_key_data *server_key;
     krb5_keyblock *as_encrypting_key = NULL;
     krb5_data *response = NULL;
     const char *emsg = 0;
@@ -220,32 +219,13 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
     if (errcode)
         goto egress;
 
-    /*
-     * Find the server key
-     */
-    if ((errcode = krb5_dbe_find_enctype(kdc_context, state->server,
-                                         -1, /* ignore keytype   */
-                                         -1, /* Ignore salttype  */
-                                         0,  /* Get highest kvno */
-                                         &server_key))) {
+    errcode = get_first_current_key(kdc_context, state->server,
+                                    &state->server_keyblock);
+    if (errcode) {
         state->status = "FINDING_SERVER_KEY";
         goto egress;
     }
 
-    /*
-     * Convert server->key into a real key
-     * (it may be encrypted in the database)
-     *
-     *  server_keyblock is later used to generate auth data signatures
-     */
-    if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
-                                             server_key,
-                                             &state->server_keyblock,
-                                             NULL))) {
-        state->status = "DECRYPT_SERVER_KEY";
-        goto egress;
-    }
-
     /* Start assembling the response */
     state->reply.msg_type = KRB5_AS_REP;
     state->reply.client = state->enc_tkt_reply.client; /* post canonization */
@@ -311,7 +291,7 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
     if (errcode)
         goto egress;
 
-    state->ticket_reply.enc_part.kvno = server_key->key_data_kvno;
+    state->ticket_reply.enc_part.kvno = current_kvno(state->server);
     errcode = kdc_fast_response_handle_padata(state->rstate,
                                               state->request,
                                               &state->reply,
@@ -659,6 +639,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
         goto errout;
     }
     state->rock.local_tgt = state->local_tgt;
+    state->rock.local_tgt_key = &state->local_tgt_key;
 
     au_state->stage = VALIDATE_POL;
 
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 463a9c0..8860fe8 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -120,7 +120,6 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
     krb5_timestamp kdc_time, authtime = 0;
     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;
     krb5_const_principal authdata_client;
     krb5_principal stkt_authdata_client = NULL;
@@ -523,28 +522,11 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
         encrypting_key = t2enc->session;
     } else {
-        /*
-         * Find the server key
-         */
-        if ((errcode = krb5_dbe_find_enctype(kdc_context, server,
-                                             -1, /* ignore keytype */
-                                             -1, /* Ignore salttype */
-                                             0,  /* Get highest kvno */
-                                             &server_key))) {
+        errcode = get_first_current_key(kdc_context, server, &server_keyblock);
+        if (errcode) {
             status = "FINDING_SERVER_KEY";
             goto cleanup;
         }
-
-        /*
-         * Convert server.key into a real key
-         * (it may be encrypted in the database)
-         */
-        if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
-                                                 server_key, &server_keyblock,
-                                                 NULL))) {
-            status = "DECRYPT_SERVER_KEY";
-            goto cleanup;
-        }
         encrypting_key = &server_keyblock;
     }
 
@@ -680,7 +662,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
         kau_u2u(kdc_context, TRUE, au_state);
         st_idx++;
     } else {
-        ticket_kvno = server_key->key_data_kvno;
+        ticket_kvno = current_kvno(server);
     }
 
     errcode = krb5_encrypt_tkt_part(kdc_context, encrypting_key,
diff --git a/src/kdc/fast_util.c b/src/kdc/fast_util.c
index e4bb3ce..ff3338e 100644
--- a/src/kdc/fast_util.c
+++ b/src/kdc/fast_util.c
@@ -508,7 +508,7 @@ get_cookie_key(krb5_context context, krb5_db_entry *tgt,
     *key_out = NULL;
     memset(&storage, 0, sizeof(storage));
 
-    if (kvno == tgt->key_data[0].key_data_kvno) {
+    if (kvno == current_kvno(tgt)) {
         /* Use the already-decrypted first key. */
         key = tgt_key;
     } else {
@@ -711,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(local_tgt->key_data[0].key_data_kvno, pa->contents + 4);
+    store_32_be(current_kvno(local_tgt), pa->contents + 4);
     memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
     *cookie_out = pa;
 
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 04cd0e7..8d83274 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -811,7 +811,6 @@ add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
 {
     krb5_error_code ret;
     krb5_timestamp now;
-    krb5_key_data *kd;
     krb5_keyblock kb;
     krb5_checksum cksum;
     krb5_data d;
@@ -827,29 +826,21 @@ add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
                              KRB5_PADATA_AS_FRESHNESS) == NULL)
         return 0;
 
-    /* Fetch and decrypt the current local krbtgt key. */
-    ret = krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, 0, &kd);
-    if (ret)
-        goto cleanup;
-    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL);
-    if (ret)
-        goto cleanup;
-
     /* Compute a checksum over the current KDC time. */
     ret = krb5_timeofday(context, &now);
     if (ret)
         goto cleanup;
     store_32_be(now, ckbuf);
     d = make_data(ckbuf, sizeof(ckbuf));
-    ret = krb5_c_make_checksum(context, 0, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,
-                               &d, &cksum);
+    ret = krb5_c_make_checksum(context, 0, rock->local_tgt_key,
+                               KRB5_KEYUSAGE_PA_AS_FRESHNESS, &d, &cksum);
 
     /* Compose a freshness token from the time, krbtgt kvno, and checksum. */
     ret = k5_alloc_pa_data(KRB5_PADATA_AS_FRESHNESS, 8 + cksum.length, &pa);
     if (ret)
         goto cleanup;
     store_32_be(now, pa->contents);
-    store_32_be(kd->key_data_kvno, pa->contents + 4);
+    store_32_be(current_kvno(rock->local_tgt), pa->contents + 4);
     memcpy(pa->contents + 8, cksum.contents, cksum.length);
 
     ret = k5_add_pa_data_element(pa_list, &pa);
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 4390b28..b3bca52 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -496,6 +496,22 @@ errout:
     return retval;
 }
 
+/* Find the first key data entry (of a valid enctype) of the highest kvno in
+ * entry, and decrypt it into *key_out. */
+krb5_error_code
+get_first_current_key(krb5_context context, krb5_db_entry *entry,
+                      krb5_keyblock *key_out)
+{
+    krb5_error_code ret;
+    krb5_key_data *kd;
+
+    memset(key_out, 0, sizeof(*key_out));
+    ret = krb5_dbe_find_enctype(context, entry, -1, -1, 0, &kd);
+    if (ret)
+        return ret;
+    return krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
+}
+
 /*
  * 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
@@ -514,7 +530,6 @@ get_local_tgt(krb5_context context, const krb5_data *realm,
     krb5_error_code ret;
     krb5_principal princ;
     krb5_db_entry *storage = NULL, *tgt;
-    krb5_key_data *kd;
 
     *alias_out = NULL;
     *storage_out = NULL;
@@ -535,11 +550,7 @@ get_local_tgt(krb5_context context, const krb5_data *realm,
         tgt = candidate;
     }
 
-    /* Find and decrypt the first valid key of the current kvno. */
-    ret = krb5_dbe_find_enctype(context, tgt, -1, -1, 0, &kd);
-    if (ret)
-        goto cleanup;
-    ret = krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
+    ret = get_first_current_key(context, tgt, key_out);
     if (ret)
         goto cleanup;
 
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index 0b087d2..ff87cd6 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -71,6 +71,10 @@ kdc_get_server_key (krb5_context, krb5_ticket *, unsigned int,
                     krb5_db_entry **, krb5_keyblock **, krb5_kvno *);
 
 krb5_error_code
+get_first_current_key(krb5_context context, krb5_db_entry *entry,
+                      krb5_keyblock *key_out);
+
+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_keyblock *kb_out);
@@ -421,6 +425,7 @@ struct krb5_kdcpreauth_rock_st {
     krb5_data *inner_body;
     krb5_db_entry *client;
     krb5_db_entry *local_tgt;
+    krb5_keyblock *local_tgt_key;
     krb5_key_data *client_key;
     krb5_keyblock *client_keyblock;
     struct kdc_request_state *rstate;
@@ -526,4 +531,11 @@ int errcode_to_protocol(krb5_error_code code);
 
 char *data2string(krb5_data *d);
 
+/* Return the current key version of entry, or 0 if it has no keys. */
+static inline krb5_kvno
+current_kvno(krb5_db_entry *entry)
+{
+    return (entry->n_key_data == 0) ? 0 : entry->key_data[0].key_data_kvno;
+}
+
 #endif /* __KRB5_KDC_UTIL__ */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.