Allow clearpolicy restriction for kadmin addprinc
[krb5.git] / src / lib / kadm5 / srv / svr_principal.c
index c44463bf727cd8cda501f318d60b7ee9ac947b1c..a0b110def012d343d536ad59ded21219348e7c08 100644 (file)
@@ -1,48 +1,36 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
  *
  * $Header$
  */
-
-#if !defined(lint) && !defined(__CODECENTER__)
-static char *rcsid = "$Header$";
-#endif
-
-#include       <sys/types.h>
-#include       <sys/time.h>
-#include       <errno.h>
-#include       <kadm5/admin.h>
-#include       <kdb.h>
-#include       <stdio.h>
-#include       <string.h>
-#include       "server_internal.h"
-#include       <stdarg.h>
-#include       <stdlib.h>
+#include "k5-int.h"
+#include        <sys/time.h>
+#include        <kadm5/admin.h>
+#include        <kdb.h>
+#include        "server_internal.h"
 #ifdef USE_PASSWORD_SERVER
-#include       <sys/wait.h>
-#include       <signal.h>
-
+#include        <sys/wait.h>
+#include        <signal.h>
 #endif
 
+#include <krb5/kadm5_hook_plugin.h>
+
 #ifdef USE_VALGRIND
 #include <valgrind/memcheck.h>
 #else
 #define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0)
 #endif
 
-extern krb5_principal      master_princ;
-extern krb5_principal      hist_princ;
-extern  krb5_keyblock      master_keyblock;
-extern  krb5_keylist_node  *master_keylist;
+extern  krb5_principal      master_princ;
+extern  krb5_principal      hist_princ;
+extern  krb5_keyblock       master_keyblock;
 extern  krb5_actkvno_node  *active_mkey_list;
-extern krb5_keyblock       hist_key;
-extern krb5_db_entry       master_db;
-extern krb5_db_entry       hist_db;
-extern  krb5_kvno          hist_kvno;
+extern  krb5_db_entry       master_db;
 
-static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
-                           int n_key_data, krb5_key_data *key_data,
-                           krb5_keyblock **keyblocks, int *n_keys);
+static int decrypt_key_data(krb5_context context,
+                            int n_key_data, krb5_key_data *key_data,
+                            krb5_keyblock **keyblocks, int *n_keys);
 
 static krb5_error_code
 kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
@@ -56,12 +44,12 @@ kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_pr
         return ENOMEM;
 
     VALGRIND_CHECK_DEFINED(*inprinc);
-    memcpy(tempprinc, inprinc, sizeof(krb5_principal_data));
+    *tempprinc = *inprinc;
 
     nelems = (int) krb5_princ_size(context, inprinc);
     tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
     if (tempprinc->data == 0) {
-       krb5_db_free(context, (char *)tempprinc);
+        krb5_db_free(context, (char *)tempprinc);
         return ENOMEM;
     }
 
@@ -79,17 +67,17 @@ kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_pr
         if (len)
             memcpy(krb5_princ_component(context, tempprinc, i)->data,
                    krb5_princ_component(context, inprinc, i)->data, len);
-       krb5_princ_component(context, tempprinc, i)->magic = KV5M_DATA;
+        krb5_princ_component(context, tempprinc, i)->magic = KV5M_DATA;
     }
 
     tempprinc->realm.data =
-       krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
+        krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
     if (!tempprinc->realm.data && tempprinc->realm.length) {
-            for (i = 0; i < nelems; i++)
-               krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
-            krb5_db_free(context, tempprinc->data);
-            krb5_db_free(context, tempprinc);
-            return ENOMEM;
+        for (i = 0; i < nelems; i++)
+            krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
+        krb5_db_free(context, tempprinc->data);
+        krb5_db_free(context, tempprinc);
+        return ENOMEM;
     }
     if (tempprinc->realm.length)
         memcpy(tempprinc->realm.data, inprinc->realm.data,
@@ -122,90 +110,117 @@ kadm5_free_principal(krb5_context context, krb5_principal val)
  * XXX Functions that ought to be in libkrb5.a, but aren't.
  */
 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
-   krb5_context context;
-   krb5_key_data *from, *to;
+    krb5_context context;
+    krb5_key_data *from, *to;
 {
-     int i, idx;
-
-     *to = *from;
-
-     idx = (from->key_data_ver == 1 ? 1 : 2);
-
-     for (i = 0; i < idx; i++) {
-       if ( from->key_data_length[i] ) {
-        to->key_data_contents[i] = malloc(from->key_data_length[i]);
-        if (to->key_data_contents[i] == NULL) {
-          for (i = 0; i < idx; i++) {
-            if (to->key_data_contents[i]) {
-              memset(to->key_data_contents[i], 0,
-                     to->key_data_length[i]);
-              free(to->key_data_contents[i]);
-            }
-          }
-          return ENOMEM;
-        }
-        memcpy(to->key_data_contents[i], from->key_data_contents[i],
-               from->key_data_length[i]);
-       }
-     }
-     return 0;
+    int i, idx;
+
+    *to = *from;
+
+    idx = (from->key_data_ver == 1 ? 1 : 2);
+
+    for (i = 0; i < idx; i++) {
+        if ( from->key_data_length[i] ) {
+            to->key_data_contents[i] = malloc(from->key_data_length[i]);
+            if (to->key_data_contents[i] == NULL) {
+                for (i = 0; i < idx; i++) {
+                    if (to->key_data_contents[i]) {
+                        memset(to->key_data_contents[i], 0,
+                               to->key_data_length[i]);
+                        free(to->key_data_contents[i]);
+                    }
+                }
+                return ENOMEM;
+            }
+            memcpy(to->key_data_contents[i], from->key_data_contents[i],
+                   from->key_data_length[i]);
+        }
+    }
+    return 0;
 }
 
 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
 {
-     krb5_tl_data *n;
-
-     n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
-     if (n == NULL)
-         return NULL;
-     n->tl_data_contents = malloc(tl->tl_data_length);
-     if (n->tl_data_contents == NULL) {
-         free(n);
-         return NULL;
-     }
-     memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
-     n->tl_data_type = tl->tl_data_type;
-     n->tl_data_length = tl->tl_data_length;
-     n->tl_data_next = NULL;
-     return n;
+    krb5_tl_data *n;
+
+    n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
+    if (n == NULL)
+        return NULL;
+    n->tl_data_contents = malloc(tl->tl_data_length);
+    if (n->tl_data_contents == NULL) {
+        free(n);
+        return NULL;
+    }
+    memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
+    n->tl_data_type = tl->tl_data_type;
+    n->tl_data_length = tl->tl_data_length;
+    n->tl_data_next = NULL;
+    return n;
 }
 
 /* This is in lib/kdb/kdb_cpw.c, but is static */
 static void cleanup_key_data(context, count, data)
-   krb5_context          context;
-   int                   count;
-   krb5_key_data       * data;
+    krb5_context   context;
+    int                    count;
+    krb5_key_data        * data;
 {
-     int i, j;
+    int i, j;
 
-     for (i = 0; i < count; i++)
-         for (j = 0; j < data[i].key_data_ver; j++)
-              if (data[i].key_data_length[j])
-                  krb5_db_free(context, data[i].key_data_contents[j]);
-     krb5_db_free(context, data);
+    for (i = 0; i < count; i++)
+        for (j = 0; j < data[i].key_data_ver; j++)
+            if (data[i].key_data_length[j])
+                krb5_db_free(context, data[i].key_data_contents[j]);
+    krb5_db_free(context, data);
+}
+
+/*
+ * Set *passptr to NULL if the request looks like the first part of a krb5 1.6
+ * addprinc -randkey operation.  The krb5 1.6 dummy password for these requests
+ * was invalid UTF-8, which runs afoul of the arcfour string-to-key.
+ */
+static void
+check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
+                int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
+{
+    int i;
+    char *password = *passptr;
+
+    /* Old-style randkey operations disallowed tickets to start. */
+    if (!(mask & KADM5_ATTRIBUTES) ||
+        !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
+        return;
+
+    /* The 1.6 dummy password was the octets 1..255. */
+    for (i = 0; (unsigned char) password[i] == i + 1; i++);
+    if (password[i] != '\0' || i != 255)
+        return;
+
+    /* This will make the caller use a random password instead. */
+    *passptr = NULL;
 }
 
 kadm5_ret_t
 kadm5_create_principal(void *server_handle,
-                           kadm5_principal_ent_t entry, long mask,
-                           char *password)
+                       kadm5_principal_ent_t entry, long mask,
+                       char *password)
 {
     return
-       kadm5_create_principal_3(server_handle, entry, mask,
-                                0, NULL, password);
+        kadm5_create_principal_3(server_handle, entry, mask,
+                                 0, NULL, password);
 }
 kadm5_ret_t
 kadm5_create_principal_3(void *server_handle,
-                        kadm5_principal_ent_t entry, long mask,
-                        int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
-                        char *password)
+                         kadm5_principal_ent_t entry, long mask,
+                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                         char *password)
 {
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
-    kadm5_policy_ent_rec       polent;
-    krb5_int32                 now;
-    krb5_tl_data               *tl_data_orig, *tl_data_tail;
-    unsigned int               ret;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
+    kadm5_policy_ent_rec        polent;
+    krb5_boolean                have_polent = FALSE;
+    krb5_int32                  now;
+    krb5_tl_data                *tl_data_orig, *tl_data_tail;
+    unsigned int                ret;
     kadm5_server_handle_t handle = server_handle;
     krb5_keyblock               *act_mkey;
     krb5_kvno                   act_kvno;
@@ -214,20 +229,29 @@ kadm5_create_principal_3(void *server_handle,
 
     krb5_clear_error_message(handle->context);
 
+    check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password);
+
     /*
      * Argument sanity checking, and opening up the DB
      */
     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
-       (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
-       (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
-       (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
-       (mask & KADM5_FAIL_AUTH_COUNT))
-       return KADM5_BAD_MASK;
+       (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
+       (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
+       (mask & KADM5_LAST_FAILED) || (mask & KADM5_FAIL_AUTH_COUNT))
+        return KADM5_BAD_MASK;
+    if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
+        return KADM5_BAD_MASK;
     if((mask & ~ALL_PRINC_MASK))
-       return KADM5_BAD_MASK;
-    if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
-       return EINVAL;
+        return KADM5_BAD_MASK;
+    if (entry == NULL)
+        return EINVAL;
+
+    /* Use default keysalts if caller did not provide any. */
+    if (n_ks_tuple == 0) {
+        ks_tuple = handle->params.keysalts;
+        n_ks_tuple = handle->params.num_keysalts;
+    }
 
     /*
      * Check to see if the principal exists
@@ -236,15 +260,18 @@ kadm5_create_principal_3(void *server_handle,
 
     switch(ret) {
     case KADM5_UNK_PRINC:
-       break;
+        break;
     case 0:
-       kdb_free_entry(handle, &kdb, &adb);
-       return KADM5_DUP;
+        kdb_free_entry(handle, kdb, &adb);
+        return KADM5_DUP;
     default:
-       return ret;
+        return ret;
     }
 
-    memset(&kdb, 0, sizeof(krb5_db_entry));
+    kdb = krb5_db_alloc(handle->context, NULL, sizeof(*kdb));
+    if (kdb == NULL)
+        return ENOMEM;
+    memset(kdb, 0, sizeof(*kdb));
     memset(&adb, 0, sizeof(osa_princ_ent_rec));
 
     /*
@@ -252,135 +279,118 @@ kadm5_create_principal_3(void *server_handle,
      * If we can not find the one specified return an error
      */
     if ((mask & KADM5_POLICY)) {
-        if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
-                                    &polent)) != KADM5_OK) {
-           if(ret == EINVAL)
-               return KADM5_BAD_POLICY;
-           else
-               return ret;
-       }
-    }
-    if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
-                           &polent, entry->principal))) {
-       if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-       return ret;
+        if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
+                                    &polent)) != KADM5_OK) {
+            if (ret == EINVAL)
+                ret = KADM5_BAD_POLICY;
+            if (ret)
+                goto cleanup;
+        }
+        have_polent = TRUE;
+    }
+    if (password) {
+        ret = passwd_check(handle, password, have_polent ? &polent : NULL,
+                           entry->principal);
+        if (ret)
+            goto cleanup;
     }
     /*
      * Start populating the various DB fields, using the
      * "defaults" for fields that were not specified by the
      * mask.
      */
-    if ((ret = krb5_timeofday(handle->context, &now))) {
-        if (mask & KADM5_POLICY)
-             (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-        return ret;
-    }
+    if ((ret = krb5_timeofday(handle->context, &now)))
+        goto cleanup;
 
-    kdb.magic = KRB5_KDB_MAGIC_NUMBER;
-    kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
+    kdb->magic = KRB5_KDB_MAGIC_NUMBER;
+    kdb->len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
 
     if ((mask & KADM5_ATTRIBUTES))
-       kdb.attributes = entry->attributes;
+        kdb->attributes = entry->attributes;
     else
-       kdb.attributes = handle->params.flags;
+        kdb->attributes = handle->params.flags;
 
     if ((mask & KADM5_MAX_LIFE))
-       kdb.max_life = entry->max_life;
+        kdb->max_life = entry->max_life;
     else
-       kdb.max_life = handle->params.max_life;
+        kdb->max_life = handle->params.max_life;
 
     if (mask & KADM5_MAX_RLIFE)
-        kdb.max_renewable_life = entry->max_renewable_life;
+        kdb->max_renewable_life = entry->max_renewable_life;
     else
-        kdb.max_renewable_life = handle->params.max_rlife;
+        kdb->max_renewable_life = handle->params.max_rlife;
 
     if ((mask & KADM5_PRINC_EXPIRE_TIME))
-       kdb.expiration = entry->princ_expire_time;
+        kdb->expiration = entry->princ_expire_time;
     else
-       kdb.expiration = handle->params.expiration;
-
-    kdb.pw_expiration = 0;
-    if ((mask & KADM5_POLICY)) {
-       if(polent.pw_max_life)
-           kdb.pw_expiration = now + polent.pw_max_life;
-       else
-           kdb.pw_expiration = 0;
+        kdb->expiration = handle->params.expiration;
+
+    kdb->pw_expiration = 0;
+    if (have_polent) {
+        if(polent.pw_max_life)
+            kdb->pw_expiration = now + polent.pw_max_life;
+        else
+            kdb->pw_expiration = 0;
     }
     if ((mask & KADM5_PW_EXPIRATION))
-        kdb.pw_expiration = entry->pw_expiration;
+        kdb->pw_expiration = entry->pw_expiration;
 
-    kdb.last_success = 0;
-    kdb.last_failed = 0;
-    kdb.fail_auth_count = 0;
+    kdb->last_success = 0;
+    kdb->last_failed = 0;
+    kdb->fail_auth_count = 0;
 
     /* this is kind of gross, but in order to free the tl data, I need
        to free the entire kdb entry, and that will try to free the
        principal. */
 
     if ((ret = kadm5_copy_principal(handle->context,
-                                   entry->principal, &(kdb.princ)))) {
-       if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-       return(ret);
-    }
+                                    entry->principal, &(kdb->princ))))
+        goto cleanup;
 
-    if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
-         krb5_db_free_principal(handle->context, &kdb, 1);
-        if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-        return(ret);
-    }
+    if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
+        goto cleanup;
 
     if (mask & KADM5_TL_DATA) {
-       /* splice entry->tl_data onto the front of kdb.tl_data */
-       tl_data_orig = kdb.tl_data;
-       for (tl_data_tail = entry->tl_data; tl_data_tail;
-            tl_data_tail = tl_data_tail->tl_data_next)
-       {
-           ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl_data_tail);
-           if( ret )
-           {
-               krb5_db_free_principal(handle->context, &kdb, 1);
-               if (mask & KADM5_POLICY)
-                   (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-               return ret;
-           }
-       }
+        /* splice entry->tl_data onto the front of kdb->tl_data */
+        tl_data_orig = kdb->tl_data;
+        for (tl_data_tail = entry->tl_data; tl_data_tail;
+             tl_data_tail = tl_data_tail->tl_data_next)
+        {
+            ret = krb5_dbe_update_tl_data(handle->context, kdb, tl_data_tail);
+            if( ret )
+                goto cleanup;
+        }
     }
 
     /* initialize the keys */
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                active_mkey_list, &act_kvno, &act_mkey);
-    if (ret) {
-        krb5_db_free_principal(handle->context, &kdb, 1);
-        if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-        return (ret);
-    }
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
+                                 &act_mkey);
+    if (ret)
+        goto cleanup;
 
-    if ((ret = krb5_dbe_cpw(handle->context, act_mkey,
-                           n_ks_tuple?ks_tuple:handle->params.keysalts,
-                           n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
-                           password,
-                           (mask & KADM5_KVNO)?entry->kvno:1,
-                           FALSE, &kdb))) {
-       krb5_db_free_principal(handle->context, &kdb, 1);
-       if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-       return(ret);
+    if (password) {
+        ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
+                           password, (mask & KADM5_KVNO)?entry->kvno:1,
+                           FALSE, kdb);
+    } else {
+        /* Null password means create with random key (new in 1.8). */
+        ret = krb5_dbe_crk(handle->context, &master_keyblock,
+                           ks_tuple, n_ks_tuple, FALSE, kdb);
     }
+    if (ret)
+        goto cleanup;
 
     /* Record the master key VNO used to encrypt this entry's keys */
-    ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno);
+    ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
     if (ret)
-    {
-       krb5_db_free_principal(handle->context, &kdb, 1);
-       if (mask & KADM5_POLICY)
-           (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-       return ret;
-    }
+        goto cleanup;
+
+    ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
+                               KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
+                               n_ks_tuple, ks_tuple, password);
+    if (ret)
+        goto cleanup;
 
     /* populate the admin-server-specific fields.  In the OV server,
        this used to be in a separate database.  Since there's already
@@ -388,70 +398,66 @@ kadm5_create_principal_3(void *server_handle,
        I'm going to keep it, and make all the admin stuff occupy a
        single tl_data record, */
 
-    adb.admin_history_kvno = hist_kvno;
-    if ((mask & KADM5_POLICY)) {
-       adb.aux_attributes = KADM5_POLICY;
+    adb.admin_history_kvno = INITIAL_HIST_KVNO;
+    if (have_polent) {
+        adb.aux_attributes = KADM5_POLICY;
 
-       /* this does *not* need to be strdup'ed, because adb is xdr */
-       /* encoded in osa_adb_create_princ, and not ever freed */
+        /* this does *not* need to be strdup'ed, because adb is xdr */
+        /* encoded in osa_adb_create_princ, and not ever freed */
 
-       adb.policy = entry->policy;
+        adb.policy = entry->policy;
     }
 
     /* increment the policy ref count, if any */
 
-    if ((mask & KADM5_POLICY)) {
-       polent.policy_refcnt++;
-       if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
-                                                   KADM5_REF_COUNT))
-           != KADM5_OK) {
-           krb5_db_free_principal(handle->context, &kdb, 1);
-           if (mask & KADM5_POLICY)
-                (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-           return(ret);
-       }
+    if (have_polent) {
+        polent.policy_refcnt++;
+        if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                KADM5_REF_COUNT))
+            != KADM5_OK)
+            goto cleanup;
     }
 
     /* In all cases key and the principal data is set, let the database provider know */
-    kdb.mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
+    kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
 
     /* store the new db entry */
-    ret = kdb_put_entry(handle, &kdb, &adb);
+    ret = kdb_put_entry(handle, kdb, &adb);
 
-    krb5_db_free_principal(handle->context, &kdb, 1);
 
     if (ret) {
-       if ((mask & KADM5_POLICY)) {
-           /* decrement the policy ref count */
-
-           polent.policy_refcnt--;
-           /*
-            * if this fails, there's nothing we can do anyway.  the
-            * policy refcount wil be too high.
-            */
-           (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
-                                                    KADM5_REF_COUNT);
-       }
-
-       if (mask & KADM5_POLICY)
-            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-       return(ret);
+        if (have_polent) {
+            /* decrement the policy ref count */
+
+            polent.policy_refcnt--;
+            /*
+             * if this fails, there's nothing we can do anyway.  the
+             * policy refcount wil be too high.
+             */
+            (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                KADM5_REF_COUNT);
+        }
     }
 
-    if (mask & KADM5_POLICY)
-        (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+    (void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
+                                KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
+                                n_ks_tuple, ks_tuple, password);
 
-    return KADM5_OK;
+cleanup:
+    krb5_db_free_principal(handle->context, kdb);
+    if (have_polent)
+        (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+    return ret;
 }
 
 
 kadm5_ret_t
 kadm5_delete_principal(void *server_handle, krb5_principal principal)
 {
-    unsigned int               ret;
-    kadm5_policy_ent_rec       polent;
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
+    unsigned int                ret;
+    kadm5_policy_ent_rec        polent;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
     kadm5_server_handle_t handle = server_handle;
 
     CHECK_HANDLE(server_handle);
@@ -459,47 +465,58 @@ kadm5_delete_principal(void *server_handle, krb5_principal principal)
     krb5_clear_error_message(handle->context);
 
     if (principal == NULL)
-       return EINVAL;
+        return EINVAL;
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+        return(ret);
+    ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
+                               KADM5_HOOK_STAGE_PRECOMMIT, principal);
+    if (ret) {
+        kdb_free_entry(handle, kdb, &adb);
+        return ret;
+    }
 
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       if ((ret = kadm5_get_policy(handle->lhandle,
-                                   adb.policy, &polent))
-           == KADM5_OK) {
-           polent.policy_refcnt--;
-           if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
-                                                        KADM5_REF_COUNT))
-               != KADM5_OK) {
-               (void) kadm5_free_policy_ent(handle->lhandle, &polent);
-               kdb_free_entry(handle, &kdb, &adb);
-               return(ret);
-           }
-       }
-       if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
-            kdb_free_entry(handle, &kdb, &adb);
-            return ret;
-       }
+        if ((ret = kadm5_get_policy(handle->lhandle,
+                                    adb.policy, &polent))
+            == KADM5_OK) {
+            polent.policy_refcnt--;
+            if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                    KADM5_REF_COUNT))
+                != KADM5_OK) {
+                (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+                kdb_free_entry(handle, kdb, &adb);
+                return(ret);
+            }
+        }
+        if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
+            kdb_free_entry(handle, kdb, &adb);
+            return ret;
+        }
     }
 
     ret = kdb_delete_entry(handle, principal);
 
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
+
+    if (ret == 0)
+        (void) k5_kadm5_hook_remove(handle->context,
+                                    handle->hook_handles,
+                                    KADM5_HOOK_STAGE_POSTCOMMIT, principal);
 
     return ret;
 }
 
 kadm5_ret_t
 kadm5_modify_principal(void *server_handle,
-                           kadm5_principal_ent_t entry, long mask)
+                       kadm5_principal_ent_t entry, long mask)
 {
-    int                            ret, ret2, i;
+    int                     ret, ret2, i;
     kadm5_policy_ent_rec    npol, opol;
-    int                            have_npol = 0, have_opol = 0;
-    krb5_db_entry          kdb;
-    krb5_tl_data           *tl_data_orig;
-    osa_princ_ent_rec      adb;
+    int                     have_npol = 0, have_opol = 0;
+    krb5_db_entry           *kdb;
+    krb5_tl_data            *tl_data_orig;
+    osa_princ_ent_rec       adb;
     kadm5_server_handle_t handle = server_handle;
 
     CHECK_HANDLE(server_handle);
@@ -511,241 +528,269 @@ kadm5_modify_principal(void *server_handle,
        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
        (mask & KADM5_LAST_FAILED))
-       return KADM5_BAD_MASK;
+        return KADM5_BAD_MASK;
     if((mask & ~ALL_PRINC_MASK))
-       return KADM5_BAD_MASK;
+        return KADM5_BAD_MASK;
     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
-       return KADM5_BAD_MASK;
+        return KADM5_BAD_MASK;
     if(entry == (kadm5_principal_ent_t) NULL)
-       return EINVAL;
+        return EINVAL;
     if (mask & KADM5_TL_DATA) {
-        tl_data_orig = entry->tl_data;
-        while (tl_data_orig) {
-             if (tl_data_orig->tl_data_type < 256)
-                  return KADM5_BAD_TL_TYPE;
-             tl_data_orig = tl_data_orig->tl_data_next;
-        }
+        tl_data_orig = entry->tl_data;
+        while (tl_data_orig) {
+            if (tl_data_orig->tl_data_type < 256)
+                return KADM5_BAD_TL_TYPE;
+            tl_data_orig = tl_data_orig->tl_data_next;
+        }
     }
 
     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
     if (ret)
-       return(ret);
+        return(ret);
 
     /*
      * This is pretty much the same as create ...
      */
 
     if ((mask & KADM5_POLICY)) {
-        /* get the new policy */
-        ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
-        if (ret) {
-             switch (ret) {
-             case EINVAL:
-                  ret = KADM5_BAD_POLICY;
-                  break;
-             case KADM5_UNK_POLICY:
-             case KADM5_BAD_POLICY:
-                  ret =  KADM5_UNK_POLICY;
-                  break;
-             }
-             goto done;
-        }
-        have_npol = 1;
-
-        /* if we already have a policy, get it to decrement the refcnt */
-        if(adb.aux_attributes & KADM5_POLICY) {
-             /* ... but not if the old and new are the same */
-             if(strcmp(adb.policy, entry->policy)) {
-                  ret = kadm5_get_policy(handle->lhandle,
-                                         adb.policy, &opol);
-                  switch(ret) {
-                  case EINVAL:
-                  case KADM5_BAD_POLICY:
-                  case KADM5_UNK_POLICY:
-                       break;
-                  case KADM5_OK:
-                       have_opol = 1;
-                       opol.policy_refcnt--;
-                       break;
-                  default:
-                       goto done;
-                       break;
-                  }
-                  npol.policy_refcnt++;
-             }
-        } else npol.policy_refcnt++;
-
-        /* set us up to use the new policy */
-        adb.aux_attributes |= KADM5_POLICY;
-        if (adb.policy)
-             free(adb.policy);
-        adb.policy = strdup(entry->policy);
-
-        /* set pw_max_life based on new policy */
-        if (npol.pw_max_life) {
-            ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
-                                                  &(kdb.pw_expiration));
-            if (ret)
-                goto done;
-            kdb.pw_expiration += npol.pw_max_life;
-        } else {
-            kdb.pw_expiration = 0;
-        }
+        /* get the new policy */
+        ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
+        if (ret) {
+            switch (ret) {
+            case EINVAL:
+                ret = KADM5_BAD_POLICY;
+                break;
+            case KADM5_UNK_POLICY:
+            case KADM5_BAD_POLICY:
+                ret =  KADM5_UNK_POLICY;
+                break;
+            }
+            goto done;
+        }
+        have_npol = 1;
+
+        /* if we already have a policy, get it to decrement the refcnt */
+        if(adb.aux_attributes & KADM5_POLICY) {
+            /* ... but not if the old and new are the same */
+            if(strcmp(adb.policy, entry->policy)) {
+                ret = kadm5_get_policy(handle->lhandle,
+                                       adb.policy, &opol);
+                switch(ret) {
+                case EINVAL:
+                case KADM5_BAD_POLICY:
+                case KADM5_UNK_POLICY:
+                    break;
+                case KADM5_OK:
+                    have_opol = 1;
+                    opol.policy_refcnt--;
+                    break;
+                default:
+                    goto done;
+                    break;
+                }
+                npol.policy_refcnt++;
+            }
+        } else npol.policy_refcnt++;
+
+        /* set us up to use the new policy */
+        adb.aux_attributes |= KADM5_POLICY;
+        if (adb.policy)
+            free(adb.policy);
+        adb.policy = strdup(entry->policy);
+
+        /* set pw_max_life based on new policy */
+        if (npol.pw_max_life) {
+            ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
+                                                  &(kdb->pw_expiration));
+            if (ret)
+                goto done;
+            kdb->pw_expiration += npol.pw_max_life;
+        } else {
+            kdb->pw_expiration = 0;
+        }
     }
 
     if ((mask & KADM5_POLICY_CLR) &&
-       (adb.aux_attributes & KADM5_POLICY)) {
-        ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
-        switch(ret) {
-        case EINVAL:
-        case KADM5_BAD_POLICY:
-        case KADM5_UNK_POLICY:
-             ret = KADM5_BAD_DB;
-             goto done;
-             break;
-        case KADM5_OK:
-             have_opol = 1;
-             if (adb.policy)
-                  free(adb.policy);
-             adb.policy = NULL;
-             adb.aux_attributes &= ~KADM5_POLICY;
-             kdb.pw_expiration = 0;
-             opol.policy_refcnt--;
-             break;
-        default:
-             goto done;
-             break;
-        }
+        (adb.aux_attributes & KADM5_POLICY)) {
+        ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
+        switch(ret) {
+        case EINVAL:
+        case KADM5_BAD_POLICY:
+        case KADM5_UNK_POLICY:
+            ret = KADM5_BAD_DB;
+            goto done;
+            break;
+        case KADM5_OK:
+            have_opol = 1;
+            if (adb.policy)
+                free(adb.policy);
+            adb.policy = NULL;
+            adb.aux_attributes &= ~KADM5_POLICY;
+            kdb->pw_expiration = 0;
+            opol.policy_refcnt--;
+            break;
+        default:
+            goto done;
+            break;
+        }
     }
 
     if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
-       (((have_opol) &&
-         (ret =
-          kadm5_modify_policy_internal(handle->lhandle, &opol,
-                                            KADM5_REF_COUNT))) ||
-        ((have_npol) &&
-         (ret =
-          kadm5_modify_policy_internal(handle->lhandle, &npol,
-                                            KADM5_REF_COUNT)))))
-       goto done;
+        (((have_opol) &&
+          (ret =
+           kadm5_modify_policy_internal(handle->lhandle, &opol,
+                                        KADM5_REF_COUNT))) ||
+         ((have_npol) &&
+          (ret =
+           kadm5_modify_policy_internal(handle->lhandle, &npol,
+                                        KADM5_REF_COUNT)))))
+        goto done;
 
     if ((mask & KADM5_ATTRIBUTES))
-       kdb.attributes = entry->attributes;
+        kdb->attributes = entry->attributes;
     if ((mask & KADM5_MAX_LIFE))
-       kdb.max_life = entry->max_life;
+        kdb->max_life = entry->max_life;
     if ((mask & KADM5_PRINC_EXPIRE_TIME))
-       kdb.expiration = entry->princ_expire_time;
+        kdb->expiration = entry->princ_expire_time;
     if (mask & KADM5_PW_EXPIRATION)
-        kdb.pw_expiration = entry->pw_expiration;
+        kdb->pw_expiration = entry->pw_expiration;
     if (mask & KADM5_MAX_RLIFE)
-        kdb.max_renewable_life = entry->max_renewable_life;
-    if (mask & KADM5_FAIL_AUTH_COUNT)
-        kdb.fail_auth_count = entry->fail_auth_count;
+        kdb->max_renewable_life = entry->max_renewable_life;
 
     if((mask & KADM5_KVNO)) {
-        for (i = 0; i < kdb.n_key_data; i++)
-             kdb.key_data[i].key_data_kvno = entry->kvno;
+        for (i = 0; i < kdb->n_key_data; i++)
+            kdb->key_data[i].key_data_kvno = entry->kvno;
     }
 
     if (mask & KADM5_TL_DATA) {
-        krb5_tl_data *tl;
+        krb5_tl_data *tl;
 
-        /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
+        /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
 
-        for (tl = entry->tl_data; tl;
-             tl = tl->tl_data_next)
-        {
-            ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl);
-            if( ret )
-            {
-                goto done;
-            }
-        }
+        for (tl = entry->tl_data; tl;
+             tl = tl->tl_data_next)
+        {
+            ret = krb5_dbe_update_tl_data(handle->context, kdb, tl);
+            if( ret )
+            {
+                goto done;
+            }
+        }
+    }
+
+    /*
+     * Setting entry->fail_auth_count to 0 can be used to manually unlock
+     * an account. It is not possible to set fail_auth_count to any other
+     * value using kadmin.
+     */
+    if (mask & KADM5_FAIL_AUTH_COUNT) {
+        if (entry->fail_auth_count != 0) {
+            ret = KADM5_BAD_SERVER_PARAMS;
+            goto done;
+        }
+
+        kdb->fail_auth_count = 0;
     }
 
     /* let the mask propagate to the database provider */
-    kdb.mask = mask;
+    kdb->mask = mask;
+
+    ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles,
+                               KADM5_HOOK_STAGE_PRECOMMIT, entry, mask);
+    if (ret)
+        goto done;
 
-    ret = kdb_put_entry(handle, &kdb, &adb);
+    ret = kdb_put_entry(handle, kdb, &adb);
     if (ret) goto done;
+    (void) k5_kadm5_hook_modify(handle->context, handle->hook_handles,
+                                KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask);
 
     ret = KADM5_OK;
 done:
     if (have_opol) {
-        ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
-        ret = ret ? ret : ret2;
+        ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
+        ret = ret ? ret : ret2;
     }
     if (have_npol) {
-        ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
-        ret = ret ? ret : ret2;
+        ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
+        ret = ret ? ret : ret2;
     }
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
     return ret;
 }
 
 kadm5_ret_t
 kadm5_rename_principal(void *server_handle,
-                           krb5_principal source, krb5_principal target)
+                       krb5_principal source, krb5_principal target)
 {
-    krb5_db_entry      kdb;
-    osa_princ_ent_rec  adb;
-    int                        ret, i;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+    krb5_error_code ret;
     kadm5_server_handle_t handle = server_handle;
+    krb5_int16 stype, i;
+    krb5_data *salt = NULL;
 
     CHECK_HANDLE(server_handle);
 
     krb5_clear_error_message(handle->context);
 
     if (source == NULL || target == NULL)
-       return EINVAL;
+        return EINVAL;
 
     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
-       kdb_free_entry(handle, &kdb, &adb);
-       return(KADM5_DUP);
+        kdb_free_entry(handle, kdb, &adb);
+        return(KADM5_DUP);
     }
 
     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
-       return ret;
-
-    /* this is kinda gross, but unavoidable */
-
-    for (i=0; i<kdb.n_key_data; i++) {
-       if ((kdb.key_data[i].key_data_ver == 1) ||
-           (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
-           ret = KADM5_NO_RENAME_SALT;
-           goto done;
-       }
+        return ret;
+
+    /* Transform salts as necessary. */
+    for (i = 0; i < kdb->n_key_data; i++) {
+        ret = krb5_dbe_compute_salt(handle->context, &kdb->key_data[i],
+                                    kdb->princ, &stype, &salt);
+        if (ret == KRB5_KDB_BAD_SALTTYPE)
+            ret = KADM5_NO_RENAME_SALT;
+        if (ret)
+            goto done;
+        kdb->key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
+        free(kdb->key_data[i].key_data_contents[1]);
+        kdb->key_data[i].key_data_contents[1] = (krb5_octet *)salt->data;
+        kdb->key_data[i].key_data_length[1] = salt->length;
+        kdb->key_data[i].key_data_ver = 2;
+        free(salt);
+        salt = NULL;
     }
 
-    kadm5_free_principal(handle->context, kdb.princ);
-    ret = kadm5_copy_principal(handle->context, target, &kdb.princ);
+    kadm5_free_principal(handle->context, kdb->princ);
+    ret = kadm5_copy_principal(handle->context, target, &kdb->princ);
     if (ret) {
-       kdb.princ = NULL; /* so freeing the dbe doesn't lose */
-       goto done;
+        kdb->princ = NULL; /* so freeing the dbe doesn't lose */
+        goto done;
     }
 
-    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
-       goto done;
+    if ((ret = kdb_put_entry(handle, kdb, &adb)))
+        goto done;
 
     ret = kdb_delete_entry(handle, source);
 
 done:
-    kdb_free_entry(handle, &kdb, &adb);
+    krb5_free_data(handle->context, salt);
+    kdb_free_entry(handle, kdb, &adb);
     return ret;
 }
 
 kadm5_ret_t
 kadm5_get_principal(void *server_handle, krb5_principal principal,
-                   kadm5_principal_ent_t entry,
-                   long in_mask)
+                    kadm5_principal_ent_t entry,
+                    long in_mask)
 {
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
-    krb5_error_code            ret = 0;
-    long                       mask;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
+    krb5_error_code             ret = 0;
+    long                        mask;
     int i;
     kadm5_server_handle_t handle = server_handle;
-    kadm5_principal_ent_rec    entry_local, *entry_orig;
 
     CHECK_HANDLE(server_handle);
 
@@ -756,187 +801,134 @@ kadm5_get_principal(void *server_handle, krb5_principal principal,
      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
      * filled with allocated memory.
      */
-    if (handle->api_version == KADM5_API_VERSION_1) {
-        mask = KADM5_PRINCIPAL_NORMAL_MASK;
-        entry_orig = entry;
-        entry = &entry_local;
-    } else {
-        mask = in_mask;
-    }
+    mask = in_mask;
 
     memset(entry, 0, sizeof(*entry));
 
     if (principal == NULL)
-       return EINVAL;
+        return EINVAL;
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return ret;
+        return ret;
 
     if ((mask & KADM5_POLICY) &&
-       adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
-       if ((entry->policy = strdup(adb.policy)) == NULL) {
-           ret = ENOMEM;
-           goto done;
-       }
+        adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
+        if ((entry->policy = strdup(adb.policy)) == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
     }
 
     if (mask & KADM5_AUX_ATTRIBUTES)
-        entry->aux_attributes = adb.aux_attributes;
+        entry->aux_attributes = adb.aux_attributes;
 
     if ((mask & KADM5_PRINCIPAL) &&
-       (ret = krb5_copy_principal(handle->context, kdb.princ,
-                                  &entry->principal))) {
-       goto done;
+        (ret = krb5_copy_principal(handle->context, kdb->princ,
+                                   &entry->principal))) {
+        goto done;
     }
 
     if (mask & KADM5_PRINC_EXPIRE_TIME)
-        entry->princ_expire_time = kdb.expiration;
+        entry->princ_expire_time = kdb->expiration;
 
     if ((mask & KADM5_LAST_PWD_CHANGE) &&
-       (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
-                                              &(entry->last_pwd_change)))) {
-       goto done;
+        (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
+                                               &(entry->last_pwd_change)))) {
+        goto done;
     }
 
     if (mask & KADM5_PW_EXPIRATION)
-        entry->pw_expiration = kdb.pw_expiration;
+        entry->pw_expiration = kdb->pw_expiration;
     if (mask & KADM5_MAX_LIFE)
-        entry->max_life = kdb.max_life;
+        entry->max_life = kdb->max_life;
 
     /* this is a little non-sensical because the function returns two */
     /* values that must be checked separately against the mask */
     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
-       ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
-                                            &(entry->mod_date),
-                                            &(entry->mod_name));
-       if (ret) {
-           goto done;
-       }
+        ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
+                                             &(entry->mod_date),
+                                             &(entry->mod_name));
+        if (ret) {
+            goto done;
+        }
 
-       if (! (mask & KADM5_MOD_TIME))
-           entry->mod_date = 0;
-       if (! (mask & KADM5_MOD_NAME)) {
-           krb5_free_principal(handle->context, entry->principal);
-           entry->principal = NULL;
-       }
+        if (! (mask & KADM5_MOD_TIME))
+            entry->mod_date = 0;
+        if (! (mask & KADM5_MOD_NAME)) {
+            krb5_free_principal(handle->context, entry->mod_name);
+            entry->mod_name = NULL;
+        }
     }
 
     if (mask & KADM5_ATTRIBUTES)
-        entry->attributes = kdb.attributes;
+        entry->attributes = kdb->attributes;
 
     if (mask & KADM5_KVNO)
-        for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
-             if (kdb.key_data[i].key_data_kvno > entry->kvno)
-                  entry->kvno = kdb.key_data[i].key_data_kvno;
-
-    ret = krb5_dbe_lookup_mkvno(handle->context, &kdb, &entry->mkvno);
-    if (ret)
-       goto done;
+        for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
+            if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
+                entry->kvno = kdb->key_data[i].key_data_kvno;
 
-    /*
-     * It's my understanding that KADM5_API_VERSION_1 is for OpenVision admin
-     * system compatiblity and is not required to maintain at this point so I'm
-     * commenting out this code.
-     * -- Will Fiveash
-     */
-#if 0 /************** Begin IFDEF'ed OUT *******************************/
-    if (handle->api_version == KADM5_API_VERSION_2)
-        entry->mkvno = 0;
-    else {
-        /* XXX I'll be damned if I know how to deal with this one --marc */
-        entry->mkvno = 1;
+    if (mask & KADM5_MKVNO) {
+        ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
+        if (ret)
+            goto done;
     }
-#endif /**************** END IFDEF'ed OUT *******************************/
 
-    /*
-     * The new fields that only exist in version 2 start here
-     */
-    if (handle->api_version == KADM5_API_VERSION_2) {
-        if (mask & KADM5_MAX_RLIFE)
-             entry->max_renewable_life = kdb.max_renewable_life;
-        if (mask & KADM5_LAST_SUCCESS)
-             entry->last_success = kdb.last_success;
-        if (mask & KADM5_LAST_FAILED)
-             entry->last_failed = kdb.last_failed;
-        if (mask & KADM5_FAIL_AUTH_COUNT)
-             entry->fail_auth_count = kdb.fail_auth_count;
-        if (mask & KADM5_TL_DATA) {
-             krb5_tl_data *tl, *tl2;
-
-             entry->tl_data = NULL;
-
-             tl = kdb.tl_data;
-             while (tl) {
-                  if (tl->tl_data_type > 255) {
-                       if ((tl2 = dup_tl_data(tl)) == NULL) {
-                            ret = ENOMEM;
-                            goto done;
-                       }
-                       tl2->tl_data_next = entry->tl_data;
-                       entry->tl_data = tl2;
-                       entry->n_tl_data++;
-                  }
-
-                  tl = tl->tl_data_next;
-             }
-        }
-        if (mask & KADM5_KEY_DATA) {
-             entry->n_key_data = kdb.n_key_data;
-             if(entry->n_key_data) {
-                     entry->key_data = (krb5_key_data *)
-                             malloc(entry->n_key_data*sizeof(krb5_key_data));
-                     if (entry->key_data == NULL) {
-                             ret = ENOMEM;
-                             goto done;
-                     }
-             } else
-                     entry->key_data = NULL;
-
-             for (i = 0; i < entry->n_key_data; i++)
-                 ret = krb5_copy_key_data_contents(handle->context,
-                                                   &kdb.key_data[i],
-                                                   &entry->key_data[i]);
-                  if (ret)
-                       goto done;
-        }
+    if (mask & KADM5_MAX_RLIFE)
+        entry->max_renewable_life = kdb->max_renewable_life;
+    if (mask & KADM5_LAST_SUCCESS)
+        entry->last_success = kdb->last_success;
+    if (mask & KADM5_LAST_FAILED)
+        entry->last_failed = kdb->last_failed;
+    if (mask & KADM5_FAIL_AUTH_COUNT)
+        entry->fail_auth_count = kdb->fail_auth_count;
+    if (mask & KADM5_TL_DATA) {
+        krb5_tl_data *tl, *tl2;
+
+        entry->tl_data = NULL;
+
+        tl = kdb->tl_data;
+        while (tl) {
+            if (tl->tl_data_type > 255) {
+                if ((tl2 = dup_tl_data(tl)) == NULL) {
+                    ret = ENOMEM;
+                    goto done;
+                }
+                tl2->tl_data_next = entry->tl_data;
+                entry->tl_data = tl2;
+                entry->n_tl_data++;
+            }
+
+            tl = tl->tl_data_next;
+        }
     }
+    if (mask & KADM5_KEY_DATA) {
+        entry->n_key_data = kdb->n_key_data;
+        if(entry->n_key_data) {
+            entry->key_data = malloc(entry->n_key_data*sizeof(krb5_key_data));
+            if (entry->key_data == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+        } else
+            entry->key_data = NULL;
 
-    /*
-     * If KADM5_API_VERSION_1, we return an allocated structure, and
-     * we need to convert the new structure back into the format the
-     * caller is expecting.
-     */
-    if (handle->api_version == KADM5_API_VERSION_1) {
-        kadm5_principal_ent_t_v1 newv1;
-
-        newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
-        if (newv1 == NULL) {
-             ret = ENOMEM;
-             goto done;
-        }
-
-        newv1->principal = entry->principal;
-        newv1->princ_expire_time = entry->princ_expire_time;
-        newv1->last_pwd_change = entry->last_pwd_change;
-        newv1->pw_expiration = entry->pw_expiration;
-        newv1->max_life = entry->max_life;
-        newv1->mod_name = entry->mod_name;
-        newv1->mod_date = entry->mod_date;
-        newv1->attributes = entry->attributes;
-        newv1->kvno = entry->kvno;
-        newv1->mkvno = entry->mkvno;
-        newv1->policy = entry->policy;
-        newv1->aux_attributes = entry->aux_attributes;
-
-        *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
+        for (i = 0; i < entry->n_key_data; i++)
+            ret = krb5_copy_key_data_contents(handle->context,
+                                              &kdb->key_data[i],
+                                              &entry->key_data[i]);
+        if (ret)
+            goto done;
     }
 
     ret = KADM5_OK;
 
 done:
-    if (ret && entry->principal)
-        krb5_free_principal(handle->context, entry->principal);
-    kdb_free_entry(handle, &kdb, &adb);
+    if (ret && entry->principal) {
+        krb5_free_principal(handle->context, entry->principal);
+        entry->principal = NULL;
+    }
+    kdb_free_entry(handle, kdb, &adb);
 
     return ret;
 }
@@ -949,66 +941,67 @@ done:
  *
  * Arguments:
  *
- *     context                 (r) the krb5 context
- *     hist_keyblock           (r) the key that hist_key_data is
- *                             encrypted in
- *     n_new_key_data          (r) length of new_key_data
- *     new_key_data            (r) keys to check against
- *                             pw_hist_data, encrypted in hist_keyblock
- *     n_pw_hist_data          (r) length of pw_hist_data
- *     pw_hist_data            (r) passwords to check new_key_data against
+ *      context                 (r) the krb5 context
+ *      hist_keyblock           (r) the key that hist_key_data is
+ *                              encrypted in
+ *      n_new_key_data          (r) length of new_key_data
+ *      new_key_data            (r) keys to check against
+ *                              pw_hist_data, encrypted in hist_keyblock
+ *      n_pw_hist_data          (r) length of pw_hist_data
+ *      pw_hist_data            (r) passwords to check new_key_data against
  *
  * Effects:
  * For each new_key in new_key_data:
- *     decrypt new_key with the master_keyblock
- *     for each password in pw_hist_data:
- *             for each hist_key in password:
- *                     decrypt hist_key with hist_keyblock
- *                     compare the new_key and hist_key
+ *      decrypt new_key with the master_keyblock
+ *      for each password in pw_hist_data:
+ *              for each hist_key in password:
+ *                      decrypt hist_key with hist_keyblock
+ *                      compare the new_key and hist_key
  *
  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
  * new_key_data is the same as a key in pw_hist_data, or 0.
  */
 static kadm5_ret_t
 check_pw_reuse(krb5_context context,
-              krb5_keyblock *mkey,
-              krb5_keyblock *hist_keyblock,
-              int n_new_key_data, krb5_key_data *new_key_data,
-              unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
+               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;
+    unsigned int x, y, z;
+    krb5_keyblock newkey, histkey, *kb;
+    krb5_key_data *key_data;
     krb5_error_code ret;
 
-    for (x = 0; x < n_new_key_data; x++) {
-       ret = krb5_dbekd_decrypt_key_data(context,
-                                         mkey,
-                                         &(new_key_data[x]),
-                                         &newkey, NULL);
-       if (ret)
-           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);
-                }
-                krb5_free_keyblock_contents(context, &histkey);
-            }
-       }
-       krb5_free_keyblock_contents(context, &newkey);
+    assert (n_new_key_data >= 0);
+    for (x = 0; x < (unsigned) n_new_key_data; x++) {
+        /* Check only entries with the most recent kvno. */
+        if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
+            break;
+        ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
+                                        &newkey, NULL);
+        if (ret)
+            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++) {
+                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(0);
@@ -1022,10 +1015,12 @@ check_pw_reuse(krb5_context context,
  *
  * Arguments:
  *
- *     context         (r) krb5_context to use
- *     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
+ *      context         (r) krb5_context to use
+ *      mkey            (r) master keyblock to decrypt key data with
+ *      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
  *
  * Effects:
  *
@@ -1035,49 +1030,47 @@ check_pw_reuse(krb5_context context,
  * set to n_key_data.
  */
 static
-int create_history_entry(krb5_context context, krb5_keyblock *mkey, int n_key_data,
-                        krb5_key_data *key_data, osa_pw_hist_ent *hist)
+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)
 {
-     int i, ret;
-     krb5_keyblock key;
-     krb5_keysalt salt;
-
-     hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
-     if (hist->key_data == NULL)
-         return ENOMEM;
-     memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
-
-     for (i = 0; i < n_key_data; i++) {
-        ret = krb5_dbekd_decrypt_key_data(context,
-                                          mkey,
-                                          &key_data[i],
-                                          &key, &salt);
-        if (ret)
-            return ret;
-
-        ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
-                                          &key, &salt,
-                                          key_data[i].key_data_kvno,
-                                          &hist->key_data[i]);
-        if (ret)
-            return ret;
-
-        krb5_free_keyblock_contents(context, &key);
-        /* krb5_free_keysalt(context, &salt); */
-     }
-
-     hist->n_key_data = n_key_data;
-     return 0;
+    int i, ret;
+    krb5_keyblock key;
+    krb5_keysalt salt;
+
+    hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
+    if (hist->key_data == NULL)
+        return ENOMEM;
+    memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
+
+    for (i = 0; i < n_key_data; i++) {
+        ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
+                                        &salt);
+        if (ret)
+            return ret;
+
+        ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
+                                        key_data[i].key_data_kvno,
+                                        &hist->key_data[i]);
+        if (ret)
+            return ret;
+
+        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;
+    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);
+    for (i = 0; i < hist->n_key_data; i++)
+        krb5_free_key_data_contents(context, &hist->key_data[i]);
+    free(hist->key_data);
 }
 
 /*
@@ -1087,10 +1080,11 @@ void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
  *
  * Arguments:
  *
- *     context         (r) krb5_context to use
- *     adb             (r/w) admin principal entry to add keys to
- *     pol             (r) adb's policy
- *     pw              (r) keys for the password to add to adb's key history
+ *      context         (r) krb5_context to use
+ *      hist_kvno       (r) kvno of current history key
+ *      adb             (r/w) admin principal entry to add keys to
+ *      pol             (r) adb's policy
+ *      pw              (r) keys for the password to add to adb's key history
  *
  * Effects:
  *
@@ -1107,111 +1101,122 @@ void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
  * adb->old_key_len).
  */
 static kadm5_ret_t add_to_history(krb5_context context,
-                                 osa_princ_ent_t adb,
-                                 kadm5_policy_ent_t pol,
-                                 osa_pw_hist_ent *pw)
+                                  krb5_kvno hist_kvno,
+                                  osa_princ_ent_t adb,
+                                  kadm5_policy_ent_t pol,
+                                  osa_pw_hist_ent *pw)
 {
-     osa_pw_hist_ent *histp;
-     uint32_t nhist;
-     unsigned int i, knext, nkeys;
-
-     nhist = pol->pw_history_num;
-     /* A history of 1 means just check the current password */
-     if (nhist <= 1)
-         return 0;
-
-     nkeys = adb->old_key_len;
-     knext = adb->old_key_next;
-     /* resize the adb->old_keys array if necessary */
-     if (nkeys + 1 < nhist) {
-         if (adb->old_keys == NULL) {
-              adb->old_keys = (osa_pw_hist_ent *)
-                   malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
-         } else {
-              adb->old_keys = (osa_pw_hist_ent *)
-                   realloc(adb->old_keys,
-                           (nkeys + 1) * sizeof (osa_pw_hist_ent));
-         }
-         if (adb->old_keys == NULL)
-              return(ENOMEM);
-
-         memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
-         nkeys = ++adb->old_key_len;
-         /*
-          * To avoid losing old keys, shift forward each entry after
-          * knext.
-          */
-         for (i = nkeys - 1; i > knext; i--) {
-             adb->old_keys[i] = adb->old_keys[i - 1];
-         }
-         memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
-     } else if (nkeys + 1 > nhist) {
-        /*
-         * The policy must have changed!  Shrink the array.
-         * Can't simply realloc() down, since it might be wrapped.
-         * To understand the arithmetic below, note that we are
-         * copying into new positions 0 .. N-1 from old positions
-         * old_key_next-N .. old_key_next-1, modulo old_key_len,
-         * where N = pw_history_num - 1 is the length of the
-         * shortened list.        Matt Crawford, FNAL
-         */
-        /*
-         * M = adb->old_key_len, N = pol->pw_history_num - 1
-         *
-         * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
-         */
-        int j;
-        osa_pw_hist_t tmp;
-
-        tmp = (osa_pw_hist_ent *)
-            malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
-        if (tmp == NULL)
-            return ENOMEM;
-        for (i = 0; i < nhist - 1; i++) {
-            /*
-             * Add nkeys once before taking remainder to avoid
-             * negative values.
-             */
-            j = (i + nkeys + knext - (nhist - 1)) % nkeys;
-            tmp[i] = adb->old_keys[j];
-        }
-        /* Now free the ones we don't keep (the oldest ones) */
-        for (i = 0; i < nkeys - (nhist - 1); i++) {
-            j = (i + nkeys + knext) % nkeys;
-            histp = &adb->old_keys[j];
-            for (j = 0; j < histp->n_key_data; j++) {
-                krb5_free_key_data_contents(context, &histp->key_data[j]);
-            }
-            free(histp->key_data);
-        }
-        free(adb->old_keys);
-        adb->old_keys = tmp;
-        nkeys = adb->old_key_len = nhist - 1;
-        knext = adb->old_key_next = 0;
-     }
-
-     /*
-      * If nhist decreased since the last password change, and nkeys+1
-      * is less than the previous nhist, it is possible for knext to
-      * index into unallocated space.  This condition would not be
-      * caught by the resizing code above.
-      */
-     if (knext + 1 > nkeys)
-        knext = adb->old_key_next = 0;
-     /* free the old pw history entry if it contains data */
-     histp = &adb->old_keys[knext];
-     for (i = 0; i < histp->n_key_data; i++)
-         krb5_free_key_data_contents(context, &histp->key_data[i]);
-     free(histp->key_data);
-
-     /* store the new entry */
-     adb->old_keys[knext] = *pw;
-
-     /* update the next pointer */
-     if (++adb->old_key_next == nhist - 1)
-        adb->old_key_next = 0;
-
-     return(0);
+    osa_pw_hist_ent *histp;
+    uint32_t nhist;
+    unsigned int i, knext, nkeys;
+
+    nhist = pol->pw_history_num;
+    /* A history of 1 means just check the current password */
+    if (nhist <= 1)
+        return 0;
+
+    if (adb->admin_history_kvno != hist_kvno) {
+        /* The history key has changed since the last password change, so we
+         * have to reset the password history. */
+        free(adb->old_keys);
+        adb->old_keys = NULL;
+        adb->old_key_len = 0;
+        adb->old_key_next = 0;
+        adb->admin_history_kvno = hist_kvno;
+    }
+
+    nkeys = adb->old_key_len;
+    knext = adb->old_key_next;
+    /* resize the adb->old_keys array if necessary */
+    if (nkeys + 1 < nhist) {
+        if (adb->old_keys == NULL) {
+            adb->old_keys = (osa_pw_hist_ent *)
+                malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
+        } else {
+            adb->old_keys = (osa_pw_hist_ent *)
+                realloc(adb->old_keys,
+                        (nkeys + 1) * sizeof (osa_pw_hist_ent));
+        }
+        if (adb->old_keys == NULL)
+            return(ENOMEM);
+
+        memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
+        nkeys = ++adb->old_key_len;
+        /*
+         * To avoid losing old keys, shift forward each entry after
+         * knext.
+         */
+        for (i = nkeys - 1; i > knext; i--) {
+            adb->old_keys[i] = adb->old_keys[i - 1];
+        }
+        memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
+    } else if (nkeys + 1 > nhist) {
+        /*
+         * The policy must have changed!  Shrink the array.
+         * Can't simply realloc() down, since it might be wrapped.
+         * To understand the arithmetic below, note that we are
+         * copying into new positions 0 .. N-1 from old positions
+         * old_key_next-N .. old_key_next-1, modulo old_key_len,
+         * where N = pw_history_num - 1 is the length of the
+         * shortened list.        Matt Crawford, FNAL
+         */
+        /*
+         * M = adb->old_key_len, N = pol->pw_history_num - 1
+         *
+         * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
+         */
+        int j;
+        osa_pw_hist_t tmp;
+
+        tmp = (osa_pw_hist_ent *)
+            malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
+        if (tmp == NULL)
+            return ENOMEM;
+        for (i = 0; i < nhist - 1; i++) {
+            /*
+             * Add nkeys once before taking remainder to avoid
+             * negative values.
+             */
+            j = (i + nkeys + knext - (nhist - 1)) % nkeys;
+            tmp[i] = adb->old_keys[j];
+        }
+        /* Now free the ones we don't keep (the oldest ones) */
+        for (i = 0; i < nkeys - (nhist - 1); i++) {
+            j = (i + nkeys + knext) % nkeys;
+            histp = &adb->old_keys[j];
+            for (j = 0; j < histp->n_key_data; j++) {
+                krb5_free_key_data_contents(context, &histp->key_data[j]);
+            }
+            free(histp->key_data);
+        }
+        free(adb->old_keys);
+        adb->old_keys = tmp;
+        nkeys = adb->old_key_len = nhist - 1;
+        knext = adb->old_key_next = 0;
+    }
+
+    /*
+     * If nhist decreased since the last password change, and nkeys+1
+     * is less than the previous nhist, it is possible for knext to
+     * index into unallocated space.  This condition would not be
+     * caught by the resizing code above.
+     */
+    if (knext + 1 > nkeys)
+        knext = adb->old_key_next = 0;
+    /* free the old pw history entry if it contains data */
+    histp = &adb->old_keys[knext];
+    for (i = 0; i < (unsigned int) histp->n_key_data; i++)
+        krb5_free_key_data_contents(context, &histp->key_data[i]);
+    free(histp->key_data);
+
+    /* store the new entry */
+    adb->old_keys[knext] = *pw;
+
+    /* update the next pointer */
+    if (++adb->old_key_next == nhist - 1)
+        adb->old_key_next = 0;
+
+    return(0);
 }
 
 /* FIXME: don't use global variable for this */
@@ -1254,22 +1259,22 @@ kadm5_launch_task (krb5_context context,
 
     ret = pipe (data_pipe);
     if (ret)
-       ret = errno;
+        ret = errno;
 
     if (!ret) {
         pid_t pid = fork ();
         if (pid == -1) {
             ret = errno;
-           close (data_pipe[0]);
-           close (data_pipe[1]);
+            close (data_pipe[0]);
+            close (data_pipe[1]);
         } else if (pid == 0) {
             /* The child: */
 
             if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
-               _exit (1);
+                _exit (1);
 
-           close (data_pipe[0]);
-           close (data_pipe[1]);
+            close (data_pipe[0]);
+            close (data_pipe[1]);
 
             execv (task_path, task_argv);
 
@@ -1278,21 +1283,21 @@ kadm5_launch_task (krb5_context context,
             /* The parent: */
             int status;
 
-           ret = 0;
+            ret = 0;
 
-           close (data_pipe[0]);
+            close (data_pipe[0]);
 
-           /* Write out the buffer to the child, add \n */
-           if (buffer) {
-               if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
-                   || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
-               {
-                   /* kill the child to make sure waitpid() won't hang later */
-                   ret = errno;
-                   kill (pid, SIGKILL);
-               }
-           }
-           close (data_pipe[1]);
+            /* Write out the buffer to the child, add \n */
+            if (buffer) {
+                if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
+                    || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
+                {
+                    /* kill the child to make sure waitpid() won't hang later */
+                    ret = errno;
+                    kill (pid, SIGKILL);
+                }
+            }
+            close (data_pipe[1]);
 
             waitpid (pid, &status, 0);
 
@@ -1300,7 +1305,7 @@ kadm5_launch_task (krb5_context context,
                 if (WIFEXITED (status)) {
                     /* child read password and exited.  Check the return value. */
                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
-                       ret = KRB5KDC_ERR_POLICY; /* password change rejected */
+                        ret = KRB5KDC_ERR_POLICY; /* password change rejected */
                     }
                 } else {
                     /* child read password but crashed or was killed */
@@ -1317,29 +1322,29 @@ kadm5_launch_task (krb5_context context,
 
 kadm5_ret_t
 kadm5_chpass_principal(void *server_handle,
-                           krb5_principal principal, char *password)
+                       krb5_principal principal, char *password)
 {
     return
-       kadm5_chpass_principal_3(server_handle, principal, FALSE,
-                                0, NULL, password);
+        kadm5_chpass_principal_3(server_handle, principal, FALSE,
+                                 0, NULL, password);
 }
 
 kadm5_ret_t
 kadm5_chpass_principal_3(void *server_handle,
-                        krb5_principal principal, krb5_boolean keepold,
-                        int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
-                        char *password)
+                         krb5_principal principal, krb5_boolean keepold,
+                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                         char *password)
 {
-    krb5_int32                 now;
-    kadm5_policy_ent_rec       pol;
-    osa_princ_ent_rec          adb;
-    krb5_db_entry              kdb, kdb_save;
-    int                                ret, ret2, last_pwd, hist_added;
-    int                                have_pol = 0;
-    kadm5_server_handle_t      handle = server_handle;
-    osa_pw_hist_ent            hist;
-    krb5_keyblock               *act_mkey;
-    krb5_kvno                   act_kvno;
+    krb5_int32                  now;
+    kadm5_policy_ent_rec        pol;
+    osa_princ_ent_rec           adb;
+    krb5_db_entry               *kdb;
+    int                         ret, ret2, last_pwd, hist_added;
+    int                         have_pol = 0;
+    kadm5_server_handle_t       handle = server_handle;
+    osa_pw_hist_ent             hist;
+    krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
+    krb5_kvno                   act_kvno, hist_kvno;
 
     CHECK_HANDLE(server_handle);
 
@@ -1349,112 +1354,111 @@ kadm5_chpass_principal_3(void *server_handle,
     memset(&hist, 0, sizeof(hist));
 
     if (principal == NULL || password == NULL)
-       return EINVAL;
+        return EINVAL;
     if ((krb5_principal_compare(handle->context,
-                               principal, hist_princ)) == TRUE)
-       return KADM5_PROTECT_PRINCIPAL;
-
-    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+                                principal, hist_princ)) == TRUE)
+        return KADM5_PROTECT_PRINCIPAL;
 
-    /* we are going to need the current keys after the new keys are set */
-    if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
-        kdb_free_entry(handle, &kdb, &adb);
-        return(ret);
+    /* Use default keysalts if caller did not provide any. */
+    if (n_ks_tuple == 0) {
+        ks_tuple = handle->params.keysalts;
+        n_ks_tuple = handle->params.num_keysalts;
     }
 
+    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+        return(ret);
+
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
-            goto done;
-       have_pol = 1;
+        if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
+            goto done;
+        have_pol = 1;
+
+        /* Create a password history entry before we change kdb's key_data. */
+        ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
+        if (ret)
+            goto done;
+        ret = create_history_entry(handle->context, &hist_keyblocks[0],
+                                   kdb->n_key_data, kdb->key_data, &hist);
+        if (ret)
+            goto done;
     }
 
-    if ((ret = passwd_check(handle, password, adb.aux_attributes &
-                           KADM5_POLICY, &pol, principal)))
-        goto done;
+    if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
+                            principal)))
+        goto done;
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                active_mkey_list, &act_kvno, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
+                                 &act_mkey);
     if (ret)
-       goto done;
+        goto done;
 
-    ret = krb5_dbe_cpw(handle->context, act_mkey,
-                      n_ks_tuple?ks_tuple:handle->params.keysalts,
-                      n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
-                      password, 0 /* increment kvno */,
-                      keepold, &kdb);
+    ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
+                       password, 0 /* increment kvno */,
+                       keepold, kdb);
     if (ret)
-       goto done;
+        goto done;
 
-    ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno);
+    ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
     if (ret)
-        goto done;
+        goto done;
 
-    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     ret = krb5_timeofday(handle->context, &now);
     if (ret)
-        goto done;
+        goto done;
 
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       /* the policy was loaded before */
+        /* the policy was loaded before */
 
-       ret = krb5_dbe_lookup_last_pwd_change(handle->context,
-                                             &kdb, &last_pwd);
-       if (ret)
-           goto done;
+        ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
+        if (ret)
+            goto done;
 
 #if 0
-        /*
-         * The spec says this check is overridden if the caller has
-         * modify privilege.  The admin server therefore makes this
-         * check itself (in chpass_principal_wrapper, misc.c). A
-         * local caller implicitly has all authorization bits.
-         */
-       if ((now - last_pwd) < pol.pw_min_life &&
-           !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
-            ret = KADM5_PASS_TOOSOON;
-            goto done;
-       }
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c). A
+         * local caller implicitly has all authorization bits.
+         */
+        if ((now - last_pwd) < pol.pw_min_life &&
+            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+        }
 #endif
 
-       ret = create_history_entry(handle->context,
-                                  act_mkey,
-                                  kdb_save.n_key_data,
-                                  kdb_save.key_data, &hist);
-       if (ret)
-           goto done;
-
-       ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
-                            kdb.n_key_data, kdb.key_data,
-                            1, &hist);
-       if (ret)
-           goto done;
-
-       if (pol.pw_history_num > 1) {
-           if (adb.admin_history_kvno != hist_kvno) {
-               ret = KADM5_BAD_HIST_KEY;
-               goto done;
-           }
-
-           ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
-                                kdb.n_key_data, kdb.key_data,
-                                adb.old_key_len, adb.old_keys);
-           if (ret)
-               goto done;
-
-           ret = add_to_history(handle->context, &adb, &pol, &hist);
-           if (ret)
-               goto done;
-           hist_added = 1;
-       }
-
-       if (pol.pw_max_life)
-          kdb.pw_expiration = now + pol.pw_max_life;
-       else
-          kdb.pw_expiration = 0;
+        ret = check_pw_reuse(handle->context, hist_keyblocks,
+                             kdb->n_key_data, kdb->key_data,
+                             1, &hist);
+        if (ret)
+            goto done;
+
+        if (pol.pw_history_num > 1) {
+            /* 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_keyblocks,
+                                     kdb->n_key_data, kdb->key_data,
+                                     adb.old_key_len, adb.old_keys);
+                if (ret)
+                    goto done;
+            }
+
+            ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
+                                 &hist);
+            if (ret)
+                goto done;
+            hist_added = 1;
+        }
+
+        if (pol.pw_max_life)
+            kdb->pw_expiration = now + pol.pw_max_life;
+        else
+            kdb->pw_expiration = 0;
     } else {
-       kdb.pw_expiration = 0;
+        kdb->pw_expiration = 0;
     }
 
 #ifdef USE_PASSWORD_SERVER
@@ -1486,177 +1490,180 @@ kadm5_chpass_principal_3(void *server_handle,
     }
 #endif
 
-    ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
+    ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
     if (ret)
-       goto done;
+        goto done;
+
+    /* unlock principal on this KDC */
+    kdb->fail_auth_count = 0;
 
     /* key data and attributes changed, let the database provider know */
-    kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
+    kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES |
+        KADM5_FAIL_AUTH_COUNT;
+    /* | KADM5_CPW_FUNCTION */
+
+    ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+                               KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
+                               n_ks_tuple, ks_tuple, password);
+    if (ret)
+        goto done;
 
-    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
-       goto done;
+    if ((ret = kdb_put_entry(handle, kdb, &adb)))
+        goto done;
 
+    (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+                                KADM5_HOOK_STAGE_POSTCOMMIT, principal,
+                                keepold, n_ks_tuple, ks_tuple, password);
     ret = KADM5_OK;
 done:
     if (!hist_added && hist.key_data)
-        free_history_entry(handle->context, &hist);
-    kdb_free_entry(handle, &kdb, &adb);
-    kdb_free_entry(handle, &kdb_save, NULL);
-    krb5_db_free_principal(handle->context, &kdb, 1);
+        free_history_entry(handle->context, &hist);
+    kdb_free_entry(handle, kdb, &adb);
+    kdb_free_keyblocks(handle, hist_keyblocks);
 
     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
-       && !ret)
-        ret = ret2;
+        && !ret)
+        ret = ret2;
 
     return ret;
 }
 
 kadm5_ret_t
 kadm5_randkey_principal(void *server_handle,
-                       krb5_principal principal,
-                       krb5_keyblock **keyblocks,
-                       int *n_keys)
+                        krb5_principal principal,
+                        krb5_keyblock **keyblocks,
+                        int *n_keys)
 {
     return
-       kadm5_randkey_principal_3(server_handle, principal,
-                                 FALSE, 0, NULL,
-                                 keyblocks, n_keys);
+        kadm5_randkey_principal_3(server_handle, principal,
+                                  FALSE, 0, NULL,
+                                  keyblocks, n_keys);
 }
 kadm5_ret_t
 kadm5_randkey_principal_3(void *server_handle,
-                       krb5_principal principal,
-                       krb5_boolean keepold,
-                       int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
-                       krb5_keyblock **keyblocks,
-                       int *n_keys)
+                          krb5_principal principal,
+                          krb5_boolean keepold,
+                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                          krb5_keyblock **keyblocks,
+                          int *n_keys)
 {
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
-    krb5_int32                 now;
-    kadm5_policy_ent_rec       pol;
-    krb5_key_data              *key_data;
-    int                                ret, last_pwd, have_pol = 0;
-    kadm5_server_handle_t      handle = server_handle;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
+    krb5_int32                  now;
+    kadm5_policy_ent_rec        pol;
+    int                         ret, last_pwd, have_pol = 0;
+    kadm5_server_handle_t       handle = server_handle;
     krb5_keyblock               *act_mkey;
 
     if (keyblocks)
-        *keyblocks = NULL;
+        *keyblocks = NULL;
 
     CHECK_HANDLE(server_handle);
 
+    /* Use default keysalts if caller did not provide any. */
+    if (n_ks_tuple == 0) {
+        ks_tuple = handle->params.keysalts;
+        n_ks_tuple = handle->params.num_keysalts;
+    }
+
     krb5_clear_error_message(handle->context);
 
     if (principal == NULL)
-       return EINVAL;
-    if (hist_princ && /* this will be NULL when initializing the databse */
-       ((krb5_principal_compare(handle->context,
-                                principal, hist_princ)) == TRUE))
-       return KADM5_PROTECT_PRINCIPAL;
+        return EINVAL;
+    if (krb5_principal_compare(handle->context, principal, hist_princ)) {
+        /* If changing the history entry, the new entry must have exactly one
+         * key. */
+        if (keepold)
+            return KADM5_PROTECT_PRINCIPAL;
+        n_ks_tuple = 1;
+    }
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+        return(ret);
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                active_mkey_list, NULL, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                 &act_mkey);
     if (ret)
-       goto done;
+        goto done;
 
-    ret = krb5_dbe_crk(handle->context, act_mkey,
-                      n_ks_tuple?ks_tuple:handle->params.keysalts,
-                      n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
-                      keepold,
-                      &kdb);
+    ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple,
+                       keepold, kdb);
     if (ret)
-       goto done;
+        goto done;
 
-    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     ret = krb5_timeofday(handle->context, &now);
     if (ret)
-       goto done;
+        goto done;
 
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
-                                   &pol)) != KADM5_OK)
-          goto done;
-       have_pol = 1;
+        if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+                                    &pol)) != KADM5_OK)
+            goto done;
+        have_pol = 1;
 
-       ret = krb5_dbe_lookup_last_pwd_change(handle->context,
-                                             &kdb, &last_pwd);
-       if (ret)
-            goto done;
+        ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
+        if (ret)
+            goto done;
 
 #if 0
-        /*
-         * The spec says this check is overridden if the caller has
-         * modify privilege.  The admin server therefore makes this
-         * check itself (in chpass_principal_wrapper, misc.c).  A
-         * local caller implicitly has all authorization bits.
-         */
-       if((now - last_pwd) < pol.pw_min_life &&
-          !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
-            ret = KADM5_PASS_TOOSOON;
-            goto done;
-       }
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c).  A
+         * local caller implicitly has all authorization bits.
+         */
+        if((now - last_pwd) < pol.pw_min_life &&
+           !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+        }
 #endif
 
-       if(pol.pw_history_num > 1) {
-           if(adb.admin_history_kvno != hist_kvno) {
-               ret = KADM5_BAD_HIST_KEY;
-               goto done;
-           }
-
-           ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
-                                kdb.n_key_data, kdb.key_data,
-                                adb.old_key_len, adb.old_keys);
-           if (ret)
-               goto done;
-       }
-       if (pol.pw_max_life)
-          kdb.pw_expiration = now + pol.pw_max_life;
-       else
-          kdb.pw_expiration = 0;
+        if (pol.pw_max_life)
+            kdb->pw_expiration = now + pol.pw_max_life;
+        else
+            kdb->pw_expiration = 0;
     } else {
-       kdb.pw_expiration = 0;
+        kdb->pw_expiration = 0;
     }
 
-    ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
+    ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
     if (ret)
-        goto done;
+        goto done;
+
+    /* unlock principal on this KDC */
+    kdb->fail_auth_count = 0;
 
     if (keyblocks) {
-        if (handle->api_version == KADM5_API_VERSION_1) {
-             /* Version 1 clients will expect to see a DES_CRC enctype. */
-            ret = krb5_dbe_find_enctype(handle->context, &kdb,
-                                        ENCTYPE_DES_CBC_CRC,
-                                        -1, -1, &key_data);
-            if (ret)
-                goto done;
-
-            ret = decrypt_key_data(handle->context, act_mkey, 1, key_data,
-                                    keyblocks, NULL);
-            if (ret)
-                goto done;
-        } else {
-            ret = decrypt_key_data(handle->context, act_mkey,
-                                    kdb.n_key_data, kdb.key_data,
-                                    keyblocks, n_keys);
-            if (ret)
-                goto done;
-        }
+        ret = decrypt_key_data(handle->context,
+                               kdb->n_key_data, kdb->key_data,
+                               keyblocks, n_keys);
+        if (ret)
+            goto done;
     }
 
     /* key data changed, let the database provider know */
-    kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */;
+    kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
+    /* | KADM5_RANDKEY_USED */;
 
-    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
-       goto done;
+    ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+                               KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
+                               n_ks_tuple, ks_tuple, NULL);
+    if (ret)
+        goto done;
+    if ((ret = kdb_put_entry(handle, kdb, &adb)))
+        goto done;
 
+    (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+                                KADM5_HOOK_STAGE_POSTCOMMIT, principal,
+                                keepold, n_ks_tuple, ks_tuple, NULL);
     ret = KADM5_OK;
 done:
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
     if (have_pol)
-        kadm5_free_policy_ent(handle->lhandle, &pol);
+        kadm5_free_policy_ent(handle->lhandle, &pol);
 
     return ret;
 }
@@ -1670,19 +1677,19 @@ done:
  */
 kadm5_ret_t
 kadm5_setv4key_principal(void *server_handle,
-                      krb5_principal principal,
-                      krb5_keyblock *keyblock)
+                         krb5_principal principal,
+                         krb5_keyblock *keyblock)
 {
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
-    krb5_int32                 now;
-    kadm5_policy_ent_rec       pol;
-    krb5_keysalt               keysalt;
-    int                                i, k, kvno, ret, have_pol = 0;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
+    krb5_int32                  now;
+    kadm5_policy_ent_rec        pol;
+    krb5_keysalt                keysalt;
+    int                         i, k, kvno, ret, have_pol = 0;
 #if 0
     int                         last_pwd;
 #endif
-    kadm5_server_handle_t      handle = server_handle;
+    kadm5_server_handle_t       handle = server_handle;
     krb5_key_data               tmp_key_data;
     krb5_keyblock               *act_mkey;
 
@@ -1693,180 +1700,165 @@ kadm5_setv4key_principal(void *server_handle,
     krb5_clear_error_message(handle->context);
 
     if (principal == NULL || keyblock == NULL)
-       return EINVAL;
+        return EINVAL;
     if (hist_princ && /* this will be NULL when initializing the databse */
-       ((krb5_principal_compare(handle->context,
-                                principal, hist_princ)) == TRUE))
-       return KADM5_PROTECT_PRINCIPAL;
+        ((krb5_principal_compare(handle->context,
+                                 principal, hist_princ)) == TRUE))
+        return KADM5_PROTECT_PRINCIPAL;
 
     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
-       return KADM5_SETV4KEY_INVAL_ENCTYPE;
+        return KADM5_SETV4KEY_INVAL_ENCTYPE;
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+        return(ret);
 
-    for (kvno = 0, i=0; i<kdb.n_key_data; i++)
-        if (kdb.key_data[i].key_data_kvno > kvno)
-             kvno = kdb.key_data[i].key_data_kvno;
+    for (kvno = 0, i=0; i<kdb->n_key_data; i++)
+        if (kdb->key_data[i].key_data_kvno > kvno)
+            kvno = kdb->key_data[i].key_data_kvno;
 
-    if (kdb.key_data != NULL)
-        cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
+    if (kdb->key_data != NULL)
+        cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
 
-    kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
-    if (kdb.key_data == NULL)
-        return ENOMEM;
-    memset(kdb.key_data, 0, sizeof(krb5_key_data));
-    kdb.n_key_data = 1;
+    kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
+    if (kdb->key_data == NULL)
+        return ENOMEM;
+    memset(kdb->key_data, 0, sizeof(krb5_key_data));
+    kdb->n_key_data = 1;
     keysalt.type = KRB5_KDB_SALTTYPE_V4;
     /* XXX data.magic? */
     keysalt.data.length = 0;
     keysalt.data.data = NULL;
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                active_mkey_list, NULL, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                 &act_mkey);
     if (ret)
-       goto done;
+        goto done;
 
     /* use tmp_key_data as temporary location and reallocate later */
-    ret = krb5_dbekd_encrypt_key_data(handle->context, act_mkey,
-                                     keyblock, &keysalt, kvno + 1,
-                                     &tmp_key_data);
+    ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
+                                    &keysalt, kvno + 1, &tmp_key_data);
     if (ret) {
-       goto done;
+        goto done;
     }
 
     for (k = 0; k < tmp_key_data.key_data_ver; k++) {
-       kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
-       kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
-       if (tmp_key_data.key_data_contents[k]) {
-           kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
-           if (kdb.key_data->key_data_contents[k] == NULL) {
-               cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
-               kdb.key_data = NULL;
-               kdb.n_key_data = 0;
-               ret = ENOMEM;
-               goto done;
-           }
-           memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
+        kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
+        kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
+        if (tmp_key_data.key_data_contents[k]) {
+            kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
+            if (kdb->key_data->key_data_contents[k] == NULL) {
+                cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
+                kdb->key_data = NULL;
+                kdb->n_key_data = 0;
+                ret = ENOMEM;
+                goto done;
+            }
+            memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
 
-           memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
-           free (tmp_key_data.key_data_contents[k]);
-           tmp_key_data.key_data_contents[k] = NULL;
-       }
+            memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
+            free (tmp_key_data.key_data_contents[k]);
+            tmp_key_data.key_data_contents[k] = NULL;
+        }
     }
 
 
 
-    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     ret = krb5_timeofday(handle->context, &now);
     if (ret)
-       goto done;
+        goto done;
 
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
-                                   &pol)) != KADM5_OK)
-          goto done;
-       have_pol = 1;
+        if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+                                    &pol)) != KADM5_OK)
+            goto done;
+        have_pol = 1;
 
 #if 0
-       /*
-         * The spec says this check is overridden if the caller has
-         * modify privilege.  The admin server therefore makes this
-         * check itself (in chpass_principal_wrapper, misc.c).  A
-         * local caller implicitly has all authorization bits.
-         */
-       if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
-                                                 &kdb, &last_pwd))
-            goto done;
-       if((now - last_pwd) < pol.pw_min_life &&
-          !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
-            ret = KADM5_PASS_TOOSOON;
-            goto done;
-       }
-#endif
-#if 0
-       /*
-        * Should we be checking/updating pw history here?
-        */
-       if(pol.pw_history_num > 1) {
-           if(adb.admin_history_kvno != hist_kvno) {
-               ret = KADM5_BAD_HIST_KEY;
-               goto done;
-           }
-
-           if (ret = check_pw_reuse(handle->context,
-                                    &hist_key,
-                                    kdb.n_key_data, kdb.key_data,
-                                    adb.old_key_len, adb.old_keys))
-               goto done;
-       }
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c).  A
+         * local caller implicitly has all authorization bits.
+         */
+        if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+                                                  kdb, &last_pwd))
+            goto done;
+        if((now - last_pwd) < pol.pw_min_life &&
+           !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+        }
 #endif
 
-       if (pol.pw_max_life)
-          kdb.pw_expiration = now + pol.pw_max_life;
-       else
-          kdb.pw_expiration = 0;
+        if (pol.pw_max_life)
+            kdb->pw_expiration = now + pol.pw_max_life;
+        else
+            kdb->pw_expiration = 0;
     } else {
-       kdb.pw_expiration = 0;
+        kdb->pw_expiration = 0;
     }
 
-    ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
+    ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
     if (ret)
-        goto done;
+        goto done;
 
-    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
-       goto done;
+    /* unlock principal on this KDC */
+    kdb->fail_auth_count = 0;
+
+    if ((ret = kdb_put_entry(handle, kdb, &adb)))
+        goto done;
 
     ret = KADM5_OK;
 done:
     for (i = 0; i < tmp_key_data.key_data_ver; i++) {
-       if (tmp_key_data.key_data_contents[i]) {
-           memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
-           free (tmp_key_data.key_data_contents[i]);
-       }
+        if (tmp_key_data.key_data_contents[i]) {
+            memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
+            free (tmp_key_data.key_data_contents[i]);
+        }
     }
 
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
     if (have_pol)
-        kadm5_free_policy_ent(handle->lhandle, &pol);
+        kadm5_free_policy_ent(handle->lhandle, &pol);
 
     return ret;
 }
 
 kadm5_ret_t
 kadm5_setkey_principal(void *server_handle,
-                      krb5_principal principal,
-                      krb5_keyblock *keyblocks,
-                      int n_keys)
+                       krb5_principal principal,
+                       krb5_keyblock *keyblocks,
+                       int n_keys)
 {
     return
-       kadm5_setkey_principal_3(server_handle, principal,
-                                FALSE, 0, NULL,
-                                keyblocks, n_keys);
+        kadm5_setkey_principal_3(server_handle, principal,
+                                 FALSE, 0, NULL,
+                                 keyblocks, n_keys);
 }
 
 kadm5_ret_t
 kadm5_setkey_principal_3(void *server_handle,
-                        krb5_principal principal,
-                        krb5_boolean keepold,
-                        int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
-                        krb5_keyblock *keyblocks,
-                        int n_keys)
+                         krb5_principal principal,
+                         krb5_boolean keepold,
+                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                         krb5_keyblock *keyblocks,
+                         int n_keys)
 {
-    krb5_db_entry              kdb;
-    osa_princ_ent_rec          adb;
-    krb5_int32                 now;
-    kadm5_policy_ent_rec       pol;
-    krb5_key_data              *old_key_data;
-    int                                n_old_keys;
-    int                                i, j, k, kvno, ret, have_pol = 0;
+    krb5_db_entry               *kdb;
+    osa_princ_ent_rec           adb;
+    krb5_int32                  now;
+    kadm5_policy_ent_rec        pol;
+    krb5_key_data               *old_key_data;
+    int                         n_old_keys;
+    int                         i, j, k, kvno, ret, have_pol = 0;
 #if 0
     int                         last_pwd;
 #endif
-    kadm5_server_handle_t      handle = server_handle;
-    krb5_boolean               similar;
-    krb5_keysalt               keysalt;
+    kadm5_server_handle_t       handle = server_handle;
+    krb5_boolean                similar;
+    krb5_keysalt                keysalt;
     krb5_key_data         tmp_key_data;
     krb5_key_data        *tptr;
     krb5_keyblock               *act_mkey;
@@ -1876,190 +1868,174 @@ kadm5_setkey_principal_3(void *server_handle,
     krb5_clear_error_message(handle->context);
 
     if (principal == NULL || keyblocks == NULL)
-       return EINVAL;
+        return EINVAL;
     if (hist_princ && /* this will be NULL when initializing the databse */
-       ((krb5_principal_compare(handle->context,
-                                principal, hist_princ)) == TRUE))
-       return KADM5_PROTECT_PRINCIPAL;
+        ((krb5_principal_compare(handle->context,
+                                 principal, hist_princ)) == TRUE))
+        return KADM5_PROTECT_PRINCIPAL;
 
     for (i = 0; i < n_keys; i++) {
-       for (j = i+1; j < n_keys; j++) {
-           if ((ret = krb5_c_enctype_compare(handle->context,
-                                             keyblocks[i].enctype,
-                                             keyblocks[j].enctype,
-                                             &similar)))
-               return(ret);
-           if (similar) {
-               if (n_ks_tuple) {
-                   if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
-                       return KADM5_SETKEY_DUP_ENCTYPES;
-               } else
-                   return KADM5_SETKEY_DUP_ENCTYPES;
-           }
-       }
+        for (j = i+1; j < n_keys; j++) {
+            if ((ret = krb5_c_enctype_compare(handle->context,
+                                              keyblocks[i].enctype,
+                                              keyblocks[j].enctype,
+                                              &similar)))
+                return(ret);
+            if (similar) {
+                if (n_ks_tuple) {
+                    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
+                        return KADM5_SETKEY_DUP_ENCTYPES;
+                } else
+                    return KADM5_SETKEY_DUP_ENCTYPES;
+            }
+        }
     }
 
     if (n_ks_tuple && n_ks_tuple != n_keys)
-       return KADM5_SETKEY3_ETYPE_MISMATCH;
+        return KADM5_SETKEY3_ETYPE_MISMATCH;
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+        return(ret);
 
-    for (kvno = 0, i=0; i<kdb.n_key_data; i++)
-        if (kdb.key_data[i].key_data_kvno > kvno)
-             kvno = kdb.key_data[i].key_data_kvno;
+    for (kvno = 0, i=0; i<kdb->n_key_data; i++)
+        if (kdb->key_data[i].key_data_kvno > kvno)
+            kvno = kdb->key_data[i].key_data_kvno;
 
     if (keepold) {
-       old_key_data = kdb.key_data;
-       n_old_keys = kdb.n_key_data;
+        old_key_data = kdb->key_data;
+        n_old_keys = kdb->n_key_data;
     } else {
-       if (kdb.key_data != NULL)
-           cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
-       n_old_keys = 0;
-       old_key_data = NULL;
+        if (kdb->key_data != NULL)
+            cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
+        n_old_keys = 0;
+        old_key_data = NULL;
     }
 
-    kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
-                                                *sizeof(krb5_key_data));
-    if (kdb.key_data == NULL) {
-       ret = ENOMEM;
-       goto done;
+    kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
+                                                  *sizeof(krb5_key_data));
+    if (kdb->key_data == NULL) {
+        ret = ENOMEM;
+        goto done;
     }
 
-    memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
-    kdb.n_key_data = 0;
+    memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
+    kdb->n_key_data = 0;
 
     for (i = 0; i < n_keys; i++) {
-       if (n_ks_tuple) {
-           keysalt.type = ks_tuple[i].ks_salttype;
-           keysalt.data.length = 0;
-           keysalt.data.data = NULL;
-           if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
-               ret = KADM5_SETKEY3_ETYPE_MISMATCH;
-               goto done;
-           }
-       }
-       memset (&tmp_key_data, 0, sizeof(tmp_key_data));
-
-       ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                    active_mkey_list, NULL, &act_mkey);
-       if (ret)
-           goto done;
-
-       ret = krb5_dbekd_encrypt_key_data(handle->context,
-                                         act_mkey,
-                                         &keyblocks[i],
-                                         n_ks_tuple ? &keysalt : NULL,
-                                         kvno + 1,
-                                         &tmp_key_data);
-       if (ret)
-           goto done;
-
-       tptr = &kdb.key_data[i];
-       tptr->key_data_ver = tmp_key_data.key_data_ver;
-       tptr->key_data_kvno = tmp_key_data.key_data_kvno;
-       for (k = 0; k < tmp_key_data.key_data_ver; k++) {
-           tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
-           tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
-           if (tmp_key_data.key_data_contents[k]) {
-               tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
-               if (tptr->key_data_contents[k] == NULL) {
-                   int i1;
-                   for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
-                       if (tmp_key_data.key_data_contents[i1]) {
-                           memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
-                           free (tmp_key_data.key_data_contents[i1]);
-                       }
-                   }
-
-                   ret =  ENOMEM;
-                   goto done;
-               }
-               memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
-
-               memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
-               free (tmp_key_data.key_data_contents[k]);
-               tmp_key_data.key_data_contents[k] = NULL;
-           }
-       }
-       kdb.n_key_data++;
+        if (n_ks_tuple) {
+            keysalt.type = ks_tuple[i].ks_salttype;
+            keysalt.data.length = 0;
+            keysalt.data.data = NULL;
+            if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
+                ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+                goto done;
+            }
+        }
+        memset (&tmp_key_data, 0, sizeof(tmp_key_data));
+
+        ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                     &act_mkey);
+        if (ret)
+            goto done;
+
+        ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
+                                        &keyblocks[i],
+                                        n_ks_tuple ? &keysalt : NULL, kvno + 1,
+                                        &tmp_key_data);
+        if (ret)
+            goto done;
+
+        tptr = &kdb->key_data[i];
+        tptr->key_data_ver = tmp_key_data.key_data_ver;
+        tptr->key_data_kvno = tmp_key_data.key_data_kvno;
+        for (k = 0; k < tmp_key_data.key_data_ver; k++) {
+            tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
+            tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
+            if (tmp_key_data.key_data_contents[k]) {
+                tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
+                if (tptr->key_data_contents[k] == NULL) {
+                    int i1;
+                    for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
+                        if (tmp_key_data.key_data_contents[i1]) {
+                            memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
+                            free (tmp_key_data.key_data_contents[i1]);
+                        }
+                    }
+
+                    ret =  ENOMEM;
+                    goto done;
+                }
+                memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
+
+                memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
+                free (tmp_key_data.key_data_contents[k]);
+                tmp_key_data.key_data_contents[k] = NULL;
+            }
+        }
+        kdb->n_key_data++;
     }
 
     /* copy old key data if necessary */
     for (i = 0; i < n_old_keys; i++) {
-       kdb.key_data[i+n_keys] = old_key_data[i];
-       memset(&old_key_data[i], 0, sizeof (krb5_key_data));
-       kdb.n_key_data++;
+        kdb->key_data[i+n_keys] = old_key_data[i];
+        memset(&old_key_data[i], 0, sizeof (krb5_key_data));
+        kdb->n_key_data++;
     }
 
     if (old_key_data)
-       krb5_db_free(handle->context, old_key_data);
+        krb5_db_free(handle->context, old_key_data);
 
-    /* assert(kdb.n_key_data == n_keys + n_old_keys) */
-    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+    /* assert(kdb->n_key_data == n_keys + n_old_keys) */
+    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     if ((ret = krb5_timeofday(handle->context, &now)))
-       goto done;
+        goto done;
 
     if ((adb.aux_attributes & KADM5_POLICY)) {
-       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
-                                   &pol)) != KADM5_OK)
-          goto done;
-       have_pol = 1;
+        if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+                                    &pol)) != KADM5_OK)
+            goto done;
+        have_pol = 1;
 
 #if 0
-       /*
-         * The spec says this check is overridden if the caller has
-         * modify privilege.  The admin server therefore makes this
-         * check itself (in chpass_principal_wrapper, misc.c).  A
-         * local caller implicitly has all authorization bits.
-         */
-       if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
-                                                 &kdb, &last_pwd))
-            goto done;
-       if((now - last_pwd) < pol.pw_min_life &&
-          !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
-            ret = KADM5_PASS_TOOSOON;
-            goto done;
-       }
-#endif
-#if 0
-       /*
-        * Should we be checking/updating pw history here?
-        */
-       if (pol.pw_history_num > 1) {
-           if(adb.admin_history_kvno != hist_kvno) {
-               ret = KADM5_BAD_HIST_KEY;
-               goto done;
-           }
-
-           if (ret = check_pw_reuse(handle->context,
-                                    &hist_key,
-                                    kdb.n_key_data, kdb.key_data,
-                                    adb.old_key_len, adb.old_keys))
-               goto done;
-       }
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c).  A
+         * local caller implicitly has all authorization bits.
+         */
+        if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+                                                  kdb, &last_pwd))
+            goto done;
+        if((now - last_pwd) < pol.pw_min_life &&
+           !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+        }
 #endif
 
-       if (pol.pw_max_life)
-          kdb.pw_expiration = now + pol.pw_max_life;
-       else
-          kdb.pw_expiration = 0;
+        if (pol.pw_max_life)
+            kdb->pw_expiration = now + pol.pw_max_life;
+        else
+            kdb->pw_expiration = 0;
     } else {
-       kdb.pw_expiration = 0;
+        kdb->pw_expiration = 0;
     }
 
-    if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
+    if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
         goto done;
 
-    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
-       goto done;
+    /* unlock principal on this KDC */
+    kdb->fail_auth_count = 0;
+
+    if ((ret = kdb_put_entry(handle, kdb, &adb)))
+        goto done;
 
     ret = KADM5_OK;
 done:
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
     if (have_pol)
-        kadm5_free_policy_ent(handle->lhandle, &pol);
+        kadm5_free_policy_ent(handle->lhandle, &pol);
 
     return ret;
 }
@@ -2074,15 +2050,13 @@ kadm5_get_principal_keys(void *server_handle /* IN */,
                          krb5_keyblock **keyblocks /* OUT */,
                          int *n_keys /* OUT */)
 {
-    krb5_db_entry               kdb;
+    krb5_db_entry               *kdb;
     osa_princ_ent_rec           adb;
-    krb5_key_data               *key_data;
     kadm5_ret_t                 ret;
     kadm5_server_handle_t       handle = server_handle;
-    krb5_keyblock               *mkey_ptr;
 
     if (keyblocks)
-         *keyblocks = NULL;
+        *keyblocks = NULL;
 
     CHECK_HANDLE(server_handle);
 
@@ -2090,50 +2064,19 @@ kadm5_get_principal_keys(void *server_handle /* IN */,
         return EINVAL;
 
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
-       return(ret);
+        return(ret);
 
     if (keyblocks) {
-       if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb,
-                                      &mkey_ptr))) {
-            krb5_keylist_node *tmp_mkey_list;
-            /* try refreshing master key list */
-            /* XXX it would nice if we had the mkvno here for optimization */
-            if (krb5_db_fetch_mkey_list(handle->context, master_princ,
-                                        &master_keyblock, 0,
-                                        &tmp_mkey_list) == 0) {
-                krb5_dbe_free_key_list(handle->context, master_keylist);
-                master_keylist = tmp_mkey_list;
-                if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
-                                              &kdb, &mkey_ptr))) {
-                    goto done;
-                }
-            } else {
-                goto done;
-            }
-        }
-
-         if (handle->api_version == KADM5_API_VERSION_1) {
-              /* Version 1 clients will expect to see a DES_CRC enctype. */
-              if ((ret = krb5_dbe_find_enctype(handle->context, &kdb,
-                                              ENCTYPE_DES_CBC_CRC,
-                                              -1, -1, &key_data)))
-                   goto done;
-
-              if ((ret = decrypt_key_data(handle->context, mkey_ptr, 1, key_data,
-                                         keyblocks, NULL)))
-                   goto done;
-         } else {
-              ret = decrypt_key_data(handle->context, mkey_ptr,
-                                     kdb.n_key_data, kdb.key_data,
-                                     keyblocks, n_keys);
-              if (ret)
-                   goto done;
-         }
+        ret = decrypt_key_data(handle->context,
+                               kdb->n_key_data, kdb->key_data,
+                               keyblocks, n_keys);
+        if (ret)
+            goto done;
     }
 
     ret = KADM5_OK;
 done:
-    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, kdb, &adb);
 
     return ret;
 }
@@ -2141,45 +2084,44 @@ done:
 
 /*
  * Allocate an array of n_key_data krb5_keyblocks, fill in each
- * element with the results of decrypting the nth key in key_data with
- * mkey, and if n_keys is not NULL fill it in with the
+ * element with the results of decrypting the nth key in key_data,
+ * and if n_keys is not NULL fill it in with the
  * number of keys decrypted.
  */
-static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
-                           int n_key_data, krb5_key_data *key_data,
-                           krb5_keyblock **keyblocks, int *n_keys)
+static int decrypt_key_data(krb5_context context,
+                            int n_key_data, krb5_key_data *key_data,
+                            krb5_keyblock **keyblocks, int *n_keys)
 {
-     krb5_keyblock *keys;
-     int ret, i;
-
-     keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
-     if (keys == NULL)
-         return ENOMEM;
-     memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
-
-     for (i = 0; i < n_key_data; i++) {
-          ret = krb5_dbekd_decrypt_key_data(context, mkey,
-                                           &key_data[i],
-                                           &keys[i], NULL);
-         if (ret) {
-              for (; i >= 0; i--) {
-                  if (keys[i].contents) {
-                      memset (keys[i].contents, 0, keys[i].length);
-                      free( keys[i].contents );
-                  }
-              }
-
-              memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
-              free(keys);
-              return ret;
-         }
-     }
-
-     *keyblocks = keys;
-     if (n_keys)
-         *n_keys = n_key_data;
-
-     return 0;
+    krb5_keyblock *keys;
+    int ret, i;
+
+    keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
+    if (keys == NULL)
+        return ENOMEM;
+    memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
+
+    for (i = 0; i < n_key_data; i++) {
+        ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
+                                        NULL);
+        if (ret) {
+            for (; i >= 0; i--) {
+                if (keys[i].contents) {
+                    memset (keys[i].contents, 0, keys[i].length);
+                    free( keys[i].contents );
+                }
+            }
+
+            memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
+            free(keys);
+            return ret;
+        }
+    }
+
+    *keyblocks = keys;
+    if (n_keys)
+        *n_keys = n_key_data;
+
+    return 0;
 }
 
 /*
@@ -2189,15 +2131,15 @@ static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
  *
  * Arguments:
  *
- *     server_handle   (r) kadm5 handle
- *     entry           (r) principal retrieved with kadm5_get_principal
- *     ktype           (r) enctype to search for, or -1 to ignore
- *     stype           (r) salt type to search for, or -1 to ignore
- *     kvno            (r) kvno to search for, -1 for max, 0 for max
- *                     only if it also matches ktype and stype
- *     keyblock        (w) keyblock to fill in
- *     keysalt         (w) keysalt to fill in, or NULL
- *     kvnop           (w) kvno to fill in, or NULL
+ *      server_handle   (r) kadm5 handle
+ *      entry           (r) principal retrieved with kadm5_get_principal
+ *      ktype           (r) enctype to search for, or -1 to ignore
+ *      stype           (r) salt type to search for, or -1 to ignore
+ *      kvno            (r) kvno to search for, -1 for max, 0 for max
+ *                      only if it also matches ktype and stype
+ *      keyblock        (w) keyblock to fill in
+ *      keysalt         (w) keysalt to fill in, or NULL
+ *      kvnop           (w) kvno to fill in, or NULL
  *
  * Effects: Searches the key_data array of entry, which must have been
  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
@@ -2213,10 +2155,10 @@ static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
  * returned.
  */
 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
-                             kadm5_principal_ent_t entry, krb5_int32
-                             ktype, krb5_int32 stype, krb5_int32
-                             kvno, krb5_keyblock *keyblock,
-                             krb5_keysalt *keysalt, int *kvnop)
+                              kadm5_principal_ent_t entry, krb5_int32
+                              ktype, krb5_int32 stype, krb5_int32
+                              kvno, krb5_keyblock *keyblock,
+                              krb5_keysalt *keysalt, int *kvnop)
 {
     kadm5_server_handle_t handle = server_handle;
     krb5_db_entry dbent;
@@ -2227,28 +2169,24 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
     CHECK_HANDLE(server_handle);
 
     if (entry->n_key_data == 0 || entry->key_data == NULL)
-        return EINVAL;
+        return EINVAL;
 
     /* find_enctype only uses these two fields */
     dbent.n_key_data = entry->n_key_data;
     dbent.key_data = entry->key_data;
     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
-                                   stype, kvno, &key_data)))
-        return ret;
+                                     stype, kvno, &key_data)))
+        return ret;
 
     /* find_mkey only uses this field */
     dbent.tl_data = entry->tl_data;
-    if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent,
-                                  &mkey_ptr))) {
-        krb5_keylist_node *tmp_mkey_list;
+    if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
         /* try refreshing master key list */
         /* XXX it would nice if we had the mkvno here for optimization */
         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
-                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
-            krb5_dbe_free_key_list(handle->context, master_keylist);
-            master_keylist = tmp_mkey_list;
-            if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
-                                          &dbent, &mkey_ptr))) {
+                                    &master_keyblock) == 0) {
+            if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
+                                          &mkey_ptr))) {
                 return ret;
             }
         } else {
@@ -2256,10 +2194,9 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
         }
     }
 
-    if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
-                                          mkey_ptr, key_data,
-                                          keyblock, keysalt)))
-        return ret;
+    if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
+                                         keyblock, keysalt)))
+        return ret;
 
     /*
      * Coerce the enctype of the output keyblock in case we got an
@@ -2270,7 +2207,121 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
         keyblock->enctype = ktype;
 
     if (kvnop)
-        *kvnop = key_data->key_data_kvno;
+        *kvnop = key_data->key_data_kvno;
 
     return KADM5_OK;
 }
+
+kadm5_ret_t
+kadm5_purgekeys(void *server_handle,
+                krb5_principal principal,
+                int keepkvno)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+    krb5_key_data *old_keydata;
+    int n_old_keydata;
+    int i, j, k;
+
+    CHECK_HANDLE(server_handle);
+
+    if (principal == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, &adb);
+    if (ret)
+        return(ret);
+
+    if (keepkvno <= 0) {
+        keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
+                                             kdb->key_data);
+    }
+
+    old_keydata = kdb->key_data;
+    n_old_keydata = kdb->n_key_data;
+    kdb->n_key_data = 0;
+    kdb->key_data = krb5_db_alloc(handle->context, NULL,
+                                  n_old_keydata * sizeof(krb5_key_data));
+    if (kdb->key_data == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
+    for (i = 0, j = 0; i < n_old_keydata; i++) {
+        if (old_keydata[i].key_data_kvno < keepkvno)
+            continue;
+
+        /* Alias the key_data_contents pointers; we null them out in the
+         * source array immediately after. */
+        kdb->key_data[j] = old_keydata[i];
+        for (k = 0; k < old_keydata[i].key_data_ver; k++) {
+            old_keydata[i].key_data_contents[k] = NULL;
+        }
+        j++;
+    }
+    kdb->n_key_data = j;
+    cleanup_key_data(handle->context, n_old_keydata, old_keydata);
+
+    kdb->mask = KADM5_KEY_DATA;
+    ret = kdb_put_entry(handle, kdb, &adb);
+    if (ret)
+        goto done;
+
+done:
+    kdb_free_entry(handle, kdb, &adb);
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_get_strings(void *server_handle, krb5_principal principal,
+                  krb5_string_attr **strings_out, int *count_out)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb = NULL;
+
+    *strings_out = NULL;
+    *count_out = 0;
+    CHECK_HANDLE(server_handle);
+    if (principal == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, NULL);
+    if (ret)
+        return ret;
+
+    ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
+    kdb_free_entry(handle, kdb, NULL);
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_set_string(void *server_handle, krb5_principal principal,
+                 const char *key, const char *value)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+
+    CHECK_HANDLE(server_handle);
+    if (principal == NULL || key == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, &adb);
+    if (ret)
+        return ret;
+
+    ret = krb5_dbe_set_string(handle->context, kdb, key, value);
+    if (ret)
+        goto done;
+
+    kdb->mask = KADM5_TL_DATA;
+    ret = kdb_put_entry(handle, kdb, &adb);
+
+done:
+    kdb_free_entry(handle, kdb, &adb);
+    return ret;
+}