---

 krb5-1.4.3-kwc/src/aclocal.m4                   |    8 
 krb5-1.4.3-kwc/src/configure.in                 |    2 
 krb5-1.4.3-kwc/src/include/krb5/autoconf.h.in   |    9 
 krb5-1.4.3-kwc/src/lib/krb5/ccache/Makefile.in  |   12 
 krb5-1.4.3-kwc/src/lib/krb5/ccache/cc_keyring.c | 2042 ++++++++++++++++++++++++
 krb5-1.4.3-kwc/src/lib/krb5/ccache/ccbase.c     |   18 
 krb5-1.4.3-kwc/src/lib/krb5/os/ccdefname.c      |    4 
 7 files changed, 2090 insertions(+), 5 deletions(-)

diff -puN src/aclocal.m4~add_keyring_ccache src/aclocal.m4
--- krb5-1.4.3/src/aclocal.m4~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/aclocal.m4	2006-05-05 10:10:48.000000000 -0400
@@ -82,6 +82,7 @@ KRB5_AC_PRAGMA_WEAK_REF
 KRB5_LIB_PARAMS
 KRB5_AC_INITFINI
 KRB5_AC_ENABLE_THREADS
+KRB5_AC_LIBKEYUTILS
 ])dnl
 
 dnl Maintainer mode, akin to what automake provides, 'cept we don't
@@ -1746,3 +1747,10 @@ AC_DEFUN([KRB5_AC_LIBUTIL],
   UTIL_LIB=-lutil])dnl
 AC_SUBST(UTIL_LIB)
 ])
+dnl
+dnl
+dnl On Linux, if libkeyutils exists we want to include it
+AC_DEFUN([KRB5_AC_LIBKEYUTILS],
+	[AC_CHECK_LIB(keyutils, add_key)
+])
+
diff -puN src/configure.in~add_keyring_ccache src/configure.in
--- krb5-1.4.3/src/configure.in~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/configure.in	2006-05-05 10:09:28.000000000 -0400
@@ -64,7 +64,7 @@ LIBUTIL=-lutil
 ])
 AC_SUBST(LIBUTIL)
 dnl for kdc
-AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h sys/sockio.h ifaddrs.h unistd.h)
+AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h sys/sockio.h ifaddrs.h unistd.h keyutils.h)
 AC_CHECK_FUNCS(openlog syslog closelog strftime vsprintf)
 KRB5_NEED_PROTO([#include <string.h>
 #ifdef HAVE_UNISTD_H
diff -puN src/include/krb5/autoconf.h.in~add_keyring_ccache src/include/krb5/autoconf.h.in
--- krb5-1.4.3/src/include/krb5/autoconf.h.in~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/include/krb5/autoconf.h.in	2006-05-05 10:10:25.000000000 -0400
@@ -108,6 +108,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define to 1 if you have the `keyutils' library (-lkeyutils). */
+#undef HAVE_LIBKEYUTILS
+
 /* Define to 1 if you have the `nsl' library (-lnsl). */
 #undef HAVE_LIBNSL
 
@@ -418,9 +421,11 @@
 /* Define to `int' if <sys/types.h> doesn't define. */
 #undef gid_t
 
-/* Define as `__inline' if that's what the C compiler calls it, or to nothing
-   if it is not supported. */
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
 #undef inline
+#endif
 
 /* Define krb5_sigtype to type of signal handler */
 #undef krb5_sigtype
diff -puN src/lib/krb5/ccache/ccbase.c~add_keyring_ccache src/lib/krb5/ccache/ccbase.c
--- krb5-1.4.3/src/lib/krb5/ccache/ccbase.c~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/lib/krb5/ccache/ccbase.c	2006-05-05 10:12:30.000000000 -0400
@@ -38,6 +38,9 @@ struct krb5_cc_typelist {
     struct krb5_cc_typelist *next;
 };
 extern const krb5_cc_ops krb5_mcc_ops;
+#ifdef HAVE_LIBKEYUTILS
+extern const krb5_cc_ops krb5_krcc_ops;
+#endif
 
 #ifdef _WIN32
 extern const krb5_cc_ops krb5_lcc_ops;
@@ -45,12 +48,21 @@ static struct krb5_cc_typelist cc_lcc_en
 static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, &cc_lcc_entry };
 #else
 static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, NULL };
+#ifdef HAVE_LIBKEYUTILS
+static struct krb5_cc_typelist cc_file_entry = { &krb5_cc_file_ops,
+						 &cc_mcc_entry };
+static struct krb5_cc_typelist cc_krcc_entry = { &krb5_krcc_ops,
+						 &cc_file_entry };
+#endif /* HAVE_LIBKEYUTILS */
 #endif
 
 static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops,
 						&cc_mcc_entry };
-
+#ifdef HAVE_LIBKEYUTILS
+static struct krb5_cc_typelist *cc_typehead = &cc_krcc_entry;
+#else
 static struct krb5_cc_typelist *cc_typehead = &cc_fcc_entry;
+#endif
 static k5_mutex_t cc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
 
 int
@@ -73,14 +85,18 @@ krb5int_cc_initialize(void)
 void
 krb5int_cc_finalize(void)
 {
+#ifndef HAVE_LIBKEYUTILS
     struct krb5_cc_typelist *t, *t_next;
+#endif
     k5_mutex_destroy(&cc_typelist_lock);
     k5_mutex_destroy(&krb5int_cc_file_mutex);
     k5_mutex_destroy(&krb5int_mcc_mutex);
+#ifndef HAVE_LIBKEYUTILS
     for (t = cc_typehead; t != &cc_fcc_entry; t = t_next) {
 	t_next = t->next;
 	free(t);
     }
+#endif
 }
 
 
diff -puN /dev/null src/lib/krb5/ccache/cc_keyring.c
--- /dev/null	2006-08-21 20:21:35.878059764 -0400
+++ krb5-1.4.3-kwc/src/lib/krb5/ccache/cc_keyring.c	2006-08-21 14:24:58.000000000 -0400
@@ -0,0 +1,2042 @@
+/*
+ * lib/krb5/ccache/cc_keyring.c
+ *
+ * Copyright (c) 2006
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization.  If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * Implementation of a credentials cache stored in the Linux keyring facility
+ *
+ * Some assumptions:
+ *
+ *	- A credentials cache "file" == a keyring with separate keys
+ *	  for the information in the ccache (see below)
+ *	- A credentials cache keyring will contain only keys,
+ *	  not other keyrings
+ *	- Each Kerberos ticket will have its own key within the ccache keyring
+ *	- The principal information for the ccache is stored in a
+ *	  special key, which is not counted in the 'numkeys' count
+ */
+#include "k5-int.h"
+#include <errno.h>
+#include <keyutils.h>
+
+#if 0
+#define KRCC_DEBUG	    1
+#define KRCC_EXTRA_DEBUG    1	/* Extra debugging -- should be removed */
+#endif
+
+#if KRCC_DEBUG
+void debug_print(char *fmt, ...);	/* prototype to silence warning */
+#include <syslog.h>
+#define DEBUG_PRINT(x) debug_print x
+void
+debug_print(char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+#ifdef DEBUG_STDERR
+	vfprintf(stderr, fmt, ap);
+#else
+	vsyslog(LOG_ERR, fmt, ap);
+#endif
+	va_end(ap);
+}
+#else
+#define DEBUG_PRINT(x)
+#endif
+
+/*
+ * We always use "user" key type
+ */
+#define KRCC_KEY_TYPE_USER "user"
+
+/*
+ * We create ccaches as separate keyrings
+ */
+#define KRCC_KEY_TYPE_KEYRING "keyring"
+
+/*
+ * Special name of the key within a ccache keyring
+ * holding principal information
+ */
+#define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
+
+/* XXX The following two belong in some library header since outside
+ * programs will need to use these same names...
+ */
+/*
+ * Special name for key to communicate key serial numbers
+ * This is used by the Linux gssd process to pass the
+ * user's keyring values it gets on an request upcall.
+ * The format of the contents should be
+ *    <session_key>:<process_key>:<thread_key>
+ */
+#define KRCC_SPEC_IDS_KEYNAME "_gssd_keyring_ids_"
+
+/*
+ * Special name for the key to communicate the name(s)
+ * of credentials caches to be used for requests.
+ * This should currently contain a single name, but
+ * in the future may contain a list that may be
+ * intelligently chosen from.
+ */
+#define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
+
+#define KRB5_OK 0
+
+/* Hopefully big enough to hold a serialized credential */
+#define GUESS_CRED_SIZE 2048
+
+#define ALLOC(NUM,TYPE) \
+    (((NUM) <= (((size_t)0-1)/ sizeof(TYPE)))           \
+     ? (TYPE *) calloc((NUM), sizeof(TYPE))             \
+     : (errno = ENOMEM,(TYPE *) 0))
+
+#define CHECK_N_GO(ret, errdest) if (ret != KRB5_OK) goto errdest
+#define CHECK(ret) if (ret != KRB5_OK) goto errout
+#define CHECK_OUT(ret) if (ret != KRB5_OK) return ret
+
+typedef struct krb5_krcc_ring_ids {
+    key_serial_t	session;
+    key_serial_t	process;
+    key_serial_t	thread;
+} krb5_krcc_ring_ids_t;
+
+typedef struct _krb5_krcc_cursor
+{
+    int     numkeys;
+    int     currkey;
+    key_serial_t princ_id;
+    key_serial_t *keys;
+} *krb5_krcc_cursor;
+
+/*
+ * This represents a credentials cache "file"
+ * where ring_id is the keyring serial number for
+ * this credentials cache "file".  Each key
+ * in the keyring contains a separate key.
+ */
+typedef struct _krb5_krcc_data
+{
+    char   *name;		/* Name for this credentials cache */
+    k5_mutex_t lock;		/* synchronization */
+    key_serial_t ring_id;	/* keyring representing ccache */
+    key_serial_t princ_id;	/* key holding principal info */
+    int     numkeys;		/* # of keys in this ring
+				 * (does NOT include principal info) */
+} krb5_krcc_data;
+
+/* Passed internally to assure we don't go past the bounds of our buffer */
+typedef struct _krb5_krcc_buffer_cursor
+{
+    char   *bpp;
+    char   *endp;
+} krb5_krcc_bc;
+
+/*
+ * Internal functions (exported via the krb5_krcc_ops)
+ */
+
+extern const krb5_cc_ops krb5_krcc_ops;
+
+static const char *KRB5_CALLCONV krb5_krcc_get_name
+    (krb5_context, krb5_ccache id);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_resolve
+    (krb5_context, krb5_ccache * id, const char *residual);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_generate_new
+    (krb5_context, krb5_ccache * id);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_initialize
+    (krb5_context, krb5_ccache id, krb5_principal princ);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_destroy
+    (krb5_context, krb5_ccache id);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_close
+    (krb5_context, krb5_ccache id);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_store
+    (krb5_context, krb5_ccache id, krb5_creds * creds);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_retrieve
+    (krb5_context, krb5_ccache id, krb5_flags whichfields,
+     krb5_creds * mcreds, krb5_creds * creds);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_get_principal
+    (krb5_context, krb5_ccache id, krb5_principal * princ);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_start_seq_get
+    (krb5_context, krb5_ccache id, krb5_cc_cursor * cursor);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_next_cred
+    (krb5_context, krb5_ccache id, krb5_cc_cursor * cursor,
+     krb5_creds * creds);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_end_seq_get
+    (krb5_context, krb5_ccache id, krb5_cc_cursor * cursor);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_remove_cred
+    (krb5_context context, krb5_ccache cache, krb5_flags flags,
+     krb5_creds * creds);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_set_flags
+    (krb5_context, krb5_ccache id, krb5_flags flags);
+
+#ifdef HAVE_GET_FLAGS
+static krb5_error_code KRB5_CALLCONV krb5_krcc_get_flags
+    (krb5_context context, krb5_ccache id, krb5_flags * flags);
+#endif
+
+/*
+ * Internal utility functions
+ */
+
+static krb5_error_code krb5_krcc_clearcache
+    (krb5_context context, krb5_ccache id);
+
+static krb5_error_code krb5_krcc_new_data(const char *, krb5_krcc_data **);
+
+static krb5_error_code krb5_krcc_save_principal
+    (krb5_context context, krb5_ccache id, krb5_principal princ);
+
+static krb5_error_code krb5_krcc_retrieve_principal
+    (krb5_context context, krb5_ccache id, krb5_principal * princ);
+
+static int krb5_krcc_get_ring_ids(krb5_krcc_ring_ids_t *p);
+
+/* Routines to parse a key from a keyring into a cred structure */
+static krb5_error_code krb5_krcc_parse
+    (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_cred
+    (krb5_context context, krb5_ccache id, krb5_creds * creds,
+     char *payload, int psize);
+static krb5_error_code krb5_krcc_parse_principal
+    (krb5_context context, krb5_ccache id, krb5_principal * princ,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_keyblock
+    (krb5_context context, krb5_ccache id, krb5_keyblock * keyblock,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_times
+    (krb5_context context, krb5_ccache id, krb5_ticket_times * t,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_krb5data
+    (krb5_context context, krb5_ccache id, krb5_data * data,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_int32
+    (krb5_context context, krb5_ccache id, krb5_int32 * i, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_octet
+    (krb5_context context, krb5_ccache id, krb5_octet * octet,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_addrs
+    (krb5_context context, krb5_ccache id, krb5_address *** a,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_addr
+    (krb5_context context, krb5_ccache id, krb5_address * a,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_authdata
+    (krb5_context context, krb5_ccache id, krb5_authdata *** ad,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_authdatum
+    (krb5_context context, krb5_ccache id, krb5_authdata * ad,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_parse_ui_2
+    (krb5_context, krb5_ccache id, krb5_ui_2 * i, krb5_krcc_bc * bc);
+
+/* Routines to unparse a cred structure into keyring key */
+static krb5_error_code krb5_krcc_unparse
+    (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_cred
+    (krb5_context context, krb5_ccache id, krb5_creds * creds,
+     char **datapp, unsigned int *lenptr);
+static krb5_error_code krb5_krcc_unparse_principal
+    (krb5_context, krb5_ccache id, krb5_principal princ, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_keyblock
+    (krb5_context, krb5_ccache id, krb5_keyblock * keyblock,
+     krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_times
+    (krb5_context, krb5_ccache id, krb5_ticket_times * t, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_krb5data
+    (krb5_context, krb5_ccache id, krb5_data * data, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_int32
+    (krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_octet
+    (krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_addrs
+    (krb5_context, krb5_ccache, krb5_address ** a, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_addr
+    (krb5_context, krb5_ccache, krb5_address * a, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_authdata
+    (krb5_context, krb5_ccache, krb5_authdata ** ad, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_authdatum
+    (krb5_context, krb5_ccache, krb5_authdata * ad, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_ui_4
+    (krb5_context, krb5_ccache id, krb5_ui_4 i, krb5_krcc_bc * bc);
+static krb5_error_code krb5_krcc_unparse_ui_2
+    (krb5_context, krb5_ccache id, krb5_int32 i, krb5_krcc_bc * bc);
+
+/* Note the following is a stub function for Linux */
+extern krb5_error_code krb5_change_cache(void);
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Creates/refreshes the cred cache id.  If the cache exists, its
+ * contents are destroyed.
+ *
+ * Errors:
+ * system errors
+ * permission errors
+ */
+
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_initialize(krb5_context context, krb5_ccache id,
+		     krb5_principal princ)
+{
+    krb5_error_code kret;
+
+    DEBUG_PRINT(("krb5_krcc_initialize: entered\n"));
+
+    kret = k5_mutex_lock(&((krb5_krcc_data *) id->data)->lock);
+    if (kret)
+	return kret;
+
+    kret = krb5_krcc_clearcache(context, id);
+    if (kret != KRB5_OK)
+	goto out;
+
+    kret = krb5_krcc_save_principal(context, id, princ);
+    if (kret == KRB5_OK)
+	krb5_change_cache();
+
+out:
+    k5_mutex_unlock(&((krb5_krcc_data *) id->data)->lock);
+    return kret;
+}
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Closes the file cache, invalidates the id, and frees any resources
+ * associated with the cache.
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_close(krb5_context context, krb5_ccache id)
+{
+    DEBUG_PRINT(("krb5_krcc_close: entered\n"));
+
+    krb5_xfree(id);
+    return KRB5_OK;
+}
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Clears out a ccache keyring, unlinking all keys within it
+ *
+ * Requires:
+ * Must be called with mutex locked.
+ *
+ * Errors:
+ * system errors
+ */
+
+static  krb5_error_code
+krb5_krcc_clearcache(krb5_context context, krb5_ccache id)
+{
+    krb5_krcc_data *d;
+    key_serial_t *keys;
+    unsigned int size;
+    int     res;
+    int     i;
+
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+
+    d = (krb5_krcc_data *) id->data;
+
+    DEBUG_PRINT(("krb5_krcc_clearcache: ring_id %d, princ_id %d, "
+		 "numkeys is %d\n", d->ring_id, d->princ_id, d->numkeys));
+    if (d->numkeys > 0) {
+
+	size = (d->numkeys + 1) * sizeof(key_serial_t);	/* +1 for princ key */
+	keys = malloc(size);
+  reread:
+	if (!keys)
+	    return KRB5_CC_NOMEM;
+
+	res = keyctl_read(d->ring_id, (char *) keys, size);
+	if (res < 0) {
+	    free(keys);
+	    return errno;
+	}
+	if (res != size) {	/* are there more keys than we thought? */
+	    DEBUG_PRINT(("krb5_krcc_clearcache: numkeys %d, res %d, "
+			 "size %d reallocing key array\n",
+			 d->numkeys, res, size));
+	    size = res;
+	    keys = realloc(keys, (unsigned int) res);
+	    goto reread;
+	}
+
+	for (i = 0; i < (size / sizeof(key_serial_t)); i++) {
+	    res = keyctl_unlink(keys[i], d->ring_id);
+	    if (res < 0) {
+		com_err("krb5_krcc_clearcache", errno, "unlinking key");
+		DEBUG_PRINT(("krb5_krcc_clearcache: error unlinking "
+			     "key %d, res == %d\n", keys[i], res));
+	    }
+	}
+	free(keys);
+	d->numkeys = 0;
+	d->princ_id = 0;
+    }
+    return KRB5_OK;
+}
+
+/*
+ * Effects:
+ * Destroys the contents of id.
+ *
+ * Errors:
+ * none
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_destroy(krb5_context context, krb5_ccache id)
+{
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+    int     res;
+
+    DEBUG_PRINT(("krb5_krcc_destroy: entered\n"));
+
+    d = (krb5_krcc_data *) id->data;
+
+    kret = k5_mutex_lock(&d->lock);
+    if (kret)
+	return kret;
+
+    krb5_krcc_clearcache(context, id);
+    krb5_xfree(d->name);
+    res = keyctl_unlink(d->ring_id, KEY_SPEC_SESSION_KEYRING);
+    if (res < 0) {
+	kret = errno;
+	com_err("krb5_krcc_clearcache", errno, "unlinking key");
+	k5_mutex_unlock(&d->lock);
+	return kret;
+    }
+    k5_mutex_destroy(&d->lock);
+    krb5_xfree(d);
+    krb5_xfree(id);
+
+    krb5_change_cache();
+
+    return KRB5_OK;
+}
+
+
+/*
+ * Requires:
+ * residual is a legal path name, and a null-terminated string
+ *
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * creates a cred cache that will reside in the keyring with
+ * a name of <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.
+ * permission errors
+ */
+
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_resolve(krb5_context context, krb5_ccache * id, const char *full_residual)
+{
+    krb5_ccache lid;
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+    key_serial_t key;
+    key_serial_t pkey = 0;
+    int     nkeys = 0;
+    int     res;
+    krb5_krcc_ring_ids_t ids;
+    key_serial_t ring_id;
+    const char *residual;
+
+    DEBUG_PRINT(("krb5_krcc_resolve: entered with name '%s'\n",
+		 full_residual));
+
+    res = krb5_krcc_get_ring_ids(&ids);
+    if (res) {
+	kret = EINVAL; /* XXX */
+	DEBUG_PRINT(("krb5_krcc_resolve: Error getting ring id values!\n"));
+	return kret;
+    }
+
+    if (strncmp(full_residual, "thread:", 7) == 0) {
+	residual = full_residual + 7;
+	ring_id = ids.thread;
+    } else if (strncmp(full_residual, "process:", 8) == 0) {
+	residual = full_residual + 8;
+	ring_id = ids.process;
+    } else {
+	residual = full_residual;
+	ring_id = ids.session;
+    }
+
+#ifdef KRCC_EXTRA_DEBUG
+    DEBUG_PRINT(("krb5_krcc_resolve: searching ring %d for residual '%s'\n",
+    		ring_id, residual)); /* XXX */
+#endif
+
+#if 0
+    /* This looks in the default order of thread, process, session */
+    key = request_key(KRCC_KEY_TYPE_KEYRING, residual, NULL, 0);
+#else
+    /*
+     * Use keyctl_search instead of request_key. If we're supposed
+     * to be looking for a process ccache, we shouldn't find a
+     * thread ccache.
+     * XXX But should we look for a session one if there isn't
+     * a process one?  Same goes for thread.  Should we look in
+     * process and session if not found in thread?
+     *
+     */
+     key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, residual, 0);
+#endif
+    if (key < 0) {		/* XXX May need to check return codes here */
+#ifdef KRCC_EXTRA_DEBUG
+	DEBUG_PRINT(("krb5_krcc_resolve: adding new keyring named '%s' "
+			"to keyring %d\n", residual, ring_id)); /* XXX */
+#endif
+	key = add_key(KRCC_KEY_TYPE_KEYRING, residual, NULL, 0, ring_id);
+	if (key < 0) {
+	    kret = errno;
+	    DEBUG_PRINT(("Error adding new keyring '%s': %s\n",
+			 residual, strerror(errno)));
+	    return kret;
+	}
+#ifdef KRCC_EXTRA_DEBUG
+	DEBUG_PRINT(("krb5_krcc_resolve: new keyring '%s' is "
+			"key %d\n", residual, key)); /* XXX */
+#endif
+    } else {
+	/* Determine key containing principal information */
+	pkey = keyctl_search(key, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, 0);
+#ifdef KRCC_EXTRA_DEBUG
+	DEBUG_PRINT(("keyctl_search for %s returned key %d\n",
+			KRCC_SPEC_PRINC_KEYNAME, pkey)); /* XXX */
+#endif
+	if (pkey < 0) {
+	    DEBUG_PRINT(("Error locating principal info for existing "
+			 "ccache in ring %d: %s\n", key, strerror(errno)));
+	    pkey = 0;
+	}
+	/* Determine how many keys exist */
+	res = keyctl_read(key, NULL, 0);
+	if (res > 0)
+	    nkeys = (res / sizeof(key_serial_t)) - 1;
+	else
+	    nkeys = 0;
+    }
+
+    lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+    if (lid == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = krb5_krcc_new_data(residual, &d);
+    if (kret) {
+	free(lid);
+	return kret;
+    }
+
+    lid->ops = &krb5_krcc_ops;
+
+    DEBUG_PRINT(("krb5_krcc_resolve: ring_id %d, princ_id %d, "
+		 "nkeys %d\n", key, pkey, nkeys));
+    d->ring_id = key;
+    d->princ_id = pkey;
+    d->numkeys = nkeys;
+    lid->data = d;
+    lid->magic = KV5M_CCACHE;
+    *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_krcc_next_cred and
+ * krb5_krcc_end_seq_get.
+ *
+ * If the cache is modified between the time of this call and the time
+ * of the final krb5_krcc_end_seq_get, the results are undefined.
+ *
+ * Errors:
+ * KRB5_CC_NOMEM
+ * system errors
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_start_seq_get(krb5_context context, krb5_ccache id,
+			krb5_cc_cursor * cursor)
+{
+    krb5_krcc_cursor krcursor;
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+    unsigned int size;
+    int     res;
+
+    DEBUG_PRINT(("krb5_krcc_start_seq_get: entered\n"));
+
+    d = id->data;
+    kret = k5_mutex_lock(&d->lock);
+    if (kret)
+	return kret;
+
+    size = sizeof(*krcursor) + ((d->numkeys + 1) * sizeof(key_serial_t));
+
+    krcursor = (krb5_krcc_cursor) malloc(size);
+    if (krcursor == NULL) {
+	k5_mutex_unlock(&d->lock);
+	return KRB5_CC_NOMEM;
+    }
+
+    krcursor->keys = (key_serial_t *) ((char *) krcursor + sizeof(*krcursor));
+    res = keyctl_read(d->ring_id, (char *) krcursor->keys,
+		      ((d->numkeys + 1) * sizeof(key_serial_t)));
+    if (res < 0 || res > ((d->numkeys + 1) * sizeof(key_serial_t))) {
+	DEBUG_PRINT(("Read %d bytes from keyring, numkeys %d: %s\n",
+		     res, d->numkeys, strerror(errno)));
+	free(krcursor);
+	k5_mutex_unlock(&d->lock);
+	return KRB5_CC_IO;
+    }
+
+    krcursor->numkeys = d->numkeys;
+    krcursor->currkey = 0;
+    krcursor->princ_id = d->princ_id;
+
+    k5_mutex_unlock(&d->lock);
+    *cursor = (krb5_cc_cursor) krcursor;
+    return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * cursor is a krb5_cc_cursor originally obtained from
+ * krb5_krcc_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_krcc_next_cred.
+ *
+ * Errors:
+ * system errors
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
+		    krb5_cc_cursor * cursor, krb5_creds * creds)
+{
+    krb5_krcc_cursor krcursor;
+    krb5_error_code kret;
+    int     psize;
+    void   *payload;
+
+    DEBUG_PRINT(("krb5_krcc_next_cred: entered\n"));
+
+    /*
+     * 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.)
+     */
+    krcursor = (krb5_krcc_cursor) * cursor;
+    if (krcursor == NULL)
+	return KRB5_CC_END;
+    memset(creds, 0, sizeof(krb5_creds));
+
+    /* If we're pointing at the entry with the principal, skip it */
+    if (krcursor->keys[krcursor->currkey] == krcursor->princ_id)
+	krcursor->currkey++;
+
+    /* If we're pointing past the end of the keys array, there are no more */
+    if (krcursor->currkey > krcursor->numkeys)
+	return KRB5_CC_END;
+
+    /* Read the key, the right size buffer will ba allocated and returned */
+    psize = keyctl_read_alloc(krcursor->keys[krcursor->currkey], &payload);
+    if (psize == -1) {
+	DEBUG_PRINT(("Error reading key %d: %s\n",
+		     krcursor->keys[krcursor->currkey],
+		     strerror(errno)));
+	kret = KRB5_CC_IO;
+	goto freepayload;
+    }
+    krcursor->currkey++;
+
+    kret = krb5_krcc_parse_cred(context, id, creds, payload, psize);
+
+  freepayload:
+    free(payload);
+    return kret;
+}
+
+/*
+ * Requires:
+ * cursor is a krb5_cc_cursor originally obtained from
+ * krb5_krcc_start_seq_get.
+ *
+ * Modifies:
+ * id, cursor
+ *
+ * Effects:
+ * Finishes sequential processing of the keyring credentials ccache id,
+ * and invalidates the cursor (it must never be used after this call).
+ */
+/* ARGSUSED */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_end_seq_get(krb5_context context, krb5_ccache id,
+		      krb5_cc_cursor * cursor)
+{
+    DEBUG_PRINT(("krb5_krcc_end_seq_get: entered\n"));
+
+    free(*cursor);
+    *cursor = 0L;
+    return KRB5_OK;
+}
+
+/* Utility routine: Creates the back-end data for a keyring cache.
+
+   Call with the global list lock held.  */
+static  krb5_error_code
+krb5_krcc_new_data(const char *name, krb5_krcc_data ** datapp)
+{
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+
+    d = malloc(sizeof(krb5_krcc_data));
+    if (d == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = k5_mutex_init(&d->lock);
+    if (kret) {
+	krb5_xfree(d);
+	return kret;
+    }
+
+    d->name = strdup(name);
+    if (d->name == NULL) {
+	k5_mutex_destroy(&d->lock);
+	krb5_xfree(d);
+	return KRB5_CC_NOMEM;
+    }
+    d->princ_id = 0;
+
+    *datapp = d;
+    return 0;
+}
+
+/*
+ * Effects:
+ * Creates a new keyring cred cache whose name is guaranteed to be
+ * unique.
+ *
+ * Returns:
+ * The filled in krb5_ccache id.
+ *
+ * Errors:
+ * KRB5_CC_NOMEM - there was insufficient memory to allocate the
+ *              krb5_ccache.  id is undefined.
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_generate_new(krb5_context context, krb5_ccache * id)
+{
+    krb5_ccache lid;
+    char    scratch[6 + 1];	/* 6 for the scratch part, +1 for NUL */
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+
+    DEBUG_PRINT(("krb5_krcc_generate_new: entered\n"));
+
+    /* Allocate memory */
+    lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
+    if (lid == NULL)
+	return KRB5_CC_NOMEM;
+
+    /* XXX incorporate lxs's patch to the memory ccache code here XXX */
+    lid->ops = &krb5_krcc_ops;
+
+    (void) strcpy(scratch, "XXXXXX");
+    mktemp(scratch);
+
+    kret = krb5_krcc_new_data(scratch, &d);
+    if (kret) {
+	krb5_xfree(lid);
+	return kret;
+    }
+    lid->data = d;
+    krb5_change_cache();
+    return KRB5_OK;
+}
+
+/*
+ * Requires:
+ * id is a keyring credential cache
+ *
+ * Returns:
+ * The name of the keyring cred cache id.
+ */
+static const char *KRB5_CALLCONV
+krb5_krcc_get_name(krb5_context context, krb5_ccache id)
+{
+    DEBUG_PRINT(("krb5_krcc_get_name: entered\n"));
+    return (char *) ((krb5_krcc_data *) id->data)->name;
+}
+
+/*
+ * Modifies:
+ * id, princ
+ *
+ * Effects:
+ * Retrieves the primary principal from id, as set with
+ * krb5_krcc_initialize.  The principal is returned is allocated
+ * storage that must be freed by the caller via krb5_free_principal.
+ *
+ * Errors:
+ * system errors
+ * KRB5_CC_NOMEM
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_get_principal(krb5_context context, krb5_ccache id,
+			krb5_principal * princ)
+{
+    DEBUG_PRINT(("krb5_krcc_get_principal: entered\n"));
+
+    return krb5_krcc_retrieve_principal(context, id, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_retrieve(krb5_context context, krb5_ccache id,
+		   krb5_flags whichfields, krb5_creds * mcreds,
+		   krb5_creds * creds)
+{
+    DEBUG_PRINT(("krb5_krcc_retrieve: entered\n"));
+
+    return krb5_cc_retrieve_cred_default(context, id, whichfields,
+					 mcreds, creds);
+}
+
+/*
+ * Non-functional stub implementation for krb5_krcc_remove
+ *
+ * Errors:
+ *    KRB5_CC_NOSUPP - not implemented
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_remove_cred(krb5_context context, krb5_ccache cache,
+		      krb5_flags flags, krb5_creds * creds)
+{
+    DEBUG_PRINT(("krb5_krcc_remove_cred: entered (returning KRB5_CC_NOSUPP)\n"));
+
+    return KRB5_CC_NOSUPP;
+}
+
+/*
+ * Requires:
+ * id is a cred cache returned by krb5_krcc_resolve or
+ * krb5_krcc_generate_new, but has not been opened by krb5_krcc_initialize.
+ *
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Sets the operational flags of id to flags.
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
+{
+    DEBUG_PRINT(("krb5_krcc_set_flags: entered\n"));
+
+    return KRB5_OK;
+}
+
+#ifdef HAVE_GET_FLAGS		/* Not included in the 1.4 release? */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags * flags)
+{
+    DEBUG_PRINT(("krb5_krcc_get_flags: entered\n"));
+
+    *flags = 0;
+    return KRB5_OK;
+}
+#endif
+
+/* store: Save away creds in the ccache keyring.  */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
+{
+    krb5_error_code kret;
+    krb5_krcc_data *d = (krb5_krcc_data *) id->data;
+    char   *payload = NULL;
+    unsigned int payloadlen;
+    key_serial_t newkey;
+    char   *keyname = NULL;
+
+    DEBUG_PRINT(("krb5_krcc_store: entered\n"));
+
+    kret = k5_mutex_lock(&d->lock);
+    if (kret)
+	return kret;
+
+    /* Get the service principal name and use it as the key name */
+    kret = krb5_unparse_name(context, creds->server, &keyname);
+    if (kret) {
+	DEBUG_PRINT(("Error unparsing service principal name!\n"));
+	goto errout;
+    }
+
+    /* Serialize credential into memory */
+    kret = krb5_krcc_unparse_cred(context, id, creds, &payload, &payloadlen);
+    if (kret != KRB5_OK)
+	goto errout;
+
+    /* Add new key (credentials) into keyring */
+    DEBUG_PRINT(("krb5_krcc_store: adding new key '%s' to keyring %d\n",
+    			keyname, d->ring_id)); /* XXX */
+    newkey = add_key(KRCC_KEY_TYPE_USER, keyname, payload, payloadlen, d->ring_id);
+    if (newkey < 0) {
+	kret = errno;
+	DEBUG_PRINT(("Error adding user key '%s': %s\n",
+		     keyname, strerror(kret)));
+    } else {
+	d->numkeys++;
+	kret = KRB5_OK;
+    }
+
+  errout:
+    if (keyname)
+	krb5_free_unparsed_name(context, keyname);
+    if (payload)
+	free(payload);
+
+    k5_mutex_unlock(&d->lock);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
+			 krb5_principal princ)
+{
+    krb5_krcc_data *d;
+    krb5_error_code kret;
+    char   *payload;
+    key_serial_t newkey;
+    unsigned int payloadsize;
+    krb5_krcc_bc bc;
+
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+
+    d = (krb5_krcc_data *) id->data;
+
+    payload = malloc(GUESS_CRED_SIZE);
+    if (payload == NULL)
+	return KRB5_CC_NOMEM;
+
+    bc.bpp = payload;
+    bc.endp = payload + GUESS_CRED_SIZE;
+
+    kret = krb5_krcc_unparse_principal(context, id, princ, &bc);
+    CHECK_N_GO(kret, errout);
+
+    /* Add new key into keyring */
+    payloadsize = bc.bpp - payload;
+    DEBUG_PRINT(("krb5_krcc_save_principal: adding new key '%s' "
+    		 "to keyring %d\n",
+		 KRCC_SPEC_PRINC_KEYNAME, d->ring_id)); /* XXX */
+    newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, payload,
+    		     payloadsize, d->ring_id);
+    if (newkey < 0) {
+	kret = errno;
+	DEBUG_PRINT(("Error adding key for principal: %s\n",
+		     strerror(kret)));
+    } else {
+	d->princ_id = newkey;
+	kret = KRB5_OK;
+    }
+
+  errout:
+    free(payload);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
+			     krb5_principal * princ)
+{
+    krb5_krcc_data *d = (krb5_krcc_data *) id->data;
+    krb5_error_code kret;
+    void   *payload = NULL;
+    int     psize;
+    krb5_krcc_bc bc;
+
+    kret = k5_mutex_lock(&d->lock);
+    if (kret)
+	return kret;
+
+    if (!d->princ_id) {
+	princ = 0L;
+	kret = KRB5_FCC_NOFILE;
+	goto errout;
+    }
+
+    psize = keyctl_read_alloc(d->princ_id, &payload);
+    if (psize == -1) {
+	DEBUG_PRINT(("Reading principal key %d: %s\n",
+		     d->princ_id, strerror(errno)));
+	kret = KRB5_CC_IO;
+	goto errout;
+    }
+    bc.bpp = payload;
+    bc.endp = (char *)payload + psize;
+    kret = krb5_krcc_parse_principal(context, id, princ, &bc);
+
+  errout:
+    if (payload)
+	free(payload);
+    k5_mutex_unlock(&d->lock);
+    return kret;
+}
+
+static int
+krb5_krcc_get_ring_ids(krb5_krcc_ring_ids_t *p)
+{
+    key_serial_t ids_key;
+    char ids_buf[128];
+    key_serial_t session, process, thread;
+    long val;
+
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: entered\n"));
+
+    if (!p)
+	return EINVAL;
+
+    /* Use the defaults in case we find no ids key */
+    p->session = KEY_SPEC_SESSION_KEYRING;
+    p->process = KEY_SPEC_PROCESS_KEYRING;
+    p->thread = KEY_SPEC_THREAD_KEYRING;
+
+    /*
+     * Note that in the "normal" case, this will not be found.
+     * The Linux gssd creates this key to communicate the
+     * user's key serial numbers.
+     */
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: requesting key type '%s' name '%s'\n",
+		KRCC_KEY_TYPE_USER, KRCC_SPEC_IDS_KEYNAME));
+    ids_key = request_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_IDS_KEYNAME, NULL, 0);
+    if (ids_key < 0)
+	goto out;
+
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: processing key %d\n", ids_key));
+    /*
+     * Read and parse the ids file
+     */
+    /* XXX
+     * This should also carry the "active" ccache name?
+     * Or better yet, we should call krb5_cc_set_default_name
+     * with the context to be used so we get the right one.
+     * This part should be done in krb5int_cc_default()???
+     * XXX
+     */
+    memset(ids_buf, '\0', sizeof(ids_buf));
+    val = keyctl_read(ids_key, ids_buf, sizeof(ids_buf));
+    if (val > sizeof(ids_buf))
+	goto out;
+
+#ifdef KRCC_EXTRA_DEBUG
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: read %d bytes ('%s') "
+		"from key %d\n", val, ids_buf, ids_key));
+#endif
+    /* Note that a format of "%i:%i:%i" does not work here */
+    val = sscanf(ids_buf, "%d:%d:%d", &session, &process, &thread);
+    if (val != 3)
+	goto out;
+
+#ifdef KRCC_EXTRA_DEBUG
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: parsed %d values: %d:%d:%d\n",
+		val, session, process, thread));
+#endif
+    p->session = session;
+    p->process = process;
+    p->thread = thread;
+
+  out:
+    DEBUG_PRINT(("krb5_krcc_get_ring_ids: returning %d:%d:%d\n",
+		p->session, p->process, p->thread));
+    return 0;
+}
+
+/*
+ * ===============================================================
+ * INTERNAL functions to parse a credential from a key payload
+ * (Borrowed heavily from krb5_fcc_{read|store}_ funtions.)
+ * ===============================================================
+ */
+
+/*
+ * Effects:
+ * Copies len bytes from the key payload buffer into buf.
+ * Updates payload pointer and returns KRB5_CC_END if we
+ * try to read past the end of the payload buffer's data.
+ *
+ * Requires:
+ * Must be called with mutex locked.
+ *
+ * Errors:
+ * KRB5_CC_END - there were not len bytes available
+ */
+static  krb5_error_code
+krb5_krcc_parse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+		unsigned int len, krb5_krcc_bc * bc)
+{
+#ifdef NEED_LOCK_ASSERTIONS
+    krb5_krcc_data *data = (krb5_krcc_data *) id->data;
+
+    k5_assert_locked(&data->lock);
+#endif
+    DEBUG_PRINT(("krb5_krcc_parse: entered\n"));
+
+    if ((bc->endp == bc->bpp) || (bc->endp - bc->bpp) < len)
+	return KRB5_CC_END;
+
+    memcpy(buf, bc->bpp, len);
+    bc->bpp += len;
+
+    return KRB5_OK;
+}
+
+/*
+ * Take a key (credential) read from a keyring entry
+ * and parse it into a credential structure.
+ */
+static  krb5_error_code
+krb5_krcc_parse_cred(krb5_context context, krb5_ccache id, krb5_creds * creds,
+		     char *payload, int psize)
+{
+    krb5_error_code kret;
+    krb5_octet octet;
+    krb5_int32 int32;
+    krb5_krcc_bc bc;
+
+    /* Parse the pieces of the credential */
+    bc.bpp = payload;
+    bc.endp = bc.bpp + psize;
+    kret = krb5_krcc_parse_principal(context, id, &creds->client, &bc);
+    CHECK_N_GO(kret, out);
+
+    kret = krb5_krcc_parse_principal(context, id, &creds->server, &bc);
+    CHECK_N_GO(kret, cleanclient);
+
+    kret = krb5_krcc_parse_keyblock(context, id, &creds->keyblock, &bc);
+    CHECK_N_GO(kret, cleanserver);
+
+    kret = krb5_krcc_parse_times(context, id, &creds->times, &bc);
+    CHECK_N_GO(kret, cleanserver);
+
+    kret = krb5_krcc_parse_octet(context, id, &octet, &bc);
+    CHECK_N_GO(kret, cleanserver);
+    creds->is_skey = octet;
+
+    kret = krb5_krcc_parse_int32(context, id, &int32, &bc);
+    CHECK_N_GO(kret, cleanserver);
+    creds->ticket_flags = int32;
+
+    kret = krb5_krcc_parse_addrs(context, id, &creds->addresses, &bc);
+    CHECK_N_GO(kret, cleanblock);
+
+    kret = krb5_krcc_parse_authdata(context, id, &creds->authdata, &bc);
+    CHECK_N_GO(kret, cleanaddrs);
+
+    kret = krb5_krcc_parse_krb5data(context, id, &creds->ticket, &bc);
+    CHECK_N_GO(kret, cleanauthdata);
+
+    kret = krb5_krcc_parse_krb5data(context, id, &creds->second_ticket, &bc);
+    CHECK_N_GO(kret, cleanticket);
+
+    kret = KRB5_OK;
+    goto out;
+
+  cleanticket:
+    memset(creds->ticket.data, 0, (unsigned) creds->ticket.length);
+    krb5_xfree(creds->ticket.data);
+  cleanauthdata:
+    /* XXX ??? */
+  cleanaddrs:
+    krb5_free_addresses(context, creds->addresses);
+  cleanblock:
+    krb5_xfree(creds->keyblock.contents);
+  cleanserver:
+    krb5_free_principal(context, creds->server);
+  cleanclient:
+    krb5_free_principal(context, creds->client);
+
+  out:
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
+			  krb5_principal * princ, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    register krb5_principal tmpprinc;
+    krb5_int32 length, type;
+    int     i;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    /* Read principal type */
+    kret = krb5_krcc_parse_int32(context, id, &type, bc);
+    if (kret != KRB5_OK)
+	return kret;
+
+    /* Read the number of components */
+    kret = krb5_krcc_parse_int32(context, id, &length, bc);
+    if (kret != KRB5_OK)
+	return kret;
+
+    if (length < 0)
+	return KRB5_CC_NOMEM;
+
+    tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data));
+    if (tmpprinc == NULL)
+	return KRB5_CC_NOMEM;
+    if (length) {
+	size_t  msize = length;
+	if (msize != length) {
+	    free(tmpprinc);
+	    return KRB5_CC_NOMEM;
+	}
+	tmpprinc->data = ALLOC(msize, krb5_data);
+	if (tmpprinc->data == 0) {
+	    free((char *) tmpprinc);
+	    return KRB5_CC_NOMEM;
+	}
+    } else
+	tmpprinc->data = 0;
+    tmpprinc->magic = KV5M_PRINCIPAL;
+    tmpprinc->length = length;
+    tmpprinc->type = type;
+
+    kret = krb5_krcc_parse_krb5data(context, id,
+				    krb5_princ_realm(context, tmpprinc), bc);
+    i = 0;
+    CHECK(kret);
+
+    for (i = 0; i < length; i++) {
+	kret = krb5_krcc_parse_krb5data(context, id,
+					krb5_princ_component(context, tmpprinc,
+							     i), bc);
+	CHECK(kret);
+    }
+    *princ = tmpprinc;
+    return KRB5_OK;
+
+  errout:
+    while (--i >= 0)
+	free(krb5_princ_component(context, tmpprinc, i)->data);
+    free((char *) tmpprinc->data);
+    free((char *) tmpprinc);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_keyblock(krb5_context context, krb5_ccache id,
+			 krb5_keyblock * keyblock, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_ui_2 ui2;
+    krb5_int32 int32;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    keyblock->magic = KV5M_KEYBLOCK;
+    keyblock->contents = 0;
+
+    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
+    CHECK(kret);
+    keyblock->enctype = ui2;
+
+    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
+    CHECK(kret);
+    if (int32 < 0)
+	return KRB5_CC_NOMEM;
+    keyblock->length = int32;
+    /* Overflow check.  */
+    if (keyblock->length != int32)
+	return KRB5_CC_NOMEM;
+    if (keyblock->length == 0)
+	return KRB5_OK;
+    keyblock->contents = ALLOC(keyblock->length, krb5_octet);
+    if (keyblock->contents == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = krb5_krcc_parse(context, id, keyblock->contents,
+			   keyblock->length, bc);
+    CHECK(kret);
+
+    return KRB5_OK;
+  errout:
+    if (keyblock->contents)
+	krb5_xfree(keyblock->contents);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_times(krb5_context context, krb5_ccache id,
+		      krb5_ticket_times * t, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 i;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_parse_int32(context, id, &i, bc);
+    CHECK(kret);
+    t->authtime = i;
+
+    kret = krb5_krcc_parse_int32(context, id, &i, bc);
+    CHECK(kret);
+    t->starttime = i;
+
+    kret = krb5_krcc_parse_int32(context, id, &i, bc);
+    CHECK(kret);
+    t->endtime = i;
+
+    kret = krb5_krcc_parse_int32(context, id, &i, bc);
+    CHECK(kret);
+    t->renew_till = i;
+
+    return 0;
+  errout:
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
+			 krb5_data * data, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 len;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    data->magic = KV5M_DATA;
+    data->data = 0;
+
+    kret = krb5_krcc_parse_int32(context, id, &len, bc);
+    CHECK(kret);
+    if (len < 0)
+	return KRB5_CC_NOMEM;
+    data->length = len;
+    if (data->length != len || data->length + 1 == 0)
+	return KRB5_CC_NOMEM;
+
+    if (data->length == 0) {
+	data->data = 0;
+	return KRB5_OK;
+    }
+
+    data->data = (char *) malloc(data->length + 1);
+    if (data->data == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = krb5_krcc_parse(context, id, data->data, (unsigned) data->length,
+			   bc);
+    CHECK(kret);
+
+    data->data[data->length] = 0;	/* Null terminate, just in case.... */
+    return KRB5_OK;
+  errout:
+    if (data->data)
+	krb5_xfree(data->data);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_int32(krb5_context context, krb5_ccache id, krb5_int32 * i,
+		      krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    unsigned char buf[4];
+    krb5_int32 val;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_parse(context, id, buf, 4, bc);
+    if (kret)
+	return kret;
+    val = buf[0];
+    val = (val << 8) | buf[1];
+    val = (val << 8) | buf[2];
+    val = (val << 8) | buf[3];
+    *i = val;
+    return 0;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_octet(krb5_context context, krb5_ccache id, krb5_octet * i,
+		      krb5_krcc_bc * bc)
+{
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+    return krb5_krcc_parse(context, id, (krb5_pointer) i, 1, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
+		      krb5_address *** addrs, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 length;
+    size_t  msize;
+    int     i;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    *addrs = 0;
+
+    /* Read the number of components */
+    kret = krb5_krcc_parse_int32(context, id, &length, bc);
+    CHECK(kret);
+
+    /*
+     * Make *addrs able to hold length pointers to krb5_address structs
+     * Add one extra for a null-terminated list
+     */
+    msize = length;
+    msize += 1;
+    if (msize == 0 || msize - 1 != length || length < 0)
+	return KRB5_CC_NOMEM;
+    *addrs = ALLOC(msize, krb5_address *);
+    if (*addrs == NULL)
+	return KRB5_CC_NOMEM;
+
+    for (i = 0; i < length; i++) {
+	(*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address));
+	if ((*addrs)[i] == NULL) {
+	    krb5_free_addresses(context, *addrs);
+	    return KRB5_CC_NOMEM;
+	}
+	kret = krb5_krcc_parse_addr(context, id, (*addrs)[i], bc);
+	CHECK(kret);
+    }
+
+    return KRB5_OK;
+  errout:
+    if (*addrs)
+	krb5_free_addresses(context, *addrs);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
+		     krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_ui_2 ui2;
+    krb5_int32 int32;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    addr->magic = KV5M_ADDRESS;
+    addr->contents = 0;
+
+    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
+    CHECK(kret);
+    addr->addrtype = ui2;
+
+    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
+    CHECK(kret);
+    if ((int32 & VALID_INT_BITS) != int32)	/* Overflow int??? */
+	return KRB5_CC_NOMEM;
+    addr->length = int32;
+    /*
+     * Length field is "unsigned int", which may be smaller
+     * than 32 bits.
+     */
+    if (addr->length != int32)
+	return KRB5_CC_NOMEM;	/* XXX */
+
+    if (addr->length == 0)
+	return KRB5_OK;
+
+    addr->contents = (krb5_octet *) malloc(addr->length);
+    if (addr->contents == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = krb5_krcc_parse(context, id, addr->contents, addr->length, bc);
+    CHECK(kret);
+
+    return KRB5_OK;
+  errout:
+    if (addr->contents)
+	krb5_xfree(addr->contents);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
+			 krb5_authdata *** a, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 length;
+    size_t  msize;
+    int     i;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    *a = 0;
+
+    /* Read the number of components */
+    kret = krb5_krcc_parse_int32(context, id, &length, bc);
+    CHECK(kret);
+
+    if (length == 0)
+	return KRB5_OK;
+
+    /*
+     * Make *a able to hold length pointers to krb5_authdata structs
+     * Add one extra for a null-terminated list
+     */
+    msize = length;
+    msize += 1;
+    if (msize == 0 || msize - 1 != length || length < 0)
+	return KRB5_CC_NOMEM;
+    *a = ALLOC(msize, krb5_authdata *);
+    if (*a == NULL)
+	return KRB5_CC_NOMEM;
+
+    for (i = 0; i < length; i++) {
+	(*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata));
+	if ((*a)[i] == NULL) {
+	    krb5_free_authdata(context, *a);
+	    return KRB5_CC_NOMEM;
+	}
+	kret = krb5_krcc_parse_authdatum(context, id, (*a)[i], bc);
+	CHECK(kret);
+    }
+
+    return KRB5_OK;
+  errout:
+    if (*a)
+	krb5_free_authdata(context, *a);
+    return kret;
+}
+
+static  krb5_error_code
+krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
+			  krb5_authdata * a, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 int32;
+    krb5_ui_2 ui2;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    a->magic = KV5M_AUTHDATA;
+    a->contents = NULL;
+
+    kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
+    CHECK(kret);
+    a->ad_type = (krb5_authdatatype) ui2;
+    kret = krb5_krcc_parse_int32(context, id, &int32, bc);
+    CHECK(kret);
+    if ((int32 & VALID_INT_BITS) != int32)	/* Overflow int??? */
+	return KRB5_CC_NOMEM;
+    a->length = int32;
+    /*
+     * Value could have gotten truncated if int is
+     * smaller than 32 bits.
+     */
+    if (a->length != int32)
+	return KRB5_CC_NOMEM;	/* XXX */
+
+    if (a->length == 0)
+	return KRB5_OK;
+
+    a->contents = (krb5_octet *) malloc(a->length);
+    if (a->contents == NULL)
+	return KRB5_CC_NOMEM;
+
+    kret = krb5_krcc_parse(context, id, a->contents, a->length, bc);
+    CHECK(kret);
+
+    return KRB5_OK;
+  errout:
+    if (a->contents)
+	krb5_xfree(a->contents);
+    return kret;
+
+}
+
+static  krb5_error_code
+krb5_krcc_parse_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 * i,
+		     krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    unsigned char buf[2];
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_parse(context, id, buf, 2, bc);
+    if (kret)
+	return kret;
+    *i = (buf[0] << 8) + buf[1];
+    return 0;
+}
+
+/*
+ * Requires:
+ * locked mutex
+ *
+ * Effects:
+ * Copies len bytes from buf into the payload buffer (bc->bpp).
+ * Detects attempts to write past end of the payload buffer.
+ * Updates payload buffer pointer accordingly.
+ *
+ * Errors:
+ * system errors
+ */
+static  krb5_error_code
+krb5_krcc_unparse(krb5_context context, krb5_ccache id, krb5_pointer buf,
+		  unsigned int len, krb5_krcc_bc * bc)
+{
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    if (bc->bpp + len > bc->endp)
+	return KRB5_CC_WRITE;
+
+    memcpy(bc->bpp, buf, len);
+    bc->bpp += len;
+
+    return KRB5_OK;
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_principal(krb5_context context, krb5_ccache id,
+			    krb5_principal princ, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_int32 i, length, tmp, type;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    type = krb5_princ_type(context, princ);
+    tmp = length = krb5_princ_size(context, princ);
+
+    kret = krb5_krcc_unparse_int32(context, id, type, bc);
+    CHECK_OUT(kret);
+
+    kret = krb5_krcc_unparse_int32(context, id, tmp, bc);
+    CHECK_OUT(kret);
+
+    kret = krb5_krcc_unparse_krb5data(context, id,
+				      krb5_princ_realm(context, princ), bc);
+    CHECK_OUT(kret);
+
+    for (i = 0; i < length; i++) {
+	kret = krb5_krcc_unparse_krb5data(context, id,
+					  krb5_princ_component(context, princ,
+							       i), bc);
+	CHECK_OUT(kret);
+    }
+
+    return KRB5_OK;
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_keyblock(krb5_context context, krb5_ccache id,
+			   krb5_keyblock * keyblock, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_unparse_ui_2(context, id, keyblock->enctype, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_ui_4(context, id, keyblock->length, bc);
+    CHECK_OUT(kret);
+    return krb5_krcc_unparse(context, id, (char *) keyblock->contents,
+			     keyblock->length, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_times(krb5_context context, krb5_ccache id,
+			krb5_ticket_times * t, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_unparse_int32(context, id, t->authtime, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_int32(context, id, t->starttime, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_int32(context, id, t->endtime, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_int32(context, id, t->renew_till, bc);
+    CHECK_OUT(kret);
+    return 0;
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_krb5data(krb5_context context, krb5_ccache id,
+			   krb5_data * data, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_unparse_ui_4(context, id, data->length, bc);
+    CHECK_OUT(kret);
+    return krb5_krcc_unparse(context, id, data->data, data->length, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_int32(krb5_context context, krb5_ccache id, krb5_int32 i,
+			krb5_krcc_bc * bc)
+{
+    unsigned char buf[4];
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    buf[3] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[2] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[1] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[0] = (unsigned char) (i & 0xFF);
+    return krb5_krcc_unparse(context, id, buf, 4, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_octet(krb5_context context, krb5_ccache id, krb5_int32 i,
+			krb5_krcc_bc * bc)
+{
+    krb5_octet ibuf;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    ibuf = (krb5_octet) i;
+    return krb5_krcc_unparse(context, id, (char *) &ibuf, 1, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
+			krb5_address ** addrs, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_address **temp;
+    krb5_int32 i, length = 0;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    /* Count the number of components */
+    if (addrs) {
+	temp = addrs;
+	while (*temp++)
+	    length += 1;
+    }
+
+    kret = krb5_krcc_unparse_int32(context, id, length, bc);
+    CHECK_OUT(kret);
+    for (i = 0; i < length; i++) {
+	kret = krb5_krcc_unparse_addr(context, id, addrs[i], bc);
+	CHECK_OUT(kret);
+    }
+
+    return KRB5_OK;
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_addr(krb5_context context, krb5_ccache id,
+		       krb5_address * addr, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_unparse_ui_2(context, id, addr->addrtype, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_ui_4(context, id, addr->length, bc);
+    CHECK_OUT(kret);
+    return krb5_krcc_unparse(context, id, (char *) addr->contents,
+			     addr->length, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_authdata(krb5_context context, krb5_ccache id,
+			   krb5_authdata ** a, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+    krb5_authdata **temp;
+    krb5_int32 i, length = 0;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    if (a != NULL) {
+	for (temp = a; *temp; temp++)
+	    length++;
+    }
+
+    kret = krb5_krcc_unparse_int32(context, id, length, bc);
+    CHECK_OUT(kret);
+    for (i = 0; i < length; i++) {
+	kret = krb5_krcc_unparse_authdatum(context, id, a[i], bc);
+	CHECK_OUT(kret);
+    }
+    return KRB5_OK;
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_authdatum(krb5_context context, krb5_ccache id,
+			    krb5_authdata * a, krb5_krcc_bc * bc)
+{
+    krb5_error_code kret;
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    kret = krb5_krcc_unparse_ui_2(context, id, a->ad_type, bc);
+    CHECK_OUT(kret);
+    kret = krb5_krcc_unparse_ui_4(context, id, a->length, bc);
+    CHECK_OUT(kret);
+    return krb5_krcc_unparse(context, id, (krb5_pointer) a->contents,
+			     a->length, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i,
+		       krb5_krcc_bc * bc)
+{
+    unsigned char buf[4];
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    buf[3] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[2] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[1] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[0] = (unsigned char) (i & 0xFF);
+    return krb5_krcc_unparse(context, id, buf, 4, bc);
+}
+
+static  krb5_error_code
+krb5_krcc_unparse_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i,
+		       krb5_krcc_bc * bc)
+{
+    unsigned char buf[2];
+
+#ifdef NEED_LOCK_ASSERTIONS
+    k5_assert_locked(&((krb5_krcc_data *) id->data)->lock);
+#endif
+
+    buf[1] = (unsigned char) (i & 0xFF);
+    i >>= 8;
+    buf[0] = (unsigned char) (i & 0xFF);
+    return krb5_krcc_unparse(context, id, buf, 2, bc);
+}
+
+/*
+ * ===============================================================
+ * INTERNAL functions to unparse a credential into a key payload
+ * (Borrowed heavily from krb5_fcc_{read|store}_ funtions.)
+ * ===============================================================
+ */
+
+/*
+ * Take a credential structure and unparse it (serialize)
+ * for storage into a keyring key entry.
+ * Caller is responsible for freeing returned buffer.
+ */
+static  krb5_error_code
+krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
+		       krb5_creds * creds, char **datapp, unsigned int *lenptr)
+{
+    krb5_error_code kret;
+    char   *buf;
+    krb5_krcc_bc bc;
+
+    if (!creds || !datapp || !lenptr)
+	return EINVAL;
+
+    *datapp = NULL;
+    *lenptr = 0;
+
+    buf = malloc(GUESS_CRED_SIZE);
+    if (buf == NULL)
+	return KRB5_CC_NOMEM;
+
+    bc.bpp = buf;
+    bc.endp = buf + GUESS_CRED_SIZE;
+
+    kret = krb5_krcc_unparse_principal(context, id, creds->client, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_principal(context, id, creds->server, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_keyblock(context, id, &creds->keyblock, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_times(context, id, &creds->times, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_octet(context, id, (krb5_int32) creds->is_skey,
+				   &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_int32(context, id, creds->ticket_flags, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_addrs(context, id, creds->addresses, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_authdata(context, id, creds->authdata, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_krb5data(context, id, &creds->ticket, &bc);
+    CHECK_N_GO(kret, errout);
+
+    kret = krb5_krcc_unparse_krb5data(context, id, &creds->second_ticket, &bc);
+    CHECK_N_GO(kret, errout);
+
+    /* Success! */
+    *datapp = buf;
+    *lenptr = bc.bpp - buf;
+    kret = KRB5_OK;
+
+  errout:
+    return kret;
+}
+
+/*
+ * ccache implementation storing credentials in the Linux keyring facility
+ * The default is to put them at the session keyring level.
+ * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
+ * be stored at the process or thread level respectively.
+ */
+const krb5_cc_ops krb5_krcc_ops = {
+    0,
+    "KEYRING",
+    krb5_krcc_get_name,
+    krb5_krcc_resolve,
+    krb5_krcc_generate_new,
+    krb5_krcc_initialize,
+    krb5_krcc_destroy,
+    krb5_krcc_close,
+    krb5_krcc_store,
+    krb5_krcc_retrieve,
+    krb5_krcc_get_principal,
+    krb5_krcc_start_seq_get,
+    krb5_krcc_next_cred,
+    krb5_krcc_end_seq_get,
+    krb5_krcc_remove_cred,
+    krb5_krcc_set_flags,
+#ifdef HAVE_GET_FLAGS
+    krb5_krcc_get_flags		/* added after 1.4 release */
+#endif
+};
diff -puN src/lib/krb5/ccache/Makefile.in~add_keyring_ccache src/lib/krb5/ccache/Makefile.in
--- krb5-1.4.3/src/lib/krb5/ccache/Makefile.in~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/lib/krb5/ccache/Makefile.in	2006-04-12 16:35:21.000000000 -0400
@@ -25,7 +25,9 @@ STLIBOBJS= \
 	ccdefault.o \
 	ccdefops.o \
 	cc_retr.o \
-	cc_file.o cc_memory.o \
+	cc_file.o \
+	cc_memory.o \
+	cc_keyring.o \
 	ccfns.o \
 	ser_cc.o
 
@@ -36,6 +38,7 @@ OBJS=	$(OUTPRE)ccbase.$(OBJEXT) \
 	$(OUTPRE)cc_retr.$(OBJEXT) \
 	$(OUTPRE)cc_file.$(OBJEXT) \
 	$(OUTPRE)cc_memory.$(OBJEXT) \
+	$(OUTPRE)cc_keyring.$(OBJEXT) \
 	$(OUTPRE)ccfns.$(OBJEXT) \
 	$(OUTPRE)ser_cc.$(OBJEXT) $(MSLSA_OBJ)
 
@@ -46,6 +49,7 @@ SRCS=	$(srcdir)/ccbase.c \
 	$(srcdir)/cc_retr.c \
 	$(srcdir)/cc_file.c \
 	$(srcdir)/cc_memory.c \
+	$(srcdir)/cc_keyring.c \
 	$(srcdir)/ccfns.c \
 	$(srcdir)/ser_cc.c $(MSLSA_SRC)
 
@@ -142,6 +146,12 @@ cc_memory.so cc_memory.po $(OUTPRE)cc_me
   $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \
   $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
   $(SRCTOP)/include/krb5/kdb.h
+cc_keyring.so cc_keyring.po $(OUTPRE)cc_keyring.$(OBJEXT): cc_keyring.c $(SRCTOP)/include/k5-int.h \
+  $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(SRCTOP)/include/krb5/kdb.h
 ccfns.so ccfns.po $(OUTPRE)ccfns.$(OBJEXT): ccfns.c $(SRCTOP)/include/k5-int.h \
   $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
diff -puN src/lib/krb5/os/ccdefname.c~add_keyring_ccache src/lib/krb5/os/ccdefname.c
--- krb5-1.4.3/src/lib/krb5/os/ccdefname.c~add_keyring_ccache	2006-04-12 16:35:21.000000000 -0400
+++ krb5-1.4.3-kwc/src/lib/krb5/os/ccdefname.c	2006-05-05 10:18:41.000000000 -0400
@@ -224,7 +224,11 @@ cleanup:
 #if !(defined(_WIN32))
 static krb5_error_code get_from_os(char *name_buf, int name_size)
 {
+#ifdef HAVE_LIBKEYUTILS
+	snprintf(name_buf, (unsigned) name_size, "KEYRING:krb5cc");
+#else
 	sprintf(name_buf, "FILE:/tmp/krb5cc_%ld", (long) getuid());
+#endif
 	return 0;
 }
 #endif

_
