set eol-style and mime-type properties
authorKen Raeburn <raeburn@mit.edu>
Tue, 19 Jun 2007 23:45:48 +0000 (23:45 +0000)
committerKen Raeburn <raeburn@mit.edu>
Tue, 19 Jun 2007 23:45:48 +0000 (23:45 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19594 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/keytab/kt_memory.c

index e103e7685bef7ec2f1b7d9577e4810dcc5ff16f6..ae232801acbaa4a1c99e7f7e594161e0ea210843 100644 (file)
-/*\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
+};
+