aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/kdb.h170
-rw-r--r--src/kadmin/cli/kadmin.c2
-rw-r--r--src/kadmin/dbutil/Makefile.in12
-rw-r--r--src/kadmin/dbutil/dump.c97
-rw-r--r--src/kadmin/dbutil/kdb5_create.c25
-rw-r--r--src/kadmin/dbutil/kdb5_mkey.c1419
-rw-r--r--src/kadmin/dbutil/kdb5_stash.c49
-rw-r--r--src/kadmin/dbutil/kdb5_util.M31
-rw-r--r--src/kadmin/dbutil/kdb5_util.c79
-rw-r--r--src/kadmin/dbutil/kdb5_util.h17
-rw-r--r--src/kadmin/server/ovsec_kadmd.c6
-rw-r--r--src/kdc/do_as_req.c44
-rw-r--r--src/kdc/do_tgs_req.c24
-rw-r--r--src/kdc/extern.c1
-rw-r--r--src/kdc/extern.h6
-rw-r--r--src/kdc/kdc_preauth.c88
-rw-r--r--src/kdc/kdc_util.c22
-rw-r--r--src/kdc/main.c23
-rw-r--r--src/lib/kadm5/srv/libkadm5srv.exports1
-rw-r--r--src/lib/kadm5/srv/server_kdb.c43
-rw-r--r--src/lib/kadm5/srv/svr_iters.c1
-rw-r--r--src/lib/kadm5/srv/svr_principal.c153
-rw-r--r--src/lib/kdb/kdb5.c782
-rw-r--r--src/lib/kdb/kdb_cpw.c18
-rw-r--r--src/lib/kdb/kdb_default.c220
-rw-r--r--src/lib/kdb/keytab.c7
-rw-r--r--src/lib/kdb/libkdb5.exports20
-rw-r--r--src/lib/krb5/error_tables/kdb5_err.et3
-rw-r--r--src/plugins/kdb/db2/db2_exp.c28
-rw-r--r--src/plugins/kdb/db2/kdb_db2.c42
-rw-r--r--src/plugins/kdb/db2/kdb_db2.h13
-rw-r--r--src/plugins/kdb/ldap/ldap_exp.c4
-rw-r--r--src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c27
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h6
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c45
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c42
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c9
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h2
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c18
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h1
40 files changed, 3404 insertions, 196 deletions
diff --git a/src/include/kdb.h b/src/include/kdb.h
index a3142c1..240ac0f 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -53,6 +53,11 @@
* 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,
@@ -337,15 +392,61 @@ krb5_dbekd_encrypt_key_data( krb5_context context,
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
@@ -383,6 +484,11 @@ krb5_dbe_lookup_last_pwd_change( krb5_context context,
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,
krb5_tl_data * new_tl_data);
@@ -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,
diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
index 469deaa..af6673d 100644
--- a/src/kadmin/cli/kadmin.c
+++ b/src/kadmin/cli/kadmin.c
@@ -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++) {
diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in
index dc5881b..e88d8b3 100644
--- a/src/kadmin/dbutil/Makefile.in
+++ b/src/kadmin/dbutil/Makefile.in
@@ -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
diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c
index 24f4d09..55677f7 100644
--- a/src/kadmin/dbutil/dump.c
+++ b/src/kadmin/dbutil/dump.c
@@ -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;
diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c
index c7fb31b..6a638a3 100644
--- a/src/kadmin/dbutil/kdb5_create.c
+++ b/src/kadmin/dbutil/kdb5_create.c
@@ -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
index 0000000..d2d5176
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_mkey.c
@@ -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;
+}
diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c
index 3583a32..cdd947a 100644
--- a/src/kadmin/dbutil/kdb5_stash.c
+++ b/src/kadmin/dbutil/kdb5_stash.c
@@ -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) {
diff --git a/src/kadmin/dbutil/kdb5_util.M b/src/kadmin/dbutil/kdb5_util.M
index dc34bc8..294357f 100644
--- a/src/kadmin/dbutil/kdb5_util.M
+++ b/src/kadmin/dbutil/kdb5_util.M
@@ -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)
diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c
index 9bc8537..6cb70c1 100644
--- a/src/kadmin/dbutil/kdb5_util.c
+++ b/src/kadmin/dbutil/kdb5_util.c
@@ -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
@@ -53,6 +53,11 @@
* 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)
diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h
index 59f92d4..6e99ac3 100644
--- a/src/kadmin/dbutil/kdb5_util.h
+++ b/src/kadmin/dbutil/kdb5_util.h
@@ -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);
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
index 573a2c0..81e7475 100644
--- a/src/kadmin/server/ovsec_kadmd.c
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -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.");
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 9571fb2..8db39ac 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -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";
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 0843002..a6ce704 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -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";
diff --git a/src/kdc/extern.c b/src/kdc/extern.c
index 2a2c1ae..7ebc7bb 100644
--- a/src/kdc/extern.c
+++ b/src/kdc/extern.c
@@ -27,6 +27,7 @@
*/
#include "k5-int.h"
+#include "kdb.h"
#include "extern.h"
/* real declarations of KDC's externs */
diff --git a/src/kdc/extern.h b/src/kdc/extern.h
index 20cc4bc..88e8b0d 100644
--- a/src/kdc/extern.h
+++ b/src/kdc/extern.h
@@ -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
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 6ec1564..7aacca4 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -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");
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 34a8ed0..28b4a37 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -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
diff --git a/src/kdc/main.c b/src/kdc/main.c
index b933468..bb4d758 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -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,
diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports
index a025776..545d43b 100644
--- a/src/lib/kadm5/srv/libkadm5srv.exports
+++ b/src/lib/kadm5/srv/libkadm5srv.exports
@@ -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
diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c
index 836cd00..47f00c0 100644
--- a/src/lib/kadm5/srv/server_kdb.c
+++ b/src/lib/kadm5/srv/server_kdb.c
@@ -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;
diff --git a/src/lib/kadm5/srv/svr_iters.c b/src/lib/kadm5/srv/svr_iters.c
index cd3fb41..757d3ab 100644
--- a/src/lib/kadm5/srv/svr_iters.c
+++ b/src/lib/kadm5/srv/svr_iters.c
@@ -46,6 +46,7 @@ struct iter_data {
#endif
};
+/* XXX Duplicated in kdb5_util! */
/*
* Function: glob_to_regexp
*
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index 2bdc1f5..2071437 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -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;
diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c
index 88df6bc..2252c3a 100644
--- a/src/lib/kdb/kdb5.c
+++ b/src/lib/kdb/kdb5.c
@@ -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
@@ -23,6 +23,11 @@
*/
/*
+ * 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;
}
@@ -1403,6 +1491,35 @@ krb5_db_set_mkey(krb5_context context, krb5_keyblock * 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)
{
krb5_error_code status = 0;
@@ -1432,6 +1549,90 @@ krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
}
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,
krb5_principal mname,
@@ -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)
{
@@ -1886,6 +2281,347 @@ krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
}
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;
krb5_db_entry *entry;
@@ -1903,6 +2639,44 @@ krb5_dbe_update_last_pwd_change(context, entry, stamp)
}
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;
krb5_db_entry *entry;
diff --git a/src/lib/kdb/kdb_cpw.c b/src/lib/kdb/kdb_cpw.c
index a59d98e..2062055 100644
--- a/src/lib/kdb/kdb_cpw.c
+++ b/src/lib/kdb/kdb_cpw.c
@@ -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;
diff --git a/src/lib/kdb/kdb_default.c b/src/lib/kdb/kdb_default.c
index f173e12..df87916 100644
--- a/src/lib/kdb/kdb_default.c
+++ b/src/lib/kdb/kdb_default.c
@@ -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
@@ -25,6 +25,11 @@
*
*/
+/*
+ * 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)
{
diff --git a/src/lib/kdb/keytab.c b/src/lib/kdb/keytab.c
index 632c927..47626f1 100644
--- a/src/lib/kdb/keytab.c
+++ b/src/lib/kdb/keytab.c
@@ -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;
diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports
index cbd8711..c0b161f 100644
--- a/src/lib/kdb/libkdb5.exports
+++ b/src/lib/kdb/libkdb5.exports
@@ -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
diff --git a/src/lib/krb5/error_tables/kdb5_err.et b/src/lib/krb5/error_tables/kdb5_err.et
index b15af8c..cd7214d 100644
--- a/src/lib/krb5/error_tables/kdb5_err.et
+++ b/src/lib/krb5/error_tables/kdb5_err.et
@@ -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"
diff --git a/src/plugins/kdb/db2/db2_exp.c b/src/plugins/kdb/db2/db2_exp.c
index 123d20a..5c81624 100644
--- a/src/plugins/kdb/db2/db2_exp.c
+++ b/src/plugins/kdb/db2/db2_exp.c
@@ -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,
};
diff --git a/src/plugins/kdb/db2/kdb_db2.c b/src/plugins/kdb/db2/kdb_db2.c
index 704e47d..90c8933 100644
--- a/src/plugins/kdb/db2/kdb_db2.c
+++ b/src/plugins/kdb/db2/kdb_db2.c
@@ -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.
*
@@ -1171,8 +1202,19 @@ krb5_db2_db_iterate_ext(krb5_context context,
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) {
diff --git a/src/plugins/kdb/db2/kdb_db2.h b/src/plugins/kdb/db2/kdb_db2.h
index d6cb1e8..640c4d6 100644
--- a/src/plugins/kdb/db2/kdb_db2.h
+++ b/src/plugins/kdb/db2/kdb_db2.h
@@ -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 */
diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c
index eaeef2a..dcfe93c 100644
--- a/src/plugins/kdb/ldap/ldap_exp.c
+++ b/src/plugins/kdb/ldap/ldap_exp.c
@@ -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
diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
index c13d967..60d9e25 100644
--- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
+++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
@@ -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:
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
index 74bf4b1..802ab0f 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
@@ -267,6 +267,12 @@ 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 **);
krb5_error_code
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
index d4c6ac8..f8e1d44 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c
@@ -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 */
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c
index 9a36419..6da0806 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c
@@ -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;
+}
+
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
index 79ca634..f0734de 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
@@ -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 */
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
index 18e2acc..502e71c 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
@@ -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);
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
index 561a65d..e52a618 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
@@ -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);
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h
index ffe6c36..db17509 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h
@@ -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;