diff options
| author | Greg Hudson <ghudson@mit.edu> | 2012-04-24 01:05:41 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2012-04-24 01:05:41 +0000 |
| commit | 2782e80a12bccd920fa71e23166ac97c4470a637 (patch) | |
| tree | 2c2e4c0f03fdbb9144043494b65b4f404d99fdfd /src/lib/kadm5 | |
| parent | 8230c4b7b7323cdef2a6c877deb710a15380f40f (diff) | |
| download | krb5-2782e80a12bccd920fa71e23166ac97c4470a637.tar.gz krb5-2782e80a12bccd920fa71e23166ac97c4470a637.tar.xz krb5-2782e80a12bccd920fa71e23166ac97c4470a637.zip | |
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.
ticket: 7099
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25819 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/kadm5')
| -rw-r--r-- | src/lib/kadm5/server_internal.h | 6 | ||||
| -rw-r--r-- | src/lib/kadm5/srv/server_kdb.c | 55 | ||||
| -rw-r--r-- | src/lib/kadm5/srv/svr_principal.c | 46 |
3 files changed, 62 insertions, 45 deletions
diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h index 877852254..220e2b694 100644 --- a/src/lib/kadm5/server_internal.h +++ b/src/lib/kadm5/server_internal.h @@ -81,8 +81,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 3860b6b2f..f4217dd49 100644 --- a/src/lib/kadm5/srv/server_kdb.c +++ b/src/lib/kadm5/srv/server_kdb.c @@ -151,27 +151,20 @@ create_hist(kadm5_server_handle_t handle) } /* - * Function: kdb_get_hist_key - * - * Purpose: Fetches the current history key, creating it if necessary - * - * 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. If the history principal does not exist, - * it will be created. + * 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; /* Fetch the history principal, creating it if necessary. */ ret = kdb_get_entry(handle, hist_princ, &kdb, NULL); @@ -195,18 +188,40 @@ kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock *hist_keyblock, if (ret) goto done; - ret = krb5_dbe_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_dbe_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 f77490fe1..00541dff1 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -962,12 +962,13 @@ done: */ static kadm5_ret_t check_pw_reuse(krb5_context context, - 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) { unsigned int x, y, z; - krb5_keyblock newkey, histkey; + krb5_keyblock newkey, histkey, *kb; + krb5_key_data *key_data; krb5_error_code ret; assert (n_new_key_data >= 0); @@ -981,22 +982,22 @@ check_pw_reuse(krb5_context context, return(ret); for (y = 0; y < n_pw_hist_data; y++) { for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) { - ret = krb5_dbe_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_dbe_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); @@ -1341,7 +1342,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); @@ -1350,7 +1351,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; @@ -1373,10 +1373,10 @@ kadm5_chpass_principal_3(void *server_handle, have_pol = 1; /* Create a password history entry before we change kdb's key_data. */ - 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, &hist_keyblock, + ret = create_history_entry(handle->context, &hist_keyblocks[0], kdb->n_key_data, kdb->key_data, &hist); if (ret) goto done; @@ -1428,7 +1428,7 @@ kadm5_chpass_principal_3(void *server_handle, } #endif - ret = check_pw_reuse(handle->context, &hist_keyblock, + ret = check_pw_reuse(handle->context, hist_keyblocks, kdb->n_key_data, kdb->key_data, 1, &hist); if (ret) @@ -1438,7 +1438,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, &hist_keyblock, + ret = check_pw_reuse(handle->context, hist_keyblocks, kdb->n_key_data, kdb->key_data, adb.old_key_len, adb.old_keys); if (ret) @@ -1518,7 +1518,7 @@ done: if (!hist_added && hist.key_data) free_history_entry(handle->context, &hist); kdb_free_entry(handle, kdb, &adb); - 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) |
