summaryrefslogtreecommitdiff
path: root/src/lib/krb5/ccache/cc_memory.c
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
committerCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
commit33a9b234e7087f573ef08cd7318c6497ba08b439 (patch)
treed0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/krb5/ccache/cc_memory.c
Notes
Diffstat (limited to 'src/lib/krb5/ccache/cc_memory.c')
-rw-r--r--src/lib/krb5/ccache/cc_memory.c772
1 files changed, 772 insertions, 0 deletions
diff --git a/src/lib/krb5/ccache/cc_memory.c b/src/lib/krb5/ccache/cc_memory.c
new file mode 100644
index 000000000000..0354575c5c16
--- /dev/null
+++ b/src/lib/krb5/ccache/cc_memory.c
@@ -0,0 +1,772 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/ccache/cc_memory.c - Memory-based credential cache */
+/*
+ * Copyright 1990,1991,2000,2004,2008 by the Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "cc-int.h"
+#include "../krb/int-proto.h"
+#include <errno.h>
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_close
+(krb5_context, krb5_ccache id );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_destroy
+(krb5_context, krb5_ccache id );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_end_seq_get
+(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_generate_new
+(krb5_context, krb5_ccache *id );
+
+static const char * KRB5_CALLCONV krb5_mcc_get_name
+(krb5_context, krb5_ccache id );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_get_principal
+(krb5_context, krb5_ccache id , krb5_principal *princ );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_initialize
+(krb5_context, krb5_ccache id , krb5_principal princ );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_next_cred
+(krb5_context,
+ krb5_ccache id ,
+ krb5_cc_cursor *cursor ,
+ krb5_creds *creds );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_resolve
+(krb5_context, krb5_ccache *id , const char *residual );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_retrieve
+(krb5_context,
+ krb5_ccache id ,
+ krb5_flags whichfields ,
+ krb5_creds *mcreds ,
+ krb5_creds *creds );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_start_seq_get
+(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_store
+(krb5_context, krb5_ccache id , krb5_creds *creds );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_set_flags
+(krb5_context, krb5_ccache id , krb5_flags flags );
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_new
+(krb5_context, krb5_cc_ptcursor *);
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_next
+(krb5_context, krb5_cc_ptcursor, krb5_ccache *);
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_free
+(krb5_context, krb5_cc_ptcursor *);
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_last_change_time
+(krb5_context, krb5_ccache, krb5_timestamp *);
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_lock
+(krb5_context context, krb5_ccache id);
+
+static krb5_error_code KRB5_CALLCONV krb5_mcc_unlock
+(krb5_context context, krb5_ccache id);
+
+
+extern const krb5_cc_ops krb5_mcc_ops;
+extern krb5_error_code krb5_change_cache (void);
+
+#define KRB5_OK 0
+
+/* Individual credentials within a cache, in a linked list. */
+typedef struct _krb5_mcc_link {
+ struct _krb5_mcc_link *next;
+ krb5_creds *creds;
+} krb5_mcc_link, *krb5_mcc_cursor;
+
+/* Per-cache data header. */
+typedef struct _krb5_mcc_data {
+ char *name;
+ k5_cc_mutex lock;
+ krb5_principal prin;
+ krb5_mcc_cursor link;
+ krb5_timestamp changetime;
+ /* Time offsets for clock-skewed clients. */
+ krb5_int32 time_offset;
+ krb5_int32 usec_offset;
+} krb5_mcc_data;
+
+/* List of memory caches. */
+typedef struct krb5_mcc_list_node {
+ struct krb5_mcc_list_node *next;
+ krb5_mcc_data *cache;
+} krb5_mcc_list_node;
+
+/* Iterator over memory caches. */
+struct krb5_mcc_ptcursor_data {
+ struct krb5_mcc_list_node *cur;
+};
+
+k5_cc_mutex krb5int_mcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
+static krb5_mcc_list_node *mcc_head = 0;
+
+static void update_mcc_change_time(krb5_mcc_data *);
+
+static void krb5_mcc_free (krb5_context context, krb5_ccache id);
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Creates/refreshes the memory cred cache id. If the cache exists, its
+ * contents are destroyed.
+ *
+ * Errors:
+ * system errors
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
+{
+ krb5_os_context os_ctx = &context->os_context;
+ krb5_error_code ret;
+ krb5_mcc_data *d;
+
+ d = (krb5_mcc_data *)id->data;
+ k5_cc_mutex_lock(context, &d->lock);
+
+ krb5_mcc_free(context, id);
+
+ d = (krb5_mcc_data *)id->data;
+ ret = krb5_copy_principal(context, princ,
+ &d->prin);
+ update_mcc_change_time(d);
+
+ if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
+ /* Store client time offsets in the cache */
+ d->time_offset = os_ctx->time_offset;
+ d->usec_offset = os_ctx->usec_offset;
+ }
+
+ k5_cc_mutex_unlock(context, &d->lock);
+ if (ret == KRB5_OK)
+ krb5_change_cache();
+ return ret;
+}
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Invalidates the id, and frees any resources associated with accessing
+ * the cache.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_close(krb5_context context, krb5_ccache id)
+{
+ free(id);
+ return KRB5_OK;
+}
+
+static void
+krb5_mcc_free(krb5_context context, krb5_ccache id)
+{
+ krb5_mcc_cursor curr,next;
+ krb5_mcc_data *d;
+
+ d = (krb5_mcc_data *) id->data;
+ for (curr = d->link; curr;) {
+ krb5_free_creds(context, curr->creds);
+ next = curr->next;
+ free(curr);
+ curr = next;
+ }
+ d->link = NULL;
+ krb5_free_principal(context, d->prin);
+}
+
+/*
+ * Effects:
+ * Destroys the contents of id. id is invalid after call.
+ *
+ * Errors:
+ * system errors (locks related)
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_destroy(krb5_context context, krb5_ccache id)
+{
+ krb5_mcc_list_node **curr, *node;
+ krb5_mcc_data *d;
+
+ k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
+
+ d = (krb5_mcc_data *)id->data;
+ for (curr = &mcc_head; *curr; curr = &(*curr)->next) {
+ if ((*curr)->cache == d) {
+ node = *curr;
+ *curr = node->next;
+ free(node);
+ break;
+ }
+ }
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+
+ k5_cc_mutex_lock(context, &d->lock);
+
+ krb5_mcc_free(context, id);
+ free(d->name);
+ k5_cc_mutex_unlock(context, &d->lock);
+ k5_cc_mutex_destroy(&d->lock);
+ free(d);
+ free(id);
+
+ krb5_change_cache ();
+ return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * residual is a legal path name, and a null-terminated string
+ *
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * creates or accesses a memory-based cred cache that is referenced by
+ * residual.
+ *
+ * Returns:
+ * A filled in krb5_ccache structure "id".
+ *
+ * Errors:
+ * KRB5_CC_NOMEM - there was insufficient memory to allocate the
+ * krb5_ccache. id is undefined.
+ * system errors (mutex locks related)
+ */
+static krb5_error_code new_mcc_data (const char *, krb5_mcc_data **);
+
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
+{
+ krb5_os_context os_ctx = &context->os_context;
+ krb5_ccache lid;
+ krb5_mcc_list_node *ptr;
+ krb5_error_code err;
+ krb5_mcc_data *d;
+
+ k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
+ for (ptr = mcc_head; ptr; ptr=ptr->next)
+ if (!strcmp(ptr->cache->name, residual))
+ break;
+ if (ptr)
+ d = ptr->cache;
+ else {
+ err = new_mcc_data(residual, &d);
+ if (err) {
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+ return err;
+ }
+ }
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+
+ lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+ if (lid == NULL)
+ return KRB5_CC_NOMEM;
+
+ if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) &&
+ !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
+ /* Use the time offset from the cache entry */
+ os_ctx->time_offset = d->time_offset;
+ os_ctx->usec_offset = d->usec_offset;
+ os_ctx->os_flags = ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
+ KRB5_OS_TOFFSET_VALID);
+ }
+
+ lid->ops = &krb5_mcc_ops;
+ lid->data = d;
+ *id = lid;
+ return KRB5_OK;
+}
+
+/*
+ * Effects:
+ * Prepares for a sequential search of the credentials cache.
+ * Returns a krb5_cc_cursor to be used with krb5_mcc_next_cred and
+ * krb5_mcc_end_seq_get.
+ *
+ * If the cache is modified between the time of this call and the time
+ * of the final krb5_mcc_end_seq_get, the results are undefined.
+ *
+ * Errors:
+ * KRB5_CC_NOMEM
+ * system errors
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_start_seq_get(krb5_context context, krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_mcc_cursor mcursor;
+ krb5_mcc_data *d;
+
+ d = id->data;
+ k5_cc_mutex_lock(context, &d->lock);
+ mcursor = d->link;
+ k5_cc_mutex_unlock(context, &d->lock);
+ *cursor = (krb5_cc_cursor) mcursor;
+ return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * cursor is a krb5_cc_cursor originally obtained from
+ * krb5_mcc_start_seq_get.
+ *
+ * Modifes:
+ * cursor, creds
+ *
+ * Effects:
+ * Fills in creds with the "next" credentals structure from the cache
+ * id. The actual order the creds are returned in is arbitrary.
+ * Space is allocated for the variable length fields in the
+ * credentials structure, so the object returned must be passed to
+ * krb5_destroy_credential.
+ *
+ * The cursor is updated for the next call to krb5_mcc_next_cred.
+ *
+ * Errors:
+ * system errors
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_next_cred(krb5_context context, krb5_ccache id,
+ krb5_cc_cursor *cursor, krb5_creds *creds)
+{
+ krb5_mcc_cursor mcursor;
+ krb5_error_code retval;
+
+ /* Once the node in the linked list is created, it's never
+ modified, so we don't need to worry about locking here. (Note
+ that we don't support _remove_cred.) */
+ mcursor = (krb5_mcc_cursor) *cursor;
+ if (mcursor == NULL)
+ return KRB5_CC_END;
+ memset(creds, 0, sizeof(krb5_creds));
+ if (mcursor->creds) {
+ retval = k5_copy_creds_contents(context, mcursor->creds, creds);
+ if (retval)
+ return retval;
+ }
+ *cursor = (krb5_cc_cursor)mcursor->next;
+ return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * cursor is a krb5_cc_cursor originally obtained from
+ * krb5_mcc_start_seq_get.
+ *
+ * Modifies:
+ * id, cursor
+ *
+ * Effects:
+ * Finishes sequential processing of the memory credentials ccache id,
+ * and invalidates the cursor (it must never be used after this call).
+ */
+/* ARGSUSED */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
+{
+ *cursor = 0L;
+ return KRB5_OK;
+}
+
+/* Utility routine: Creates the back-end data for a memory cache, and
+ threads it into the global linked list.
+
+ Call with the global list lock held. */
+static krb5_error_code
+new_mcc_data (const char *name, krb5_mcc_data **dataptr)
+{
+ krb5_error_code err;
+ krb5_mcc_data *d;
+ krb5_mcc_list_node *n;
+
+ d = malloc(sizeof(krb5_mcc_data));
+ if (d == NULL)
+ return KRB5_CC_NOMEM;
+
+ err = k5_cc_mutex_init(&d->lock);
+ if (err) {
+ free(d);
+ return err;
+ }
+
+ d->name = strdup(name);
+ if (d->name == NULL) {
+ k5_cc_mutex_destroy(&d->lock);
+ free(d);
+ return KRB5_CC_NOMEM;
+ }
+ d->link = NULL;
+ d->prin = NULL;
+ d->changetime = 0;
+ d->time_offset = 0;
+ d->usec_offset = 0;
+ update_mcc_change_time(d);
+
+ n = malloc(sizeof(krb5_mcc_list_node));
+ if (n == NULL) {
+ free(d->name);
+ k5_cc_mutex_destroy(&d->lock);
+ free(d);
+ return KRB5_CC_NOMEM;
+ }
+
+ n->cache = d;
+ n->next = mcc_head;
+ mcc_head = n;
+
+ *dataptr = d;
+ return 0;
+}
+
+/*
+ * Effects:
+ * Creates a new memory cred cache whose name is guaranteed to be
+ * unique. The name begins with the string TKT_ROOT (from mcc.h).
+ *
+ * Returns:
+ * The filled in krb5_ccache id.
+ *
+ * Errors:
+ * KRB5_CC_NOMEM - there was insufficient memory to allocate the
+ * krb5_ccache. id is undefined.
+ * system errors (from open, mutex locking)
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_generate_new (krb5_context context, krb5_ccache *id)
+{
+ krb5_ccache lid;
+ char uniquename[8];
+ krb5_error_code err;
+ krb5_mcc_data *d;
+
+ /* Allocate memory */
+ lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+ if (lid == NULL)
+ return KRB5_CC_NOMEM;
+
+ lid->ops = &krb5_mcc_ops;
+
+ k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
+
+ /* Check for uniqueness with mutex locked to avoid race conditions */
+ while (1) {
+ krb5_mcc_list_node *ptr;
+
+ err = krb5int_random_string (context, uniquename, sizeof (uniquename));
+ if (err) {
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+ free(lid);
+ return err;
+ }
+
+ for (ptr = mcc_head; ptr; ptr=ptr->next) {
+ if (!strcmp(ptr->cache->name, uniquename)) {
+ break; /* got a match, loop again */
+ }
+ }
+ if (!ptr) break; /* got to the end without finding a match */
+ }
+
+ err = new_mcc_data(uniquename, &d);
+
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+ if (err) {
+ free(lid);
+ return err;
+ }
+ lid->data = d;
+ *id = lid;
+ krb5_change_cache ();
+ return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * id is a file credential cache
+ *
+ * Returns:
+ * A pointer to the name of the file cred cache id.
+ */
+const char * KRB5_CALLCONV
+krb5_mcc_get_name (krb5_context context, krb5_ccache id)
+{
+ return (char *) ((krb5_mcc_data *) id->data)->name;
+}
+
+/*
+ * Modifies:
+ * id, princ
+ *
+ * Effects:
+ * Retrieves the primary principal from id, as set with
+ * krb5_mcc_initialize. The principal is returned is allocated
+ * storage that must be freed by the caller via krb5_free_principal.
+ *
+ * Errors:
+ * system errors
+ * ENOMEM
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
+{
+ krb5_mcc_data *ptr = (krb5_mcc_data *)id->data;
+ if (!ptr->prin) {
+ *princ = 0L;
+ return KRB5_FCC_NOFILE;
+ }
+ return krb5_copy_principal(context, ptr->prin, princ);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
+ krb5_creds *mcreds, krb5_creds *creds)
+{
+ return k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
+ creds);
+}
+
+/*
+ * Non-functional stub implementation for krb5_mcc_remove
+ *
+ * Errors:
+ * KRB5_CC_NOSUPP - not implemented
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
+ krb5_creds *creds)
+{
+ return KRB5_CC_NOSUPP;
+}
+
+
+/*
+ * Requires:
+ * id is a cred cache returned by krb5_mcc_resolve or
+ * krb5_mcc_generate_new.
+ *
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Sets the operational flags of id to flags.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
+{
+ return KRB5_OK;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
+{
+ *flags = 0;
+ return KRB5_OK;
+}
+
+/*
+ * Modifies:
+ * the memory cache
+ *
+ * Effects:
+ * Save away creds in the ccache.
+ *
+ * Errors:
+ * system errors (mutex locking)
+ * ENOMEM
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_mcc_store(krb5_context ctx, krb5_ccache id, krb5_creds *creds)
+{
+ krb5_error_code err;
+ krb5_mcc_link *new_node;
+ krb5_mcc_data *mptr = (krb5_mcc_data *)id->data;
+
+ new_node = malloc(sizeof(krb5_mcc_link));
+ if (new_node == NULL)
+ return ENOMEM;
+ err = krb5_copy_creds(ctx, creds, &new_node->creds);
+ if (err)
+ goto cleanup;
+ k5_cc_mutex_lock(ctx, &mptr->lock);
+ new_node->next = mptr->link;
+ mptr->link = new_node;
+ update_mcc_change_time(mptr);
+ k5_cc_mutex_unlock(ctx, &mptr->lock);
+ return 0;
+cleanup:
+ free(new_node);
+ return err;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_ptcursor_new(
+ krb5_context context,
+ krb5_cc_ptcursor *cursor)
+{
+ krb5_cc_ptcursor n = NULL;
+ struct krb5_mcc_ptcursor_data *cdata = NULL;
+
+ *cursor = NULL;
+
+ n = malloc(sizeof(*n));
+ if (n == NULL)
+ return ENOMEM;
+ n->ops = &krb5_mcc_ops;
+ cdata = malloc(sizeof(struct krb5_mcc_ptcursor_data));
+ if (cdata == NULL) {
+ free(n);
+ return ENOMEM;
+ }
+ n->data = cdata;
+ k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
+ cdata->cur = mcc_head;
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+ *cursor = n;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_ptcursor_next(
+ krb5_context context,
+ krb5_cc_ptcursor cursor,
+ krb5_ccache *ccache)
+{
+ struct krb5_mcc_ptcursor_data *cdata = NULL;
+
+ *ccache = NULL;
+ cdata = cursor->data;
+ if (cdata->cur == NULL)
+ return 0;
+
+ *ccache = malloc(sizeof(**ccache));
+ if (*ccache == NULL)
+ return ENOMEM;
+
+ (*ccache)->ops = &krb5_mcc_ops;
+ (*ccache)->data = cdata->cur->cache;
+ k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
+ cdata->cur = cdata->cur->next;
+ k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_ptcursor_free(
+ krb5_context context,
+ krb5_cc_ptcursor *cursor)
+{
+ if (*cursor == NULL)
+ return 0;
+ if ((*cursor)->data != NULL)
+ free((*cursor)->data);
+ free(*cursor);
+ *cursor = NULL;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_last_change_time(
+ krb5_context context,
+ krb5_ccache id,
+ krb5_timestamp *change_time)
+{
+ krb5_mcc_data *data = (krb5_mcc_data *) id->data;
+
+ k5_cc_mutex_lock(context, &data->lock);
+ *change_time = data->changetime;
+ k5_cc_mutex_unlock(context, &data->lock);
+ return 0;
+}
+
+/*
+ Utility routine: called by krb5_mcc_* functions to keep
+ result of krb5_mcc_last_change_time up to date
+*/
+
+static void
+update_mcc_change_time(krb5_mcc_data *d)
+{
+ krb5_timestamp now_time = time(NULL);
+ d->changetime = (d->changetime >= now_time) ?
+ d->changetime + 1 : now_time;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_lock(krb5_context context, krb5_ccache id)
+{
+ krb5_mcc_data *data = (krb5_mcc_data *) id->data;
+
+ k5_cc_mutex_lock(context, &data->lock);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_mcc_unlock(krb5_context context, krb5_ccache id)
+{
+ krb5_mcc_data *data = (krb5_mcc_data *) id->data;
+
+ k5_cc_mutex_unlock(context, &data->lock);
+ return 0;
+}
+
+const krb5_cc_ops krb5_mcc_ops = {
+ 0,
+ "MEMORY",
+ krb5_mcc_get_name,
+ krb5_mcc_resolve,
+ krb5_mcc_generate_new,
+ krb5_mcc_initialize,
+ krb5_mcc_destroy,
+ krb5_mcc_close,
+ krb5_mcc_store,
+ krb5_mcc_retrieve,
+ krb5_mcc_get_principal,
+ krb5_mcc_start_seq_get,
+ krb5_mcc_next_cred,
+ krb5_mcc_end_seq_get,
+ krb5_mcc_remove_cred,
+ krb5_mcc_set_flags,
+ krb5_mcc_get_flags,
+ krb5_mcc_ptcursor_new,
+ krb5_mcc_ptcursor_next,
+ krb5_mcc_ptcursor_free,
+ NULL, /* move */
+ krb5_mcc_last_change_time,
+ NULL, /* wasdefault */
+ krb5_mcc_lock,
+ krb5_mcc_unlock,
+ NULL, /* switch_to */
+};