svn rev #21721: branches/mkey_migrate/src/ include/ include/krb5/ kadmin/cli/ ...

wfiveash@MIT.EDU wfiveash at MIT.EDU
Fri Jan 9 15:12:04 EST 2009


http://src.mit.edu/fisheye/changelog/krb5/?cs=21721
Commit By: wfiveash
Log Message:
First commit.  This project is not completely finished (the list_mkeys,
purge_mkeys, sync_stash and update_princ_encryption still need to be
written) so the purpose of this commit is to allow early review of the
addition of support for > 1 master key and the "active" master keylist.
This commit does not include any changes required to sync this level of
the branch with the current level of the trunk.  That will follow this
commit.



Changed Files:
U   branches/mkey_migrate/src/include/k5-int.h
U   branches/mkey_migrate/src/include/kdb.h
U   branches/mkey_migrate/src/include/krb5/krb5.hin
U   branches/mkey_migrate/src/kadmin/cli/kadmin.c
U   branches/mkey_migrate/src/kadmin/dbutil/Makefile.in
U   branches/mkey_migrate/src/kadmin/dbutil/dump.c
U   branches/mkey_migrate/src/kadmin/dbutil/kdb5_create.c
A   branches/mkey_migrate/src/kadmin/dbutil/kdb5_mkey.c
U   branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.c
U   branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.h
U   branches/mkey_migrate/src/kadmin/server/ovsec_kadmd.c
U   branches/mkey_migrate/src/kdc/do_as_req.c
U   branches/mkey_migrate/src/kdc/do_tgs_req.c
U   branches/mkey_migrate/src/kdc/extern.h
U   branches/mkey_migrate/src/kdc/kdc_preauth.c
U   branches/mkey_migrate/src/kdc/kdc_util.c
U   branches/mkey_migrate/src/kdc/main.c
U   branches/mkey_migrate/src/lib/kadm5/admin.h
U   branches/mkey_migrate/src/lib/kadm5/misc_free.c
U   branches/mkey_migrate/src/lib/kadm5/server_internal.h
U   branches/mkey_migrate/src/lib/kadm5/srv/libkadm5srv.exports
U   branches/mkey_migrate/src/lib/kadm5/srv/server_kdb.c
U   branches/mkey_migrate/src/lib/kadm5/srv/svr_principal.c
U   branches/mkey_migrate/src/lib/kdb/kdb5.c
U   branches/mkey_migrate/src/lib/kdb/kdb5.h
U   branches/mkey_migrate/src/lib/kdb/kdb_cpw.c
U   branches/mkey_migrate/src/lib/kdb/kdb_default.c
U   branches/mkey_migrate/src/lib/kdb/keytab.c
U   branches/mkey_migrate/src/lib/kdb/libkdb5.exports
U   branches/mkey_migrate/src/lib/krb5/error_tables/kdb5_err.et
U   branches/mkey_migrate/src/lib/krb5/krb/kfree.c
U   branches/mkey_migrate/src/plugins/kdb/db2/db2_exp.c
U   branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.c
U   branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.h
U   branches/mkey_migrate/src/plugins/kdb/ldap/ldap_exp.c
U   branches/mkey_migrate/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
Modified: branches/mkey_migrate/src/include/k5-int.h
===================================================================
--- branches/mkey_migrate/src/include/k5-int.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/include/k5-int.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -1140,6 +1140,11 @@
 void KRB5_CALLCONV krb5_free_pa_enc_ts
 	(krb5_context, krb5_pa_enc_ts *);
 
+#include "kdb.h"
+
+void KRB5_CALLCONV krb5_free_key_data_contents
+	(krb5_context, krb5_key_data *);
+
 /* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
 #include "com_err.h"
 #include "k5-plugin.h"

Modified: branches/mkey_migrate/src/include/kdb.h
===================================================================
--- branches/mkey_migrate/src/include/kdb.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/include/kdb.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -171,8 +171,47 @@
 #define KRB5_TL_DB_ARGS                 0x7fff
 #endif /* SECURID */
 #define KRB5_TL_USER_CERTIFICATE        0x0007
-    
+#define KRB5_TL_MKVNO                   0x0008
+#define KRB5_TL_ACTKVNO                 0x0009
+#define KRB5_TL_MKEY_AUX                0x000a
+
+/* version number for KRB5_TL_ACTKVNO data */
+#define KRB5_TL_ACTKVNO_VER_1	1
+
+/* version number for KRB5_TL_MKEY_AUX data */
+#define KRB5_TL_MKEY_AUX_VER_1	1
+
+/* XXX WAF: the indef. struct typedefs below may not be needed so ifdef'ing them
+ * out for now */
 /*
+ * Doing this because it this struct should be defined in k5-int.h but this
+ * header file can't include that one.
+ */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+#ifndef _KRB5_INT_H
+struct _krb5_actkvno_node;
+typedef struct _krb5_actkvno_node krb5_actkvno_node;
+struct _krb5_mkey_aux_node;
+typedef struct _krb5_mkey_aux_node krb5_mkey_aux_node;
+#endif
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+typedef struct _krb5_actkvno_node {
+    struct _krb5_actkvno_node *next;
+    krb5_kvno act_kvno;
+    krb5_timestamp act_time;
+} krb5_actkvno_node;
+
+typedef struct _krb5_mkey_aux_node {
+    struct _krb5_mkey_aux_node *next;
+    krb5_kvno	  mkey_kvno; /* kvno of mkey protecting the latest_mkey */
+    /* XXX WAF: maybe krb5_data isn't adequate, the kvno and enctype is needed I
+     * think.  Perhaps krb5_key_data should be used?
+     */
+    krb5_key_data    latest_mkey; /* most recent mkey */
+} krb5_mkey_aux_node;
+
+/*
  * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
  * on the principal.
  */
@@ -294,6 +333,17 @@
                                             krb5_kvno      kvno,
 					    krb5_keyblock  *mkey );
 krb5_error_code
+krb5_db_fetch_mkey_list( krb5_context    context,
+		         krb5_principal  mname,
+		         const krb5_keyblock * mkey,
+		         krb5_kvno             mkvno,
+		         krb5_keyblock_node  **mkeys_list );
+
+krb5_error_code
+krb5_db_free_mkey_list( krb5_context         context,
+		        krb5_keyblock_node  *mkey_list );
+
+krb5_error_code
 krb5_dbe_find_enctype( krb5_context	kcontext,
 		       krb5_db_entry	*dbentp,
 		       krb5_int32		ktype,
@@ -333,15 +383,60 @@
 			     krb5_key_data	        * key_data);
 
 krb5_error_code
+krb5_dbe_fetch_act_mkey_list(krb5_context        context,
+			     krb5_principal      mprinc,
+			     krb5_actkvno_node  **act_mkey_list);
+
+krb5_error_code
+krb5_dbe_find_act_mkey( krb5_context          context,
+                        krb5_keyblock_node  * mkey_list,
+                        krb5_actkvno_node   * act_mkey_list,
+                        krb5_keyblock      ** act_mkey);
+
+krb5_error_code
+krb5_dbe_find_mkey( krb5_context	 context,
+                    krb5_keyblock_node * mkey_list,
+                    krb5_db_entry      * entry,
+                    krb5_keyblock      ** mkey);
+
+krb5_error_code
+krb5_dbe_lookup_mkvno( krb5_context    context,
+		       krb5_db_entry * entry,
+		       krb5_kvno     * mkvno);
+
+krb5_error_code
 krb5_dbe_lookup_mod_princ_data( krb5_context          context,
 				krb5_db_entry       * entry,
 				krb5_timestamp      * mod_time,
 				krb5_principal      * mod_princ);
  
+krb5_error_code
+krb5_dbe_lookup_mkey_aux( krb5_context         context,
+		          krb5_db_entry      * entry,
+		          krb5_mkey_aux_node ** mkey_aux_data_list);
+krb5_error_code
+krb5_dbe_update_mkvno( krb5_context    context,
+		       krb5_db_entry * entry,
+		       krb5_kvno       mkvno);
 
 krb5_error_code
-krb5_dbe_update_last_pwd_change( krb5_context          context,
-				 krb5_db_entry       * entry,
+krb5_dbe_lookup_actkvno( krb5_context         context,
+		         krb5_db_entry      * entry,
+		         krb5_actkvno_node ** actkvno_list);
+
+krb5_error_code
+krb5_dbe_update_mkey_aux( krb5_context          context,
+		          krb5_db_entry       * entry,
+		          krb5_mkey_aux_node  * mkey_aux_data_list);
+
+krb5_error_code
+krb5_dbe_update_actkvno(krb5_context    context,
+			krb5_db_entry * entry,
+			const krb5_actkvno_node *actkvno_list);
+
+krb5_error_code
+krb5_dbe_update_last_pwd_change( krb5_context     context,
+				 krb5_db_entry  * entry,
 				 krb5_timestamp	  stamp);
 
 krb5_error_code
@@ -417,6 +512,12 @@
 	      char 		* passwd,
 	      krb5_db_entry	* db_entry);
 
+int
+get_key_data_kvno( krb5_context	   context,
+		   int		   count,
+		   krb5_key_data * data);
+
+
 /* default functions. Should not be directly called */
 /*
  *   Default functions prototype
@@ -453,13 +554,26 @@
 			    krb5_kvno      kvno,
 			    krb5_keyblock *mkey);
 
+krb5_error_code
+krb5_def_fetch_mkey_list( krb5_context            context,
+			    krb5_principal        mprinc,
+			    const krb5_keyblock  *mkey,
+			    krb5_kvno             mkvno,
+			    krb5_keyblock_node  **mkeys_list);
+
 krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
 				   char *pwd,
 				   krb5_keyblock *key );
 
+krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext,
+				        krb5_keyblock_node *keylist );
+
 krb5_error_code kdb_def_get_mkey ( krb5_context kcontext,
 				   krb5_keyblock **key );
 
+krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext,
+				        krb5_keyblock_node **keylist );
+
 krb5_error_code
 krb5_dbe_def_cpw( krb5_context	  context,
 		  krb5_keyblock       * master_key,

Modified: branches/mkey_migrate/src/include/krb5/krb5.hin
===================================================================
--- branches/mkey_migrate/src/include/krb5/krb5.hin	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/include/krb5/krb5.hin	2009-01-09 20:11:57 UTC (rev 21721)
@@ -334,6 +334,12 @@
     krb5_octet *contents;
 } krb5_keyblock;
 
+typedef struct _krb5_keylist_node {
+       krb5_keyblock keyblock;
+       krb5_kvno     kvno;
+       struct _krb5_keylist_node *next;
+ } krb5_keyblock_node;
+
 #ifdef KRB5_OLD_CRYPTO
 typedef struct _krb5_encrypt_block {
     krb5_magic magic;

Modified: branches/mkey_migrate/src/kadmin/cli/kadmin.c
===================================================================
--- branches/mkey_migrate/src/kadmin/cli/kadmin.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/cli/kadmin.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -1547,6 +1547,8 @@
 	    } else
 		printf("no salt\n");
 	}
+	printf("MKey: vno %d\n",
+	       dprinc.mkvno);
 
 	printf("Attributes:");
 	for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {

Modified: branches/mkey_migrate/src/kadmin/dbutil/Makefile.in
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/Makefile.in	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/Makefile.in	2009-01-09 20:11:57 UTC (rev 21721)
@@ -15,14 +15,18 @@
 ###	kdb5_destroy.o ovload.o import_err.o strtok.o
 ###
 
-SRCS = kdb5_util.c kdb5_create.c kadm5_create.c string_table.c kdb5_destroy.c kdb5_stash.c import_err.c strtok.c dump.c ovload.c
+SRCS = kdb5_util.c kdb5_create.c kadm5_create.c string_table.c kdb5_destroy.c \
+	   kdb5_stash.c import_err.c strtok.c dump.c ovload.c kdb5_mkey.c
 
-OBJS = kdb5_util.o kdb5_create.o kadm5_create.o string_table.o kdb5_destroy.o kdb5_stash.o import_err.o strtok.o dump.o ovload.o
+OBJS = kdb5_util.o kdb5_create.o kadm5_create.o string_table.o kdb5_destroy.o \
+	   kdb5_stash.o import_err.o strtok.o dump.o ovload.o kdb5_mkey.o
 
+GETDATE = ../cli/getdate.o
+
 all:: $(PROG)
 
-$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB4COMPAT_DEPLIBS)
-	$(CC_LINK) -o $(PROG) $(OBJS) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB4COMPAT_LIBS)
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB4COMPAT_DEPLIBS) $(GETDATE)
+	$(CC_LINK) -o $(PROG) $(OBJS) $(GETDATE) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB4COMPAT_LIBS)
 
 import_err.c import_err.h: $(srcdir)/import_err.et
 

Modified: branches/mkey_migrate/src/kadmin/dbutil/dump.c
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/dump.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/dump.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -274,6 +274,7 @@
 
     is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);
 
+    /* XXX WAF: need to fix this! */
     if (is_mkey && db_entry->n_key_data != 1)
 	    fprintf(stderr,
 		    "Master key db entry has %d keys, expecting only 1!\n",

Modified: branches/mkey_migrate/src/kadmin/dbutil/kdb5_create.c
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/kdb5_create.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/kdb5_create.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -230,6 +230,10 @@
 
 	pw_size = 1024;
 	pw_str = malloc(pw_size);
+	if (pw_str == NULL) {
+	    com_err(progname, ENOMEM, "while creating new master key");
+	    exit_status++; return;
+	}
 	
 	retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
 				    pw_str, &pw_size);

Added: branches/mkey_migrate/src/kadmin/dbutil/kdb5_mkey.c
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/kdb5_mkey.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/kdb5_mkey.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -0,0 +1,492 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <k5-int.h>
+#include <kdb.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "kdb5_util.h"
+
+extern krb5_keyblock master_keyblock; /* current mkey */
+extern krb5_principal master_princ;
+extern krb5_data master_salt;
+extern char *mkey_password;
+extern char *progname;
+extern int exit_status;
+extern kadm5_config_params global_params;
+extern krb5_context util_context;
+extern time_t get_date(char *);
+
+static char *strdate(krb5_timestamp when)
+{
+    struct tm *tm;
+    static char out[40];
+
+    time_t lcltim = when;
+    tm = localtime(&lcltim);
+    strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
+    return out;
+}
+
+void
+kdb5_add_mkey(int argc, char *argv[])
+{
+    int optchar;
+    krb5_error_code	retval;
+    char		*mkey_fullname;
+    char		*pw_str = 0;
+    unsigned int	pw_size = 0;
+    int			do_stash = 0, nentries = 0;
+    int			old_key_data_count, i, j;
+    krb5_boolean	more = 0;
+    krb5_data		pwd;
+    krb5_kvno		old_kvno, new_mkey_kvno;
+    krb5_keyblock	new_master_keyblock;
+    krb5_keyblock 	plainkey;
+    krb5_key_data	tmp_key_data, *old_key_data, *key_data;
+    krb5_enctype	new_master_enctype = DEFAULT_KDC_ENCTYPE;
+    char		*new_mkey_password;
+    krb5_db_entry	master_entry;
+    krb5_timestamp	now;
+    krb5_mkey_aux_node  *mkey_aux_data_head, **mkey_aux_data,
+			*cur_mkey_aux_data, *next_mkey_aux_data;
+
+    /*
+     * The command table entry for this command causes open_db_and_mkey() to be
+     * called first to open the KDB and get the current mkey.
+     */
+
+    while ((optchar = getopt(argc, argv, "k:s")) != -1) {
+	switch(optchar) {
+	case 'k':
+	    if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
+		com_err(progname, EINVAL, ": %s is an invalid enctype", optarg);
+		exit_status++;
+		return;
+	    }
+	    break;
+	case 's':
+	    do_stash++;
+	    break;
+	case '?':
+	default:
+	    usage();
+	    return;
+	}
+    }
+    
+    /* assemble & parse the master key name */
+    if ((retval = krb5_db_setup_mkey_name(util_context,
+					  global_params.mkey_name,
+					  global_params.realm,  
+					  &mkey_fullname, &master_princ))) {
+	com_err(progname, retval, "while setting up master key name");
+	exit_status++;
+	return;
+    }
+
+    retval = krb5_db_get_principal(util_context, master_princ, &master_entry, &nentries,
+				   &more);
+    if (retval != 0) {
+	com_err(progname, retval, "while setting up master key name");
+	exit_status++;
+	return;
+    }
+
+    printf("Creating new master key for master key principal '%s'\n",
+	   mkey_fullname);
+
+    printf("You will be prompted for a new database Master Password.\n");
+    printf("It is important that you NOT FORGET this password.\n");
+    fflush(stdout);
+
+    pw_size = 1024;
+    pw_str = malloc(pw_size);
+    if (pw_str == NULL) {
+	com_err(progname, ENOMEM, "while creating new master key");
+	exit_status++;
+	return;
+    }
+    
+    retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+				pw_str, &pw_size);
+    if (retval) {
+	com_err(progname, retval, "while reading new master key from keyboard");
+	exit_status++;
+	return;
+    }
+    new_mkey_password = pw_str;
+
+    pwd.data = new_mkey_password;
+    pwd.length = strlen(new_mkey_password);
+    retval = krb5_principal2salt(util_context, master_princ, &master_salt);
+    if (retval) {
+	com_err(progname, retval, "while calculating master key salt");
+	exit_status++;
+	return;
+    }
+
+    retval = krb5_c_string_to_key(util_context, new_master_enctype, 
+				  &pwd, &master_salt, &new_master_keyblock);
+    if (retval) {
+	com_err(progname, retval, "while transforming master key from password");
+	exit_status++;
+	return;
+    }
+
+    /* First save the old keydata */
+    old_kvno = get_key_data_kvno(util_context, master_entry.n_key_data,
+				 master_entry.key_data);
+    old_key_data_count = master_entry.n_key_data;
+    old_key_data = master_entry.key_data;
+
+    /* alloc enough space to hold new and existing key_data */
+    /*
+     * The encrypted key is malloc'ed by krb5_dbekd_encrypt_key_data and
+     * krb5_key_data key_data_contents is a pointer to this key.  Using some
+     * logic from master_key_convert().
+     */
+    master_entry.key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * (old_key_data_count + 1));
+    if (master_entry.key_data == NULL) {
+	com_err(progname, ENOMEM, "while adding new master key");
+	exit_status++;
+	return;
+    }
+    memset((char *) master_entry.key_data, 0, sizeof(krb5_key_data) * (old_key_data_count + 1));
+    master_entry.n_key_data = old_key_data_count + 1;
+
+    new_mkey_kvno = old_kvno + 1;
+    /* deal with wrapping? */
+    if (new_mkey_kvno == 0)
+	new_mkey_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
+
+    /* Note, mkey does not have salt */
+    /* add new mkey encrypted with itself to mkey princ entry */
+    if ((retval = krb5_dbekd_encrypt_key_data(util_context, &new_master_keyblock,
+					      &new_master_keyblock, NULL, 
+					      (int) new_mkey_kvno, master_entry.key_data))) {
+	com_err(progname, retval, "while creating new master key");
+	exit_status++;
+	return;
+    }
+
+    /*
+     * Need to decrypt old keys with the current mkey which is in the global
+     * master_keyblock and encrypt those keys with the latest mkey.
+     *
+     * The new mkey is followed by existing keys.
+     *
+     * First, set up for creating a krb5_mkey_aux_node list which will be used
+     * to update the mkey aux data for the mkey princ entry.
+     */
+    mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+    if (mkey_aux_data_head == NULL) {
+	com_err(progname, ENOMEM, "while creating mkey_aux_data");
+	exit_status++;
+	return;
+    }
+    memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
+    mkey_aux_data = &mkey_aux_data_head;
+
+    for (i = 0; i < old_key_data_count; i++) {
+	key_data = &old_key_data[i];
+
+	retval = krb5_dbekd_decrypt_key_data(util_context, &master_keyblock,
+					     key_data, &plainkey, NULL);
+	if (retval) {
+	    com_err(progname, retval, "while decrypting master keys");
+	    exit_status++;
+	    return;
+	}
+
+	/*
+	 * Create a list of krb5_mkey_aux_node nodes.  One node contains the new
+	 * mkey encrypted by an old mkey and the old mkey's kvno (one node per
+	 * old mkey).
+	 */
+
+	if (*mkey_aux_data == NULL) {
+	    /* *mkey_aux_data points to next field of previous node */
+	    *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+	    if (mkey_aux_data == NULL) {
+		com_err(progname, ENOMEM, "while creating mkey_aux_data");
+		exit_status++;
+		return;
+	    }
+	    memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
+	}
+
+	/* encrypt the new mkey with the older mkey */
+	retval = krb5_dbekd_encrypt_key_data(util_context, &plainkey,
+					     &new_master_keyblock,
+					     NULL, /* no keysalt */
+					     (int) key_data->key_data_kvno,
+					     &tmp_key_data);
+	if (retval) {
+	    com_err(progname, retval, "while encrypting master keys");
+	    exit_status++;
+	    return;
+	}
+
+	(*mkey_aux_data)->latest_mkey = tmp_key_data;
+	(*mkey_aux_data)->mkey_kvno = key_data->key_data_kvno;
+
+	mkey_aux_data = &((*mkey_aux_data)->next);
+
+	/* Store old key in master_entry keydata, + 1 to skip the first key_data entry */
+	retval = krb5_dbekd_encrypt_key_data(util_context, &new_master_keyblock,
+					     &plainkey, NULL, /* no keysalt */
+					     (int) key_data->key_data_kvno,
+					     &master_entry.key_data[i+1]);
+	if (retval) {
+	    com_err(progname, retval, "while encrypting master keys");
+	    exit_status++;
+	    return;
+	}
+
+	/* free plain text key and old key data entry */
+	krb5_free_keyblock_contents(util_context, &plainkey);
+	for (j = 0; j < key_data->key_data_ver; j++) {
+	    if (key_data->key_data_length[j]) {
+		/* the key_data contents are encrypted so no clearing first */
+		free(key_data->key_data_contents[j]);
+	    }
+	}
+    }
+
+    if ((retval = krb5_dbe_update_mkey_aux(util_context, &master_entry,
+					   mkey_aux_data_head))) {
+	com_err(progname, retval, "while updating mkey aux data");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_timeofday(util_context, &now))) {
+	com_err(progname, retval, "while getting current time");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
+						 now, master_princ))) {
+	com_err(progname, retval, "while updating the master key principal modification time");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
+	(void) krb5_db_fini(util_context);
+	com_err(progname, retval, "while adding master key entry to the database");
+	exit_status++;
+	return;
+    }
+
+    if (do_stash) {
+	retval = krb5_db_store_master_key(util_context,
+					  global_params.stash_file,
+					  master_princ,
+					  new_mkey_kvno,
+					  &new_master_keyblock,
+					  mkey_password);
+	if (retval) {
+	    com_err(progname, errno, "while storing key");
+	    printf("Warning: couldn't stash master key.\n");
+	}
+    }
+    /* clean up */
+    (void) krb5_db_fini(util_context);
+    memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+    free(master_keyblock.contents);
+    memset((char *)new_master_keyblock.contents, 0, new_master_keyblock.length);
+    free(new_master_keyblock.contents);
+    if (pw_str) {
+	memset(pw_str, 0, pw_size);
+	free(pw_str);
+    }
+    free(master_salt.data);
+    free(mkey_fullname);
+    for (cur_mkey_aux_data = mkey_aux_data_head; cur_mkey_aux_data != NULL;
+	 cur_mkey_aux_data = next_mkey_aux_data) {
+
+	next_mkey_aux_data = cur_mkey_aux_data->next;
+	krb5_free_key_data_contents(util_context, &(cur_mkey_aux_data->latest_mkey));
+	free(cur_mkey_aux_data);
+    }
+    return;
+}
+
+void
+kdb5_use_mkey(int argc, char *argv[])
+{
+    krb5_error_code	retval;
+    char		*mkey_fullname;
+    krb5_kvno		use_kvno;
+    krb5_timestamp	now, start_time;
+    krb5_actkvno_node	*actkvno_list, *new_actkvno_list_head, *new_actkvno, *prev_actkvno, *cur_actkvno;
+    krb5_db_entry	master_entry;
+    int			nentries = 0;
+    krb5_boolean	more = 0;
+
+    if (argc < 1 || argc > 2) {
+	/* usage calls exit */
+	usage();
+    }
+
+    use_kvno = (int) strtol(argv[0], (char **)NULL, 10);
+    if (use_kvno == 0) {
+	com_err(progname, EINVAL, ": 0 is an invalid KVNO value.");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_timeofday(util_context, &now))) {
+	com_err(progname, retval, "while getting current time.");
+	exit_status++;
+	return;
+    }
+
+    if (argc == 2) {
+	start_time = (krb5_timestamp) get_date(argv[0]);
+    } else {
+	start_time = now;
+    }
+
+    /*
+     * Need to:
+     *
+     * 1. get mkey princ
+     * 2. verify that mprinc actually has a mkey with the new actkvno
+     * 2. get krb5_actkvno_node list
+     * 3. add use_kvno to actkvno list (sorted in right spot)
+     * 4. update mkey princ's tl data
+     * 5. put mkey princ.
+     */
+
+    /* assemble & parse the master key name */
+    if ((retval = krb5_db_setup_mkey_name(util_context,
+					  global_params.mkey_name,
+					  global_params.realm,  
+					  &mkey_fullname, &master_princ))) {
+	com_err(progname, retval, "while setting up master key name");
+	exit_status++;
+	return;
+    }
+
+    retval = krb5_db_get_principal(util_context, master_princ, &master_entry, &nentries,
+				   &more);
+    if (retval != 0) {
+	com_err(progname, retval, "while setting up master key name");
+	exit_status++;
+	return;
+    }
+
+    /* XXX WAF: verify that the provided kvno is valid */
+
+    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
+    if (retval != 0) {
+	com_err(progname, retval, "while setting up master key name");
+	exit_status++;
+	return;
+    }
+
+    /*
+     * determine which nodes to delete and where to insert new act kvno node
+     */
+
+    /* alloc enough space to hold new and existing key_data */
+    new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+    if (new_actkvno == NULL) {
+	com_err(progname, ENOMEM, "while adding new master key");
+	exit_status++;
+	return;
+    }
+
+    new_actkvno->act_kvno = use_kvno;
+    new_actkvno->act_time = start_time;
+
+    if (actkvno_list == NULL || new_actkvno->act_time < actkvno_list->act_time) {
+	/* insert new actkvno at head of list and link rest following */
+	new_actkvno->next = actkvno_list;
+	new_actkvno_list_head = new_actkvno;
+    } else {
+	for (new_actkvno_list_head = prev_actkvno = cur_actkvno = actkvno_list; cur_actkvno != NULL;
+	     prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+	    if (cur_actkvno->act_time <= now) {
+		if (new_actkvno->act_time < cur_actkvno->act_time) {
+		    /*
+		     * This is a problem as the new actkvno would be skipped and
+		     * not added to the entries for the mkey princ.
+		     */
+		    com_err(progname, EINVAL,
+			    "Activation time %s is less than a existing currently active kvno %d (activation time %s)",
+			    strdate(new_actkvno->act_time), cur_actkvno->act_kvno, strdate(cur_actkvno->act_time));
+		    exit_status++;
+		    return;
+		}
+		/*
+		 * New list head should point to the most current valid node in
+		 * order to trim out of date entries.
+		 */
+		new_actkvno_list_head = cur_actkvno;
+	    }
+
+	    if (new_actkvno->act_time < cur_actkvno->act_time) {
+		if (new_actkvno_list_head == cur_actkvno) {
+		    /*
+		     * XXX WAF: trying to minimize race condition issue here,
+		     * maybe there is a better way to do this?
+		     */
+		    com_err(progname, EINVAL,
+			    "Activation time %s is less than an existing currently active kvno %d (activation time %s)",
+			    strdate(new_actkvno->act_time), cur_actkvno->act_kvno, strdate(cur_actkvno->act_time));
+		    exit_status++;
+		    return;
+		}
+		prev_actkvno->next = new_actkvno;
+		new_actkvno->next = cur_actkvno;
+		break;
+	    } else if (cur_actkvno->next == NULL) {
+		/* end of line, just add new node to end of list */
+		cur_actkvno->next = new_actkvno;
+		break;
+	    }
+	} /* end for (new_actkvno_list_head = prev_actkvno = cur_actkvno = actkvno_list */
+    }
+
+    if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry, new_actkvno_list_head))) {
+	com_err(progname, retval, "while updating actkvno data for master principal entry.");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
+						 now, master_princ))) {
+	com_err(progname, retval, "while updating the master key principal modification time");
+	exit_status++;
+	return;
+    }
+
+    if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
+	(void) krb5_db_fini(util_context);
+	com_err(progname, retval, "while adding master key entry to the database");
+	exit_status++;
+	return;
+    }
+
+    /* clean up */
+    (void) krb5_db_fini(util_context);
+    free(mkey_fullname);
+    for (cur_actkvno = actkvno_list; cur_actkvno != NULL;) {
+
+	prev_actkvno = cur_actkvno;
+	cur_actkvno = cur_actkvno->next;
+	free(prev_actkvno);
+    }
+    return;
+}

Modified: branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.c
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -96,6 +96,7 @@
 }
 
 extern krb5_keyblock master_keyblock;
+extern krb5_keyblock_node *master_keylist;
 extern krb5_principal master_princ;
 krb5_db_entry master_entry;
 int	valid_master_key = 0;
@@ -123,6 +124,8 @@
 /*      {"dump_v4", dump_v4db, 1}, */
 /*      {"load_v4", load_v4db, 0}, */
      {"ark", add_random_key, 1},
+     {"add_mkey", kdb5_add_mkey, 1}, /* 1 is opendb */
+     {"use_mkey", kdb5_use_mkey, 1}, /* 1 is opendb */
      {NULL, NULL, 0},
 };
 
@@ -480,6 +483,7 @@
 	exit_status++;
 	return(0);
     }
+
     if ((retval = krb5_db_verify_master_key(util_context, master_princ, 
 					    kvno, &master_keyblock))) {
 	com_err(progname, retval, "while verifying master key");
@@ -488,6 +492,18 @@
 	return(1);
     }
 
+    /*
+     * I think I need to get the mkey list here so the ark command will
+     * work properly.
+     */
+    if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
+				       &master_keyblock, kvno, &master_keylist))) {
+	com_err(progname, retval, "while getting master key list");
+	com_err(progname, 0, "Warning: proceeding without master key list");
+	exit_status++;
+	return(0);
+    }
+
     seed.length = master_keyblock.length;
     seed.data = master_keyblock.contents;
 
@@ -496,6 +512,7 @@
 	exit_status++;
 	memset((char *)master_keyblock.contents, 0, master_keyblock.length);
 	krb5_free_keyblock_contents(util_context, &master_keyblock);
+        krb5_db_free_mkey_list(util_context, master_keylist);
 	return(1);
     }
 
@@ -516,6 +533,7 @@
 
     if (finished)
 	return 0;
+    krb5_db_free_mkey_list(util_context, master_keylist);
     retval = krb5_db_fini(util_context);
     memset((char *)master_keyblock.contents, 0, master_keyblock.length);
     finished = TRUE;
@@ -546,6 +564,7 @@
     char *me = progname;
     char *ks_str = NULL;
     char *pr_str;
+    krb5_keyblock *tmp_mkey;
 
     if (argc < 2)
 	usage();
@@ -600,7 +619,16 @@
 	free_keysalts = 0;
     } else
 	free_keysalts = 1;
-    ret = krb5_dbe_ark(util_context, &master_keyblock,
+
+    /* Find the mkey used to protect the existing keys */
+    ret = krb5_dbe_find_mkey(util_context, master_keylist, &dbent, &tmp_mkey);
+    if (ret) {
+	com_err(me, ret, "while finding mkey");
+	exit_status++;
+	return;
+    }
+
+    ret = krb5_dbe_ark(util_context, tmp_mkey,
 		       keysalts, num_keysalts,
 		       &dbent);
     if (free_keysalts)

Modified: branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.h
===================================================================
--- branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/dbutil/kdb5_util.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -82,6 +82,8 @@
 extern void kdb5_create (int argc, char **argv);
 extern void kdb5_destroy (int argc, char **argv);
 extern void kdb5_stash (int argc, char **argv);
+extern void kdb5_add_mkey (int argc, char **argv);
+extern void kdb5_use_mkey (int argc, char **argv);
 
 extern void update_ok_file (char *file_name);
 

Modified: branches/mkey_migrate/src/kadmin/server/ovsec_kadmd.c
===================================================================
--- branches/mkey_migrate/src/kadmin/server/ovsec_kadmd.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kadmin/server/ovsec_kadmd.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -99,6 +99,7 @@
 #define OVSEC_KADM_CHANGEPW_SERVICE	"ovsec_adm/changepw"
 
 extern krb5_keyblock master_keyblock;
+extern krb5_keyblock_node  *master_keylist;
 
 char *build_princ_name(char *name, char *realm);
 void log_badauth(OM_uint32 major, OM_uint32 minor,
@@ -626,6 +627,11 @@
 	  krb5_klog_syslog(LOG_ERR, "Can't set master key for kdb keytab.");
 	  goto kterr;
      }
+     ret = krb5_db_set_mkey_list(hctx, master_keylist);
+     if (ret) {
+	  krb5_klog_syslog(LOG_ERR, "Can't set master key list for kdb keytab.");
+	  goto kterr;
+     }
      ret = krb5_kt_register(context, &krb5_kt_kdb_ops);
      if (ret) {
 	  krb5_klog_syslog(LOG_ERR, "Can't register kdb keytab.");

Modified: branches/mkey_migrate/src/kdc/do_as_req.c
===================================================================
--- branches/mkey_migrate/src/kdc/do_as_req.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/do_as_req.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -76,6 +76,7 @@
     krb5_keyblock encrypting_key;
     const char *status;
     krb5_key_data  *server_key, *client_key;
+    krb5_keyblock *tmp_mkey;
     krb5_enctype useenctype;
 #ifdef	KRBCONF_KDC_MODIFIES_KDB
     krb5_boolean update_client = 0;
@@ -334,9 +335,14 @@
 	goto errout;
     }
 
+    if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey))) {
+	status = "FINDING_MASTER_KEY";
+	goto errout;
+    }
+
     /* convert server.key into a real key (it may be encrypted
        in the database) */
-    if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 
+    if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey, 
 					       server_key, &encrypting_key,
 					       NULL))) {
 	status = "DECRYPT_SERVER_KEY";
@@ -373,8 +379,13 @@
 	goto errout;
     }
 
+    if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &client, &tmp_mkey))) {
+	status = "FINDING_MASTER_KEY";
+	goto errout;
+    }
+
     /* convert client.key_data into a real key */
-    if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 
+    if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey, 
 					       client_key, &encrypting_key,
 					       NULL))) {
 	status = "DECRYPT_CLIENT_KEY";

Modified: branches/mkey_migrate/src/kdc/do_tgs_req.c
===================================================================
--- branches/mkey_migrate/src/kdc/do_tgs_req.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/do_tgs_req.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -71,7 +71,7 @@
     int nprincs = 0;
     krb5_boolean more;
     krb5_timestamp kdc_time, authtime=0;
-    krb5_keyblock session_key;
+    krb5_keyblock session_key, *tmp_mkey;
     krb5_timestamp until, rtime;
     krb5_keyblock encrypting_key;
     krb5_key_data  *server_key;
@@ -587,10 +587,16 @@
 	    status = "FINDING_SERVER_KEY";
 	    goto cleanup;
 	}
+
+	if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey))) {
+	    status = "FINDING_MASTER_KEY";
+	    goto cleanup;
+	}
+
 	/* convert server.key into a real key (it may be encrypted
 	 *        in the database) */
 	if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
-						   &master_keyblock, 
+						   tmp_mkey, 
 						   server_key, &encrypting_key,
 						   NULL))) {
 	    status = "DECRYPT_SERVER_KEY";

Modified: branches/mkey_migrate/src/kdc/extern.h
===================================================================
--- branches/mkey_migrate/src/kdc/extern.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/extern.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -41,6 +41,7 @@
     krb5_context	realm_context;	/* Context to be used for realm	    */
     krb5_keytab		realm_keytab; 	/* keytab to be used for this realm */
     char *		realm_profile;	/* Profile file for this realm	    */
+    krb5_keyblock_node * mkey_list;	/* list of mkeys in use for this realm */
     /*
      * Database per-realm data.
      */
@@ -48,6 +49,12 @@
     char *		realm_stash;	/* Stash file name for realm	    */
     char *		realm_mpname;	/* Master principal name for realm  */
     krb5_principal	realm_mprinc;	/* Master principal for realm	    */
+    /* XXX WAF: is realm_mkey the most current key in the keytab (or from
+     * command line)?  Or should this be the active key?  I need to make sure
+     * this is handled properly.  what about the kvno of this key?
+     * or maybe this should go away and be replaced with a function that
+     * returns the proper mkey given a princ.
+     */
     krb5_keyblock	realm_mkey;	/* Master key for this realm	    */
     /*
      * TGS per-realm data.
@@ -81,6 +88,7 @@
 #define	max_life_for_realm		kdc_active_realm->realm_maxlife
 #define	max_renewable_life_for_realm	kdc_active_realm->realm_maxrlife
 #define	master_keyblock			kdc_active_realm->realm_mkey
+#define	master_keylist			kdc_active_realm->mkey_list
 #define	master_princ			kdc_active_realm->realm_mprinc
 #define	tgs_server			kdc_active_realm->realm_tgsprinc
 #define reject_bad_transit		kdc_active_realm->realm_reject_bad_transit

Modified: branches/mkey_migrate/src/kdc/kdc_preauth.c
===================================================================
--- branches/mkey_migrate/src/kdc/kdc_preauth.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/kdc_preauth.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -620,7 +620,7 @@
     int i, k;
     krb5_data *ret;
     krb5_deltat *delta;
-    krb5_keyblock *keys;
+    krb5_keyblock *keys, *tmp_mkey;
     krb5_key_data *entry_key;
 
     switch (type) {
@@ -655,13 +655,15 @@
 	ret->data = (char *) keys;
 	ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
 	memset(ret->data, 0, ret->length);
+	if ((ret = krb5_dbe_find_mkey(context, master_keylist, &entry, &tmp_mkey)))
+	    return (ret);
 	k = 0;
 	for (i = 0; i < request->nktypes; i++) {
 	    entry_key = NULL;
 	    if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
 				      -1, 0, &entry_key) != 0)
 		continue;
-	    if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+	    if (krb5_dbekd_decrypt_key_data(context, tmp_mkey,
 					    entry_key, &keys[k], NULL) != 0) {
 		if (keys[k].contents != NULL)
 		    krb5_free_keyblock_contents(context, &keys[k]);
@@ -1291,7 +1293,7 @@
     krb5_data			scratch;
     krb5_data			enc_ts_data;
     krb5_enc_data 		*enc_data = 0;
-    krb5_keyblock		key;
+    krb5_keyblock		key, *tmp_mkey;
     krb5_key_data *		client_key;
     krb5_int32			start;
     krb5_timestamp		timenow;
@@ -1309,6 +1311,9 @@
     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
 	goto cleanup;
 
+    if ((retval = krb5_dbe_find_mkey(context, master_keylist, &client, &tmp_mkey)))
+	goto cleanup;
+
     start = 0;
     decrypt_err = 0;
     while (1) {
@@ -1317,7 +1322,7 @@
 					      -1, 0, &client_key)))
 	    goto cleanup;
 
-	if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock, 
+	if ((retval = krb5_dbekd_decrypt_key_data(context, tmp_mkey, 
 						  client_key, &key, NULL)))
 	    goto cleanup;
 
@@ -1901,7 +1906,7 @@
     krb5_sam_challenge		sc;
     krb5_predicted_sam_response	psr;
     krb5_data *			scratch;
-    krb5_keyblock encrypting_key;
+    krb5_keyblock encrypting_key, *tmp_mkey;
     char response[9];
     char inputblock[8];
     krb5_data predict_response;
@@ -1965,6 +1970,9 @@
       if (sc.sam_type) {
 	/* so use assoc to get the key out! */
 	{
+	  if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, &assoc, &tmp_mkey)))
+	      return (retval);
+
 	  /* here's what do_tgs_req does */
 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
 					 ENCTYPE_DES_CBC_RAW,
@@ -1981,7 +1989,7 @@
 	  }
 	  /* convert server.key into a real key */
 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
-					       &master_keyblock, 
+					       tmp_mkey, 
 					       assoc_key, &encrypting_key,
 					       NULL);
 	  if (retval) {
@@ -2467,7 +2475,7 @@
     unsigned		    cert_hash_len;
     unsigned		    key_dex;
     unsigned		    cert_match = 0;
-    krb5_keyblock	    decrypted_key;
+    krb5_keyblock	    decrypted_key, *tmp_mkey;
     
     /* the data we get from the AS-REQ */
     krb5_timestamp	    client_ctime = 0;
@@ -2611,6 +2619,8 @@
 	goto cleanup;
     }
     cert_hash_len = strlen(cert_hash);
+    if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry, &tmp_mkey)))
+	goto cleanup;
     for(key_dex=0; key_dex<client->n_key_data; key_dex++) {
 	krb5_key_data *key_data = &client->key_data[key_dex];
 	kdcPkinitDebug("--- key %u type[0] %u length[0] %u type[1] %u length[1] %u\n",
@@ -2625,7 +2635,7 @@
 	 * Unfortunately this key is stored encrypted even though it's
 	 * not sensitive... 
 	 */
-	krtn = krb5_dbekd_decrypt_key_data(context, &master_keyblock, 
+	krtn = krb5_dbekd_decrypt_key_data(context, tmp_mkey, 
 		    key_data, &decrypted_key, NULL);
 	if(krtn) {
 	    kdcPkinitDebug("verify_pkinit_request: error decrypting cert hash block\n");

Modified: branches/mkey_migrate/src/kdc/kdc_util.c
===================================================================
--- branches/mkey_migrate/src/kdc/kdc_util.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/kdc_util.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -381,6 +381,7 @@
     krb5_boolean 	  more;
     int	nprincs;
     krb5_key_data	* server_key;
+    krb5_keyblock       * tmp_mkey;
 
     nprincs = 1;
 
@@ -404,6 +405,11 @@
 	}
 	return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
     }
+
+    retval = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey);
+    if (retval)
+	goto errout;
+
     retval = krb5_dbe_find_enctype(kdc_context, &server,
 				   ticket->enc_part.enctype, -1,
 				   ticket->enc_part.kvno, &server_key);
@@ -415,7 +421,7 @@
     }
     *kvno = server_key->key_data_kvno;
     if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
-	retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
+	retval = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey,
 					     server_key,
 					     *key, NULL);
 	if (retval) {

Modified: branches/mkey_migrate/src/kdc/main.c
===================================================================
--- branches/mkey_migrate/src/kdc/main.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/kdc/main.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -156,6 +156,7 @@
     krb5_boolean	manual;
     krb5_realm_params	*rparams;
     int			kdb_open_flags;
+    krb5_kvno       mkvno = IGNORE_VNO;
 
     memset((char *) rdp, 0, sizeof(kdc_realm_t));
     if (!realm) {
@@ -263,18 +264,25 @@
     }
 
     /*
-     * Get the master key.
+     * Get the master key (note, may not be the most current mkey).
      */
     if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc,
 				   rdp->realm_mkey.enctype, manual,
 				   FALSE, rdp->realm_stash,
-				   NULL, NULL, &rdp->realm_mkey))) {
+				   &mkvno, NULL, &rdp->realm_mkey))) {
 	com_err(progname, kret,
 		"while fetching master key %s for realm %s",
 		rdp->realm_mpname, realm);
 	goto whoops;
     }
 
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+    /*
+     * Commenting krb5_db_verify_master_key out because it requires the most
+     * current mkey which may not be the case here.  The call to
+     * krb5_db_fetch_mkey_list() will end up verifying that the mkey is viable
+     * anyway.
+     */
     /* Verify the master key */
     if ((kret = krb5_db_verify_master_key(rdp->realm_context,
 					  rdp->realm_mprinc,
@@ -284,7 +292,15 @@
 		"while verifying master key for realm %s", realm);
 	goto whoops;
     }
+#endif /**************** END IFDEF'ed OUT *******************************/
 
+    if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc,
+				   &rdp->realm_mkey, mkvno, &rdp->mkey_list))) {
+	com_err(progname, kret,
+		"while fetching master keys list for realm %s", realm);
+	goto whoops;
+    }
+
     if ((kret = krb5_db_set_mkey(rdp->realm_context, &rdp->realm_mkey))) {
 	com_err(progname, kret,
 		"while setting master key for realm %s", realm);

Modified: branches/mkey_migrate/src/lib/kadm5/admin.h
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/admin.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/admin.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -184,6 +184,7 @@
 	krb5_int16 n_tl_data;
         krb5_tl_data *tl_data;
 	krb5_key_data *key_data;
+	int foo; /* XXX WAF: just to see if it breaks the build */
 } kadm5_principal_ent_rec_v2, *kadm5_principal_ent_t_v2;
 
 typedef struct _kadm5_principal_ent_t_v1 {

Modified: branches/mkey_migrate/src/lib/kadm5/misc_free.c
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/misc_free.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/misc_free.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -37,7 +37,8 @@
      free(names);
     return KADM5_OK;
 }
-
+/* XXX WAF: maybe delete this if all compiles */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
 /* XXX this ought to be in libkrb5.a, but isn't */
 kadm5_ret_t krb5_free_key_data_contents(context, key)
    krb5_context context;
@@ -54,6 +55,7 @@
      }
      return KADM5_OK;
 }
+#endif /**************** END IFDEF'ed OUT *******************************/
 
 kadm5_ret_t kadm5_free_key_data(void *server_handle,
 				krb5_int16 *n_key_data,

Modified: branches/mkey_migrate/src/lib/kadm5/server_internal.h
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/server_internal.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/server_internal.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -85,8 +85,11 @@
 kadm5_ret_t krb5_copy_key_data_contents(krb5_context context,
 					krb5_key_data *from, 
 					krb5_key_data *to);
+/* XXX WAF: maybe delete this if all compiles */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
 kadm5_ret_t krb5_free_key_data_contents(krb5_context context, 
 					krb5_key_data *key);
+#endif /**************** END IFDEF'ed OUT *******************************/
 
 /*
  * *Warning* 

Modified: branches/mkey_migrate/src/lib/kadm5/srv/libkadm5srv.exports
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/srv/libkadm5srv.exports	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/srv/libkadm5srv.exports	2009-01-09 20:11:57 UTC (rev 21721)
@@ -84,6 +84,7 @@
 krb5_string_to_keysalts
 master_db
 master_keyblock
+master_keylist
 master_princ
 osa_free_princ_ent
 ovsec_kadm_chpass_principal

Modified: branches/mkey_migrate/src/lib/kadm5/srv/server_kdb.c
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/srv/server_kdb.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/srv/server_kdb.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -15,7 +15,9 @@
 #include "server_internal.h"
 
 krb5_principal	    master_princ;
-krb5_keyblock	    master_keyblock;
+krb5_keyblock	    master_keyblock; /* local mkey */
+krb5_keyblock_node  *master_keylist = NULL;
+krb5_actkvno_node   *active_mkey_list = NULL;
 krb5_db_entry	    master_db;
 
 krb5_principal	    hist_princ;
@@ -32,6 +34,8 @@
     int		   ret = 0;
     char	   *realm;
     krb5_boolean   from_kbd = FALSE;
+    krb5_keyblock  *mkey;
+    krb5_kvno       mkvno = IGNORE_VNO;
 
     if (from_keyboard)
       from_kbd = TRUE;
@@ -50,23 +54,46 @@
 
     master_keyblock.enctype = handle->params.enctype;
 
+    /* 
+     * Fetch the local mkey, may not be the latest but that's okay because we
+     * really want the list of all mkeys and those can be retrieved with any
+     * valid mkey.
+     */
     ret = krb5_db_fetch_mkey(handle->context, master_princ,
 			     master_keyblock.enctype, from_kbd,
 			     FALSE /* only prompt once */,
 			     handle->params.stash_file,
-			     NULL /* don't care about kvno */,
+			     &mkvno  /* get the kvno of the returned mkey */,
 			     NULL /* I'm not sure about this,
 				     but it's what the kdc does --marc */,
 			     &master_keyblock);
     if (ret)
 	goto done;
 				 
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+    /*
+     * XXX WAF: since the local mkey may not be latest, hold off on verifying it
+     * since krb5_db_fetch_mkey_list will do this work.
+     */
     if ((ret = krb5_db_verify_master_key(handle->context, master_princ,
 					 IGNORE_VNO, &master_keyblock))) {
 	  krb5_db_fini(handle->context);
 	  return ret;
     }
+#endif /**************** END IFDEF'ed OUT *******************************/
 
+    if ((ret = krb5_db_fetch_mkey_list(handle->context, master_princ,
+				       mkey, mkvno, &master_keylist))) {
+	krb5_db_fini(handle->context);
+	return (ret);
+    }
+
+    if ((ret = krb5_dbe_fetch_act_mkey_list(handle->context, master_princ,
+				            &active_mkey_list))) {
+	krb5_db_fini(handle->context);
+	return (ret);
+    }
+
 done:
     if (r == NULL)
 	free(realm);
@@ -106,6 +133,7 @@
     char    *realm, *hist_name;
     krb5_key_data *key_data;
     krb5_key_salt_tuple ks[1];
+    krb5_keyblock *tmp_mkey;
 
     if (r == NULL)  {
 	if ((ret = krb5_get_default_realm(handle->context, &realm)))
@@ -177,7 +205,11 @@
     if (ret)
 	goto done;
 
-    ret = krb5_dbekd_decrypt_key_data(handle->context, &master_keyblock,
+    ret = krb5_dbe_find_mkey(handle->context, master_keylist, &hist_db, &tmp_mkey);
+    if (ret)
+	goto done;
+
+    ret = krb5_dbekd_decrypt_key_data(handle->context, tmp_mkey,
 				  key_data, &hist_key, NULL);
     if (ret)
 	goto done;

Modified: branches/mkey_migrate/src/lib/kadm5/srv/svr_principal.c
===================================================================
--- branches/mkey_migrate/src/lib/kadm5/srv/svr_principal.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kadm5/srv/svr_principal.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -26,13 +26,15 @@
 
 extern	krb5_principal	    master_princ;
 extern	krb5_principal	    hist_princ;
-extern	krb5_keyblock	    master_keyblock;
+/* extern	krb5_keyblock	    master_keyblock; */
+extern  krb5_keyblock_node  *master_keylist;
+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;
 
-static int decrypt_key_data(krb5_context context,
+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);
 
@@ -198,6 +200,7 @@
     krb5_tl_data		*tl_data_orig, *tl_data_tail;
     unsigned int		ret;
     kadm5_server_handle_t handle = server_handle;
+    krb5_keyblock               *act_mkey;
 
     CHECK_HANDLE(server_handle);
 
@@ -340,7 +343,12 @@
 
     /* initialize the keys */
 
-    if ((ret = krb5_dbe_cpw(handle->context, &master_keyblock,
+    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+				 active_mkey_list, &act_mkey);
+    if (ret)
+	return (ret);
+
+    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,
@@ -352,6 +360,17 @@
 	return(ret);
     }
 
+    /* XXX WAF: this needs to be changed to use real mkvno */
+    /* Record the master key VNO used to encrypt this entry's keys */
+    ret = krb5_dbe_update_mkvno(handle->context, &kdb, 1);
+    if (ret)
+    {
+	krb5_db_free_principal(handle->context, &kdb, 1);
+	if (mask & KADM5_POLICY)
+	    (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+	return ret;
+    }
+
     /* populate the admin-server-specific fields.  In the OV server,
        this used to be in a separate database.  Since there's already
        marshalling code for the admin fields, to keep things simple,
@@ -800,12 +819,24 @@
 	      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;
+
+    /*
+     * 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;
     }
+#endif /**************** END IFDEF'ed OUT *******************************/
 
     /*
      * The new fields that only exist in version 2 start here
@@ -930,6 +961,7 @@
  */
 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)
@@ -940,7 +972,7 @@
 
     for (x = 0; x < n_new_key_data; x++) {
 	ret = krb5_dbekd_decrypt_key_data(context,
-					  &master_keyblock,
+					  mkey,
 					  &(new_key_data[x]),
 					  &newkey, NULL);
 	if (ret)
@@ -993,7 +1025,7 @@
  * set to n_key_data.
  */
 static
-int create_history_entry(krb5_context context, int n_key_data,
+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 i, ret;
@@ -1007,7 +1039,7 @@
 
      for (i = 0; i < n_key_data; i++) {
 	 ret = krb5_dbekd_decrypt_key_data(context,
-					   &master_keyblock,
+					   mkey,
 					   &key_data[i],
 					   &key, &salt);
 	 if (ret)
@@ -1296,6 +1328,7 @@
     int				have_pol = 0;
     kadm5_server_handle_t	handle = server_handle;
     osa_pw_hist_ent		hist;
+    krb5_keyblock               *act_mkey;
 
     CHECK_HANDLE(server_handle);
 
@@ -1329,7 +1362,12 @@
 			    KADM5_POLICY, &pol, principal)))
 	 goto done;
 
-    ret = krb5_dbe_cpw(handle->context, &master_keyblock,
+    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+				 active_mkey_list, &act_mkey);
+    if (ret)
+	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 */,
@@ -1366,12 +1404,13 @@
 #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, &hist_key,
+	ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
 			     kdb.n_key_data, kdb.key_data,
 			     1, &hist);
 	if (ret)
@@ -1383,7 +1422,7 @@
 		goto done;
 	    }
 
-	    ret = check_pw_reuse(handle->context, &hist_key,
+	    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)
@@ -1483,6 +1522,7 @@
     krb5_key_data		*key_data;
     int				ret, last_pwd, have_pol = 0;
     kadm5_server_handle_t	handle = server_handle;
+    krb5_keyblock               *act_mkey;
 
     if (keyblocks)
 	 *keyblocks = NULL;
@@ -1501,7 +1541,12 @@
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);
 
-    ret = krb5_dbe_crk(handle->context, &master_keyblock,
+    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+				 active_mkey_list, &act_mkey);
+    if (ret)
+	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,
@@ -1546,7 +1591,7 @@
 		goto done;
 	    }
 
-	    ret = check_pw_reuse(handle->context, &hist_key,
+	    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)
@@ -1573,12 +1618,12 @@
 	     if (ret)
 		 goto done;
 	     
-	     ret = decrypt_key_data(handle->context, 1, key_data,
+	     ret = decrypt_key_data(handle->context, act_mkey, 1, key_data,
 				     keyblocks, NULL);
 	     if (ret)
 		 goto done;
 	 } else {
-	     ret = decrypt_key_data(handle->context,
+	     ret = decrypt_key_data(handle->context, act_mkey,
 				     kdb.n_key_data, kdb.key_data,
 				     keyblocks, n_keys);
 	     if (ret)
@@ -1624,6 +1669,7 @@
 #endif
     kadm5_server_handle_t	handle = server_handle;
     krb5_key_data               tmp_key_data;
+    krb5_keyblock               *act_mkey;
 
     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
 
@@ -1661,8 +1707,13 @@
     keysalt.data.length = 0;
     keysalt.data.data = NULL;
 
+    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+				 active_mkey_list, &act_mkey);
+    if (ret)
+	goto done;
+
     /* use tmp_key_data as temporary location and reallocate later */
-    ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
+    ret = krb5_dbekd_encrypt_key_data(handle->context, act_mkey,
 				      keyblock, &keysalt, kvno + 1,
 				      &tmp_key_data);
     if (ret) {
@@ -1803,6 +1854,7 @@
     krb5_keysalt		keysalt;
     krb5_key_data         tmp_key_data;
     krb5_key_data        *tptr;
+    krb5_keyblock               *act_mkey;
 
     CHECK_HANDLE(server_handle);
 
@@ -1874,15 +1926,20 @@
 	}
 	memset (&tmp_key_data, 0, sizeof(tmp_key_data));
 
+	ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+	                             active_mkey_list, &act_mkey);
+	if (ret)
+	    goto done;
+
 	ret = krb5_dbekd_encrypt_key_data(handle->context,
-					  &master_keyblock,
+					  act_mkey,
 					  &keyblocks[i],
 					  n_ks_tuple ? &keysalt : NULL,
 					  kvno + 1,
 					  &tmp_key_data);
-	if (ret) {
+	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;
@@ -2007,6 +2064,7 @@
     krb5_key_data               *key_data;
     kadm5_ret_t                 ret;
     kadm5_server_handle_t       handle = server_handle;
+    krb5_keyblock               *tmp_mkey;
 
     if (keyblocks)
          *keyblocks = NULL;
@@ -2020,6 +2078,9 @@
        return(ret);
 
     if (keyblocks) {
+	ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb, &tmp_mkey);
+	if (ret)
+	    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,
@@ -2027,11 +2088,11 @@
                                               -1, -1, &key_data)))
                    goto done;
 
-              if ((ret = decrypt_key_data(handle->context, 1, key_data,
+              if ((ret = decrypt_key_data(handle->context, tmp_mkey, 1, key_data,
                                          keyblocks, NULL)))
                    goto done;
          } else {
-              ret = decrypt_key_data(handle->context,
+              ret = decrypt_key_data(handle->context, tmp_mkey,
                                      kdb.n_key_data, kdb.key_data,
                                      keyblocks, n_keys);
               if (ret)
@@ -2050,10 +2111,10 @@
 /*
  * 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
- * master_keyblock, and if n_keys is not NULL fill it in with the
+ * mkey, and if n_keys is not NULL fill it in with the
  * number of keys decrypted.
  */
-static int decrypt_key_data(krb5_context context,
+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)
 {
@@ -2066,7 +2127,7 @@
      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
 
      for (i = 0; i < n_key_data; i++) {
-          ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+          ret = krb5_dbekd_decrypt_key_data(context, mkey,
 					    &key_data[i], 
 					    &keys[i], NULL);
 	  if (ret) {
@@ -2129,6 +2190,7 @@
     kadm5_server_handle_t handle = server_handle;
     krb5_db_entry dbent;
     krb5_key_data *key_data;
+    krb5_keyblock *tmp_mkey;
     int ret;
 
     CHECK_HANDLE(server_handle);
@@ -2143,8 +2205,14 @@
 				    stype, kvno, &key_data)))
 	 return ret;
 
+    /* find_mkey only uses this field */
+    dbent.tl_data = entry->tl_data;
+    ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent, &tmp_mkey);
+    if (ret)
+	return (ret);
+
     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
-					   &master_keyblock, key_data,
+					   tmp_mkey, key_data,
 					   keyblock, keysalt)))
 	 return ret;
 

Modified: branches/mkey_migrate/src/lib/kdb/kdb5.c
===================================================================
--- branches/mkey_migrate/src/lib/kdb/kdb5.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/kdb5.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -100,6 +100,31 @@
     return k5_mutex_unlock(&db_lock);
 }
 
+static void
+krb5_free_actkvno_list(krb5_context context, krb5_actkvno_node *val)
+{
+    krb5_actkvno_node *temp, *prev;
+
+    for (temp = val; temp != NULL;) {
+	prev = temp;
+	temp = temp->next;
+	krb5_xfree(prev);
+    }
+}
+
+static void
+krb5_free_mkey_aux_list(krb5_context context, krb5_mkey_aux_node *val)
+{
+    krb5_mkey_aux_node *temp, *prev;
+
+    for (temp = val; temp != NULL;) {
+	prev = temp;
+	temp = temp->next;
+	krb5_free_key_data_contents(context, &prev->latest_mkey);
+	krb5_xfree(prev);
+    }
+}
+
 #define kdb_init_lib_lock(a) 0
 #define kdb_destroy_lib_lock(a) (void)0
 #define kdb_lock_lib_lock(a, b) 0
@@ -195,10 +220,18 @@
 	lib->vftabl.set_master_key = kdb_def_set_mkey;
     }
 
+    if (lib->vftabl.set_master_key_list == NULL) {
+	lib->vftabl.set_master_key_list = kdb_def_set_mkey_list;
+    }
+
     if (lib->vftabl.get_master_key == NULL) {
 	lib->vftabl.get_master_key = kdb_def_get_mkey;
     }
 
+    if (lib->vftabl.get_master_key_list == NULL) {
+	lib->vftabl.get_master_key_list = kdb_def_get_mkey_list;
+    }
+
     if (lib->vftabl.fetch_master_key == NULL) {
 	lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey;
     }
@@ -207,6 +240,10 @@
 	lib->vftabl.verify_master_key = krb5_def_verify_master_key;
     }
 
+    if (lib->vftabl.fetch_master_key_list == NULL) {
+	lib->vftabl.fetch_master_key_list = krb5_def_fetch_mkey_list;
+    }
+
     if (lib->vftabl.dbe_search_enctype == NULL) {
 	lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
     }
@@ -1109,7 +1146,7 @@
 		upd->kdb_princ_name.utf8str_t_val = princ_name;
 		upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
 
-                if (status = ulog_add_update(kcontext, upd))
+                if ((status = ulog_add_update(kcontext, upd)))
 			goto err_lock;
 		upd++;
         }
@@ -1360,6 +1397,12 @@
 }
 
 krb5_error_code
+krb5_db_set_mkey_list(krb5_context context, krb5_keyblock_node * keylist)
+{
+    return krb5_db_set_master_key_ext(context, NULL, keylist);
+}
+
+krb5_error_code
 krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
 {
     krb5_error_code status = 0;
@@ -1389,6 +1432,90 @@
 }
 
 krb5_error_code
+krb5_db_get_mkey_list(krb5_context kcontext, krb5_keyblock_node ** keylist)
+{
+    krb5_error_code status = 0;
+    kdb5_dal_handle *dal_handle;
+
+    if (kcontext->dal_handle == NULL) {
+	status = kdb_setup_lib_handle(kcontext);
+	if (status) {
+	    goto clean_n_exit;
+	}
+    }
+
+    dal_handle = kcontext->dal_handle;
+    status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
+    if (status) {
+	goto clean_n_exit;
+    }
+
+    /* Let's use temp key and copy it later to avoid memory problems
+       when freed by the caller.  */
+    status = dal_handle->lib_handle->vftabl.get_master_key_list(kcontext, keylist);
+    get_errmsg(kcontext, status);
+    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+
+  clean_n_exit:
+    return status;
+}
+
+krb5_error_code
+krb5_db_fetch_mkey_list(krb5_context     context,
+                   krb5_principal        mname,
+                   const krb5_keyblock * mkey,
+                   krb5_kvno             mkvno,
+		   krb5_keyblock_node  **mkey_list)
+{
+	kdb5_dal_handle *dal_handle;
+	krb5_error_code status = 0;
+
+	if (context->dal_handle == NULL) {
+	    status = kdb_setup_lib_handle(context);
+	    if (status) {
+		goto clean_n_exit;
+	    }
+	}
+
+	dal_handle = context->dal_handle;
+	status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
+	if (status) {
+	    goto clean_n_exit;
+	}
+
+	status = dal_handle->lib_handle->vftabl.fetch_master_key_list(context,
+                                                                      mname,
+                                                                      mkey,
+                                                                      mkvno,
+                                                                      mkey_list);
+	get_errmsg(context, status);
+	kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+
+	if (status) {
+	    goto clean_n_exit;
+	}
+
+clean_n_exit:
+    return status;
+}
+
+krb5_error_code
+krb5_db_free_mkey_list(krb5_context    context,
+		       krb5_keyblock_node  *mkey_list)
+{
+    krb5_keyblock_node *cur, *prev;
+
+    for (cur = mkey_list; cur != NULL;) {
+	prev = cur;
+	cur = cur->next;
+	krb5_free_keyblock_contents(context, &prev->keyblock);
+	krb5_xfree(prev);
+    }
+
+    return 0;
+}
+
+krb5_error_code
 krb5_db_store_master_key(krb5_context kcontext,
 			 char *keyfile,
 			 krb5_principal mname,
@@ -1575,6 +1702,255 @@
     return status;
 }
 
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+/* XXX WAF: don't think this is needed now that I've modified
+ * krb5_def_fetch_mkey_list.  Keeping it around just in case. */
+/*
+ * get most current master key which may be stored with the master key princ.
+ */ 
+
+krb5_error_code
+krb5_db_fetch_latest_mkey(krb5_context    context,
+                   krb5_principal  mname,
+                   krb5_enctype    etype,
+                   krb5_boolean    fromkeyboard,
+                   krb5_boolean    twice,
+                   char          * db_args,
+                   krb5_kvno     * kvno,
+                   krb5_data     * salt,
+                   krb5_keyblock * key)
+{
+    krb5_keyblock tmp_mkey, tmp_clearkey;
+    krb5_kvno     tmp_kvno;
+    krb5_db_entry master_entry;
+    int nprinc;
+    krb5_boolean more, found_key = FALSE;
+    krb5_mkey_aux_node	*mkey_aux_data_list, *aux_data_entry;
+    krb5_error_code retval = 0;
+
+    memset(&tmp_mkey, 0, sizeof(tmp_mkey));
+    memset(&tmp_clearkey, 0, sizeof(tmp_clearkey));
+
+    /* fetch the local mkey either from stash or via keyboard interactive */
+    if ((retval = krb5_db_fetch_mkey(context, mname, etype, fromkeyboard,
+				   twice, db_args, &tmp_kvno, NULL, &tmp_mkey))) {
+	return (retval);
+    }
+
+    nprinc = 1;
+    retval = krb5_db_get_principal(context, mname, &master_entry, &nprinc, &more);
+    if (retval != 0)
+	goto clean_n_exit;
+
+    if ((retval = krb5_dbekd_decrypt_key_data(context, &tmp_mkey,
+					      &master_entry.key_data[0],
+					      &tmp_clearkey, NULL)) != 0) {
+	/*
+	 * Note the tmp_kvno may provide a hint as to which mkey_aux tuple to
+	 * decrypt.
+	 */
+	if ((retval = krb5_dbe_lookup_mkey_aux(context, &master_entry, &mkey_aux_data_list)))
+	    goto clean_n_exit;
+
+	/* for performance sake, try decrypting with matching kvno */
+	for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+	     aux_data_entry = aux_data_entry->next) {
+
+	    if (aux_data_entry->mkey_kvno == tmp_kvno) {
+		if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+				   &tmp_clearkey, NULL) == 0) {
+		    found_key = TRUE;
+		    break;
+		}
+	    }
+	}
+	if (found_key != TRUE) {
+	    /* given the importance of acquiring the latest mkey, try brute force */
+	    for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+		 aux_data_entry = aux_data_entry->next) {
+
+		if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+						&tmp_clearkey, NULL) == 0) {
+		    found_key = TRUE;
+		    /* XXX WAF: should I issue warning about kvno not matching?
+		     */
+		    break;
+		}
+	    }
+	    if (found_key != TRUE) {
+		krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+			"Unable to decrypt latest master key with the provided master key\n");
+		retval = KRB5_KDB_BADMASTERKEY;
+		goto clean_n_exit;
+	    }
+	}
+
+	if ((retval = krb5_db_verify_master_key(context,
+						mname,
+						tmp_kvno,
+						&tmp_clearkey))) {
+	    krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+		"Failed to verify Latest master key decrypted with the provided master key\n");
+	    retval = KRB5_KDB_BADMASTERKEY;
+	    goto clean_n_exit;
+	}
+    }
+
+    key->contents = malloc(tmp_clearkey.length);
+    if (key->contents == NULL) {
+	retval = ENOMEM;
+	goto clean_n_exit;
+    }
+
+    key->magic = tmp_clearkey.magic;
+    key->enctype = tmp_clearkey.enctype;
+    key->length = tmp_clearkey.length;
+    memcpy(key->contents, tmp_clearkey.contents, tmp_clearkey.length);
+
+clean_n_exit:
+    if (tmp_mkey.contents) {
+	memset(tmp_mkey.contents, 0, tmp_mkey.length);
+	krb5_db_free(context, tmp_mkey.contents);
+    }
+    if (tmp_clearkey.contents) {
+	memset(tmp_clearkey.contents, 0, tmp_clearkey.length);
+	krb5_db_free(context, tmp_clearkey.contents);
+    }
+    krb5_db_free_principal(context, &master_entry, nprinc);
+    return (retval);
+}
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+krb5_error_code
+krb5_dbe_fetch_act_mkey_list(krb5_context        context,
+			     krb5_principal      mprinc,
+			     krb5_actkvno_node  **act_mkey_list)
+{
+    krb5_error_code retval = 0;
+    krb5_db_entry master_entry;
+    int nprinc;
+    krb5_boolean more;
+
+    if (act_mkey_list == NULL)
+	return (EINVAL);
+
+    nprinc = 1;
+    if ((retval = krb5_db_get_principal(context, mprinc,
+					&master_entry, &nprinc, &more)))
+	return (retval);
+	
+    if (nprinc != 1) {
+	if (nprinc)
+	    krb5_db_free_principal(context, &master_entry, nprinc);
+	return(KRB5_KDB_NOMASTERKEY);
+    } else if (more) {
+	krb5_db_free_principal(context, &master_entry, nprinc);
+	return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+    }
+
+    retval = krb5_dbe_lookup_actkvno(context, &master_entry, act_mkey_list);
+
+    krb5_db_free_principal(context, &master_entry, nprinc);
+    return retval;
+}
+
+/*
+ * Locates the "active" mkey used when encrypting a princ's keys.  Note, the
+ * caller must not free the output act_mkey.
+ */
+
+krb5_error_code
+krb5_dbe_find_act_mkey(krb5_context         context,
+                       krb5_keyblock_node  *mkey_list,
+                       krb5_actkvno_node   *act_mkey_list,
+                       krb5_keyblock      **act_mkey)
+{
+    krb5_kvno act_kvno;
+    krb5_error_code retval;
+    krb5_keyblock_node *cur_keyblock = mkey_list;
+    krb5_actkvno_node   *prev_actkvno, *cur_actkvno;
+    krb5_timestamp	now;
+    krb5_boolean	found = FALSE;
+
+    if ((retval = krb5_timeofday(context, &now)))
+        return (retval);
+
+    /*
+     * The list should be sorted in time, early to later so if the first entry
+     * is later than now, this is a problem
+     */
+    if (act_mkey_list->act_time > now) {
+        return (KRB5_KDB_NOACTMASTERKEY);
+    }
+
+    /* find the most current entry <= now */
+    for (prev_actkvno = cur_actkvno = act_mkey_list; cur_actkvno != NULL;
+         prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+        if (cur_actkvno->act_time == now) {
+            act_kvno = cur_actkvno->act_kvno;
+            found = TRUE;
+            break;
+        } else if (cur_actkvno->act_time > now && prev_actkvno->act_time <= now) {
+            act_kvno = prev_actkvno->act_kvno;
+            found = TRUE;
+            break;
+        }
+    }
+
+    if (!found) {
+        /*
+         * The end of the list was encountered and all entries are < now so use
+         * the latest entry.
+         */
+        if (prev_actkvno->act_time <= now) {
+            act_kvno = prev_actkvno->act_kvno;
+        } else {
+            /* XXX this shouldn't happen */
+            return (KRB5_KDB_NOACTMASTERKEY);
+        }
+    }
+
+    while (cur_keyblock && cur_keyblock->kvno != act_kvno)
+        cur_keyblock = cur_keyblock->next;
+
+    if (cur_keyblock) {
+        *act_mkey = &cur_keyblock->keyblock;
+        return (0);
+    } else {
+        return (KRB5_KDB_NO_MATCHING_KEY);
+    }
+}
+
+/*
+ * Locates the mkey used to protect a princ's keys.  Note, the caller must not
+ * free the output key.
+ */
+krb5_error_code
+krb5_dbe_find_mkey(krb5_context	      context,
+                   krb5_keyblock_node *mkey_list,
+                   krb5_db_entry      *entry,
+                   krb5_keyblock      **mkey)
+{
+    krb5_kvno mkvno;
+    krb5_error_code retval;
+    krb5_keyblock_node *cur_keyblock = mkey_list;
+
+    retval = krb5_dbe_lookup_mkvno(context, entry, &mkvno);
+    if (retval)
+	return (retval);
+
+    while (cur_keyblock && cur_keyblock->kvno != mkvno)
+	cur_keyblock = cur_keyblock->next;
+
+    if (cur_keyblock) {
+	*mkey = &cur_keyblock->keyblock;
+	return (0);
+    } else {
+	return (KRB5_KDB_NO_MATCHING_KEY);
+    }
+}
+
 void   *
 krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
 {
@@ -1849,6 +2225,290 @@
 }
 
 krb5_error_code
+krb5_dbe_lookup_mkvno(krb5_context	context,
+		      krb5_db_entry	*entry,
+		      krb5_kvno		*mkvno)
+{
+    krb5_tl_data tl_data;
+    krb5_error_code code;
+    krb5_int16 tmp;
+
+    tl_data.tl_data_type = KRB5_TL_MKVNO;
+
+    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+	return (code);
+
+    if (tl_data.tl_data_length == 0) {
+	*mkvno = 1; /* default for princs that lack the KRB5_TL_MKVNO data */
+	return (0);
+    }
+
+    krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp);
+    *mkvno = (krb5_kvno) tmp;
+    return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkvno(krb5_context    context,
+		      krb5_db_entry * entry,
+		      krb5_kvno       mkvno)
+{
+    krb5_tl_data tl_data;
+    krb5_octet buf[2];		/* this is the encoded size of an int16 */
+
+    tl_data.tl_data_type = KRB5_TL_MKVNO;
+    tl_data.tl_data_length = sizeof(buf);
+    /* use standard encoding */
+    krb5_kdb_encode_int16((krb5_ui_2) mkvno, buf);
+    tl_data.tl_data_contents = buf;
+
+    return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
+krb5_error_code
+krb5_dbe_lookup_mkey_aux(krb5_context		context,
+		      krb5_db_entry		* entry,
+		      krb5_mkey_aux_node	** mkey_aux_data_list)
+{
+    krb5_tl_data tl_data;
+    krb5_int16 version;
+    krb5_mkey_aux_node *head_data = NULL, *new_data = NULL,
+		       *prev_data = NULL;
+    krb5_octet *curloc; /* current location pointer */
+    krb5_error_code code;
+
+    tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
+    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+	return (code);
+
+    if (tl_data.tl_data_contents == NULL) {
+	*mkey_aux_data_list = NULL;
+	return (0);
+    } else {
+	/* get version to determine how to parse the data */
+	krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
+	if (version == KRB5_TL_MKEY_AUX_VER_1) {
+
+	    /* curloc points to first tuple entry in the tl_data_contents */
+	    curloc = tl_data.tl_data_contents + sizeof(version);
+
+	    while (curloc != (tl_data.tl_data_contents + tl_data.tl_data_length)) {
+		assert(curloc < tl_data.tl_data_contents + tl_data.tl_data_length);
+
+		new_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+		if (new_data == NULL) {
+		    krb5_free_mkey_aux_list(context, head_data);
+		    return (ENOMEM);
+		}
+		krb5_kdb_decode_int16(curloc, new_data->mkey_kvno);
+		curloc += sizeof(krb5_ui_2);
+		krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_kvno);
+		curloc += sizeof(krb5_ui_2);
+		krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_type[0]);
+		curloc += sizeof(krb5_ui_2);
+		krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_length[0]);
+		curloc += sizeof(krb5_ui_2);
+
+		new_data->latest_mkey.key_data_contents[0] = (krb5_octet *)
+		    malloc(new_data->latest_mkey.key_data_length[0]);
+
+		if (new_data->latest_mkey.key_data_contents[0] == NULL) {
+		    krb5_free_mkey_aux_list(context, head_data);
+		    return (ENOMEM);
+		}
+		memcpy(new_data->latest_mkey.key_data_contents[0], curloc, new_data->latest_mkey.key_data_length[0]);
+
+		new_data->next = NULL;
+		if (prev_data != NULL)
+		    prev_data->next = new_data;
+		else
+		    head_data = new_data;
+		prev_data = new_data;
+	    }
+	} else {
+	    krb5_set_error_message (context, KRB5_KDB_BAD_VERSION,
+		    "Illegal version number for KRB5_TL_MKEY_AUX %d\n",
+		    version);
+	    return (KRB5_KDB_BAD_VERSION);
+	}
+    }
+    *mkey_aux_data_list = head_data;
+    return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkey_aux(krb5_context		context,
+		      krb5_db_entry		* entry,
+		      krb5_mkey_aux_node	* mkey_aux_data_list)
+{
+    krb5_tl_data tl_data;
+    krb5_int16 version;
+    krb5_octet *nextloc;
+    krb5_mkey_aux_node *aux_data_entry;
+
+    tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
+    /*
+     * determine out how much space to allocate
+     */
+    tl_data.tl_data_length = sizeof(version); /* version */
+    for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; aux_data_entry = aux_data_entry->next) {
+	tl_data.tl_data_length += sizeof(krb5_ui_2); /* mkey_kvno */
+	tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey kvno */
+	tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey enctype */
+	tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey length */
+	tl_data.tl_data_length += aux_data_entry->latest_mkey.key_data_length[0]; /* mkey data */
+    }
+
+    tl_data.tl_data_contents = (krb5_octet *) malloc(tl_data.tl_data_length);
+    if (tl_data.tl_data_contents == NULL) {
+	return (ENOMEM);
+    }
+
+    nextloc = tl_data.tl_data_contents;
+    /* version */
+    krb5_kdb_encode_int16((krb5_ui_2)KRB5_TL_MKEY_AUX_VER_1, (unsigned char *)nextloc);
+    nextloc += sizeof(krb5_ui_2);
+    for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; aux_data_entry = aux_data_entry->next) {
+	krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->mkey_kvno, (unsigned char *)nextloc);
+	nextloc += sizeof(krb5_ui_2);
+	krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_kvno, (unsigned char *)nextloc);
+	nextloc += sizeof(krb5_ui_2);
+	krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_type[0], (unsigned char *)nextloc);
+	nextloc += sizeof(krb5_ui_2);
+	krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_length[0], (unsigned char *)nextloc);
+	nextloc += sizeof(krb5_ui_2);
+
+	if (aux_data_entry->latest_mkey.key_data_length[0] > 0) {
+	    memcpy(nextloc, aux_data_entry->latest_mkey.key_data_contents[0],
+		aux_data_entry->latest_mkey.key_data_length[0]);
+	    nextloc += aux_data_entry->latest_mkey.key_data_length[0];
+	}
+    }
+
+    return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
+/* XXX WAF: should probably #ifdef this to be defined if version 1 is in use */
+/*
+ * If version of the KRB5_TL_ACTKVNO data is KRB5_TL_ACTKVNO_VER_1 then size of
+ * a actkvno tuple {act_kvno, act_time} entry is:
+ */
+#define ACTKVNO_TUPLE_SIZE sizeof(krb5_int16) + sizeof(krb5_int32)
+#define act_kvno(cp) (cp) /* return pointer to start of act_kvno data */
+#define act_time(cp) (cp) + sizeof(krb5_int16) /* return pointer to start of act_time data */
+
+krb5_error_code
+krb5_dbe_lookup_actkvno(krb5_context context,
+		      krb5_db_entry *entry,
+		      krb5_actkvno_node **actkvno_list)
+{
+    krb5_tl_data tl_data;
+    krb5_error_code code;
+    krb5_int16 version;
+    krb5_actkvno_node *head_data = NULL, *new_data = NULL, *prev_data = NULL;
+    unsigned int num_actkvno, i;
+    krb5_octet *next_tuple;
+
+    tl_data.tl_data_type = KRB5_TL_ACTKVNO;
+
+    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+	return (code);
+
+    if (tl_data.tl_data_contents == NULL) {
+	*actkvno_list = NULL;
+	return (0);
+    } else {
+	/* get version to determine how to parse the data */
+	krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
+	if (version == KRB5_TL_ACTKVNO_VER_1) {
+	    /*
+	     * Find number of tuple entries, remembering to account for version
+	     * field.
+	     */
+	    num_actkvno = (tl_data.tl_data_length - sizeof(version)) / ACTKVNO_TUPLE_SIZE;
+	    prev_data = NULL;
+	    /* next_tuple points to first tuple entry in the tl_data_contents */
+	    next_tuple = tl_data.tl_data_contents + sizeof(version);
+	    for (i = 0; i < num_actkvno; i++) {
+		new_data = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+		if (new_data == NULL) {
+		    krb5_free_actkvno_list(context, head_data);
+		    return (ENOMEM);
+		}
+		krb5_kdb_decode_int16(act_kvno(next_tuple), new_data->act_kvno);
+		krb5_kdb_decode_int32(act_time(next_tuple), new_data->act_time);
+		/* XXX WAF: may be able to deal with list pointers in a better
+		 * way, see add_mkey() */
+		new_data->next = NULL;
+		if (prev_data != NULL)
+		    prev_data->next = new_data;
+		else
+		    head_data = new_data;
+		prev_data = new_data;
+		next_tuple += ACTKVNO_TUPLE_SIZE;
+	    }
+	} else {
+	    krb5_set_error_message (context, KRB5_KDB_BAD_VERSION,
+		    "Illegal version number for KRB5_TL_ACTKVNO %d\n",
+		    version);
+	    return (KRB5_KDB_BAD_VERSION);
+	}
+    }
+    *actkvno_list = head_data;
+    return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_actkvno(krb5_context context,
+			krb5_db_entry *entry,
+			const krb5_actkvno_node *actkvno_list)
+{
+    krb5_error_code retval = 0;
+    krb5_int16 version;
+    krb5_tl_data new_tl_data;
+    krb5_octet *nextloc;
+    const krb5_actkvno_node *cur_actkvno;
+
+    /* XXX WAF: should kvno be verified that it exists for the princ entry? */
+    /* No, this should be handed by functions higher in the stack verifying the user data */
+
+    if (actkvno_list == NULL) {
+	return (EINVAL);
+    }
+
+    /* allocate initial KRB5_TL_ACTKVNO tl_data entry */
+    new_tl_data.tl_data_length = sizeof(version);
+    new_tl_data.tl_data_contents = (krb5_octet *) malloc(new_tl_data.tl_data_length);
+    if (new_tl_data.tl_data_contents == NULL) {
+	return (ENOMEM);
+    }
+    krb5_kdb_encode_int16((krb5_ui_2)KRB5_TL_ACTKVNO_VER_1, (unsigned char *)new_tl_data.tl_data_contents);
+
+    for (cur_actkvno = actkvno_list; cur_actkvno != NULL; cur_actkvno = cur_actkvno->next) {
+	new_tl_data.tl_data_length += ACTKVNO_TUPLE_SIZE;
+	new_tl_data.tl_data_contents = (krb5_octet *) realloc(new_tl_data.tl_data_contents, new_tl_data.tl_data_length);
+	if (new_tl_data.tl_data_contents == NULL) {
+	    return (ENOMEM);
+	}
+
+	/*
+	 * using realloc so tl_data_contents is required to correctly calculate
+	 * next location to store new tuple.
+	 */
+	nextloc = new_tl_data.tl_data_contents + new_tl_data.tl_data_length - ACTKVNO_TUPLE_SIZE;
+	krb5_kdb_encode_int16((krb5_ui_2)cur_actkvno->act_kvno, (unsigned char *)nextloc);
+	nextloc += sizeof(krb5_ui_2);
+	krb5_kdb_encode_int32((krb5_ui_4)cur_actkvno->act_time, (unsigned char *)nextloc);
+    }
+
+    new_tl_data.tl_data_type = KRB5_TL_ACTKVNO;
+    retval = krb5_dbe_update_tl_data(context, entry, &new_tl_data);
+    free(new_tl_data.tl_data_contents);
+
+    return (retval);
+}
+
+krb5_error_code
 krb5_dbe_update_last_pwd_change(context, entry, stamp)
     krb5_context context;
     krb5_db_entry *entry;

Modified: branches/mkey_migrate/src/lib/kdb/kdb5.h
===================================================================
--- branches/mkey_migrate/src/lib/kdb/kdb5.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/kdb5.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -129,7 +129,13 @@
     krb5_error_code (*get_master_key) (krb5_context kcontext,
 				       krb5_keyblock **key);
 
+    krb5_error_code (*set_master_key_list) (krb5_context kcontext,
+				            krb5_keyblock_node *keylist);
 
+    krb5_error_code (*get_master_key_list) (krb5_context kcontext,
+				            krb5_keyblock_node **keylist);
+
+
     krb5_error_code (*setup_master_key_name) (krb5_context kcontext,
 					      char *keyname,
 					      char *realm,
@@ -154,6 +160,13 @@
 					  krb5_kvno     kvno,
 					  krb5_keyblock *mkey);
 
+    krb5_error_code (*fetch_master_key_list) (krb5_context kcontext,
+					      krb5_principal mname,
+					      const krb5_keyblock *key,
+					      krb5_kvno            kvno,
+					      krb5_keyblock_node  **mkeys_list);
+
+
     krb5_error_code (*dbe_search_enctype) (krb5_context kcontext,
 					   krb5_db_entry *dbentp,
 					   krb5_int32 *start,

Modified: branches/mkey_migrate/src/lib/kdb/kdb_cpw.c
===================================================================
--- branches/mkey_migrate/src/lib/kdb/kdb_cpw.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/kdb_cpw.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -56,7 +56,7 @@
 #include <stdio.h>
 #include <errno.h>
 
-static int
+int
 get_key_data_kvno(context, count, data)
     krb5_context	  context;
     int			  count;

Modified: branches/mkey_migrate/src/lib/kdb/kdb_default.c
===================================================================
--- branches/mkey_migrate/src/lib/kdb/kdb_default.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/kdb_default.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -377,6 +377,10 @@
 }
 #endif /* LEAN_CLIENT */
 
+/* XXX WAF: I'm now thinking this fucntion should check to see if the fetched
+ * key matches the latest mkey in the master princ.  If it doesn't then the
+ * latest mkey should be returned by using the mkey_aux tl data.
+ */
 krb5_error_code
 krb5_db_def_fetch_mkey(krb5_context   context,
                        krb5_principal mname,
@@ -432,6 +436,9 @@
     }
 }
 
+/*
+ * Note, this verifies that the input mkey is currently protecting all the mkeys
+ */
 krb5_error_code
 krb5_def_verify_master_key(krb5_context    context,
                            krb5_principal  mprinc,
@@ -486,7 +493,153 @@
     return retval;
 }
 
+krb5_error_code
+krb5_def_fetch_mkey_list(krb5_context        context,
+                       krb5_principal        mprinc,
+                       const krb5_keyblock  *mkey,
+                       krb5_kvno             mkvno,
+                       krb5_keyblock_node  **mkeys_list)
+{
+    krb5_error_code retval;
+    krb5_db_entry master_entry;
+    int nprinc;
+    krb5_boolean more, found_key = FALSE;
+    krb5_keyblock tmp_mkey, tmp_clearkey;
+    krb5_keyblock_node *mkey_list_head, **mkey_list_node;
+    krb5_key_data *key_data;
+    krb5_mkey_aux_node	*mkey_aux_data_list, *aux_data_entry;
+    int i;
 
+    if (mkeys_list == NULL)
+	return (EINVAL);
+
+    memset(&tmp_mkey, 0, sizeof(tmp_mkey));
+    memset(&tmp_clearkey, 0, sizeof(tmp_clearkey));
+
+    nprinc = 1;
+    if ((retval = krb5_db_get_principal(context, mprinc,
+					&master_entry, &nprinc, &more)))
+	return (retval);
+	
+    if (nprinc != 1) {
+	if (nprinc)
+	    krb5_db_free_principal(context, &master_entry, nprinc);
+	return(KRB5_KDB_NOMASTERKEY);
+    } else if (more) {
+	krb5_db_free_principal(context, &master_entry, nprinc);
+	return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+    }
+
+    /*
+     * Check if the input mkey is the latest key and if it isn't then find the
+     * latest mkey.
+     */
+    if ((retval = krb5_dbekd_decrypt_key_data(context, &tmp_mkey,
+					      &master_entry.key_data[0],
+					      &tmp_clearkey, NULL)) != 0) {
+	/*
+	 * Note the mkvno may provide a hint as to which mkey_aux tuple to
+	 * decrypt.
+	 */
+	if ((retval = krb5_dbe_lookup_mkey_aux(context, &master_entry, &mkey_aux_data_list)))
+	    goto clean_n_exit;
+
+	/* for performance sake, try decrypting with matching kvno */
+	for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+	     aux_data_entry = aux_data_entry->next) {
+
+	    if (aux_data_entry->mkey_kvno == mkvno) {
+		if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+				   &tmp_clearkey, NULL) == 0) {
+		    found_key = TRUE;
+		    break;
+		}
+	    }
+	}
+	if (found_key != TRUE) {
+	    /* given the importance of acquiring the latest mkey, try brute force */
+	    for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+		 aux_data_entry = aux_data_entry->next) {
+
+		if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+						&tmp_clearkey, NULL) == 0) {
+		    found_key = TRUE;
+		    /* XXX WAF: should I issue warning about kvno not matching?
+		     */
+		    break;
+		}
+	    }
+	    if (found_key != TRUE) {
+		krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+			"Unable to decrypt latest master key with the provided master key\n");
+		retval = KRB5_KDB_BADMASTERKEY;
+		goto clean_n_exit;
+	    }
+	}
+    }
+
+    /*
+     * Extract all the mkeys from master_entry using the most current mkey and
+     * create a mkey list for the mkeys field in kdc_realm_t. 
+     */
+
+    mkey_list_head = (krb5_keyblock_node *) malloc(sizeof(krb5_keyblock_node));
+    if (mkey_list_head == NULL) {
+	retval = ENOMEM;
+	goto clean_n_exit;
+    }
+
+    memset(mkey_list_head, 0, sizeof(krb5_keyblock_node));
+    mkey_list_node = &mkey_list_head;
+
+    for (i=0; i < master_entry.n_key_data; i++) {
+	if (*mkey_list_node == NULL) {
+	    /* *mkey_list_node points to next field of previous node */
+	    *mkey_list_node = (krb5_keyblock_node *) malloc(sizeof(krb5_keyblock_node));
+	    if (*mkey_list_node == NULL) {
+		retval = ENOMEM;
+		goto clean_n_exit;
+	    }
+	    memset(*mkey_list_node, 0, sizeof(krb5_keyblock_node));
+	}
+	key_data = &master_entry.key_data[i];
+	retval = krb5_dbekd_decrypt_key_data(context, mkey,
+					     key_data, &((*mkey_list_node)->keyblock),
+					     NULL);
+	if (retval)
+	    goto clean_n_exit;
+
+	mkey_list_node = &((*mkey_list_node)->next);
+    }
+
+    *mkeys_list = mkey_list_head;
+
+clean_n_exit:
+
+    if (tmp_mkey.contents) {
+	memset(tmp_mkey.contents, 0, tmp_mkey.length);
+	krb5_db_free(context, tmp_mkey.contents);
+    }
+
+    if (tmp_clearkey.contents) {
+	memset(tmp_clearkey.contents, 0, tmp_clearkey.length);
+	krb5_db_free(context, tmp_clearkey.contents);
+    }
+
+    krb5_db_free_principal(context, &master_entry, nprinc);
+    if (retval != 0) {
+	krb5_keyblock_node *cur_node, *next_node;
+
+	for (cur_node = mkey_list_head; cur_node != NULL; cur_node = next_node) {
+	    next_node = cur_node->next;
+	    krb5_free_keyblock(context, &(cur_node->keyblock));
+	    krb5_xfree(cur_node);
+	}
+    }
+    
+    return retval;
+}
+
 krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
 				   char *pwd,
 				   krb5_keyblock *key )
@@ -502,6 +655,20 @@
     return 0;
 }
 
+krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext,
+				        krb5_keyblock_node *keylist )
+{
+    /* printf("default set master key\n"); */
+    return 0;
+}
+
+krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext,
+				        krb5_keyblock_node **keylist )
+{
+    /* printf("default get master key\n"); */
+    return 0;
+}
+
 krb5_error_code krb5_def_promote_db (krb5_context kcontext,
 				     char *s, char **args)
 {

Modified: branches/mkey_migrate/src/lib/kdb/keytab.c
===================================================================
--- branches/mkey_migrate/src/lib/kdb/keytab.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/keytab.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -124,6 +124,7 @@
     krb5_keytab_entry 	* entry;
 {
     krb5_context	  context;
+    krb5_keyblock_node  * master_keylist;
     krb5_keyblock       * master_key;
     krb5_error_code 	  kerror = 0;
     krb5_key_data 	* key_data;
@@ -163,10 +164,14 @@
     }
 
     /* match key */
-    kerror = krb5_db_get_mkey(context, &master_key);
+    kerror = krb5_db_get_mkey_list(context, &master_keylist);
     if (kerror)
 	goto error;
 
+    kerror = krb5_dbe_find_mkey(context, master_keylist, &db_entry, &master_key);
+    if (kerror)
+	goto error;
+
     /* For cross realm tgts, we match whatever enctype is provided;
      * for other principals, we only match the first enctype that is
      * found.  Since the TGS and AS code do the same thing, then we

Modified: branches/mkey_migrate/src/lib/kdb/libkdb5.exports
===================================================================
--- branches/mkey_migrate/src/lib/kdb/libkdb5.exports	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/kdb/libkdb5.exports	2009-01-09 20:11:57 UTC (rev 21721)
@@ -1,3 +1,4 @@
+get_key_data_kvno
 krb5_db_open
 krb5_db_inited
 krb5_db_alloc
@@ -5,7 +6,10 @@
 krb5_db_create
 krb5_db_delete_principal
 krb5_db_destroy
+krb5_db_fetch_latest_mkey
 krb5_db_fetch_mkey
+krb5_db_fetch_mkey_list
+krb5_db_free_mkey_list
 krb5_db_fini
 krb5_db_free_principal
 krb5_db_get_age
@@ -24,12 +28,19 @@
 krb5_dbe_cpw
 krb5_dbe_create_key_data
 krb5_dbe_crk
+krb5_dbe_fetch_act_mkey_list
 krb5_dbe_find_enctype
+krb5_dbe_find_mkey
 krb5_dbe_lookup_last_pwd_change
+krb5_dbe_lookup_actkvno
+krb5_dbe_lookup_mkvno
 krb5_dbe_lookup_mod_princ_data
 krb5_dbe_lookup_tl_data
 krb5_dbe_search_enctype
+krb5_dbe_update_actkvno
 krb5_dbe_update_last_pwd_change
+krb5_dbe_update_mkey_aux
+krb5_dbe_update_mkvno
 krb5_dbe_update_mod_princ_data
 krb5_dbe_update_tl_data
 krb5_dbekd_decrypt_key_data

Modified: branches/mkey_migrate/src/lib/krb5/error_tables/kdb5_err.et
===================================================================
--- branches/mkey_migrate/src/lib/krb5/error_tables/kdb5_err.et	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/krb5/error_tables/kdb5_err.et	2009-01-09 20:11:57 UTC (rev 21721)
@@ -57,6 +57,7 @@
 ec KRB5_KDB_INVALIDKEYSIZE,	"Key size in database is invalid"
 ec KRB5_KDB_CANTREAD_STORED,	"Cannot find/read stored master key"
 ec KRB5_KDB_BADSTORED_MKEY,	"Stored master key is corrupted"
+ec KRB5_KDB_NOACTMASTERKEY,	"Cannot find active master key"
 
 ec KRB5_KDB_CANTLOCK_DB,	"Insufficient access to lock database"
 

Modified: branches/mkey_migrate/src/lib/krb5/krb/kfree.c
===================================================================
--- branches/mkey_migrate/src/lib/krb5/krb/kfree.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/lib/krb5/krb/kfree.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -27,6 +27,7 @@
  */
 
 #include "k5-int.h"
+#include <kdb.h>
 
 void KRB5_CALLCONV
 krb5_free_address(krb5_context context, krb5_address *val)
@@ -713,3 +714,20 @@
 	return;
     krb5_xfree(pa_enc_ts);
 }
+
+void KRB5_CALLCONV
+krb5_free_key_data_contents(krb5_context context,
+                            krb5_key_data *key)
+{
+     int i, idx;
+     
+     idx = (key->key_data_ver == 1 ? 1 : 2);
+     for (i = 0; i < idx; i++) {
+	  if (key->key_data_contents[i]) {
+	       memset(key->key_data_contents[i], 0, key->key_data_length[i]);
+	       free(key->key_data_contents[i]);
+	  }
+     }
+     return;
+}
+

Modified: branches/mkey_migrate/src/plugins/kdb/db2/db2_exp.c
===================================================================
--- branches/mkey_migrate/src/plugins/kdb/db2/db2_exp.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/plugins/kdb/db2/db2_exp.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -192,6 +192,15 @@
 WRAP_K (krb5_db2_db_get_mkey,
 	( krb5_context context, krb5_keyblock **key),
 	(context, key));
+
+WRAP_K (krb5_db2_set_master_key_list,
+	( krb5_context kcontext, krb5_keyblock_node *keylist),
+	(kcontext, keylist));
+
+WRAP_K (krb5_db2_db_get_mkey_list,
+	( krb5_context context, krb5_keyblock_node **keylist),
+	(context, keylist));
+
 WRAP_K (krb5_db2_promote_db,
 	( krb5_context kcontext, char *conf_section, char **db_args ),
 	(kcontext, conf_section, db_args));
@@ -251,6 +260,8 @@
   /* db_free */                                wrap_krb5_db2_free,
   /* set_master_key */			       wrap_krb5_db2_set_master_key_ext,
   /* get_master_key */			       wrap_krb5_db2_db_get_mkey,
-  /* blah blah blah */ 0,0,0,0,0,0,
+  /* set_master_key_list */		       wrap_krb5_db2_set_master_key_list,
+  /* get_master_key_list */	    	       wrap_krb5_db2_db_get_mkey_list,
+  /* blah blah blah */ 0,0,0,0,0,0,0,
   /* promote_db */			       wrap_krb5_db2_promote_db,
 };

Modified: branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.c
===================================================================
--- branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -434,6 +434,37 @@
     return 0;
 }
 
+krb5_error_code
+krb5_db2_db_set_mkey_list(krb5_context context, krb5_keyblock_node *key_list)
+{
+    krb5_db2_context *db_ctx;
+    kdb5_dal_handle *dal_handle;
+
+    if (!k5db2_inited(context))
+	return (KRB5_KDB_DBNOTINITED);
+
+    dal_handle = context->dal_handle;
+    db_ctx = dal_handle->db_context;
+    db_ctx->db_master_key_list = key_list;
+    return 0;
+}
+
+krb5_error_code
+krb5_db2_db_get_mkey_list(krb5_context context, krb5_keyblock_node **key_list)
+{
+    krb5_db2_context *db_ctx;
+    kdb5_dal_handle *dal_handle;
+
+    if (!k5db2_inited(context))
+	return (KRB5_KDB_DBNOTINITED);
+
+    dal_handle = context->dal_handle;
+    db_ctx = dal_handle->db_context;
+    *key_list = db_ctx->db_master_key_list;
+
+    return 0;
+}
+
 /*
  * Set the "name" of the current database to some alternate value.
  *

Modified: branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.h
===================================================================
--- branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.h	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/plugins/kdb/db2/kdb_db2.h	2009-01-09 20:11:57 UTC (rev 21721)
@@ -42,7 +42,8 @@
     int                 db_locks_held;  /* Number of times locked       */
     int                 db_lock_mode;   /* Last lock mode, e.g. greatest*/
     krb5_boolean        db_nb_locks;    /* [Non]Blocking lock modes     */
-    krb5_keyblock      *db_master_key;  /* Master key of database       */
+    krb5_keyblock      *db_master_key; /* Master key of database */
+    krb5_keyblock_node *db_master_key_list;  /* Master key list of database */
     osa_adb_policy_t    policy_db;
     krb5_boolean tempdb;
 } krb5_db2_context;
@@ -121,8 +122,15 @@
 krb5_error_code
 krb5_db2_db_get_mkey( krb5_context context,
 		      krb5_keyblock **key);
+krb5_error_code
+krb5_db2_db_set_mkey_list( krb5_context context,
+		      krb5_keyblock_node *keylist);
 
 krb5_error_code
+krb5_db2_db_get_mkey_list( krb5_context context,
+		      krb5_keyblock_node **keylist);
+
+krb5_error_code
 krb5_db2_db_put_principal( krb5_context context,
 			   krb5_db_entry *entries,
 			   register int *nentries,

Modified: branches/mkey_migrate/src/plugins/kdb/ldap/ldap_exp.c
===================================================================
--- branches/mkey_migrate/src/plugins/kdb/ldap/ldap_exp.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/plugins/kdb/ldap/ldap_exp.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -78,10 +78,13 @@
             /* optional functions */
   /* set_master_key */			       krb5_ldap_set_mkey,
   /* get_master_key */			       krb5_ldap_get_mkey,
+  /* set_master_key_list */		       NULL,
+  /* get_master_key_list */		       NULL,
   /* setup_master_key_name */		       NULL,
   /* store_master_key */		       NULL,
   /* fetch_master_key */		       NULL /* krb5_ldap_fetch_mkey */,
   /* verify_master_key */		       NULL /* krb5_ldap_verify_master_key */,
+  /* fetch_master_key_list */		       NULL,
   /* Search enc type */                        NULL,
   /* Change pwd   */                           NULL
 

Modified: branches/mkey_migrate/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
===================================================================
--- branches/mkey_migrate/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c	2009-01-09 01:42:37 UTC (rev 21720)
+++ branches/mkey_migrate/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c	2009-01-09 20:11:57 UTC (rev 21721)
@@ -149,6 +149,50 @@
     return(0);
 }
 
+krb5_error_code
+krb5_dbe_lookup_mkvno(krb5_context context,
+		      krb5_db_entry *entry,
+		      krb5_kvno *mkvno)
+{
+    krb5_tl_data tl_data;
+    krb5_error_code code;
+    krb5_int16 tmp;
+
+    tl_data.tl_data_type = KRB5_TL_MKVNO;
+
+    if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+	return (code);
+
+    /* XXX need to think about this */
+    if (tl_data.tl_data_length != 2) {
+	*mkvno = 0;
+	return (0);
+    }
+
+    /* XXX this needs to be the inverse of how this is encoded */
+    krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp);
+
+    *mkvno = (krb5_kvno) tmp;
+
+    return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkvno(krb5_context    context,
+		      krb5_db_entry * entry,
+		      krb5_kvno       mkvno)
+{
+    krb5_tl_data tl_data;
+    krb5_octet buf[2];		/* this is the encoded size of an int16 */
+
+    tl_data.tl_data_type = KRB5_TL_MKVNO;
+    tl_data.tl_data_length = sizeof(buf);
+    krb5_kdb_encode_int16((krb5_int16) mkvno, buf);
+    tl_data.tl_data_contents = buf;
+
+    return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
 /* it seems odd that there's no function to remove a tl_data, but if
    I need one, I'll add one */
 




More information about the cvs-krb5 mailing list