summaryrefslogtreecommitdiffstats
path: root/src/lib/kdb/kdb5.c
diff options
context:
space:
mode:
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;