Merge Kevin Coffman's keyring ccache branch for Linux, with some modifications:
authorKen Raeburn <raeburn@mit.edu>
Mon, 2 Oct 2006 22:50:10 +0000 (22:50 +0000)
committerKen Raeburn <raeburn@mit.edu>
Mon, 2 Oct 2006 22:50:10 +0000 (22:50 +0000)
aclocal.m4: Enable keyring ccache if the header and library are available; no
configure-time option.  No error if it's not found.

ccdefname.c: Keep old default of FILE: cache, at least for now.

libkrb5.exports: Don't export krb5_krcc_ops.

ccbase.c: Only initialize krb5int_krcc_mutex if USE_KEYRING_CCACHE; destroy it
in finalization.  Define INITIAL_TYPEHEAD macro (for file vs keyring), and use
it for initialization and in krb5int_cc_finalize.  Re-enable freeing of
additional registered-type structures.

cc_keyring.c: Avoid calls to com_err from within library.

cc_file.c: Punt change; generate_new is badly broken, and we expect to replace
it with a new API anyways.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@18638 dc483132-0cff-0310-8789-dd5450dbe970

src/aclocal.m4
src/lib/krb5/ccache/Makefile.in
src/lib/krb5/ccache/cc-int.h
src/lib/krb5/ccache/cc_keyring.c [new file with mode: 0644]
src/lib/krb5/ccache/ccbase.c
src/lib/krb5/ccache/t_cc.c

index 51ca472f4e11d8c945fb235726e89994c4fc83be..c4ce88aa5b7330cb0dc35f01d347e1c7c1975017 100644 (file)
@@ -106,6 +106,7 @@ KRB5_LIB_PARAMS
 KRB5_AC_INITFINI
 KRB5_AC_ENABLE_THREADS
 KRB5_AC_FIND_DLOPEN
+KRB5_AC_KEYRING_CCACHE
 ])dnl
 
 dnl Maintainer mode, akin to what automake provides, 'cept we don't
@@ -1811,3 +1812,14 @@ dnl  AC_MSG_NOTICE(disabling ldap backend module support)
 fi
 AC_SUBST(OPENLDAP_PLUGIN)
 ])dnl
+dnl
+dnl If libkeyutils exists (on Linux) include it and use keyring ccache
+AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
+  AC_CHECK_HEADERS([keyutils.h],
+    AC_CHECK_LIB(keyutils, add_key, 
+      [dnl Pre-reqs were found
+       AC_DEFINE(USE_KEYRING_CCACHE, 1, [Define if the keyring ccache should be enabled])
+       LIBS="-lkeyutils $LIBS"
+      ]))
+])dnl
+dnl
index 44ba2a91b6ab68fb37e136e39eec16d00fea8e8f..7d611f839207f213551d301ab1dd3af441e3cc1e 100644 (file)
@@ -26,7 +26,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
 
@@ -37,6 +39,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)
 
@@ -47,6 +50,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)
 
@@ -157,6 +161,14 @@ cc_memory.so cc_memory.po $(OUTPRE)cc_memory.$(OBJEXT): \
   $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
   $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h cc_memory.c
+cc_keyring.so cc_keyring.po $(OUTPRE)cc_keyring.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/port-sockets.h \
+  $(SRCTOP)/include/socket-utils.h cc_keyring.c
 ccfns.so ccfns.po $(OUTPRE)ccfns.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h \
index b2d6c78c9e390ec2ef08d84d8136f70b74a8b850..430e4c3755b1394ade96c2c49a539813947b44de 100644 (file)
@@ -43,6 +43,7 @@ void
 krb5int_cc_finalize(void);
 
 extern k5_mutex_t krb5int_mcc_mutex;
+extern k5_mutex_t krb5int_krcc_mutex;
 extern k5_mutex_t krb5int_cc_file_mutex;
 
 #endif /* __KRB5_CCACHE_H__ */
diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c
new file mode 100644 (file)
index 0000000..8b36fe0
--- /dev/null
@@ -0,0 +1,2080 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Original stdio support copyright 1995 by Cygnus Support.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * 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"
+
+#ifdef USE_KEYRING_CCACHE
+
+#include <errno.h>
+#include <keyutils.h>
+
+#ifdef DEBUG
+#define KRCC_DEBUG         1
+#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 really belong in some external
+ * 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 in an 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 4096
+
+#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 parent_id;    /* parent keyring of this ccache keyring */
+    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;
+
+/* Global mutex */
+k5_mutex_t krb5int_krcc_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+
+/*
+ * 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);
+
+static krb5_error_code KRB5_CALLCONV krb5_krcc_get_flags
+    (krb5_context context, krb5_ccache id, krb5_flags * flags);
+
+/*
+ * 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 *, key_serial_t ring, key_serial_t parent_ring,
+     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:
+ * Invalidates the id, and frees any resources associated with the cache.
+ * (Does NOT destroy the underlying ccache in the keyring.)
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_krcc_close(krb5_context context, krb5_ccache id)
+{
+    krb5_krcc_data *d;
+
+    DEBUG_PRINT(("krb5_krcc_close: entered\n"));
+
+    d = (krb5_krcc_data *) id->data;
+
+    krb5_xfree(d->name);
+    k5_mutex_destroy(&d->lock);
+    krb5_xfree(d);
+
+    krb5_xfree(id);
+
+    return KRB5_OK;
+}
+
+/*
+ * Modifies:
+ * id
+ *
+ * Effects:
+ * Clears out a ccache keyring, unlinking all keys within it
+ * (Including the "hidden" key with the principal information)
+ *
+ * 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) {
+               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:
+ * system errors
+ */
+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, d->parent_id);
+    if (res < 0) {
+       kret = errno;
+       DEBUG_PRINT(("krb5_krcc_destroy: unlinking key %d from ring %d: %s",
+                    d->ring_id, d->parent_id, error_message(errno)));
+       goto cleanup;
+    }
+cleanup:
+    k5_mutex_unlock(&d->lock);
+    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;
+       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;
+    }
+
+    DEBUG_PRINT(("krb5_krcc_resolve: searching ring %d for residual '%s'\n",
+               ring_id, residual));
+
+    /*
+     * 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 in the session ring if we don't find it
+     * in the process ring?  Same goes for thread.  Should we look in
+     * the process and session rings if not found in the thread ring?
+     *
+     */
+    key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, residual, 0);
+    if (key < 0) {
+       key = add_key(KRCC_KEY_TYPE_KEYRING, residual, NULL, 0, ring_id);
+       if (key < 0) {
+           kret = errno;
+           DEBUG_PRINT(("krb5_krcc_resolve: Error adding new "
+                        "keyring '%s': %s\n", residual, strerror(errno)));
+           return kret;
+       }
+       DEBUG_PRINT(("krb5_krcc_resolve: new keyring '%s', "
+                       "key %d, added to keyring %d\n",
+                       residual, key, ring_id));
+    } else {
+       DEBUG_PRINT(("krb5_krcc_resolve: found existing "
+                       "key %d, with name '%s' in keyring %d\n",
+                       key, residual, ring_id));
+       /* Determine key containing principal information */
+       pkey = keyctl_search(key, KRCC_KEY_TYPE_USER,
+                            KRCC_SPEC_PRINC_KEYNAME, 0);
+       if (pkey < 0) {
+           DEBUG_PRINT(("krb5_krcc_resolve: 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, key, ring_id, &d);
+    if (kret) {
+       free(lid);
+       return kret;
+    }
+
+    DEBUG_PRINT(("krb5_krcc_resolve: ring_id %d, princ_id %d, "
+                "nkeys %d\n", key, pkey, nkeys));
+    d->princ_id = pkey;
+    d->numkeys = nkeys;
+    lid->ops = &krb5_krcc_ops;
+    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, key_serial_t ring,
+                  key_serial_t parent_ring, 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;
+    d->ring_id = ring;
+    d->parent_id = parent_ring;
+    d->numkeys = 0;
+
+    *datapp = d;
+    return 0;
+}
+
+/* Utility routine: Creates a random memory ccache name. 
+ * This algorithm was selected because it creates readable 
+ * random ccache names in a fixed size buffer.  */
+
+static krb5_error_code
+random_string (krb5_context context, char *string, krb5_int32 length)
+{
+    static const unsigned char charlist[] =
+       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    krb5_error_code err = 0;
+    unsigned char *bytes = NULL;
+    size_t bytecount = length - 1;
+
+    if (!err) {
+       bytes = malloc (bytecount);
+       if (bytes == NULL) { err = ENOMEM; }
+    }
+
+    if (!err) {
+       krb5_data data;
+       data.length = bytecount;
+       data.data = (char *) bytes;
+       err = krb5_c_random_make_octets (context, &data);
+    }
+
+    if (!err) {
+       krb5_int32 i;
+       for (i = 0; i < bytecount; i++) {
+           string [i] = charlist[bytes[i] % (sizeof (charlist) - 1)];
+       }
+       string[length - 1] = '\0';
+    }
+
+    if (bytes != NULL) { free (bytes); }
+
+    return err;
+}
+
+/*
+ * 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 uniquename[8];
+    krb5_error_code kret;
+    krb5_krcc_data *d;
+    key_serial_t ring_id = KEY_SPEC_SESSION_KEYRING;
+    key_serial_t key;
+
+    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;
+
+    lid->ops = &krb5_krcc_ops;
+
+    kret = k5_mutex_lock(&krb5int_krcc_mutex);
+    if (kret) {
+       free(lid);
+       return kret;
+    }
+
+/* XXX These values are platform-specific and should not be here! */
+/* XXX There is a bug in FC5 where these are not included in errno.h  */
+#ifndef ENOKEY
+#define ENOKEY          126     /* Required key not available */
+#endif
+#ifndef EKEYEXPIRED
+#define EKEYEXPIRED     127     /* Key has expired */
+#endif
+#ifndef EKEYREVOKED
+#define EKEYREVOKED     128     /* Key has been revoked */
+#endif
+#ifndef EKEYREJECTED
+#define EKEYREJECTED    129     /* Key was rejected by service */
+#endif
+
+    /*
+     * Loop until we successfully create a new ccache keyring with
+     * a unique name, or we get an error.
+     */
+    while (1) {
+       random_string(context, uniquename, sizeof(uniquename));
+       DEBUG_PRINT(("krb5_krcc_generate_new: searching for name '%s'\n",
+                    uniquename));
+       key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, uniquename, 0);
+/*XXX*/ DEBUG_PRINT(("krb5_krcc_generate_new: after searching for '%s', key = %d, errno = %d\n", uniquename, key, errno));
+       if (key < 0 && errno == ENOKEY) {
+           /* name does not already exist, create it to reserve the name */
+           key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0, ring_id);
+           if (key < 0) {
+               kret = errno;
+               DEBUG_PRINT(("krb5_krcc_generate_new: '%s' trying to "
+                            "create '%s'\n", strerror(errno), uniquename));
+               k5_mutex_unlock(&krb5int_krcc_mutex);
+               return kret;
+           }
+           break;
+       }
+    }
+           
+    kret = krb5_krcc_new_data(uniquename, key, ring_id, &d);
+    k5_mutex_unlock(&krb5int_krcc_mutex);
+    if (kret) {
+       krb5_xfree(lid);
+       return kret;
+    }
+    lid->data = d;
+    *id = lid;
+    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;
+}
+
+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;
+}
+
+/* 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));
+    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;
+#ifdef KRCC_DEBUG
+    {
+       krb5_error_code rc;
+       char *princname = NULL;
+       rc = krb5_unparse_name(context, princ, &princname);
+       DEBUG_PRINT(("krb5_krcc_save_principal: adding new key '%s' "
+                    "to keyring %d for principal '%s'\n",
+                    KRCC_SPEC_PRINC_KEYNAME, d->ring_id,
+                    rc ? "<unknown>" : princname));
+       if (rc == 0)
+           krb5_free_unparsed_name(context, princname);
+    }
+#endif
+    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 principal key: %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 while creating a
+     * context to communicate the user's key serial numbers.
+     */
+    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 '%s' key %d\n",
+               KRCC_SPEC_IDS_KEYNAME, ids_key));
+    /*
+     * Read and parse the ids file
+     */
+    memset(ids_buf, '\0', sizeof(ids_buf));
+    val = keyctl_read(ids_key, ids_buf, sizeof(ids_buf));
+    if (val > sizeof(ids_buf))
+       goto out;
+
+    val = sscanf(ids_buf, "%d:%d:%d", &session, &process, &thread);
+    if (val != 3)
+       goto out;
+
+    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)
+{
+    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;
+
+    /* 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;
+
+    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;
+
+    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;
+
+    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;
+
+    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)
+{
+    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;
+
+    *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;
+
+    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;
+
+    *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;
+
+    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];
+
+    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)
+{
+    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;
+
+    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;
+
+    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;
+
+    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;
+
+    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];
+
+    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;
+
+    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;
+
+    /* 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;
+
+    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;
+
+    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;
+
+    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];
+
+    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];
+
+    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,
+    krb5_krcc_get_flags                /* added after 1.4 release */
+};
+
+#else /* !USE_KEYRING_CCACHE */
+
+/*
+ * Export this, but it shouldn't be used.
+ */
+const krb5_cc_ops krb5_krcc_ops = {
+    0,
+    "KEYRING",
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL                       /* added after 1.4 release */
+};
+#endif /* USE_KEYRING_CCACHE */
index 3a68ecac4f7681d955011dd582b15d0d177c4b19..431e43bc93ef11263655517b5c1699fc7ea1d08a 100644 (file)
@@ -38,6 +38,9 @@ struct krb5_cc_typelist {
     struct krb5_cc_typelist *next;
 };
 extern const krb5_cc_ops krb5_mcc_ops;
+#ifdef USE_KEYRING_CCACHE
+extern const krb5_cc_ops krb5_krcc_ops;
+#endif
 
 #ifdef _WIN32
 extern const krb5_cc_ops krb5_lcc_ops;
@@ -45,12 +48,22 @@ static struct krb5_cc_typelist cc_lcc_entry = { &krb5_lcc_ops, NULL };
 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 USE_KEYRING_CCACHE
+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 /* USE_KEYRING_CCACHE */
 #endif
 
 static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops,
                                                &cc_mcc_entry };
-
-static struct krb5_cc_typelist *cc_typehead = &cc_fcc_entry;
+#ifdef USE_KEYRING_CCACHE
+#define INITIAL_TYPEHEAD (&cc_krcc_entry)
+#else
+#define INITIAL_TYPEHEAD (&cc_fcc_entry)
+#endif
+static struct krb5_cc_typelist *cc_typehead = INITIAL_TYPEHEAD;
 static k5_mutex_t cc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
 
 int
@@ -67,6 +80,11 @@ krb5int_cc_initialize(void)
     err = k5_mutex_finish_init(&krb5int_cc_file_mutex);
     if (err)
        return err;
+#ifdef USE_KEYRING_CCACHE
+    err = k5_mutex_finish_init(&krb5int_krcc_mutex);
+    if (err)
+       return err;
+#endif
     return 0;
 }
 
@@ -77,7 +95,10 @@ krb5int_cc_finalize(void)
     k5_mutex_destroy(&cc_typelist_lock);
     k5_mutex_destroy(&krb5int_cc_file_mutex);
     k5_mutex_destroy(&krb5int_mcc_mutex);
-    for (t = cc_typehead; t != &cc_fcc_entry; t = t_next) {
+#ifdef USE_KEYRING_CCACHE
+    k5_mutex_destroy(&krb5int_krcc_mutex);
+#endif
+    for (t = cc_typehead; t != INITIAL_TYPEHEAD; t = t_next) {
        t_next = t->next;
        free(t);
     }
index 1d2e272f21cd6bba6e3a6b30774cde4a622af954..0db618b765200f9f4224a3d421031eb737aa3c1a 100644 (file)
@@ -285,6 +285,7 @@ int main (void)
 
     test_misc(context);
     do_test(context, "");
+    do_test(context, "KEYRING:");
     do_test(context, "MEMORY:");
     do_test(context, "FILE:");