krb5 commit: Only store latest keys in key history entry

Greg Hudson ghudson at mit.edu
Wed Feb 3 13:03:11 EST 2016


https://github.com/krb5/krb5/commit/d7f91ac2f6655e77bb3658c2c8cc6132f958a340
commit d7f91ac2f6655e77bb3658c2c8cc6132f958a340
Author: Sarah Day <sarahday at mit.edu>
Date:   Thu Jan 21 11:17:12 2016 -0500

    Only store latest keys in key history entry
    
    If a password is changed with the -keepold option, then changed again,
    the history entry contains both the latest password and the one that
    was kept.  Fix create_history_entry to only store the latest kvno in
    the history entry.  Also add a test to ensure that the bug is fixed.
    
    ticket: 8354

 src/lib/kadm5/srv/svr_principal.c |   86 +++++++++++++++++++++++++------------
 src/tests/t_kdb.py                |    7 +++
 2 files changed, 65 insertions(+), 28 deletions(-)

diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index 1d4365c..9b1efbb 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -1084,6 +1084,16 @@ check_pw_reuse(krb5_context context,
     return(0);
 }
 
+static void
+free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
+{
+    int i;
+
+    for (i = 0; i < hist->n_key_data; i++)
+        krb5_free_key_data_contents(context, &hist->key_data[i]);
+    free(hist->key_data);
+}
+
 /*
  * Function: create_history_entry
  *
@@ -1097,7 +1107,7 @@ check_pw_reuse(krb5_context context,
  *      hist_key        (r) history keyblock to encrypt key data with
  *      n_key_data      (r) number of elements in key_data
  *      key_data        (r) keys to add to the history entry
- *      hist            (w) history entry to fill in
+ *      hist_out        (w) history entry to fill in
  *
  * Effects:
  *
@@ -1109,45 +1119,62 @@ check_pw_reuse(krb5_context context,
 static
 int create_history_entry(krb5_context context,
                          krb5_keyblock *hist_key, int n_key_data,
-                         krb5_key_data *key_data, osa_pw_hist_ent *hist)
+                         krb5_key_data *key_data, osa_pw_hist_ent *hist_out)
 {
-    krb5_error_code ret;
+    int i;
+    krb5_error_code ret = 0;
     krb5_keyblock key;
     krb5_keysalt salt;
-    int i;
+    krb5_ui_2 kvno;
+    osa_pw_hist_ent hist;
 
-    hist->key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
-    if (hist->key_data == NULL)
-        return ret;
+    hist_out->key_data = NULL;
+    hist_out->n_key_data = 0;
+
+    if (n_key_data < 0)
+        return EINVAL;
+
+    memset(&key, 0, sizeof(key));
+    memset(&hist, 0, sizeof(hist));
+
+    if (n_key_data == 0)
+        goto cleanup;
+
+    hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
+    if (hist.key_data == NULL)
+        goto cleanup;
+
+    /* We only want to store the most recent kvno, and key_data should already
+     * be sorted in descending order by kvno. */
+    kvno = key_data[0].key_data_kvno;
 
     for (i = 0; i < n_key_data; i++) {
-        ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
+        if (key_data[i].key_data_kvno < kvno)
+            break;
+        ret = krb5_dbe_decrypt_key_data(context, NULL,
+                                        &key_data[i], &key,
                                         &salt);
         if (ret)
-            return ret;
+            goto cleanup;
 
         ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
                                         key_data[i].key_data_kvno,
-                                        &hist->key_data[i]);
+                                        &hist.key_data[hist.n_key_data]);
         if (ret)
-            return ret;
-
+            goto cleanup;
+        hist.n_key_data++;
         krb5_free_keyblock_contents(context, &key);
         /* krb5_free_keysalt(context, &salt); */
     }
 
-    hist->n_key_data = n_key_data;
-    return 0;
-}
-
-static
-void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
-{
-    int i;
+    *hist_out = hist;
+    hist.n_key_data = 0;
+    hist.key_data = NULL;
 
-    for (i = 0; i < hist->n_key_data; i++)
-        krb5_free_key_data_contents(context, &hist->key_data[i]);
-    free(hist->key_data);
+cleanup:
+    krb5_free_keyblock_contents(context, &key);
+    free_history_entry(context, &hist);
+    return ret;
 }
 
 /*
@@ -1526,11 +1553,14 @@ kadm5_chpass_principal_3(void *server_handle,
                     goto done;
             }
 
-            ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
-                                 &hist);
-            if (ret)
-                goto done;
-            hist_added = 1;
+            /* Don't save empty history. */
+            if (hist.n_key_data > 0) {
+                ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
+                                     &hist);
+                if (ret)
+                    goto done;
+                hist_added = 1;
+            }
         }
 
         if (pol.pw_max_life)
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index 2d2d675..27d0e3f 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -367,6 +367,13 @@ out = realm.run([kadminl, 'getprinc', 'keylessprinc'])
 if 'Number of keys: 0' not in out:
     fail('After purgekeys -all, keys remain')
 
+# Test for 8354 (old password history entries when -keepold is used)
+realm.run([kadminl, 'addpol', '-history', '2', 'keepoldpasspol'])
+realm.run([kadminl, 'addprinc', '-policy', 'keepoldpasspol', '-pw', 'aaaa',
+           'keepoldpassprinc'])
+for p in ('bbbb', 'cccc', 'aaaa'):
+    realm.run([kadminl, 'cpw', '-keepold', '-pw', p, 'keepoldpassprinc'])
+
 realm.stop()
 
 # Briefly test dump and load.


More information about the cvs-krb5 mailing list