diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/krb5/keytab/kt_memory.c | 1354 |
1 files changed, 677 insertions, 677 deletions
diff --git a/src/lib/krb5/keytab/kt_memory.c b/src/lib/krb5/keytab/kt_memory.c index e103e7685..ae232801a 100644 --- a/src/lib/krb5/keytab/kt_memory.c +++ b/src/lib/krb5/keytab/kt_memory.c @@ -1,677 +1,677 @@ -/*
- * 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
-};
-
+/* + * 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 +}; + |