-/*\r
- * lib/krb5/keytab/kt_memory.c\r
- *\r
- * Copyright 2007 by Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation files\r
- * (the "Software"), to deal in the Software without restriction,\r
- * including without limitation the rights to use, copy, modify, merge,\r
- * publish, distribute, sublicense, and/or sell copies of the Software,\r
- * and to permit persons to whom the Software is furnished to do so,\r
- * subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-#include "k5-int.h"\r
-#include "kt-int.h"\r
-#include <stdio.h>\r
-\r
-#define HEIMDAL_COMPATIBLE\r
-\r
-/*\r
- * Information needed by internal routines of the file-based ticket\r
- * cache implementation.\r
- */\r
-\r
-\r
-/*\r
- * Constants\r
- */\r
-#define IGNORE_VNO 0\r
-#define IGNORE_ENCTYPE 0\r
-\r
-/* \r
- * Types\r
- */\r
-/* From krb5.h: \r
- * typedef struct krb5_keytab_entry_st {\r
- * krb5_magic magic;\r
- * krb5_principal principal; principal of this key\r
- * krb5_timestamp timestamp; time entry written to keytable \r
- * krb5_kvno vno; key version number \r
- * krb5_keyblock key; the secret key\r
- *} krb5_keytab_entry;\r
- */\r
-\r
-/* Individual key entries within a table, in a linked list */\r
-typedef struct _krb5_mkt_link {\r
- struct _krb5_mkt_link *next;\r
- krb5_keytab_entry *entry;\r
-} krb5_mkt_link, *krb5_mkt_cursor;\r
-\r
-/* Per-keytab data header */\r
-typedef struct _krb5_mkt_data {\r
- char *name; /* Name of the keytab */\r
- k5_mutex_t lock; /* Thread-safety - all but link */\r
- krb5_int32 refcount; \r
- krb5_mkt_cursor link;\r
-} krb5_mkt_data;\r
-\r
-/* List of memory key tables */\r
-typedef struct _krb5_mkt_list_node {\r
- struct _krb5_mkt_list_node *next;\r
- krb5_keytab keytab;\r
-} krb5_mkt_list_node;\r
-\r
-/* Iterator over memory key tables */\r
-typedef struct _krb5_mkt_ptcursor_data {\r
- struct _krb5_mkt_list_node *cur;\r
-} krb5_mkt_ptcursor_data;\r
-\r
-/* \r
- * Globals \r
- */\r
-static krb5_mkt_list_node * krb5int_mkt_list = NULL;\r
-static k5_mutex_t krb5int_mkt_mutex = K5_MUTEX_PARTIAL_INITIALIZER;\r
-\r
-/*\r
- * Macros\r
- */\r
-#define KTLOCK(id) k5_mutex_lock(&(((krb5_mkt_data *)(id)->data)->lock))\r
-#define KTUNLOCK(id) k5_mutex_unlock(&(((krb5_mkt_data *)(id)->data)->lock))\r
-#define KTCHECKLOCK(id) k5_mutex_assert_locked(&(((krb5_mkt_data *)(id)->data)->lock))\r
-\r
-#define KTGLOCK k5_mutex_lock(&krb5int_mkt_mutex)\r
-#define KTGUNLOCK k5_mutex_unlock(&krb5int_mkt_mutex)\r
-#define KTGCHECKLOCK k5_mutex_assert_locked(&krb5int_mkt_mutex)\r
-\r
-#define KTLINK(id) (((krb5_mkt_data *)(id)->data)->link)\r
-#define KTREFCNT(id) (((krb5_mkt_data *)(id)->data)->refcount)\r
-#define KTNAME(id) (((krb5_mkt_data *)(id)->data)->name)\r
-\r
-extern const struct _krb5_kt_ops krb5_mkt_ops;\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_resolve \r
- (krb5_context,\r
- const char *,\r
- krb5_keytab *);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_get_name \r
- (krb5_context,\r
- krb5_keytab,\r
- char *,\r
- unsigned int);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_close \r
- (krb5_context,\r
- krb5_keytab);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_get_entry \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_const_principal,\r
- krb5_kvno,\r
- krb5_enctype,\r
- krb5_keytab_entry *);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_start_seq_get \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_kt_cursor *);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_get_next \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_keytab_entry *,\r
- krb5_kt_cursor *);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_end_get \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_kt_cursor *);\r
-\r
-/* routines to be included on extended version (write routines) */\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_add \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_keytab_entry *);\r
-\r
-krb5_error_code KRB5_CALLCONV krb5_mkt_remove \r
- (krb5_context,\r
- krb5_keytab,\r
- krb5_keytab_entry *);\r
-\r
-int krb5int_mkt_initialize(void) {\r
- return k5_mutex_finish_init(&krb5int_mkt_mutex);\r
-}\r
-\r
-void krb5int_mkt_finalize(void) {\r
- krb5_mkt_list_node *node, *next_node;\r
- krb5_mkt_cursor cursor, next_cursor;\r
-\r
- k5_mutex_destroy(&krb5int_mkt_mutex);\r
-\r
- for (node = krb5int_mkt_list; node; node = next_node) {\r
- next_node = node->next;\r
-\r
- /* destroy the contents of node->keytab */\r
- krb5_xfree(KTNAME(node->keytab));\r
-\r
- /* free the keytab entries */\r
- for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {\r
- next_cursor = cursor->next;\r
- /* the call to krb5_kt_free_entry uses a NULL in place of the\r
- * krb5_context since we know that the context isn't used by\r
- * krb5_kt_free_entry or krb5_free_principal. */\r
- krb5_kt_free_entry(NULL, cursor->entry);\r
- krb5_xfree(cursor->entry);\r
- krb5_xfree(cursor);\r
- }\r
-\r
- /* destroy the lock */\r
- k5_mutex_destroy(&(((krb5_mkt_data *)node->keytab->data)->lock));\r
-\r
- /* free the private data */\r
- krb5_xfree(node->keytab->data);\r
-\r
- /* and the keytab */\r
- krb5_xfree(node->keytab);\r
-\r
- /* and finally the node */\r
- krb5_xfree(node);\r
- }\r
-}\r
-/*\r
- * This is an implementation specific resolver. It returns a keytab \r
- * initialized with memory keytab routines.\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)\r
-{\r
- krb5_mkt_data *data = 0;\r
- krb5_mkt_list_node *list;\r
- krb5_error_code err = 0;\r
-\r
- /* First determine if a memory keytab of this name already exists */\r
- err = KTGLOCK;\r
- if (err)\r
- return(err);\r
-\r
- for (list = krb5int_mkt_list; list; list = list->next)\r
- {\r
- if (strcmp(name,KTNAME(list->keytab)) == 0) {\r
- /* Found */\r
- *id = list->keytab;\r
- goto done;\r
- }\r
- }\r
-\r
- /* We will now create the new key table with the specified name.\r
- * We do not drop the global lock, therefore the name will indeed\r
- * be unique when we add it.\r
- */\r
-\r
- if ((list = (krb5_mkt_list_node *)malloc(sizeof(krb5_mkt_list_node))) == NULL) {\r
- err = ENOMEM;\r
- goto done;\r
- }\r
-\r
- if ((list->keytab = (krb5_keytab)malloc(sizeof(struct _krb5_kt))) == NULL) {\r
- krb5_xfree(list);\r
- err = ENOMEM;\r
- goto done; \r
- }\r
-\r
- list->keytab->ops = &krb5_mkt_ops;\r
- if ((data = (krb5_mkt_data *)malloc(sizeof(krb5_mkt_data))) == NULL) {\r
- krb5_xfree(list->keytab);\r
- krb5_xfree(list);\r
- err = ENOMEM;\r
- goto done;\r
- }\r
-\r
- err = k5_mutex_init(&data->lock);\r
- if (err) {\r
- krb5_xfree(data);\r
- krb5_xfree(list->keytab);\r
- krb5_xfree(list);\r
- goto done;\r
- }\r
-\r
- if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {\r
- k5_mutex_destroy(&data->lock);\r
- krb5_xfree(data);\r
- krb5_xfree(list->keytab);\r
- krb5_xfree(list);\r
- err = ENOMEM;\r
- goto done;\r
- }\r
-\r
- (void) strcpy(data->name, name);\r
-\r
- data->link = NULL;\r
- data->refcount = 0;\r
- list->keytab->data = (krb5_pointer)data;\r
- list->keytab->magic = KV5M_KEYTAB;\r
-\r
- list->next = krb5int_mkt_list;\r
- krb5int_mkt_list = list;\r
-\r
- *id = list->keytab;\r
-\r
- done:\r
- err = KTLOCK(*id);\r
- if (err) {\r
- k5_mutex_destroy(&data->lock);\r
- if (data && data->name) \r
- krb5_xfree(data->name);\r
- krb5_xfree(data);\r
- if (list && list->keytab)\r
- krb5_xfree(list->keytab);\r
- krb5_xfree(list);\r
- } else {\r
- KTREFCNT(*id)++;\r
- KTUNLOCK(*id);\r
- }\r
-\r
- KTGUNLOCK;\r
- return(err);\r
-}\r
-\r
-\r
-/*\r
- * "Close" a memory-based keytab. This is effectively a no-op.\r
- * We check to see if the keytab exists and that is about it.\r
- * Closing a file keytab does not destroy the contents. Closing\r
- * a memory keytab shouldn't either.\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_close(krb5_context context, krb5_keytab id)\r
-{\r
- krb5_mkt_list_node **listp;\r
-#ifdef HEIMDAL_COMPATIBLE\r
- krb5_mkt_list_node *node;\r
- krb5_mkt_data * data;\r
-#endif\r
- krb5_error_code err = 0;\r
-\r
- /* First determine if a memory keytab of this name already exists */\r
- err = KTGLOCK;\r
- if (err)\r
- return(err);\r
- \r
- for (listp = &krb5int_mkt_list; *listp; listp = &((*listp)->next))\r
- {\r
- if (id == (*listp)->keytab) {\r
- /* Found */\r
- break;\r
- }\r
- }\r
-\r
- if (*listp == NULL) {\r
- /* The specified keytab could not be found */\r
- err = KRB5_KT_NOTFOUND;\r
- goto done;\r
- }\r
-\r
- /* reduce the refcount and return */\r
- err = KTLOCK(id);\r
- if (err)\r
- goto done;\r
-\r
- KTREFCNT(id)--;\r
- KTUNLOCK(id);\r
-\r
-#ifdef HEIMDAL_COMPATIBLE\r
- /* In Heimdal if the refcount hits 0, the MEMORY keytab is \r
- * destroyed since there is no krb5_kt_destroy function.\r
- * There is no need to lock the entry while performing \r
- * these operations as the refcount will be 0 and we are\r
- * holding the global lock.\r
- */\r
- data = (krb5_mkt_data *)id->data;\r
- if (data->refcount == 0) {\r
- krb5_mkt_cursor cursor, next_cursor;\r
-\r
- node = *listp;\r
- *listp = node->next;\r
-\r
- /* destroy the contents of node->keytab (aka id) */\r
- krb5_xfree(data->name);\r
-\r
- /* free the keytab entries */\r
- for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {\r
- next_cursor = cursor->next;\r
-\r
- krb5_kt_free_entry(context, cursor->entry);\r
- krb5_xfree(cursor->entry);\r
- krb5_xfree(cursor);\r
- }\r
-\r
- /* destroy the lock */\r
- k5_mutex_destroy(&(data->lock));\r
-\r
- /* free the private data */\r
- krb5_xfree(data);\r
-\r
- /* and the keytab */\r
- krb5_xfree(node->keytab);\r
-\r
- /* and finally the node */\r
- krb5_xfree(node);\r
- }\r
-#endif /* HEIMDAL_COMPATIBLE */\r
-\r
- done:\r
- KTGUNLOCK;\r
- return(err);\r
-}\r
-\r
-/*\r
- * This is the get_entry routine for the memory based keytab implementation.\r
- * It either retrieves the entry or returns an error.\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV\r
-krb5_mkt_get_entry(krb5_context context, krb5_keytab id,\r
- krb5_const_principal principal, krb5_kvno kvno,\r
- krb5_enctype enctype, krb5_keytab_entry *out_entry)\r
-{\r
- krb5_mkt_cursor cursor;\r
- krb5_keytab_entry *entry, *match = NULL;\r
- krb5_error_code err = 0;\r
- int found_wrong_kvno = 0;\r
- krb5_boolean similar = 0;\r
-\r
- err = KTLOCK(id);\r
- if (err)\r
- return err;\r
-\r
- for (cursor = KTLINK(id); cursor && cursor->entry; cursor = cursor->next) {\r
- entry = cursor->entry;\r
-\r
- /* if the principal isn't the one requested, continue to the next. */\r
-\r
- if (!krb5_principal_compare(context, principal, entry->principal))\r
- continue;\r
-\r
- /* if the enctype is not ignored and doesn't match, \r
- and continue to the next */\r
- if (enctype != IGNORE_ENCTYPE) {\r
- if ((err = krb5_c_enctype_compare(context, enctype, \r
- entry->key.enctype,\r
- &similar))) {\r
- /* we can't determine the enctype of the entry */\r
- continue;\r
- }\r
-\r
- if (!similar)\r
- continue;\r
- }\r
-\r
- if (kvno == IGNORE_VNO) {\r
- if (match == NULL)\r
- match = entry;\r
- else if (entry->vno > match->vno)\r
- match = entry;\r
- } else {\r
- if (entry->vno == kvno) {\r
- match = entry;\r
- break;\r
- } else {\r
- found_wrong_kvno++;\r
- }\r
- }\r
- }\r
-\r
- /* if we found an entry that matches, ... */\r
- if (match) { \r
- out_entry->magic = match->magic;\r
- out_entry->timestamp = match->timestamp;\r
- out_entry->vno = match->vno;\r
- out_entry->key = match->key; \r
- err = krb5_copy_keyblock_contents(context, &(match->key),\r
- &(out_entry->key));\r
- /*\r
- * Coerce the enctype of the output keyblock in case we\r
- * got an inexact match on the enctype.\r
- */\r
- if(enctype != IGNORE_ENCTYPE)\r
- out_entry->key.enctype = enctype;\r
- if(!err) {\r
- err = krb5_copy_principal(context, \r
- match->principal, \r
- &(out_entry->principal));\r
- }\r
- } else {\r
- if (!err)\r
- err = found_wrong_kvno ? KRB5_KT_KVNONOTFOUND : KRB5_KT_NOTFOUND;\r
- }\r
-\r
- KTUNLOCK(id);\r
- return(err);\r
-}\r
-\r
-/*\r
- * Get the name of the memory-based keytab.\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV\r
-krb5_mkt_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)\r
-{\r
- memset(name, 0, len);\r
-\r
- if (len < strlen(id->ops->prefix)+2)\r
- return(KRB5_KT_NAME_TOOLONG);\r
- strcpy(name, id->ops->prefix);\r
- name += strlen(id->ops->prefix);\r
- name[0] = ':';\r
- name++;\r
- len -= strlen(id->ops->prefix)+1;\r
-\r
- if (len < strlen(KTNAME(id))+1)\r
- return(KRB5_KT_NAME_TOOLONG);\r
- strcpy(name, KTNAME(id));\r
- /* strcpy will NUL-terminate the destination */\r
-\r
- return(0);\r
-}\r
-\r
-/*\r
- * krb5_mkt_start_seq_get()\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV\r
-krb5_mkt_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)\r
-{\r
- krb5_error_code err = 0;\r
-\r
- err = KTLOCK(id);\r
- if (err)\r
- return(err);\r
-\r
- *cursorp = (krb5_kt_cursor)KTLINK(id);\r
- KTUNLOCK(id);\r
-\r
- return(0);\r
-}\r
-\r
-/*\r
- * krb5_mkt_get_next()\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)\r
-{\r
- krb5_mkt_cursor mkt_cursor = (krb5_mkt_cursor)*cursor;\r
- krb5_error_code err = 0;\r
-\r
- err = KTLOCK(id);\r
- if (err)\r
- return err;\r
-\r
- if (mkt_cursor == NULL) {\r
- KTUNLOCK(id);\r
- return KRB5_KT_END;\r
- }\r
-\r
- entry->magic = mkt_cursor->entry->magic;\r
- entry->timestamp = mkt_cursor->entry->timestamp;\r
- entry->vno = mkt_cursor->entry->vno;\r
- entry->key = mkt_cursor->entry->key; \r
- err = krb5_copy_keyblock_contents(context, &(mkt_cursor->entry->key), \r
- &(entry->key));\r
- if (!err) \r
- err = krb5_copy_principal(context, mkt_cursor->entry->principal,\r
- &(entry->principal));\r
- if (!err)\r
- *cursor = (krb5_kt_cursor *)mkt_cursor->next;\r
- KTUNLOCK(id);\r
- return(err);\r
-}\r
-\r
-/*\r
- * krb5_mkt_end_get()\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)\r
-{\r
- *cursor = NULL;\r
- return(0);\r
-}\r
-\r
-\r
-/*\r
- * krb5_mkt_add()\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)\r
-{\r
- krb5_error_code err = 0;\r
- krb5_mkt_cursor cursor;\r
-\r
- err = KTLOCK(id);\r
- if (err)\r
- return err;\r
-\r
- cursor = (krb5_mkt_cursor)malloc(sizeof(krb5_mkt_link));\r
- if (cursor == NULL) {\r
- err = ENOMEM;\r
- goto done;\r
- }\r
- cursor->entry = (krb5_keytab_entry *)malloc(sizeof(krb5_keytab_entry));\r
- if (cursor->entry == NULL) {\r
- krb5_xfree(cursor);\r
- err = ENOMEM;\r
- goto done;\r
- }\r
- cursor->entry->magic = entry->magic;\r
- cursor->entry->timestamp = entry->timestamp;\r
- cursor->entry->vno = entry->vno;\r
- err = krb5_copy_keyblock_contents(context, &(entry->key), \r
- &(cursor->entry->key));\r
- if (err) {\r
- krb5_xfree(cursor->entry);\r
- krb5_xfree(cursor);\r
- goto done;\r
- }\r
-\r
- err = krb5_copy_principal(context, entry->principal, &(cursor->entry->principal));\r
- if (err) {\r
- krb5_free_keyblock_contents(context, &(cursor->entry->key));\r
- krb5_xfree(cursor->entry);\r
- krb5_xfree(cursor);\r
- goto done;\r
- }\r
-\r
- if (KTLINK(id) == NULL) {\r
- cursor->next = NULL;\r
- KTLINK(id) = cursor;\r
- } else {\r
- cursor->next = KTLINK(id);\r
- KTLINK(id) = cursor;\r
- }\r
-\r
- done:\r
- KTUNLOCK(id);\r
- return err;\r
-}\r
-\r
-/*\r
- * krb5_mkt_remove()\r
- */\r
-\r
-krb5_error_code KRB5_CALLCONV \r
-krb5_mkt_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)\r
-{\r
- krb5_mkt_cursor *pcursor, next;\r
- krb5_error_code err = 0;\r
-\r
- err = KTLOCK(id);\r
- if (err)\r
- return err;\r
-\r
- if ( KTLINK(id) == NULL ) {\r
- err = KRB5_KT_NOTFOUND;\r
- goto done;\r
- }\r
- \r
- for ( pcursor = &KTLINK(id); *pcursor; pcursor = &(*pcursor)->next ) {\r
- if ( (*pcursor)->entry->vno == entry->vno &&\r
- (*pcursor)->entry->key.enctype == entry->key.enctype &&\r
- krb5_principal_compare(context, (*pcursor)->entry->principal, entry->principal))\r
- break;\r
- }\r
-\r
- if (!*pcursor) {\r
- err = KRB5_KT_NOTFOUND;\r
- goto done;\r
- }\r
-\r
- krb5_kt_free_entry(context, (*pcursor)->entry);\r
- krb5_xfree((*pcursor)->entry);\r
- next = (*pcursor)->next;\r
- krb5_xfree(*pcursor);\r
- (*pcursor) = next;\r
-\r
- done:\r
- KTUNLOCK(id);\r
- return err;\r
-}\r
-\r
-\r
-/*\r
- * krb5_mkt_ops\r
- */\r
-\r
-const struct _krb5_kt_ops krb5_mkt_ops = {\r
- 0,\r
- "MEMORY", /* Prefix -- this string should not appear anywhere else! */\r
- krb5_mkt_resolve,\r
- krb5_mkt_get_name, \r
- krb5_mkt_close,\r
- krb5_mkt_get_entry,\r
- krb5_mkt_start_seq_get,\r
- krb5_mkt_get_next,\r
- krb5_mkt_end_get,\r
- krb5_mkt_add,\r
- krb5_mkt_remove,\r
- NULL\r
-};\r
-\r
+/*
+ * lib/krb5/keytab/kt_memory.c
+ *
+ * Copyright 2007 by Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "k5-int.h"
+#include "kt-int.h"
+#include <stdio.h>
+
+#define HEIMDAL_COMPATIBLE
+
+/*
+ * Information needed by internal routines of the file-based ticket
+ * cache implementation.
+ */
+
+
+/*
+ * Constants
+ */
+#define IGNORE_VNO 0
+#define IGNORE_ENCTYPE 0
+
+/*
+ * Types
+ */
+/* From krb5.h:
+ * typedef struct krb5_keytab_entry_st {
+ * krb5_magic magic;
+ * krb5_principal principal; principal of this key
+ * krb5_timestamp timestamp; time entry written to keytable
+ * krb5_kvno vno; key version number
+ * krb5_keyblock key; the secret key
+ *} krb5_keytab_entry;
+ */
+
+/* Individual key entries within a table, in a linked list */
+typedef struct _krb5_mkt_link {
+ struct _krb5_mkt_link *next;
+ krb5_keytab_entry *entry;
+} krb5_mkt_link, *krb5_mkt_cursor;
+
+/* Per-keytab data header */
+typedef struct _krb5_mkt_data {
+ char *name; /* Name of the keytab */
+ k5_mutex_t lock; /* Thread-safety - all but link */
+ krb5_int32 refcount;
+ krb5_mkt_cursor link;
+} krb5_mkt_data;
+
+/* List of memory key tables */
+typedef struct _krb5_mkt_list_node {
+ struct _krb5_mkt_list_node *next;
+ krb5_keytab keytab;
+} krb5_mkt_list_node;
+
+/* Iterator over memory key tables */
+typedef struct _krb5_mkt_ptcursor_data {
+ struct _krb5_mkt_list_node *cur;
+} krb5_mkt_ptcursor_data;
+
+/*
+ * Globals
+ */
+static krb5_mkt_list_node * krb5int_mkt_list = NULL;
+static k5_mutex_t krb5int_mkt_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+
+/*
+ * Macros
+ */
+#define KTLOCK(id) k5_mutex_lock(&(((krb5_mkt_data *)(id)->data)->lock))
+#define KTUNLOCK(id) k5_mutex_unlock(&(((krb5_mkt_data *)(id)->data)->lock))
+#define KTCHECKLOCK(id) k5_mutex_assert_locked(&(((krb5_mkt_data *)(id)->data)->lock))
+
+#define KTGLOCK k5_mutex_lock(&krb5int_mkt_mutex)
+#define KTGUNLOCK k5_mutex_unlock(&krb5int_mkt_mutex)
+#define KTGCHECKLOCK k5_mutex_assert_locked(&krb5int_mkt_mutex)
+
+#define KTLINK(id) (((krb5_mkt_data *)(id)->data)->link)
+#define KTREFCNT(id) (((krb5_mkt_data *)(id)->data)->refcount)
+#define KTNAME(id) (((krb5_mkt_data *)(id)->data)->name)
+
+extern const struct _krb5_kt_ops krb5_mkt_ops;
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_resolve
+ (krb5_context,
+ const char *,
+ krb5_keytab *);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_get_name
+ (krb5_context,
+ krb5_keytab,
+ char *,
+ unsigned int);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_close
+ (krb5_context,
+ krb5_keytab);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_get_entry
+ (krb5_context,
+ krb5_keytab,
+ krb5_const_principal,
+ krb5_kvno,
+ krb5_enctype,
+ krb5_keytab_entry *);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_start_seq_get
+ (krb5_context,
+ krb5_keytab,
+ krb5_kt_cursor *);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_get_next
+ (krb5_context,
+ krb5_keytab,
+ krb5_keytab_entry *,
+ krb5_kt_cursor *);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_end_get
+ (krb5_context,
+ krb5_keytab,
+ krb5_kt_cursor *);
+
+/* routines to be included on extended version (write routines) */
+krb5_error_code KRB5_CALLCONV krb5_mkt_add
+ (krb5_context,
+ krb5_keytab,
+ krb5_keytab_entry *);
+
+krb5_error_code KRB5_CALLCONV krb5_mkt_remove
+ (krb5_context,
+ krb5_keytab,
+ krb5_keytab_entry *);
+
+int krb5int_mkt_initialize(void) {
+ return k5_mutex_finish_init(&krb5int_mkt_mutex);
+}
+
+void krb5int_mkt_finalize(void) {
+ krb5_mkt_list_node *node, *next_node;
+ krb5_mkt_cursor cursor, next_cursor;
+
+ k5_mutex_destroy(&krb5int_mkt_mutex);
+
+ for (node = krb5int_mkt_list; node; node = next_node) {
+ next_node = node->next;
+
+ /* destroy the contents of node->keytab */
+ krb5_xfree(KTNAME(node->keytab));
+
+ /* free the keytab entries */
+ for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
+ next_cursor = cursor->next;
+ /* the call to krb5_kt_free_entry uses a NULL in place of the
+ * krb5_context since we know that the context isn't used by
+ * krb5_kt_free_entry or krb5_free_principal. */
+ krb5_kt_free_entry(NULL, cursor->entry);
+ krb5_xfree(cursor->entry);
+ krb5_xfree(cursor);
+ }
+
+ /* destroy the lock */
+ k5_mutex_destroy(&(((krb5_mkt_data *)node->keytab->data)->lock));
+
+ /* free the private data */
+ krb5_xfree(node->keytab->data);
+
+ /* and the keytab */
+ krb5_xfree(node->keytab);
+
+ /* and finally the node */
+ krb5_xfree(node);
+ }
+}
+/*
+ * This is an implementation specific resolver. It returns a keytab
+ * initialized with memory keytab routines.
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)
+{
+ krb5_mkt_data *data = 0;
+ krb5_mkt_list_node *list;
+ krb5_error_code err = 0;
+
+ /* First determine if a memory keytab of this name already exists */
+ err = KTGLOCK;
+ if (err)
+ return(err);
+
+ for (list = krb5int_mkt_list; list; list = list->next)
+ {
+ if (strcmp(name,KTNAME(list->keytab)) == 0) {
+ /* Found */
+ *id = list->keytab;
+ goto done;
+ }
+ }
+
+ /* We will now create the new key table with the specified name.
+ * We do not drop the global lock, therefore the name will indeed
+ * be unique when we add it.
+ */
+
+ if ((list = (krb5_mkt_list_node *)malloc(sizeof(krb5_mkt_list_node))) == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+
+ if ((list->keytab = (krb5_keytab)malloc(sizeof(struct _krb5_kt))) == NULL) {
+ krb5_xfree(list);
+ err = ENOMEM;
+ goto done;
+ }
+
+ list->keytab->ops = &krb5_mkt_ops;
+ if ((data = (krb5_mkt_data *)malloc(sizeof(krb5_mkt_data))) == NULL) {
+ krb5_xfree(list->keytab);
+ krb5_xfree(list);
+ err = ENOMEM;
+ goto done;
+ }
+
+ err = k5_mutex_init(&data->lock);
+ if (err) {
+ krb5_xfree(data);
+ krb5_xfree(list->keytab);
+ krb5_xfree(list);
+ goto done;
+ }
+
+ if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
+ k5_mutex_destroy(&data->lock);
+ krb5_xfree(data);
+ krb5_xfree(list->keytab);
+ krb5_xfree(list);
+ err = ENOMEM;
+ goto done;
+ }
+
+ (void) strcpy(data->name, name);
+
+ data->link = NULL;
+ data->refcount = 0;
+ list->keytab->data = (krb5_pointer)data;
+ list->keytab->magic = KV5M_KEYTAB;
+
+ list->next = krb5int_mkt_list;
+ krb5int_mkt_list = list;
+
+ *id = list->keytab;
+
+ done:
+ err = KTLOCK(*id);
+ if (err) {
+ k5_mutex_destroy(&data->lock);
+ if (data && data->name)
+ krb5_xfree(data->name);
+ krb5_xfree(data);
+ if (list && list->keytab)
+ krb5_xfree(list->keytab);
+ krb5_xfree(list);
+ } else {
+ KTREFCNT(*id)++;
+ KTUNLOCK(*id);
+ }
+
+ KTGUNLOCK;
+ return(err);
+}
+
+
+/*
+ * "Close" a memory-based keytab. This is effectively a no-op.
+ * We check to see if the keytab exists and that is about it.
+ * Closing a file keytab does not destroy the contents. Closing
+ * a memory keytab shouldn't either.
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_close(krb5_context context, krb5_keytab id)
+{
+ krb5_mkt_list_node **listp;
+#ifdef HEIMDAL_COMPATIBLE
+ krb5_mkt_list_node *node;
+ krb5_mkt_data * data;
+#endif
+ krb5_error_code err = 0;
+
+ /* First determine if a memory keytab of this name already exists */
+ err = KTGLOCK;
+ if (err)
+ return(err);
+
+ for (listp = &krb5int_mkt_list; *listp; listp = &((*listp)->next))
+ {
+ if (id == (*listp)->keytab) {
+ /* Found */
+ break;
+ }
+ }
+
+ if (*listp == NULL) {
+ /* The specified keytab could not be found */
+ err = KRB5_KT_NOTFOUND;
+ goto done;
+ }
+
+ /* reduce the refcount and return */
+ err = KTLOCK(id);
+ if (err)
+ goto done;
+
+ KTREFCNT(id)--;
+ KTUNLOCK(id);
+
+#ifdef HEIMDAL_COMPATIBLE
+ /* In Heimdal if the refcount hits 0, the MEMORY keytab is
+ * destroyed since there is no krb5_kt_destroy function.
+ * There is no need to lock the entry while performing
+ * these operations as the refcount will be 0 and we are
+ * holding the global lock.
+ */
+ data = (krb5_mkt_data *)id->data;
+ if (data->refcount == 0) {
+ krb5_mkt_cursor cursor, next_cursor;
+
+ node = *listp;
+ *listp = node->next;
+
+ /* destroy the contents of node->keytab (aka id) */
+ krb5_xfree(data->name);
+
+ /* free the keytab entries */
+ for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
+ next_cursor = cursor->next;
+
+ krb5_kt_free_entry(context, cursor->entry);
+ krb5_xfree(cursor->entry);
+ krb5_xfree(cursor);
+ }
+
+ /* destroy the lock */
+ k5_mutex_destroy(&(data->lock));
+
+ /* free the private data */
+ krb5_xfree(data);
+
+ /* and the keytab */
+ krb5_xfree(node->keytab);
+
+ /* and finally the node */
+ krb5_xfree(node);
+ }
+#endif /* HEIMDAL_COMPATIBLE */
+
+ done:
+ KTGUNLOCK;
+ return(err);
+}
+
+/*
+ * This is the get_entry routine for the memory based keytab implementation.
+ * It either retrieves the entry or returns an error.
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_get_entry(krb5_context context, krb5_keytab id,
+ krb5_const_principal principal, krb5_kvno kvno,
+ krb5_enctype enctype, krb5_keytab_entry *out_entry)
+{
+ krb5_mkt_cursor cursor;
+ krb5_keytab_entry *entry, *match = NULL;
+ krb5_error_code err = 0;
+ int found_wrong_kvno = 0;
+ krb5_boolean similar = 0;
+
+ err = KTLOCK(id);
+ if (err)
+ return err;
+
+ for (cursor = KTLINK(id); cursor && cursor->entry; cursor = cursor->next) {
+ entry = cursor->entry;
+
+ /* if the principal isn't the one requested, continue to the next. */
+
+ if (!krb5_principal_compare(context, principal, entry->principal))
+ continue;
+
+ /* if the enctype is not ignored and doesn't match,
+ and continue to the next */
+ if (enctype != IGNORE_ENCTYPE) {
+ if ((err = krb5_c_enctype_compare(context, enctype,
+ entry->key.enctype,
+ &similar))) {
+ /* we can't determine the enctype of the entry */
+ continue;
+ }
+
+ if (!similar)
+ continue;
+ }
+
+ if (kvno == IGNORE_VNO) {
+ if (match == NULL)
+ match = entry;
+ else if (entry->vno > match->vno)
+ match = entry;
+ } else {
+ if (entry->vno == kvno) {
+ match = entry;
+ break;
+ } else {
+ found_wrong_kvno++;
+ }
+ }
+ }
+
+ /* if we found an entry that matches, ... */
+ if (match) {
+ out_entry->magic = match->magic;
+ out_entry->timestamp = match->timestamp;
+ out_entry->vno = match->vno;
+ out_entry->key = match->key;
+ err = krb5_copy_keyblock_contents(context, &(match->key),
+ &(out_entry->key));
+ /*
+ * Coerce the enctype of the output keyblock in case we
+ * got an inexact match on the enctype.
+ */
+ if(enctype != IGNORE_ENCTYPE)
+ out_entry->key.enctype = enctype;
+ if(!err) {
+ err = krb5_copy_principal(context,
+ match->principal,
+ &(out_entry->principal));
+ }
+ } else {
+ if (!err)
+ err = found_wrong_kvno ? KRB5_KT_KVNONOTFOUND : KRB5_KT_NOTFOUND;
+ }
+
+ KTUNLOCK(id);
+ return(err);
+}
+
+/*
+ * Get the name of the memory-based keytab.
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
+{
+ memset(name, 0, len);
+
+ if (len < strlen(id->ops->prefix)+2)
+ return(KRB5_KT_NAME_TOOLONG);
+ strcpy(name, id->ops->prefix);
+ name += strlen(id->ops->prefix);
+ name[0] = ':';
+ name++;
+ len -= strlen(id->ops->prefix)+1;
+
+ if (len < strlen(KTNAME(id))+1)
+ return(KRB5_KT_NAME_TOOLONG);
+ strcpy(name, KTNAME(id));
+ /* strcpy will NUL-terminate the destination */
+
+ return(0);
+}
+
+/*
+ * krb5_mkt_start_seq_get()
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
+{
+ krb5_error_code err = 0;
+
+ err = KTLOCK(id);
+ if (err)
+ return(err);
+
+ *cursorp = (krb5_kt_cursor)KTLINK(id);
+ KTUNLOCK(id);
+
+ return(0);
+}
+
+/*
+ * krb5_mkt_get_next()
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
+{
+ krb5_mkt_cursor mkt_cursor = (krb5_mkt_cursor)*cursor;
+ krb5_error_code err = 0;
+
+ err = KTLOCK(id);
+ if (err)
+ return err;
+
+ if (mkt_cursor == NULL) {
+ KTUNLOCK(id);
+ return KRB5_KT_END;
+ }
+
+ entry->magic = mkt_cursor->entry->magic;
+ entry->timestamp = mkt_cursor->entry->timestamp;
+ entry->vno = mkt_cursor->entry->vno;
+ entry->key = mkt_cursor->entry->key;
+ err = krb5_copy_keyblock_contents(context, &(mkt_cursor->entry->key),
+ &(entry->key));
+ if (!err)
+ err = krb5_copy_principal(context, mkt_cursor->entry->principal,
+ &(entry->principal));
+ if (!err)
+ *cursor = (krb5_kt_cursor *)mkt_cursor->next;
+ KTUNLOCK(id);
+ return(err);
+}
+
+/*
+ * krb5_mkt_end_get()
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
+{
+ *cursor = NULL;
+ return(0);
+}
+
+
+/*
+ * krb5_mkt_add()
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
+{
+ krb5_error_code err = 0;
+ krb5_mkt_cursor cursor;
+
+ err = KTLOCK(id);
+ if (err)
+ return err;
+
+ cursor = (krb5_mkt_cursor)malloc(sizeof(krb5_mkt_link));
+ if (cursor == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ cursor->entry = (krb5_keytab_entry *)malloc(sizeof(krb5_keytab_entry));
+ if (cursor->entry == NULL) {
+ krb5_xfree(cursor);
+ err = ENOMEM;
+ goto done;
+ }
+ cursor->entry->magic = entry->magic;
+ cursor->entry->timestamp = entry->timestamp;
+ cursor->entry->vno = entry->vno;
+ err = krb5_copy_keyblock_contents(context, &(entry->key),
+ &(cursor->entry->key));
+ if (err) {
+ krb5_xfree(cursor->entry);
+ krb5_xfree(cursor);
+ goto done;
+ }
+
+ err = krb5_copy_principal(context, entry->principal, &(cursor->entry->principal));
+ if (err) {
+ krb5_free_keyblock_contents(context, &(cursor->entry->key));
+ krb5_xfree(cursor->entry);
+ krb5_xfree(cursor);
+ goto done;
+ }
+
+ if (KTLINK(id) == NULL) {
+ cursor->next = NULL;
+ KTLINK(id) = cursor;
+ } else {
+ cursor->next = KTLINK(id);
+ KTLINK(id) = cursor;
+ }
+
+ done:
+ KTUNLOCK(id);
+ return err;
+}
+
+/*
+ * krb5_mkt_remove()
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mkt_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
+{
+ krb5_mkt_cursor *pcursor, next;
+ krb5_error_code err = 0;
+
+ err = KTLOCK(id);
+ if (err)
+ return err;
+
+ if ( KTLINK(id) == NULL ) {
+ err = KRB5_KT_NOTFOUND;
+ goto done;
+ }
+
+ for ( pcursor = &KTLINK(id); *pcursor; pcursor = &(*pcursor)->next ) {
+ if ( (*pcursor)->entry->vno == entry->vno &&
+ (*pcursor)->entry->key.enctype == entry->key.enctype &&
+ krb5_principal_compare(context, (*pcursor)->entry->principal, entry->principal))
+ break;
+ }
+
+ if (!*pcursor) {
+ err = KRB5_KT_NOTFOUND;
+ goto done;
+ }
+
+ krb5_kt_free_entry(context, (*pcursor)->entry);
+ krb5_xfree((*pcursor)->entry);
+ next = (*pcursor)->next;
+ krb5_xfree(*pcursor);
+ (*pcursor) = next;
+
+ done:
+ KTUNLOCK(id);
+ return err;
+}
+
+
+/*
+ * krb5_mkt_ops
+ */
+
+const struct _krb5_kt_ops krb5_mkt_ops = {
+ 0,
+ "MEMORY", /* Prefix -- this string should not appear anywhere else! */
+ krb5_mkt_resolve,
+ krb5_mkt_get_name,
+ krb5_mkt_close,
+ krb5_mkt_get_entry,
+ krb5_mkt_start_seq_get,
+ krb5_mkt_get_next,
+ krb5_mkt_end_get,
+ krb5_mkt_add,
+ krb5_mkt_remove,
+ NULL
+};
+