summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorWill Fiveash <will.fiveash@oracle.com>2009-01-30 23:55:14 +0000
committerWill Fiveash <will.fiveash@oracle.com>2009-01-30 23:55:14 +0000
commite246f7e7b2cddfca9eb744f24e50dd034247a74b (patch)
tree97ec348048dab2eec4206fa99df1e18adab77cf1 /src/lib
parent77b1e1108ca32617fe43825748c68c575e77f010 (diff)
downloadkrb5-e246f7e7b2cddfca9eb744f24e50dd034247a74b.tar.gz
krb5-e246f7e7b2cddfca9eb744f24e50dd034247a74b.tar.xz
krb5-e246f7e7b2cddfca9eb744f24e50dd034247a74b.zip
Master Key Migration Project
Commit for the Master Key Migration Project. http://k5wiki.kerberos.org/wiki/Projects/Master_Key_Migration This commit provides the ability to add a new master key (with an enctype differing from the current master key) to the master key principal and stash file and then migrate the encryption of existing principals long term keys to use the new master key. In addition deletion of master keys is provided. ticket: 6354 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21844 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/kadm5/srv/libkadm5srv.exports1
-rw-r--r--src/lib/kadm5/srv/server_kdb.c43
-rw-r--r--src/lib/kadm5/srv/svr_iters.c1
-rw-r--r--src/lib/kadm5/srv/svr_principal.c153
-rw-r--r--src/lib/kdb/kdb5.c782
-rw-r--r--src/lib/kdb/kdb_cpw.c18
-rw-r--r--src/lib/kdb/kdb_default.c220
-rw-r--r--src/lib/kdb/keytab.c7
-rw-r--r--src/lib/kdb/libkdb5.exports20
-rw-r--r--src/lib/krb5/error_tables/kdb5_err.et3
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"