diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/kadm5/srv/libkadm5srv.exports | 1 | ||||
-rw-r--r-- | src/lib/kadm5/srv/server_kdb.c | 43 | ||||
-rw-r--r-- | src/lib/kadm5/srv/svr_iters.c | 1 | ||||
-rw-r--r-- | src/lib/kadm5/srv/svr_principal.c | 153 | ||||
-rw-r--r-- | src/lib/kdb/kdb5.c | 782 | ||||
-rw-r--r-- | src/lib/kdb/kdb_cpw.c | 18 | ||||
-rw-r--r-- | src/lib/kdb/kdb_default.c | 220 | ||||
-rw-r--r-- | src/lib/kdb/keytab.c | 7 | ||||
-rw-r--r-- | src/lib/kdb/libkdb5.exports | 20 | ||||
-rw-r--r-- | src/lib/krb5/error_tables/kdb5_err.et | 3 |
10 files changed, 1193 insertions, 55 deletions
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" |