diff options
Diffstat (limited to 'src/lib/kdb/kdb5.c')
-rw-r--r-- | src/lib/kdb/kdb5.c | 782 |
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; |