diff options
Diffstat (limited to 'src/kadmin/dbutil')
-rw-r--r-- | src/kadmin/dbutil/Makefile.in | 12 | ||||
-rw-r--r-- | src/kadmin/dbutil/dump.c | 97 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_create.c | 25 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_mkey.c | 1419 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_stash.c | 49 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_util.M | 31 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_util.c | 79 | ||||
-rw-r--r-- | src/kadmin/dbutil/kdb5_util.h | 17 |
8 files changed, 1637 insertions, 92 deletions
diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in index dc5881b436..e88d8b3239 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 24f4d09f20..55677f7a5e 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 c7fb31bb20..6a638a3512 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 0000000000..d2d517657b --- /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", ®exp) != 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 3583a3285f..cdd947ac4d 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 dc34bc8ca1..294357fc97 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 9bc853736d..6cb70c1cdf 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 59f92d4eb2..6e99ac3783 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); |