diff options
author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /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.c | 772 |
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 */ +}; |