void KRB5_CALLCONV krb5_free_pa_enc_ts
(krb5_context, krb5_pa_enc_ts *);
+#include "kdb.h"
+
+void KRB5_CALLCONV krb5_free_key_data_contents
+ (krb5_context, krb5_key_data *);
+
/* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
#include "com_err.h"
#include "k5-plugin.h"
#define KRB5_TL_DB_ARGS 0x7fff
#endif /* SECURID */
#define KRB5_TL_USER_CERTIFICATE 0x0007
-
+#define KRB5_TL_MKVNO 0x0008
+#define KRB5_TL_ACTKVNO 0x0009
+#define KRB5_TL_MKEY_AUX 0x000a
+
+/* version number for KRB5_TL_ACTKVNO data */
+#define KRB5_TL_ACTKVNO_VER_1 1
+
+/* version number for KRB5_TL_MKEY_AUX data */
+#define KRB5_TL_MKEY_AUX_VER_1 1
+
+/* XXX WAF: the indef. struct typedefs below may not be needed so ifdef'ing them
+ * out for now */
+/*
+ * Doing this because it this struct should be defined in k5-int.h but this
+ * header file can't include that one.
+ */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+#ifndef _KRB5_INT_H
+struct _krb5_actkvno_node;
+typedef struct _krb5_actkvno_node krb5_actkvno_node;
+struct _krb5_mkey_aux_node;
+typedef struct _krb5_mkey_aux_node krb5_mkey_aux_node;
+#endif
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+typedef struct _krb5_actkvno_node {
+ struct _krb5_actkvno_node *next;
+ krb5_kvno act_kvno;
+ krb5_timestamp act_time;
+} krb5_actkvno_node;
+
+typedef struct _krb5_mkey_aux_node {
+ struct _krb5_mkey_aux_node *next;
+ krb5_kvno mkey_kvno; /* kvno of mkey protecting the latest_mkey */
+ /* XXX WAF: maybe krb5_data isn't adequate, the kvno and enctype is needed I
+ * think. Perhaps krb5_key_data should be used?
+ */
+ krb5_key_data latest_mkey; /* most recent mkey */
+} krb5_mkey_aux_node;
+
/*
* Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
* on the principal.
krb5_kvno kvno,
krb5_keyblock *mkey );
krb5_error_code
+krb5_db_fetch_mkey_list( krb5_context context,
+ krb5_principal mname,
+ const krb5_keyblock * mkey,
+ krb5_kvno mkvno,
+ krb5_keyblock_node **mkeys_list );
+
+krb5_error_code
+krb5_db_free_mkey_list( krb5_context context,
+ krb5_keyblock_node *mkey_list );
+
+krb5_error_code
krb5_dbe_find_enctype( krb5_context kcontext,
krb5_db_entry *dbentp,
krb5_int32 ktype,
int keyver,
krb5_key_data * key_data);
+krb5_error_code
+krb5_dbe_fetch_act_mkey_list(krb5_context context,
+ krb5_principal mprinc,
+ krb5_actkvno_node **act_mkey_list);
+
+krb5_error_code
+krb5_dbe_find_act_mkey( krb5_context context,
+ krb5_keyblock_node * mkey_list,
+ krb5_actkvno_node * act_mkey_list,
+ krb5_keyblock ** act_mkey);
+
+krb5_error_code
+krb5_dbe_find_mkey( krb5_context context,
+ krb5_keyblock_node * mkey_list,
+ krb5_db_entry * entry,
+ krb5_keyblock ** mkey);
+
+krb5_error_code
+krb5_dbe_lookup_mkvno( krb5_context context,
+ krb5_db_entry * entry,
+ krb5_kvno * mkvno);
+
krb5_error_code
krb5_dbe_lookup_mod_princ_data( krb5_context context,
krb5_db_entry * entry,
krb5_timestamp * mod_time,
krb5_principal * mod_princ);
+krb5_error_code
+krb5_dbe_lookup_mkey_aux( krb5_context context,
+ krb5_db_entry * entry,
+ krb5_mkey_aux_node ** mkey_aux_data_list);
+krb5_error_code
+krb5_dbe_update_mkvno( krb5_context context,
+ krb5_db_entry * entry,
+ krb5_kvno mkvno);
krb5_error_code
-krb5_dbe_update_last_pwd_change( krb5_context context,
- krb5_db_entry * entry,
+krb5_dbe_lookup_actkvno( krb5_context context,
+ krb5_db_entry * entry,
+ krb5_actkvno_node ** actkvno_list);
+
+krb5_error_code
+krb5_dbe_update_mkey_aux( krb5_context context,
+ krb5_db_entry * entry,
+ krb5_mkey_aux_node * mkey_aux_data_list);
+
+krb5_error_code
+krb5_dbe_update_actkvno(krb5_context context,
+ krb5_db_entry * entry,
+ const krb5_actkvno_node *actkvno_list);
+
+krb5_error_code
+krb5_dbe_update_last_pwd_change( krb5_context context,
+ krb5_db_entry * entry,
krb5_timestamp stamp);
krb5_error_code
char * passwd,
krb5_db_entry * db_entry);
+int
+get_key_data_kvno( krb5_context context,
+ int count,
+ krb5_key_data * data);
+
+
/* default functions. Should not be directly called */
/*
* Default functions prototype
krb5_kvno kvno,
krb5_keyblock *mkey);
+krb5_error_code
+krb5_def_fetch_mkey_list( krb5_context context,
+ krb5_principal mprinc,
+ const krb5_keyblock *mkey,
+ krb5_kvno mkvno,
+ krb5_keyblock_node **mkeys_list);
+
krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
char *pwd,
krb5_keyblock *key );
+krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext,
+ krb5_keyblock_node *keylist );
+
krb5_error_code kdb_def_get_mkey ( krb5_context kcontext,
krb5_keyblock **key );
+krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext,
+ krb5_keyblock_node **keylist );
+
krb5_error_code
krb5_dbe_def_cpw( krb5_context context,
krb5_keyblock * master_key,
krb5_octet *contents;
} krb5_keyblock;
+typedef struct _krb5_keylist_node {
+ krb5_keyblock keyblock;
+ krb5_kvno kvno;
+ struct _krb5_keylist_node *next;
+ } krb5_keyblock_node;
+
#ifdef KRB5_OLD_CRYPTO
typedef struct _krb5_encrypt_block {
krb5_magic magic;
} else
printf("no salt\n");
}
+ printf("MKey: vno %d\n",
+ dprinc.mkvno);
printf("Attributes:");
for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
### kdb5_destroy.o ovload.o import_err.o strtok.o
###
-SRCS = kdb5_util.c kdb5_create.c kadm5_create.c string_table.c kdb5_destroy.c kdb5_stash.c import_err.c strtok.c dump.c ovload.c
+SRCS = kdb5_util.c kdb5_create.c kadm5_create.c string_table.c kdb5_destroy.c \
+ kdb5_stash.c import_err.c strtok.c dump.c ovload.c kdb5_mkey.c
-OBJS = kdb5_util.o kdb5_create.o kadm5_create.o string_table.o kdb5_destroy.o kdb5_stash.o import_err.o strtok.o dump.o ovload.o
+OBJS = kdb5_util.o kdb5_create.o kadm5_create.o string_table.o kdb5_destroy.o \
+ kdb5_stash.o import_err.o strtok.o dump.o ovload.o kdb5_mkey.o
+
+GETDATE = ../cli/getdate.o
all:: $(PROG)
-$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB4COMPAT_DEPLIBS)
- $(CC_LINK) -o $(PROG) $(OBJS) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB4COMPAT_LIBS)
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB4COMPAT_DEPLIBS) $(GETDATE)
+ $(CC_LINK) -o $(PROG) $(OBJS) $(GETDATE) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB4COMPAT_LIBS)
import_err.c import_err.h: $(srcdir)/import_err.et
is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);
+ /* XXX WAF: need to fix this! */
if (is_mkey && db_entry->n_key_data != 1)
fprintf(stderr,
"Master key db entry has %d keys, expecting only 1!\n",
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);
--- /dev/null
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <k5-int.h>
+#include <kdb.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "kdb5_util.h"
+
+extern krb5_keyblock master_keyblock; /* current mkey */
+extern krb5_principal master_princ;
+extern krb5_data master_salt;
+extern char *mkey_password;
+extern char *progname;
+extern int exit_status;
+extern kadm5_config_params global_params;
+extern krb5_context util_context;
+extern time_t get_date(char *);
+
+static char *strdate(krb5_timestamp when)
+{
+ struct tm *tm;
+ static char out[40];
+
+ time_t lcltim = when;
+ tm = localtime(&lcltim);
+ strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
+ return out;
+}
+
+void
+kdb5_add_mkey(int argc, char *argv[])
+{
+ int optchar;
+ krb5_error_code retval;
+ char *mkey_fullname;
+ char *pw_str = 0;
+ unsigned int pw_size = 0;
+ int do_stash = 0, nentries = 0;
+ int old_key_data_count, i, j;
+ krb5_boolean more = 0;
+ krb5_data pwd;
+ krb5_kvno old_kvno, new_mkey_kvno;
+ krb5_keyblock new_master_keyblock;
+ krb5_keyblock plainkey;
+ krb5_key_data tmp_key_data, *old_key_data, *key_data;
+ krb5_enctype new_master_enctype = DEFAULT_KDC_ENCTYPE;
+ char *new_mkey_password;
+ krb5_db_entry master_entry;
+ krb5_timestamp now;
+ krb5_mkey_aux_node *mkey_aux_data_head, **mkey_aux_data,
+ *cur_mkey_aux_data, *next_mkey_aux_data;
+
+ /*
+ * The command table entry for this command causes open_db_and_mkey() to be
+ * called first to open the KDB and get the current mkey.
+ */
+
+ while ((optchar = getopt(argc, argv, "k:s")) != -1) {
+ switch(optchar) {
+ case 'k':
+ if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
+ com_err(progname, EINVAL, ": %s is an invalid enctype", optarg);
+ exit_status++;
+ return;
+ }
+ break;
+ case 's':
+ do_stash++;
+ break;
+ case '?':
+ default:
+ usage();
+ return;
+ }
+ }
+
+ /* assemble & parse the master key name */
+ if ((retval = krb5_db_setup_mkey_name(util_context,
+ global_params.mkey_name,
+ global_params.realm,
+ &mkey_fullname, &master_princ))) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return;
+ }
+
+ retval = krb5_db_get_principal(util_context, master_princ, &master_entry, &nentries,
+ &more);
+ if (retval != 0) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return;
+ }
+
+ printf("Creating new master key for master key principal '%s'\n",
+ mkey_fullname);
+
+ printf("You will be prompted for a new database Master Password.\n");
+ printf("It is important that you NOT FORGET this password.\n");
+ fflush(stdout);
+
+ pw_size = 1024;
+ pw_str = malloc(pw_size);
+ if (pw_str == NULL) {
+ com_err(progname, ENOMEM, "while creating new master key");
+ exit_status++;
+ return;
+ }
+
+ retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(progname, retval, "while reading new master key from keyboard");
+ exit_status++;
+ return;
+ }
+ new_mkey_password = pw_str;
+
+ pwd.data = new_mkey_password;
+ pwd.length = strlen(new_mkey_password);
+ retval = krb5_principal2salt(util_context, master_princ, &master_salt);
+ if (retval) {
+ com_err(progname, retval, "while calculating master key salt");
+ exit_status++;
+ return;
+ }
+
+ retval = krb5_c_string_to_key(util_context, new_master_enctype,
+ &pwd, &master_salt, &new_master_keyblock);
+ if (retval) {
+ com_err(progname, retval, "while transforming master key from password");
+ exit_status++;
+ return;
+ }
+
+ /* First save the old keydata */
+ old_kvno = get_key_data_kvno(util_context, master_entry.n_key_data,
+ master_entry.key_data);
+ old_key_data_count = master_entry.n_key_data;
+ old_key_data = master_entry.key_data;
+
+ /* alloc enough space to hold new and existing key_data */
+ /*
+ * The encrypted key is malloc'ed by krb5_dbekd_encrypt_key_data and
+ * krb5_key_data key_data_contents is a pointer to this key. Using some
+ * logic from master_key_convert().
+ */
+ master_entry.key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * (old_key_data_count + 1));
+ if (master_entry.key_data == NULL) {
+ com_err(progname, ENOMEM, "while adding new master key");
+ exit_status++;
+ return;
+ }
+ memset((char *) master_entry.key_data, 0, sizeof(krb5_key_data) * (old_key_data_count + 1));
+ master_entry.n_key_data = old_key_data_count + 1;
+
+ new_mkey_kvno = old_kvno + 1;
+ /* deal with wrapping? */
+ if (new_mkey_kvno == 0)
+ new_mkey_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
+
+ /* Note, mkey does not have salt */
+ /* add new mkey encrypted with itself to mkey princ entry */
+ if ((retval = krb5_dbekd_encrypt_key_data(util_context, &new_master_keyblock,
+ &new_master_keyblock, NULL,
+ (int) new_mkey_kvno, master_entry.key_data))) {
+ com_err(progname, retval, "while creating new master key");
+ exit_status++;
+ return;
+ }
+
+ /*
+ * Need to decrypt old keys with the current mkey which is in the global
+ * master_keyblock and encrypt those keys with the latest mkey.
+ *
+ * The new mkey is followed by existing keys.
+ *
+ * First, set up for creating a krb5_mkey_aux_node list which will be used
+ * to update the mkey aux data for the mkey princ entry.
+ */
+ mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+ if (mkey_aux_data_head == NULL) {
+ com_err(progname, ENOMEM, "while creating mkey_aux_data");
+ exit_status++;
+ return;
+ }
+ memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
+ mkey_aux_data = &mkey_aux_data_head;
+
+ for (i = 0; i < old_key_data_count; i++) {
+ key_data = &old_key_data[i];
+
+ retval = krb5_dbekd_decrypt_key_data(util_context, &master_keyblock,
+ key_data, &plainkey, NULL);
+ if (retval) {
+ com_err(progname, retval, "while decrypting master keys");
+ exit_status++;
+ return;
+ }
+
+ /*
+ * Create a list of krb5_mkey_aux_node nodes. One node contains the new
+ * mkey encrypted by an old mkey and the old mkey's kvno (one node per
+ * old mkey).
+ */
+
+ if (*mkey_aux_data == NULL) {
+ /* *mkey_aux_data points to next field of previous node */
+ *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+ if (mkey_aux_data == NULL) {
+ com_err(progname, ENOMEM, "while creating mkey_aux_data");
+ exit_status++;
+ return;
+ }
+ memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
+ }
+
+ /* encrypt the new mkey with the older mkey */
+ retval = krb5_dbekd_encrypt_key_data(util_context, &plainkey,
+ &new_master_keyblock,
+ NULL, /* no keysalt */
+ (int) key_data->key_data_kvno,
+ &tmp_key_data);
+ if (retval) {
+ com_err(progname, retval, "while encrypting master keys");
+ exit_status++;
+ return;
+ }
+
+ (*mkey_aux_data)->latest_mkey = tmp_key_data;
+ (*mkey_aux_data)->mkey_kvno = key_data->key_data_kvno;
+
+ mkey_aux_data = &((*mkey_aux_data)->next);
+
+ /* Store old key in master_entry keydata, + 1 to skip the first key_data entry */
+ retval = krb5_dbekd_encrypt_key_data(util_context, &new_master_keyblock,
+ &plainkey, NULL, /* no keysalt */
+ (int) key_data->key_data_kvno,
+ &master_entry.key_data[i+1]);
+ if (retval) {
+ com_err(progname, retval, "while encrypting master keys");
+ exit_status++;
+ return;
+ }
+
+ /* free plain text key and old key data entry */
+ krb5_free_keyblock_contents(util_context, &plainkey);
+ for (j = 0; j < key_data->key_data_ver; j++) {
+ if (key_data->key_data_length[j]) {
+ /* the key_data contents are encrypted so no clearing first */
+ free(key_data->key_data_contents[j]);
+ }
+ }
+ }
+
+ if ((retval = krb5_dbe_update_mkey_aux(util_context, &master_entry,
+ mkey_aux_data_head))) {
+ com_err(progname, retval, "while updating mkey aux data");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, "while getting current time");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
+ now, master_princ))) {
+ com_err(progname, retval, "while updating the master key principal modification time");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
+ (void) krb5_db_fini(util_context);
+ com_err(progname, retval, "while adding master key entry to the database");
+ exit_status++;
+ return;
+ }
+
+ if (do_stash) {
+ retval = krb5_db_store_master_key(util_context,
+ global_params.stash_file,
+ master_princ,
+ new_mkey_kvno,
+ &new_master_keyblock,
+ mkey_password);
+ if (retval) {
+ com_err(progname, errno, "while storing key");
+ printf("Warning: couldn't stash master key.\n");
+ }
+ }
+ /* clean up */
+ (void) krb5_db_fini(util_context);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ free(master_keyblock.contents);
+ memset((char *)new_master_keyblock.contents, 0, new_master_keyblock.length);
+ free(new_master_keyblock.contents);
+ if (pw_str) {
+ memset(pw_str, 0, pw_size);
+ free(pw_str);
+ }
+ free(master_salt.data);
+ free(mkey_fullname);
+ for (cur_mkey_aux_data = mkey_aux_data_head; cur_mkey_aux_data != NULL;
+ cur_mkey_aux_data = next_mkey_aux_data) {
+
+ next_mkey_aux_data = cur_mkey_aux_data->next;
+ krb5_free_key_data_contents(util_context, &(cur_mkey_aux_data->latest_mkey));
+ free(cur_mkey_aux_data);
+ }
+ return;
+}
+
+void
+kdb5_use_mkey(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ char *mkey_fullname;
+ krb5_kvno use_kvno;
+ krb5_timestamp now, start_time;
+ krb5_actkvno_node *actkvno_list, *new_actkvno_list_head, *new_actkvno, *prev_actkvno, *cur_actkvno;
+ krb5_db_entry master_entry;
+ int nentries = 0;
+ krb5_boolean more = 0;
+
+ if (argc < 1 || argc > 2) {
+ /* usage calls exit */
+ usage();
+ }
+
+ use_kvno = (int) strtol(argv[0], (char **)NULL, 10);
+ if (use_kvno == 0) {
+ com_err(progname, EINVAL, ": 0 is an invalid KVNO value.");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, "while getting current time.");
+ exit_status++;
+ return;
+ }
+
+ if (argc == 2) {
+ start_time = (krb5_timestamp) get_date(argv[0]);
+ } else {
+ start_time = now;
+ }
+
+ /*
+ * Need to:
+ *
+ * 1. get mkey princ
+ * 2. verify that mprinc actually has a mkey with the new actkvno
+ * 2. get krb5_actkvno_node list
+ * 3. add use_kvno to actkvno list (sorted in right spot)
+ * 4. update mkey princ's tl data
+ * 5. put mkey princ.
+ */
+
+ /* assemble & parse the master key name */
+ if ((retval = krb5_db_setup_mkey_name(util_context,
+ global_params.mkey_name,
+ global_params.realm,
+ &mkey_fullname, &master_princ))) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return;
+ }
+
+ retval = krb5_db_get_principal(util_context, master_princ, &master_entry, &nentries,
+ &more);
+ if (retval != 0) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return;
+ }
+
+ /* XXX WAF: verify that the provided kvno is valid */
+
+ retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
+ if (retval != 0) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return;
+ }
+
+ /*
+ * determine which nodes to delete and where to insert new act kvno node
+ */
+
+ /* alloc enough space to hold new and existing key_data */
+ new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+ if (new_actkvno == NULL) {
+ com_err(progname, ENOMEM, "while adding new master key");
+ exit_status++;
+ return;
+ }
+
+ new_actkvno->act_kvno = use_kvno;
+ new_actkvno->act_time = start_time;
+
+ if (actkvno_list == NULL || new_actkvno->act_time < actkvno_list->act_time) {
+ /* insert new actkvno at head of list and link rest following */
+ new_actkvno->next = actkvno_list;
+ new_actkvno_list_head = new_actkvno;
+ } else {
+ for (new_actkvno_list_head = prev_actkvno = cur_actkvno = actkvno_list; cur_actkvno != NULL;
+ prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+ if (cur_actkvno->act_time <= now) {
+ if (new_actkvno->act_time < cur_actkvno->act_time) {
+ /*
+ * This is a problem as the new actkvno would be skipped and
+ * not added to the entries for the mkey princ.
+ */
+ com_err(progname, EINVAL,
+ "Activation time %s is less than a existing currently active kvno %d (activation time %s)",
+ strdate(new_actkvno->act_time), cur_actkvno->act_kvno, strdate(cur_actkvno->act_time));
+ exit_status++;
+ return;
+ }
+ /*
+ * New list head should point to the most current valid node in
+ * order to trim out of date entries.
+ */
+ new_actkvno_list_head = cur_actkvno;
+ }
+
+ if (new_actkvno->act_time < cur_actkvno->act_time) {
+ if (new_actkvno_list_head == cur_actkvno) {
+ /*
+ * XXX WAF: trying to minimize race condition issue here,
+ * maybe there is a better way to do this?
+ */
+ com_err(progname, EINVAL,
+ "Activation time %s is less than an existing currently active kvno %d (activation time %s)",
+ strdate(new_actkvno->act_time), cur_actkvno->act_kvno, strdate(cur_actkvno->act_time));
+ exit_status++;
+ return;
+ }
+ prev_actkvno->next = new_actkvno;
+ new_actkvno->next = cur_actkvno;
+ break;
+ } else if (cur_actkvno->next == NULL) {
+ /* end of line, just add new node to end of list */
+ cur_actkvno->next = new_actkvno;
+ break;
+ }
+ } /* end for (new_actkvno_list_head = prev_actkvno = cur_actkvno = actkvno_list */
+ }
+
+ if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry, new_actkvno_list_head))) {
+ com_err(progname, retval, "while updating actkvno data for master principal entry.");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
+ now, master_princ))) {
+ com_err(progname, retval, "while updating the master key principal modification time");
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
+ (void) krb5_db_fini(util_context);
+ com_err(progname, retval, "while adding master key entry to the database");
+ exit_status++;
+ return;
+ }
+
+ /* clean up */
+ (void) krb5_db_fini(util_context);
+ free(mkey_fullname);
+ for (cur_actkvno = actkvno_list; cur_actkvno != NULL;) {
+
+ prev_actkvno = cur_actkvno;
+ cur_actkvno = cur_actkvno->next;
+ free(prev_actkvno);
+ }
+ return;
+}
}
extern krb5_keyblock master_keyblock;
+extern krb5_keyblock_node *master_keylist;
extern krb5_principal master_princ;
krb5_db_entry master_entry;
int valid_master_key = 0;
/* {"dump_v4", dump_v4db, 1}, */
/* {"load_v4", load_v4db, 0}, */
{"ark", add_random_key, 1},
+ {"add_mkey", kdb5_add_mkey, 1}, /* 1 is opendb */
+ {"use_mkey", kdb5_use_mkey, 1}, /* 1 is opendb */
{NULL, NULL, 0},
};
exit_status++;
return(0);
}
+
if ((retval = krb5_db_verify_master_key(util_context, master_princ,
kvno, &master_keyblock))) {
com_err(progname, retval, "while verifying master key");
return(1);
}
+ /*
+ * I think I need to get the mkey list here so the ark command will
+ * work properly.
+ */
+ if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
+ &master_keyblock, kvno, &master_keylist))) {
+ com_err(progname, retval, "while getting master key list");
+ com_err(progname, 0, "Warning: proceeding without master key list");
+ exit_status++;
+ return(0);
+ }
+
seed.length = master_keyblock.length;
seed.data = master_keyblock.contents;
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);
}
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;
char *me = progname;
char *ks_str = NULL;
char *pr_str;
+ krb5_keyblock *tmp_mkey;
if (argc < 2)
usage();
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)
extern void kdb5_create (int argc, char **argv);
extern void kdb5_destroy (int argc, char **argv);
extern void kdb5_stash (int argc, char **argv);
+extern void kdb5_add_mkey (int argc, char **argv);
+extern void kdb5_use_mkey (int argc, char **argv);
extern void update_ok_file (char *file_name);
#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw"
extern krb5_keyblock master_keyblock;
+extern krb5_keyblock_node *master_keylist;
char *build_princ_name(char *name, char *realm);
void log_badauth(OM_uint32 major, OM_uint32 minor,
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.");
krb5_keyblock encrypting_key;
const char *status;
krb5_key_data *server_key, *client_key;
+ krb5_keyblock *tmp_mkey;
krb5_enctype useenctype;
#ifdef KRBCONF_KDC_MODIFIES_KDB
krb5_boolean update_client = 0;
goto errout;
}
+ if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey))) {
+ status = "FINDING_MASTER_KEY";
+ goto errout;
+ }
+
/* convert server.key into a real key (it may be encrypted
in the database) */
- if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
+ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey,
server_key, &encrypting_key,
NULL))) {
status = "DECRYPT_SERVER_KEY";
goto errout;
}
+ if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &client, &tmp_mkey))) {
+ status = "FINDING_MASTER_KEY";
+ goto errout;
+ }
+
/* convert client.key_data into a real key */
- if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
+ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey,
client_key, &encrypting_key,
NULL))) {
status = "DECRYPT_CLIENT_KEY";
int nprincs = 0;
krb5_boolean more;
krb5_timestamp kdc_time, authtime=0;
- krb5_keyblock session_key;
+ krb5_keyblock session_key, *tmp_mkey;
krb5_timestamp until, rtime;
krb5_keyblock encrypting_key;
krb5_key_data *server_key;
status = "FINDING_SERVER_KEY";
goto cleanup;
}
+
+ if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey))) {
+ status = "FINDING_MASTER_KEY";
+ goto cleanup;
+ }
+
/* convert server.key into a real key (it may be encrypted
* in the database) */
if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
- &master_keyblock,
+ tmp_mkey,
server_key, &encrypting_key,
NULL))) {
status = "DECRYPT_SERVER_KEY";
krb5_context realm_context; /* Context to be used for realm */
krb5_keytab realm_keytab; /* keytab to be used for this realm */
char * realm_profile; /* Profile file for this realm */
+ krb5_keyblock_node * mkey_list; /* list of mkeys in use for this realm */
/*
* Database per-realm data.
*/
char * realm_stash; /* Stash file name for realm */
char * realm_mpname; /* Master principal name for realm */
krb5_principal realm_mprinc; /* Master principal for realm */
+ /* XXX WAF: is realm_mkey the most current key in the keytab (or from
+ * command line)? Or should this be the active key? I need to make sure
+ * this is handled properly. what about the kvno of this key?
+ * or maybe this should go away and be replaced with a function that
+ * returns the proper mkey given a princ.
+ */
krb5_keyblock realm_mkey; /* Master key for this realm */
/*
* TGS per-realm data.
#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
int i, k;
krb5_data *ret;
krb5_deltat *delta;
- krb5_keyblock *keys;
+ krb5_keyblock *keys, *tmp_mkey;
krb5_key_data *entry_key;
switch (type) {
ret->data = (char *) keys;
ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
memset(ret->data, 0, ret->length);
+ if ((ret = krb5_dbe_find_mkey(context, master_keylist, &entry, &tmp_mkey)))
+ return (ret);
k = 0;
for (i = 0; i < request->nktypes; i++) {
entry_key = NULL;
if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
-1, 0, &entry_key) != 0)
continue;
- if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+ if (krb5_dbekd_decrypt_key_data(context, tmp_mkey,
entry_key, &keys[k], NULL) != 0) {
if (keys[k].contents != NULL)
krb5_free_keyblock_contents(context, &keys[k]);
krb5_data scratch;
krb5_data enc_ts_data;
krb5_enc_data *enc_data = 0;
- krb5_keyblock key;
+ krb5_keyblock key, *tmp_mkey;
krb5_key_data * client_key;
krb5_int32 start;
krb5_timestamp timenow;
if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
goto cleanup;
+ if ((retval = krb5_dbe_find_mkey(context, master_keylist, &client, &tmp_mkey)))
+ goto cleanup;
+
start = 0;
decrypt_err = 0;
while (1) {
-1, 0, &client_key)))
goto cleanup;
- if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+ if ((retval = krb5_dbekd_decrypt_key_data(context, tmp_mkey,
client_key, &key, NULL)))
goto cleanup;
krb5_sam_challenge sc;
krb5_predicted_sam_response psr;
krb5_data * scratch;
- krb5_keyblock encrypting_key;
+ krb5_keyblock encrypting_key, *tmp_mkey;
char response[9];
char inputblock[8];
krb5_data predict_response;
if (sc.sam_type) {
/* so use assoc to get the key out! */
{
+ if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, &assoc, &tmp_mkey)))
+ return (retval);
+
/* here's what do_tgs_req does */
retval = krb5_dbe_find_enctype(kdc_context, &assoc,
ENCTYPE_DES_CBC_RAW,
}
/* convert server.key into a real key */
retval = krb5_dbekd_decrypt_key_data(kdc_context,
- &master_keyblock,
+ tmp_mkey,
assoc_key, &encrypting_key,
NULL);
if (retval) {
unsigned cert_hash_len;
unsigned key_dex;
unsigned cert_match = 0;
- krb5_keyblock decrypted_key;
+ krb5_keyblock decrypted_key, *tmp_mkey;
/* the data we get from the AS-REQ */
krb5_timestamp client_ctime = 0;
goto cleanup;
}
cert_hash_len = strlen(cert_hash);
+ if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry, &tmp_mkey)))
+ goto cleanup;
for(key_dex=0; key_dex<client->n_key_data; key_dex++) {
krb5_key_data *key_data = &client->key_data[key_dex];
kdcPkinitDebug("--- key %u type[0] %u length[0] %u type[1] %u length[1] %u\n",
* Unfortunately this key is stored encrypted even though it's
* not sensitive...
*/
- krtn = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+ krtn = krb5_dbekd_decrypt_key_data(context, tmp_mkey,
key_data, &decrypted_key, NULL);
if(krtn) {
kdcPkinitDebug("verify_pkinit_request: error decrypting cert hash block\n");
krb5_boolean more;
int nprincs;
krb5_key_data * server_key;
+ krb5_keyblock * tmp_mkey;
nprincs = 1;
}
return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
}
+
+ retval = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, &tmp_mkey);
+ if (retval)
+ goto errout;
+
retval = krb5_dbe_find_enctype(kdc_context, &server,
ticket->enc_part.enctype, -1,
ticket->enc_part.kvno, &server_key);
}
*kvno = server_key->key_data_kvno;
if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
- retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
+ retval = krb5_dbekd_decrypt_key_data(kdc_context, tmp_mkey,
server_key,
*key, NULL);
if (retval) {
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) {
}
/*
- * 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,
"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,
krb5_int16 n_tl_data;
krb5_tl_data *tl_data;
krb5_key_data *key_data;
+ int foo; /* XXX WAF: just to see if it breaks the build */
} kadm5_principal_ent_rec_v2, *kadm5_principal_ent_t_v2;
typedef struct _kadm5_principal_ent_t_v1 {
free(names);
return KADM5_OK;
}
-
+/* XXX WAF: maybe delete this if all compiles */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
/* XXX this ought to be in libkrb5.a, but isn't */
kadm5_ret_t krb5_free_key_data_contents(context, key)
krb5_context context;
}
return KADM5_OK;
}
+#endif /**************** END IFDEF'ed OUT *******************************/
kadm5_ret_t kadm5_free_key_data(void *server_handle,
krb5_int16 *n_key_data,
kadm5_ret_t krb5_copy_key_data_contents(krb5_context context,
krb5_key_data *from,
krb5_key_data *to);
+/* XXX WAF: maybe delete this if all compiles */
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
kadm5_ret_t krb5_free_key_data_contents(krb5_context context,
krb5_key_data *key);
+#endif /**************** END IFDEF'ed OUT *******************************/
/*
* *Warning*
krb5_string_to_keysalts
master_db
master_keyblock
+master_keylist
master_princ
osa_free_princ_ent
ovsec_kadm_chpass_principal
#include "server_internal.h"
krb5_principal master_princ;
-krb5_keyblock master_keyblock;
+krb5_keyblock master_keyblock; /* local mkey */
+krb5_keyblock_node *master_keylist = NULL;
+krb5_actkvno_node *active_mkey_list = NULL;
krb5_db_entry master_db;
krb5_principal hist_princ;
int ret = 0;
char *realm;
krb5_boolean from_kbd = FALSE;
+ krb5_keyblock *mkey;
+ krb5_kvno mkvno = IGNORE_VNO;
if (from_keyboard)
from_kbd = TRUE;
master_keyblock.enctype = handle->params.enctype;
+ /*
+ * Fetch the local mkey, may not be the latest but that's okay because we
+ * really want the list of all mkeys and those can be retrieved with any
+ * valid mkey.
+ */
ret = krb5_db_fetch_mkey(handle->context, master_princ,
master_keyblock.enctype, from_kbd,
FALSE /* only prompt once */,
handle->params.stash_file,
- NULL /* don't care about kvno */,
+ &mkvno /* get the kvno of the returned mkey */,
NULL /* I'm not sure about this,
but it's what the kdc does --marc */,
&master_keyblock);
if (ret)
goto done;
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+ /*
+ * XXX WAF: since the local mkey may not be latest, hold off on verifying it
+ * since krb5_db_fetch_mkey_list will do this work.
+ */
if ((ret = krb5_db_verify_master_key(handle->context, master_princ,
IGNORE_VNO, &master_keyblock))) {
krb5_db_fini(handle->context);
return ret;
}
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+ if ((ret = krb5_db_fetch_mkey_list(handle->context, master_princ,
+ mkey, mkvno, &master_keylist))) {
+ krb5_db_fini(handle->context);
+ return (ret);
+ }
+
+ if ((ret = krb5_dbe_fetch_act_mkey_list(handle->context, master_princ,
+ &active_mkey_list))) {
+ krb5_db_fini(handle->context);
+ return (ret);
+ }
done:
if (r == NULL)
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)))
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;
extern krb5_principal master_princ;
extern krb5_principal hist_princ;
-extern krb5_keyblock master_keyblock;
+/* extern krb5_keyblock master_keyblock; */
+extern krb5_keyblock_node *master_keylist;
+extern krb5_actkvno_node *active_mkey_list;
extern krb5_keyblock hist_key;
extern krb5_db_entry master_db;
extern krb5_db_entry hist_db;
extern krb5_kvno hist_kvno;
-static int decrypt_key_data(krb5_context context,
+static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
int n_key_data, krb5_key_data *key_data,
krb5_keyblock **keyblocks, int *n_keys);
krb5_tl_data *tl_data_orig, *tl_data_tail;
unsigned int ret;
kadm5_server_handle_t handle = server_handle;
+ krb5_keyblock *act_mkey;
CHECK_HANDLE(server_handle);
/* initialize the keys */
- if ((ret = krb5_dbe_cpw(handle->context, &master_keyblock,
+ ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+ active_mkey_list, &act_mkey);
+ if (ret)
+ return (ret);
+
+ if ((ret = krb5_dbe_cpw(handle->context, act_mkey,
n_ks_tuple?ks_tuple:handle->params.keysalts,
n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
password,
return(ret);
}
+ /* XXX WAF: this needs to be changed to use real mkvno */
+ /* Record the master key VNO used to encrypt this entry's keys */
+ ret = krb5_dbe_update_mkvno(handle->context, &kdb, 1);
+ if (ret)
+ {
+ krb5_db_free_principal(handle->context, &kdb, 1);
+ if (mask & KADM5_POLICY)
+ (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+ return ret;
+ }
+
/* populate the admin-server-specific fields. In the OV server,
this used to be in a separate database. Since there's already
marshalling code for the admin fields, to keep things simple,
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
*/
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)
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)
* 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;
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)
int have_pol = 0;
kadm5_server_handle_t handle = server_handle;
osa_pw_hist_ent hist;
+ krb5_keyblock *act_mkey;
CHECK_HANDLE(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_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 */,
#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)
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)
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;
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
- ret = krb5_dbe_crk(handle->context, &master_keyblock,
+ ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+ active_mkey_list, &act_mkey);
+ if (ret)
+ goto done;
+
+ ret = krb5_dbe_crk(handle->context, act_mkey,
n_ks_tuple?ks_tuple:handle->params.keysalts,
n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
keepold,
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)
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)
#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));
keysalt.data.length = 0;
keysalt.data.data = NULL;
+ ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+ active_mkey_list, &act_mkey);
+ if (ret)
+ goto done;
+
/* use tmp_key_data as temporary location and reallocate later */
- ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
+ ret = krb5_dbekd_encrypt_key_data(handle->context, act_mkey,
keyblock, &keysalt, kvno + 1,
&tmp_key_data);
if (ret) {
krb5_keysalt keysalt;
krb5_key_data tmp_key_data;
krb5_key_data *tptr;
+ krb5_keyblock *act_mkey;
CHECK_HANDLE(server_handle);
}
memset (&tmp_key_data, 0, sizeof(tmp_key_data));
+ ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
+ active_mkey_list, &act_mkey);
+ if (ret)
+ goto done;
+
ret = krb5_dbekd_encrypt_key_data(handle->context,
- &master_keyblock,
+ act_mkey,
&keyblocks[i],
n_ks_tuple ? &keysalt : NULL,
kvno + 1,
&tmp_key_data);
- if (ret) {
+ if (ret)
goto done;
- }
+
tptr = &kdb.key_data[i];
tptr->key_data_ver = tmp_key_data.key_data_ver;
tptr->key_data_kvno = tmp_key_data.key_data_kvno;
krb5_key_data *key_data;
kadm5_ret_t ret;
kadm5_server_handle_t handle = server_handle;
+ krb5_keyblock *tmp_mkey;
if (keyblocks)
*keyblocks = NULL;
return(ret);
if (keyblocks) {
+ ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb, &tmp_mkey);
+ if (ret)
+ goto done;
if (handle->api_version == KADM5_API_VERSION_1) {
/* Version 1 clients will expect to see a DES_CRC enctype. */
if ((ret = krb5_dbe_find_enctype(handle->context, &kdb,
-1, -1, &key_data)))
goto done;
- if ((ret = decrypt_key_data(handle->context, 1, key_data,
+ if ((ret = decrypt_key_data(handle->context, tmp_mkey, 1, key_data,
keyblocks, NULL)))
goto done;
} else {
- ret = decrypt_key_data(handle->context,
+ ret = decrypt_key_data(handle->context, tmp_mkey,
kdb.n_key_data, kdb.key_data,
keyblocks, n_keys);
if (ret)
/*
* 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)
{
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) {
kadm5_server_handle_t handle = server_handle;
krb5_db_entry dbent;
krb5_key_data *key_data;
+ krb5_keyblock *tmp_mkey;
int ret;
CHECK_HANDLE(server_handle);
stype, kvno, &key_data)))
return ret;
+ /* find_mkey only uses this field */
+ dbent.tl_data = entry->tl_data;
+ ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent, &tmp_mkey);
+ if (ret)
+ return (ret);
+
if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
- &master_keyblock, key_data,
+ tmp_mkey, key_data,
keyblock, keysalt)))
return ret;
return k5_mutex_unlock(&db_lock);
}
+static void
+krb5_free_actkvno_list(krb5_context context, krb5_actkvno_node *val)
+{
+ krb5_actkvno_node *temp, *prev;
+
+ for (temp = val; temp != NULL;) {
+ prev = temp;
+ temp = temp->next;
+ krb5_xfree(prev);
+ }
+}
+
+static void
+krb5_free_mkey_aux_list(krb5_context context, krb5_mkey_aux_node *val)
+{
+ krb5_mkey_aux_node *temp, *prev;
+
+ for (temp = val; temp != NULL;) {
+ prev = temp;
+ temp = temp->next;
+ krb5_free_key_data_contents(context, &prev->latest_mkey);
+ krb5_xfree(prev);
+ }
+}
+
#define kdb_init_lib_lock(a) 0
#define kdb_destroy_lib_lock(a) (void)0
#define kdb_lock_lib_lock(a, b) 0
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;
}
lib->vftabl.verify_master_key = krb5_def_verify_master_key;
}
+ if (lib->vftabl.fetch_master_key_list == NULL) {
+ lib->vftabl.fetch_master_key_list = krb5_def_fetch_mkey_list;
+ }
+
if (lib->vftabl.dbe_search_enctype == NULL) {
lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype;
}
upd->kdb_princ_name.utf8str_t_val = princ_name;
upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
- if (status = ulog_add_update(kcontext, upd))
+ if ((status = ulog_add_update(kcontext, upd)))
goto err_lock;
upd++;
}
return krb5_db_set_master_key_ext(context, NULL, key);
}
+krb5_error_code
+krb5_db_set_mkey_list(krb5_context context, krb5_keyblock_node * keylist)
+{
+ return krb5_db_set_master_key_ext(context, NULL, keylist);
+}
+
krb5_error_code
krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key)
{
return status;
}
+krb5_error_code
+krb5_db_get_mkey_list(krb5_context kcontext, krb5_keyblock_node ** keylist)
+{
+ krb5_error_code status = 0;
+ kdb5_dal_handle *dal_handle;
+
+ if (kcontext->dal_handle == NULL) {
+ status = kdb_setup_lib_handle(kcontext);
+ if (status) {
+ goto clean_n_exit;
+ }
+ }
+
+ dal_handle = kcontext->dal_handle;
+ status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
+ if (status) {
+ goto clean_n_exit;
+ }
+
+ /* Let's use temp key and copy it later to avoid memory problems
+ when freed by the caller. */
+ status = dal_handle->lib_handle->vftabl.get_master_key_list(kcontext, keylist);
+ get_errmsg(kcontext, status);
+ kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+
+ clean_n_exit:
+ return status;
+}
+
+krb5_error_code
+krb5_db_fetch_mkey_list(krb5_context context,
+ krb5_principal mname,
+ const krb5_keyblock * mkey,
+ krb5_kvno mkvno,
+ krb5_keyblock_node **mkey_list)
+{
+ kdb5_dal_handle *dal_handle;
+ krb5_error_code status = 0;
+
+ if (context->dal_handle == NULL) {
+ status = kdb_setup_lib_handle(context);
+ if (status) {
+ goto clean_n_exit;
+ }
+ }
+
+ dal_handle = context->dal_handle;
+ status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
+ if (status) {
+ goto clean_n_exit;
+ }
+
+ status = dal_handle->lib_handle->vftabl.fetch_master_key_list(context,
+ mname,
+ mkey,
+ mkvno,
+ mkey_list);
+ get_errmsg(context, status);
+ kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+
+ if (status) {
+ goto clean_n_exit;
+ }
+
+clean_n_exit:
+ return status;
+}
+
+krb5_error_code
+krb5_db_free_mkey_list(krb5_context context,
+ krb5_keyblock_node *mkey_list)
+{
+ krb5_keyblock_node *cur, *prev;
+
+ for (cur = mkey_list; cur != NULL;) {
+ prev = cur;
+ cur = cur->next;
+ krb5_free_keyblock_contents(context, &prev->keyblock);
+ krb5_xfree(prev);
+ }
+
+ return 0;
+}
+
krb5_error_code
krb5_db_store_master_key(krb5_context kcontext,
char *keyfile,
return status;
}
+#if 0 /************** Begin IFDEF'ed OUT *******************************/
+/* XXX WAF: don't think this is needed now that I've modified
+ * krb5_def_fetch_mkey_list. Keeping it around just in case. */
+/*
+ * get most current master key which may be stored with the master key princ.
+ */
+
+krb5_error_code
+krb5_db_fetch_latest_mkey(krb5_context context,
+ krb5_principal mname,
+ krb5_enctype etype,
+ krb5_boolean fromkeyboard,
+ krb5_boolean twice,
+ char * db_args,
+ krb5_kvno * kvno,
+ krb5_data * salt,
+ krb5_keyblock * key)
+{
+ krb5_keyblock tmp_mkey, tmp_clearkey;
+ krb5_kvno tmp_kvno;
+ krb5_db_entry master_entry;
+ int nprinc;
+ krb5_boolean more, found_key = FALSE;
+ krb5_mkey_aux_node *mkey_aux_data_list, *aux_data_entry;
+ krb5_error_code retval = 0;
+
+ memset(&tmp_mkey, 0, sizeof(tmp_mkey));
+ memset(&tmp_clearkey, 0, sizeof(tmp_clearkey));
+
+ /* fetch the local mkey either from stash or via keyboard interactive */
+ if ((retval = krb5_db_fetch_mkey(context, mname, etype, fromkeyboard,
+ twice, db_args, &tmp_kvno, NULL, &tmp_mkey))) {
+ return (retval);
+ }
+
+ nprinc = 1;
+ retval = krb5_db_get_principal(context, mname, &master_entry, &nprinc, &more);
+ if (retval != 0)
+ goto clean_n_exit;
+
+ if ((retval = krb5_dbekd_decrypt_key_data(context, &tmp_mkey,
+ &master_entry.key_data[0],
+ &tmp_clearkey, NULL)) != 0) {
+ /*
+ * Note the tmp_kvno may provide a hint as to which mkey_aux tuple to
+ * decrypt.
+ */
+ if ((retval = krb5_dbe_lookup_mkey_aux(context, &master_entry, &mkey_aux_data_list)))
+ goto clean_n_exit;
+
+ /* for performance sake, try decrypting with matching kvno */
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+ aux_data_entry = aux_data_entry->next) {
+
+ if (aux_data_entry->mkey_kvno == tmp_kvno) {
+ if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+ &tmp_clearkey, NULL) == 0) {
+ found_key = TRUE;
+ break;
+ }
+ }
+ }
+ if (found_key != TRUE) {
+ /* given the importance of acquiring the latest mkey, try brute force */
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+ aux_data_entry = aux_data_entry->next) {
+
+ if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+ &tmp_clearkey, NULL) == 0) {
+ found_key = TRUE;
+ /* XXX WAF: should I issue warning about kvno not matching?
+ */
+ break;
+ }
+ }
+ if (found_key != TRUE) {
+ krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+ "Unable to decrypt latest master key with the provided master key\n");
+ retval = KRB5_KDB_BADMASTERKEY;
+ goto clean_n_exit;
+ }
+ }
+
+ if ((retval = krb5_db_verify_master_key(context,
+ mname,
+ tmp_kvno,
+ &tmp_clearkey))) {
+ krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+ "Failed to verify Latest master key decrypted with the provided master key\n");
+ retval = KRB5_KDB_BADMASTERKEY;
+ goto clean_n_exit;
+ }
+ }
+
+ key->contents = malloc(tmp_clearkey.length);
+ if (key->contents == NULL) {
+ retval = ENOMEM;
+ goto clean_n_exit;
+ }
+
+ key->magic = tmp_clearkey.magic;
+ key->enctype = tmp_clearkey.enctype;
+ key->length = tmp_clearkey.length;
+ memcpy(key->contents, tmp_clearkey.contents, tmp_clearkey.length);
+
+clean_n_exit:
+ if (tmp_mkey.contents) {
+ memset(tmp_mkey.contents, 0, tmp_mkey.length);
+ krb5_db_free(context, tmp_mkey.contents);
+ }
+ if (tmp_clearkey.contents) {
+ memset(tmp_clearkey.contents, 0, tmp_clearkey.length);
+ krb5_db_free(context, tmp_clearkey.contents);
+ }
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return (retval);
+}
+#endif /**************** END IFDEF'ed OUT *******************************/
+
+krb5_error_code
+krb5_dbe_fetch_act_mkey_list(krb5_context context,
+ krb5_principal mprinc,
+ krb5_actkvno_node **act_mkey_list)
+{
+ krb5_error_code retval = 0;
+ krb5_db_entry master_entry;
+ int nprinc;
+ krb5_boolean more;
+
+ if (act_mkey_list == NULL)
+ return (EINVAL);
+
+ nprinc = 1;
+ if ((retval = krb5_db_get_principal(context, mprinc,
+ &master_entry, &nprinc, &more)))
+ return (retval);
+
+ if (nprinc != 1) {
+ if (nprinc)
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return(KRB5_KDB_NOMASTERKEY);
+ } else if (more) {
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+ }
+
+ retval = krb5_dbe_lookup_actkvno(context, &master_entry, act_mkey_list);
+
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return retval;
+}
+
+/*
+ * Locates the "active" mkey used when encrypting a princ's keys. Note, the
+ * caller must not free the output act_mkey.
+ */
+
+krb5_error_code
+krb5_dbe_find_act_mkey(krb5_context context,
+ krb5_keyblock_node *mkey_list,
+ krb5_actkvno_node *act_mkey_list,
+ krb5_keyblock **act_mkey)
+{
+ krb5_kvno act_kvno;
+ krb5_error_code retval;
+ krb5_keyblock_node *cur_keyblock = mkey_list;
+ krb5_actkvno_node *prev_actkvno, *cur_actkvno;
+ krb5_timestamp now;
+ krb5_boolean found = FALSE;
+
+ if ((retval = krb5_timeofday(context, &now)))
+ return (retval);
+
+ /*
+ * The list should be sorted in time, early to later so if the first entry
+ * is later than now, this is a problem
+ */
+ if (act_mkey_list->act_time > now) {
+ return (KRB5_KDB_NOACTMASTERKEY);
+ }
+
+ /* find the most current entry <= now */
+ for (prev_actkvno = cur_actkvno = act_mkey_list; cur_actkvno != NULL;
+ prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+ if (cur_actkvno->act_time == now) {
+ act_kvno = cur_actkvno->act_kvno;
+ found = TRUE;
+ break;
+ } else if (cur_actkvno->act_time > now && prev_actkvno->act_time <= now) {
+ act_kvno = prev_actkvno->act_kvno;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * The end of the list was encountered and all entries are < now so use
+ * the latest entry.
+ */
+ if (prev_actkvno->act_time <= now) {
+ act_kvno = prev_actkvno->act_kvno;
+ } else {
+ /* XXX this shouldn't happen */
+ return (KRB5_KDB_NOACTMASTERKEY);
+ }
+ }
+
+ while (cur_keyblock && cur_keyblock->kvno != act_kvno)
+ cur_keyblock = cur_keyblock->next;
+
+ if (cur_keyblock) {
+ *act_mkey = &cur_keyblock->keyblock;
+ return (0);
+ } else {
+ return (KRB5_KDB_NO_MATCHING_KEY);
+ }
+}
+
+/*
+ * Locates the mkey used to protect a princ's keys. Note, the caller must not
+ * free the output key.
+ */
+krb5_error_code
+krb5_dbe_find_mkey(krb5_context context,
+ krb5_keyblock_node *mkey_list,
+ krb5_db_entry *entry,
+ krb5_keyblock **mkey)
+{
+ krb5_kvno mkvno;
+ krb5_error_code retval;
+ krb5_keyblock_node *cur_keyblock = mkey_list;
+
+ retval = krb5_dbe_lookup_mkvno(context, entry, &mkvno);
+ if (retval)
+ return (retval);
+
+ while (cur_keyblock && cur_keyblock->kvno != mkvno)
+ cur_keyblock = cur_keyblock->next;
+
+ if (cur_keyblock) {
+ *mkey = &cur_keyblock->keyblock;
+ return (0);
+ } else {
+ return (KRB5_KDB_NO_MATCHING_KEY);
+ }
+}
+
void *
krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size)
{
return (0);
}
+krb5_error_code
+krb5_dbe_lookup_mkvno(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_kvno *mkvno)
+{
+ krb5_tl_data tl_data;
+ krb5_error_code code;
+ krb5_int16 tmp;
+
+ tl_data.tl_data_type = KRB5_TL_MKVNO;
+
+ if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+ return (code);
+
+ if (tl_data.tl_data_length == 0) {
+ *mkvno = 1; /* default for princs that lack the KRB5_TL_MKVNO data */
+ return (0);
+ }
+
+ krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp);
+ *mkvno = (krb5_kvno) tmp;
+ return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkvno(krb5_context context,
+ krb5_db_entry * entry,
+ krb5_kvno mkvno)
+{
+ krb5_tl_data tl_data;
+ krb5_octet buf[2]; /* this is the encoded size of an int16 */
+
+ tl_data.tl_data_type = KRB5_TL_MKVNO;
+ tl_data.tl_data_length = sizeof(buf);
+ /* use standard encoding */
+ krb5_kdb_encode_int16((krb5_ui_2) mkvno, buf);
+ tl_data.tl_data_contents = buf;
+
+ return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
+krb5_error_code
+krb5_dbe_lookup_mkey_aux(krb5_context context,
+ krb5_db_entry * entry,
+ krb5_mkey_aux_node ** mkey_aux_data_list)
+{
+ krb5_tl_data tl_data;
+ krb5_int16 version;
+ krb5_mkey_aux_node *head_data = NULL, *new_data = NULL,
+ *prev_data = NULL;
+ krb5_octet *curloc; /* current location pointer */
+ krb5_error_code code;
+
+ tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
+ if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+ return (code);
+
+ if (tl_data.tl_data_contents == NULL) {
+ *mkey_aux_data_list = NULL;
+ return (0);
+ } else {
+ /* get version to determine how to parse the data */
+ krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
+ if (version == KRB5_TL_MKEY_AUX_VER_1) {
+
+ /* curloc points to first tuple entry in the tl_data_contents */
+ curloc = tl_data.tl_data_contents + sizeof(version);
+
+ while (curloc != (tl_data.tl_data_contents + tl_data.tl_data_length)) {
+ assert(curloc < tl_data.tl_data_contents + tl_data.tl_data_length);
+
+ new_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+ if (new_data == NULL) {
+ krb5_free_mkey_aux_list(context, head_data);
+ return (ENOMEM);
+ }
+ krb5_kdb_decode_int16(curloc, new_data->mkey_kvno);
+ curloc += sizeof(krb5_ui_2);
+ krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_kvno);
+ curloc += sizeof(krb5_ui_2);
+ krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_type[0]);
+ curloc += sizeof(krb5_ui_2);
+ krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_length[0]);
+ curloc += sizeof(krb5_ui_2);
+
+ new_data->latest_mkey.key_data_contents[0] = (krb5_octet *)
+ malloc(new_data->latest_mkey.key_data_length[0]);
+
+ if (new_data->latest_mkey.key_data_contents[0] == NULL) {
+ krb5_free_mkey_aux_list(context, head_data);
+ return (ENOMEM);
+ }
+ memcpy(new_data->latest_mkey.key_data_contents[0], curloc, new_data->latest_mkey.key_data_length[0]);
+
+ new_data->next = NULL;
+ if (prev_data != NULL)
+ prev_data->next = new_data;
+ else
+ head_data = new_data;
+ prev_data = new_data;
+ }
+ } else {
+ krb5_set_error_message (context, KRB5_KDB_BAD_VERSION,
+ "Illegal version number for KRB5_TL_MKEY_AUX %d\n",
+ version);
+ return (KRB5_KDB_BAD_VERSION);
+ }
+ }
+ *mkey_aux_data_list = head_data;
+ return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkey_aux(krb5_context context,
+ krb5_db_entry * entry,
+ krb5_mkey_aux_node * mkey_aux_data_list)
+{
+ krb5_tl_data tl_data;
+ krb5_int16 version;
+ krb5_octet *nextloc;
+ krb5_mkey_aux_node *aux_data_entry;
+
+ tl_data.tl_data_type = KRB5_TL_MKEY_AUX;
+ /*
+ * determine out how much space to allocate
+ */
+ tl_data.tl_data_length = sizeof(version); /* version */
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; aux_data_entry = aux_data_entry->next) {
+ tl_data.tl_data_length += sizeof(krb5_ui_2); /* mkey_kvno */
+ tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey kvno */
+ tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey enctype */
+ tl_data.tl_data_length += sizeof(krb5_ui_2); /* latest_mkey length */
+ tl_data.tl_data_length += aux_data_entry->latest_mkey.key_data_length[0]; /* mkey data */
+ }
+
+ tl_data.tl_data_contents = (krb5_octet *) malloc(tl_data.tl_data_length);
+ if (tl_data.tl_data_contents == NULL) {
+ return (ENOMEM);
+ }
+
+ nextloc = tl_data.tl_data_contents;
+ /* version */
+ krb5_kdb_encode_int16((krb5_ui_2)KRB5_TL_MKEY_AUX_VER_1, (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; aux_data_entry = aux_data_entry->next) {
+ krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->mkey_kvno, (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+ krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_kvno, (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+ krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_type[0], (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+ krb5_kdb_encode_int16((krb5_ui_2)aux_data_entry->latest_mkey.key_data_length[0], (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+
+ if (aux_data_entry->latest_mkey.key_data_length[0] > 0) {
+ memcpy(nextloc, aux_data_entry->latest_mkey.key_data_contents[0],
+ aux_data_entry->latest_mkey.key_data_length[0]);
+ nextloc += aux_data_entry->latest_mkey.key_data_length[0];
+ }
+ }
+
+ return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
+/* XXX WAF: should probably #ifdef this to be defined if version 1 is in use */
+/*
+ * If version of the KRB5_TL_ACTKVNO data is KRB5_TL_ACTKVNO_VER_1 then size of
+ * a actkvno tuple {act_kvno, act_time} entry is:
+ */
+#define ACTKVNO_TUPLE_SIZE sizeof(krb5_int16) + sizeof(krb5_int32)
+#define act_kvno(cp) (cp) /* return pointer to start of act_kvno data */
+#define act_time(cp) (cp) + sizeof(krb5_int16) /* return pointer to start of act_time data */
+
+krb5_error_code
+krb5_dbe_lookup_actkvno(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_actkvno_node **actkvno_list)
+{
+ krb5_tl_data tl_data;
+ krb5_error_code code;
+ krb5_int16 version;
+ krb5_actkvno_node *head_data = NULL, *new_data = NULL, *prev_data = NULL;
+ unsigned int num_actkvno, i;
+ krb5_octet *next_tuple;
+
+ tl_data.tl_data_type = KRB5_TL_ACTKVNO;
+
+ if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+ return (code);
+
+ if (tl_data.tl_data_contents == NULL) {
+ *actkvno_list = NULL;
+ return (0);
+ } else {
+ /* get version to determine how to parse the data */
+ krb5_kdb_decode_int16(tl_data.tl_data_contents, version);
+ if (version == KRB5_TL_ACTKVNO_VER_1) {
+ /*
+ * Find number of tuple entries, remembering to account for version
+ * field.
+ */
+ num_actkvno = (tl_data.tl_data_length - sizeof(version)) / ACTKVNO_TUPLE_SIZE;
+ prev_data = NULL;
+ /* next_tuple points to first tuple entry in the tl_data_contents */
+ next_tuple = tl_data.tl_data_contents + sizeof(version);
+ for (i = 0; i < num_actkvno; i++) {
+ new_data = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+ if (new_data == NULL) {
+ krb5_free_actkvno_list(context, head_data);
+ return (ENOMEM);
+ }
+ krb5_kdb_decode_int16(act_kvno(next_tuple), new_data->act_kvno);
+ krb5_kdb_decode_int32(act_time(next_tuple), new_data->act_time);
+ /* XXX WAF: may be able to deal with list pointers in a better
+ * way, see add_mkey() */
+ new_data->next = NULL;
+ if (prev_data != NULL)
+ prev_data->next = new_data;
+ else
+ head_data = new_data;
+ prev_data = new_data;
+ next_tuple += ACTKVNO_TUPLE_SIZE;
+ }
+ } else {
+ krb5_set_error_message (context, KRB5_KDB_BAD_VERSION,
+ "Illegal version number for KRB5_TL_ACTKVNO %d\n",
+ version);
+ return (KRB5_KDB_BAD_VERSION);
+ }
+ }
+ *actkvno_list = head_data;
+ return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_actkvno(krb5_context context,
+ krb5_db_entry *entry,
+ const krb5_actkvno_node *actkvno_list)
+{
+ krb5_error_code retval = 0;
+ krb5_int16 version;
+ krb5_tl_data new_tl_data;
+ krb5_octet *nextloc;
+ const krb5_actkvno_node *cur_actkvno;
+
+ /* XXX WAF: should kvno be verified that it exists for the princ entry? */
+ /* No, this should be handed by functions higher in the stack verifying the user data */
+
+ if (actkvno_list == NULL) {
+ return (EINVAL);
+ }
+
+ /* allocate initial KRB5_TL_ACTKVNO tl_data entry */
+ new_tl_data.tl_data_length = sizeof(version);
+ new_tl_data.tl_data_contents = (krb5_octet *) malloc(new_tl_data.tl_data_length);
+ if (new_tl_data.tl_data_contents == NULL) {
+ return (ENOMEM);
+ }
+ krb5_kdb_encode_int16((krb5_ui_2)KRB5_TL_ACTKVNO_VER_1, (unsigned char *)new_tl_data.tl_data_contents);
+
+ for (cur_actkvno = actkvno_list; cur_actkvno != NULL; cur_actkvno = cur_actkvno->next) {
+ new_tl_data.tl_data_length += ACTKVNO_TUPLE_SIZE;
+ new_tl_data.tl_data_contents = (krb5_octet *) realloc(new_tl_data.tl_data_contents, new_tl_data.tl_data_length);
+ if (new_tl_data.tl_data_contents == NULL) {
+ return (ENOMEM);
+ }
+
+ /*
+ * using realloc so tl_data_contents is required to correctly calculate
+ * next location to store new tuple.
+ */
+ nextloc = new_tl_data.tl_data_contents + new_tl_data.tl_data_length - ACTKVNO_TUPLE_SIZE;
+ krb5_kdb_encode_int16((krb5_ui_2)cur_actkvno->act_kvno, (unsigned char *)nextloc);
+ nextloc += sizeof(krb5_ui_2);
+ krb5_kdb_encode_int32((krb5_ui_4)cur_actkvno->act_time, (unsigned char *)nextloc);
+ }
+
+ new_tl_data.tl_data_type = KRB5_TL_ACTKVNO;
+ retval = krb5_dbe_update_tl_data(context, entry, &new_tl_data);
+ free(new_tl_data.tl_data_contents);
+
+ return (retval);
+}
+
krb5_error_code
krb5_dbe_update_last_pwd_change(context, entry, stamp)
krb5_context context;
krb5_error_code (*get_master_key) (krb5_context kcontext,
krb5_keyblock **key);
+ krb5_error_code (*set_master_key_list) (krb5_context kcontext,
+ krb5_keyblock_node *keylist);
+
+ krb5_error_code (*get_master_key_list) (krb5_context kcontext,
+ krb5_keyblock_node **keylist);
+
krb5_error_code (*setup_master_key_name) (krb5_context kcontext,
char *keyname,
krb5_kvno kvno,
krb5_keyblock *mkey);
+ krb5_error_code (*fetch_master_key_list) (krb5_context kcontext,
+ krb5_principal mname,
+ const krb5_keyblock *key,
+ krb5_kvno kvno,
+ krb5_keyblock_node **mkeys_list);
+
+
krb5_error_code (*dbe_search_enctype) (krb5_context kcontext,
krb5_db_entry *dbentp,
krb5_int32 *start,
#include <stdio.h>
#include <errno.h>
-static int
+int
get_key_data_kvno(context, count, data)
krb5_context context;
int count;
}
#endif /* LEAN_CLIENT */
+/* XXX WAF: I'm now thinking this fucntion should check to see if the fetched
+ * key matches the latest mkey in the master princ. If it doesn't then the
+ * latest mkey should be returned by using the mkey_aux tl data.
+ */
krb5_error_code
krb5_db_def_fetch_mkey(krb5_context context,
krb5_principal mname,
}
}
+/*
+ * 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,
return retval;
}
+krb5_error_code
+krb5_def_fetch_mkey_list(krb5_context context,
+ krb5_principal mprinc,
+ const krb5_keyblock *mkey,
+ krb5_kvno mkvno,
+ krb5_keyblock_node **mkeys_list)
+{
+ krb5_error_code retval;
+ krb5_db_entry master_entry;
+ int nprinc;
+ krb5_boolean more, found_key = FALSE;
+ krb5_keyblock tmp_mkey, tmp_clearkey;
+ krb5_keyblock_node *mkey_list_head, **mkey_list_node;
+ krb5_key_data *key_data;
+ krb5_mkey_aux_node *mkey_aux_data_list, *aux_data_entry;
+ int i;
+
+ if (mkeys_list == NULL)
+ return (EINVAL);
+
+ memset(&tmp_mkey, 0, sizeof(tmp_mkey));
+ memset(&tmp_clearkey, 0, sizeof(tmp_clearkey));
+
+ nprinc = 1;
+ if ((retval = krb5_db_get_principal(context, mprinc,
+ &master_entry, &nprinc, &more)))
+ return (retval);
+
+ if (nprinc != 1) {
+ if (nprinc)
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return(KRB5_KDB_NOMASTERKEY);
+ } else if (more) {
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+ }
+
+ /*
+ * Check if the input mkey is the latest key and if it isn't then find the
+ * latest mkey.
+ */
+ if ((retval = krb5_dbekd_decrypt_key_data(context, &tmp_mkey,
+ &master_entry.key_data[0],
+ &tmp_clearkey, NULL)) != 0) {
+ /*
+ * Note the mkvno may provide a hint as to which mkey_aux tuple to
+ * decrypt.
+ */
+ if ((retval = krb5_dbe_lookup_mkey_aux(context, &master_entry, &mkey_aux_data_list)))
+ goto clean_n_exit;
+
+ /* for performance sake, try decrypting with matching kvno */
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+ aux_data_entry = aux_data_entry->next) {
+
+ if (aux_data_entry->mkey_kvno == mkvno) {
+ if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+ &tmp_clearkey, NULL) == 0) {
+ found_key = TRUE;
+ break;
+ }
+ }
+ }
+ if (found_key != TRUE) {
+ /* given the importance of acquiring the latest mkey, try brute force */
+ for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
+ aux_data_entry = aux_data_entry->next) {
+
+ if (krb5_dbekd_decrypt_key_data(context, &tmp_mkey, &aux_data_entry->latest_mkey,
+ &tmp_clearkey, NULL) == 0) {
+ found_key = TRUE;
+ /* XXX WAF: should I issue warning about kvno not matching?
+ */
+ break;
+ }
+ }
+ if (found_key != TRUE) {
+ krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY,
+ "Unable to decrypt latest master key with the provided master key\n");
+ retval = KRB5_KDB_BADMASTERKEY;
+ goto clean_n_exit;
+ }
+ }
+ }
+
+ /*
+ * Extract all the mkeys from master_entry using the most current mkey and
+ * create a mkey list for the mkeys field in kdc_realm_t.
+ */
+
+ mkey_list_head = (krb5_keyblock_node *) malloc(sizeof(krb5_keyblock_node));
+ if (mkey_list_head == NULL) {
+ retval = ENOMEM;
+ goto clean_n_exit;
+ }
+
+ memset(mkey_list_head, 0, sizeof(krb5_keyblock_node));
+ mkey_list_node = &mkey_list_head;
+
+ for (i=0; i < master_entry.n_key_data; i++) {
+ if (*mkey_list_node == NULL) {
+ /* *mkey_list_node points to next field of previous node */
+ *mkey_list_node = (krb5_keyblock_node *) malloc(sizeof(krb5_keyblock_node));
+ if (*mkey_list_node == NULL) {
+ retval = ENOMEM;
+ goto clean_n_exit;
+ }
+ memset(*mkey_list_node, 0, sizeof(krb5_keyblock_node));
+ }
+ key_data = &master_entry.key_data[i];
+ retval = krb5_dbekd_decrypt_key_data(context, mkey,
+ key_data, &((*mkey_list_node)->keyblock),
+ NULL);
+ if (retval)
+ goto clean_n_exit;
+
+ mkey_list_node = &((*mkey_list_node)->next);
+ }
+
+ *mkeys_list = mkey_list_head;
+
+clean_n_exit:
+
+ if (tmp_mkey.contents) {
+ memset(tmp_mkey.contents, 0, tmp_mkey.length);
+ krb5_db_free(context, tmp_mkey.contents);
+ }
+
+ if (tmp_clearkey.contents) {
+ memset(tmp_clearkey.contents, 0, tmp_clearkey.length);
+ krb5_db_free(context, tmp_clearkey.contents);
+ }
+
+ krb5_db_free_principal(context, &master_entry, nprinc);
+ if (retval != 0) {
+ krb5_keyblock_node *cur_node, *next_node;
+
+ for (cur_node = mkey_list_head; cur_node != NULL; cur_node = next_node) {
+ next_node = cur_node->next;
+ krb5_free_keyblock(context, &(cur_node->keyblock));
+ krb5_xfree(cur_node);
+ }
+ }
+
+ return retval;
+}
krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
char *pwd,
return 0;
}
+krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext,
+ krb5_keyblock_node *keylist )
+{
+ /* printf("default set master key\n"); */
+ return 0;
+}
+
+krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext,
+ krb5_keyblock_node **keylist )
+{
+ /* printf("default get master key\n"); */
+ return 0;
+}
+
krb5_error_code krb5_def_promote_db (krb5_context kcontext,
char *s, char **args)
{
krb5_keytab_entry * entry;
{
krb5_context context;
+ krb5_keyblock_node * master_keylist;
krb5_keyblock * master_key;
krb5_error_code kerror = 0;
krb5_key_data * key_data;
}
/* 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;
+get_key_data_kvno
krb5_db_open
krb5_db_inited
krb5_db_alloc
krb5_db_create
krb5_db_delete_principal
krb5_db_destroy
+krb5_db_fetch_latest_mkey
krb5_db_fetch_mkey
+krb5_db_fetch_mkey_list
+krb5_db_free_mkey_list
krb5_db_fini
krb5_db_free_principal
krb5_db_get_age
krb5_dbe_cpw
krb5_dbe_create_key_data
krb5_dbe_crk
+krb5_dbe_fetch_act_mkey_list
krb5_dbe_find_enctype
+krb5_dbe_find_mkey
krb5_dbe_lookup_last_pwd_change
+krb5_dbe_lookup_actkvno
+krb5_dbe_lookup_mkvno
krb5_dbe_lookup_mod_princ_data
krb5_dbe_lookup_tl_data
krb5_dbe_search_enctype
+krb5_dbe_update_actkvno
krb5_dbe_update_last_pwd_change
+krb5_dbe_update_mkey_aux
+krb5_dbe_update_mkvno
krb5_dbe_update_mod_princ_data
krb5_dbe_update_tl_data
krb5_dbekd_decrypt_key_data
ec KRB5_KDB_INVALIDKEYSIZE, "Key size in database is invalid"
ec KRB5_KDB_CANTREAD_STORED, "Cannot find/read stored master key"
ec KRB5_KDB_BADSTORED_MKEY, "Stored master key is corrupted"
+ec KRB5_KDB_NOACTMASTERKEY, "Cannot find active master key"
ec KRB5_KDB_CANTLOCK_DB, "Insufficient access to lock database"
*/
#include "k5-int.h"
+#include <kdb.h>
void KRB5_CALLCONV
krb5_free_address(krb5_context context, krb5_address *val)
return;
krb5_xfree(pa_enc_ts);
}
+
+void KRB5_CALLCONV
+krb5_free_key_data_contents(krb5_context context,
+ krb5_key_data *key)
+{
+ int i, idx;
+
+ idx = (key->key_data_ver == 1 ? 1 : 2);
+ for (i = 0; i < idx; i++) {
+ if (key->key_data_contents[i]) {
+ memset(key->key_data_contents[i], 0, key->key_data_length[i]);
+ free(key->key_data_contents[i]);
+ }
+ }
+ return;
+}
+
WRAP_K (krb5_db2_db_get_mkey,
( krb5_context context, krb5_keyblock **key),
(context, key));
+
+WRAP_K (krb5_db2_set_master_key_list,
+ ( krb5_context kcontext, krb5_keyblock_node *keylist),
+ (kcontext, keylist));
+
+WRAP_K (krb5_db2_db_get_mkey_list,
+ ( krb5_context context, krb5_keyblock_node **keylist),
+ (context, keylist));
+
WRAP_K (krb5_db2_promote_db,
( krb5_context kcontext, char *conf_section, char **db_args ),
(kcontext, conf_section, db_args));
/* db_free */ wrap_krb5_db2_free,
/* set_master_key */ wrap_krb5_db2_set_master_key_ext,
/* get_master_key */ wrap_krb5_db2_db_get_mkey,
- /* blah blah blah */ 0,0,0,0,0,0,
+ /* set_master_key_list */ wrap_krb5_db2_set_master_key_list,
+ /* get_master_key_list */ wrap_krb5_db2_db_get_mkey_list,
+ /* blah blah blah */ 0,0,0,0,0,0,0,
/* promote_db */ wrap_krb5_db2_promote_db,
};
return 0;
}
+krb5_error_code
+krb5_db2_db_set_mkey_list(krb5_context context, krb5_keyblock_node *key_list)
+{
+ krb5_db2_context *db_ctx;
+ kdb5_dal_handle *dal_handle;
+
+ if (!k5db2_inited(context))
+ return (KRB5_KDB_DBNOTINITED);
+
+ dal_handle = context->dal_handle;
+ db_ctx = dal_handle->db_context;
+ db_ctx->db_master_key_list = key_list;
+ return 0;
+}
+
+krb5_error_code
+krb5_db2_db_get_mkey_list(krb5_context context, krb5_keyblock_node **key_list)
+{
+ krb5_db2_context *db_ctx;
+ kdb5_dal_handle *dal_handle;
+
+ if (!k5db2_inited(context))
+ return (KRB5_KDB_DBNOTINITED);
+
+ dal_handle = context->dal_handle;
+ db_ctx = dal_handle->db_context;
+ *key_list = db_ctx->db_master_key_list;
+
+ return 0;
+}
+
/*
* Set the "name" of the current database to some alternate value.
*
int db_locks_held; /* Number of times locked */
int db_lock_mode; /* Last lock mode, e.g. greatest*/
krb5_boolean db_nb_locks; /* [Non]Blocking lock modes */
- krb5_keyblock *db_master_key; /* Master key of database */
+ krb5_keyblock *db_master_key; /* Master key of database */
+ krb5_keyblock_node *db_master_key_list; /* Master key list of database */
osa_adb_policy_t policy_db;
krb5_boolean tempdb;
} krb5_db2_context;
krb5_error_code
krb5_db2_db_get_mkey( krb5_context context,
krb5_keyblock **key);
+krb5_error_code
+krb5_db2_db_set_mkey_list( krb5_context context,
+ krb5_keyblock_node *keylist);
+
+krb5_error_code
+krb5_db2_db_get_mkey_list( krb5_context context,
+ krb5_keyblock_node **keylist);
krb5_error_code
krb5_db2_db_put_principal( krb5_context context,
/* optional functions */
/* set_master_key */ krb5_ldap_set_mkey,
/* get_master_key */ krb5_ldap_get_mkey,
+ /* set_master_key_list */ NULL,
+ /* get_master_key_list */ NULL,
/* setup_master_key_name */ NULL,
/* store_master_key */ NULL,
/* fetch_master_key */ NULL /* krb5_ldap_fetch_mkey */,
/* verify_master_key */ NULL /* krb5_ldap_verify_master_key */,
+ /* fetch_master_key_list */ NULL,
/* Search enc type */ NULL,
/* Change pwd */ NULL
return(0);
}
+krb5_error_code
+krb5_dbe_lookup_mkvno(krb5_context context,
+ krb5_db_entry *entry,
+ krb5_kvno *mkvno)
+{
+ krb5_tl_data tl_data;
+ krb5_error_code code;
+ krb5_int16 tmp;
+
+ tl_data.tl_data_type = KRB5_TL_MKVNO;
+
+ if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)))
+ return (code);
+
+ /* XXX need to think about this */
+ if (tl_data.tl_data_length != 2) {
+ *mkvno = 0;
+ return (0);
+ }
+
+ /* XXX this needs to be the inverse of how this is encoded */
+ krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp);
+
+ *mkvno = (krb5_kvno) tmp;
+
+ return (0);
+}
+
+krb5_error_code
+krb5_dbe_update_mkvno(krb5_context context,
+ krb5_db_entry * entry,
+ krb5_kvno mkvno)
+{
+ krb5_tl_data tl_data;
+ krb5_octet buf[2]; /* this is the encoded size of an int16 */
+
+ tl_data.tl_data_type = KRB5_TL_MKVNO;
+ tl_data.tl_data_length = sizeof(buf);
+ krb5_kdb_encode_int16((krb5_int16) mkvno, buf);
+ tl_data.tl_data_contents = buf;
+
+ return (krb5_dbe_update_tl_data(context, entry, &tl_data));
+}
+
/* it seems odd that there's no function to remove a tl_data, but if
I need one, I'll add one */