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 *