Master Key Migration Project
authorWill Fiveash <will.fiveash@oracle.com>
Fri, 30 Jan 2009 23:55:14 +0000 (23:55 +0000)
committerWill Fiveash <will.fiveash@oracle.com>
Fri, 30 Jan 2009 23:55:14 +0000 (23:55 +0000)
Commit for the Master Key Migration Project.
http://k5wiki.kerberos.org/wiki/Projects/Master_Key_Migration

This commit provides the ability to add a new master key (with an
enctype differing from the current master key) to the master key
principal and stash file and then migrate the encryption of existing
principals long term keys to use the new master key.  In addition
deletion of master keys is provided.

ticket: 6354

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21844 dc483132-0cff-0310-8789-dd5450dbe970

40 files changed:
src/include/kdb.h
src/kadmin/cli/kadmin.c
src/kadmin/dbutil/Makefile.in
src/kadmin/dbutil/dump.c
src/kadmin/dbutil/kdb5_create.c
src/kadmin/dbutil/kdb5_mkey.c [new file with mode: 0644]
src/kadmin/dbutil/kdb5_stash.c
src/kadmin/dbutil/kdb5_util.M
src/kadmin/dbutil/kdb5_util.c
src/kadmin/dbutil/kdb5_util.h
src/kadmin/server/ovsec_kadmd.c
src/kdc/do_as_req.c
src/kdc/do_tgs_req.c
src/kdc/extern.c
src/kdc/extern.h
src/kdc/kdc_preauth.c
src/kdc/kdc_util.c
src/kdc/main.c
src/lib/kadm5/srv/libkadm5srv.exports
src/lib/kadm5/srv/server_kdb.c
src/lib/kadm5/srv/svr_iters.c
src/lib/kadm5/srv/svr_principal.c
src/lib/kdb/kdb5.c
src/lib/kdb/kdb_cpw.c
src/lib/kdb/kdb_default.c
src/lib/kdb/keytab.c
src/lib/kdb/libkdb5.exports
src/lib/krb5/error_tables/kdb5_err.et
src/plugins/kdb/db2/db2_exp.c
src/plugins/kdb/db2/kdb_db2.c
src/plugins/kdb/db2/kdb_db2.h
src/plugins/kdb/ldap/ldap_exp.c
src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c
src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h

index a3142c1d7713c11ead1a09b90647e70fb59b12fb..240ac0fd523b6f4aaa90e06ff6d2cbbb2aec3a9e 100644 (file)
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #ifndef KRB5_KDB5__
 #define KRB5_KDB5__
 
@@ -173,7 +178,34 @@ typedef struct __krb5_key_salt_tuple {
 #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
+
+/* version number for KRB5_TL_MKEY_AUX data */
+#define KRB5_TL_MKEY_AUX_VER    1
+
+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 */
+    krb5_key_data    latest_mkey; /* most recent mkey */
+} krb5_mkey_aux_node;
+
+typedef struct _krb5_keylist_node {
+    krb5_keyblock keyblock;
+    krb5_kvno     kvno;
+    struct _krb5_keylist_node *next;
+} krb5_keylist_node;
+
 /*
  * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
  * on the principal.
@@ -276,6 +308,13 @@ krb5_error_code krb5_db_set_mkey ( krb5_context context,
                                   krb5_keyblock *key);
 krb5_error_code krb5_db_get_mkey ( krb5_context kcontext,
                                   krb5_keyblock **key );
+
+krb5_error_code krb5_db_set_mkey_list( krb5_context context,
+                                       krb5_keylist_node * keylist);
+
+krb5_error_code krb5_db_get_mkey_list( krb5_context kcontext,
+                                       krb5_keylist_node ** keylist);
+
 krb5_error_code krb5_db_free_master_key ( krb5_context kcontext,
                                          krb5_keyblock *key );
 krb5_error_code krb5_db_store_master_key  ( krb5_context kcontext, 
@@ -284,6 +323,11 @@ krb5_error_code krb5_db_store_master_key  ( krb5_context kcontext,
                                            krb5_kvno kvno,
                                            krb5_keyblock *key,
                                            char *master_pwd);
+krb5_error_code krb5_db_store_master_key_list  ( krb5_context kcontext, 
+                                                char *keyfile, 
+                                                krb5_principal mname,
+                                                krb5_keylist_node *keylist,
+                                                char *master_pwd);
 krb5_error_code krb5_db_fetch_mkey  ( krb5_context   context,
                                      krb5_principal mname,
                                      krb5_enctype   etype,
@@ -298,6 +342,17 @@ krb5_error_code krb5_db_verify_master_key ( krb5_context   kcontext,
                                             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_keylist_node  **mkeys_list );
+
+krb5_error_code
+krb5_db_free_mkey_list( krb5_context         context,
+                       krb5_keylist_node  *mkey_list );
+
+krb5_error_code
 krb5_dbe_find_enctype( krb5_context    kcontext,
                       krb5_db_entry    *dbentp,
                       krb5_int32               ktype,
@@ -336,16 +391,62 @@ krb5_dbekd_encrypt_key_data( krb5_context                   context,
                             int                          keyver,
                             krb5_key_data              * key_data);
 
+krb5_error_code
+krb5_dbe_fetch_act_key_list(krb5_context          context,
+                            krb5_principal       princ,
+                            krb5_actkvno_node  **act_key_list);
+
+krb5_error_code
+krb5_dbe_find_act_mkey( krb5_context          context,
+                        krb5_keylist_node   * mkey_list,
+                        krb5_actkvno_node   * act_mkey_list,
+                        krb5_kvno           * act_kvno,
+                        krb5_keyblock      ** act_mkey);
+
+krb5_error_code
+krb5_dbe_find_mkey( krb5_context        context,
+                    krb5_keylist_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
@@ -382,6 +483,11 @@ krb5_dbe_lookup_last_pwd_change( krb5_context          context,
                                 krb5_db_entry       * entry,
                                 krb5_timestamp      * stamp);
 
+krb5_error_code
+krb5_dbe_delete_tl_data( krb5_context    context,
+                         krb5_db_entry * entry,
+                         krb5_int16      tl_data_type);
+
 krb5_error_code
 krb5_dbe_update_tl_data( krb5_context          context,
                         krb5_db_entry       * entry,
@@ -421,6 +527,12 @@ krb5_dbe_apw( krb5_context   context,
              char              * passwd,
              krb5_db_entry     * db_entry);
 
+int
+krb5_db_get_key_data_kvno( krb5_context           context,
+                          int             count,
+                          krb5_key_data * data);
+
+
 /* default functions. Should not be directly called */
 /*
  *   Default functions prototype
@@ -443,6 +555,12 @@ krb5_def_store_mkey( krb5_context context,
                     krb5_keyblock *key,
                     char *master_pwd);
 
+krb5_error_code
+krb5_def_store_mkey_list( krb5_context context,
+                         char *keyfile,
+                         krb5_principal mname,
+                         krb5_keylist_node *keylist,
+                         char *master_pwd);
 
 krb5_error_code
 krb5_db_def_fetch_mkey( krb5_context   context,
@@ -457,13 +575,26 @@ krb5_def_verify_master_key( krb5_context   context,
                            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_keylist_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_keylist_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_keylist_node **keylist );
+
 krb5_error_code
 krb5_dbe_def_cpw( krb5_context   context,
                  krb5_keyblock       * master_key,
@@ -536,7 +667,6 @@ krb5_db_free_policy( krb5_context kcontext,
                     osa_policy_ent_t policy);
 
 
-
 krb5_error_code
 krb5_db_set_context
        (krb5_context, void *db_context);
@@ -545,6 +675,21 @@ krb5_error_code
 krb5_db_get_context
        (krb5_context, void **db_context);
 
+void
+krb5_dbe_free_key_data_contents(krb5_context, krb5_key_data *);
+
+void
+krb5_dbe_free_key_list(krb5_context, krb5_keylist_node *);
+
+void
+krb5_dbe_free_actkvno_list(krb5_context, krb5_actkvno_node *);
+
+void
+krb5_dbe_free_mkey_aux_list(krb5_context, krb5_mkey_aux_node *);
+
+void
+krb5_dbe_free_tl_data(krb5_context, krb5_tl_data *);
+
 #define KRB5_KDB_DEF_FLAGS     0
 
 #define KDB_MAX_DB_NAME                        128
@@ -669,6 +814,11 @@ typedef struct _kdb_vftabl {
     krb5_error_code (*get_master_key)    ( krb5_context kcontext,
                                           krb5_keyblock **key);
 
+    krb5_error_code (*set_master_key_list) ( krb5_context kcontext,
+                                            krb5_keylist_node *keylist);
+
+    krb5_error_code (*get_master_key_list) ( krb5_context kcontext,
+                                            krb5_keylist_node **keylist);
 
     krb5_error_code (*setup_master_key_name) ( krb5_context kcontext,
                                               char *keyname,
@@ -694,6 +844,18 @@ typedef struct _kdb_vftabl {
                                           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_keylist_node  **mkeys_list);
+
+    krb5_error_code (*store_master_key_list)  ( krb5_context kcontext, 
+                                               char *db_arg, 
+                                               krb5_principal mname,
+                                               krb5_keylist_node *keylist,
+                                               char *master_pwd);
+
     krb5_error_code (*dbe_search_enctype) ( krb5_context kcontext, 
                                            krb5_db_entry *dbentp, 
                                            krb5_int32 *start, 
index 469deaa77121b4976225a652efd7dd4992a104bb..af6673d079ce66aac7a2beaf4e9d0eaffb4dd9de 100644 (file)
@@ -1535,6 +1535,8 @@ void kadmin_getprinc(argc, argv)
            } else
                printf("no salt\n");
        }
+       printf("MKey: vno %d\n",
+              dprinc.mkvno);
 
        printf("Attributes:");
        for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
index dc5881b436958b076963c7488d593f1605817ee2..e88d8b3239f37f6b5423529b683f169ba22fb923 100644 (file)
@@ -10,14 +10,18 @@ KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
 
 PROG = kdb5_util
 
-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) $(KRB5_BASE_DEPLIBS)
-       $(CC_LINK) -o $(PROG) $(OBJS) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS)
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(GETDATE)
+       $(CC_LINK) -o $(PROG) $(OBJS) $(GETDATE) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS)
 
 import_err.c import_err.h: $(srcdir)/import_err.et
 
index 24f4d09f2005a4276405636928186ebeee25e476..55677f7a5e6cd438e4ec96bfadc06f542344ea19 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * kadmin/dbutil/dump.c
  *
- * Copyright 1990,1991,2001,2006,2008 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2001,2006,2008,2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -46,7 +46,8 @@
  * Needed for master key conversion.
  */
 static int                     mkey_convert;
-static krb5_keyblock           new_master_keyblock;
+krb5_keyblock                  new_master_keyblock;
+krb5_kvno                       new_mkvno;
 
 static int     backwards;
 static int     recursive;
@@ -178,6 +179,7 @@ extern krb5_boolean dbactive;
 extern int             exit_status;
 extern krb5_context    util_context;
 extern kadm5_config_params global_params;
+extern krb5_db_entry      master_entry;
 
 /* Strings */
 
@@ -260,7 +262,7 @@ static const char dump_tmptrail[] = "~";
 /*
  * Re-encrypt the key_data with the new master key...
  */
-static krb5_error_code master_key_convert(context, db_entry)
+krb5_error_code master_key_convert(context, db_entry)
     krb5_context         context;
     krb5_db_entry      * db_entry;
 {
@@ -274,47 +276,49 @@ static krb5_error_code master_key_convert(context, db_entry)
 
     is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);
 
-    if (is_mkey && db_entry->n_key_data != 1)
-           fprintf(stderr,
-                   "Master key db entry has %d keys, expecting only 1!\n",
-                   db_entry->n_key_data);
-    for (i=0; i < db_entry->n_key_data; i++) {
-       key_data = &db_entry->key_data[i];
-       if (key_data->key_data_length == 0)
-           continue;
-       retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
-                                            key_data, &v5plainkey,
-                                            &keysalt);
-       if (retval)
-               return retval;
-
-       memset(&new_key_data, 0, sizeof(new_key_data));
-
-       if (is_mkey) {
-               key_ptr = &new_master_keyblock;
-               /* override mkey princ's kvno */
-               if (global_params.mask & KADM5_CONFIG_KVNO)
-                       kvno = global_params.kvno;
-               else
-                       kvno = (krb5_kvno) key_data->key_data_kvno;
-       } else {
-               key_ptr = &v5plainkey;
-               kvno = (krb5_kvno) key_data->key_data_kvno;
-       }
-
-       retval = krb5_dbekd_encrypt_key_data(context, &new_master_keyblock,
-                                            key_ptr, &keysalt,
-                                            (int) kvno,
-                                            &new_key_data);
-       if (retval)
-               return retval;
-       krb5_free_keyblock_contents(context, &v5plainkey);
-       for (j = 0; j < key_data->key_data_ver; j++) {
-           if (key_data->key_data_length[j]) {
-               free(key_data->key_data_contents[j]);
-           }
-       }
-       *key_data = new_key_data;
+    if (is_mkey) {
+        retval = add_new_mkey(context, db_entry, &new_master_keyblock, new_mkvno);
+        if (retval)
+            return retval;
+    } else {
+        for (i=0; i < db_entry->n_key_data; i++) {
+            krb5_keyblock   *tmp_mkey;
+
+            key_data = &db_entry->key_data[i];
+            if (key_data->key_data_length == 0)
+                continue;
+            retval = krb5_dbe_find_mkey(context, master_keylist, db_entry, &tmp_mkey);
+            if (retval)
+                return retval;
+            retval = krb5_dbekd_decrypt_key_data(context, tmp_mkey,
+                                                 key_data, &v5plainkey,
+                                                 &keysalt);
+            if (retval)
+                    return retval;
+
+            memset(&new_key_data, 0, sizeof(new_key_data));
+
+            key_ptr = &v5plainkey;
+            kvno = (krb5_kvno) key_data->key_data_kvno;
+
+            retval = krb5_dbekd_encrypt_key_data(context, &new_master_keyblock,
+                                                 key_ptr, &keysalt,
+                                                 (int) kvno,
+                                                 &new_key_data);
+            if (retval)
+                    return retval;
+            krb5_free_keyblock_contents(context, &v5plainkey);
+            for (j = 0; j < key_data->key_data_ver; j++) {
+                if (key_data->key_data_length[j]) {
+                    free(key_data->key_data_contents[j]);
+                }
+            }
+            *key_data = new_key_data;
+        }
+       assert(new_mkvno > 0);
+        retval = krb5_dbe_update_mkvno(context, db_entry, new_mkvno);
+        if (retval)
+                return retval;
     }
     return 0;
 }
@@ -1189,6 +1193,11 @@ dump_db(argc, argv)
                            exit(1);
                    }
            }
+            /*
+             * get new master key vno that will be used to protect princs, used
+             * later on.
+             */
+            new_mkvno = get_next_kvno(util_context, &master_entry);
     }
 
     kret = 0;
index c7fb31bb20fce64d87d73ec6f24bec56dbb20678..6a638a35122d32985e4476d7a48aae1f29caae3e 100644 (file)
@@ -230,6 +230,10 @@ master key name '%s'\n",
 
        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);
@@ -315,6 +319,9 @@ master key name '%s'\n",
        com_err(progname, retval, "while adding entries to the database");
        exit_status++; return;
     }
+
+
+
     /*
      * Always stash the master key so kadm5_create does not prompt for
      * it; delete the file below if it was not requested.  DO NOT EXIT
@@ -414,11 +421,10 @@ add_principal(context, princ, op, pblock)
     krb5_error_code      retval;
     krb5_db_entry        entry;
     krb5_kvno             mkey_kvno;
-
     krb5_timestamp       now;
     struct iterate_args          iargs;
-
     int                          nentries = 1;
+    krb5_actkvno_node     actkvno;
 
     memset((char *) &entry, 0, sizeof(entry));
 
@@ -455,6 +461,21 @@ add_principal(context, princ, op, pblock)
                                                  &master_keyblock, NULL, 
                                                  mkey_kvno, entry.key_data)))
            return retval;
+        /*
+         * There should always be at least one "active" mkey so creating the
+         * KRB5_TL_ACTKVNO entry now so the initial mkey is active.
+         */
+        actkvno.next = NULL;
+        actkvno.act_kvno = mkey_kvno;
+        /* earliest possible time in case system clock is set back */
+        actkvno.act_time = 0;
+        if ((retval = krb5_dbe_update_actkvno(context, &entry, &actkvno)))
+            return retval;
+
+        /* so getprinc shows the right kvno */
+        if ((retval = krb5_dbe_update_mkvno(context, &entry, mkey_kvno)))
+            return retval;
+
        break;
     case TGT_KEY:
        iargs.ctx = context;
diff --git a/src/kadmin/dbutil/kdb5_mkey.c b/src/kadmin/dbutil/kdb5_mkey.c
new file mode 100644 (file)
index 0000000..d2d5176
--- /dev/null
@@ -0,0 +1,1419 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 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"
+
+#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
+#define SOLARIS_REGEXPS
+#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
+#define POSIX_REGEXPS
+#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
+#define BSD_REGEXPS
+#else
+#error I cannot find any regexp functions
+#endif
+#ifdef SOLARIS_REGEXPS
+#include       <regexpr.h>
+#endif
+#ifdef POSIX_REGEXPS
+#include       <regex.h>
+#endif
+
+extern krb5_keyblock master_keyblock; /* current mkey */
+extern krb5_kvno   master_kvno;
+extern krb5_principal master_princ;
+extern krb5_keylist_node *master_keylist;
+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;
+}
+
+krb5_kvno
+get_next_kvno(krb5_context context, krb5_db_entry *entry)
+{
+    krb5_kvno new_kvno;
+
+    new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
+                                         entry->key_data);
+    new_kvno++;
+    /* deal with wrapping */
+    if (new_kvno == 0)
+        new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
+
+    return (new_kvno);
+}
+
+krb5_error_code
+add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
+             krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
+{
+    krb5_error_code retval = 0;
+    int old_key_data_count, i;
+    krb5_kvno new_mkey_kvno;
+    krb5_key_data tmp_key_data, *old_key_data;
+    krb5_mkey_aux_node  *mkey_aux_data_head = NULL, **mkey_aux_data;
+    krb5_keylist_node  *keylist_node;
+
+    /* do this before modifying master_entry key_data */
+    new_mkey_kvno = get_next_kvno(context, master_entry);
+    /* verify the requested mkvno if not 0 is the one that would be used here. */
+    if (use_mkvno != 0 && new_mkey_kvno != use_mkvno)
+        return (KRB5_KDB_KVNONOMATCH);
+
+    /* save the old keydata */
+    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)
+        return (ENOMEM);
+
+    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;
+
+    /* Note, mkey does not have salt */
+    /* add new mkey encrypted with itself to mkey princ entry */
+    if ((retval = krb5_dbekd_encrypt_key_data(context, new_mkey,
+                                              new_mkey, NULL, 
+                                              (int) new_mkey_kvno,
+                                              &master_entry->key_data[0]))) {
+        return (retval);
+    }
+    /* the mvkno should be that of the newest mkey */
+    if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) {
+        krb5_free_key_data_contents(context, &master_entry->key_data[0]);
+        return (retval);
+    }
+    /*
+     * Need to decrypt old keys with the current mkey which is in the global
+     * master_keyblock and encrypt those keys with the latest mkey.  And while
+     * the old keys are being decrypted, use those to create the
+     * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of
+     * the older mkeys.
+     *
+     * 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) {
+        retval = ENOMEM;
+        goto clean_n_exit;
+    }
+    memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
+    mkey_aux_data = &mkey_aux_data_head;
+
+    for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
+         keylist_node = keylist_node->next, i++) {
+
+        /*
+         * 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) {
+                retval = ENOMEM;
+                goto clean_n_exit;
+            }
+            memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
+        }
+
+        memset(&tmp_key_data, 0, sizeof(tmp_key_data));
+        /* encrypt the new mkey with the older mkey */
+        retval = krb5_dbekd_encrypt_key_data(context, &keylist_node->keyblock,
+                                             new_mkey,
+                                             NULL, /* no keysalt */
+                                             (int) new_mkey_kvno,
+                                             &tmp_key_data);
+        if (retval)
+            goto clean_n_exit;
+
+        (*mkey_aux_data)->latest_mkey = tmp_key_data;
+        (*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
+        mkey_aux_data = &((*mkey_aux_data)->next);
+
+        /*
+         * Store old key in master_entry keydata past the new mkey
+         */
+        retval = krb5_dbekd_encrypt_key_data(context, new_mkey,
+                                             &keylist_node->keyblock,
+                                             NULL, /* no keysalt */
+                                             (int) keylist_node->kvno,
+                                             &master_entry->key_data[i]);
+        if (retval)
+            goto clean_n_exit;
+    }
+    assert(i == old_key_data_count + 1);
+
+    if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
+                                           mkey_aux_data_head))) {
+        goto clean_n_exit;
+    }
+
+clean_n_exit:
+    if (mkey_aux_data_head)
+        krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
+    return (retval);
+}
+
+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;
+    krb5_boolean more = 0;
+    krb5_data pwd;
+    krb5_kvno new_mkey_kvno;
+    krb5_keyblock new_mkeyblock;
+    krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN;
+    char *new_mkey_password;
+    krb5_db_entry master_entry;
+    krb5_timestamp now;
+
+    /*
+     * 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, "e:s")) != -1) {
+        switch(optchar) {
+        case 'e':
+            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;
+        }
+    }
+
+    if (new_master_enctype == ENCTYPE_UNKNOWN)
+        new_master_enctype = global_params.enctype;
+
+    /* 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 getting master key principal %s",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries == 0) {
+        com_err(progname, KRB5_KDB_NOENTRY,
+                "principal %s not found in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries > 1) {
+        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
+                "principal %s has multiple entries in Kerberos database",
+                mkey_fullname);
+        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_mkeyblock);
+    if (retval) {
+        com_err(progname, retval, "while transforming master key from password");
+        exit_status++;
+        return;
+    }
+
+    retval = add_new_mkey(util_context, &master_entry, &new_mkeyblock, 0);
+    if (retval) {
+        com_err(progname, retval, "adding new master key to master principal");
+        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_mkeyblock,
+                                          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);
+    zap((char *)master_keyblock.contents, master_keyblock.length);
+    free(master_keyblock.contents);
+    zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
+    free(new_mkeyblock.contents);
+    if (pw_str) {
+        zap(pw_str, pw_size);
+        free(pw_str);
+    }
+    free(master_salt.data);
+    free(mkey_fullname);
+
+    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, found;
+    krb5_keylist_node  *keylist_node;
+
+    if (argc < 2 || argc > 3) {
+        /* usage calls exit */
+        usage();
+    }
+
+    use_kvno = atoi(argv[1]);
+    if (use_kvno == 0) {
+        com_err(progname, EINVAL, "0 is an invalid KVNO value");
+        exit_status++;
+        return;
+    } else {
+        /* verify use_kvno is valid */
+        for (keylist_node = master_keylist, found = FALSE; keylist_node != NULL;
+             keylist_node = keylist_node->next) {
+            if (use_kvno == keylist_node->kvno) {
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) {
+            com_err(progname, EINVAL, "%d is an invalid KVNO value", use_kvno);
+            exit_status++;
+            return;
+        }
+    }
+
+    if ((retval = krb5_timeofday(util_context, &now))) {
+        com_err(progname, retval, "while getting current time");
+        exit_status++;
+        return;
+    }
+
+    if (argc == 3) {
+        time_t t = get_date(argv[2]);
+         if (t == -1) {
+            com_err(progname, 0, "could not parse date-time string '%s'",
+                    argv[2]);
+            exit_status++;
+            return;
+        } else
+            start_time = (krb5_timestamp) t;
+    } else {
+        start_time = now;
+    }
+
+    /*
+     * Need to:
+     *
+     * 1. get mkey princ
+     * 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 getting master key principal %s",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries == 0) {
+        com_err(progname, KRB5_KDB_NOENTRY,
+                "principal %s not found in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries > 1) {
+        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
+                "principal %s has multiple entries in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    }
+
+    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
+    if (retval != 0) {
+        com_err(progname, retval,
+                "while looking up active version of master key");
+        exit_status++;
+        return;
+    }
+
+    /* 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;
+    }
+    memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
+
+    new_actkvno->act_kvno = use_kvno;
+    new_actkvno->act_time = start_time;
+
+    /*
+     * determine which nodes to delete and where to insert new act kvno node
+     */
+
+    if (actkvno_list == NULL) {
+        /* new actkvno is the list */
+        new_actkvno_list_head = new_actkvno;
+    } else {
+        krb5_boolean inserted = FALSE, trimed = FALSE;
+
+        for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
+             cur_actkvno != NULL;
+             prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+            if (cur_actkvno->act_kvno == use_kvno) {
+                cur_actkvno->act_time = start_time;
+                inserted = TRUE;   /* fake it */
+            }
+            if (!inserted) {
+                if (new_actkvno->act_time < cur_actkvno->act_time) {
+                    if (prev_actkvno) {
+                        prev_actkvno->next = new_actkvno;
+                        new_actkvno->next = cur_actkvno;
+                    } else {
+                        new_actkvno->next = actkvno_list;
+                        actkvno_list = new_actkvno;
+                    }
+                    inserted = TRUE;
+                } else if (cur_actkvno->next == NULL) {
+                    /* end of line, just add new node to end of list */
+                    cur_actkvno->next = new_actkvno;
+                    inserted = TRUE;
+                }
+            }
+            if (!trimed) {
+                /* trim entries in past that are superceded */
+                if (cur_actkvno->act_time > now) {
+                    if (prev_actkvno) {
+                        new_actkvno_list_head = prev_actkvno;
+                    } else {
+                        new_actkvno_list_head = actkvno_list;
+                    }
+                    trimed = TRUE;
+                } else if (cur_actkvno->next == NULL) {
+                    /* XXX this is buggy, fix soon. */
+                    new_actkvno_list_head = cur_actkvno;
+                    trimed = TRUE;
+                }
+            }
+            if (trimed && inserted)
+                break;
+        }
+    }
+
+    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);
+    krb5_dbe_free_actkvno_list(util_context, actkvno_list);
+    return;
+}
+
+void
+kdb5_list_mkeys(int argc, char *argv[])
+{
+    krb5_error_code retval;
+    char  *mkey_fullname, *output_str = NULL, enctype[BUFSIZ];
+    krb5_kvno  act_kvno;
+    krb5_timestamp act_time;
+    krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno, *prev_actkvno;
+    krb5_db_entry master_entry;
+    int   nentries = 0;
+    krb5_boolean more = 0;
+    krb5_keylist_node  *cur_kb_node;
+    krb5_keyblock *act_mkey;
+
+    if (master_keylist == NULL) {
+        com_err(progname, retval, "master keylist not initialized");
+        exit_status++;
+        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 getting master key principal %s",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries == 0) {
+        com_err(progname, KRB5_KDB_NOENTRY,
+                "principal %s not found in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries > 1) {
+        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
+                "principal %s has multiple entries in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    }
+
+    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
+    if (retval != 0) {
+        com_err(progname, retval, "while looking up active kvno list");
+        exit_status++;
+        return;
+    }
+
+    if (actkvno_list == NULL) {
+        act_kvno = master_entry.key_data[0].key_data_kvno;
+    } else {
+        retval = krb5_dbe_find_act_mkey(util_context, master_keylist,
+                                        actkvno_list, &act_kvno, &act_mkey);
+        if (retval == KRB5_KDB_NOACTMASTERKEY) {
+            /* Maybe we went through a time warp, and the only keys
+               with activation dates have them set in the future?  */
+            com_err(progname, retval, "");
+            /* Keep going.  */
+            act_kvno = -1;
+        } else if (retval != 0) {
+            com_err(progname, retval, "while looking up active master key");
+            exit_status++;
+            return;
+        }
+    }
+
+    printf("Master keys for Principal: %s\n", mkey_fullname);
+
+    for (cur_kb_node = master_keylist; cur_kb_node != NULL;
+         cur_kb_node = cur_kb_node->next) {
+
+        if ((retval = krb5_enctype_to_string(cur_kb_node->keyblock.enctype,
+                                             enctype, sizeof(enctype)))) {
+            com_err(progname, retval, "while getting enctype description");
+            exit_status++;
+            return;
+        }
+
+        if (actkvno_list != NULL) {
+            act_time = 0;
+            for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
+                 cur_actkvno = cur_actkvno->next) {
+                if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
+                    act_time = cur_actkvno->act_time;
+                    break;
+                }
+            }
+        } else {
+            /*
+             * mkey princ doesn't have an active knvo list so assume the current
+             * key is active now
+             */
+            if ((retval = krb5_timeofday(util_context, &act_time))) {
+                com_err(progname, retval, "while getting current time");
+                exit_status++;
+                return;
+            }
+        }
+
+        if (cur_kb_node->kvno == act_kvno) {
+            /* * indicates kvno is currently active */
+            retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, Active on: %s *\n",
+                              cur_kb_node->kvno, enctype, strdate(act_time));
+        } else {
+            if (act_time) {
+                retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, Active on: %s\n",
+                                  cur_kb_node->kvno, enctype, strdate(act_time));
+            } else {
+                retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, No activate time set\n",
+                                  cur_kb_node->kvno, enctype);
+            }
+        }
+        if (retval == -1) {
+            com_err(progname, ENOMEM, "asprintf could not allocate enough memory to hold output");
+            exit_status++;
+            return;
+        }
+        printf("%s", output_str);
+        free(output_str);
+        output_str = NULL;
+    }
+
+    /* clean up */
+    (void) krb5_db_fini(util_context);
+    free(mkey_fullname);
+    free(output_str);
+    for (cur_actkvno = actkvno_list; cur_actkvno != NULL;) {
+        prev_actkvno = cur_actkvno;
+        cur_actkvno = cur_actkvno->next;
+        free(prev_actkvno);
+    }
+    return;
+}
+
+struct update_enc_mkvno {
+    unsigned int re_match_count;
+    unsigned int already_current;
+    unsigned int updated;
+    unsigned int dry_run : 1;
+    unsigned int verbose : 1;
+#ifdef SOLARIS_REGEXPS
+    char *expbuf;
+#endif
+#ifdef POSIX_REGEXPS
+    regex_t preg;
+#endif
+#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
+    unsigned char placeholder;
+#endif
+};
+
+/* XXX Duplicated in libkadm5srv! */
+/*
+ * Function: glob_to_regexp
+ *
+ * Arguments:
+ *
+ *     glob    (r) the shell-style glob (?*[]) to convert
+ *     realm   (r) the default realm to append, or NULL
+ *     regexp  (w) the ed-style regexp created from glob
+ *
+ * Effects:
+ *
+ * regexp is filled in with allocated memory contained a regular
+ * expression to be used with re_comp/compile that matches what the
+ * shell-style glob would match.  If glob does not contain an "@"
+ * character and realm is not NULL, "@*" is appended to the regexp.
+ *
+ * Conversion algorithm:
+ *
+ *     quoted characters are copied quoted
+ *     ? is converted to .
+ *     * is converted to .*
+ *     active characters are quoted: ^, $, .
+ *     [ and ] are active but supported and have the same meaning, so
+ *             they are copied
+ *     other characters are copied
+ *     regexp is anchored with ^ and $
+ */
+static int glob_to_regexp(char *glob, char *realm, char **regexp)
+{
+     int append_realm;
+     char *p;
+
+     /* validate the glob */
+     if (glob[strlen(glob)-1] == '\\')
+         return EINVAL;
+
+     /* A character of glob can turn into two in regexp, plus ^ and $ */
+     /* and trailing null.  If glob has no @, also allocate space for */
+     /* the realm. */
+     append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
+     p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
+     if (p == NULL)
+         return ENOMEM;
+     *regexp = p;
+
+     *p++ = '^';
+     while (*glob) {
+         switch (*glob) {
+         case '?':
+              *p++ = '.';
+              break;
+         case '*':
+              *p++ = '.';
+              *p++ = '*';
+              break;
+         case '.':
+         case '^':
+         case '$':
+              *p++ = '\\';
+              *p++ = *glob;
+              break;
+         case '\\':
+              *p++ = '\\';
+              *p++ = *++glob;
+              break;
+         default:
+              *p++ = *glob;
+              break;
+         }
+         glob++;
+     }
+
+     if (append_realm) {
+         *p++ = '@';
+         *p++ = '.';
+         *p++ = '*';
+     }
+
+     *p++ = '$';
+     *p++ = '\0';
+     return 0;
+}
+
+static int
+update_princ_encryption_1(void *cb, krb5_db_entry *ent)
+{
+    struct update_enc_mkvno *p = cb;
+    char *pname = 0;
+    krb5_error_code retval;
+    int match;
+    krb5_timestamp now;
+    int nentries = 1;
+    int result;
+    krb5_kvno old_mkvno;
+
+    retval = krb5_unparse_name(util_context, ent->princ, &pname);
+    if (retval) {
+        com_err(progname, retval,
+                "getting string representation of principal name");
+        goto fail;
+    }
+
+    if (krb5_principal_compare (util_context, ent->princ, master_princ)) {
+        goto skip;
+    }
+
+#ifdef SOLARIS_REGEXPS
+    match = (step(pname, p->expbuf) != 0);
+#endif
+#ifdef POSIX_REGEXPS
+    match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
+#endif
+#ifdef BSD_REGEXPS
+    match = (re_exec(pname) != 0);
+#endif
+    if (!match) {
+        goto skip;
+    }
+    p->re_match_count++;
+    retval = krb5_dbe_lookup_mkvno(util_context, ent, &old_mkvno);
+    if (retval) {
+        com_err(progname, retval,
+                "determining master key used for principal '%s'",
+                pname);
+        goto fail;
+    }
+    /* Line up "skip" and "update" messages for viewing.  */
+    if (old_mkvno == new_mkvno) {
+        if (p->dry_run && p->verbose)
+            printf("would skip:   %s\n", pname);
+        else if (p->verbose)
+            printf("skipping: %s\n", pname);
+        p->already_current++;
+        goto skip;
+    }
+    if (p->dry_run) {
+        if (p->verbose)
+            printf("would update: %s\n", pname);
+        p->updated++;
+        goto skip;
+    } else if (p->verbose)
+        printf("updating: %s\n", pname);
+    retval = master_key_convert (util_context, ent);
+    if (retval) {
+        com_err(progname, retval,
+                "error re-encrypting key for principal '%s'", pname);
+        goto fail;
+    }
+    if ((retval = krb5_timeofday(util_context, &now))) {
+        com_err(progname, retval, "while getting current time");
+        goto fail;
+    }
+
+    if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
+                                                 now, master_princ))) {
+        com_err(progname, retval,
+                "while updating principal '%s' modification time", pname);
+        goto fail;
+    }
+
+    if ((retval = krb5_db_put_principal(util_context, ent, &nentries))) {
+        com_err(progname, retval,
+                "while updating principal '%s' key data in the database",
+                pname);
+        goto fail;
+    }
+    p->updated++;
+skip:
+    result = 0;
+    goto egress;
+fail:
+    exit_status++;
+    result = 1;
+egress:
+    if (pname)
+        krb5_free_unparsed_name(util_context, pname);
+    return result;
+}
+
+extern int are_you_sure (const char *, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+    __attribute__((__format__(__printf__, 1, 2)))
+#endif
+    ;
+
+int
+are_you_sure (const char *format, ...)
+{
+    va_list va;
+    char ansbuf[100];
+
+    va_start(va, format);
+    vprintf(format, va);
+    va_end(va);
+    printf("\n(type 'yes' to confirm)? ");
+    fflush(stdout);
+    if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
+        return 0;
+    if (strcmp(ansbuf, "yes\n"))
+        return 0;
+    return 1;
+}
+
+void
+kdb5_update_princ_encryption(int argc, char *argv[])
+{
+    struct update_enc_mkvno data = { 0 };
+    char *name_pattern = NULL;
+    int force = 0;
+    int optchar;
+    krb5_error_code retval;
+    krb5_actkvno_node *actkvno_list;
+    krb5_db_entry master_entry;
+    int nentries = 1;
+    krb5_boolean more = FALSE;
+    char *mkey_fullname = 0;
+#ifdef BSD_REGEXPS
+    char *msg;
+#endif
+    char *regexp = NULL;
+    krb5_keyblock *tmp_keyblock = NULL;
+
+    while ((optchar = getopt(argc, argv, "fnv")) != -1) {
+        switch (optchar) {
+        case 'f':
+            force = 1;
+            break;
+        case 'n':
+            data.dry_run = 1;
+            break;
+        case 'v':
+            data.verbose = 1;
+            break;
+        case '?':
+        case ':':
+        default:
+            usage();
+        }
+    }
+    if (argv[optind] != NULL) {
+        name_pattern = argv[optind];
+        if (argv[optind+1] != NULL)
+            usage();
+    }
+
+    retval = krb5_unparse_name(util_context, master_princ, &mkey_fullname);
+    if (retval) {
+        com_err(progname, retval, "while formatting master principal name");
+        exit_status++;
+        goto cleanup;
+    }
+
+    if (master_keylist == NULL) {
+        com_err(progname, retval, "master keylist not initialized");
+        exit_status++;
+        goto cleanup;
+    }
+
+    /* The glob_to_regexp code only cares if the "realm" parameter is
+       NULL or not; the string data is irrelevant.  */
+    if (name_pattern == NULL)
+        name_pattern = "*";
+    if (glob_to_regexp(name_pattern, "hi", &regexp) != 0) {
+        com_err(progname, ENOMEM,
+                "converting glob pattern '%s' to regular expression",
+                name_pattern);
+        exit_status++;
+        goto cleanup;
+    }
+
+    if (
+#ifdef SOLARIS_REGEXPS
+        ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
+#endif
+#ifdef POSIX_REGEXPS
+        ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
+#endif
+#ifdef BSD_REGEXPS
+        ((msg = (char *) re_comp(regexp)) != NULL)
+#endif
+        ) {
+        /* XXX syslog msg or regerr(regerrno) */
+        com_err(progname, 0, "error compiling converted regexp '%s'", regexp);
+        free(regexp);
+        exit_status++;
+        goto cleanup;
+    }
+
+    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
+                                   &nentries, &more);
+    if (retval != 0) {
+        com_err(progname, retval, "while getting master key principal %s",
+                mkey_fullname);
+        exit_status++;
+        goto cleanup;
+    }
+    if (nentries != 1) {
+        com_err(progname, 0,
+                "cannot find master key principal %s in database!",
+                mkey_fullname);
+        exit_status++;
+        goto cleanup;
+    }
+
+    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
+    if (retval != 0) {
+        com_err(progname, retval, "while looking up active kvno list");
+        exit_status++;
+        goto cleanup;
+    }
+
+    /* Master key is always stored encrypted in the latest version of
+       itself.  */
+    new_mkvno = krb5_db_get_key_data_kvno(util_context,
+                                          master_entry.n_key_data,
+                                          master_entry.key_data);
+
+    retval = krb5_dbe_find_mkey(util_context, master_keylist,
+                                &master_entry, &tmp_keyblock);
+    if (retval) {
+        com_err(progname, retval, "retrieving the most recent master key");
+        exit_status++;
+        goto cleanup;
+    }
+    new_master_keyblock = *tmp_keyblock;
+
+    if (!force &&
+        !data.dry_run &&
+        !are_you_sure("Re-encrypt all keys not using master key vno %u?",
+                      new_mkvno)) {
+        printf("OK, doing nothing.\n");
+        exit_status++;
+        goto cleanup;
+    }
+    if (data.verbose) {
+        if (data.dry_run)
+            printf("Principals whose keys WOULD BE re-encrypted to master key vno %u:\n",
+                   new_mkvno);
+        else
+            printf("Principals whose keys are being re-encrypted to master key vno %u if necessary:\n",
+                   new_mkvno);
+    }
+
+    retval = krb5_db_iterate(util_context, name_pattern,
+                             update_princ_encryption_1, &data);
+    /* If exit_status is set, then update_princ_encryption_1 already
+       printed a message.  */
+    if (retval != 0 && exit_status == 0) {
+        com_err(progname, retval, "trying to process principal database");
+        exit_status++;
+    }
+    (void) krb5_db_fini(util_context);
+    if (data.dry_run)
+        printf("%u principals processed: %u would be updated, %u already current\n",
+               data.re_match_count, data.updated, data.already_current);
+    else
+        printf("%u principals processed: %u updated, %u already current\n",
+               data.re_match_count, data.updated, data.already_current);
+
+cleanup:
+    free(regexp);
+    memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
+    krb5_free_keyblock(util_context, tmp_keyblock);
+    krb5_free_unparsed_name(util_context, mkey_fullname);
+}
+
+struct kvnos_in_use {
+    krb5_kvno               kvno;
+    unsigned int            use_count;
+};
+
+struct purge_args {
+    krb5_context         kcontext;
+    struct kvnos_in_use  *kvnos;
+    unsigned int         num_kvnos;
+};
+
+static krb5_error_code
+find_mkvnos_in_use(krb5_pointer   ptr,
+                   krb5_db_entry *entry)
+{
+    krb5_error_code retval;
+    struct purge_args * args;
+    unsigned int i;
+    krb5_kvno mkvno;
+
+    args = (struct purge_args *) ptr;
+
+    retval = krb5_dbe_lookup_mkvno(args->kcontext, entry, &mkvno);
+    if (retval)
+        return (retval);
+
+    for (i = 0; i < args->num_kvnos; i++) {
+        if (args->kvnos[i].kvno == mkvno) {
+            /* XXX do I need to worry about use_count wrapping? */
+            args->kvnos[i].use_count++;
+            break;
+        }
+    }
+    return 0;
+}
+
+void
+kdb5_purge_mkeys(int argc, char *argv[])
+{
+    int optchar;
+    krb5_error_code retval;
+    char  *mkey_fullname;
+    krb5_timestamp now;
+    krb5_db_entry master_entry;
+    int   nentries = 0;
+    krb5_boolean more = FALSE;
+    krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
+    struct purge_args args;
+    char buf[5];
+    unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
+    unsigned int old_key_data_count;
+    krb5_actkvno_node *cur_actkvno_list, *actkvno_entry, *prev_actkvno_entry;
+    krb5_mkey_aux_node *cur_mkey_aux_list, *mkey_aux_entry, *prev_mkey_aux_entry;
+    krb5_key_data *old_key_data;
+
+    optind = 1;
+    while ((optchar = getopt(argc, argv, "fnv")) != -1) {
+        switch(optchar) {
+        case 'f':
+            force = TRUE;
+            break;
+        case 'n':
+            dry_run = TRUE; /* mkey princ will not be modified */
+            force = TRUE; /* implied */
+            break;
+        case 'v':
+            verbose = TRUE;
+            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 getting master key principal %s",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries == 0) {
+        com_err(progname, KRB5_KDB_NOENTRY,
+                "principal %s not found in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    } else if (nentries > 1) {
+        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
+                "principal %s has multiple entries in Kerberos database",
+                mkey_fullname);
+        exit_status++;
+        return;
+    }
+
+    if (!force) {
+        printf("Will purge all unused master keys stored in the '%s' principal, are you sure?\n",
+               mkey_fullname);
+        printf("(type 'yes' to confirm)? ");
+        if (fgets(buf, sizeof(buf), stdin) == NULL) {
+            exit_status++;
+            return;
+        }
+        if (strcmp(buf, "yes\n")) {
+            exit_status++;
+            return;
+        }
+        printf("OK, purging unused master keys from '%s'...\n", mkey_fullname);
+    }
+
+    /* save the old keydata */
+    old_key_data_count = master_entry.n_key_data;
+    if (old_key_data_count == 1) {
+        if (verbose)
+            printf("There is only one master key which can not be purged.\n");
+        return;
+    }
+    old_key_data = master_entry.key_data;
+
+    args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
+    if (args.kvnos == NULL) {
+        retval = ENOMEM;
+        com_err(progname, ENOMEM, "while allocating args.kvnos");
+        exit_status++;
+        return;
+    }
+    memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
+    args.num_kvnos = old_key_data_count;
+    args.kcontext = util_context;
+
+    /* populate the kvnos array with all the current mkvnos */
+    for (i = 0; i < old_key_data_count; i++)
+        args.kvnos[i].kvno =  master_entry.key_data[i].key_data_kvno;
+
+    if ((retval = krb5_db_iterate(util_context,
+                                  NULL,
+                                  find_mkvnos_in_use,
+                                  (krb5_pointer) &args))) {
+        com_err(progname, retval, "while finding master keys in use");
+        exit_status++;
+        return;
+    }
+    /*
+     * args.kvnos has been marked with the mkvno's that are currently protecting
+     * princ entries
+     */
+    if (dry_run)
+        printf("Would purge the follwing master key(s) from %s:\n", mkey_fullname);
+    else
+        printf("Purging the follwing master key(s) from %s:\n", mkey_fullname);
+
+    /* find # of keys still in use or print out verbose info */
+    for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
+        if (args.kvnos[i].use_count > 0) {
+            num_kvnos_inuse++;
+        } else {
+            /* this key would be deleted */
+            if (args.kvnos[i].kvno == master_kvno) {
+                com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
+                        "master key stash file needs updating, command aborting");
+                exit_status++;
+                return;
+            }
+            num_kvnos_purged++;
+            printf("KNVO: %d\n", args.kvnos[i].kvno);
+        }
+    }
+    /* didn't find any keys to purge */
+    if (num_kvnos_inuse == args.num_kvnos) {
+        printf("All keys in use, nothing purged.\n");
+        goto clean_and_exit;
+    }
+    if (dry_run) {
+        /* bail before doing anything else */
+        printf("%d key(s) would be purged.\n", num_kvnos_purged);
+        goto clean_and_exit;
+    }
+
+    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &cur_actkvno_list);
+    if (retval != 0) {
+        com_err(progname, retval, "while looking up active kvno list");
+        exit_status++;
+        return;
+    }
+
+    retval = krb5_dbe_lookup_mkey_aux(util_context, &master_entry, &cur_mkey_aux_list);
+    if (retval != 0) {
+        com_err(progname, retval, "while looking up mkey aux data list");
+        exit_status++;
+        return;
+    }
+
+    master_entry.key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
+    if (master_entry.key_data == NULL) {
+        retval = ENOMEM;
+        com_err(progname, ENOMEM, "while allocating key_data");
+        exit_status++;
+        return;
+    }
+    memset((char *) master_entry.key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
+    master_entry.n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
+
+    /*
+     * Assuming that the latest mkey will not be purged because it will always
+     * be "in use" so this code will not bother with encrypting keys again.
+     */
+    for (i = k = 0; i < old_key_data_count; i++) {
+        for (j = 0; j < args.num_kvnos; j++) {
+            if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
+                if (args.kvnos[j].use_count != 0) {
+                    master_entry.key_data[k++] = old_key_data[i];
+                    break;
+                } else {
+                    /* remove unused mkey */
+                    /* adjust the actkno data */
+                    for (prev_actkvno_entry = actkvno_entry = cur_actkvno_list;
+                         actkvno_entry != NULL;
+                         actkvno_entry = actkvno_entry->next) {
+
+                        if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
+                            if (actkvno_entry == cur_actkvno_list) {
+                                /* remove from head */
+                                cur_actkvno_list = actkvno_entry->next;
+                                prev_actkvno_entry = cur_actkvno_list;
+                            } else if (actkvno_entry->next == NULL) {
+                                /* remove from tail */
+                                prev_actkvno_entry->next = NULL;
+                            } else {
+                                /* remove in between */
+                                prev_actkvno_entry->next = actkvno_entry->next;
+                            }
+                            /* XXX WAF: free actkvno_entry */
+                            break; /* deleted entry, no need to loop further */
+                        } else {
+                            prev_actkvno_entry = actkvno_entry;
+                        }
+                    }
+                    /* adjust the mkey aux data */
+                    for (prev_mkey_aux_entry = mkey_aux_entry = cur_mkey_aux_list;
+                         mkey_aux_entry != NULL;
+                         mkey_aux_entry = mkey_aux_entry->next) {
+
+                        if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
+                            if (mkey_aux_entry == cur_mkey_aux_list) {
+                                cur_mkey_aux_list = mkey_aux_entry->next;
+                                prev_mkey_aux_entry = cur_mkey_aux_list;
+                            } else if (mkey_aux_entry->next == NULL) {
+                                prev_mkey_aux_entry->next = NULL;
+                            } else {
+                                prev_mkey_aux_entry->next = mkey_aux_entry->next;
+                            }
+                            /* XXX WAF: free mkey_aux_entry */
+                            break; /* deleted entry, no need to loop further */
+                        } else {
+                            prev_mkey_aux_entry = mkey_aux_entry;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    assert(k == num_kvnos_inuse);
+
+    if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry,
+                                          cur_actkvno_list))) {
+        com_err(progname, retval,
+                "while updating actkvno data for master principal entry");
+        exit_status++;
+        return;
+    }
+
+    if ((retval = krb5_dbe_update_mkey_aux(util_context, &master_entry,
+                                           cur_mkey_aux_list))) {
+        com_err(progname, retval,
+                "while updating mkey_aux data for master principal entry");
+        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;
+    }
+    printf("%d key(s) purged.\n", num_kvnos_purged);
+
+clean_and_exit:
+    /* clean up */
+    (void) krb5_db_fini(util_context);
+    free(args.kvnos);
+    free(mkey_fullname);
+    return;
+}
index 3583a3285f0b1ee4c3abb4671fa9e2aa009159f1..cdd947ac4d60875c0ff2d4dacf3568f17832eaf1 100644 (file)
@@ -60,6 +60,7 @@
 #include "kdb5_util.h"
 
 extern krb5_keyblock master_keyblock;
+extern krb5_keylist_node *master_keylist;
 extern krb5_principal master_princ;
 extern kadm5_config_params global_params;
 
@@ -145,36 +146,38 @@ kdb5_stash(argc, argv)
     else
         mkey_kvno = IGNORE_VNO; /* use whatever krb5_db_fetch_mkey finds */
 
-    /* TRUE here means read the keyboard, but only once */
-    retval = krb5_db_fetch_mkey(context, master_princ,
-                               master_keyblock.enctype,
-                               TRUE, FALSE, (char *) NULL,
-                                &mkey_kvno,
-                               NULL, &master_keyblock);
-    if (retval) {
-       com_err(progname, retval, "while reading master key");
-       (void) krb5_db_fini(context);
-       exit_status++; return; 
-    }
+    if (!valid_master_key) {
+       /* TRUE here means read the keyboard, but only once */
+       retval = krb5_db_fetch_mkey(context, master_princ,
+                                   master_keyblock.enctype,
+                                   TRUE, FALSE, (char *) NULL,
+                                   &mkey_kvno,
+                                   NULL, &master_keyblock);
+       if (retval) {
+           com_err(progname, retval, "while reading master key");
+           (void) krb5_db_fini(context);
+           exit_status++; return; 
+       }
 
-    retval = krb5_db_verify_master_key(context, master_princ, 
-                                       mkey_kvno,
-                                      &master_keyblock);
-    if (retval) {
-       com_err(progname, retval, "while verifying master key");
-       (void) krb5_db_fini(context);
-       exit_status++; return; 
-    }  
+       retval = krb5_db_fetch_mkey_list(context, master_princ,
+                                        &master_keyblock, mkey_kvno,
+                                        &master_keylist);
+       if (retval) {
+           com_err(progname, retval, "while getting master key list");
+           (void) krb5_db_fini(context);
+           exit_status++; return;
+       }
+    } else {
+       printf("Using existing stashed keys to update stash file.\n");
+    }
 
-    retval = krb5_db_store_master_key(context, keyfile, master_princ, 
-                                      mkey_kvno, &master_keyblock, NULL);
+    retval = krb5_db_store_master_key_list(context, keyfile, master_princ, 
+                                          master_keylist, NULL);
     if (retval) {
        com_err(progname, errno, "while storing key");
-       memset((char *)master_keyblock.contents, 0, master_keyblock.length);
        (void) krb5_db_fini(context);
        exit_status++; return; 
     }
-    memset((char *)master_keyblock.contents, 0, master_keyblock.length);
 
     retval = krb5_db_fini(context);
     if (retval) {
index dc34bc8ca1016decce3fb8338b05d41599e296ee..294357fc9703fa11fe580d8db398a89bc0964c01 100644 (file)
@@ -215,5 +215,36 @@ default.
 .TP
 \fBark\fP
 Adds a random key.
+.TP
+\fBadd_mkey\fP ...
+This option needs documentation.
+.TP
+\fBuse_mkey\fP ...
+This option needs documentation.
+.TP
+\fBlist_mkeys\fP
+This option needs documentation.
+.TP
+\fBupdate_princ_encryption\fP [\fB\-f\fP] [\fB\-n\fP] [\fB\-v\fP] [\fBprinc\-pattern\fP]
+Update all principal records (or only those matching the
+.B princ\-pattern
+glob pattern) to re-encrypt the key data using the latest version of
+the database master key, if they are encrypted using older versions,
+and give a count at the end of the number of principals updated.
+If the
+.B \-f
+option is not given, ask for confirmation before starting to make
+changes.  The
+.B \-v
+option causes each principal processed (each one matching the pattern)
+to be listed, and an indication given as to whether it needed updating
+or not.
+The
+.B \-n
+option causes the actions not to be taken, only the normal or verbose
+status messages displayed; this implies
+.B \-f
+since no database changes will be performed and thus there's little
+reason to seek confirmation.
 .SH SEE ALSO
 kadmin(8)
index 9bc853736d097e7c522bc36bde22447372f8389b..6cb70c1cdf933a93f93ea3e63fd173db1f5770d7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * admin/edit/kdb5_edit.c
  *
- * (C) Copyright 1990,1991, 1996, 2008 by the Massachusetts Institute of Technology.
+ * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #include <stdio.h>
 #include <k5-int.h>
 #include <kadm5/admin.h>
@@ -80,7 +85,7 @@ kadm5_config_params global_params;
 void usage()
 {
      fprintf(stderr, "Usage: "
-          "kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n"
+            "kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n"
             "\t        [-kv mkeyVNO] [-sf stashfilename] [-m] cmd [cmd_options]\n"
             "\tcreate  [-s]\n"
             "\tdestroy [-f]\n"
@@ -90,12 +95,22 @@ void usage()
             "\t        [-rev] [-recurse] [filename [princs...]]\n"
             "\tload    [-old] [-ov] [-b6] [-verbose] [-update] filename\n"
             "\tark     [-e etype_list] principal\n"
+            "\tadd_mkey [-e etype] [-s]\n"
+            "\tuse_mkey kvno [time]\n"
+            "\tlist_mkeys\n"
+             );
+     /* avoid a string length compiler warning */
+     fprintf(stderr,
+            "\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n"
+            "\tpurge_mkeys [-f] [-n] [-v]\n"
             "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n"
             "\t\t\tLook at each database documentation for supported arguments\n");
      exit(1);
 }
 
 extern krb5_keyblock master_keyblock;
+krb5_kvno   master_kvno; /* fetched */
+extern krb5_keylist_node *master_keylist;
 extern krb5_principal master_princ;
 krb5_db_entry master_entry;
 int    valid_master_key = 0;
@@ -116,11 +131,16 @@ struct _cmd_table {
      int opendb;
 } cmd_table[] = {
      {"create", kdb5_create, 0},
-     {"destroy", kdb5_destroy, 1},
+     {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */
      {"stash", kdb5_stash, 1},
      {"dump", dump_db, 1},
      {"load", load_db, 0},
      {"ark", add_random_key, 1},
+     {"add_mkey", kdb5_add_mkey, 1},
+     {"use_mkey", kdb5_use_mkey, 1},
+     {"list_mkeys", kdb5_list_mkeys, 1},
+     {"update_princ_encryption", kdb5_update_princ_encryption, 1},
+     {"purge_mkeys", kdb5_purge_mkeys, 1},
      {NULL, NULL, 0},
 };
 
@@ -382,7 +402,6 @@ static int open_db_and_mkey()
     int nentries;
     krb5_boolean more;
     krb5_data scratch, pwd, seed;
-    krb5_kvno kvno;
 
     dbactive = FALSE;
     valid_master_key = 0;
@@ -425,11 +444,9 @@ static int open_db_and_mkey()
     }
 
     if (global_params.mask & KADM5_CONFIG_KVNO)
-        kvno = global_params.kvno; /* user specified */
+        master_kvno = global_params.kvno; /* user specified */
     else
-        kvno = (krb5_kvno) master_entry.key_data->key_data_kvno;
-
-    krb5_db_free_principal(util_context, &master_entry, nentries);
+        master_kvno = IGNORE_VNO;
 
     /* the databases are now open, and the master principal exists */
     dbactive = TRUE;
@@ -463,33 +480,48 @@ static int open_db_and_mkey()
        free(scratch.data);
        mkey_password = 0;
 
-    } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 
+    } else {
+        if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 
                                            master_keyblock.enctype,
                                            manual_mkey, FALSE,
                                            global_params.stash_file,
-                                           &kvno,
-                                           0, &master_keyblock))) {
-       com_err(progname, retval, "while reading master key");
-       com_err(progname, 0, "Warning: proceeding without master key");
-       exit_status++;
-       return(0);
+                                           &master_kvno,
+                                            0, &master_keyblock))) {
+            com_err(progname, retval, "while reading master key");
+            com_err(progname, 0, "Warning: proceeding without master key");
+            exit_status++;
+            return(0);
+        }
     }
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+    /* krb5_db_fetch_mkey_list will verify the mkey */
     if ((retval = krb5_db_verify_master_key(util_context, master_princ, 
-                                           kvno, &master_keyblock))) {
+                                           master_kvno, &master_keyblock))) {
        com_err(progname, retval, "while verifying master key");
        exit_status++;
        krb5_free_keyblock_contents(util_context, &master_keyblock);
        return(1);
     }
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+    if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
+                                         &master_keyblock, master_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;
+    seed.data = (char *) master_keyblock.contents;
 
     if ((retval = krb5_c_random_seed(util_context, &seed))) {
        com_err(progname, retval, "while seeding random number generator");
        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);
     }
 
@@ -510,6 +542,7 @@ quit()
 
     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;
@@ -540,6 +573,7 @@ add_random_key(argc, argv)
     char *me = progname;
     char *ks_str = NULL;
     char *pr_str;
+    krb5_keyblock *tmp_mkey;
 
     if (argc < 2)
        usage();
@@ -594,7 +628,16 @@ add_random_key(argc, argv)
        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)
index 59f92d4eb2b052a636f8f3087d7915a1aa1ad706..6e99ac3783d810f32cb6c3f2219f06786d37f67b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * admin/edit/kdb5_edit.h
  *
- * Copyright 1992, 2008 by the Massachusetts Institute of Technology.
+ * Copyright 1992, 2008, 2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -45,6 +45,9 @@ extern int valid_master_key;
 extern krb5_db_entry master_db;
 extern char **db5util_db_args;
 extern int    db5util_db_args_size;
+extern krb5_kvno new_mkvno;
+extern krb5_keylist_node *master_keylist;
+extern krb5_keyblock new_master_keyblock;
 extern int add_db_arg(char *arg);
 
 extern void usage(void);
@@ -80,10 +83,22 @@ extern void dump_db (int argc, char **argv);
 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 kdb5_list_mkeys (int argc, char **argv);
+extern void kdb5_update_princ_encryption (int argc, char **argv);
+extern krb5_error_code master_key_convert(krb5_context context,
+                                         krb5_db_entry *db_entry);
+extern void kdb5_purge_mkeys (int argc, char **argv);
 
 extern void update_ok_file (char *file_name);
 
 extern int kadm5_create (kadm5_config_params *params);
 
+extern krb5_error_code add_new_mkey(krb5_context, krb5_db_entry *,
+                                    krb5_keyblock *, krb5_kvno);
+
+extern krb5_kvno get_next_kvno(krb5_context, krb5_db_entry *);
+
 void usage (void);
 
index 573a2c04c72c55f66e63cb7979215fb6ab3f1e3f..81e74758fb459a417833d5e293eea3f0e94ced88 100644 (file)
@@ -98,6 +98,7 @@ void *global_server_handle;
 #define OVSEC_KADM_CHANGEPW_SERVICE    "ovsec_adm/changepw"
 
 extern krb5_keyblock master_keyblock;
+extern krb5_keylist_node  *master_keylist;
 
 char *build_princ_name(char *name, char *realm);
 void log_badauth(OM_uint32 major, OM_uint32 minor,
@@ -395,6 +396,11 @@ int main(int argc, char *argv[])
          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.");
index 9571fb21224f9e8ca3b3417b3b06dd3fe93233bb..8db39ac4f2da1254936a12e3cf0926bc41ac2fa5 100644 (file)
@@ -104,6 +104,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     const char *status;
     krb5_key_data *server_key, *client_key;
     krb5_keyblock server_keyblock, client_keyblock;
+    krb5_keyblock *mkey_ptr;
     krb5_enctype useenctype;
     krb5_boolean update_client = 0;
     krb5_data e_data;
@@ -115,6 +116,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     void *pa_context = NULL;
     int did_log = 0;
     const char *emsg = 0;
+    krb5_keylist_node *tmp_mkey_list;
 
 #if APPLE_PKINIT
     asReqDebug("process_as_req top realm %s name %s\n", 
@@ -425,9 +427,28 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
        goto errout;
     }
 
+    if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server,
+                                      &mkey_ptr))) {
+        /* try refreshing master key list */
+        /* XXX it would nice if we had the mkvno here for optimization */
+        if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
+                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(kdc_context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
+                                              &server, &mkey_ptr))) {
+                status = "FINDING_MASTER_KEY";
+                goto errout;
+            }
+        } else {
+            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, mkey_ptr
     /* server_keyblock is later used to generate auth data signatures */
                                               server_key, &server_keyblock,
                                               NULL))) {
@@ -456,8 +477,27 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
        goto errout;
     }
 
+    if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &client,
+                                      &mkey_ptr))) {
+        /* try refreshing master key list */
+        /* XXX it would nice if we had the mkvno here for optimization */
+        if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
+                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(kdc_context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
+                                              &client, &mkey_ptr))) {
+                status = "FINDING_MASTER_KEY";
+                goto errout;
+            }
+        } else {
+            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, mkey_ptr
                                               client_key, &client_keyblock,
                                               NULL))) {
        status = "DECRYPT_CLIENT_KEY";
index 084300256990d41770ea1b2f972e8aaf56c130ed..a6ce704bd7b1d3e2737db80972a56cebb4b822e2 100644 (file)
@@ -105,6 +105,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
     krb5_keyblock session_key;
     krb5_timestamp until, rtime;
     krb5_keyblock encrypting_key;
+    krb5_keyblock *mkey_ptr;
     krb5_key_data  *server_key;
     char *cname = 0, *sname = 0, *altcname = 0;
     krb5_last_req_entry *nolrarray[2], nolrentry;
@@ -572,10 +573,31 @@ tgt_again:
             status = "FINDING_SERVER_KEY";
             goto cleanup;
         }
+
+        if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server,
+                                          &mkey_ptr))) {
+            krb5_keylist_node *tmp_mkey_list;
+            /* try refreshing master key list */
+            /* XXX it would nice if we had the mkvno here for optimization */
+            if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
+                                        &master_keyblock, 0, &tmp_mkey_list) == 0) {
+                krb5_dbe_free_key_list(kdc_context, master_keylist);
+                master_keylist = tmp_mkey_list;
+                if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
+                                                  &server, &mkey_ptr))) {
+                    status = "FINDING_MASTER_KEY";
+                    goto cleanup;
+                }
+            } else {
+                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
+                                                   mkey_ptr
                                                    server_key, &encrypting_key,
                                                    NULL))) {
             status = "DECRYPT_SERVER_KEY";
index 2a2c1ae22eca2d770edf7624ba2a04b349d0b398..7ebc7bb3a64e5f70c1297a1fab63877176eeaef4 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include "k5-int.h"
+#include "kdb.h"
 #include "extern.h"
 
 /* real declarations of KDC's externs */
index 20cc4bc049a61dd1c86debbad06f0b7e08a0a76a..88e8b0ddef956ab18361def47f617a26793b8056 100644 (file)
@@ -53,7 +53,12 @@ typedef struct __kdc_realm_data {
     char *             realm_stash;    /* Stash file name for realm        */
     char *             realm_mpname;   /* Master principal name for realm  */
     krb5_principal     realm_mprinc;   /* Master principal for realm       */
+    /*
+     * Note realm_mkey is mkey read from stash or keyboard and may not be the
+     * latest.  The mkey_list will have all the mkeys in use.
+     */
     krb5_keyblock      realm_mkey;     /* Master key for this realm        */
+    krb5_keylist_node * mkey_list;     /* list of mkeys in use for this realm */
     /*
      * TGS per-realm data.
      */
@@ -86,6 +91,7 @@ kdc_realm_t *find_realm_data (char *, krb5_ui_4);
 #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
index 6ec156440f6a737b03fba70a7afb960778da7c16..7aacca402e0599ebc5a30eddbaae22ef7499c827 100644 (file)
@@ -665,8 +665,9 @@ get_entry_data(krb5_context context,
     int i, k;
     krb5_data *ret;
     krb5_deltat *delta;
-    krb5_keyblock *keys;
+    krb5_keyblock *keys, *mkey_ptr;
     krb5_key_data *entry_key;
+    krb5_error_code error;
 
     switch (type) {
     case krb5plugin_preauth_entry_request_certificate:
@@ -700,13 +701,32 @@ get_entry_data(krb5_context context,
        ret->data = (char *) keys;
        ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
        memset(ret->data, 0, ret->length);
+       if ((error = krb5_dbe_find_mkey(context, master_keylist, entry,
+                                        &mkey_ptr))) {
+            krb5_keylist_node *tmp_mkey_list;
+            /* try refreshing the mkey list in case it's been updated */
+            if (krb5_db_fetch_mkey_list(context, master_princ,
+                                        &master_keyblock, 0,
+                                        &tmp_mkey_list) == 0) {
+                krb5_dbe_free_key_list(context, master_keylist);
+                master_keylist = tmp_mkey_list;
+                if ((error = krb5_dbe_find_mkey(context, master_keylist, entry,
+                                                &mkey_ptr))) {
+                    free(ret);
+                    return (error);
+                }
+            } else {
+                free(ret);
+                return (error);
+            }
+        }
        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, mkey_ptr,
                                            entry_key, &keys[k], NULL) != 0) {
                if (keys[k].contents != NULL)
                    krb5_free_keyblock_contents(context, &keys[k]);
@@ -1337,7 +1357,7 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
     krb5_data                  scratch;
     krb5_data                  enc_ts_data;
     krb5_enc_data              *enc_data = 0;
-    krb5_keyblock              key;
+    krb5_keyblock              key, *mkey_ptr;
     krb5_key_data *            client_key;
     krb5_int32                 start;
     krb5_timestamp             timenow;
@@ -1355,6 +1375,24 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
        goto cleanup;
 
+    if ((retval = krb5_dbe_find_mkey(context, master_keylist, client,
+                                     &mkey_ptr))) {
+        krb5_keylist_node *tmp_mkey_list;
+        /* try refreshing the mkey list in case it's been updated */
+        if (krb5_db_fetch_mkey_list(context, master_princ,
+                                    &master_keyblock, 0,
+                                    &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((retval = krb5_dbe_find_mkey(context, master_keylist, client,
+                                             &mkey_ptr))) {
+                goto cleanup;
+            }
+        } else {
+            goto cleanup;
+        }
+    }
+
     start = 0;
     decrypt_err = 0;
     while (1) {
@@ -1363,7 +1401,7 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
                                              -1, 0, &client_key)))
            goto cleanup;
 
-       if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock
+       if ((retval = krb5_dbekd_decrypt_key_data(context, mkey_ptr
                                                  client_key, &key, NULL)))
            goto cleanup;
 
@@ -1946,7 +1984,7 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request,
     krb5_sam_challenge         sc;
     krb5_predicted_sam_response        psr;
     krb5_data *                        scratch;
-    krb5_keyblock encrypting_key;
+    krb5_keyblock encrypting_key, *mkey_ptr;
     char response[9];
     char inputblock[8];
     krb5_data predict_response;
@@ -2010,6 +2048,24 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request,
       if (sc.sam_type) {
        /* so use assoc to get the key out! */
        {
+          if ((retval = krb5_dbe_find_mkey(context, master_keylist, &assoc,
+                                           &mkey_ptr))) {
+              krb5_keylist_node *tmp_mkey_list;
+              /* try refreshing the mkey list in case it's been updated */
+              if (krb5_db_fetch_mkey_list(context, master_princ,
+                                          &master_keyblock, 0,
+                                          &tmp_mkey_list) == 0) {
+                  krb5_dbe_free_key_list(context, master_keylist);
+                  master_keylist = tmp_mkey_list;
+                  if ((retval = krb5_dbe_find_mkey(context, master_keylist, &assoc,
+                                                   &mkey_ptr))) {
+                      return (retval);
+                  }
+              } else {
+                  return (retval);
+              }
+          }
+
          /* here's what do_tgs_req does */
          retval = krb5_dbe_find_enctype(kdc_context, &assoc,
                                         ENCTYPE_DES_CBC_RAW,
@@ -2026,7 +2082,7 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request,
          }
          /* convert server.key into a real key */
          retval = krb5_dbekd_decrypt_key_data(kdc_context,
-                                              &master_keyblock
+                                              mkey_ptr
                                               assoc_key, &encrypting_key,
                                               NULL);
          if (retval) {
@@ -2513,7 +2569,7 @@ static krb5_error_code verify_pkinit_request(
     unsigned               cert_hash_len;
     unsigned               key_dex;
     unsigned               cert_match = 0;
-    krb5_keyblock          decrypted_key;
+    krb5_keyblock          decrypted_key, *mkey_ptr;
     
     /* the data we get from the AS-REQ */
     krb5_timestamp         client_ctime = 0;
@@ -2657,6 +2713,22 @@ static krb5_error_code verify_pkinit_request(
        goto cleanup;
     }
     cert_hash_len = strlen(cert_hash);
+    if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry, &mkey_ptr))) {
+        krb5_keylist_node *tmp_mkey_list;
+        /* try refreshing the mkey list in case it's been updated */
+        if (krb5_db_fetch_mkey_list(context, master_princ,
+                                    &master_keyblock, 0,
+                                    &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry,
+                                           &mkey_ptr))) {
+                goto cleanup;
+            }
+        } else {
+            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",
@@ -2671,7 +2743,7 @@ static krb5_error_code verify_pkinit_request(
         * 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, mkey_ptr
                    key_data, &decrypted_key, NULL);
        if(krtn) {
            kdcPkinitDebug("verify_pkinit_request: error decrypting cert hash block\n");
index 34a8ed0c3fdf36654138b9092c903e52c541ab1d..28b4a37ca0e82673d28c9ff8779bd324cdeeda4f 100644 (file)
@@ -415,6 +415,7 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
     krb5_error_code      retval;
     krb5_boolean         more, similar;
     krb5_key_data      * server_key;
+    krb5_keyblock       * mkey_ptr;
 
     *nprincs = 1;
 
@@ -445,6 +446,25 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
        retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
        goto errout;
     }
+
+    if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, server,
+                                     &mkey_ptr))) {
+        krb5_keylist_node *tmp_mkey_list;
+        /* try refreshing master key list */
+        /* XXX it would nice if we had the mkvno here for optimization */
+        if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
+                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(kdc_context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist,
+                                             server, &mkey_ptr))) {
+                goto errout;
+            }
+        } else {
+            goto errout;
+        }
+    }
+
     retval = krb5_dbe_find_enctype(kdc_context, server,
                                   match_enctype ? ticket->enc_part.enctype : -1,
                                   -1, (krb5_int32)ticket->enc_part.kvno,
@@ -456,7 +476,7 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
        goto errout;
     }
     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, mkey_ptr,
                                             server_key,
                                             *key, NULL);
     } else
index b9334680b446708463d058a532b2a12179942c94..bb4d7584d7370f3400089d5dbc589b9d344b023e 100644 (file)
@@ -154,9 +154,12 @@ finish_realm(kdc_realm_t *rdp)
        if (rdp->realm_mprinc)
            krb5_free_principal(rdp->realm_context, rdp->realm_mprinc);
        if (rdp->realm_mkey.length && rdp->realm_mkey.contents) {
+            /* XXX shouldn't memset be zap for safety? */
            memset(rdp->realm_mkey.contents, 0, rdp->realm_mkey.length);
            free(rdp->realm_mkey.contents);
        }
+        if (rdp->mkey_list)
+            krb5_dbe_free_key_list(rdp->realm_context, rdp->mkey_list);
        krb5_db_fini(rdp->realm_context);
        if (rdp->realm_tgsprinc)
            krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc);
@@ -242,6 +245,7 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm,
     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) {
@@ -374,18 +378,25 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm,
     }
 
     /*
-     * 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,
@@ -395,6 +406,14 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm,
                "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,
index a02577667b6a284aa00623da363e3c079c22d66c..545d43b708e64440e789af0ca70a6ddf2bcf259d 100644 (file)
@@ -87,6 +87,7 @@ krb5_string_to_keysalts
 krb5_match_config_pattern
 master_db
 master_keyblock
+master_keylist
 master_princ
 osa_free_princ_ent
 ovsec_kadm_chpass_principal
index 836cd00b768b942f4bf1a1c3208aefb54a570b73..47f00c0713761dbd95cd96b1917a04faa57e5dcb 100644 (file)
@@ -4,6 +4,11 @@
  * $Header$
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #if !defined(lint) && !defined(__CODECENTER__)
 static char *rcsid = "$Header$";
 #endif
@@ -15,7 +20,9 @@ static char *rcsid = "$Header$";
 #include "server_internal.h"
 
 krb5_principal     master_princ;
-krb5_keyblock      master_keyblock;
+krb5_keyblock      master_keyblock; /* local mkey */
+krb5_keylist_node  *master_keylist = NULL;
+krb5_actkvno_node   *active_mkey_list = NULL;
 krb5_db_entry      master_db;
 
 krb5_principal     hist_princ;
@@ -32,6 +39,7 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
     int                   ret = 0;
     char          *realm;
     krb5_boolean   from_kbd = FALSE;
+    krb5_kvno       mkvno = IGNORE_VNO;
 
     if (from_keyboard)
       from_kbd = TRUE;
@@ -50,22 +58,45 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
 
     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 *******************************/
+    /*
+     * krb5_db_fetch_mkey_list will verify mkey so don't call
+     * krb5_db_verify_master_key()
+     */
     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,
+                                      &master_keyblock, mkvno, &master_keylist))) {
+       krb5_db_fini(handle->context);
+       return (ret);
+    }
+
+    if ((ret = krb5_dbe_fetch_act_key_list(handle->context, master_princ,
+                                          &active_mkey_list))) {
+       krb5_db_fini(handle->context);
+       return (ret);
+    }
 
 done:
     if (r == NULL)
@@ -106,6 +137,7 @@ krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
     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 +209,12 @@ krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
     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;
index cd3fb417707d44afb9f9341f2b964acd2c69d6be..757d3ab0e6750d4c0643fe5aff91b74e610f98b3 100644 (file)
@@ -46,6 +46,7 @@ struct iter_data {
 #endif
 };
 
+/* XXX Duplicated in kdb5_util!  */
 /*
  * Function: glob_to_regexp
  *
index 2bdc1f5b29e1997898772ff42bf2fe7b4916562f..207143710e9d95ae670d34b73df633660d6352b2 100644 (file)
@@ -32,13 +32,15 @@ static char *rcsid = "$Header$";
 
 extern krb5_principal      master_princ;
 extern krb5_principal      hist_princ;
-extern krb5_keyblock       master_keyblock;
+extern  krb5_keyblock      master_keyblock;
+extern  krb5_keylist_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);
 
@@ -205,6 +207,8 @@ kadm5_create_principal_3(void *server_handle,
     krb5_tl_data               *tl_data_orig, *tl_data_tail;
     unsigned int               ret;
     kadm5_server_handle_t handle = server_handle;
+    krb5_keyblock               *act_mkey;
+    krb5_kvno                   act_kvno;
 
     CHECK_HANDLE(server_handle);
 
@@ -347,7 +351,16 @@ kadm5_create_principal_3(void *server_handle,
 
     /* 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_kvno, &act_mkey);
+    if (ret) {
+        krb5_db_free_principal(handle->context, &kdb, 1);
+        if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+        return (ret);
+    }
+
+    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,
@@ -359,6 +372,16 @@ kadm5_create_principal_3(void *server_handle,
        return(ret);
     }
 
+    /* Record the master key VNO used to encrypt this entry's keys */
+    ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno);
+    if (ret)
+    {
+       krb5_db_free_principal(handle->context, &kdb, 1);
+       if (mask & KADM5_POLICY)
+           (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return ret;
+    }
+
     /* 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,
@@ -806,12 +829,24 @@ kadm5_get_principal(void *server_handle, krb5_principal principal,
              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
@@ -936,6 +971,7 @@ done:
  */
 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)
@@ -946,7 +982,7 @@ check_pw_reuse(krb5_context context,
 
     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)
@@ -999,7 +1035,7 @@ check_pw_reuse(krb5_context context,
  * 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;
@@ -1013,7 +1049,7 @@ int create_history_entry(krb5_context context, int n_key_data,
 
      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)
@@ -1302,6 +1338,8 @@ kadm5_chpass_principal_3(void *server_handle,
     int                                have_pol = 0;
     kadm5_server_handle_t      handle = server_handle;
     osa_pw_hist_ent            hist;
+    krb5_keyblock               *act_mkey;
+    krb5_kvno                   act_kvno;
 
     CHECK_HANDLE(server_handle);
 
@@ -1335,7 +1373,12 @@ kadm5_chpass_principal_3(void *server_handle,
                            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_kvno, &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 */,
@@ -1343,6 +1386,10 @@ kadm5_chpass_principal_3(void *server_handle,
     if (ret)
        goto done;
 
+    ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno);
+    if (ret)
+        goto done;
+
     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     ret = krb5_timeofday(handle->context, &now);
@@ -1372,12 +1419,13 @@ kadm5_chpass_principal_3(void *server_handle,
 #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)
@@ -1389,7 +1437,7 @@ kadm5_chpass_principal_3(void *server_handle,
                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)
@@ -1489,6 +1537,7 @@ kadm5_randkey_principal_3(void *server_handle,
     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;
@@ -1507,7 +1556,12 @@ kadm5_randkey_principal_3(void *server_handle,
     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, NULL, &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,
@@ -1552,7 +1606,7 @@ kadm5_randkey_principal_3(void *server_handle,
                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)
@@ -1579,12 +1633,12 @@ kadm5_randkey_principal_3(void *server_handle,
             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)
@@ -1630,6 +1684,7 @@ kadm5_setv4key_principal(void *server_handle,
 #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));
 
@@ -1667,8 +1722,13 @@ kadm5_setv4key_principal(void *server_handle,
     keysalt.data.length = 0;
     keysalt.data.data = NULL;
 
+    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+                                active_mkey_list, NULL, &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) {
@@ -1809,6 +1869,7 @@ kadm5_setkey_principal_3(void *server_handle,
     krb5_keysalt               keysalt;
     krb5_key_data         tmp_key_data;
     krb5_key_data        *tptr;
+    krb5_keyblock               *act_mkey;
 
     CHECK_HANDLE(server_handle);
 
@@ -1880,15 +1941,20 @@ kadm5_setkey_principal_3(void *server_handle,
        }
        memset (&tmp_key_data, 0, sizeof(tmp_key_data));
 
+       ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+                                    active_mkey_list, NULL, &act_mkey);
+       if (ret)
+           goto done;
+
        ret = krb5_dbekd_encrypt_key_data(handle->context,
-                                         &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;
@@ -2013,6 +2079,7 @@ kadm5_get_principal_keys(void *server_handle /* IN */,
     krb5_key_data               *key_data;
     kadm5_ret_t                 ret;
     kadm5_server_handle_t       handle = server_handle;
+    krb5_keyblock               *mkey_ptr;
 
     if (keyblocks)
          *keyblocks = NULL;
@@ -2026,6 +2093,25 @@ kadm5_get_principal_keys(void *server_handle /* IN */,
        return(ret);
 
     if (keyblocks) {
+       if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb,
+                                      &mkey_ptr))) {
+            krb5_keylist_node *tmp_mkey_list;
+            /* try refreshing master key list */
+            /* XXX it would nice if we had the mkvno here for optimization */
+            if (krb5_db_fetch_mkey_list(handle->context, master_princ,
+                                        &master_keyblock, 0,
+                                        &tmp_mkey_list) == 0) {
+                krb5_dbe_free_key_list(handle->context, master_keylist);
+                master_keylist = tmp_mkey_list;
+                if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
+                                              &kdb, &mkey_ptr))) {
+                    goto done;
+                }
+            } else {
+                goto done;
+            }
+        }
+
          if (handle->api_version == KADM5_API_VERSION_1) {
               /* Version 1 clients will expect to see a DES_CRC enctype. */
               if ((ret = krb5_dbe_find_enctype(handle->context, &kdb,
@@ -2033,11 +2119,11 @@ kadm5_get_principal_keys(void *server_handle /* IN */,
                                               -1, -1, &key_data)))
                    goto done;
 
-              if ((ret = decrypt_key_data(handle->context, 1, key_data,
+              if ((ret = decrypt_key_data(handle->context, mkey_ptr, 1, key_data,
                                          keyblocks, NULL)))
                    goto done;
          } else {
-              ret = decrypt_key_data(handle->context,
+              ret = decrypt_key_data(handle->context, mkey_ptr,
                                      kdb.n_key_data, kdb.key_data,
                                      keyblocks, n_keys);
               if (ret)
@@ -2056,10 +2142,10 @@ done:
 /*
  * Allocate an array of n_key_data krb5_keyblocks, fill in each
  * element with the results of decrypting the nth key in key_data with
- * 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)
 {
@@ -2072,7 +2158,7 @@ static int decrypt_key_data(krb5_context context,
      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) {
@@ -2135,6 +2221,7 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
     kadm5_server_handle_t handle = server_handle;
     krb5_db_entry dbent;
     krb5_key_data *key_data;
+    krb5_keyblock *mkey_ptr;
     int ret;
 
     CHECK_HANDLE(server_handle);
@@ -2149,8 +2236,28 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
                                    stype, kvno, &key_data)))
         return ret;
 
+    /* find_mkey only uses this field */
+    dbent.tl_data = entry->tl_data;
+    if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent,
+                                  &mkey_ptr))) {
+        krb5_keylist_node *tmp_mkey_list;
+        /* try refreshing master key list */
+        /* XXX it would nice if we had the mkvno here for optimization */
+        if (krb5_db_fetch_mkey_list(handle->context, master_princ,
+                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
+            krb5_dbe_free_key_list(handle->context, master_keylist);
+            master_keylist = tmp_mkey_list;
+            if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
+                                          &dbent, &mkey_ptr))) {
+                return ret;
+            }
+        } else {
+            return ret;
+        }
+    }
+
     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
-                                          &master_keyblock, key_data,
+                                          mkey_ptr, key_data,
                                           keyblock, keysalt)))
         return ret;
 
index 88df6bcc61fbe813f1f6d4dbf7dcd7f87738f950..2252c3ad0359d417504522774dcee60d12be035d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006, 2008 by the Massachusetts Institute of Technology.
+ * Copyright 2006, 2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * or implied warranty.
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 /*
  * This code was based on code donated to MIT by Novell for
  * distribution under the MIT license.
@@ -101,6 +106,73 @@ kdb_unlock_list()
     return k5_mutex_unlock(&db_lock);
 }
 
+/*
+ * XXX eventually this should be consolidated with krb5_free_key_data_contents
+ * so there is only a single version.
+ */
+void
+krb5_dbe_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]) {
+            zap(key->key_data_contents[i], key->key_data_length[i]);
+            free(key->key_data_contents[i]);
+        }
+    }
+    return;
+}
+
+void
+krb5_dbe_free_key_list(krb5_context context, krb5_keylist_node *val)
+{
+    krb5_keylist_node *temp = val, *prev;
+
+    while (temp != NULL) {
+        prev = temp;
+        temp = temp->next;
+        krb5_free_keyblock_contents(context, &(prev->keyblock));
+        krb5_xfree(prev);
+    }
+}
+
+void
+krb5_dbe_free_actkvno_list(krb5_context context, krb5_actkvno_node *val)
+{
+    krb5_actkvno_node *temp = val, *prev;
+
+    while (temp != NULL) {
+        prev = temp;
+        temp = temp->next;
+        krb5_xfree(prev);
+    }
+}
+
+void
+krb5_dbe_free_mkey_aux_list(krb5_context context, krb5_mkey_aux_node *val)
+{
+    krb5_mkey_aux_node *temp = val, *prev;
+
+    while (temp != NULL) {
+        prev = temp;
+        temp = temp->next;
+        krb5_dbe_free_key_data_contents(context, &prev->latest_mkey);
+        krb5_xfree(prev);
+    }
+}
+
+void
+krb5_dbe_free_tl_data(krb5_context context, krb5_tl_data *tl_data)
+{
+    if (tl_data) {
+        if (tl_data->tl_data_contents)
+            free(tl_data->tl_data_contents);
+        free(tl_data);
+    }
+}
+
 #define kdb_init_lib_lock(a) 0
 #define kdb_destroy_lib_lock(a) (void)0
 #define kdb_lock_lib_lock(a, b) 0
@@ -196,10 +268,18 @@ kdb_setup_opt_functions(db_library lib)
        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;
     }
@@ -208,6 +288,14 @@ kdb_setup_opt_functions(db_library lib)
        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.store_master_key_list == NULL) {
+       lib->vftabl.store_master_key_list = krb5_def_store_mkey_list;
+    }
+
     if (lib->vftabl.dbe_search_enctype == NULL) {
        lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
     }
@@ -1402,6 +1490,35 @@ krb5_db_set_mkey(krb5_context context, krb5_keyblock * key)
     return krb5_db_set_master_key_ext(context, NULL, key);
 }
 
+krb5_error_code
+krb5_db_set_mkey_list(krb5_context kcontext,
+                      krb5_keylist_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;
+    }
+
+    status = dal_handle->lib_handle->vftabl.set_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_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
 {
@@ -1431,6 +1548,90 @@ krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
     return status;
 }
 
+krb5_error_code
+krb5_db_get_mkey_list(krb5_context kcontext, krb5_keylist_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_keylist_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_keylist_node  *mkey_list)
+{
+    krb5_keylist_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,
@@ -1466,6 +1667,41 @@ krb5_db_store_master_key(krb5_context kcontext,
     return status;
 }
 
+krb5_error_code
+krb5_db_store_master_key_list(krb5_context kcontext,
+                             char *keyfile,
+                             krb5_principal mname,
+                             krb5_keylist_node *keylist,
+                             char *master_pwd)
+{
+    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;
+    }
+
+    status = dal_handle->lib_handle->vftabl.store_master_key_list(kcontext,
+                                                                 keyfile,
+                                                                 mname,
+                                                                 keylist,
+                                                                 master_pwd);
+    get_errmsg(kcontext, status);
+    kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+
+  clean_n_exit:
+    return status;
+}
+
 char   *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1;
 char   *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2;
 
@@ -1534,7 +1770,7 @@ krb5_db_fetch_mkey(krb5_context    context,
 
        if (!salt)
            free(scratch.data);
-       memset(password, 0, sizeof(password));  /* erase it */
+       zap(password, sizeof(password));        /* erase it */
 
     } else {
        kdb5_dal_handle *dal_handle;
@@ -1552,7 +1788,9 @@ krb5_db_fetch_mkey(krb5_context    context,
            goto clean_n_exit;
        }
 
-       tmp_key.enctype = key->enctype;
+        /* get the enctype from the stash */
+       tmp_key.enctype = ENCTYPE_UNKNOWN;
+
        retval = dal_handle->lib_handle->vftabl.fetch_master_key(context,
                                                                 mname,
                                                                 &tmp_key,
@@ -1579,7 +1817,7 @@ krb5_db_fetch_mkey(krb5_context    context,
 
   clean_n_exit:
     if (tmp_key.contents) {
-       memset(tmp_key.contents, 0, tmp_key.length);
+       zap(tmp_key.contents, tmp_key.length);
        krb5_db_free(context, tmp_key.contents);
     }
     return retval;
@@ -1618,6 +1856,163 @@ krb5_db_verify_master_key(krb5_context     kcontext,
     return status;
 }
 
+krb5_error_code
+krb5_dbe_fetch_act_key_list(krb5_context         context,
+                            krb5_principal       princ,
+                            krb5_actkvno_node  **act_key_list)
+{
+    krb5_error_code retval = 0;
+    krb5_db_entry entry;
+    int nprinc;
+    krb5_boolean more;
+
+    if (act_key_list == NULL)
+        return (EINVAL);
+
+    nprinc = 1;
+    if ((retval = krb5_db_get_principal(context, princ, &entry,
+                                        &nprinc, &more))) {
+        return (retval);
+    }
+
+    if (nprinc != 1) {
+        if (nprinc) {
+            krb5_db_free_principal(context, &entry, nprinc);
+        }
+        return(KRB5_KDB_NOMASTERKEY);
+    } else if (more) {
+        krb5_db_free_principal(context, &entry, nprinc);
+        return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+    }
+
+    retval = krb5_dbe_lookup_actkvno(context, &entry, act_key_list);
+
+    if (*act_key_list == NULL) {
+        krb5_actkvno_node   *tmp_actkvno;
+        krb5_timestamp       now;
+        /*
+         * for mkey princ entries without KRB5_TL_ACTKVNO data provide a default
+         */
+
+        if ((retval = krb5_timeofday(context, &now)))
+            return (retval);
+
+        tmp_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+        if (tmp_actkvno == NULL)
+            return (ENOMEM);
+
+        memset(tmp_actkvno, 0, sizeof(krb5_actkvno_node));
+        tmp_actkvno->act_time = now;
+        /* use most current key */
+        tmp_actkvno->act_kvno = entry.key_data[0].key_data_kvno;
+
+        *act_key_list = tmp_actkvno;
+    }
+
+    krb5_db_free_principal(context, &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_keylist_node  *mkey_list,
+                       krb5_actkvno_node   *act_mkey_list,
+                       krb5_kvno           *act_kvno,
+                       krb5_keyblock      **act_mkey)
+{
+    krb5_kvno tmp_act_kvno;
+    krb5_error_code retval;
+    krb5_keylist_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) {
+            tmp_act_kvno = cur_actkvno->act_kvno;
+            found = TRUE;
+            break;
+        } else if (cur_actkvno->act_time > now && prev_actkvno->act_time <= now) {
+            tmp_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) {
+            tmp_act_kvno = prev_actkvno->act_kvno;
+        } else {
+            /* XXX this shouldn't happen */
+            return (KRB5_KDB_NOACTMASTERKEY);
+        }
+    }
+
+    while (cur_keyblock && cur_keyblock->kvno != tmp_act_kvno)
+        cur_keyblock = cur_keyblock->next;
+
+    if (cur_keyblock) {
+        *act_mkey = &cur_keyblock->keyblock;
+        if (act_kvno != NULL)
+            *act_kvno = tmp_act_kvno;
+        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_keylist_node  * mkey_list,
+                   krb5_db_entry      * entry,
+                   krb5_keyblock     ** mkey)
+{
+    krb5_kvno mkvno;
+    krb5_error_code retval;
+    krb5_keylist_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)
 {
@@ -1885,6 +2280,347 @@ krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
     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);
+
+    if (tl_data.tl_data_length == 0) {
+       *mkvno = 1; /* default for princs that lack the KRB5_TL_MKVNO data */
+       return (0);
+    } else if (tl_data.tl_data_length != 2) {
+       return (KRB5_KDB_TRUNCATED_RECORD);
+    }
+
+    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 */
+    krb5_int16 tmp_kvno = (krb5_int16) mkvno;
+
+    tl_data.tl_data_type = KRB5_TL_MKVNO;
+    tl_data.tl_data_length = sizeof(buf);
+    krb5_kdb_encode_int16(tmp_kvno, 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 == 1) {
+            /* variable size, must be at least 10 bytes */
+            if (tl_data.tl_data_length < 10)
+                return (KRB5_KDB_TRUNCATED_RECORD);
+
+            /* 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)) {
+
+                new_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+                if (new_data == NULL) {
+                    krb5_dbe_free_mkey_aux_list(context, head_data);
+                    return (ENOMEM);
+                }
+                memset(new_data, 0, sizeof(krb5_mkey_aux_node));
+
+                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_dbe_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]);
+                curloc += new_data->latest_mkey.key_data_length[0];
+
+                /* always using key data ver 1 for mkeys */
+                new_data->latest_mkey.key_data_ver = 1;
+
+                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);
+}
+
+#if KRB5_TL_MKEY_AUX_VER == 1
+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, tmp_kvno;
+    unsigned char *nextloc;
+    krb5_mkey_aux_node *aux_data_entry;
+
+    if (!mkey_aux_data_list) {
+        /* delete the KRB5_TL_MKEY_AUX from the entry */
+        krb5_dbe_delete_tl_data(context, entry, KRB5_TL_MKEY_AUX);
+        return (0);
+    }
+
+    memset(&tl_data, 0, sizeof(tl_data));
+    tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
+    /*
+     * determine out how much space to allocate.  Note key_data_ver not stored
+     * as this is hard coded to one and is accounted for in
+     * krb5_dbe_lookup_mkey_aux.
+     */
+    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 */
+                                   sizeof(krb5_ui_2) + /* latest_mkey kvno */
+                                   sizeof(krb5_ui_2) + /* latest_mkey enctype */
+                                   sizeof(krb5_ui_2) + /* latest_mkey length */
+                                   aux_data_entry->latest_mkey.key_data_length[0]);
+    }
+
+    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_TL_MKEY_AUX_VER;
+    krb5_kdb_encode_int16(version, 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) {
+
+        tmp_kvno = (krb5_int16) aux_data_entry->mkey_kvno;
+        krb5_kdb_encode_int16(tmp_kvno, nextloc);
+        nextloc += sizeof(krb5_ui_2);
+
+        krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_kvno,
+                              nextloc);
+        nextloc += sizeof(krb5_ui_2);
+
+        krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_type[0],
+                              nextloc);
+        nextloc += sizeof(krb5_ui_2);
+
+        krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_length[0],
+                              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));
+}
+#endif /* KRB5_TL_MKEY_AUX_VER == 1 */
+
+#if KRB5_TL_ACTKVNO_VER == 1
+/*
+ * 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 */
+#endif
+
+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, tmp_kvno;
+    krb5_actkvno_node *head_data = NULL, *new_data = NULL, *prev_data = NULL;
+    unsigned int num_actkvno, i;
+    krb5_octet *next_tuple;
+
+    memset(&tl_data, 0, sizeof(tl_data));
+    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 == 1) {
+
+            /* variable size, must be at least 8 bytes */
+            if (tl_data.tl_data_length < 8)
+                return (KRB5_KDB_TRUNCATED_RECORD);
+
+            /*
+             * 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_dbe_free_actkvno_list(context, head_data);
+                    return (ENOMEM);
+                }
+                memset(new_data, 0, sizeof(krb5_actkvno_node));
+
+                /* using tmp_kvno to avoid type mismatch */
+                krb5_kdb_decode_int16(act_kvno(next_tuple), tmp_kvno);
+                new_data->act_kvno = (krb5_kvno) tmp_kvno;
+                krb5_kdb_decode_int32(act_time(next_tuple), new_data->act_time);
+
+                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);
+}
+
+/*
+ * Add KRB5_TL_ACTKVNO TL data entries to krb5_db_entry *entry
+ */
+#if KRB5_TL_ACTKVNO_VER == 1
+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, tmp_kvno;
+    krb5_tl_data new_tl_data;
+    unsigned char *nextloc;
+    const krb5_actkvno_node *cur_actkvno;
+    krb5_octet *tmpptr;
+
+    if (actkvno_list == NULL) {
+        return (EINVAL);
+    }
+
+    memset(&new_tl_data, 0, sizeof(new_tl_data));
+    /* 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);
+
+    /* add the current version # for the data format used for KRB5_TL_ACTKVNO */
+    version = KRB5_TL_ACTKVNO_VER;
+    krb5_kdb_encode_int16(version, (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;
+        tmpptr = realloc(new_tl_data.tl_data_contents, new_tl_data.tl_data_length);
+        if (tmpptr == NULL) {
+            free(new_tl_data.tl_data_contents);
+            return (ENOMEM);
+        } else {
+            new_tl_data.tl_data_contents = tmpptr;
+        }
+
+        /*
+         * 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;
+        /* using tmp_kvno to avoid type mismatch issues */
+        tmp_kvno = (krb5_int16) cur_actkvno->act_kvno;
+        krb5_kdb_encode_int16(tmp_kvno, nextloc);
+        nextloc += sizeof(krb5_ui_2);
+        krb5_kdb_encode_int32((krb5_ui_4)cur_actkvno->act_time, 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);
+}
+#endif /* KRB5_TL_ACTKVNO_VER == 1 */
+
 krb5_error_code
 krb5_dbe_update_last_pwd_change(context, entry, stamp)
     krb5_context context;
@@ -1902,6 +2638,44 @@ krb5_dbe_update_last_pwd_change(context, entry, stamp)
     return (krb5_dbe_update_tl_data(context, entry, &tl_data));
 }
 
+krb5_error_code
+krb5_dbe_delete_tl_data(krb5_context context,
+                        krb5_db_entry *entry,
+                        krb5_int16 tl_data_type) 
+{
+    krb5_tl_data *tl_data, *prev_tl_data, *free_tl_data;
+
+    /*
+     * Find existing entries of the specified type and remove them from the
+     * entry's tl_data list.
+     */
+
+    for (prev_tl_data = tl_data = entry->tl_data; tl_data != NULL;) {
+        if (tl_data->tl_data_type == tl_data_type) {
+            if (tl_data == entry->tl_data) {
+                /* remove from head */
+                entry->tl_data = tl_data->tl_data_next;
+                prev_tl_data = entry->tl_data;
+            } else if (tl_data->tl_data_next == NULL) {
+                /* remove from tail */
+                prev_tl_data->tl_data_next = NULL;
+            } else {
+                /* remove in between */
+                prev_tl_data->tl_data_next = tl_data->tl_data_next;
+            }
+            free_tl_data = tl_data;
+            tl_data = tl_data->tl_data_next;
+            krb5_dbe_free_tl_data(context, free_tl_data);
+            entry->n_tl_data--;
+        } else {
+            tl_data = tl_data->tl_data_next;
+            prev_tl_data = tl_data;
+        }
+    }
+
+    return (0);
+}
+
 krb5_error_code
 krb5_dbe_update_tl_data(context, entry, new_tl_data)
     krb5_context context;
index a59d98e737ccff68a7a4d75482f75d75353f9bf3..2062055d03d73fa26c33306c8fb38c0afaad8376 100644 (file)
@@ -56,8 +56,8 @@
 #include <stdio.h>
 #include <errno.h>
 
-static int
-get_key_data_kvno(context, count, data)
+int
+krb5_db_get_key_data_kvno(context, count, data)
     krb5_context         context;
     int                          count;
     krb5_key_data      * data;
@@ -260,7 +260,8 @@ krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
     int                          i;
 
     /* First save the old keydata */
-    kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
+    kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
+                                    db_entry->key_data);
     key_data_count = db_entry->n_key_data;
     key_data = db_entry->key_data;
     db_entry->key_data = NULL;
@@ -315,7 +316,8 @@ krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
     int                          i;
 
     /* First save the old keydata */
-    kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
+    kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
+                                    db_entry->key_data);
     key_data_count = db_entry->n_key_data;
     key_data = db_entry->key_data;
     db_entry->key_data = NULL;
@@ -553,8 +555,8 @@ krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
     int                          i;
 
     /* First save the old keydata */
-    old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
-                                db_entry->key_data);
+    old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
+                                        db_entry->key_data);
     key_data_count = db_entry->n_key_data;
     key_data = db_entry->key_data;
     db_entry->key_data = NULL;
@@ -612,8 +614,8 @@ krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
     int                          i;
 
     /* First save the old keydata */
-    old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
-                                db_entry->key_data);
+    old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
+                                        db_entry->key_data);
     key_data_count = db_entry->n_key_data;
     key_data = db_entry->key_data;
     db_entry->key_data = NULL;
index f173e127dfeaa5f7ee69d26dc5031d0763a5cf48..df87916242dafd962a025d497a59e68b690086e0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lib/kdb/kdb_helper.c
  *
- * Copyright 1995, 2008 by the Massachusetts Institute of Technology. 
+ * Copyright 1995, 2009 by the Massachusetts Institute of Technology. 
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * 
  */
 
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
 #include "k5-int.h"
 #include "kdb.h"
 #include <string.h>
@@ -133,12 +138,11 @@ krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap)
 #endif
 
 krb5_error_code
-krb5_def_store_mkey(krb5_context   context,
-                    char           *keyfile,
-                    krb5_principal mname,
-                    krb5_kvno      kvno,
-                    krb5_keyblock  *key,
-                    char           *master_pwd)
+krb5_def_store_mkey_list(krb5_context       context,
+                        char               *keyfile,
+                        krb5_principal     mname,
+                        krb5_keylist_node  *keylist,
+                        char               *master_pwd)
 {
     krb5_error_code retval = 0;
     char defkeyfile[MAXPATHLEN+1];
@@ -199,12 +203,17 @@ krb5_def_store_mkey(krb5_context   context,
     if (retval != 0)
         goto out;
 
-    memset((char *) &new_entry, 0, sizeof(new_entry));
-    new_entry.principal = mname;
-    new_entry.key = *key;
-    new_entry.vno = kvno;
+    while (keylist && !retval) {
+        memset((char *) &new_entry, 0, sizeof(new_entry));
+        new_entry.principal = mname;
+        new_entry.key = keylist->keyblock;
+        new_entry.vno = keylist->kvno;
+
+        retval = krb5_kt_add_entry(context, kt, &new_entry);
+        keylist = keylist->next;
+    }
+    krb5_kt_close(context, kt);
 
-    retval = krb5_kt_add_entry(context, kt, &new_entry);
     if (retval != 0) {
         /* delete tmp keyfile if it exists and an error occurrs */
         if (stat(keyfile, &stb) >= 0)
@@ -222,12 +231,27 @@ krb5_def_store_mkey(krb5_context   context,
 out:
     if (tmp_ktname != NULL)
         free(tmp_ktname);
-    if (kt)
-       krb5_kt_close(context, kt);
 
     return retval;
 }
 
+krb5_error_code
+krb5_def_store_mkey(krb5_context   context,
+                    char           *keyfile,
+                    krb5_principal mname,
+                    krb5_kvno      kvno,
+                    krb5_keyblock  *key,
+                    char           *master_pwd)
+{
+    krb5_keylist_node list;
+
+    list.kvno = kvno;
+    list.keyblock = *key;
+    list.next = NULL;
+    return krb5_def_store_mkey_list(context, keyfile, mname, &list,
+                                   master_pwd);
+}
+
 static krb5_error_code
 krb5_db_def_fetch_mkey_stash(krb5_context   context,
                             const char *keyfile,
@@ -288,7 +312,7 @@ krb5_db_def_fetch_mkey_stash(krb5_context   context,
     if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]),
                                            key->length, kf) != key->length) {
        retval = KRB5_KDB_CANTREAD_STORED;
-       memset(key->contents, 0,  key->length);
+       zap(key->contents, key->length);
        free(key->contents);
        key->contents = 0;
     } else
@@ -421,6 +445,9 @@ krb5_db_def_fetch_mkey(krb5_context   context,
     }
 }
 
+/*
+ * 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,
@@ -468,13 +495,160 @@ krb5_def_verify_master_key(krb5_context    context,
             kvno, master_entry.key_data->key_data_kvno);
     }
 
-    memset((char *)tempkey.contents, 0, tempkey.length);
+    zap((char *)tempkey.contents, tempkey.length);
     free(tempkey.contents);
     krb5_db_free_principal(context, &master_entry, nprinc);
     
     return retval;
 }
 
+krb5_error_code
+krb5_def_fetch_mkey_list(krb5_context        context,
+                       krb5_principal        mprinc,
+                       const krb5_keyblock  *mkey,
+                       krb5_kvno             mkvno,
+                       krb5_keylist_node  **mkeys_list)
+{
+    krb5_error_code retval;
+    krb5_db_entry master_entry;
+    int nprinc;
+    krb5_boolean more, found_key = FALSE;
+    krb5_keyblock cur_mkey;
+    krb5_keylist_node *mkey_list_head = NULL, **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(&cur_mkey, 0, sizeof(cur_mkey));
+
+    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 (mkey->enctype == master_entry.key_data[0].key_data_type[0]) {
+        if (krb5_dbekd_decrypt_key_data(context, mkey,
+                                        &master_entry.key_data[0],
+                                        &cur_mkey, NULL) == 0) {
+            found_key = TRUE;
+        }
+    }
+
+    if (!found_key) {
+        /*
+         * 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;
+
+        /* mkvno may be 0 in some cases like keyboard and should be ignored */
+        if (mkvno != 0) {
+            /* 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, mkey,
+                                                    &aux_data_entry->latest_mkey,
+                                                    &cur_mkey, NULL) == 0) {
+                        found_key = TRUE;
+                        break;
+                    }
+                }
+            }
+        }
+        if (!found_key) {
+            /* 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 (mkey->enctype == aux_data_entry->latest_mkey.key_data_type[0] &&
+                    (krb5_dbekd_decrypt_key_data(context, mkey,
+                                                 &aux_data_entry->latest_mkey,
+                                                 &cur_mkey, NULL) == 0)) {
+                    found_key = TRUE;
+                    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_keylist_node *) malloc(sizeof(krb5_keylist_node));
+    if (mkey_list_head == NULL) {
+        retval = ENOMEM;
+        goto clean_n_exit;
+    }
+
+    memset(mkey_list_head, 0, sizeof(krb5_keylist_node));
+
+    /* Set mkey_list_head to the current mkey as an optimization. */
+    /* mkvno may not be latest so ... */
+    mkey_list_head->kvno = master_entry.key_data[0].key_data_kvno;
+    /* this is the latest clear mkey (avoids a redundant decrypt) */
+    mkey_list_head->keyblock = cur_mkey;
+
+    /* loop through any other master keys creating a list of krb5_keylist_nodes */
+    mkey_list_node = &mkey_list_head->next;
+    for (i = 1; 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_keylist_node *) malloc(sizeof(krb5_keylist_node));
+            if (*mkey_list_node == NULL) {
+                retval = ENOMEM;
+                goto clean_n_exit;
+            }
+            memset(*mkey_list_node, 0, sizeof(krb5_keylist_node));
+        }
+        key_data = &master_entry.key_data[i];
+        retval = krb5_dbekd_decrypt_key_data(context, &cur_mkey,
+                                             key_data,
+                                             &((*mkey_list_node)->keyblock),
+                                             NULL);
+        if (retval)
+            goto clean_n_exit;
+
+        (*mkey_list_node)->kvno = key_data->key_data_kvno;
+        mkey_list_node = &((*mkey_list_node)->next);
+    }
+
+    *mkeys_list = mkey_list_head;
+
+clean_n_exit:
+    krb5_db_free_principal(context, &master_entry, nprinc);
+    if (retval != 0)
+        krb5_dbe_free_key_list(context, mkey_list_head);
+    return retval;
+}
 
 krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
                                   char *pwd,
@@ -491,6 +665,20 @@ krb5_error_code kdb_def_get_mkey ( krb5_context kcontext,
     return 0;
 }
 
+krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext,
+                                       krb5_keylist_node *keylist )
+{
+    /* printf("default set master key\n"); */
+    return 0;
+}
+
+krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext,
+                                       krb5_keylist_node **keylist )
+{
+    /* printf("default get master key\n"); */
+    return 0;
+}
+
 krb5_error_code krb5_def_promote_db (krb5_context kcontext,
                                     char *s, char **args)
 {
index 632c9270dded8674dda011ef869b676aa3555628..47626f15216ac7d0302a5ee9c8772e9f714f98cb 100644 (file)
@@ -123,6 +123,7 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry)
     krb5_keytab_entry  * entry;
 {
     krb5_context         context;
+    krb5_keylist_node  * master_keylist;
     krb5_keyblock       * master_key;
     krb5_error_code      kerror = 0;
     krb5_key_data      * key_data;
@@ -162,7 +163,11 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry)
     }
 
     /* 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;
 
index cbd8711811d240f089196fbe56ca4f3ed96b63a5..c0b161f614523e938ddfdd4c0158f15d61430b92 100644 (file)
@@ -6,10 +6,14 @@ krb5_db_create
 krb5_db_delete_principal
 krb5_db_destroy
 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
+krb5_db_get_key_data_kvno
 krb5_db_get_mkey
+krb5_db_get_mkey_list
 krb5_db_get_context
 krb5_db_get_principal
 krb5_db_get_principal_ext
@@ -19,21 +23,36 @@ krb5_db_lock
 krb5_db_put_principal
 krb5_db_set_context
 krb5_db_set_mkey
+krb5_db_set_mkey_list
 krb5_db_setup_mkey_name
 krb5_db_unlock
 krb5_db_store_master_key
+krb5_db_store_master_key_list
 krb5_db_verify_master_key
 krb5_dbe_apw
 krb5_dbe_ark
 krb5_dbe_cpw
 krb5_dbe_create_key_data
 krb5_dbe_crk
+krb5_dbe_find_act_mkey
+krb5_dbe_fetch_act_key_list
 krb5_dbe_find_enctype
+krb5_dbe_find_mkey
+krb5_dbe_free_actkvno_list
+krb5_dbe_free_key_data_contents
+krb5_dbe_free_mkey_aux_list
+krb5_dbe_free_key_list
 krb5_dbe_lookup_last_pwd_change
+krb5_dbe_lookup_actkvno
+krb5_dbe_lookup_mkey_aux
+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
@@ -52,6 +71,7 @@ krb5_db_iter_policy
 krb5_db_delete_policy
 krb5_db_free_policy
 krb5_def_store_mkey
+krb5_def_store_mkey_list
 krb5_db_promote
 ulog_map
 ulog_set_role
index b15af8c87321ca38600145fc8f705d7382dd9f0a..cd7214d9b65359f40e7bc1b625a36bb4f091ce5e 100644 (file)
@@ -57,6 +57,9 @@ ec KRB5_KDB_BADMASTERKEY,     "Master key does not match database"
 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_KVNONOMATCH,       "KVNO of new master key does not match expected value"
+ec KRB5_KDB_STORED_MKEY_NOTCURRENT,    "Stored master key is not current"
 
 ec KRB5_KDB_CANTLOCK_DB,       "Insufficient access to lock database"
 
index 123d20afb15f562448ccfcda14e1ef72f97acf58..5c816246814e8382723147020c0d476211d84e24 100644 (file)
@@ -59,7 +59,7 @@ static char *_csrc = "@(#) %filespec: db2_exp.c~5 %  (%full_filespec: db2_exp.c~
    locking code into the top and bottom of each referenced function
    won't do.  (We aren't doing recursive locks, currently.)  */
 
-static k5_mutex_t *krb5_db2_mutex;
+k5_mutex_t *krb5_db2_mutex;
 
 #define WRAP(NAME,TYPE,ARGLIST,ARGNAMES,ERROR_RESULT)  \
        static TYPE wrap_##NAME ARGLIST                 \
@@ -178,21 +178,21 @@ WRAP_VOID (krb5_db2_free_policy,
           ( krb5_context kcontext, osa_policy_ent_t entry ),
           (kcontext, entry));
 
-WRAP (krb5_db2_alloc, void *,
-      ( krb5_context kcontext,  
-       void *ptr, 
-       size_t size ),
-      (kcontext, ptr, size), NULL);
-WRAP_VOID (krb5_db2_free,
-          ( krb5_context kcontext, void *ptr ),
-          (kcontext, ptr));
-
 WRAP_K (krb5_db2_set_master_key_ext,
        ( krb5_context kcontext, char *pwd, krb5_keyblock *key),
        (kcontext, pwd, key));
 WRAP_K (krb5_db2_db_get_mkey,
        ( krb5_context context, krb5_keyblock **key),
        (context, key));
+
+WRAP_K (krb5_db2_db_set_mkey_list,
+       ( krb5_context kcontext, krb5_keylist_node *keylist),
+       (kcontext, keylist));
+
+WRAP_K (krb5_db2_db_get_mkey_list,
+       ( krb5_context context, krb5_keylist_node **keylist),
+       (context, keylist));
+
 WRAP_K (krb5_db2_promote_db,
        ( krb5_context kcontext, char *conf_section, char **db_args ),
        (kcontext, conf_section, db_args));
@@ -248,11 +248,13 @@ kdb_vftabl kdb_function_table = {
   /* db_free_supported_realms */              NULL,
   /* errcode_2_string */                       NULL,
   /* release_errcode_string */                NULL,
-  /* db_alloc */                               wrap_krb5_db2_alloc,
-  /* db_free */                                wrap_krb5_db2_free,
+  /* db_alloc */                               krb5_db2_alloc,
+  /* db_free */                                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_db_set_mkey_list,
+  /* get_master_key_list */                   wrap_krb5_db2_db_get_mkey_list,
+  /* blah blah blah */ 0,0,0,0,0,0,0,0,
   /* promote_db */                            wrap_krb5_db2_promote_db,
   0,0,0,
 };
index 704e47d6b2582854bd6fb1ec1ca3ea532dc6518f..90c893305faa078e7fa1508ce247fd35d0b04c72 100644 (file)
@@ -431,6 +431,37 @@ krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key)
     return 0;
 }
 
+krb5_error_code
+krb5_db2_db_set_mkey_list(krb5_context context, krb5_keylist_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_keylist_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.
  *
@@ -1169,10 +1200,21 @@ krb5_db2_db_iterate_ext(krb5_context context,
        contdata.data = contents.data;
        contdata.length = contents.size;
        retval = krb5_decode_princ_contents(context, &contdata, &entries);
+       if (retval)
+           break;
+       retval = k5_mutex_unlock(krb5_db2_mutex);
        if (retval)
            break;
        retval = (*func) (func_arg, &entries);
        krb5_dbe_free_contents(context, &entries);
+       /* Note: If re-locking fails, the wrapper in db2_exp.c will
+          still try to unlock it again.  That would be a bug.  Fix
+          when integrating the locking better.  */
+       if (retval) {
+           (void) k5_mutex_lock(krb5_db2_mutex);
+           break;
+       }
+       retval = k5_mutex_lock(krb5_db2_mutex);
        if (retval)
            break;
        if (!recursive) {
index d6cb1e8817d2dd65cd164c19ad16aea77635a71c..640c4d62d3ae61bec439d33952cdf5fc9d0d3d3b 100644 (file)
@@ -42,7 +42,8 @@ typedef struct _krb5_db2_context {
     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_keylist_node *db_master_key_list;  /* Master key list of database */
     osa_adb_policy_t    policy_db;
     krb5_boolean tempdb;
 } krb5_db2_context;
@@ -121,6 +122,13 @@ krb5_db2_db_set_mkey( krb5_context context,
 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_keylist_node *keylist);
+
+krb5_error_code
+krb5_db2_db_get_mkey_list( krb5_context context,
+                     krb5_keylist_node **keylist);
 
 krb5_error_code
 krb5_db2_db_put_principal( krb5_context context,
@@ -208,4 +216,7 @@ krb5_error_code krb5_db2_delete_policy ( krb5_context kcontext,
 void krb5_db2_free_policy( krb5_context kcontext,
                           osa_policy_ent_t entry );
 
+/* Thread-safety wrapper slapped on top of original implementation.  */
+extern k5_mutex_t *krb5_db2_mutex;
+
 #endif /* KRB5_KDB_DB2_H */
index eaeef2a8c4a144eff58c47cdff4736ed7b907984..dcfe93cf9e2660da1aafcd993c98cf279257b13c 100644 (file)
@@ -78,10 +78,14 @@ kdb_vftabl kdb_function_table = {
             /* optional functions */
   /* set_master_key */                        krb5_ldap_set_mkey,
   /* get_master_key */                        krb5_ldap_get_mkey,
+  /* set_master_key_list */                   krb5_ldap_set_mkey_list,
+  /* get_master_key_list */                   krb5_ldap_get_mkey_list,
   /* 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,
+  /* store_master_key_list */                 NULL,
   /* Search enc type */                        NULL,
   /* Change pwd   */                           NULL
 
index c13d96710880cf3af6e52b6ce8185091bbeb518d..60d9e25f72369645799090ca4112970c74dcbc4f 100644 (file)
@@ -2379,6 +2379,8 @@ kdb_ldap_create_principal (context, princ, op, pblock)
     krb5_ldap_context *ldap_context=NULL;
     struct iterate_args   iargs;
     krb5_data       *pdata;
+    krb5_timestamp now;
+    krb5_actkvno_node     actkvno;
 
     if ((pblock == NULL) || (context == NULL)) {
        retval = EINVAL;
@@ -2425,14 +2427,12 @@ kdb_ldap_create_principal (context, princ, op, pblock)
     entry.tl_data = tl_data;
     entry.n_tl_data += 1;
     /* Set the creator's name */
-    {
-       krb5_timestamp now;
-       if ((retval = krb5_timeofday(context, &now)))
-           goto cleanup;
-       if ((retval = krb5_dbe_update_mod_princ_data_new(context, &entry,
-                                                        now, &db_create_princ)))
-           goto cleanup;
-    }
+    if ((retval = krb5_timeofday(context, &now)))
+        goto cleanup;
+    if ((retval = krb5_dbe_update_mod_princ_data_new(context, &entry,
+                                                     now, &db_create_princ)))
+        goto cleanup;
+
     entry.attributes = pblock->flags;
     entry.max_life = pblock->max_life;
     entry.max_renewable_life = pblock->max_rlife;
@@ -2507,6 +2507,17 @@ kdb_ldap_create_principal (context, princ, op, pblock)
        if (retval) {
            goto cleanup;
        }
+        /*
+         * There should always be at least one "active" mkey so creating the
+         * KRB5_TL_ACTKVNO entry now so the initial mkey is active.
+         */
+        actkvno.next = NULL;
+        actkvno.act_kvno = kvno;
+        actkvno.act_time = now;
+        retval = krb5_dbe_update_actkvno(context, &entry, &actkvno);
+       if (retval)
+           goto cleanup;
+
        break;
 
     case NULL_KEY:
index 74bf4b17e91d3c34f4dec4ea342300bc218b33ae..802ab0fc36ae3611ef1b6fb5def329a0903a8631 100644 (file)
@@ -266,6 +266,12 @@ krb5_ldap_get_mkey(krb5_context, krb5_keyblock **);
 krb5_error_code
 krb5_ldap_set_mkey(krb5_context, char *, krb5_keyblock *);
 
+krb5_error_code
+krb5_ldap_get_mkey_list (krb5_context context, krb5_keylist_node **key_list);
+
+krb5_error_code
+krb5_ldap_set_mkey_list(krb5_context, krb5_keylist_node *);
+
 krb5_error_code
 krb5_ldap_create(krb5_context , char *, char **);
 
index d4c6ac832bc90ca853d780b0aadeeff1082caa3d..f8e1d4415e4b858427b44f3a43e1433ddb2d16d6 100644 (file)
@@ -148,6 +148,51 @@ krb5_dbe_lookup_last_pwd_change(context, entry, stamp)
 
     return(0);
 }
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+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));
+}
+#endif /**************** END IFDEF'ed OUT *******************************/
 
 /* it seems odd that there's no function to remove a tl_data, but if
    I need one, I'll add one */
index 9a364192ad58cba413a3b5661fad97183ebf1fa0..6da080664153c06b793ac0c2a657b018037524bf 100644 (file)
@@ -98,3 +98,45 @@ krb5_ldap_set_mkey (context, pwd, key)
     memcpy(r_params->mkey.contents, key->contents, key->length);
     return 0;
 }
+
+krb5_error_code
+krb5_ldap_get_mkey_list (krb5_context context, krb5_keylist_node **key_list)
+
+{
+    kdb5_dal_handle             *dal_handle=NULL;
+    krb5_ldap_context           *ldap_context=NULL;
+
+    /* Clear the global error string */
+    krb5_clear_error_message(context);
+
+    dal_handle = context->dal_handle;
+    ldap_context = (krb5_ldap_context *) dal_handle->db_context;
+
+    if (ldap_context == NULL || ldap_context->lrparams == NULL)
+       return KRB5_KDB_DBNOTINITED;
+
+    *key_list = ldap_context->lrparams->mkey_list;
+    return 0;
+}
+
+krb5_error_code
+krb5_ldap_set_mkey_list(krb5_context context, krb5_keylist_node *key_list)
+{
+    kdb5_dal_handle             *dal_handle=NULL;
+    krb5_ldap_context           *ldap_context=NULL;
+    krb5_ldap_realm_params      *r_params = NULL;
+
+    /* Clear the global error string */
+    krb5_clear_error_message(context);
+
+    dal_handle = context->dal_handle;
+    ldap_context = (krb5_ldap_context *) dal_handle->db_context;
+
+    if (ldap_context == NULL || ldap_context->lrparams == NULL)
+       return KRB5_KDB_DBNOTINITED;
+
+    r_params = ldap_context->lrparams;
+    r_params->mkey_list = key_list;
+    return 0;
+}
+
index 79ca63472f7c252aa7fa5ff54172096e4d6bf7b1..f0734deb2a55a173c96345b356eb1739c3171f72 100644 (file)
@@ -2059,9 +2059,16 @@ populate_krb5_db_entry (krb5_context context,
 
     /* KRBSECRETKEY */
     if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
+        krb5_kvno mkvno = 0;
+
        mask |= KDB_SECRET_KEY_ATTR;
-       if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data)) != 0)
+       if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data, &mkvno)) != 0)
            goto cleanup;
+        if (mkvno != 0) {
+            /* don't add the tl data if mkvno == 0 */
+            if ((st=krb5_dbe_update_mkvno(context, entry, mkvno)) != 0)
+                goto cleanup;
+        }
     }
 
     /* LAST PASSWORD CHANGE */
index 18e2acc0605bfc9b1a70000fe3fe54856f51d6ca..502e71ccd559fa45685b06c0e8c80540c56903b8 100644 (file)
@@ -112,7 +112,7 @@ krb5_ldap_parse_principal_name(char *, char **);
 
 krb5_error_code
 krb5_decode_krbsecretkey(krb5_context, krb5_db_entry *, struct berval **,
-    krb5_tl_data *);
+                        krb5_tl_data *, krb5_kvno *);
 
 krb5_error_code
 berval2tl_data(struct berval *in, krb5_tl_data **out);
index 561a65d99b9c0816b3d28a6654baf0f6efa45275..e52a61897f637d85cf1e60d8af1436f1fcb04027 100644 (file)
@@ -345,7 +345,7 @@ asn1_encode_sequence_of_keys (krb5_key_data *key_data, krb5_int16 n_key_data,
 
 static krb5_error_code
 asn1_decode_sequence_of_keys (krb5_data *in, krb5_key_data **out,
-                             krb5_int16 *n_key_data, int *mkvno)
+                             krb5_int16 *n_key_data, krb5_kvno *mkvno)
 {
     krb5_error_code err;
     ldap_seqof_key_data *p;
@@ -371,7 +371,7 @@ asn1_decode_sequence_of_keys (krb5_data *in, krb5_key_data **out,
 
 /* Decoding ASN.1 encoded key */
 static struct berval **
-krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data) {
+krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data, krb5_kvno mkvno) {
     struct berval **ret = NULL;
     int currkvno;
     int num_versions = 1;
@@ -396,7 +396,7 @@ krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data) {
        if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) {
            asn1_encode_sequence_of_keys (key_data+last,
                                          (krb5_int16) i - last + 1,
-                                         0, /* For now, mkvno == 0*/
+                                         mkvno,
                                          &code);
            ret[j] = malloc (sizeof (struct berval));
            if (ret[j] == NULL) {
@@ -927,8 +927,12 @@ krb5_ldap_put_principal(context, entries, nentries, db_args)
        }
 
        if (entries->mask & KADM5_KEY_DATA || entries->mask & KADM5_KVNO) {
+            krb5_kvno mkvno;
+
+            if ((st=krb5_dbe_lookup_mkvno(context, entries, &mkvno)) != 0)
+                goto cleanup;
            bersecretkey = krb5_encode_krbsecretkey (entries->key_data,
-                                                    entries->n_key_data);
+                                                    entries->n_key_data, mkvno);
 
            if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey",
                                              LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, bersecretkey)) != 0)
@@ -1220,11 +1224,12 @@ cleanup:
 }
 
 krb5_error_code
-krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data)
+krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data, mkvno)
     krb5_context                context;
     krb5_db_entry               *entries;
     struct berval               **bvalues;
     krb5_tl_data                *userinfo_tl_data;
+    krb5_kvno                   *mkvno;
 {
     char                        *user=NULL;
     int                         i=0, j=0, noofkeys=0;
@@ -1235,7 +1240,6 @@ krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data)
        goto cleanup;
 
     for (i=0; bvalues[i] != NULL; ++i) {
-       int mkvno; /* Not used currently */
        krb5_int16 n_kd;
        krb5_key_data *kd;
        krb5_data in;
@@ -1248,7 +1252,7 @@ krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data)
        st = asn1_decode_sequence_of_keys (&in,
                                           &kd,
                                           &n_kd,
-                                          &mkvno);
+                                          mkvno);
 
        if (st != 0) {
            const char *msg = error_message(st);
index ffe6c36658fd1b41d96df732520def7268793b7c..db17509aeb807189bf7094ca5425b21e36c650db 100644 (file)
@@ -68,6 +68,7 @@ typedef struct _krb5_ldap_realm_params {
   char          **passwdservers;
   krb5_tl_data  *tl_data;
   krb5_keyblock mkey;
+  krb5_keylist_node *mkey_list; /* all master keys in use for the realm */
   long          mask;
 } krb5_ldap_realm_params;