2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * lib/krb5/ccache/cc_memory.c
2N/A *
2N/A * Copyright 1990,1991,2000,2004,2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A *
2N/A * implementation of memory-based credentials cache
2N/A */
2N/A#include "cc-int.h"
2N/A#include <errno.h>
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_close
2N/A(krb5_context, krb5_ccache id );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_destroy
2N/A(krb5_context, krb5_ccache id );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_end_seq_get
2N/A(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_generate_new
2N/A(krb5_context, krb5_ccache *id );
2N/A
2N/Astatic const char * KRB5_CALLCONV krb5_mcc_get_name
2N/A(krb5_context, krb5_ccache id );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_get_principal
2N/A(krb5_context, krb5_ccache id , krb5_principal *princ );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_initialize
2N/A(krb5_context, krb5_ccache id , krb5_principal princ );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_next_cred
2N/A(krb5_context,
2N/A krb5_ccache id ,
2N/A krb5_cc_cursor *cursor ,
2N/A krb5_creds *creds );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_resolve
2N/A(krb5_context, krb5_ccache *id , const char *residual );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_retrieve
2N/A(krb5_context,
2N/A krb5_ccache id ,
2N/A krb5_flags whichfields ,
2N/A krb5_creds *mcreds ,
2N/A krb5_creds *creds );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_start_seq_get
2N/A(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_store
2N/A(krb5_context, krb5_ccache id , krb5_creds *creds );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_set_flags
2N/A(krb5_context, krb5_ccache id , krb5_flags flags );
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_new
2N/A(krb5_context, krb5_cc_ptcursor *);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_next
2N/A(krb5_context, krb5_cc_ptcursor, krb5_ccache *);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_free
2N/A(krb5_context, krb5_cc_ptcursor *);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_last_change_time
2N/A(krb5_context, krb5_ccache, krb5_timestamp *);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_lock
2N/A(krb5_context context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_mcc_unlock
2N/A(krb5_context context, krb5_ccache id);
2N/A
2N/A
2N/Aextern const krb5_cc_ops krb5_mcc_ops;
2N/Aextern krb5_error_code krb5_change_cache (void);
2N/A
2N/A#define KRB5_OK 0
2N/A
2N/A/* Individual credentials within a cache, in a linked list. */
2N/Atypedef struct _krb5_mcc_link {
2N/A struct _krb5_mcc_link *next;
2N/A krb5_creds *creds;
2N/A} krb5_mcc_link, *krb5_mcc_cursor;
2N/A
2N/A/* Per-cache data header. */
2N/Atypedef struct _krb5_mcc_data {
2N/A char *name;
2N/A k5_cc_mutex lock;
2N/A krb5_principal prin;
2N/A krb5_mcc_cursor link;
2N/A krb5_timestamp changetime;
2N/A} krb5_mcc_data;
2N/A
2N/A/* List of memory caches. */
2N/Atypedef struct krb5_mcc_list_node {
2N/A struct krb5_mcc_list_node *next;
2N/A krb5_mcc_data *cache;
2N/A} krb5_mcc_list_node;
2N/A
2N/A/* Iterator over memory caches. */
2N/Astruct krb5_mcc_ptcursor_data {
2N/A struct krb5_mcc_list_node *cur;
2N/A};
2N/A
2N/Ak5_cc_mutex krb5int_mcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
2N/Astatic krb5_mcc_list_node *mcc_head = 0;
2N/A
2N/Astatic void update_mcc_change_time(krb5_mcc_data *);
2N/A
2N/Astatic void krb5_mcc_free (krb5_context context, krb5_ccache id);
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Creates/refreshes the memory cred cache id. If the cache exists, its
2N/A * contents are destroyed.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_mcc_data *d;
2N/A
2N/A d = (krb5_mcc_data *)id->data;
2N/A ret = k5_cc_mutex_lock(context, &d->lock);
2N/A if (ret)
2N/A return ret;
2N/A
2N/A krb5_mcc_free(context, id);
2N/A
2N/A d = (krb5_mcc_data *)id->data;
2N/A ret = krb5_copy_principal(context, princ,
2N/A &d->prin);
2N/A update_mcc_change_time(d);
2N/A
2N/A k5_cc_mutex_unlock(context, &d->lock);
2N/A if (ret == KRB5_OK)
2N/A krb5_change_cache();
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Invalidates the id, and frees any resources associated with accessing
2N/A * the cache.
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_close(krb5_context context, krb5_ccache id)
2N/A{
2N/A free(id);
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic void
2N/Akrb5_mcc_free(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_mcc_cursor curr,next;
2N/A krb5_mcc_data *d;
2N/A
2N/A d = (krb5_mcc_data *) id->data;
2N/A for (curr = d->link; curr;) {
2N/A krb5_free_creds(context, curr->creds);
2N/A next = curr->next;
2N/A free(curr);
2N/A curr = next;
2N/A }
2N/A d->link = NULL;
2N/A krb5_free_principal(context, d->prin);
2N/A}
2N/A
2N/A/*
2N/A * Effects:
2N/A * Destroys the contents of id. id is invalid after call.
2N/A *
2N/A * Errors:
2N/A * system errors (locks related)
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_destroy(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_mcc_list_node **curr, *node;
2N/A krb5_mcc_data *d;
2N/A krb5_error_code err;
2N/A
2N/A err = k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
2N/A if (err)
2N/A return err;
2N/A
2N/A d = (krb5_mcc_data *)id->data;
2N/A for (curr = &mcc_head; *curr; curr = &(*curr)->next) {
2N/A if ((*curr)->cache == d) {
2N/A node = *curr;
2N/A *curr = node->next;
2N/A free(node);
2N/A break;
2N/A }
2N/A }
2N/A k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A
2N/A err = k5_cc_mutex_lock(context, &d->lock);
2N/A if (err)
2N/A return err;
2N/A
2N/A krb5_mcc_free(context, id);
2N/A free(d->name);
2N/A k5_cc_mutex_unlock(context, &d->lock);
2N/A k5_cc_mutex_destroy(&d->lock);
2N/A free(d);
2N/A free(id);
2N/A
2N/A krb5_change_cache ();
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * residual is a legal path name, and a null-terminated string
2N/A *
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * creates or accesses a memory-based cred cache that is referenced by
2N/A * residual.
2N/A *
2N/A * Returns:
2N/A * A filled in krb5_ccache structure "id".
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM - there was insufficient memory to allocate the
2N/A * krb5_ccache. id is undefined.
2N/A * system errors (mutex locks related)
2N/A */
2N/Astatic krb5_error_code new_mcc_data (const char *, krb5_mcc_data **);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
2N/A{
2N/A krb5_ccache lid;
2N/A krb5_mcc_list_node *ptr;
2N/A krb5_error_code err;
2N/A krb5_mcc_data *d;
2N/A
2N/A err = k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
2N/A if (err)
2N/A return err;
2N/A for (ptr = mcc_head; ptr; ptr=ptr->next)
2N/A if (!strcmp(ptr->cache->name, residual))
2N/A break;
2N/A if (ptr)
2N/A d = ptr->cache;
2N/A else {
2N/A err = new_mcc_data(residual, &d);
2N/A if (err) {
2N/A k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A return err;
2N/A }
2N/A }
2N/A k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A
2N/A lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2N/A if (lid == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A lid->ops = &krb5_mcc_ops;
2N/A lid->data = d;
2N/A *id = lid;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Effects:
2N/A * Prepares for a sequential search of the credentials cache.
2N/A * Returns a krb5_cc_cursor to be used with krb5_mcc_next_cred and
2N/A * krb5_mcc_end_seq_get.
2N/A *
2N/A * If the cache is modified between the time of this call and the time
2N/A * of the final krb5_mcc_end_seq_get, the results are undefined.
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM
2N/A * system errors
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_start_seq_get(krb5_context context, krb5_ccache id,
2N/A krb5_cc_cursor *cursor)
2N/A{
2N/A krb5_mcc_cursor mcursor;
2N/A krb5_error_code err;
2N/A krb5_mcc_data *d;
2N/A
2N/A d = id->data;
2N/A err = k5_cc_mutex_lock(context, &d->lock);
2N/A if (err)
2N/A return err;
2N/A mcursor = d->link;
2N/A k5_cc_mutex_unlock(context, &d->lock);
2N/A *cursor = (krb5_cc_cursor) mcursor;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * cursor is a krb5_cc_cursor originally obtained from
2N/A * krb5_mcc_start_seq_get.
2N/A *
2N/A * Modifes:
2N/A * cursor, creds
2N/A *
2N/A * Effects:
2N/A * Fills in creds with the "next" credentals structure from the cache
2N/A * id. The actual order the creds are returned in is arbitrary.
2N/A * Space is allocated for the variable length fields in the
2N/A * credentials structure, so the object returned must be passed to
2N/A * krb5_destroy_credential.
2N/A *
2N/A * The cursor is updated for the next call to krb5_mcc_next_cred.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_next_cred(krb5_context context, krb5_ccache id,
2N/A krb5_cc_cursor *cursor, krb5_creds *creds)
2N/A{
2N/A krb5_mcc_cursor mcursor;
2N/A krb5_error_code retval;
2N/A
2N/A /* Once the node in the linked list is created, it's never
2N/A modified, so we don't need to worry about locking here. (Note
2N/A that we don't support _remove_cred.) */
2N/A mcursor = (krb5_mcc_cursor) *cursor;
2N/A if (mcursor == NULL)
2N/A return KRB5_CC_END;
2N/A memset(creds, 0, sizeof(krb5_creds));
2N/A if (mcursor->creds) {
2N/A retval = krb5int_copy_creds_contents(context, mcursor->creds, creds);
2N/A if (retval)
2N/A return retval;
2N/A }
2N/A *cursor = (krb5_cc_cursor)mcursor->next;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * cursor is a krb5_cc_cursor originally obtained from
2N/A * krb5_mcc_start_seq_get.
2N/A *
2N/A * Modifies:
2N/A * id, cursor
2N/A *
2N/A * Effects:
2N/A * Finishes sequential processing of the memory credentials ccache id,
2N/A * and invalidates the cursor (it must never be used after this call).
2N/A */
2N/A/* ARGSUSED */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
2N/A{
2N/A *cursor = 0L;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/* Utility routine: Creates the back-end data for a memory cache, and
2N/A threads it into the global linked list.
2N/A
2N/A Call with the global list lock held. */
2N/Astatic krb5_error_code
2N/Anew_mcc_data (const char *name, krb5_mcc_data **dataptr)
2N/A{
2N/A krb5_error_code err;
2N/A krb5_mcc_data *d;
2N/A krb5_mcc_list_node *n;
2N/A
2N/A d = malloc(sizeof(krb5_mcc_data));
2N/A if (d == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A err = k5_cc_mutex_init(&d->lock);
2N/A if (err) {
2N/A free(d);
2N/A return err;
2N/A }
2N/A
2N/A d->name = strdup(name);
2N/A if (d->name == NULL) {
2N/A k5_cc_mutex_destroy(&d->lock);
2N/A free(d);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A d->link = NULL;
2N/A d->prin = NULL;
2N/A d->changetime = 0;
2N/A update_mcc_change_time(d);
2N/A
2N/A n = malloc(sizeof(krb5_mcc_list_node));
2N/A if (n == NULL) {
2N/A free(d->name);
2N/A k5_cc_mutex_destroy(&d->lock);
2N/A free(d);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A
2N/A n->cache = d;
2N/A n->next = mcc_head;
2N/A mcc_head = n;
2N/A
2N/A *dataptr = d;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Effects:
2N/A * Creates a new memory cred cache whose name is guaranteed to be
2N/A * unique. The name begins with the string TKT_ROOT (from mcc.h).
2N/A *
2N/A * Returns:
2N/A * The filled in krb5_ccache id.
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM - there was insufficient memory to allocate the
2N/A * krb5_ccache. id is undefined.
2N/A * system errors (from open, mutex locking)
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_generate_new (krb5_context context, krb5_ccache *id)
2N/A{
2N/A krb5_ccache lid;
2N/A char uniquename[8];
2N/A krb5_error_code err;
2N/A krb5_mcc_data *d;
2N/A
2N/A /* Allocate memory */
2N/A lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2N/A if (lid == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A lid->ops = &krb5_mcc_ops;
2N/A
2N/A err = k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
2N/A if (err) {
2N/A free(lid);
2N/A return err;
2N/A }
2N/A
2N/A /* Check for uniqueness with mutex locked to avoid race conditions */
2N/A while (1) {
2N/A krb5_mcc_list_node *ptr;
2N/A
2N/A err = krb5int_random_string (context, uniquename, sizeof (uniquename));
2N/A if (err) {
2N/A k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A free(lid);
2N/A return err;
2N/A }
2N/A
2N/A for (ptr = mcc_head; ptr; ptr=ptr->next) {
2N/A if (!strcmp(ptr->cache->name, uniquename)) {
2N/A break; /* got a match, loop again */
2N/A }
2N/A }
2N/A if (!ptr) break; /* got to the end without finding a match */
2N/A }
2N/A
2N/A err = new_mcc_data(uniquename, &d);
2N/A
2N/A k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A if (err) {
2N/A free(lid);
2N/A return err;
2N/A }
2N/A lid->data = d;
2N/A *id = lid;
2N/A krb5_change_cache ();
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/* Utility routine: Creates a random memory ccache name.
2N/A * This algorithm was selected because it creates readable
2N/A * random ccache names in a fixed size buffer. */
2N/A
2N/Akrb5_error_code
2N/Akrb5int_random_string (krb5_context context, char *string, unsigned int length)
2N/A{
2N/A static const unsigned char charlist[] =
2N/A "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
2N/A krb5_error_code err = 0;
2N/A unsigned char *bytes = NULL;
2N/A unsigned int bytecount = length - 1;
2N/A
2N/A if (!err) {
2N/A bytes = malloc (bytecount);
2N/A if (bytes == NULL) { err = ENOMEM; }
2N/A }
2N/A
2N/A if (!err) {
2N/A krb5_data data;
2N/A data.length = bytecount;
2N/A data.data = (char *) bytes;
2N/A err = krb5_c_random_make_octets (context, &data);
2N/A }
2N/A
2N/A if (!err) {
2N/A unsigned int i;
2N/A for (i = 0; i < bytecount; i++) {
2N/A string [i] = charlist[bytes[i] % (sizeof (charlist) - 1)];
2N/A }
2N/A string[length - 1] = '\0';
2N/A }
2N/A
2N/A if (bytes != NULL) { free (bytes); }
2N/A
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is a file credential cache
2N/A *
2N/A * Returns:
2N/A * A pointer to the name of the file cred cache id.
2N/A */
2N/Aconst char * KRB5_CALLCONV
2N/Akrb5_mcc_get_name (krb5_context context, krb5_ccache id)
2N/A{
2N/A return (char *) ((krb5_mcc_data *) id->data)->name;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id, princ
2N/A *
2N/A * Effects:
2N/A * Retrieves the primary principal from id, as set with
2N/A * krb5_mcc_initialize. The principal is returned is allocated
2N/A * storage that must be freed by the caller via krb5_free_principal.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A * ENOMEM
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2N/A{
2N/A krb5_mcc_data *ptr = (krb5_mcc_data *)id->data;
2N/A if (!ptr->prin) {
2N/A *princ = 0L;
2N/A return KRB5_FCC_NOFILE;
2N/A }
2N/A return krb5_copy_principal(context, ptr->prin, princ);
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
2N/A krb5_creds *mcreds, krb5_creds *creds)
2N/A{
2N/A return krb5_cc_retrieve_cred_default (context, id, whichfields,
2N/A mcreds, creds);
2N/A}
2N/A
2N/A/*
2N/A * Non-functional stub implementation for krb5_mcc_remove
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOSUPP - not implemented
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
2N/A krb5_creds *creds)
2N/A{
2N/A return KRB5_CC_NOSUPP;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is a cred cache returned by krb5_mcc_resolve or
2N/A * krb5_mcc_generate_new.
2N/A *
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Sets the operational flags of id to flags.
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2N/A{
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2N/A{
2N/A *flags = 0;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * the memory cache
2N/A *
2N/A * Effects:
2N/A * Save away creds in the ccache.
2N/A *
2N/A * Errors:
2N/A * system errors (mutex locking)
2N/A * ENOMEM
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_store(krb5_context ctx, krb5_ccache id, krb5_creds *creds)
2N/A{
2N/A krb5_error_code err;
2N/A krb5_mcc_link *new_node;
2N/A krb5_mcc_data *mptr = (krb5_mcc_data *)id->data;
2N/A
2N/A new_node = malloc(sizeof(krb5_mcc_link));
2N/A if (new_node == NULL)
2N/A return ENOMEM;
2N/A err = krb5_copy_creds(ctx, creds, &new_node->creds);
2N/A if (err)
2N/A goto cleanup;
2N/A err = k5_cc_mutex_lock(ctx, &mptr->lock);
2N/A if (err) {
2N/A /* Solaris Kerberos - fix mem leaks */
2N/A krb5_free_creds(ctx, new_node->creds);
2N/A goto cleanup;
2N/A }
2N/A new_node->next = mptr->link;
2N/A mptr->link = new_node;
2N/A update_mcc_change_time(mptr);
2N/A k5_cc_mutex_unlock(ctx, &mptr->lock);
2N/A return 0;
2N/Acleanup:
2N/A free(new_node);
2N/A return err;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_ptcursor_new(
2N/A krb5_context context,
2N/A krb5_cc_ptcursor *cursor)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_cc_ptcursor n = NULL;
2N/A struct krb5_mcc_ptcursor_data *cdata = NULL;
2N/A
2N/A *cursor = NULL;
2N/A
2N/A n = malloc(sizeof(*n));
2N/A if (n == NULL)
2N/A return ENOMEM;
2N/A n->ops = &krb5_mcc_ops;
2N/A cdata = malloc(sizeof(struct krb5_mcc_ptcursor_data));
2N/A if (cdata == NULL) {
2N/A ret = ENOMEM;
2N/A goto errout;
2N/A }
2N/A n->data = cdata;
2N/A ret = k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A cdata->cur = mcc_head;
2N/A ret = k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/Aerrout:
2N/A if (ret) {
2N/A krb5_mcc_ptcursor_free(context, &n);
2N/A }
2N/A *cursor = n;
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_ptcursor_next(
2N/A krb5_context context,
2N/A krb5_cc_ptcursor cursor,
2N/A krb5_ccache *ccache)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A struct krb5_mcc_ptcursor_data *cdata = NULL;
2N/A
2N/A *ccache = NULL;
2N/A cdata = cursor->data;
2N/A if (cdata->cur == NULL)
2N/A return 0;
2N/A
2N/A *ccache = malloc(sizeof(**ccache));
2N/A if (*ccache == NULL)
2N/A return ENOMEM;
2N/A
2N/A (*ccache)->ops = &krb5_mcc_ops;
2N/A (*ccache)->data = cdata->cur->cache;
2N/A ret = k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A cdata->cur = cdata->cur->next;
2N/A ret = k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
2N/A if (ret)
2N/A goto errout;
2N/Aerrout:
2N/A if (ret && *ccache != NULL) {
2N/A free(*ccache);
2N/A *ccache = NULL;
2N/A }
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_ptcursor_free(
2N/A krb5_context context,
2N/A krb5_cc_ptcursor *cursor)
2N/A{
2N/A if (*cursor == NULL)
2N/A return 0;
2N/A if ((*cursor)->data != NULL)
2N/A free((*cursor)->data);
2N/A free(*cursor);
2N/A *cursor = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_last_change_time(
2N/A krb5_context context,
2N/A krb5_ccache id,
2N/A krb5_timestamp *change_time)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_mcc_data *data = (krb5_mcc_data *) id->data;
2N/A
2N/A *change_time = 0;
2N/A
2N/A ret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (!ret) {
2N/A *change_time = data->changetime;
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A Utility routine: called by krb5_mcc_* functions to keep
2N/A result of krb5_mcc_last_change_time up to date
2N/A*/
2N/A
2N/Astatic void
2N/Aupdate_mcc_change_time(krb5_mcc_data *d)
2N/A{
2N/A krb5_timestamp now_time = time(NULL);
2N/A d->changetime = (d->changetime >= now_time) ?
2N/A d->changetime + 1 : now_time;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_lock(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_mcc_data *data = (krb5_mcc_data *) id->data;
2N/A ret = k5_cc_mutex_lock(context, &data->lock);
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_mcc_unlock(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_mcc_data *data = (krb5_mcc_data *) id->data;
2N/A ret = k5_cc_mutex_unlock(context, &data->lock);
2N/A return ret;
2N/A}
2N/A
2N/Aconst krb5_cc_ops krb5_mcc_ops = {
2N/A 0,
2N/A "MEMORY",
2N/A krb5_mcc_get_name,
2N/A krb5_mcc_resolve,
2N/A krb5_mcc_generate_new,
2N/A krb5_mcc_initialize,
2N/A krb5_mcc_destroy,
2N/A krb5_mcc_close,
2N/A krb5_mcc_store,
2N/A krb5_mcc_retrieve,
2N/A krb5_mcc_get_principal,
2N/A krb5_mcc_start_seq_get,
2N/A krb5_mcc_next_cred,
2N/A krb5_mcc_end_seq_get,
2N/A krb5_mcc_remove_cred,
2N/A krb5_mcc_set_flags,
2N/A krb5_mcc_get_flags,
2N/A krb5_mcc_ptcursor_new,
2N/A krb5_mcc_ptcursor_next,
2N/A krb5_mcc_ptcursor_free,
2N/A NULL, /* move */
2N/A krb5_mcc_last_change_time,
2N/A NULL, /* wasdefault */
2N/A krb5_mcc_lock,
2N/A krb5_mcc_unlock,
2N/A};