summaryrefslogtreecommitdiffstats
path: root/src/lib/kdb/kdb5.c
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/kdb/kdb5.c
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/kdb/kdb5.c')
-rw-r--r--src/lib/kdb/kdb5.c782
1 files changed, 778 insertions, 4 deletions
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;