[krbdev.mit.edu #7099] Decrypting history key entries can fail after 1.8 upgrade

Greg Hudson via RT rt-comment at krbdev.mit.edu
Tue Apr 24 12:31:00 EDT 2012


Index: src/lib/kadm5/server_internal.h
===================================================================
--- src/lib/kadm5/server_internal.h	(revision 25068)
+++ src/lib/kadm5/server_internal.h	(working copy)
@@ -74,8 +74,10 @@
 krb5_error_code     kdb_init_hist(kadm5_server_handle_t handle,
                                   char *r);
 krb5_error_code     kdb_get_hist_key(kadm5_server_handle_t handle,
-                                     krb5_keyblock *hist_keyblock,
-                                     krb5_kvno *hist_kvno);
+                                     krb5_keyblock **keyblocks_out,
+                                     krb5_kvno *kvno_out);
+void                kdb_free_keyblocks(kadm5_server_handle_t handle,
+                                       krb5_keyblock *keyblocks);
 krb5_error_code     kdb_get_entry(kadm5_server_handle_t handle,
                                   krb5_principal principal, krb5_db_entry *kdb,
                                   osa_princ_ent_rec *adb);
Index: src/lib/kadm5/srv/svr_principal.c
===================================================================
--- src/lib/kadm5/srv/svr_principal.c	(revision 25068)
+++ src/lib/kadm5/srv/svr_principal.c	(working copy)
@@ -968,12 +968,13 @@
 static kadm5_ret_t
 check_pw_reuse(krb5_context context,
                krb5_keyblock *mkey,
-               krb5_keyblock *hist_keyblock,
+               krb5_keyblock *hist_keyblocks,
                int n_new_key_data, krb5_key_data *new_key_data,
                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
 {
     int x, y, z;
-    krb5_keyblock newkey, histkey;
+    krb5_keyblock newkey, histkey, *kb;
+    krb5_key_data *key_data;
     krb5_error_code ret;
 
     for (x = 0; x < n_new_key_data; x++) {
@@ -985,21 +986,20 @@
             return(ret);
         for (y = 0; y < n_pw_hist_data; y++) {
             for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
-                ret = krb5_dbekd_decrypt_key_data(context,
-                                                  hist_keyblock,
-                                                  &pw_hist_data[y].key_data[z],
-                                                  &histkey, NULL);
-                if (ret)
-                    return(ret);
-
-                if ((newkey.length == histkey.length) &&
-                    (newkey.enctype == histkey.enctype) &&
-                    (memcmp(newkey.contents, histkey.contents,
-                            histkey.length) == 0)) {
-                    krb5_free_keyblock_contents(context, &histkey);
-                    krb5_free_keyblock_contents(context, &newkey);
-
-                    return(KADM5_PASS_REUSE);
+                for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
+                    key_data = &pw_hist_data[y].key_data[z];
+                    ret = krb5_dbekd_decrypt_key_data(context, kb, key_data,
+                                                      &histkey, NULL);
+                    if (ret)
+                        continue;
+                    if (newkey.length == histkey.length &&
+                        newkey.enctype == histkey.enctype &&
+                        memcmp(newkey.contents, histkey.contents,
+                               histkey.length) == 0) {
+                        krb5_free_keyblock_contents(context, &histkey);
+                        krb5_free_keyblock_contents(context, &newkey);
+                        return KADM5_PASS_REUSE;
+                    }
                 }
                 krb5_free_keyblock_contents(context, &histkey);
             }
@@ -1349,7 +1349,7 @@
     int                         have_pol = 0;
     kadm5_server_handle_t       handle = server_handle;
     osa_pw_hist_ent             hist;
-    krb5_keyblock               *act_mkey, hist_keyblock;
+    krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
     krb5_kvno                   act_kvno, hist_kvno;
 
     CHECK_HANDLE(server_handle);
@@ -1358,7 +1358,6 @@
 
     hist_added = 0;
     memset(&hist, 0, sizeof(hist));
-    memset(&hist_keyblock, 0, sizeof(hist_keyblock));
 
     if (principal == NULL || password == NULL)
         return EINVAL;
@@ -1430,18 +1429,18 @@
         }
 #endif
 
-        ret = kdb_get_hist_key(handle, &hist_keyblock, &hist_kvno);
+        ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
         if (ret)
             goto done;
 
         ret = create_history_entry(handle->context,
-                                   act_mkey, &hist_keyblock,
+                                   act_mkey, &hist_keyblocks[0],
                                    kdb_save.n_key_data,
                                    kdb_save.key_data, &hist);
         if (ret)
             goto done;
 
-        ret = check_pw_reuse(handle->context, act_mkey, &hist_keyblock,
+        ret = check_pw_reuse(handle->context, act_mkey, hist_keyblocks,
                              kdb.n_key_data, kdb.key_data,
                              1, &hist);
         if (ret)
@@ -1451,7 +1450,7 @@
             /* If hist_kvno has changed since the last password change, we
              * can't check the history. */
             if (adb.admin_history_kvno == hist_kvno) {
-                ret = check_pw_reuse(handle->context, act_mkey, &hist_keyblock,
+                ret = check_pw_reuse(handle->context, act_mkey, hist_keyblocks,
                                      kdb.n_key_data, kdb.key_data,
                                      adb.old_key_len, adb.old_keys);
                 if (ret)
@@ -1524,7 +1523,7 @@
     kdb_free_entry(handle, &kdb, &adb);
     kdb_free_entry(handle, &kdb_save, NULL);
     krb5_db_free_principal(handle->context, &kdb, 1);
-    krb5_free_keyblock_contents(handle->context, &hist_keyblock);
+    kdb_free_keyblocks(handle, hist_keyblocks);
 
     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
         && !ret)
Index: src/lib/kadm5/srv/server_kdb.c
===================================================================
--- src/lib/kadm5/srv/server_kdb.c	(revision 25068)
+++ src/lib/kadm5/srv/server_kdb.c	(working copy)
@@ -177,26 +177,20 @@
 }
 
 /*
- * Function: kdb_get_hist_key
- *
- * Purpose: Fetches the current history key
- *
- * Arguments:
- *
- *      handle          (r) kadm5 api server handle
- *      hist_keyblock   (w) keyblock to fill in with history key
- *      hist_kvno       (w) kvno to fill in with history kvno
- *
- * Effects: This function looks up the history principal and retrieves the
- * current history key and version.
+ * Fetch the current history key(s), creating the history principal if
+ * necessary.  Database created since krb5 1.3 will have only one key, but
+ * databases created before that may have multiple keys (of the same kvno)
+ * and we need to try them all.  History keys will be returned in a list
+ * terminated by an entry with enctype 0.
  */
 krb5_error_code
-kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock *hist_keyblock,
-                 krb5_kvno *hist_kvno)
+kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock **keyblocks_out,
+                 krb5_kvno *kvno_out)
 {
     krb5_error_code ret;
     krb5_db_entry kdb;
-    krb5_keyblock *mkey;
+    krb5_keyblock *mkey, *kblist = NULL;
+    krb5_int16 i;
 
     ret = kdb_get_entry(handle, hist_princ, &kdb, NULL);
     if (ret)
@@ -214,18 +208,39 @@
     if (ret)
         goto done;
 
-    ret = krb5_dbekd_decrypt_key_data(handle->context, mkey,
-                                      &kdb.key_data[0], hist_keyblock, NULL);
-    if (ret)
+    kblist = k5alloc((kdb.n_key_data + 1) * sizeof(*kblist), &ret);
+    if (kblist == NULL)
         goto done;
+    for (i = 0; i < kdb.n_key_data; i++) {
+        ret = krb5_dbekd_decrypt_key_data(handle->context, mkey,
+                                          &kdb.key_data[i], &kblist[i],
+                                          NULL);
+        if (ret)
+            goto done;
+    }
 
-    *hist_kvno = kdb.key_data[0].key_data_kvno;
+    *keyblocks_out = kblist;
+    kblist = NULL;
+    *kvno_out = kdb.key_data[0].key_data_kvno;
 
 done:
     kdb_free_entry(handle, &kdb, NULL);
+    kdb_free_keyblocks(handle, kblist);
     return ret;
 }
 
+void
+kdb_free_keyblocks(kadm5_server_handle_t handle, krb5_keyblock *keyblocks)
+{
+    krb5_keyblock *kb;
+
+    if (keyblocks == NULL)
+        return;
+    for (kb = keyblocks; kb->enctype != 0; kb++)
+        krb5_free_keyblock_contents(handle->context, kb);
+    free(keyblocks);
+}
+
 /*
  * Function: kdb_get_entry
  *



More information about the krb5-bugs mailing list