krb5 commit [krb5-1.8]: Try all history keys to decrypt password history

Tom Yu tlyu at MIT.EDU
Thu Jun 21 12:58:17 EDT 2012


https://github.com/krb5/krb5/commit/992f1fa3e4af37bb26c94e946cd6eb9c9966e59b
commit 992f1fa3e4af37bb26c94e946cd6eb9c9966e59b
Author: Tom Yu <tlyu at mit.edu>
Date:   Thu Jun 21 12:51:11 2012 -0400

    Try all history keys to decrypt password history
    
    A database created prior to 1.3 will have multiple password history
    keys, and kadmin prior to 1.8 won't necessarily choose the first one.
    So if there are multiple keys, we have to try them all.  If none of
    the keys can decrypt a password history entry, don't fail the password
    change operation; it's not worth it without positive evidence of
    password reuse.
    
    (back ported from commit 2782e80a12bccd920fa71e23166ac97c4470a637)
    
    ticket: 7180 (new)
    version_fixed: 1.8.7
    status: resolved

 src/lib/kadm5/server_internal.h   |    6 +++-
 src/lib/kadm5/srv/server_kdb.c    |   54 ++++++++++++++++++++++++-------------
 src/lib/kadm5/srv/svr_principal.c |   47 +++++++++++++++----------------
 3 files changed, 62 insertions(+), 45 deletions(-)

diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h
index 3276b27..a55f802 100644
--- a/src/lib/kadm5/server_internal.h
+++ b/src/lib/kadm5/server_internal.h
@@ -74,8 +74,10 @@ krb5_error_code     kdb_init_master(kadm5_server_handle_t handle,
 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);
diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c
index 337c142..4106092 100644
--- a/src/lib/kadm5/srv/server_kdb.c
+++ b/src/lib/kadm5/srv/server_kdb.c
@@ -177,26 +177,20 @@ done:
 }
 
 /*
- * 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,40 @@ kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock *hist_keyblock,
     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;
 }
 
+/* Free all keyblocks in a list (terminated by a keyblock with enctype 0). */
+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
  *
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index c9a6881..f173919 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -968,12 +968,13 @@ done:
 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,23 +986,22 @@ check_pw_reuse(krb5_context context,
             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)) {
+                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);
-                    krb5_free_keyblock_contents(context, &newkey);
-
-                    return(KADM5_PASS_REUSE);
                 }
-                krb5_free_keyblock_contents(context, &histkey);
             }
         }
         krb5_free_keyblock_contents(context, &newkey);
@@ -1349,7 +1349,7 @@ kadm5_chpass_principal_3(void *server_handle,
     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 @@ kadm5_chpass_principal_3(void *server_handle,
 
     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 @@ kadm5_chpass_principal_3(void *server_handle,
         }
 #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 @@ kadm5_chpass_principal_3(void *server_handle,
             /* 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 @@ done:
     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)


More information about the cvs-krb5 mailing list