2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/keytab/kt_memory.c
2N/A *
2N/A * Copyright 2007 by Secure Endpoints Inc.
2N/A *
2N/A * Permission is hereby granted, free of charge, to any person
2N/A * obtaining a copy of this software and associated documentation files
2N/A * (the "Software"), to deal in the Software without restriction,
2N/A * including without limitation the rights to use, copy, modify, merge,
2N/A * publish, distribute, sublicense, and/or sell copies of the Software,
2N/A * and to permit persons to whom the Software is furnished to do so,
2N/A * subject to the following conditions:
2N/A *
2N/A * The above copyright notice and this permission notice shall be
2N/A * included in all copies or substantial portions of the Software.
2N/A *
2N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2N/A * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2N/A * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2N/A * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2N/A * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2N/A * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2N/A * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2N/A * SOFTWARE.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "kt-int.h"
2N/A#include <stdio.h>
2N/A
2N/A#ifndef LEAN_CLIENT
2N/A
2N/A#define HEIMDAL_COMPATIBLE
2N/A
2N/A/*
2N/A * Information needed by internal routines of the file-based ticket
2N/A * cache implementation.
2N/A */
2N/A
2N/A
2N/A/*
2N/A * Constants
2N/A */
2N/A
2N/A/*
2N/A * Types
2N/A */
2N/A/* From krb5.h:
2N/A * typedef struct krb5_keytab_entry_st {
2N/A * krb5_magic magic;
2N/A * krb5_principal principal; principal of this key
2N/A * krb5_timestamp timestamp; time entry written to keytable
2N/A * krb5_kvno vno; key version number
2N/A * krb5_keyblock key; the secret key
2N/A *} krb5_keytab_entry;
2N/A */
2N/A
2N/A/* Individual key entries within a table, in a linked list */
2N/Atypedef struct _krb5_mkt_link {
2N/A struct _krb5_mkt_link *next;
2N/A krb5_keytab_entry *entry;
2N/A} krb5_mkt_link, *krb5_mkt_cursor;
2N/A
2N/A/* Per-keytab data header */
2N/Atypedef struct _krb5_mkt_data {
2N/A char *name; /* Name of the keytab */
2N/A k5_mutex_t lock; /* Thread-safety - all but link */
2N/A krb5_int32 refcount;
2N/A krb5_mkt_cursor link;
2N/A} krb5_mkt_data;
2N/A
2N/A/* List of memory key tables */
2N/Atypedef struct _krb5_mkt_list_node {
2N/A struct _krb5_mkt_list_node *next;
2N/A krb5_keytab keytab;
2N/A} krb5_mkt_list_node;
2N/A
2N/A/* Iterator over memory key tables */
2N/Atypedef struct _krb5_mkt_ptcursor_data {
2N/A struct _krb5_mkt_list_node *cur;
2N/A} krb5_mkt_ptcursor_data;
2N/A
2N/A/*
2N/A * Globals
2N/A */
2N/Astatic krb5_mkt_list_node * krb5int_mkt_list = NULL;
2N/Astatic k5_mutex_t krb5int_mkt_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
2N/A
2N/A/*
2N/A * Macros
2N/A */
2N/A#define KTLOCK(id) k5_mutex_lock(&(((krb5_mkt_data *)(id)->data)->lock))
2N/A#define KTUNLOCK(id) k5_mutex_unlock(&(((krb5_mkt_data *)(id)->data)->lock))
2N/A#define KTCHECKLOCK(id) k5_mutex_assert_locked(&(((krb5_mkt_data *)(id)->data)->lock))
2N/A
2N/A#define KTGLOCK k5_mutex_lock(&krb5int_mkt_mutex)
2N/A#define KTGUNLOCK k5_mutex_unlock(&krb5int_mkt_mutex)
2N/A#define KTGCHECKLOCK k5_mutex_assert_locked(&krb5int_mkt_mutex)
2N/A
2N/A#define KTLINK(id) (((krb5_mkt_data *)(id)->data)->link)
2N/A#define KTREFCNT(id) (((krb5_mkt_data *)(id)->data)->refcount)
2N/A#define KTNAME(id) (((krb5_mkt_data *)(id)->data)->name)
2N/A
2N/Aextern const struct _krb5_kt_ops krb5_mkt_ops;
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_resolve(krb5_context, const char *, krb5_keytab *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_name(krb5_context, krb5_keytab, char *, unsigned int);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_close(krb5_context, krb5_keytab);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_entry(krb5_context, krb5_keytab, krb5_const_principal, krb5_kvno,
2N/A krb5_enctype, krb5_keytab_entry *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_start_seq_get(krb5_context, krb5_keytab, krb5_kt_cursor *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_next(krb5_context, krb5_keytab, krb5_keytab_entry *,
2N/A krb5_kt_cursor *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_end_get(krb5_context, krb5_keytab, krb5_kt_cursor *);
2N/A
2N/A/* routines to be included on extended version (write routines) */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_add(krb5_context, krb5_keytab, krb5_keytab_entry *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_remove(krb5_context, krb5_keytab, krb5_keytab_entry *);
2N/A
2N/Aint
2N/Akrb5int_mkt_initialize(void)
2N/A{
2N/A return k5_mutex_finish_init(&krb5int_mkt_mutex);
2N/A}
2N/A
2N/Avoid
2N/Akrb5int_mkt_finalize(void)
2N/A{
2N/A krb5_mkt_list_node *node, *next_node;
2N/A krb5_mkt_cursor cursor, next_cursor;
2N/A
2N/A k5_mutex_destroy(&krb5int_mkt_mutex);
2N/A
2N/A for (node = krb5int_mkt_list; node; node = next_node) {
2N/A next_node = node->next;
2N/A
2N/A /* destroy the contents of node->keytab */
2N/A free(KTNAME(node->keytab));
2N/A
2N/A /* free the keytab entries */
2N/A for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
2N/A next_cursor = cursor->next;
2N/A /* the call to krb5_kt_free_entry uses a NULL in place of the
2N/A * krb5_context since we know that the context isn't used by
2N/A * krb5_kt_free_entry or krb5_free_principal. */
2N/A krb5_kt_free_entry(NULL, cursor->entry);
2N/A free(cursor->entry);
2N/A free(cursor);
2N/A }
2N/A
2N/A /* destroy the lock */
2N/A k5_mutex_destroy(&(((krb5_mkt_data *)node->keytab->data)->lock));
2N/A
2N/A /* free the private data */
2N/A free(node->keytab->data);
2N/A
2N/A /* and the keytab */
2N/A free(node->keytab);
2N/A
2N/A /* and finally the node */
2N/A free(node);
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Acreate_list_node(const char *name, krb5_mkt_list_node **listp)
2N/A{
2N/A krb5_mkt_list_node *list;
2N/A krb5_mkt_data *data = NULL;
2N/A krb5_error_code err;
2N/A
2N/A *listp = NULL;
2N/A
2N/A list = calloc(1, sizeof(krb5_mkt_list_node));
2N/A if (list == NULL) {
2N/A err = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A list->keytab = calloc(1, sizeof(struct _krb5_kt));
2N/A if (list->keytab == NULL) {
2N/A err = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A list->keytab->ops = &krb5_mkt_ops;
2N/A
2N/A data = calloc(1, sizeof(krb5_mkt_data));
2N/A if (data == NULL) {
2N/A err = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A data->link = NULL;
2N/A data->refcount = 0;
2N/A
2N/A data->name = strdup(name);
2N/A if (data->name == NULL) {
2N/A err = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A err = k5_mutex_init(&data->lock);
2N/A if (err)
2N/A goto cleanup;
2N/A
2N/A list->keytab->data = data;
2N/A list->keytab->magic = KV5M_KEYTAB;
2N/A list->next = NULL;
2N/A *listp = list;
2N/A return 0;
2N/A
2N/Acleanup:
2N/A /* data->lock was initialized last, so no need to destroy. */
2N/A if (data)
2N/A free(data->name);
2N/A free(data);
2N/A if (list)
2N/A free(list->keytab);
2N/A free(list);
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * This is an implementation specific resolver. It returns a keytab
2N/A * initialized with memory keytab routines.
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)
2N/A{
2N/A krb5_mkt_list_node *list;
2N/A krb5_error_code err = 0;
2N/A
2N/A *id = NULL;
2N/A
2N/A /* First determine if a memory keytab of this name already exists */
2N/A err = KTGLOCK;
2N/A if (err)
2N/A return err;
2N/A
2N/A for (list = krb5int_mkt_list; list; list = list->next) {
2N/A if (strcmp(name,KTNAME(list->keytab)) == 0)
2N/A break;
2N/A }
2N/A
2N/A if (!list) {
2N/A /* We will now create the new key table with the specified name.
2N/A * We do not drop the global lock, therefore the name will indeed
2N/A * be unique when we add it.
2N/A */
2N/A err = create_list_node(name, &list);
2N/A if (err)
2N/A goto done;
2N/A list->next = krb5int_mkt_list;
2N/A krb5int_mkt_list = list;
2N/A }
2N/A
2N/A /* Increment the reference count on the keytab we found or created. */
2N/A err = KTLOCK(list->keytab);
2N/A if (err)
2N/A goto done;
2N/A KTREFCNT(list->keytab)++;
2N/A KTUNLOCK(list->keytab);
2N/A *id = list->keytab;
2N/Adone:
2N/A KTGUNLOCK;
2N/A return err;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * "Close" a memory-based keytab. This is effectively a no-op.
2N/A * We check to see if the keytab exists and that is about it.
2N/A * Closing a file keytab does not destroy the contents. Closing
2N/A * a memory keytab shouldn't either.
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_close(krb5_context context, krb5_keytab id)
2N/A{
2N/A krb5_mkt_list_node **listp;
2N/A#ifdef HEIMDAL_COMPATIBLE
2N/A krb5_mkt_list_node *node;
2N/A krb5_mkt_data * data;
2N/A#endif
2N/A krb5_error_code err = 0;
2N/A
2N/A /* First determine if a memory keytab of this name already exists */
2N/A err = KTGLOCK;
2N/A if (err)
2N/A return(err);
2N/A
2N/A for (listp = &krb5int_mkt_list; *listp; listp = &((*listp)->next))
2N/A {
2N/A if (id == (*listp)->keytab) {
2N/A /* Found */
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (*listp == NULL) {
2N/A /* The specified keytab could not be found */
2N/A err = KRB5_KT_NOTFOUND;
2N/A goto done;
2N/A }
2N/A
2N/A /* reduce the refcount and return */
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A goto done;
2N/A
2N/A KTREFCNT(id)--;
2N/A KTUNLOCK(id);
2N/A
2N/A#ifdef HEIMDAL_COMPATIBLE
2N/A /* In Heimdal if the refcount hits 0, the MEMORY keytab is
2N/A * destroyed since there is no krb5_kt_destroy function.
2N/A * There is no need to lock the entry while performing
2N/A * these operations as the refcount will be 0 and we are
2N/A * holding the global lock.
2N/A */
2N/A data = (krb5_mkt_data *)id->data;
2N/A if (data->refcount == 0) {
2N/A krb5_mkt_cursor cursor, next_cursor;
2N/A
2N/A node = *listp;
2N/A *listp = node->next;
2N/A
2N/A /* destroy the contents of node->keytab (aka id) */
2N/A free(data->name);
2N/A
2N/A /* free the keytab entries */
2N/A for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
2N/A next_cursor = cursor->next;
2N/A
2N/A krb5_kt_free_entry(context, cursor->entry);
2N/A free(cursor->entry);
2N/A free(cursor);
2N/A }
2N/A
2N/A /* destroy the lock */
2N/A k5_mutex_destroy(&(data->lock));
2N/A
2N/A /* free the private data */
2N/A free(data);
2N/A
2N/A /* and the keytab */
2N/A free(node->keytab);
2N/A
2N/A /* and finally the node */
2N/A free(node);
2N/A }
2N/A#endif /* HEIMDAL_COMPATIBLE */
2N/A
2N/Adone:
2N/A KTGUNLOCK;
2N/A return(err);
2N/A}
2N/A
2N/A/*
2N/A * This is the get_entry routine for the memory based keytab implementation.
2N/A * It either retrieves the entry or returns an error.
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_entry(krb5_context context, krb5_keytab id,
2N/A krb5_const_principal principal, krb5_kvno kvno,
2N/A krb5_enctype enctype, krb5_keytab_entry *out_entry)
2N/A{
2N/A krb5_mkt_cursor cursor;
2N/A krb5_keytab_entry *entry, *match = NULL;
2N/A krb5_error_code err = 0;
2N/A int found_wrong_kvno = 0;
2N/A krb5_boolean similar = 0;
2N/A
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A return err;
2N/A
2N/A for (cursor = KTLINK(id); cursor && cursor->entry; cursor = cursor->next) {
2N/A entry = cursor->entry;
2N/A
2N/A /* if the principal isn't the one requested, continue to the next. */
2N/A
2N/A if (!krb5_principal_compare(context, principal, entry->principal))
2N/A continue;
2N/A
2N/A /* if the enctype is not ignored and doesn't match,
2N/A and continue to the next */
2N/A if (enctype != IGNORE_ENCTYPE) {
2N/A if ((err = krb5_c_enctype_compare(context, enctype,
2N/A entry->key.enctype,
2N/A &similar))) {
2N/A /* we can't determine the enctype of the entry */
2N/A continue;
2N/A }
2N/A
2N/A if (!similar)
2N/A continue;
2N/A }
2N/A
2N/A if (kvno == IGNORE_VNO) {
2N/A if (match == NULL)
2N/A match = entry;
2N/A else if (entry->vno > match->vno)
2N/A match = entry;
2N/A } else {
2N/A if (entry->vno == kvno) {
2N/A match = entry;
2N/A break;
2N/A } else {
2N/A found_wrong_kvno++;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* if we found an entry that matches, ... */
2N/A if (match) {
2N/A out_entry->magic = match->magic;
2N/A out_entry->timestamp = match->timestamp;
2N/A out_entry->vno = match->vno;
2N/A out_entry->key = match->key;
2N/A err = krb5_copy_keyblock_contents(context, &(match->key),
2N/A &(out_entry->key));
2N/A /*
2N/A * Coerce the enctype of the output keyblock in case we
2N/A * got an inexact match on the enctype.
2N/A */
2N/A if(enctype != IGNORE_ENCTYPE)
2N/A out_entry->key.enctype = enctype;
2N/A if(!err) {
2N/A err = krb5_copy_principal(context,
2N/A match->principal,
2N/A &(out_entry->principal));
2N/A }
2N/A } else {
2N/A if (!err)
2N/A err = found_wrong_kvno ? KRB5_KT_KVNONOTFOUND : KRB5_KT_NOTFOUND;
2N/A }
2N/A
2N/A KTUNLOCK(id);
2N/A return(err);
2N/A}
2N/A
2N/A/*
2N/A * Get the name of the memory-based keytab.
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
2N/A{
2N/A int result;
2N/A
2N/A memset(name, 0, len);
2N/A result = snprintf(name, len, "%s:%s", id->ops->prefix, KTNAME(id));
2N/A if (SNPRINTF_OVERFLOW(result, len))
2N/A return(KRB5_KT_NAME_TOOLONG);
2N/A return(0);
2N/A}
2N/A
2N/A/*
2N/A * krb5_mkt_start_seq_get()
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
2N/A{
2N/A krb5_error_code err = 0;
2N/A
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A return(err);
2N/A
2N/A *cursorp = (krb5_kt_cursor)KTLINK(id);
2N/A KTUNLOCK(id);
2N/A
2N/A return(0);
2N/A}
2N/A
2N/A/*
2N/A * krb5_mkt_get_next()
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
2N/A{
2N/A krb5_mkt_cursor mkt_cursor = (krb5_mkt_cursor)*cursor;
2N/A krb5_error_code err = 0;
2N/A
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A return err;
2N/A
2N/A if (mkt_cursor == NULL) {
2N/A KTUNLOCK(id);
2N/A return KRB5_KT_END;
2N/A }
2N/A
2N/A entry->magic = mkt_cursor->entry->magic;
2N/A entry->timestamp = mkt_cursor->entry->timestamp;
2N/A entry->vno = mkt_cursor->entry->vno;
2N/A entry->key = mkt_cursor->entry->key;
2N/A err = krb5_copy_keyblock_contents(context, &(mkt_cursor->entry->key),
2N/A &(entry->key));
2N/A if (!err)
2N/A err = krb5_copy_principal(context, mkt_cursor->entry->principal,
2N/A &(entry->principal));
2N/A if (!err)
2N/A *cursor = (krb5_kt_cursor *)mkt_cursor->next;
2N/A KTUNLOCK(id);
2N/A return(err);
2N/A}
2N/A
2N/A/*
2N/A * krb5_mkt_end_get()
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
2N/A{
2N/A *cursor = NULL;
2N/A return(0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * krb5_mkt_add()
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
2N/A{
2N/A krb5_error_code err = 0;
2N/A krb5_mkt_cursor cursor;
2N/A
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A return err;
2N/A
2N/A cursor = (krb5_mkt_cursor)malloc(sizeof(krb5_mkt_link));
2N/A if (cursor == NULL) {
2N/A err = ENOMEM;
2N/A goto done;
2N/A }
2N/A cursor->entry = (krb5_keytab_entry *)malloc(sizeof(krb5_keytab_entry));
2N/A if (cursor->entry == NULL) {
2N/A free(cursor);
2N/A err = ENOMEM;
2N/A goto done;
2N/A }
2N/A cursor->entry->magic = entry->magic;
2N/A cursor->entry->timestamp = entry->timestamp;
2N/A cursor->entry->vno = entry->vno;
2N/A err = krb5_copy_keyblock_contents(context, &(entry->key),
2N/A &(cursor->entry->key));
2N/A if (err) {
2N/A free(cursor->entry);
2N/A free(cursor);
2N/A goto done;
2N/A }
2N/A
2N/A err = krb5_copy_principal(context, entry->principal, &(cursor->entry->principal));
2N/A if (err) {
2N/A krb5_free_keyblock_contents(context, &(cursor->entry->key));
2N/A free(cursor->entry);
2N/A free(cursor);
2N/A goto done;
2N/A }
2N/A
2N/A if (KTLINK(id) == NULL) {
2N/A cursor->next = NULL;
2N/A KTLINK(id) = cursor;
2N/A } else {
2N/A cursor->next = KTLINK(id);
2N/A KTLINK(id) = cursor;
2N/A }
2N/A
2N/Adone:
2N/A KTUNLOCK(id);
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * krb5_mkt_remove()
2N/A */
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_mkt_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
2N/A{
2N/A krb5_mkt_cursor *pcursor, next;
2N/A krb5_error_code err = 0;
2N/A
2N/A err = KTLOCK(id);
2N/A if (err)
2N/A return err;
2N/A
2N/A if ( KTLINK(id) == NULL ) {
2N/A err = KRB5_KT_NOTFOUND;
2N/A goto done;
2N/A }
2N/A
2N/A for ( pcursor = &KTLINK(id); *pcursor; pcursor = &(*pcursor)->next ) {
2N/A if ( (*pcursor)->entry->vno == entry->vno &&
2N/A (*pcursor)->entry->key.enctype == entry->key.enctype &&
2N/A krb5_principal_compare(context, (*pcursor)->entry->principal, entry->principal))
2N/A break;
2N/A }
2N/A
2N/A if (!*pcursor) {
2N/A err = KRB5_KT_NOTFOUND;
2N/A goto done;
2N/A }
2N/A
2N/A krb5_kt_free_entry(context, (*pcursor)->entry);
2N/A free((*pcursor)->entry);
2N/A next = (*pcursor)->next;
2N/A free(*pcursor);
2N/A (*pcursor) = next;
2N/A
2N/Adone:
2N/A KTUNLOCK(id);
2N/A return err;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * krb5_mkt_ops
2N/A */
2N/A
2N/Aconst struct _krb5_kt_ops krb5_mkt_ops = {
2N/A 0,
2N/A "MEMORY", /* Prefix -- this string should not appear anywhere else! */
2N/A krb5_mkt_resolve,
2N/A krb5_mkt_get_name,
2N/A krb5_mkt_close,
2N/A krb5_mkt_get_entry,
2N/A krb5_mkt_start_seq_get,
2N/A krb5_mkt_get_next,
2N/A krb5_mkt_end_get,
2N/A krb5_mkt_add,
2N/A krb5_mkt_remove,
2N/A NULL
2N/A};
2N/A
2N/A#endif /* LEAN_CLIENT */