diff options
Diffstat (limited to 'src')
40 files changed, 3404 insertions, 196 deletions
diff --git a/src/include/kdb.h b/src/include/kdb.h index a3142c1d77..240ac0fd52 100644 --- a/src/include/kdb.h +++ b/src/include/kdb.h @@ -53,6 +53,11 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + #ifndef KRB5_KDB5__ #define KRB5_KDB5__ @@ -173,7 +178,34 @@ typedef struct __krb5_key_salt_tuple { #define KRB5_TL_DB_ARGS 0x7fff #endif /* SECURID */ #define KRB5_TL_USER_CERTIFICATE 0x0007 - +#define KRB5_TL_MKVNO 0x0008 +#define KRB5_TL_ACTKVNO 0x0009 +#define KRB5_TL_MKEY_AUX 0x000a + +/* version number for KRB5_TL_ACTKVNO data */ +#define KRB5_TL_ACTKVNO_VER 1 + +/* version number for KRB5_TL_MKEY_AUX data */ +#define KRB5_TL_MKEY_AUX_VER 1 + +typedef struct _krb5_actkvno_node { + struct _krb5_actkvno_node *next; + krb5_kvno act_kvno; + krb5_timestamp act_time; +} krb5_actkvno_node; + +typedef struct _krb5_mkey_aux_node { + struct _krb5_mkey_aux_node *next; + krb5_kvno mkey_kvno; /* kvno of mkey protecting the latest_mkey */ + krb5_key_data latest_mkey; /* most recent mkey */ +} krb5_mkey_aux_node; + +typedef struct _krb5_keylist_node { + krb5_keyblock keyblock; + krb5_kvno kvno; + struct _krb5_keylist_node *next; +} krb5_keylist_node; + /* * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set * on the principal. @@ -276,6 +308,13 @@ krb5_error_code krb5_db_set_mkey ( krb5_context context, krb5_keyblock *key); krb5_error_code krb5_db_get_mkey ( krb5_context kcontext, krb5_keyblock **key ); + +krb5_error_code krb5_db_set_mkey_list( krb5_context context, + krb5_keylist_node * keylist); + +krb5_error_code krb5_db_get_mkey_list( krb5_context kcontext, + krb5_keylist_node ** keylist); + krb5_error_code krb5_db_free_master_key ( krb5_context kcontext, krb5_keyblock *key ); krb5_error_code krb5_db_store_master_key ( krb5_context kcontext, @@ -284,6 +323,11 @@ krb5_error_code krb5_db_store_master_key ( krb5_context kcontext, krb5_kvno kvno, krb5_keyblock *key, char *master_pwd); +krb5_error_code krb5_db_store_master_key_list ( krb5_context kcontext, + char *keyfile, + krb5_principal mname, + krb5_keylist_node *keylist, + char *master_pwd); krb5_error_code krb5_db_fetch_mkey ( krb5_context context, krb5_principal mname, krb5_enctype etype, @@ -298,6 +342,17 @@ krb5_error_code krb5_db_verify_master_key ( krb5_context kcontext, krb5_kvno kvno, krb5_keyblock *mkey ); krb5_error_code +krb5_db_fetch_mkey_list( krb5_context context, + krb5_principal mname, + const krb5_keyblock * mkey, + krb5_kvno mkvno, + krb5_keylist_node **mkeys_list ); + +krb5_error_code +krb5_db_free_mkey_list( krb5_context context, + krb5_keylist_node *mkey_list ); + +krb5_error_code krb5_dbe_find_enctype( krb5_context kcontext, krb5_db_entry *dbentp, krb5_int32 ktype, @@ -337,15 +392,61 @@ krb5_dbekd_encrypt_key_data( krb5_context context, krb5_key_data * key_data); krb5_error_code +krb5_dbe_fetch_act_key_list(krb5_context context, + krb5_principal princ, + krb5_actkvno_node **act_key_list); + +krb5_error_code +krb5_dbe_find_act_mkey( krb5_context context, + krb5_keylist_node * mkey_list, + krb5_actkvno_node * act_mkey_list, + krb5_kvno * act_kvno, + krb5_keyblock ** act_mkey); + +krb5_error_code +krb5_dbe_find_mkey( krb5_context context, + krb5_keylist_node * mkey_list, + krb5_db_entry * entry, + krb5_keyblock ** mkey); + +krb5_error_code +krb5_dbe_lookup_mkvno( krb5_context context, + krb5_db_entry * entry, + krb5_kvno * mkvno); + +krb5_error_code krb5_dbe_lookup_mod_princ_data( krb5_context context, krb5_db_entry * entry, krb5_timestamp * mod_time, krb5_principal * mod_princ); +krb5_error_code +krb5_dbe_lookup_mkey_aux( krb5_context context, + krb5_db_entry * entry, + krb5_mkey_aux_node ** mkey_aux_data_list); +krb5_error_code +krb5_dbe_update_mkvno( krb5_context context, + krb5_db_entry * entry, + krb5_kvno mkvno); krb5_error_code -krb5_dbe_update_last_pwd_change( krb5_context context, - krb5_db_entry * entry, +krb5_dbe_lookup_actkvno( krb5_context context, + krb5_db_entry * entry, + krb5_actkvno_node ** actkvno_list); + +krb5_error_code +krb5_dbe_update_mkey_aux( krb5_context context, + krb5_db_entry * entry, + krb5_mkey_aux_node * mkey_aux_data_list); + +krb5_error_code +krb5_dbe_update_actkvno(krb5_context context, + krb5_db_entry * entry, + const krb5_actkvno_node *actkvno_list); + +krb5_error_code +krb5_dbe_update_last_pwd_change( krb5_context context, + krb5_db_entry * entry, krb5_timestamp stamp); krb5_error_code @@ -383,6 +484,11 @@ krb5_dbe_lookup_last_pwd_change( krb5_context context, krb5_timestamp * stamp); krb5_error_code +krb5_dbe_delete_tl_data( krb5_context context, + krb5_db_entry * entry, + krb5_int16 tl_data_type); + +krb5_error_code krb5_dbe_update_tl_data( krb5_context context, krb5_db_entry * entry, krb5_tl_data * new_tl_data); @@ -421,6 +527,12 @@ krb5_dbe_apw( krb5_context context, char * passwd, krb5_db_entry * db_entry); +int +krb5_db_get_key_data_kvno( krb5_context context, + int count, + krb5_key_data * data); + + /* default functions. Should not be directly called */ /* * Default functions prototype @@ -443,6 +555,12 @@ krb5_def_store_mkey( krb5_context context, krb5_keyblock *key, char *master_pwd); +krb5_error_code +krb5_def_store_mkey_list( krb5_context context, + char *keyfile, + krb5_principal mname, + krb5_keylist_node *keylist, + char *master_pwd); krb5_error_code krb5_db_def_fetch_mkey( krb5_context context, @@ -457,13 +575,26 @@ krb5_def_verify_master_key( krb5_context context, krb5_kvno kvno, krb5_keyblock *mkey); +krb5_error_code +krb5_def_fetch_mkey_list( krb5_context context, + krb5_principal mprinc, + const krb5_keyblock *mkey, + krb5_kvno mkvno, + krb5_keylist_node **mkeys_list); + krb5_error_code kdb_def_set_mkey ( krb5_context kcontext, char *pwd, krb5_keyblock *key ); +krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext, + krb5_keylist_node *keylist ); + krb5_error_code kdb_def_get_mkey ( krb5_context kcontext, krb5_keyblock **key ); +krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext, + krb5_keylist_node **keylist ); + krb5_error_code krb5_dbe_def_cpw( krb5_context context, krb5_keyblock * master_key, @@ -536,7 +667,6 @@ krb5_db_free_policy( krb5_context kcontext, osa_policy_ent_t policy); - krb5_error_code krb5_db_set_context (krb5_context, void *db_context); @@ -545,6 +675,21 @@ krb5_error_code krb5_db_get_context (krb5_context, void **db_context); +void +krb5_dbe_free_key_data_contents(krb5_context, krb5_key_data *); + +void +krb5_dbe_free_key_list(krb5_context, krb5_keylist_node *); + +void +krb5_dbe_free_actkvno_list(krb5_context, krb5_actkvno_node *); + +void +krb5_dbe_free_mkey_aux_list(krb5_context, krb5_mkey_aux_node *); + +void +krb5_dbe_free_tl_data(krb5_context, krb5_tl_data *); + #define KRB5_KDB_DEF_FLAGS 0 #define KDB_MAX_DB_NAME 128 @@ -669,6 +814,11 @@ typedef struct _kdb_vftabl { krb5_error_code (*get_master_key) ( krb5_context kcontext, krb5_keyblock **key); + krb5_error_code (*set_master_key_list) ( krb5_context kcontext, + krb5_keylist_node *keylist); + + krb5_error_code (*get_master_key_list) ( krb5_context kcontext, + krb5_keylist_node **keylist); krb5_error_code (*setup_master_key_name) ( krb5_context kcontext, char *keyname, @@ -694,6 +844,18 @@ typedef struct _kdb_vftabl { krb5_kvno kvno, krb5_keyblock *mkey ); + krb5_error_code (*fetch_master_key_list) (krb5_context kcontext, + krb5_principal mname, + const krb5_keyblock *key, + krb5_kvno kvno, + krb5_keylist_node **mkeys_list); + + krb5_error_code (*store_master_key_list) ( krb5_context kcontext, + char *db_arg, + krb5_principal mname, + krb5_keylist_node *keylist, + char *master_pwd); + krb5_error_code (*dbe_search_enctype) ( krb5_context kcontext, krb5_db_entry *dbentp, krb5_int32 *start, diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index 469deaa771..af6673d079 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -1535,6 +1535,8 @@ void kadmin_getprinc(argc, argv) } else printf("no salt\n"); } + printf("MKey: vno %d\n", + dprinc.mkvno); printf("Attributes:"); for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in index 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); diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c index 573a2c04c7..81e74758fb 100644 --- a/src/kadmin/server/ovsec_kadmd.c +++ b/src/kadmin/server/ovsec_kadmd.c @@ -98,6 +98,7 @@ void *global_server_handle; #define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" extern krb5_keyblock master_keyblock; +extern krb5_keylist_node *master_keylist; char *build_princ_name(char *name, char *realm); void log_badauth(OM_uint32 major, OM_uint32 minor, @@ -395,6 +396,11 @@ int main(int argc, char *argv[]) krb5_klog_syslog(LOG_ERR, "Can't set master key for kdb keytab."); goto kterr; } + ret = krb5_db_set_mkey_list(hctx, master_keylist); + if (ret) { + krb5_klog_syslog(LOG_ERR, "Can't set master key list for kdb keytab."); + goto kterr; + } ret = krb5_kt_register(context, &krb5_kt_kdb_ops); if (ret) { krb5_klog_syslog(LOG_ERR, "Can't register kdb keytab."); diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index 9571fb2122..8db39ac4f2 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -104,6 +104,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const char *status; krb5_key_data *server_key, *client_key; krb5_keyblock server_keyblock, client_keyblock; + krb5_keyblock *mkey_ptr; krb5_enctype useenctype; krb5_boolean update_client = 0; krb5_data e_data; @@ -115,6 +116,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, void *pa_context = NULL; int did_log = 0; const char *emsg = 0; + krb5_keylist_node *tmp_mkey_list; #if APPLE_PKINIT asReqDebug("process_as_req top realm %s name %s\n", @@ -425,9 +427,28 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, goto errout; } + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, + &mkey_ptr))) { + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(kdc_context, master_princ, + &master_keyblock, 0, &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(kdc_context, master_keylist); + master_keylist = tmp_mkey_list; + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, + &server, &mkey_ptr))) { + status = "FINDING_MASTER_KEY"; + goto errout; + } + } else { + status = "FINDING_MASTER_KEY"; + goto errout; + } + } + /* convert server.key into a real key (it may be encrypted in the database) */ - if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, + if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, mkey_ptr, /* server_keyblock is later used to generate auth data signatures */ server_key, &server_keyblock, NULL))) { @@ -456,8 +477,27 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, goto errout; } + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &client, + &mkey_ptr))) { + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(kdc_context, master_princ, + &master_keyblock, 0, &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(kdc_context, master_keylist); + master_keylist = tmp_mkey_list; + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, + &client, &mkey_ptr))) { + status = "FINDING_MASTER_KEY"; + goto errout; + } + } else { + status = "FINDING_MASTER_KEY"; + goto errout; + } + } + /* convert client.key_data into a real key */ - if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, + if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, mkey_ptr, client_key, &client_keyblock, NULL))) { status = "DECRYPT_CLIENT_KEY"; diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index 0843002569..a6ce704bd7 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -105,6 +105,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_keyblock session_key; krb5_timestamp until, rtime; krb5_keyblock encrypting_key; + krb5_keyblock *mkey_ptr; krb5_key_data *server_key; char *cname = 0, *sname = 0, *altcname = 0; krb5_last_req_entry *nolrarray[2], nolrentry; @@ -572,10 +573,31 @@ tgt_again: status = "FINDING_SERVER_KEY"; goto cleanup; } + + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(kdc_context, master_princ, + &master_keyblock, 0, &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(kdc_context, master_keylist); + master_keylist = tmp_mkey_list; + if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, + &server, &mkey_ptr))) { + status = "FINDING_MASTER_KEY"; + goto cleanup; + } + } else { + status = "FINDING_MASTER_KEY"; + goto cleanup; + } + } + /* convert server.key into a real key (it may be encrypted * in the database) */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, - &master_keyblock, + mkey_ptr, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; diff --git a/src/kdc/extern.c b/src/kdc/extern.c index 2a2c1ae22e..7ebc7bb3a6 100644 --- a/src/kdc/extern.c +++ b/src/kdc/extern.c @@ -27,6 +27,7 @@ */ #include "k5-int.h" +#include "kdb.h" #include "extern.h" /* real declarations of KDC's externs */ diff --git a/src/kdc/extern.h b/src/kdc/extern.h index 20cc4bc049..88e8b0ddef 100644 --- a/src/kdc/extern.h +++ b/src/kdc/extern.h @@ -53,7 +53,12 @@ typedef struct __kdc_realm_data { char * realm_stash; /* Stash file name for realm */ char * realm_mpname; /* Master principal name for realm */ krb5_principal realm_mprinc; /* Master principal for realm */ + /* + * Note realm_mkey is mkey read from stash or keyboard and may not be the + * latest. The mkey_list will have all the mkeys in use. + */ krb5_keyblock realm_mkey; /* Master key for this realm */ + krb5_keylist_node * mkey_list; /* list of mkeys in use for this realm */ /* * TGS per-realm data. */ @@ -86,6 +91,7 @@ kdc_realm_t *find_realm_data (char *, krb5_ui_4); #define max_life_for_realm kdc_active_realm->realm_maxlife #define max_renewable_life_for_realm kdc_active_realm->realm_maxrlife #define master_keyblock kdc_active_realm->realm_mkey +#define master_keylist kdc_active_realm->mkey_list #define master_princ kdc_active_realm->realm_mprinc #define tgs_server kdc_active_realm->realm_tgsprinc #define reject_bad_transit kdc_active_realm->realm_reject_bad_transit diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index 6ec156440f..7aacca402e 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -665,8 +665,9 @@ get_entry_data(krb5_context context, int i, k; krb5_data *ret; krb5_deltat *delta; - krb5_keyblock *keys; + krb5_keyblock *keys, *mkey_ptr; krb5_key_data *entry_key; + krb5_error_code error; switch (type) { case krb5plugin_preauth_entry_request_certificate: @@ -700,13 +701,32 @@ get_entry_data(krb5_context context, ret->data = (char *) keys; ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1); memset(ret->data, 0, ret->length); + if ((error = krb5_dbe_find_mkey(context, master_keylist, entry, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing the mkey list in case it's been updated */ + if (krb5_db_fetch_mkey_list(context, master_princ, + &master_keyblock, 0, + &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(context, master_keylist); + master_keylist = tmp_mkey_list; + if ((error = krb5_dbe_find_mkey(context, master_keylist, entry, + &mkey_ptr))) { + free(ret); + return (error); + } + } else { + free(ret); + return (error); + } + } k = 0; for (i = 0; i < request->nktypes; i++) { entry_key = NULL; if (krb5_dbe_find_enctype(context, entry, request->ktype[i], -1, 0, &entry_key) != 0) continue; - if (krb5_dbekd_decrypt_key_data(context, &master_keyblock, + if (krb5_dbekd_decrypt_key_data(context, mkey_ptr, entry_key, &keys[k], NULL) != 0) { if (keys[k].contents != NULL) krb5_free_keyblock_contents(context, &keys[k]); @@ -1337,7 +1357,7 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client, krb5_data scratch; krb5_data enc_ts_data; krb5_enc_data *enc_data = 0; - krb5_keyblock key; + krb5_keyblock key, *mkey_ptr; krb5_key_data * client_key; krb5_int32 start; krb5_timestamp timenow; @@ -1355,6 +1375,24 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client, if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL) goto cleanup; + if ((retval = krb5_dbe_find_mkey(context, master_keylist, client, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing the mkey list in case it's been updated */ + if (krb5_db_fetch_mkey_list(context, master_princ, + &master_keyblock, 0, + &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(context, master_keylist); + master_keylist = tmp_mkey_list; + if ((retval = krb5_dbe_find_mkey(context, master_keylist, client, + &mkey_ptr))) { + goto cleanup; + } + } else { + goto cleanup; + } + } + start = 0; decrypt_err = 0; while (1) { @@ -1363,7 +1401,7 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client, -1, 0, &client_key))) goto cleanup; - if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock, + if ((retval = krb5_dbekd_decrypt_key_data(context, mkey_ptr, client_key, &key, NULL))) goto cleanup; @@ -1946,7 +1984,7 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request, krb5_sam_challenge sc; krb5_predicted_sam_response psr; krb5_data * scratch; - krb5_keyblock encrypting_key; + krb5_keyblock encrypting_key, *mkey_ptr; char response[9]; char inputblock[8]; krb5_data predict_response; @@ -2010,6 +2048,24 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request, if (sc.sam_type) { /* so use assoc to get the key out! */ { + if ((retval = krb5_dbe_find_mkey(context, master_keylist, &assoc, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing the mkey list in case it's been updated */ + if (krb5_db_fetch_mkey_list(context, master_princ, + &master_keyblock, 0, + &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(context, master_keylist); + master_keylist = tmp_mkey_list; + if ((retval = krb5_dbe_find_mkey(context, master_keylist, &assoc, + &mkey_ptr))) { + return (retval); + } + } else { + return (retval); + } + } + /* here's what do_tgs_req does */ retval = krb5_dbe_find_enctype(kdc_context, &assoc, ENCTYPE_DES_CBC_RAW, @@ -2026,7 +2082,7 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request, } /* convert server.key into a real key */ retval = krb5_dbekd_decrypt_key_data(kdc_context, - &master_keyblock, + mkey_ptr, assoc_key, &encrypting_key, NULL); if (retval) { @@ -2513,7 +2569,7 @@ static krb5_error_code verify_pkinit_request( unsigned cert_hash_len; unsigned key_dex; unsigned cert_match = 0; - krb5_keyblock decrypted_key; + krb5_keyblock decrypted_key, *mkey_ptr; /* the data we get from the AS-REQ */ krb5_timestamp client_ctime = 0; @@ -2657,6 +2713,22 @@ static krb5_error_code verify_pkinit_request( goto cleanup; } cert_hash_len = strlen(cert_hash); + if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry, &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing the mkey list in case it's been updated */ + if (krb5_db_fetch_mkey_list(context, master_princ, + &master_keyblock, 0, + &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(context, master_keylist); + master_keylist = tmp_mkey_list; + if ((krtn = krb5_dbe_find_mkey(context, master_keylist, &entry, + &mkey_ptr))) { + goto cleanup; + } + } else { + goto cleanup; + } + } for(key_dex=0; key_dex<client->n_key_data; key_dex++) { krb5_key_data *key_data = &client->key_data[key_dex]; kdcPkinitDebug("--- key %u type[0] %u length[0] %u type[1] %u length[1] %u\n", @@ -2671,7 +2743,7 @@ static krb5_error_code verify_pkinit_request( * Unfortunately this key is stored encrypted even though it's * not sensitive... */ - krtn = krb5_dbekd_decrypt_key_data(context, &master_keyblock, + krtn = krb5_dbekd_decrypt_key_data(context, mkey_ptr, key_data, &decrypted_key, NULL); if(krtn) { kdcPkinitDebug("verify_pkinit_request: error decrypting cert hash block\n"); diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index 34a8ed0c3f..28b4a37ca0 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -415,6 +415,7 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags, krb5_error_code retval; krb5_boolean more, similar; krb5_key_data * server_key; + krb5_keyblock * mkey_ptr; *nprincs = 1; @@ -445,6 +446,25 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags, retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } + + if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, server, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(kdc_context, master_princ, + &master_keyblock, 0, &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(kdc_context, master_keylist); + master_keylist = tmp_mkey_list; + if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, + server, &mkey_ptr))) { + goto errout; + } + } else { + goto errout; + } + } + retval = krb5_dbe_find_enctype(kdc_context, server, match_enctype ? ticket->enc_part.enctype : -1, -1, (krb5_int32)ticket->enc_part.kvno, @@ -456,7 +476,7 @@ kdc_get_server_key(krb5_ticket *ticket, unsigned int flags, goto errout; } if ((*key = (krb5_keyblock *)malloc(sizeof **key))) { - retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, + retval = krb5_dbekd_decrypt_key_data(kdc_context, mkey_ptr, server_key, *key, NULL); } else diff --git a/src/kdc/main.c b/src/kdc/main.c index b9334680b4..bb4d7584d7 100644 --- a/src/kdc/main.c +++ b/src/kdc/main.c @@ -154,9 +154,12 @@ finish_realm(kdc_realm_t *rdp) if (rdp->realm_mprinc) krb5_free_principal(rdp->realm_context, rdp->realm_mprinc); if (rdp->realm_mkey.length && rdp->realm_mkey.contents) { + /* XXX shouldn't memset be zap for safety? */ memset(rdp->realm_mkey.contents, 0, rdp->realm_mkey.length); free(rdp->realm_mkey.contents); } + if (rdp->mkey_list) + krb5_dbe_free_key_list(rdp->realm_context, rdp->mkey_list); krb5_db_fini(rdp->realm_context); if (rdp->realm_tgsprinc) krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc); @@ -242,6 +245,7 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm, krb5_boolean manual; krb5_realm_params *rparams; int kdb_open_flags; + krb5_kvno mkvno = IGNORE_VNO; memset((char *) rdp, 0, sizeof(kdc_realm_t)); if (!realm) { @@ -374,18 +378,25 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm, } /* - * Get the master key. + * Get the master key (note, may not be the most current mkey). */ if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc, rdp->realm_mkey.enctype, manual, FALSE, rdp->realm_stash, - NULL, NULL, &rdp->realm_mkey))) { + &mkvno, NULL, &rdp->realm_mkey))) { com_err(progname, kret, "while fetching master key %s for realm %s", rdp->realm_mpname, realm); goto whoops; } +#if 0 /************** Begin IFDEF'ed OUT *******************************/ + /* + * Commenting krb5_db_verify_master_key out because it requires the most + * current mkey which may not be the case here. The call to + * krb5_db_fetch_mkey_list() will end up verifying that the mkey is viable + * anyway. + */ /* Verify the master key */ if ((kret = krb5_db_verify_master_key(rdp->realm_context, rdp->realm_mprinc, @@ -395,6 +406,14 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm, "while verifying master key for realm %s", realm); goto whoops; } +#endif /**************** END IFDEF'ed OUT *******************************/ + + if ((kret = krb5_db_fetch_mkey_list(rdp->realm_context, rdp->realm_mprinc, + &rdp->realm_mkey, mkvno, &rdp->mkey_list))) { + com_err(progname, kret, + "while fetching master keys list for realm %s", realm); + goto whoops; + } if ((kret = krb5_db_set_mkey(rdp->realm_context, &rdp->realm_mkey))) { com_err(progname, kret, diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports index a02577667b..545d43b708 100644 --- a/src/lib/kadm5/srv/libkadm5srv.exports +++ b/src/lib/kadm5/srv/libkadm5srv.exports @@ -87,6 +87,7 @@ krb5_string_to_keysalts krb5_match_config_pattern master_db master_keyblock +master_keylist master_princ osa_free_princ_ent ovsec_kadm_chpass_principal diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c index 836cd00b76..47f00c0713 100644 --- a/src/lib/kadm5/srv/server_kdb.c +++ b/src/lib/kadm5/srv/server_kdb.c @@ -4,6 +4,11 @@ * $Header$ */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif @@ -15,7 +20,9 @@ static char *rcsid = "$Header$"; #include "server_internal.h" krb5_principal master_princ; -krb5_keyblock master_keyblock; +krb5_keyblock master_keyblock; /* local mkey */ +krb5_keylist_node *master_keylist = NULL; +krb5_actkvno_node *active_mkey_list = NULL; krb5_db_entry master_db; krb5_principal hist_princ; @@ -32,6 +39,7 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle, int ret = 0; char *realm; krb5_boolean from_kbd = FALSE; + krb5_kvno mkvno = IGNORE_VNO; if (from_keyboard) from_kbd = TRUE; @@ -50,22 +58,45 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle, master_keyblock.enctype = handle->params.enctype; + /* + * Fetch the local mkey, may not be the latest but that's okay because we + * really want the list of all mkeys and those can be retrieved with any + * valid mkey. + */ ret = krb5_db_fetch_mkey(handle->context, master_princ, master_keyblock.enctype, from_kbd, FALSE /* only prompt once */, handle->params.stash_file, - NULL /* don't care about kvno */, + &mkvno /* get the kvno of the returned mkey */, NULL /* I'm not sure about this, but it's what the kdc does --marc */, &master_keyblock); if (ret) goto done; +#if 0 /************** Begin IFDEF'ed OUT *******************************/ + /* + * krb5_db_fetch_mkey_list will verify mkey so don't call + * krb5_db_verify_master_key() + */ if ((ret = krb5_db_verify_master_key(handle->context, master_princ, IGNORE_VNO, &master_keyblock))) { krb5_db_fini(handle->context); return ret; } +#endif /**************** END IFDEF'ed OUT *******************************/ + + if ((ret = krb5_db_fetch_mkey_list(handle->context, master_princ, + &master_keyblock, mkvno, &master_keylist))) { + krb5_db_fini(handle->context); + return (ret); + } + + if ((ret = krb5_dbe_fetch_act_key_list(handle->context, master_princ, + &active_mkey_list))) { + krb5_db_fini(handle->context); + return (ret); + } done: if (r == NULL) @@ -106,6 +137,7 @@ krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) char *realm, *hist_name; krb5_key_data *key_data; krb5_key_salt_tuple ks[1]; + krb5_keyblock *tmp_mkey; if (r == NULL) { if ((ret = krb5_get_default_realm(handle->context, &realm))) @@ -177,7 +209,12 @@ krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) if (ret) goto done; - ret = krb5_dbekd_decrypt_key_data(handle->context, &master_keyblock, + ret = krb5_dbe_find_mkey(handle->context, master_keylist, &hist_db, + &tmp_mkey); + if (ret) + goto done; + + ret = krb5_dbekd_decrypt_key_data(handle->context, tmp_mkey, key_data, &hist_key, NULL); if (ret) goto done; diff --git a/src/lib/kadm5/srv/svr_iters.c b/src/lib/kadm5/srv/svr_iters.c index cd3fb41770..757d3ab0e6 100644 --- a/src/lib/kadm5/srv/svr_iters.c +++ b/src/lib/kadm5/srv/svr_iters.c @@ -46,6 +46,7 @@ struct iter_data { #endif }; +/* XXX Duplicated in kdb5_util! */ /* * Function: glob_to_regexp * diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c index 2bdc1f5b29..207143710e 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -32,13 +32,15 @@ static char *rcsid = "$Header$"; extern krb5_principal master_princ; extern krb5_principal hist_princ; -extern krb5_keyblock master_keyblock; +extern krb5_keyblock master_keyblock; +extern krb5_keylist_node *master_keylist; +extern krb5_actkvno_node *active_mkey_list; extern krb5_keyblock hist_key; extern krb5_db_entry master_db; extern krb5_db_entry hist_db; extern krb5_kvno hist_kvno; -static int decrypt_key_data(krb5_context context, +static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey, int n_key_data, krb5_key_data *key_data, krb5_keyblock **keyblocks, int *n_keys); @@ -205,6 +207,8 @@ kadm5_create_principal_3(void *server_handle, krb5_tl_data *tl_data_orig, *tl_data_tail; unsigned int ret; kadm5_server_handle_t handle = server_handle; + krb5_keyblock *act_mkey; + krb5_kvno act_kvno; CHECK_HANDLE(server_handle); @@ -347,7 +351,16 @@ kadm5_create_principal_3(void *server_handle, /* initialize the keys */ - if ((ret = krb5_dbe_cpw(handle->context, &master_keyblock, + ret = krb5_dbe_find_act_mkey(handle->context, master_keylist, + active_mkey_list, &act_kvno, &act_mkey); + if (ret) { + krb5_db_free_principal(handle->context, &kdb, 1); + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return (ret); + } + + if ((ret = krb5_dbe_cpw(handle->context, act_mkey, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, password, @@ -359,6 +372,16 @@ kadm5_create_principal_3(void *server_handle, return(ret); } + /* Record the master key VNO used to encrypt this entry's keys */ + ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno); + if (ret) + { + krb5_db_free_principal(handle->context, &kdb, 1); + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return ret; + } + /* populate the admin-server-specific fields. In the OV server, this used to be in a separate database. Since there's already marshalling code for the admin fields, to keep things simple, @@ -806,12 +829,24 @@ kadm5_get_principal(void *server_handle, krb5_principal principal, if (kdb.key_data[i].key_data_kvno > entry->kvno) entry->kvno = kdb.key_data[i].key_data_kvno; + ret = krb5_dbe_lookup_mkvno(handle->context, &kdb, &entry->mkvno); + if (ret) + goto done; + + /* + * It's my understanding that KADM5_API_VERSION_1 is for OpenVision admin + * system compatiblity and is not required to maintain at this point so I'm + * commenting out this code. + * -- Will Fiveash + */ +#if 0 /************** Begin IFDEF'ed OUT *******************************/ if (handle->api_version == KADM5_API_VERSION_2) entry->mkvno = 0; else { /* XXX I'll be damned if I know how to deal with this one --marc */ entry->mkvno = 1; } +#endif /**************** END IFDEF'ed OUT *******************************/ /* * The new fields that only exist in version 2 start here @@ -936,6 +971,7 @@ done: */ static kadm5_ret_t check_pw_reuse(krb5_context context, + krb5_keyblock *mkey, krb5_keyblock *hist_keyblock, int n_new_key_data, krb5_key_data *new_key_data, unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data) @@ -946,7 +982,7 @@ check_pw_reuse(krb5_context context, for (x = 0; x < n_new_key_data; x++) { ret = krb5_dbekd_decrypt_key_data(context, - &master_keyblock, + mkey, &(new_key_data[x]), &newkey, NULL); if (ret) @@ -999,7 +1035,7 @@ check_pw_reuse(krb5_context context, * set to n_key_data. */ static -int create_history_entry(krb5_context context, int n_key_data, +int create_history_entry(krb5_context context, krb5_keyblock *mkey, int n_key_data, krb5_key_data *key_data, osa_pw_hist_ent *hist) { int i, ret; @@ -1013,7 +1049,7 @@ int create_history_entry(krb5_context context, int n_key_data, for (i = 0; i < n_key_data; i++) { ret = krb5_dbekd_decrypt_key_data(context, - &master_keyblock, + mkey, &key_data[i], &key, &salt); if (ret) @@ -1302,6 +1338,8 @@ kadm5_chpass_principal_3(void *server_handle, int have_pol = 0; kadm5_server_handle_t handle = server_handle; osa_pw_hist_ent hist; + krb5_keyblock *act_mkey; + krb5_kvno act_kvno; CHECK_HANDLE(server_handle); @@ -1335,7 +1373,12 @@ kadm5_chpass_principal_3(void *server_handle, KADM5_POLICY, &pol, principal))) goto done; - ret = krb5_dbe_cpw(handle->context, &master_keyblock, + ret = krb5_dbe_find_act_mkey(handle->context, master_keylist, + active_mkey_list, &act_kvno, &act_mkey); + if (ret) + goto done; + + ret = krb5_dbe_cpw(handle->context, act_mkey, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, password, 0 /* increment kvno */, @@ -1343,6 +1386,10 @@ kadm5_chpass_principal_3(void *server_handle, if (ret) goto done; + ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno); + if (ret) + goto done; + kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; ret = krb5_timeofday(handle->context, &now); @@ -1372,12 +1419,13 @@ kadm5_chpass_principal_3(void *server_handle, #endif ret = create_history_entry(handle->context, + act_mkey, kdb_save.n_key_data, kdb_save.key_data, &hist); if (ret) goto done; - ret = check_pw_reuse(handle->context, &hist_key, + ret = check_pw_reuse(handle->context, act_mkey, &hist_key, kdb.n_key_data, kdb.key_data, 1, &hist); if (ret) @@ -1389,7 +1437,7 @@ kadm5_chpass_principal_3(void *server_handle, goto done; } - ret = check_pw_reuse(handle->context, &hist_key, + ret = check_pw_reuse(handle->context, act_mkey, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys); if (ret) @@ -1489,6 +1537,7 @@ kadm5_randkey_principal_3(void *server_handle, krb5_key_data *key_data; int ret, last_pwd, have_pol = 0; kadm5_server_handle_t handle = server_handle; + krb5_keyblock *act_mkey; if (keyblocks) *keyblocks = NULL; @@ -1507,7 +1556,12 @@ kadm5_randkey_principal_3(void *server_handle, if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); - ret = krb5_dbe_crk(handle->context, &master_keyblock, + ret = krb5_dbe_find_act_mkey(handle->context, master_keylist, + active_mkey_list, NULL, &act_mkey); + if (ret) + goto done; + + ret = krb5_dbe_crk(handle->context, act_mkey, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, keepold, @@ -1552,7 +1606,7 @@ kadm5_randkey_principal_3(void *server_handle, goto done; } - ret = check_pw_reuse(handle->context, &hist_key, + ret = check_pw_reuse(handle->context, act_mkey, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys); if (ret) @@ -1579,12 +1633,12 @@ kadm5_randkey_principal_3(void *server_handle, if (ret) goto done; - ret = decrypt_key_data(handle->context, 1, key_data, + ret = decrypt_key_data(handle->context, act_mkey, 1, key_data, keyblocks, NULL); if (ret) goto done; } else { - ret = decrypt_key_data(handle->context, + ret = decrypt_key_data(handle->context, act_mkey, kdb.n_key_data, kdb.key_data, keyblocks, n_keys); if (ret) @@ -1630,6 +1684,7 @@ kadm5_setv4key_principal(void *server_handle, #endif kadm5_server_handle_t handle = server_handle; krb5_key_data tmp_key_data; + krb5_keyblock *act_mkey; memset( &tmp_key_data, 0, sizeof(tmp_key_data)); @@ -1667,8 +1722,13 @@ kadm5_setv4key_principal(void *server_handle, keysalt.data.length = 0; keysalt.data.data = NULL; + ret = krb5_dbe_find_act_mkey(handle->context, master_keylist, + active_mkey_list, NULL, &act_mkey); + if (ret) + goto done; + /* use tmp_key_data as temporary location and reallocate later */ - ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock, + ret = krb5_dbekd_encrypt_key_data(handle->context, act_mkey, keyblock, &keysalt, kvno + 1, &tmp_key_data); if (ret) { @@ -1809,6 +1869,7 @@ kadm5_setkey_principal_3(void *server_handle, krb5_keysalt keysalt; krb5_key_data tmp_key_data; krb5_key_data *tptr; + krb5_keyblock *act_mkey; CHECK_HANDLE(server_handle); @@ -1880,15 +1941,20 @@ kadm5_setkey_principal_3(void *server_handle, } memset (&tmp_key_data, 0, sizeof(tmp_key_data)); + ret = krb5_dbe_find_act_mkey(handle->context, master_keylist, + active_mkey_list, NULL, &act_mkey); + if (ret) + goto done; + ret = krb5_dbekd_encrypt_key_data(handle->context, - &master_keyblock, + act_mkey, &keyblocks[i], n_ks_tuple ? &keysalt : NULL, kvno + 1, &tmp_key_data); - if (ret) { + if (ret) goto done; - } + tptr = &kdb.key_data[i]; tptr->key_data_ver = tmp_key_data.key_data_ver; tptr->key_data_kvno = tmp_key_data.key_data_kvno; @@ -2013,6 +2079,7 @@ kadm5_get_principal_keys(void *server_handle /* IN */, krb5_key_data *key_data; kadm5_ret_t ret; kadm5_server_handle_t handle = server_handle; + krb5_keyblock *mkey_ptr; if (keyblocks) *keyblocks = NULL; @@ -2026,6 +2093,25 @@ kadm5_get_principal_keys(void *server_handle /* IN */, return(ret); if (keyblocks) { + if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(handle->context, master_princ, + &master_keyblock, 0, + &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(handle->context, master_keylist); + master_keylist = tmp_mkey_list; + if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, + &kdb, &mkey_ptr))) { + goto done; + } + } else { + goto done; + } + } + if (handle->api_version == KADM5_API_VERSION_1) { /* Version 1 clients will expect to see a DES_CRC enctype. */ if ((ret = krb5_dbe_find_enctype(handle->context, &kdb, @@ -2033,11 +2119,11 @@ kadm5_get_principal_keys(void *server_handle /* IN */, -1, -1, &key_data))) goto done; - if ((ret = decrypt_key_data(handle->context, 1, key_data, + if ((ret = decrypt_key_data(handle->context, mkey_ptr, 1, key_data, keyblocks, NULL))) goto done; } else { - ret = decrypt_key_data(handle->context, + ret = decrypt_key_data(handle->context, mkey_ptr, kdb.n_key_data, kdb.key_data, keyblocks, n_keys); if (ret) @@ -2056,10 +2142,10 @@ done: /* * Allocate an array of n_key_data krb5_keyblocks, fill in each * element with the results of decrypting the nth key in key_data with - * master_keyblock, and if n_keys is not NULL fill it in with the + * mkey, and if n_keys is not NULL fill it in with the * number of keys decrypted. */ -static int decrypt_key_data(krb5_context context, +static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey, int n_key_data, krb5_key_data *key_data, krb5_keyblock **keyblocks, int *n_keys) { @@ -2072,7 +2158,7 @@ static int decrypt_key_data(krb5_context context, memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); for (i = 0; i < n_key_data; i++) { - ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock, + ret = krb5_dbekd_decrypt_key_data(context, mkey, &key_data[i], &keys[i], NULL); if (ret) { @@ -2135,6 +2221,7 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle, kadm5_server_handle_t handle = server_handle; krb5_db_entry dbent; krb5_key_data *key_data; + krb5_keyblock *mkey_ptr; int ret; CHECK_HANDLE(server_handle); @@ -2149,8 +2236,28 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle, stype, kvno, &key_data))) return ret; + /* find_mkey only uses this field */ + dbent.tl_data = entry->tl_data; + if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent, + &mkey_ptr))) { + krb5_keylist_node *tmp_mkey_list; + /* try refreshing master key list */ + /* XXX it would nice if we had the mkvno here for optimization */ + if (krb5_db_fetch_mkey_list(handle->context, master_princ, + &master_keyblock, 0, &tmp_mkey_list) == 0) { + krb5_dbe_free_key_list(handle->context, master_keylist); + master_keylist = tmp_mkey_list; + if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, + &dbent, &mkey_ptr))) { + return ret; + } + } else { + return ret; + } + } + if ((ret = krb5_dbekd_decrypt_key_data(handle->context, - &master_keyblock, key_data, + mkey_ptr, key_data, keyblock, keysalt))) return ret; diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index 88df6bcc61..2252c3ad03 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -1,5 +1,5 @@ /* - * Copyright 2006, 2008 by the Massachusetts Institute of Technology. + * Copyright 2006, 2009 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -23,6 +23,11 @@ */ /* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* * This code was based on code donated to MIT by Novell for * distribution under the MIT license. */ @@ -101,6 +106,73 @@ kdb_unlock_list() return k5_mutex_unlock(&db_lock); } +/* + * XXX eventually this should be consolidated with krb5_free_key_data_contents + * so there is only a single version. + */ +void +krb5_dbe_free_key_data_contents(krb5_context context, krb5_key_data *key) +{ + int i, idx; + + idx = (key->key_data_ver == 1 ? 1 : 2); + for (i = 0; i < idx; i++) { + if (key->key_data_contents[i]) { + zap(key->key_data_contents[i], key->key_data_length[i]); + free(key->key_data_contents[i]); + } + } + return; +} + +void +krb5_dbe_free_key_list(krb5_context context, krb5_keylist_node *val) +{ + krb5_keylist_node *temp = val, *prev; + + while (temp != NULL) { + prev = temp; + temp = temp->next; + krb5_free_keyblock_contents(context, &(prev->keyblock)); + krb5_xfree(prev); + } +} + +void +krb5_dbe_free_actkvno_list(krb5_context context, krb5_actkvno_node *val) +{ + krb5_actkvno_node *temp = val, *prev; + + while (temp != NULL) { + prev = temp; + temp = temp->next; + krb5_xfree(prev); + } +} + +void +krb5_dbe_free_mkey_aux_list(krb5_context context, krb5_mkey_aux_node *val) +{ + krb5_mkey_aux_node *temp = val, *prev; + + while (temp != NULL) { + prev = temp; + temp = temp->next; + krb5_dbe_free_key_data_contents(context, &prev->latest_mkey); + krb5_xfree(prev); + } +} + +void +krb5_dbe_free_tl_data(krb5_context context, krb5_tl_data *tl_data) +{ + if (tl_data) { + if (tl_data->tl_data_contents) + free(tl_data->tl_data_contents); + free(tl_data); + } +} + #define kdb_init_lib_lock(a) 0 #define kdb_destroy_lib_lock(a) (void)0 #define kdb_lock_lib_lock(a, b) 0 @@ -196,10 +268,18 @@ kdb_setup_opt_functions(db_library lib) lib->vftabl.set_master_key = kdb_def_set_mkey; } + if (lib->vftabl.set_master_key_list == NULL) { + lib->vftabl.set_master_key_list = kdb_def_set_mkey_list; + } + if (lib->vftabl.get_master_key == NULL) { lib->vftabl.get_master_key = kdb_def_get_mkey; } + if (lib->vftabl.get_master_key_list == NULL) { + lib->vftabl.get_master_key_list = kdb_def_get_mkey_list; + } + if (lib->vftabl.fetch_master_key == NULL) { lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey; } @@ -208,6 +288,14 @@ kdb_setup_opt_functions(db_library lib) lib->vftabl.verify_master_key = krb5_def_verify_master_key; } + if (lib->vftabl.fetch_master_key_list == NULL) { + lib->vftabl.fetch_master_key_list = krb5_def_fetch_mkey_list; + } + + if (lib->vftabl.store_master_key_list == NULL) { + lib->vftabl.store_master_key_list = krb5_def_store_mkey_list; + } + if (lib->vftabl.dbe_search_enctype == NULL) { lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype; } @@ -1403,6 +1491,35 @@ krb5_db_set_mkey(krb5_context context, krb5_keyblock * key) } krb5_error_code +krb5_db_set_mkey_list(krb5_context kcontext, + krb5_keylist_node * keylist) +{ + krb5_error_code status = 0; + kdb5_dal_handle *dal_handle; + + if (kcontext->dal_handle == NULL) { + status = kdb_setup_lib_handle(kcontext); + if (status) { + goto clean_n_exit; + } + } + + dal_handle = kcontext->dal_handle; + status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); + if (status) { + goto clean_n_exit; + } + + status = dal_handle->lib_handle->vftabl.set_master_key_list(kcontext, keylist); + get_errmsg(kcontext, status); + + kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); + +clean_n_exit: + return status; +} + +krb5_error_code krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key) { krb5_error_code status = 0; @@ -1432,6 +1549,90 @@ krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key) } krb5_error_code +krb5_db_get_mkey_list(krb5_context kcontext, krb5_keylist_node ** keylist) +{ + krb5_error_code status = 0; + kdb5_dal_handle *dal_handle; + + if (kcontext->dal_handle == NULL) { + status = kdb_setup_lib_handle(kcontext); + if (status) { + goto clean_n_exit; + } + } + + dal_handle = kcontext->dal_handle; + status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); + if (status) { + goto clean_n_exit; + } + + /* Let's use temp key and copy it later to avoid memory problems + when freed by the caller. */ + status = dal_handle->lib_handle->vftabl.get_master_key_list(kcontext, keylist); + get_errmsg(kcontext, status); + kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); + +clean_n_exit: + return status; +} + +krb5_error_code +krb5_db_fetch_mkey_list(krb5_context context, + krb5_principal mname, + const krb5_keyblock * mkey, + krb5_kvno mkvno, + krb5_keylist_node **mkey_list) +{ + kdb5_dal_handle *dal_handle; + krb5_error_code status = 0; + + if (context->dal_handle == NULL) { + status = kdb_setup_lib_handle(context); + if (status) { + goto clean_n_exit; + } + } + + dal_handle = context->dal_handle; + status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); + if (status) { + goto clean_n_exit; + } + + status = dal_handle->lib_handle->vftabl.fetch_master_key_list(context, + mname, + mkey, + mkvno, + mkey_list); + get_errmsg(context, status); + kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); + + if (status) { + goto clean_n_exit; + } + +clean_n_exit: + return status; +} + +krb5_error_code +krb5_db_free_mkey_list(krb5_context context, + krb5_keylist_node *mkey_list) +{ + krb5_keylist_node *cur, *prev; + + for (cur = mkey_list; cur != NULL;) { + prev = cur; + cur = cur->next; + krb5_free_keyblock_contents(context, &prev->keyblock); + krb5_xfree(prev); + } + + return 0; +} + +krb5_error_code krb5_db_store_master_key(krb5_context kcontext, char *keyfile, krb5_principal mname, @@ -1466,6 +1667,41 @@ krb5_db_store_master_key(krb5_context kcontext, return status; } +krb5_error_code +krb5_db_store_master_key_list(krb5_context kcontext, + char *keyfile, + krb5_principal mname, + krb5_keylist_node *keylist, + char *master_pwd) +{ + krb5_error_code status = 0; + kdb5_dal_handle *dal_handle; + + if (kcontext->dal_handle == NULL) { + status = kdb_setup_lib_handle(kcontext); + if (status) { + goto clean_n_exit; + } + } + + dal_handle = kcontext->dal_handle; + status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); + if (status) { + goto clean_n_exit; + } + + status = dal_handle->lib_handle->vftabl.store_master_key_list(kcontext, + keyfile, + mname, + keylist, + master_pwd); + get_errmsg(kcontext, status); + kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); + + clean_n_exit: + return status; +} + char *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1; char *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2; @@ -1534,7 +1770,7 @@ krb5_db_fetch_mkey(krb5_context context, if (!salt) free(scratch.data); - memset(password, 0, sizeof(password)); /* erase it */ + zap(password, sizeof(password)); /* erase it */ } else { kdb5_dal_handle *dal_handle; @@ -1552,7 +1788,9 @@ krb5_db_fetch_mkey(krb5_context context, goto clean_n_exit; } - tmp_key.enctype = key->enctype; + /* get the enctype from the stash */ + tmp_key.enctype = ENCTYPE_UNKNOWN; + retval = dal_handle->lib_handle->vftabl.fetch_master_key(context, mname, &tmp_key, @@ -1579,7 +1817,7 @@ krb5_db_fetch_mkey(krb5_context context, clean_n_exit: if (tmp_key.contents) { - memset(tmp_key.contents, 0, tmp_key.length); + zap(tmp_key.contents, tmp_key.length); krb5_db_free(context, tmp_key.contents); } return retval; @@ -1618,6 +1856,163 @@ krb5_db_verify_master_key(krb5_context kcontext, return status; } +krb5_error_code +krb5_dbe_fetch_act_key_list(krb5_context context, + krb5_principal princ, + krb5_actkvno_node **act_key_list) +{ + krb5_error_code retval = 0; + krb5_db_entry entry; + int nprinc; + krb5_boolean more; + + if (act_key_list == NULL) + return (EINVAL); + + nprinc = 1; + if ((retval = krb5_db_get_principal(context, princ, &entry, + &nprinc, &more))) { + return (retval); + } + + if (nprinc != 1) { + if (nprinc) { + krb5_db_free_principal(context, &entry, nprinc); + } + return(KRB5_KDB_NOMASTERKEY); + } else if (more) { + krb5_db_free_principal(context, &entry, nprinc); + return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); + } + + retval = krb5_dbe_lookup_actkvno(context, &entry, act_key_list); + + if (*act_key_list == NULL) { + krb5_actkvno_node *tmp_actkvno; + krb5_timestamp now; + /* + * for mkey princ entries without KRB5_TL_ACTKVNO data provide a default + */ + + if ((retval = krb5_timeofday(context, &now))) + return (retval); + + tmp_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node)); + if (tmp_actkvno == NULL) + return (ENOMEM); + + memset(tmp_actkvno, 0, sizeof(krb5_actkvno_node)); + tmp_actkvno->act_time = now; + /* use most current key */ + tmp_actkvno->act_kvno = entry.key_data[0].key_data_kvno; + + *act_key_list = tmp_actkvno; + } + + krb5_db_free_principal(context, &entry, nprinc); + return retval; +} + +/* + * Locates the "active" mkey used when encrypting a princ's keys. Note, the + * caller must not free the output act_mkey. + */ + +krb5_error_code +krb5_dbe_find_act_mkey(krb5_context context, + krb5_keylist_node *mkey_list, + krb5_actkvno_node *act_mkey_list, + krb5_kvno *act_kvno, + krb5_keyblock **act_mkey) +{ + krb5_kvno tmp_act_kvno; + krb5_error_code retval; + krb5_keylist_node *cur_keyblock = mkey_list; + krb5_actkvno_node *prev_actkvno, *cur_actkvno; + krb5_timestamp now; + krb5_boolean found = FALSE; + + if ((retval = krb5_timeofday(context, &now))) + return (retval); + + /* + * The list should be sorted in time, early to later so if the first entry + * is later than now, this is a problem + */ + if (act_mkey_list->act_time > now) { + return (KRB5_KDB_NOACTMASTERKEY); + } + + /* find the most current entry <= now */ + for (prev_actkvno = cur_actkvno = act_mkey_list; cur_actkvno != NULL; + prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) { + + if (cur_actkvno->act_time == now) { + tmp_act_kvno = cur_actkvno->act_kvno; + found = TRUE; + break; + } else if (cur_actkvno->act_time > now && prev_actkvno->act_time <= now) { + tmp_act_kvno = prev_actkvno->act_kvno; + found = TRUE; + break; + } + } + + if (!found) { + /* + * The end of the list was encountered and all entries are < now so use + * the latest entry. + */ + if (prev_actkvno->act_time <= now) { + tmp_act_kvno = prev_actkvno->act_kvno; + } else { + /* XXX this shouldn't happen */ + return (KRB5_KDB_NOACTMASTERKEY); + } + } + + while (cur_keyblock && cur_keyblock->kvno != tmp_act_kvno) + cur_keyblock = cur_keyblock->next; + + if (cur_keyblock) { + *act_mkey = &cur_keyblock->keyblock; + if (act_kvno != NULL) + *act_kvno = tmp_act_kvno; + return (0); + } else { + return (KRB5_KDB_NO_MATCHING_KEY); + } +} + +/* + * Locates the mkey used to protect a princ's keys. Note, the caller must not + * free the output key. + */ +krb5_error_code +krb5_dbe_find_mkey(krb5_context context, + krb5_keylist_node * mkey_list, + krb5_db_entry * entry, + krb5_keyblock ** mkey) +{ + krb5_kvno mkvno; + krb5_error_code retval; + krb5_keylist_node *cur_keyblock = mkey_list; + + retval = krb5_dbe_lookup_mkvno(context, entry, &mkvno); + if (retval) + return (retval); + + while (cur_keyblock && cur_keyblock->kvno != mkvno) + cur_keyblock = cur_keyblock->next; + + if (cur_keyblock) { + *mkey = &cur_keyblock->keyblock; + return (0); + } else { + return (KRB5_KDB_NO_MATCHING_KEY); + } +} + void * krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size) { @@ -1886,6 +2281,347 @@ krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ) } krb5_error_code +krb5_dbe_lookup_mkvno(krb5_context context, + krb5_db_entry *entry, + krb5_kvno *mkvno) +{ + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int16 tmp; + + tl_data.tl_data_type = KRB5_TL_MKVNO; + + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return (code); + + if (tl_data.tl_data_length == 0) { + *mkvno = 1; /* default for princs that lack the KRB5_TL_MKVNO data */ + return (0); + } else if (tl_data.tl_data_length != 2) { + return (KRB5_KDB_TRUNCATED_RECORD); + } + + krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp); + *mkvno = (krb5_kvno) tmp; + return (0); +} + +krb5_error_code +krb5_dbe_update_mkvno(krb5_context context, + krb5_db_entry * entry, + krb5_kvno mkvno) +{ + krb5_tl_data tl_data; + krb5_octet buf[2]; /* this is the encoded size of an int16 */ + krb5_int16 tmp_kvno = (krb5_int16) mkvno; + + tl_data.tl_data_type = KRB5_TL_MKVNO; + tl_data.tl_data_length = sizeof(buf); + krb5_kdb_encode_int16(tmp_kvno, buf); + tl_data.tl_data_contents = buf; + + return (krb5_dbe_update_tl_data(context, entry, &tl_data)); +} + +krb5_error_code +krb5_dbe_lookup_mkey_aux(krb5_context context, + krb5_db_entry * entry, + krb5_mkey_aux_node ** mkey_aux_data_list) +{ + krb5_tl_data tl_data; + krb5_int16 version; + krb5_mkey_aux_node *head_data = NULL, *new_data = NULL, + *prev_data = NULL; + krb5_octet *curloc; /* current location pointer */ + krb5_error_code code; + + tl_data.tl_data_type = KRB5_TL_MKEY_AUX; + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return (code); + + if (tl_data.tl_data_contents == NULL) { + *mkey_aux_data_list = NULL; + return (0); + } else { + /* get version to determine how to parse the data */ + krb5_kdb_decode_int16(tl_data.tl_data_contents, version); + if (version == 1) { + /* variable size, must be at least 10 bytes */ + if (tl_data.tl_data_length < 10) + return (KRB5_KDB_TRUNCATED_RECORD); + + /* curloc points to first tuple entry in the tl_data_contents */ + curloc = tl_data.tl_data_contents + sizeof(version); + + while (curloc < (tl_data.tl_data_contents + tl_data.tl_data_length)) { + + new_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); + if (new_data == NULL) { + krb5_dbe_free_mkey_aux_list(context, head_data); + return (ENOMEM); + } + memset(new_data, 0, sizeof(krb5_mkey_aux_node)); + + krb5_kdb_decode_int16(curloc, new_data->mkey_kvno); + curloc += sizeof(krb5_ui_2); + krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_kvno); + curloc += sizeof(krb5_ui_2); + krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_type[0]); + curloc += sizeof(krb5_ui_2); + krb5_kdb_decode_int16(curloc, new_data->latest_mkey.key_data_length[0]); + curloc += sizeof(krb5_ui_2); + + new_data->latest_mkey.key_data_contents[0] = (krb5_octet *) + malloc(new_data->latest_mkey.key_data_length[0]); + + if (new_data->latest_mkey.key_data_contents[0] == NULL) { + krb5_dbe_free_mkey_aux_list(context, head_data); + return (ENOMEM); + } + memcpy(new_data->latest_mkey.key_data_contents[0], curloc, + new_data->latest_mkey.key_data_length[0]); + curloc += new_data->latest_mkey.key_data_length[0]; + + /* always using key data ver 1 for mkeys */ + new_data->latest_mkey.key_data_ver = 1; + + new_data->next = NULL; + if (prev_data != NULL) + prev_data->next = new_data; + else + head_data = new_data; + prev_data = new_data; + } + } else { + krb5_set_error_message(context, KRB5_KDB_BAD_VERSION, + "Illegal version number for KRB5_TL_MKEY_AUX %d\n", + version); + return (KRB5_KDB_BAD_VERSION); + } + } + *mkey_aux_data_list = head_data; + return (0); +} + +#if KRB5_TL_MKEY_AUX_VER == 1 +krb5_error_code +krb5_dbe_update_mkey_aux(krb5_context context, + krb5_db_entry * entry, + krb5_mkey_aux_node * mkey_aux_data_list) +{ + krb5_tl_data tl_data; + krb5_int16 version, tmp_kvno; + unsigned char *nextloc; + krb5_mkey_aux_node *aux_data_entry; + + if (!mkey_aux_data_list) { + /* delete the KRB5_TL_MKEY_AUX from the entry */ + krb5_dbe_delete_tl_data(context, entry, KRB5_TL_MKEY_AUX); + return (0); + } + + memset(&tl_data, 0, sizeof(tl_data)); + tl_data.tl_data_type = KRB5_TL_MKEY_AUX; + /* + * determine out how much space to allocate. Note key_data_ver not stored + * as this is hard coded to one and is accounted for in + * krb5_dbe_lookup_mkey_aux. + */ + tl_data.tl_data_length = sizeof(version); /* version */ + for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; + aux_data_entry = aux_data_entry->next) { + + tl_data.tl_data_length += (sizeof(krb5_ui_2) + /* mkey_kvno */ + sizeof(krb5_ui_2) + /* latest_mkey kvno */ + sizeof(krb5_ui_2) + /* latest_mkey enctype */ + sizeof(krb5_ui_2) + /* latest_mkey length */ + aux_data_entry->latest_mkey.key_data_length[0]); + } + + tl_data.tl_data_contents = (krb5_octet *) malloc(tl_data.tl_data_length); + if (tl_data.tl_data_contents == NULL) { + return (ENOMEM); + } + + nextloc = tl_data.tl_data_contents; + version = KRB5_TL_MKEY_AUX_VER; + krb5_kdb_encode_int16(version, nextloc); + nextloc += sizeof(krb5_ui_2); + + for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; + aux_data_entry = aux_data_entry->next) { + + tmp_kvno = (krb5_int16) aux_data_entry->mkey_kvno; + krb5_kdb_encode_int16(tmp_kvno, nextloc); + nextloc += sizeof(krb5_ui_2); + + krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_kvno, + nextloc); + nextloc += sizeof(krb5_ui_2); + + krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_type[0], + nextloc); + nextloc += sizeof(krb5_ui_2); + + krb5_kdb_encode_int16(aux_data_entry->latest_mkey.key_data_length[0], + nextloc); + nextloc += sizeof(krb5_ui_2); + + if (aux_data_entry->latest_mkey.key_data_length[0] > 0) { + memcpy(nextloc, aux_data_entry->latest_mkey.key_data_contents[0], + aux_data_entry->latest_mkey.key_data_length[0]); + nextloc += aux_data_entry->latest_mkey.key_data_length[0]; + } + } + + return (krb5_dbe_update_tl_data(context, entry, &tl_data)); +} +#endif /* KRB5_TL_MKEY_AUX_VER == 1 */ + +#if KRB5_TL_ACTKVNO_VER == 1 +/* + * If version of the KRB5_TL_ACTKVNO data is KRB5_TL_ACTKVNO_VER == 1 then size of + * a actkvno tuple {act_kvno, act_time} entry is: + */ +#define ACTKVNO_TUPLE_SIZE (sizeof(krb5_int16) + sizeof(krb5_int32)) +#define act_kvno(cp) (cp) /* return pointer to start of act_kvno data */ +#define act_time(cp) ((cp) + sizeof(krb5_int16)) /* return pointer to start of act_time data */ +#endif + +krb5_error_code +krb5_dbe_lookup_actkvno(krb5_context context, + krb5_db_entry *entry, + krb5_actkvno_node **actkvno_list) +{ + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int16 version, tmp_kvno; + krb5_actkvno_node *head_data = NULL, *new_data = NULL, *prev_data = NULL; + unsigned int num_actkvno, i; + krb5_octet *next_tuple; + + memset(&tl_data, 0, sizeof(tl_data)); + tl_data.tl_data_type = KRB5_TL_ACTKVNO; + + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return (code); + + if (tl_data.tl_data_contents == NULL) { + *actkvno_list = NULL; + return (0); + } else { + /* get version to determine how to parse the data */ + krb5_kdb_decode_int16(tl_data.tl_data_contents, version); + if (version == 1) { + + /* variable size, must be at least 8 bytes */ + if (tl_data.tl_data_length < 8) + return (KRB5_KDB_TRUNCATED_RECORD); + + /* + * Find number of tuple entries, remembering to account for version + * field. + */ + num_actkvno = (tl_data.tl_data_length - sizeof(version)) / + ACTKVNO_TUPLE_SIZE; + prev_data = NULL; + /* next_tuple points to first tuple entry in the tl_data_contents */ + next_tuple = tl_data.tl_data_contents + sizeof(version); + for (i = 0; i < num_actkvno; i++) { + new_data = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node)); + if (new_data == NULL) { + krb5_dbe_free_actkvno_list(context, head_data); + return (ENOMEM); + } + memset(new_data, 0, sizeof(krb5_actkvno_node)); + + /* using tmp_kvno to avoid type mismatch */ + krb5_kdb_decode_int16(act_kvno(next_tuple), tmp_kvno); + new_data->act_kvno = (krb5_kvno) tmp_kvno; + krb5_kdb_decode_int32(act_time(next_tuple), new_data->act_time); + + if (prev_data != NULL) + prev_data->next = new_data; + else + head_data = new_data; + prev_data = new_data; + next_tuple += ACTKVNO_TUPLE_SIZE; + } + } else { + krb5_set_error_message (context, KRB5_KDB_BAD_VERSION, + "Illegal version number for KRB5_TL_ACTKVNO %d\n", + version); + return (KRB5_KDB_BAD_VERSION); + } + } + *actkvno_list = head_data; + return (0); +} + +/* + * Add KRB5_TL_ACTKVNO TL data entries to krb5_db_entry *entry + */ +#if KRB5_TL_ACTKVNO_VER == 1 +krb5_error_code +krb5_dbe_update_actkvno(krb5_context context, + krb5_db_entry *entry, + const krb5_actkvno_node *actkvno_list) +{ + krb5_error_code retval = 0; + krb5_int16 version, tmp_kvno; + krb5_tl_data new_tl_data; + unsigned char *nextloc; + const krb5_actkvno_node *cur_actkvno; + krb5_octet *tmpptr; + + if (actkvno_list == NULL) { + return (EINVAL); + } + + memset(&new_tl_data, 0, sizeof(new_tl_data)); + /* allocate initial KRB5_TL_ACTKVNO tl_data entry */ + new_tl_data.tl_data_length = sizeof(version); + new_tl_data.tl_data_contents = (krb5_octet *) malloc(new_tl_data.tl_data_length); + if (new_tl_data.tl_data_contents == NULL) + return (ENOMEM); + + /* add the current version # for the data format used for KRB5_TL_ACTKVNO */ + version = KRB5_TL_ACTKVNO_VER; + krb5_kdb_encode_int16(version, (unsigned char *) new_tl_data.tl_data_contents); + + for (cur_actkvno = actkvno_list; cur_actkvno != NULL; + cur_actkvno = cur_actkvno->next) { + + new_tl_data.tl_data_length += ACTKVNO_TUPLE_SIZE; + tmpptr = realloc(new_tl_data.tl_data_contents, new_tl_data.tl_data_length); + if (tmpptr == NULL) { + free(new_tl_data.tl_data_contents); + return (ENOMEM); + } else { + new_tl_data.tl_data_contents = tmpptr; + } + + /* + * Using realloc so tl_data_contents is required to correctly calculate + * next location to store new tuple. + */ + nextloc = new_tl_data.tl_data_contents + new_tl_data.tl_data_length - ACTKVNO_TUPLE_SIZE; + /* using tmp_kvno to avoid type mismatch issues */ + tmp_kvno = (krb5_int16) cur_actkvno->act_kvno; + krb5_kdb_encode_int16(tmp_kvno, nextloc); + nextloc += sizeof(krb5_ui_2); + krb5_kdb_encode_int32((krb5_ui_4)cur_actkvno->act_time, nextloc); + } + + new_tl_data.tl_data_type = KRB5_TL_ACTKVNO; + retval = krb5_dbe_update_tl_data(context, entry, &new_tl_data); + free(new_tl_data.tl_data_contents); + + return (retval); +} +#endif /* KRB5_TL_ACTKVNO_VER == 1 */ + +krb5_error_code krb5_dbe_update_last_pwd_change(context, entry, stamp) krb5_context context; krb5_db_entry *entry; @@ -1903,6 +2639,44 @@ krb5_dbe_update_last_pwd_change(context, entry, stamp) } krb5_error_code +krb5_dbe_delete_tl_data(krb5_context context, + krb5_db_entry *entry, + krb5_int16 tl_data_type) +{ + krb5_tl_data *tl_data, *prev_tl_data, *free_tl_data; + + /* + * Find existing entries of the specified type and remove them from the + * entry's tl_data list. + */ + + for (prev_tl_data = tl_data = entry->tl_data; tl_data != NULL;) { + if (tl_data->tl_data_type == tl_data_type) { + if (tl_data == entry->tl_data) { + /* remove from head */ + entry->tl_data = tl_data->tl_data_next; + prev_tl_data = entry->tl_data; + } else if (tl_data->tl_data_next == NULL) { + /* remove from tail */ + prev_tl_data->tl_data_next = NULL; + } else { + /* remove in between */ + prev_tl_data->tl_data_next = tl_data->tl_data_next; + } + free_tl_data = tl_data; + tl_data = tl_data->tl_data_next; + krb5_dbe_free_tl_data(context, free_tl_data); + entry->n_tl_data--; + } else { + tl_data = tl_data->tl_data_next; + prev_tl_data = tl_data; + } + } + + return (0); +} + +krb5_error_code krb5_dbe_update_tl_data(context, entry, new_tl_data) krb5_context context; krb5_db_entry *entry; diff --git a/src/lib/kdb/kdb_cpw.c b/src/lib/kdb/kdb_cpw.c index a59d98e737..2062055d03 100644 --- a/src/lib/kdb/kdb_cpw.c +++ b/src/lib/kdb/kdb_cpw.c @@ -56,8 +56,8 @@ #include <stdio.h> #include <errno.h> -static int -get_key_data_kvno(context, count, data) +int +krb5_db_get_key_data_kvno(context, count, data) krb5_context context; int count; krb5_key_data * data; @@ -260,7 +260,8 @@ krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry) int i; /* First save the old keydata */ - kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); + kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; @@ -315,7 +316,8 @@ krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry) int i; /* First save the old keydata */ - kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); + kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; @@ -553,8 +555,8 @@ krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd, int i; /* First save the old keydata */ - old_kvno = get_key_data_kvno(context, db_entry->n_key_data, - db_entry->key_data); + old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; @@ -612,8 +614,8 @@ krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry) int i; /* First save the old keydata */ - old_kvno = get_key_data_kvno(context, db_entry->n_key_data, - db_entry->key_data); + old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; diff --git a/src/lib/kdb/kdb_default.c b/src/lib/kdb/kdb_default.c index f173e127df..df87916242 100644 --- a/src/lib/kdb/kdb_default.c +++ b/src/lib/kdb/kdb_default.c @@ -1,7 +1,7 @@ /* * lib/kdb/kdb_helper.c * - * Copyright 1995, 2008 by the Massachusetts Institute of Technology. + * Copyright 1995, 2009 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -25,6 +25,11 @@ * */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + #include "k5-int.h" #include "kdb.h" #include <string.h> @@ -133,12 +138,11 @@ krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap) #endif krb5_error_code -krb5_def_store_mkey(krb5_context context, - char *keyfile, - krb5_principal mname, - krb5_kvno kvno, - krb5_keyblock *key, - char *master_pwd) +krb5_def_store_mkey_list(krb5_context context, + char *keyfile, + krb5_principal mname, + krb5_keylist_node *keylist, + char *master_pwd) { krb5_error_code retval = 0; char defkeyfile[MAXPATHLEN+1]; @@ -199,12 +203,17 @@ krb5_def_store_mkey(krb5_context context, if (retval != 0) goto out; - memset((char *) &new_entry, 0, sizeof(new_entry)); - new_entry.principal = mname; - new_entry.key = *key; - new_entry.vno = kvno; + while (keylist && !retval) { + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = mname; + new_entry.key = keylist->keyblock; + new_entry.vno = keylist->kvno; + + retval = krb5_kt_add_entry(context, kt, &new_entry); + keylist = keylist->next; + } + krb5_kt_close(context, kt); - retval = krb5_kt_add_entry(context, kt, &new_entry); if (retval != 0) { /* delete tmp keyfile if it exists and an error occurrs */ if (stat(keyfile, &stb) >= 0) @@ -222,12 +231,27 @@ krb5_def_store_mkey(krb5_context context, out: if (tmp_ktname != NULL) free(tmp_ktname); - if (kt) - krb5_kt_close(context, kt); return retval; } +krb5_error_code +krb5_def_store_mkey(krb5_context context, + char *keyfile, + krb5_principal mname, + krb5_kvno kvno, + krb5_keyblock *key, + char *master_pwd) +{ + krb5_keylist_node list; + + list.kvno = kvno; + list.keyblock = *key; + list.next = NULL; + return krb5_def_store_mkey_list(context, keyfile, mname, &list, + master_pwd); +} + static krb5_error_code krb5_db_def_fetch_mkey_stash(krb5_context context, const char *keyfile, @@ -288,7 +312,7 @@ krb5_db_def_fetch_mkey_stash(krb5_context context, if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]), key->length, kf) != key->length) { retval = KRB5_KDB_CANTREAD_STORED; - memset(key->contents, 0, key->length); + zap(key->contents, key->length); free(key->contents); key->contents = 0; } else @@ -421,6 +445,9 @@ krb5_db_def_fetch_mkey(krb5_context context, } } +/* + * Note, this verifies that the input mkey is currently protecting all the mkeys + */ krb5_error_code krb5_def_verify_master_key(krb5_context context, krb5_principal mprinc, @@ -468,13 +495,160 @@ krb5_def_verify_master_key(krb5_context context, kvno, master_entry.key_data->key_data_kvno); } - memset((char *)tempkey.contents, 0, tempkey.length); + zap((char *)tempkey.contents, tempkey.length); free(tempkey.contents); krb5_db_free_principal(context, &master_entry, nprinc); return retval; } +krb5_error_code +krb5_def_fetch_mkey_list(krb5_context context, + krb5_principal mprinc, + const krb5_keyblock *mkey, + krb5_kvno mkvno, + krb5_keylist_node **mkeys_list) +{ + krb5_error_code retval; + krb5_db_entry master_entry; + int nprinc; + krb5_boolean more, found_key = FALSE; + krb5_keyblock cur_mkey; + krb5_keylist_node *mkey_list_head = NULL, **mkey_list_node; + krb5_key_data *key_data; + krb5_mkey_aux_node *mkey_aux_data_list, *aux_data_entry; + int i; + + if (mkeys_list == NULL) + return (EINVAL); + + memset(&cur_mkey, 0, sizeof(cur_mkey)); + + nprinc = 1; + if ((retval = krb5_db_get_principal(context, mprinc, + &master_entry, &nprinc, &more))) + return (retval); + + if (nprinc != 1) { + if (nprinc) + krb5_db_free_principal(context, &master_entry, nprinc); + return(KRB5_KDB_NOMASTERKEY); + } else if (more) { + krb5_db_free_principal(context, &master_entry, nprinc); + return (KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); + } + + /* + * Check if the input mkey is the latest key and if it isn't then find the + * latest mkey. + */ + + if (mkey->enctype == master_entry.key_data[0].key_data_type[0]) { + if (krb5_dbekd_decrypt_key_data(context, mkey, + &master_entry.key_data[0], + &cur_mkey, NULL) == 0) { + found_key = TRUE; + } + } + + if (!found_key) { + /* + * Note the mkvno may provide a hint as to which mkey_aux tuple to + * decrypt. + */ + if ((retval = krb5_dbe_lookup_mkey_aux(context, &master_entry, + &mkey_aux_data_list))) + goto clean_n_exit; + + /* mkvno may be 0 in some cases like keyboard and should be ignored */ + if (mkvno != 0) { + /* for performance sake, try decrypting with matching kvno */ + for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; + aux_data_entry = aux_data_entry->next) { + + if (aux_data_entry->mkey_kvno == mkvno) { + if (krb5_dbekd_decrypt_key_data(context, mkey, + &aux_data_entry->latest_mkey, + &cur_mkey, NULL) == 0) { + found_key = TRUE; + break; + } + } + } + } + if (!found_key) { + /* given the importance of acquiring the latest mkey, try brute force */ + for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL; + aux_data_entry = aux_data_entry->next) { + + if (mkey->enctype == aux_data_entry->latest_mkey.key_data_type[0] && + (krb5_dbekd_decrypt_key_data(context, mkey, + &aux_data_entry->latest_mkey, + &cur_mkey, NULL) == 0)) { + found_key = TRUE; + break; + } + } + if (found_key != TRUE) { + krb5_set_error_message (context, KRB5_KDB_BADMASTERKEY, + "Unable to decrypt latest master key with the provided master key\n"); + retval = KRB5_KDB_BADMASTERKEY; + goto clean_n_exit; + } + } + } + + /* + * Extract all the mkeys from master_entry using the most current mkey and + * create a mkey list for the mkeys field in kdc_realm_t. + */ + + mkey_list_head = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node)); + if (mkey_list_head == NULL) { + retval = ENOMEM; + goto clean_n_exit; + } + + memset(mkey_list_head, 0, sizeof(krb5_keylist_node)); + + /* Set mkey_list_head to the current mkey as an optimization. */ + /* mkvno may not be latest so ... */ + mkey_list_head->kvno = master_entry.key_data[0].key_data_kvno; + /* this is the latest clear mkey (avoids a redundant decrypt) */ + mkey_list_head->keyblock = cur_mkey; + + /* loop through any other master keys creating a list of krb5_keylist_nodes */ + mkey_list_node = &mkey_list_head->next; + for (i = 1; i < master_entry.n_key_data; i++) { + if (*mkey_list_node == NULL) { + /* *mkey_list_node points to next field of previous node */ + *mkey_list_node = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node)); + if (*mkey_list_node == NULL) { + retval = ENOMEM; + goto clean_n_exit; + } + memset(*mkey_list_node, 0, sizeof(krb5_keylist_node)); + } + key_data = &master_entry.key_data[i]; + retval = krb5_dbekd_decrypt_key_data(context, &cur_mkey, + key_data, + &((*mkey_list_node)->keyblock), + NULL); + if (retval) + goto clean_n_exit; + + (*mkey_list_node)->kvno = key_data->key_data_kvno; + mkey_list_node = &((*mkey_list_node)->next); + } + + *mkeys_list = mkey_list_head; + +clean_n_exit: + krb5_db_free_principal(context, &master_entry, nprinc); + if (retval != 0) + krb5_dbe_free_key_list(context, mkey_list_head); + return retval; +} krb5_error_code kdb_def_set_mkey ( krb5_context kcontext, char *pwd, @@ -491,6 +665,20 @@ krb5_error_code kdb_def_get_mkey ( krb5_context kcontext, return 0; } +krb5_error_code kdb_def_set_mkey_list ( krb5_context kcontext, + krb5_keylist_node *keylist ) +{ + /* printf("default set master key\n"); */ + return 0; +} + +krb5_error_code kdb_def_get_mkey_list ( krb5_context kcontext, + krb5_keylist_node **keylist ) +{ + /* printf("default get master key\n"); */ + return 0; +} + krb5_error_code krb5_def_promote_db (krb5_context kcontext, char *s, char **args) { diff --git a/src/lib/kdb/keytab.c b/src/lib/kdb/keytab.c index 632c9270dd..47626f1521 100644 --- a/src/lib/kdb/keytab.c +++ b/src/lib/kdb/keytab.c @@ -123,6 +123,7 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry) krb5_keytab_entry * entry; { krb5_context context; + krb5_keylist_node * master_keylist; krb5_keyblock * master_key; krb5_error_code kerror = 0; krb5_key_data * key_data; @@ -162,7 +163,11 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry) } /* match key */ - kerror = krb5_db_get_mkey(context, &master_key); + kerror = krb5_db_get_mkey_list(context, &master_keylist); + if (kerror) + goto error; + + kerror = krb5_dbe_find_mkey(context, master_keylist, &db_entry, &master_key); if (kerror) goto error; diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index cbd8711811..c0b161f614 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -6,10 +6,14 @@ krb5_db_create krb5_db_delete_principal krb5_db_destroy krb5_db_fetch_mkey +krb5_db_fetch_mkey_list +krb5_db_free_mkey_list krb5_db_fini krb5_db_free_principal krb5_db_get_age +krb5_db_get_key_data_kvno krb5_db_get_mkey +krb5_db_get_mkey_list krb5_db_get_context krb5_db_get_principal krb5_db_get_principal_ext @@ -19,21 +23,36 @@ krb5_db_lock krb5_db_put_principal krb5_db_set_context krb5_db_set_mkey +krb5_db_set_mkey_list krb5_db_setup_mkey_name krb5_db_unlock krb5_db_store_master_key +krb5_db_store_master_key_list krb5_db_verify_master_key krb5_dbe_apw krb5_dbe_ark krb5_dbe_cpw krb5_dbe_create_key_data krb5_dbe_crk +krb5_dbe_find_act_mkey +krb5_dbe_fetch_act_key_list krb5_dbe_find_enctype +krb5_dbe_find_mkey +krb5_dbe_free_actkvno_list +krb5_dbe_free_key_data_contents +krb5_dbe_free_mkey_aux_list +krb5_dbe_free_key_list krb5_dbe_lookup_last_pwd_change +krb5_dbe_lookup_actkvno +krb5_dbe_lookup_mkey_aux +krb5_dbe_lookup_mkvno krb5_dbe_lookup_mod_princ_data krb5_dbe_lookup_tl_data krb5_dbe_search_enctype +krb5_dbe_update_actkvno krb5_dbe_update_last_pwd_change +krb5_dbe_update_mkey_aux +krb5_dbe_update_mkvno krb5_dbe_update_mod_princ_data krb5_dbe_update_tl_data krb5_dbekd_decrypt_key_data @@ -52,6 +71,7 @@ krb5_db_iter_policy krb5_db_delete_policy krb5_db_free_policy krb5_def_store_mkey +krb5_def_store_mkey_list krb5_db_promote ulog_map ulog_set_role diff --git a/src/lib/krb5/error_tables/kdb5_err.et b/src/lib/krb5/error_tables/kdb5_err.et index b15af8c873..cd7214d9b6 100644 --- a/src/lib/krb5/error_tables/kdb5_err.et +++ b/src/lib/krb5/error_tables/kdb5_err.et @@ -57,6 +57,9 @@ ec KRB5_KDB_BADMASTERKEY, "Master key does not match database" ec KRB5_KDB_INVALIDKEYSIZE, "Key size in database is invalid" ec KRB5_KDB_CANTREAD_STORED, "Cannot find/read stored master key" ec KRB5_KDB_BADSTORED_MKEY, "Stored master key is corrupted" +ec KRB5_KDB_NOACTMASTERKEY, "Cannot find active master key" +ec KRB5_KDB_KVNONOMATCH, "KVNO of new master key does not match expected value" +ec KRB5_KDB_STORED_MKEY_NOTCURRENT, "Stored master key is not current" ec KRB5_KDB_CANTLOCK_DB, "Insufficient access to lock database" diff --git a/src/plugins/kdb/db2/db2_exp.c b/src/plugins/kdb/db2/db2_exp.c index 123d20afb1..5c81624681 100644 --- a/src/plugins/kdb/db2/db2_exp.c +++ b/src/plugins/kdb/db2/db2_exp.c @@ -59,7 +59,7 @@ static char *_csrc = "@(#) %filespec: db2_exp.c~5 % (%full_filespec: db2_exp.c~ locking code into the top and bottom of each referenced function won't do. (We aren't doing recursive locks, currently.) */ -static k5_mutex_t *krb5_db2_mutex; +k5_mutex_t *krb5_db2_mutex; #define WRAP(NAME,TYPE,ARGLIST,ARGNAMES,ERROR_RESULT) \ static TYPE wrap_##NAME ARGLIST \ @@ -178,21 +178,21 @@ WRAP_VOID (krb5_db2_free_policy, ( krb5_context kcontext, osa_policy_ent_t entry ), (kcontext, entry)); -WRAP (krb5_db2_alloc, void *, - ( krb5_context kcontext, - void *ptr, - size_t size ), - (kcontext, ptr, size), NULL); -WRAP_VOID (krb5_db2_free, - ( krb5_context kcontext, void *ptr ), - (kcontext, ptr)); - WRAP_K (krb5_db2_set_master_key_ext, ( krb5_context kcontext, char *pwd, krb5_keyblock *key), (kcontext, pwd, key)); WRAP_K (krb5_db2_db_get_mkey, ( krb5_context context, krb5_keyblock **key), (context, key)); + +WRAP_K (krb5_db2_db_set_mkey_list, + ( krb5_context kcontext, krb5_keylist_node *keylist), + (kcontext, keylist)); + +WRAP_K (krb5_db2_db_get_mkey_list, + ( krb5_context context, krb5_keylist_node **keylist), + (context, keylist)); + WRAP_K (krb5_db2_promote_db, ( krb5_context kcontext, char *conf_section, char **db_args ), (kcontext, conf_section, db_args)); @@ -248,11 +248,13 @@ kdb_vftabl kdb_function_table = { /* db_free_supported_realms */ NULL, /* errcode_2_string */ NULL, /* release_errcode_string */ NULL, - /* db_alloc */ wrap_krb5_db2_alloc, - /* db_free */ wrap_krb5_db2_free, + /* db_alloc */ krb5_db2_alloc, + /* db_free */ krb5_db2_free, /* set_master_key */ wrap_krb5_db2_set_master_key_ext, /* get_master_key */ wrap_krb5_db2_db_get_mkey, - /* blah blah blah */ 0,0,0,0,0,0, + /* set_master_key_list */ wrap_krb5_db2_db_set_mkey_list, + /* get_master_key_list */ wrap_krb5_db2_db_get_mkey_list, + /* blah blah blah */ 0,0,0,0,0,0,0,0, /* promote_db */ wrap_krb5_db2_promote_db, 0,0,0, }; diff --git a/src/plugins/kdb/db2/kdb_db2.c b/src/plugins/kdb/db2/kdb_db2.c index 704e47d6b2..90c893305f 100644 --- a/src/plugins/kdb/db2/kdb_db2.c +++ b/src/plugins/kdb/db2/kdb_db2.c @@ -431,6 +431,37 @@ krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key) return 0; } +krb5_error_code +krb5_db2_db_set_mkey_list(krb5_context context, krb5_keylist_node *key_list) +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + if (!k5db2_inited(context)) + return (KRB5_KDB_DBNOTINITED); + + dal_handle = context->dal_handle; + db_ctx = dal_handle->db_context; + db_ctx->db_master_key_list = key_list; + return 0; +} + +krb5_error_code +krb5_db2_db_get_mkey_list(krb5_context context, krb5_keylist_node **key_list) +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + if (!k5db2_inited(context)) + return (KRB5_KDB_DBNOTINITED); + + dal_handle = context->dal_handle; + db_ctx = dal_handle->db_context; + *key_list = db_ctx->db_master_key_list; + + return 0; +} + /* * Set the "name" of the current database to some alternate value. * @@ -1171,8 +1202,19 @@ krb5_db2_db_iterate_ext(krb5_context context, retval = krb5_decode_princ_contents(context, &contdata, &entries); if (retval) break; + retval = k5_mutex_unlock(krb5_db2_mutex); + if (retval) + break; retval = (*func) (func_arg, &entries); krb5_dbe_free_contents(context, &entries); + /* Note: If re-locking fails, the wrapper in db2_exp.c will + still try to unlock it again. That would be a bug. Fix + when integrating the locking better. */ + if (retval) { + (void) k5_mutex_lock(krb5_db2_mutex); + break; + } + retval = k5_mutex_lock(krb5_db2_mutex); if (retval) break; if (!recursive) { diff --git a/src/plugins/kdb/db2/kdb_db2.h b/src/plugins/kdb/db2/kdb_db2.h index d6cb1e8817..640c4d62d3 100644 --- a/src/plugins/kdb/db2/kdb_db2.h +++ b/src/plugins/kdb/db2/kdb_db2.h @@ -42,7 +42,8 @@ typedef struct _krb5_db2_context { int db_locks_held; /* Number of times locked */ int db_lock_mode; /* Last lock mode, e.g. greatest*/ krb5_boolean db_nb_locks; /* [Non]Blocking lock modes */ - krb5_keyblock *db_master_key; /* Master key of database */ + krb5_keyblock *db_master_key; /* Master key of database */ + krb5_keylist_node *db_master_key_list; /* Master key list of database */ osa_adb_policy_t policy_db; krb5_boolean tempdb; } krb5_db2_context; @@ -121,6 +122,13 @@ krb5_db2_db_set_mkey( krb5_context context, krb5_error_code krb5_db2_db_get_mkey( krb5_context context, krb5_keyblock **key); +krb5_error_code +krb5_db2_db_set_mkey_list( krb5_context context, + krb5_keylist_node *keylist); + +krb5_error_code +krb5_db2_db_get_mkey_list( krb5_context context, + krb5_keylist_node **keylist); krb5_error_code krb5_db2_db_put_principal( krb5_context context, @@ -208,4 +216,7 @@ krb5_error_code krb5_db2_delete_policy ( krb5_context kcontext, void krb5_db2_free_policy( krb5_context kcontext, osa_policy_ent_t entry ); +/* Thread-safety wrapper slapped on top of original implementation. */ +extern k5_mutex_t *krb5_db2_mutex; + #endif /* KRB5_KDB_DB2_H */ diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c index eaeef2a8c4..dcfe93cf9e 100644 --- a/src/plugins/kdb/ldap/ldap_exp.c +++ b/src/plugins/kdb/ldap/ldap_exp.c @@ -78,10 +78,14 @@ kdb_vftabl kdb_function_table = { /* optional functions */ /* set_master_key */ krb5_ldap_set_mkey, /* get_master_key */ krb5_ldap_get_mkey, + /* set_master_key_list */ krb5_ldap_set_mkey_list, + /* get_master_key_list */ krb5_ldap_get_mkey_list, /* setup_master_key_name */ NULL, /* store_master_key */ NULL, /* fetch_master_key */ NULL /* krb5_ldap_fetch_mkey */, /* verify_master_key */ NULL /* krb5_ldap_verify_master_key */, + /* fetch_master_key_list */ NULL, + /* store_master_key_list */ NULL, /* Search enc type */ NULL, /* Change pwd */ NULL diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c index c13d967108..60d9e25f72 100644 --- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c +++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c @@ -2379,6 +2379,8 @@ kdb_ldap_create_principal (context, princ, op, pblock) krb5_ldap_context *ldap_context=NULL; struct iterate_args iargs; krb5_data *pdata; + krb5_timestamp now; + krb5_actkvno_node actkvno; if ((pblock == NULL) || (context == NULL)) { retval = EINVAL; @@ -2425,14 +2427,12 @@ kdb_ldap_create_principal (context, princ, op, pblock) entry.tl_data = tl_data; entry.n_tl_data += 1; /* Set the creator's name */ - { - krb5_timestamp now; - if ((retval = krb5_timeofday(context, &now))) - goto cleanup; - if ((retval = krb5_dbe_update_mod_princ_data_new(context, &entry, - now, &db_create_princ))) - goto cleanup; - } + if ((retval = krb5_timeofday(context, &now))) + goto cleanup; + if ((retval = krb5_dbe_update_mod_princ_data_new(context, &entry, + now, &db_create_princ))) + goto cleanup; + entry.attributes = pblock->flags; entry.max_life = pblock->max_life; entry.max_renewable_life = pblock->max_rlife; @@ -2507,6 +2507,17 @@ kdb_ldap_create_principal (context, princ, op, pblock) if (retval) { goto cleanup; } + /* + * There should always be at least one "active" mkey so creating the + * KRB5_TL_ACTKVNO entry now so the initial mkey is active. + */ + actkvno.next = NULL; + actkvno.act_kvno = kvno; + actkvno.act_time = now; + retval = krb5_dbe_update_actkvno(context, &entry, &actkvno); + if (retval) + goto cleanup; + break; case NULL_KEY: diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h index 74bf4b17e9..802ab0fc36 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h +++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h @@ -267,6 +267,12 @@ krb5_error_code krb5_ldap_set_mkey(krb5_context, char *, krb5_keyblock *); krb5_error_code +krb5_ldap_get_mkey_list (krb5_context context, krb5_keylist_node **key_list); + +krb5_error_code +krb5_ldap_set_mkey_list(krb5_context, krb5_keylist_node *); + +krb5_error_code krb5_ldap_create(krb5_context , char *, char **); krb5_error_code diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c index d4c6ac832b..f8e1d4415e 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_xdr.c @@ -148,6 +148,51 @@ krb5_dbe_lookup_last_pwd_change(context, entry, stamp) return(0); } +#if 0 /************** Begin IFDEF'ed OUT *******************************/ +krb5_error_code +krb5_dbe_lookup_mkvno(krb5_context context, + krb5_db_entry *entry, + krb5_kvno *mkvno) +{ + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int16 tmp; + + tl_data.tl_data_type = KRB5_TL_MKVNO; + + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return (code); + + /* XXX need to think about this */ + if (tl_data.tl_data_length != 2) { + *mkvno = 0; + return (0); + } + + /* XXX this needs to be the inverse of how this is encoded */ + krb5_kdb_decode_int16(tl_data.tl_data_contents, tmp); + + *mkvno = (krb5_kvno) tmp; + + return (0); +} + +krb5_error_code +krb5_dbe_update_mkvno(krb5_context context, + krb5_db_entry * entry, + krb5_kvno mkvno) +{ + krb5_tl_data tl_data; + krb5_octet buf[2]; /* this is the encoded size of an int16 */ + + tl_data.tl_data_type = KRB5_TL_MKVNO; + tl_data.tl_data_length = sizeof(buf); + krb5_kdb_encode_int16((krb5_int16) mkvno, buf); + tl_data.tl_data_contents = buf; + + return (krb5_dbe_update_tl_data(context, entry, &tl_data)); +} +#endif /**************** END IFDEF'ed OUT *******************************/ /* it seems odd that there's no function to remove a tl_data, but if I need one, I'll add one */ diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c index 9a364192ad..6da0806641 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_fetch_mkey.c @@ -98,3 +98,45 @@ krb5_ldap_set_mkey (context, pwd, key) memcpy(r_params->mkey.contents, key->contents, key->length); return 0; } + +krb5_error_code +krb5_ldap_get_mkey_list (krb5_context context, krb5_keylist_node **key_list) + +{ + kdb5_dal_handle *dal_handle=NULL; + krb5_ldap_context *ldap_context=NULL; + + /* Clear the global error string */ + krb5_clear_error_message(context); + + dal_handle = context->dal_handle; + ldap_context = (krb5_ldap_context *) dal_handle->db_context; + + if (ldap_context == NULL || ldap_context->lrparams == NULL) + return KRB5_KDB_DBNOTINITED; + + *key_list = ldap_context->lrparams->mkey_list; + return 0; +} + +krb5_error_code +krb5_ldap_set_mkey_list(krb5_context context, krb5_keylist_node *key_list) +{ + kdb5_dal_handle *dal_handle=NULL; + krb5_ldap_context *ldap_context=NULL; + krb5_ldap_realm_params *r_params = NULL; + + /* Clear the global error string */ + krb5_clear_error_message(context); + + dal_handle = context->dal_handle; + ldap_context = (krb5_ldap_context *) dal_handle->db_context; + + if (ldap_context == NULL || ldap_context->lrparams == NULL) + return KRB5_KDB_DBNOTINITED; + + r_params = ldap_context->lrparams; + r_params->mkey_list = key_list; + return 0; +} + diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c index 79ca63472f..f0734deb2a 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c @@ -2059,9 +2059,16 @@ populate_krb5_db_entry (krb5_context context, /* KRBSECRETKEY */ if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) { + krb5_kvno mkvno = 0; + mask |= KDB_SECRET_KEY_ATTR; - if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data)) != 0) + if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data, &mkvno)) != 0) goto cleanup; + if (mkvno != 0) { + /* don't add the tl data if mkvno == 0 */ + if ((st=krb5_dbe_update_mkvno(context, entry, mkvno)) != 0) + goto cleanup; + } } /* LAST PASSWORD CHANGE */ diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h index 18e2acc060..502e71ccd5 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h @@ -112,7 +112,7 @@ krb5_ldap_parse_principal_name(char *, char **); krb5_error_code krb5_decode_krbsecretkey(krb5_context, krb5_db_entry *, struct berval **, - krb5_tl_data *); + krb5_tl_data *, krb5_kvno *); krb5_error_code berval2tl_data(struct berval *in, krb5_tl_data **out); diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c index 561a65d99b..e52a61897f 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c @@ -345,7 +345,7 @@ asn1_encode_sequence_of_keys (krb5_key_data *key_data, krb5_int16 n_key_data, static krb5_error_code asn1_decode_sequence_of_keys (krb5_data *in, krb5_key_data **out, - krb5_int16 *n_key_data, int *mkvno) + krb5_int16 *n_key_data, krb5_kvno *mkvno) { krb5_error_code err; ldap_seqof_key_data *p; @@ -371,7 +371,7 @@ asn1_decode_sequence_of_keys (krb5_data *in, krb5_key_data **out, /* Decoding ASN.1 encoded key */ static struct berval ** -krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data) { +krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data, krb5_kvno mkvno) { struct berval **ret = NULL; int currkvno; int num_versions = 1; @@ -396,7 +396,7 @@ krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data) { if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) { asn1_encode_sequence_of_keys (key_data+last, (krb5_int16) i - last + 1, - 0, /* For now, mkvno == 0*/ + mkvno, &code); ret[j] = malloc (sizeof (struct berval)); if (ret[j] == NULL) { @@ -927,8 +927,12 @@ krb5_ldap_put_principal(context, entries, nentries, db_args) } if (entries->mask & KADM5_KEY_DATA || entries->mask & KADM5_KVNO) { + krb5_kvno mkvno; + + if ((st=krb5_dbe_lookup_mkvno(context, entries, &mkvno)) != 0) + goto cleanup; bersecretkey = krb5_encode_krbsecretkey (entries->key_data, - entries->n_key_data); + entries->n_key_data, mkvno); if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, bersecretkey)) != 0) @@ -1220,11 +1224,12 @@ cleanup: } krb5_error_code -krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data) +krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data, mkvno) krb5_context context; krb5_db_entry *entries; struct berval **bvalues; krb5_tl_data *userinfo_tl_data; + krb5_kvno *mkvno; { char *user=NULL; int i=0, j=0, noofkeys=0; @@ -1235,7 +1240,6 @@ krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data) goto cleanup; for (i=0; bvalues[i] != NULL; ++i) { - int mkvno; /* Not used currently */ krb5_int16 n_kd; krb5_key_data *kd; krb5_data in; @@ -1248,7 +1252,7 @@ krb5_decode_krbsecretkey(context, entries, bvalues, userinfo_tl_data) st = asn1_decode_sequence_of_keys (&in, &kd, &n_kd, - &mkvno); + mkvno); if (st != 0) { const char *msg = error_message(st); diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h index ffe6c36658..db17509aeb 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_realm.h @@ -68,6 +68,7 @@ typedef struct _krb5_ldap_realm_params { char **passwdservers; krb5_tl_data *tl_data; krb5_keyblock mkey; + krb5_keylist_node *mkey_list; /* all master keys in use for the realm */ long mask; } krb5_ldap_realm_params; |