krb5 commit: Re-encrypt preserved key data in new master key

Greg Hudson ghudson at mit.edu
Fri Sep 5 15:04:21 EDT 2014


https://github.com/krb5/krb5/commit/32c9b8f1aa1b348388ed227394cc609e68ed833b
commit 32c9b8f1aa1b348388ed227394cc609e68ed833b
Author: Greg Hudson <ghudson at mit.edu>
Date:   Mon Aug 25 12:48:14 2014 -0400

    Re-encrypt preserved key data in new master key
    
    When we are preserving old key data in kdb_cpw.c, ensure that it is
    encrypted with the same master key as the new key data.  This ensures
    that the KRB5_TL_MKVNO tl-data on the principal entry applies to all
    of the key data, not just some of it.
    
    ticket: 7995
    target_version: 1.13
    tags: pullup

 src/lib/kdb/kdb_cpw.c |  197 +++++++++++++++++++++++++++++++------------------
 1 files changed, 126 insertions(+), 71 deletions(-)

diff --git a/src/lib/kdb/kdb_cpw.c b/src/lib/kdb/kdb_cpw.c
index 5481553..d45e251 100644
--- a/src/lib/kdb/kdb_cpw.c
+++ b/src/lib/kdb/kdb_cpw.c
@@ -1,7 +1,7 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /* lib/kdb/kdb_cpw.c */
 /*
- * Copyright 1995, 2009 by the Massachusetts Institute of Technology.
+ * Copyright 1995, 2009, 2014 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -91,6 +91,101 @@ cleanup_key_data(context, count, data)
     krb5_db_free(context, data);
 }
 
+/* Copy key data from in to out, using krb5_db_alloc storage for out. */
+static krb5_error_code
+copy_key_data(krb5_context context, krb5_key_data *in, krb5_key_data *out)
+{
+    int i;
+    void *copies[2] = { NULL, NULL };
+
+    memset(out, 0, sizeof(*out));
+
+    /* Copy the key data contents using krb5_db_alloc storage. */
+    for (i = 0; i < in->key_data_ver && i < 2; i++) {
+        if (in->key_data_length[i] == 0)
+            continue;
+        copies[i] = krb5_db_alloc(context, NULL, in->key_data_length[i]);
+        if (copies[i] == NULL) {
+            while (--i >= 0) {
+                zap(copies[i], in->key_data_length[i]);
+                krb5_db_free(context, copies[i]);
+            }
+            return ENOMEM;
+        }
+        memcpy(copies[i], in->key_data_contents[i], in->key_data_length[i]);
+    }
+
+    /* Copy the structure and replace the allocated fields with the copies. */
+    *out = *in;
+    for (i = 0; i < 2; i++)
+        out->key_data_contents[i] = copies[i];
+
+    return 0;
+}
+
+/* Copy key data from old_kd to new_kd.  new_kd will be encrypted with mkey and
+ * will use krb5_db_alloc storage. */
+static krb5_error_code
+preserve_one_old_key(krb5_context context, krb5_keyblock *mkey,
+                     krb5_db_entry *dbent, krb5_key_data *old_kd,
+                     krb5_key_data *new_kd)
+{
+    krb5_error_code ret;
+    krb5_keyblock kb;
+    krb5_keysalt salt;
+    krb5_key_data kd;
+
+    memset(new_kd, 0, sizeof(*new_kd));
+    memset(&kd, 0, sizeof(kd));
+
+    ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL);
+    if (ret == 0) {
+        /* old_kd is already encrypted in mkey, so just copy it. */
+        krb5_free_keyblock_contents(context, &kb);
+        return copy_key_data(context, old_kd, new_kd);
+    }
+
+    /* Decrypt and re-encrypt old_kd using mkey. */
+    ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt);
+    if (ret)
+        return ret;
+    ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt,
+                                    old_kd->key_data_kvno, &kd);
+    krb5_free_keyblock_contents(context, &kb);
+    krb5_free_data_contents(context, &salt.data);
+    if (ret)
+        return ret;
+
+    /* Copy the result to ensure new_kd uses db_alloc storage. */
+    ret = copy_key_data(context, &kd, new_kd);
+    krb5_dbe_free_key_data_contents(context, &kd);
+    return ret;
+}
+
+/* Add key_data to dbent, making sure that each entry is encrypted in mkey.  If
+ * kvno is non-zero, preserve only keys of that kvno. */
+static krb5_error_code
+preserve_old_keys(krb5_context context, krb5_keyblock *mkey,
+                  krb5_db_entry *dbent, int kvno, int n_key_data,
+                  krb5_key_data *key_data)
+{
+    krb5_error_code ret;
+    int i;
+
+    for (i = 0; i < n_key_data; i++) {
+        if (kvno != 0 && key_data[i].key_data_kvno != kvno)
+            continue;
+        ret = krb5_dbe_create_key_data(context, dbent);
+        if (ret)
+            return ret;
+        ret = preserve_one_old_key(context, mkey, dbent, &key_data[i],
+                                   &dbent->key_data[dbent->n_key_data - 1]);
+        if (ret)
+            return ret;
+    }
+    return 0;
+}
+
 static krb5_error_code
 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
     krb5_context          context;
@@ -242,11 +337,9 @@ krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
     krb5_db_entry       * db_entry;
 {
     int                   key_data_count;
-    int                   n_new_key_data;
     krb5_key_data       * key_data;
     krb5_error_code       retval;
     int                   kvno;
-    int                   i;
 
     /* First save the old keydata */
     kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
@@ -265,23 +358,15 @@ krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
         db_entry->n_key_data = key_data_count;
         db_entry->key_data = key_data;
-    } else if (keepold) {
-        n_new_key_data = db_entry->n_key_data;
-        for (i = 0; i < key_data_count; i++) {
-            retval = krb5_dbe_create_key_data(context, db_entry);
-            if (retval) {
-                cleanup_key_data(context, db_entry->n_key_data,
-                                 db_entry->key_data);
-                break;
-            }
-            db_entry->key_data[i+n_new_key_data] = key_data[i];
-            memset(&key_data[i], 0, sizeof(krb5_key_data));
-        }
-        krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */
-    } else {
-        cleanup_key_data(context, key_data_count, key_data);
+        return retval;
     }
-    return(retval);
+
+    if (keepold) {
+        retval = preserve_old_keys(context, master_key, db_entry, 0,
+                                   key_data_count, key_data);
+    }
+    cleanup_key_data(context, key_data_count, key_data);
+    return retval;
 }
 
 /*
@@ -302,7 +387,6 @@ krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
     krb5_key_data       * key_data;
     krb5_error_code       retval;
     int                   kvno;
-    int                   i;
 
     /* First save the old keydata */
     kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
@@ -320,23 +404,14 @@ krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
         db_entry->n_key_data = key_data_count;
         db_entry->key_data = key_data;
-    } else {
-        /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
-        for (i = 0; i < key_data_count; i++) {
-            if (key_data[i].key_data_kvno == (kvno - 1)) {
-                if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
-                    cleanup_key_data(context, db_entry->n_key_data,
-                                     db_entry->key_data);
-                    break;
-                }
-                /* We should decrypt/re-encrypt the data to use the same mkvno*/
-                db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
-                memset(&key_data[i], 0, sizeof(krb5_key_data));
-            }
-        }
-        cleanup_key_data(context, key_data_count, key_data);
+        return retval;
     }
-    return(retval);
+
+    /* Preserve only the most recent kvno. */
+    retval = preserve_old_keys(context, master_key, db_entry, kvno - 1,
+                               key_data_count, key_data);
+    cleanup_key_data(context, key_data_count, key_data);
+    return retval;
 }
 
 /* Construct a random explicit salt. */
@@ -554,11 +629,9 @@ krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
     krb5_db_entry       * db_entry;
 {
     int                   key_data_count;
-    int                   n_new_key_data;
     krb5_key_data       * key_data;
     krb5_error_code       retval;
     int                   old_kvno;
-    int                   i;
 
     /* First save the old keydata */
     old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
@@ -579,23 +652,15 @@ krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
         db_entry->n_key_data = key_data_count;
         db_entry->key_data = key_data;
-    } else if (keepold) {
-        n_new_key_data = db_entry->n_key_data;
-        for (i = 0; i < key_data_count; i++) {
-            retval = krb5_dbe_create_key_data(context, db_entry);
-            if (retval) {
-                cleanup_key_data(context, db_entry->n_key_data,
-                                 db_entry->key_data);
-                break;
-            }
-            db_entry->key_data[i+n_new_key_data] = key_data[i];
-            memset(&key_data[i], 0, sizeof(krb5_key_data));
-        }
-        krb5_db_free( context, key_data );
-    } else {
-        cleanup_key_data(context, key_data_count, key_data);
+        return retval;
     }
-    return(retval);
+
+    if (keepold) {
+        retval = preserve_old_keys(context, master_key, db_entry, 0,
+                                   key_data_count, key_data);
+    }
+    cleanup_key_data(context, key_data_count, key_data);
+    return retval;
 }
 
 /*
@@ -617,7 +682,6 @@ krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
     krb5_key_data       * key_data;
     krb5_error_code       retval;
     int                   old_kvno, new_kvno;
-    int                   i;
 
     /* First save the old keydata */
     old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
@@ -635,21 +699,12 @@ krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
         db_entry->n_key_data = key_data_count;
         db_entry->key_data = key_data;
-    } else {
-        /* Copy keys with key_data_kvno == old_kvno */
-        for (i = 0; i < key_data_count; i++) {
-            if (key_data[i].key_data_kvno == old_kvno) {
-                if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
-                    cleanup_key_data(context, db_entry->n_key_data,
-                                     db_entry->key_data);
-                    break;
-                }
-                /* We should decrypt/re-encrypt the data to use the same mkvno*/
-                db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
-                memset(&key_data[i], 0, sizeof(krb5_key_data));
-            }
-        }
-        cleanup_key_data(context, key_data_count, key_data);
+        return retval;
     }
-    return(retval);
+
+    /* Preserve only the most recent kvno. */
+    retval = preserve_old_keys(context, master_key, db_entry, old_kvno,
+                               key_data_count, key_data);
+    cleanup_key_data(context, key_data_count, key_data);
+    return retval;
 }


More information about the cvs-krb5 mailing list