summaryrefslogtreecommitdiffstats
path: root/persistent_keyring.patch
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@dahyabhai.net>2013-10-15 10:53:33 -0400
committerNalin Dahyabhai <nalin@dahyabhai.net>2013-11-19 17:32:18 -0500
commitb1f558a0f5adc777ba60395ae41f99800b538fcb (patch)
tree4750fbbf1de06af7755ef92cb3295b6fb5674171 /persistent_keyring.patch
parent8a39d5ff722e6b6ad1681e60a6d02578ba273fb6 (diff)
Drop backported patch
Diffstat (limited to 'persistent_keyring.patch')
-rw-r--r--persistent_keyring.patch3321
1 files changed, 0 insertions, 3321 deletions
diff --git a/persistent_keyring.patch b/persistent_keyring.patch
deleted file mode 100644
index 863774c..0000000
--- a/persistent_keyring.patch
+++ /dev/null
@@ -1,3321 +0,0 @@
-Pared down from the git commits, with a local copy of k5memdup0() added in
-to cc_keyring, and a wrapper 'run' in to k5test.py.
-
-diff --git a/src/aclocal.m4 b/src/aclocal.m4
-index 2c17e46..7be77c2 100644
---- a/src/aclocal.m4
-+++ b/src/aclocal.m4
-@@ -89,6 +89,7 @@ KRB5_AC_INITFINI
- KRB5_AC_ENABLE_THREADS
- KRB5_AC_FIND_DLOPEN
- KRB5_AC_KEYRING_CCACHE
-+KRB5_AC_PERSISTENT_KEYRING
- ])dnl
-
- dnl Maintainer mode, akin to what automake provides, 'cept we don't
-@@ -1664,3 +1659,12 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
- ]))
- ])dnl
- dnl
-+dnl If libkeyutils supports persistent keyrings, use them
-+AC_DEFUN(KRB5_AC_PERSISTENT_KEYRING,[
-+ AC_CHECK_HEADERS([keyutils.h],
-+ AC_CHECK_LIB(keyutils, keyctl_get_persistent,
-+ [AC_DEFINE(HAVE_PERSISTENT_KEYRING, 1,
-+ [Define if persistent keyrings are supported])
-+ ]))
-+])dnl
-+dnl
-diff --git a/src/lib/krb5/error_tables/k5e1_err.et b/src/lib/krb5/error_tables/k5e1_err.et
-index 98374ed..071b7f2 100644
---- a/src/lib/krb5/error_tables/k5e1_err.et
-+++ b/src/lib/krb5/error_tables/k5e1_err.et
-@@ -35,4 +35,7 @@ error_code KRB5_PLUGIN_BAD_MODULE_SPEC, "Invalid module specifier"
- error_code KRB5_PLUGIN_NAME_NOTFOUND, "Plugin module name not found"
- error_code KRB5KDC_ERR_DISCARD, "The KDC should discard this request"
- error_code KRB5_DCC_CANNOT_CREATE, "Can't create new subsidiary cache"
-+error_code KRB5_KCC_INVALID_ANCHOR, "Invalid keyring anchor name"
-+error_code KRB5_KCC_UNKNOWN_VERSION, "Unknown keyring collection version"
-+error_code KRB5_KCC_INVALID_UID, "Invalid UID in persistent keyring name"
- end
-diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c
-index fd1bcec..795ccd6 100644
---- a/src/lib/krb5/ccache/cc_keyring.c
-+++ b/src/lib/krb5/ccache/cc_keyring.c
-@@ -56,17 +56,42 @@
- */
-
- /*
-- * Implementation of a credentials cache stored in the Linux keyring facility
-+ * This file implements a collection-enabled credential cache type where the
-+ * credentials are stored in the Linux keyring facility.
- *
-- * Some assumptions:
-+ * A residual of this type can have three forms:
-+ * anchor:collection:subsidiary
-+ * anchor:collection
-+ * collection
- *
-- * - 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
-+ * The anchor name is "process", "thread", or "legacy" and determines where we
-+ * search for keyring collections. In the third form, the anchor name is
-+ * presumed to be "legacy". The anchor keyring for legacy caches is the
-+ * session keyring.
-+ *
-+ * If the subsidiary name is present, the residual identifies a single cache
-+ * within a collection. Otherwise, the residual identifies the collection
-+ * itself. When a residual identifying a collection is resolved, the
-+ * collection's primary key is looked up (or initialized, using the collection
-+ * name as the subsidiary name), and the resulting cache's name will use the
-+ * first name form and will identify the primary cache.
-+ *
-+ * Keyring collections are named "_krb_<collection>" and are linked from the
-+ * anchor keyring. The keys within a keyring collection are links to cache
-+ * keyrings, plus a link to one user key named "krb_ccache:primary" which
-+ * contains a serialized representation of the collection version (currently 1)
-+ * and the primary name of the collection.
-+ *
-+ * Cache keyrings contain one user key per credential which contains a
-+ * serialized representation of the credential. There is also one user key
-+ * named "__krb5_princ__" which contains a serialized representation of the
-+ * cache's default principal.
-+ *
-+ * If the anchor name is "legacy", then the initial primary cache (the one
-+ * named with the collection name) is also linked to the session keyring, and
-+ * we look for a cache in that location when initializing the collection. This
-+ * extra link allows that cache to be visible to old versions of the KEYRING
-+ * cache type, and allows us to see caches created by that code.
- */
-
- #include "cc-int.h"
-@@ -101,7 +126,20 @@ debug_print(char *fmt, ...)
- #endif
-
- /*
-- * We always use "user" key type
-+ * We try to use the big_key key type for credentials except in legacy caches.
-+ * We fall back to the user key type if the kernel does not support big_key.
-+ * If the library doesn't support keyctl_get_persistent(), we don't even try
-+ * big_key since the two features were added at the same time.
-+ */
-+#ifdef HAVE_PERSISTENT_KEYRING
-+#define KRCC_CRED_KEY_TYPE "big_key"
-+#else
-+#define KRCC_CRED_KEY_TYPE "user"
-+#endif
-+
-+/*
-+ * We use the "user" key type for collection primary names, for cache principal
-+ * names, and for credentials in legacy caches.
- */
- #define KRCC_KEY_TYPE_USER "user"
-
-@@ -117,20 +155,6 @@ debug_print(char *fmt, ...)
- #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
-@@ -139,26 +163,55 @@ debug_print(char *fmt, ...)
- */
- #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
-
-+/*
-+ * This name identifies the key containing the name of the current primary
-+ * cache within a collection.
-+ */
-+#define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
-+
-+/*
-+ * If the library context does not specify a keyring collection, unique ccaches
-+ * will be created within this collection.
-+ */
-+#define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
-+
-+/*
-+ * Collection keyring names begin with this prefix. We use a prefix so that a
-+ * cache keyring with the collection name itself can be linked directly into
-+ * the anchor, for legacy session keyring compatibility.
-+ */
-+#define KRCC_CCCOL_PREFIX "_krb_"
-+
-+/*
-+ * For the "persistent" anchor type, we look up or create this fixed keyring
-+ * name within the per-UID persistent keyring.
-+ */
-+#define KRCC_PERSISTENT_KEYRING_NAME "_krb"
-+
-+/*
-+ * Keyring name prefix and length of random name part
-+ */
-+#define KRCC_NAME_PREFIX "krb_ccache_"
-+#define KRCC_NAME_RAND_CHARS 8
-+
-+#define KRCC_COLLECTION_VERSION 1
-+
-+#define KRCC_PERSISTENT_ANCHOR "persistent"
-+#define KRCC_PROCESS_ANCHOR "process"
-+#define KRCC_THREAD_ANCHOR "thread"
-+#define KRCC_SESSION_ANCHOR "session"
-+#define KRCC_USER_ANCHOR "user"
-+#define KRCC_LEGACY_ANCHOR "legacy"
-+
- #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 MAX_CRED_SIZE (1024*1024)
-
- #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;
-@@ -169,7 +222,7 @@ typedef struct _krb5_krcc_cursor
-
- /*
- * This represents a credentials cache "file"
-- * where ring_id is the keyring serial number for
-+ * where cache_id is the keyring serial number for
- * this credentials cache "file". Each key
- * in the keyring contains a separate key.
- */
-@@ -177,12 +230,11 @@ typedef struct _krb5_krcc_data
- {
- char *name; /* Name for this credentials cache */
- k5_cc_mutex lock; /* synchronization */
-- key_serial_t parent_id; /* parent keyring of this ccache keyring */
-- key_serial_t ring_id; /* keyring representing ccache */
-+ key_serial_t collection_id; /* collection containing this cache keyring */
-+ key_serial_t cache_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_timestamp changetime;
-+ krb5_boolean is_legacy_type;
- } krb5_krcc_data;
-
- /* Passed internally to assure we don't go past the bounds of our buffer */
-@@ -190,6 +242,7 @@ typedef struct _krb5_krcc_buffer_cursor
- {
- char *bpp;
- char *endp;
-+ size_t size; /* For dry-run length calculation */
- } krb5_krcc_bc;
-
- /* Global mutex */
-@@ -258,6 +311,29 @@ static krb5_error_code KRB5_CALLCONV krb5_krcc_lock
- static krb5_error_code KRB5_CALLCONV krb5_krcc_unlock
- (krb5_context context, krb5_ccache id);
-
-+static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_new
-+(krb5_context context, krb5_cc_ptcursor *cursor_out);
-+
-+static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_next
-+(krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *cache_out);
-+
-+static krb5_error_code KRB5_CALLCONV krb5_krcc_ptcursor_free
-+(krb5_context context, krb5_cc_ptcursor *cursor);
-+
-+static krb5_error_code KRB5_CALLCONV krb5_krcc_switch_to
-+(krb5_context context, krb5_ccache cache);
-+
-+/* Like k5memdup, but add a final null byte. */
-+static inline void *
-+k5memdup0(const void *in, size_t len, krb5_error_code *code)
-+{
-+ void *ptr = k5alloc(len + 1, code);
-+
-+ if (ptr != NULL && len > 0)
-+ memcpy(ptr, in, len);
-+ return ptr;
-+}
-+
- /*
- * Internal utility functions
- */
-@@ -266,8 +331,9 @@ 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 **);
-+(const char *anchor_name, const char *collection_name,
-+ const char *subsidiary_name, key_serial_t cache_id,
-+ key_serial_t collection_id, krb5_krcc_data **datapp);
-
- static krb5_error_code krb5_krcc_save_principal
- (krb5_context context, krb5_ccache id, krb5_principal princ);
-@@ -275,100 +341,480 @@ static krb5_error_code krb5_krcc_save_principal
- 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);
-+(krb5_context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context context, 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);
-+(krb5_context, 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,
-+(krb5_context, krb5_pointer buf, unsigned int len, krb5_krcc_bc * bc);
-+static krb5_error_code krb5_krcc_unparse_cred_alloc
-+(krb5_context context, krb5_creds * creds,
- char **datapp, unsigned int *lenptr);
-+static krb5_error_code krb5_krcc_unparse_cred
-+(krb5_context context, krb5_creds * creds, krb5_krcc_bc * bc);
- static krb5_error_code krb5_krcc_unparse_principal
--(krb5_context, krb5_ccache id, krb5_principal princ, krb5_krcc_bc * bc);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, 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);
-+(krb5_context, krb5_int32 i, krb5_krcc_bc * bc);
- static void krb5_krcc_update_change_time
- (krb5_krcc_data *);
-
-+static krb5_error_code
-+krb5_krcc_parse_index(krb5_context context, krb5_int32 *version,
-+ char **primary, void *payload, int psize);
-+static krb5_error_code
-+krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
-+ const char *primary, void **datapp, int *lenptr);
-+
- /* Note the following is a stub function for Linux */
- extern krb5_error_code krb5_change_cache(void);
-
- /*
-- * Determine how many keys exist in a ccache keyring.
-- * Subtracts out the "hidden" key holding the principal information.
-+ * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
-+ * to the user keyring if uid matches the current effective uid.
-+ */
-+
-+static key_serial_t
-+get_persistent_fallback(uid_t uid)
-+{
-+ return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
-+}
-+
-+#ifdef HAVE_PERSISTENT_KEYRING
-+#define GET_PERSISTENT get_persistent_real
-+static key_serial_t
-+get_persistent_real(uid_t uid)
-+{
-+ key_serial_t key;
-+
-+ key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
-+ return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) :
-+ key;
-+}
-+#else
-+#define GET_PERSISTENT get_persistent_fallback
-+#endif
-+
-+/*
-+ * Find or create a keyring within parent with the given name. If possess is
-+ * nonzero, also make sure the key is linked from possess. This is necessary
-+ * to ensure that we have possession rights on the key when the parent is the
-+ * user or persistent keyring.
-+ */
-+static krb5_error_code
-+find_or_create_keyring(key_serial_t parent, key_serial_t possess,
-+ const char *name, key_serial_t *key_out)
-+{
-+ key_serial_t key;
-+
-+ *key_out = -1;
-+ key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
-+ if (key == -1) {
-+ if (possess != 0) {
-+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
-+ if (key == -1)
-+ return errno;
-+ if (keyctl_link(key, parent) == -1)
-+ return errno;
-+ } else {
-+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
-+ if (key == -1)
-+ return errno;
-+ }
-+ }
-+ *key_out = key;
-+ return 0;
-+}
-+
-+/* Parse a residual name into an anchor name, a collection name, and possibly a
-+ * subsidiary name. */
-+static krb5_error_code
-+parse_residual(const char *residual, char **anchor_name_out,
-+ char **collection_name_out, char **subsidiary_name_out)
-+{
-+ krb5_error_code ret;
-+ char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
-+ const char *sep;
-+
-+ *anchor_name_out = 0;
-+ *collection_name_out = NULL;
-+ *subsidiary_name_out = NULL;
-+
-+ /* Parse out the anchor name. Use the legacy anchor if not present. */
-+ sep = strchr(residual, ':');
-+ if (sep == NULL) {
-+ anchor_name = strdup(KRCC_LEGACY_ANCHOR);
-+ if (anchor_name == NULL)
-+ goto oom;
-+ } else {
-+ anchor_name = k5memdup0(residual, sep - residual, &ret);
-+ if (anchor_name == NULL)
-+ goto oom;
-+ residual = sep + 1;
-+ }
-+
-+ /* Parse out the collection and subsidiary name. */
-+ sep = strchr(residual, ':');
-+ if (sep == NULL) {
-+ collection_name = strdup(residual);
-+ if (collection_name == NULL)
-+ goto oom;
-+ subsidiary_name = NULL;
-+ } else {
-+ collection_name = k5memdup0(residual, sep - residual, &ret);
-+ if (collection_name == NULL)
-+ goto oom;
-+ subsidiary_name = strdup(sep + 1);
-+ if (subsidiary_name == NULL)
-+ goto oom;
-+ }
-+
-+ *anchor_name_out = anchor_name;
-+ *collection_name_out = collection_name;
-+ *subsidiary_name_out = subsidiary_name;
-+ return 0;
-+
-+oom:
-+ free(anchor_name);
-+ free(collection_name);
-+ free(subsidiary_name);
-+ return ENOMEM;
-+}
-+
-+/*
-+ * Return true if residual identifies a subsidiary cache which should be linked
-+ * into the anchor so it can be visible to old code. This is the case if the
-+ * residual has the legacy anchor and the subsidiary name matches the
-+ * collection name.
-+ */
-+static krb5_boolean
-+is_legacy_cache_name(const char *residual)
-+{
-+ const char *sep, *aname, *cname, *sname;
-+ size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
-+
-+ /* Get pointers to the anchor, collection, and subsidiary names. */
-+ aname = residual;
-+ sep = strchr(residual, ':');
-+ if (sep == NULL)
-+ return FALSE;
-+ alen = sep - aname;
-+ cname = sep + 1;
-+ sep = strchr(cname, ':');
-+ if (sep == NULL)
-+ return FALSE;
-+ clen = sep - cname;
-+ sname = sep + 1;
-+
-+ return alen == legacy_len && clen == strlen(sname) &&
-+ strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
-+ strncmp(cname, sname, clen) == 0;
-+}
-+
-+/* If the default cache name for context is a KEYRING cache, parse its residual
-+ * string. Otherwise set all outputs to NULL. */
-+static krb5_error_code
-+get_default(krb5_context context, char **anchor_name_out,
-+ char **collection_name_out, char **subsidiary_name_out)
-+{
-+ const char *defname;
-+
-+ *anchor_name_out = *collection_name_out = *subsidiary_name_out = NULL;
-+ defname = krb5_cc_default_name(context);
-+ if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
-+ return 0;
-+ return parse_residual(defname + 8, anchor_name_out, collection_name_out,
-+ subsidiary_name_out);
-+}
-+
-+/* Create a residual identifying a subsidiary cache. */
-+static krb5_error_code
-+make_subsidiary_residual(const char *anchor_name, const char *collection_name,
-+ const char *subsidiary_name, char **residual_out)
-+{
-+ if (asprintf(residual_out, "%s:%s:%s", anchor_name, collection_name,
-+ subsidiary_name) < 0) {
-+ *residual_out = NULL;
-+ return ENOMEM;
-+ }
-+ return 0;
-+}
-+
-+/* Retrieve or create a keyring for collection_name within the anchor, and set
-+ * *collection_id_out to its serial number. */
-+static krb5_error_code
-+get_collection(const char *anchor_name, const char *collection_name,
-+ key_serial_t *collection_id_out)
-+{
-+ krb5_error_code ret;
-+ key_serial_t persistent_id, anchor_id, possess_id = 0;
-+ char *ckname;
-+ long uidnum;
-+
-+ *collection_id_out = 0;
-+
-+ if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
-+ /*
-+ * The collection name is a uid (or empty for the current effective
-+ * uid), and we look up a fixed keyring name within the persistent
-+ * keyring for that uid. We link it to the process keyring to ensure
-+ * that we have possession rights on the collection key.
-+ */
-+ if (*collection_name != '\0') {
-+ errno = 0;
-+ uidnum = strtol(collection_name, NULL, 10);
-+ if (errno)
-+ return KRB5_KCC_INVALID_UID;
-+ } else {
-+ uidnum = geteuid();
-+ }
-+ persistent_id = GET_PERSISTENT(uidnum);
-+ if (persistent_id == -1)
-+ return KRB5_KCC_INVALID_UID;
-+ return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
-+ KRCC_PERSISTENT_KEYRING_NAME,
-+ collection_id_out);
-+ }
-+
-+ if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
-+ anchor_id = KEY_SPEC_PROCESS_KEYRING;
-+ } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
-+ anchor_id = KEY_SPEC_THREAD_KEYRING;
-+ } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
-+ anchor_id = KEY_SPEC_SESSION_KEYRING;
-+ } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
-+ /* The user keyring does not confer possession, so we need to link the
-+ * collection to the process keyring to maintain possession rights. */
-+ anchor_id = KEY_SPEC_USER_KEYRING;
-+ possess_id = KEY_SPEC_PROCESS_KEYRING;
-+ } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
-+ anchor_id = KEY_SPEC_SESSION_KEYRING;
-+ } else {
-+ return KRB5_KCC_INVALID_ANCHOR;
-+ }
-+
-+ /* Look up the collection keyring name within the anchor keyring. */
-+ if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
-+ return ENOMEM;
-+ ret = find_or_create_keyring(anchor_id, possess_id, ckname,
-+ collection_id_out);
-+ free(ckname);
-+ return ret;
-+}
-+
-+/* Store subsidiary_name into the primary index key for collection_id. */
-+static krb5_error_code
-+set_primary_name(krb5_context context, key_serial_t collection_id,
-+ const char *subsidiary_name)
-+{
-+ krb5_error_code ret;
-+ key_serial_t key;
-+ void *payload = NULL;
-+ int payloadlen;
-+
-+ ret = krb5_krcc_unparse_index(context, KRCC_COLLECTION_VERSION,
-+ subsidiary_name, &payload, &payloadlen);
-+ if (ret)
-+ return ret;
-+ key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
-+ payload, payloadlen, collection_id);
-+ free(payload);
-+ return (key == -1) ? errno : 0;
-+}
-+
-+/*
-+ * Get or initialize the primary name within collection_id and set
-+ * *subsidiary_out to its value. If initializing a legacy collection, look
-+ * for a legacy cache and add it to the collection.
-+ */
-+static krb5_error_code
-+get_primary_name(krb5_context context, const char *anchor_name,
-+ const char *collection_name, key_serial_t collection_id,
-+ char **subsidiary_out)
-+{
-+ krb5_error_code ret;
-+ key_serial_t primary_id, legacy;
-+ void *payload = NULL;
-+ int payloadlen;
-+ krb5_int32 version;
-+ char *subsidiary_name = NULL;
-+
-+ *subsidiary_out = NULL;
-+
-+ primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
-+ KRCC_COLLECTION_PRIMARY, 0);
-+ if (primary_id == -1) {
-+ /* Initialize the primary key using the collection name. We can't name
-+ * a key with the empty string, so map that to an arbitrary string. */
-+ subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
-+ collection_name);
-+ if (subsidiary_name == NULL) {
-+ ret = ENOMEM;
-+ goto cleanup;
-+ }
-+ ret = set_primary_name(context, collection_id, subsidiary_name);
-+ if (ret)
-+ goto cleanup;
-+
-+ if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
-+ /* Look for a cache created by old code. If we find one, add it to
-+ * the collection. */
-+ legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
-+ KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
-+ if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
-+ ret = errno;
-+ goto cleanup;
-+ }
-+ }
-+ } else {
-+ /* Read, parse, and free the primary key's payload. */
-+ payloadlen = keyctl_read_alloc(primary_id, &payload);
-+ if (payloadlen == -1) {
-+ ret = errno;
-+ goto cleanup;
-+ }
-+ ret = krb5_krcc_parse_index(context, &version, &subsidiary_name,
-+ payload, payloadlen);
-+ if (ret)
-+ goto cleanup;
-+
-+ if (version != KRCC_COLLECTION_VERSION) {
-+ ret = KRB5_KCC_UNKNOWN_VERSION;
-+ goto cleanup;
-+ }
-+ }
-+
-+ *subsidiary_out = subsidiary_name;
-+ subsidiary_name = NULL;
-+
-+cleanup:
-+ free(payload);
-+ free(subsidiary_name);
-+ return ret;
-+}
-+
-+/*
-+ * Create a keyring with a unique random name within collection_id. Set
-+ * *subsidiary to its name and *cache_id_out to its key serial number.
- */
--static int KRB5_CALLCONV
--krb5_krcc_getkeycount(key_serial_t cred_ring)
-+static krb5_error_code
-+unique_keyring(krb5_context context, key_serial_t collection_id,
-+ char **subsidiary_out, key_serial_t *cache_id_out)
- {
-- int res, nkeys;
-+ key_serial_t key;
-+ krb5_error_code ret;
-+ char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
-+ int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
-+ int tries;
-+
-+ *subsidiary_out = NULL;
-+ *cache_id_out = 0;
-+
-+ memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
-+ k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
-+
-+ /* Loop until we successfully create a new ccache keyring with
-+ * a unique name, or we get an error. Limit to 100 tries. */
-+ tries = 100;
-+ while (tries-- > 0) {
-+ ret = krb5int_random_string(context, uniquename + prefixlen,
-+ KRCC_NAME_RAND_CHARS);
-+ if (ret)
-+ goto cleanup;
-+
-+ key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename,
-+ 0);
-+ if (key < 0) {
-+ /* Name does not already exist. Create it to reserve the name. */
-+ key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0,
-+ collection_id);
-+ if (key < 0) {
-+ ret = errno;
-+ goto cleanup;
-+ }
-+ break;
-+ }
-+ }
-
-- res = keyctl_read(cred_ring, NULL, 0);
-- if (res > 0)
-- nkeys = (res / sizeof(key_serial_t)) - 1;
-- else
-- nkeys = 0;
-- return(nkeys);
-+ if (tries <= 0) {
-+ ret = KRB5_CC_BADNAME;
-+ goto cleanup;
-+ }
-+
-+ *subsidiary_out = strdup(uniquename);
-+ if (*subsidiary_out == NULL) {
-+ ret = ENOMEM;
-+ goto cleanup;
-+ }
-+ *cache_id_out = key;
-+ ret = KRB5_OK;
-+cleanup:
-+ k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
-+ return ret;
-+}
-+
-+static krb5_error_code
-+add_cred_key(const char *name, const void *payload, size_t plen,
-+ key_serial_t cache_id, krb5_boolean legacy_type)
-+{
-+ key_serial_t key;
-+
-+ if (!legacy_type) {
-+ /* Try the preferred cred key type; fall back if no kernel support. */
-+ key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
-+ if (key != -1)
-+ return 0;
-+ else if (errno != EINVAL && errno != ENODEV)
-+ return errno;
-+ }
-+ /* Use the user key type. */
-+ key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
-+ return (key == -1) ? errno : 0;
- }
-
- /*
-@@ -388,24 +834,40 @@ static krb5_error_code KRB5_CALLCONV
- krb5_krcc_initialize(krb5_context context, krb5_ccache id,
- krb5_principal princ)
- {
-+ krb5_krcc_data *data = (krb5_krcc_data *)id->data;
- krb5_error_code kret;
-+ const char *cache_name, *p;
-
- DEBUG_PRINT(("krb5_krcc_initialize: entered\n"));
-
-- kret = k5_cc_mutex_lock(context, &((krb5_krcc_data *) id->data)->lock);
-- if (kret)
-- return kret;
-+ k5_cc_mutex_lock(context, &data->lock);
-
- kret = krb5_krcc_clearcache(context, id);
- if (kret != KRB5_OK)
- goto out;
-
-+ if (!data->cache_id) {
-+ /* The key didn't exist at resolve time. Check again and create the
-+ * key if it still isn't there. */
-+ p = strrchr(data->name, ':');
-+ cache_name = (p != NULL) ? p + 1 : data->name;
-+ kret = find_or_create_keyring(data->collection_id, 0, cache_name,
-+ &data->cache_id);
-+ if (kret)
-+ goto out;
-+ }
-+
-+ /* If this is the legacy cache in a legacy session collection, link it
-+ * directly to the session keyring so that old code can see it. */
-+ if (is_legacy_cache_name(data->name))
-+ (void)keyctl_link(data->cache_id, KEY_SPEC_SESSION_KEYRING);
-+
- kret = krb5_krcc_save_principal(context, id, princ);
- if (kret == KRB5_OK)
- krb5_change_cache();
-
- out:
-- k5_cc_mutex_unlock(context, &((krb5_krcc_data *) id->data)->lock);
-+ k5_cc_mutex_unlock(context, &data->lock);
- return kret;
- }
-
-@@ -460,14 +922,14 @@ krb5_krcc_clearcache(krb5_context context, krb5_ccache id)
-
- 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));
-+ DEBUG_PRINT(("krb5_krcc_clearcache: cache_id %d, princ_id %d\n",
-+ d->cache_id, d->princ_id));
-
-- res = keyctl_clear(d->ring_id);
-- if (res != 0) {
-- return errno;
-+ if (d->cache_id) {
-+ res = keyctl_clear(d->cache_id);
-+ if (res != 0)
-+ return errno;
- }
-- d->numkeys = 0;
- d->princ_id = 0;
- krb5_krcc_update_change_time(d);
-
-@@ -484,7 +946,7 @@ krb5_krcc_clearcache(krb5_context context, krb5_ccache id)
- static krb5_error_code KRB5_CALLCONV
- krb5_krcc_destroy(krb5_context context, krb5_ccache id)
- {
-- krb5_error_code kret;
-+ krb5_error_code kret = 0;
- krb5_krcc_data *d;
- int res;
-
-@@ -492,30 +954,67 @@ krb5_krcc_destroy(krb5_context context, krb5_ccache id)
-
- d = (krb5_krcc_data *) id->data;
-
-- kret = k5_cc_mutex_lock(context, &d->lock);
-- if (kret)
-- return kret;
-+ k5_cc_mutex_lock(context, &d->lock);
-
- krb5_krcc_clearcache(context, id);
-- free(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;
-+ if (d->cache_id) {
-+ res = keyctl_unlink(d->cache_id, d->collection_id);
-+ if (res < 0) {
-+ kret = errno;
-+ DEBUG_PRINT(("unlinking key %d from ring %d: %s",
-+ d->cache_id, d->collection_id, error_message(errno)));
-+ }
-+ /* If this is a legacy cache, unlink it from the session anchor. */
-+ if (is_legacy_cache_name(d->name))
-+ (void)keyctl_unlink(d->cache_id, KEY_SPEC_SESSION_KEYRING);
- }
--cleanup:
-+
- k5_cc_mutex_unlock(context, &d->lock);
- k5_cc_mutex_destroy(&d->lock);
-+ free(d->name);
- free(d);
- free(id);
-
- krb5_change_cache();
-
-- return KRB5_OK;
-+ return kret;
- }
-
-+/* Create a cache handle for a cache ID. */
-+static krb5_error_code
-+make_cache(key_serial_t collection_id, key_serial_t cache_id,
-+ const char *anchor_name, const char *collection_name,
-+ const char *subsidiary_name, krb5_ccache *cache_out)
-+{
-+ krb5_error_code ret;
-+ krb5_ccache ccache = NULL;
-+ krb5_krcc_data *d;
-+ key_serial_t pkey = 0;
-+
-+ /* Determine the key containing principal information, if present. */
-+ pkey = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
-+ 0);
-+ if (pkey < 0)
-+ pkey = 0;
-+
-+ ccache = malloc(sizeof(struct _krb5_ccache));
-+ if (!ccache)
-+ return ENOMEM;
-+
-+ ret = krb5_krcc_new_data(anchor_name, collection_name, subsidiary_name,
-+ cache_id, collection_id, &d);
-+ if (ret) {
-+ free(ccache);
-+ return ret;
-+ }
-+
-+ d->princ_id = pkey;
-+ ccache->ops = &krb5_krcc_ops;
-+ ccache->data = d;
-+ ccache->magic = KV5M_CCACHE;
-+ *cache_out = ccache;
-+ return 0;
-+}
-
- /*
- * Requires:
-@@ -538,101 +1037,42 @@ cleanup:
- */
-
- static krb5_error_code KRB5_CALLCONV
--krb5_krcc_resolve(krb5_context context, krb5_ccache * id, const char *full_residual)
-+krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *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;
-+ krb5_error_code ret;
-+ key_serial_t collection_id, cache_id;
-+ char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
-
-- DEBUG_PRINT(("krb5_krcc_resolve: entered with name '%s'\n",
-- full_residual));
-+ ret = parse_residual(residual, &anchor_name, &collection_name,
-+ &subsidiary_name);
-+ if (ret)
-+ goto cleanup;
-+ ret = get_collection(anchor_name, collection_name, &collection_id);
-+ if (ret)
-+ goto cleanup;
-
-- 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 (subsidiary_name == NULL) {
-+ /* Retrieve or initialize the primary name for the collection. */
-+ ret = get_primary_name(context, anchor_name, collection_name,
-+ collection_id, &subsidiary_name);
-+ if (ret)
-+ goto cleanup;
- }
-
-- 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;
-- }
-+ /* Look up the cache keyring ID, if the cache is already initialized. */
-+ cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
-+ subsidiary_name, 0);
-+ if (cache_id < 0)
-+ cache_id = 0;
-
-- DEBUG_PRINT(("krb5_krcc_resolve: searching ring %d for residual '%s'\n",
-- ring_id, residual));
-+ ret = make_cache(collection_id, cache_id, anchor_name, collection_name,
-+ subsidiary_name, id);
-
-- /*
-- * 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 */
-- nkeys = krb5_krcc_getkeycount(key);
-- }
--
-- 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;
-+cleanup:
-+ free(anchor_name);
-+ free(collection_name);
-+ free(subsidiary_name);
-+ return ret;
- }
-
- /*
-@@ -653,47 +1093,37 @@ 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;
-+ void *keys;
-+ long size;
-
- DEBUG_PRINT(("krb5_krcc_start_seq_get: entered\n"));
-
- d = id->data;
-- kret = k5_cc_mutex_lock(context, &d->lock);
-- if (kret)
-- return kret;
--
-- /*
-- * Determine how many keys currently exist and update numkeys.
-- * We cannot depend on the current value of numkeys because
-- * the ccache may have been updated elsewhere
-- */
-- d->numkeys = krb5_krcc_getkeycount(d->ring_id);
-+ k5_cc_mutex_lock(context, &d->lock);
-
-- size = sizeof(*krcursor) + ((d->numkeys + 1) * sizeof(key_serial_t));
--
-- krcursor = (krb5_krcc_cursor) malloc(size);
-- if (krcursor == NULL) {
-+ if (!d->cache_id) {
- k5_cc_mutex_unlock(context, &d->lock);
-- return KRB5_CC_NOMEM;
-+ return KRB5_FCC_NOFILE;
- }
-
-- 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);
-+ size = keyctl_read_alloc(d->cache_id, &keys);
-+ if (size == -1) {
-+ DEBUG_PRINT(("Error getting from keyring: %s\n", strerror(errno)));
- k5_cc_mutex_unlock(context, &d->lock);
- return KRB5_CC_IO;
- }
-
-- krcursor->numkeys = d->numkeys;
-- krcursor->currkey = 0;
-+ krcursor = calloc(1, sizeof(*krcursor));
-+ if (krcursor == NULL) {
-+ free(keys);
-+ k5_cc_mutex_unlock(context, &d->lock);
-+ return KRB5_CC_NOMEM;
-+ }
-+
- krcursor->princ_id = d->princ_id;
-+ krcursor->numkeys = size / sizeof(key_serial_t);
-+ krcursor->keys = keys;
-
- k5_cc_mutex_unlock(context, &d->lock);
- *cursor = (krb5_cc_cursor) krcursor;
-@@ -741,14 +1171,14 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
- memset(creds, 0, sizeof(krb5_creds));
-
- /* If we're pointing past the end of the keys array, there are no more */
-- if (krcursor->currkey > krcursor->numkeys)
-+ if (krcursor->currkey >= krcursor->numkeys)
- return KRB5_CC_END;
-
- /* If we're pointing at the entry with the principal, skip it */
- if (krcursor->keys[krcursor->currkey] == krcursor->princ_id) {
- krcursor->currkey++;
- /* Check if we have now reached the end */
-- if (krcursor->currkey > krcursor->numkeys)
-+ if (krcursor->currkey >= krcursor->numkeys)
- return KRB5_CC_END;
- }
-
-@@ -763,7 +1193,7 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
- }
- krcursor->currkey++;
-
-- kret = krb5_krcc_parse_cred(context, id, creds, payload, psize);
-+ kret = krb5_krcc_parse_cred(context, creds, payload, psize);
-
- freepayload:
- if (payload) free(payload);
-@@ -787,19 +1217,24 @@ static krb5_error_code KRB5_CALLCONV
- krb5_krcc_end_seq_get(krb5_context context, krb5_ccache id,
- krb5_cc_cursor * cursor)
- {
-+ krb5_krcc_cursor krcursor = (krb5_krcc_cursor)*cursor;
- DEBUG_PRINT(("krb5_krcc_end_seq_get: entered\n"));
-
-- free(*cursor);
-- *cursor = 0L;
-+ if (krcursor != NULL) {
-+ free(krcursor->keys);
-+ free(krcursor);
-+ }
-+ *cursor = NULL;
- 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)
-+static krb5_error_code
-+krb5_krcc_new_data(const char *anchor_name, const char *collection_name,
-+ const char *subsidiary_name, key_serial_t cache_id,
-+ key_serial_t collection_id, krb5_krcc_data **datapp)
- {
- krb5_error_code kret;
- krb5_krcc_data *d;
-@@ -814,17 +1249,18 @@ krb5_krcc_new_data(const char *name, key_serial_t ring,
- return kret;
- }
-
-- d->name = strdup(name);
-- if (d->name == NULL) {
-+ kret = make_subsidiary_residual(anchor_name, collection_name,
-+ subsidiary_name, &d->name);
-+ if (kret) {
- k5_cc_mutex_destroy(&d->lock);
- free(d);
-- return KRB5_CC_NOMEM;
-+ return kret;
- }
- d->princ_id = 0;
-- d->ring_id = ring;
-- d->parent_id = parent_ring;
-- d->numkeys = 0;
-+ d->cache_id = cache_id;
-+ d->collection_id = collection_id;
- d->changetime = 0;
-+ d->is_legacy_type = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
- krb5_krcc_update_change_time(d);
-
- *datapp = d;
-@@ -846,82 +1282,73 @@ krb5_krcc_new_data(const char *name, key_serial_t ring,
- static krb5_error_code KRB5_CALLCONV
- krb5_krcc_generate_new(krb5_context context, krb5_ccache * id)
- {
-- krb5_ccache lid;
-- char uniquename[8];
-+ krb5_ccache lid = NULL;
- krb5_error_code kret;
-+ char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
-+ char *new_subsidiary_name = NULL, *new_residual = NULL;
- krb5_krcc_data *d;
-- key_serial_t ring_id = KEY_SPEC_SESSION_KEYRING;
-- key_serial_t key;
-+ key_serial_t collection_id;
-+ key_serial_t cache_id = 0;
-
- DEBUG_PRINT(("krb5_krcc_generate_new: entered\n"));
-
-+ /* Determine the collection in which we will create the cache.*/
-+ kret = get_default(context, &anchor_name, &collection_name,
-+ &subsidiary_name);
-+ if (kret)
-+ return kret;
-+ if (anchor_name == NULL) {
-+ kret = parse_residual(KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
-+ &collection_name, &subsidiary_name);
-+ if (kret)
-+ return kret;
-+ }
-+ if (subsidiary_name != NULL) {
-+ krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
-+ _("Can't create new subsidiary cache because "
-+ "default cache is already a subsdiary"));
-+ kret = KRB5_DCC_CANNOT_CREATE;
-+ goto cleanup;
-+ }
-+
- /* Allocate memory */
- lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
-- if (lid == NULL)
-- return KRB5_CC_NOMEM;
-+ if (lid == NULL) {
-+ kret = ENOMEM;
-+ goto cleanup;
-+ }
-
- lid->ops = &krb5_krcc_ops;
-
-- kret = k5_cc_mutex_lock(context, &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
-+ /* Make a unique keyring within the chosen collection. */
-+ kret = get_collection(anchor_name, collection_name, &collection_id);
-+ if (kret)
-+ goto cleanup;
-+ kret = unique_keyring(context, collection_id, &new_subsidiary_name,
-+ &cache_id);
-+ if (kret)
-+ goto cleanup;
-
-- /*
-- * Loop until we successfully create a new ccache keyring with
-- * a unique name, or we get an error.
-- */
-- while (1) {
-- kret = krb5int_random_string(context, uniquename, sizeof(uniquename));
-- if (kret) {
-- k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
-- free(lid);
-- return kret;
-- }
-+ kret = krb5_krcc_new_data(anchor_name, collection_name,
-+ new_subsidiary_name, cache_id, collection_id,
-+ &d);
-+ if (kret)
-+ goto cleanup;
-
-- 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_cc_mutex_unlock(context, &krb5int_krcc_mutex);
-- return kret;
-- }
-- break;
-- }
-- }
-+ lid->data = d;
-+ krb5_change_cache();
-
-- kret = krb5_krcc_new_data(uniquename, key, ring_id, &d);
-- k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
-+cleanup:
-+ free(anchor_name);
-+ free(collection_name);
-+ free(subsidiary_name);
-+ free(new_subsidiary_name);
-+ free(new_residual);
- if (kret) {
- free(lid);
- return kret;
- }
-- lid->data = d;
- *id = lid;
-- krb5_change_cache();
- return KRB5_OK;
- }
-
-@@ -1023,14 +1450,16 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
- 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_cc_mutex_lock(context, &d->lock);
-- if (kret)
-- return kret;
-+ k5_cc_mutex_lock(context, &d->lock);
-+
-+ if (!d->cache_id) {
-+ k5_cc_mutex_unlock(context, &d->lock);
-+ return KRB5_FCC_NOFILE;
-+ }
-
- /* Get the service principal name and use it as the key name */
- kret = krb5_unparse_name(context, creds->server, &keyname);
-@@ -1040,24 +1469,19 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
- }
-
- /* Serialize credential into memory */
-- kret = krb5_krcc_unparse_cred(context, id, creds, &payload, &payloadlen);
-+ kret = krb5_krcc_unparse_cred_alloc(context, 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;
-- krb5_krcc_update_change_time(d);
-- }
-+ keyname, d->cache_id));
-+ kret = add_cred_key(keyname, payload, payloadlen, d->cache_id,
-+ d->is_legacy_type);
-+ if (kret)
-+ goto errout;
-+
-+ krb5_krcc_update_change_time(d);
-
- errout:
- if (keyname)
-@@ -1073,36 +1497,30 @@ static krb5_error_code KRB5_CALLCONV
- krb5_krcc_last_change_time(krb5_context context, krb5_ccache id,
- krb5_timestamp *change_time)
- {
-- krb5_error_code ret = 0;
- krb5_krcc_data *data = (krb5_krcc_data *) id->data;
-
-- *change_time = 0;
--
-- ret = k5_cc_mutex_lock(context, &data->lock);
-- if (!ret) {
-- *change_time = data->changetime;
-- k5_cc_mutex_unlock(context, &data->lock);
-- }
--
-- return ret;
-+ k5_cc_mutex_lock(context, &data->lock);
-+ *change_time = data->changetime;
-+ k5_cc_mutex_unlock(context, &data->lock);
-+ return 0;
- }
-
- static krb5_error_code KRB5_CALLCONV
- krb5_krcc_lock(krb5_context context, krb5_ccache id)
- {
-- krb5_error_code ret = 0;
- krb5_krcc_data *data = (krb5_krcc_data *) id->data;
-- ret = k5_cc_mutex_lock(context, &data->lock);
-- return ret;
-+
-+ k5_cc_mutex_lock(context, &data->lock);
-+ return 0;
- }
-
- static krb5_error_code KRB5_CALLCONV
- krb5_krcc_unlock(krb5_context context, krb5_ccache id)
- {
-- krb5_error_code ret = 0;
- krb5_krcc_data *data = (krb5_krcc_data *) id->data;
-- ret = k5_cc_mutex_unlock(context, &data->lock);
-- return ret;
-+
-+ k5_cc_mutex_unlock(context, &data->lock);
-+ return 0;
- }
-
-
-@@ -1112,7 +1530,7 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
- {
- krb5_krcc_data *d;
- krb5_error_code kret;
-- char *payload;
-+ char *payload = NULL;
- key_serial_t newkey;
- unsigned int payloadsize;
- krb5_krcc_bc bc;
-@@ -1121,14 +1539,19 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
-
- d = (krb5_krcc_data *) id->data;
-
-- payload = malloc(GUESS_CRED_SIZE);
-+ /* Do a dry run first to calculate the size. */
-+ bc.bpp = bc.endp = NULL;
-+ bc.size = 0;
-+ kret = krb5_krcc_unparse_principal(context, princ, &bc);
-+ CHECK_N_GO(kret, errout);
-+
-+ /* Allocate a buffer and serialize for real. */
-+ payload = malloc(bc.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);
-+ bc.endp = payload + bc.size;
-+ kret = krb5_krcc_unparse_principal(context, princ, &bc);
- CHECK_N_GO(kret, errout);
-
- /* Add new key into keyring */
-@@ -1140,14 +1563,14 @@ krb5_krcc_save_principal(krb5_context context, krb5_ccache id,
- 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,
-+ KRCC_SPEC_PRINC_KEYNAME, d->cache_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);
-+ payloadsize, d->cache_id);
- if (newkey < 0) {
- kret = errno;
- DEBUG_PRINT(("Error adding principal key: %s\n", strerror(kret)));
-@@ -1172,11 +1595,9 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
- int psize;
- krb5_krcc_bc bc;
-
-- kret = k5_cc_mutex_lock(context, &d->lock);
-- if (kret)
-- return kret;
-+ k5_cc_mutex_lock(context, &d->lock);
-
-- if (!d->princ_id) {
-+ if (!d->cache_id || !d->princ_id) {
- princ = 0L;
- kret = KRB5_FCC_NOFILE;
- goto errout;
-@@ -1191,7 +1612,7 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
- }
- bc.bpp = payload;
- bc.endp = (char *)payload + psize;
-- kret = krb5_krcc_parse_principal(context, id, princ, &bc);
-+ kret = krb5_krcc_parse_principal(context, princ, &bc);
-
- errout:
- if (payload)
-@@ -1200,57 +1621,195 @@ errout:
- 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;
-+struct krcc_ptcursor_data {
-+ key_serial_t collection_id;
-+ char *anchor_name;
-+ char *collection_name;
-+ char *subsidiary_name;
-+ char *primary_name;
-+ krb5_boolean first;
-+ long num_keys;
-+ long next_key;
-+ key_serial_t *keys;
-+};
-
-- DEBUG_PRINT(("krb5_krcc_get_ring_ids: entered\n"));
-+static krb5_error_code KRB5_CALLCONV
-+krb5_krcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
-+{
-+ struct krcc_ptcursor_data *data;
-+ krb5_cc_ptcursor cursor;
-+ krb5_error_code ret;
-+ long size;
-+
-+ *cursor_out = NULL;
-+
-+ cursor = k5alloc(sizeof(struct krb5_cc_ptcursor_s), &ret);
-+ if (cursor == NULL)
-+ return ENOMEM;
-+ data = k5alloc(sizeof(struct krcc_ptcursor_data), &ret);
-+ if (data == NULL)
-+ goto error;
-+ cursor->ops = &krb5_krcc_ops;
-+ cursor->data = data;
-+ data->first = TRUE;
-+
-+ ret = get_default(context, &data->anchor_name, &data->collection_name,
-+ &data->subsidiary_name);
-+ if (ret)
-+ goto error;
-+
-+ /* If there is no default collection, return an empty cursor. */
-+ if (data->anchor_name == NULL) {
-+ *cursor_out = cursor;
-+ return 0;
-+ }
-
-- if (!p)
-- return EINVAL;
-+ ret = get_collection(data->anchor_name, data->collection_name,
-+ &data->collection_id);
-+ if (ret)
-+ goto error;
-+
-+ if (data->subsidiary_name == NULL) {
-+ ret = get_primary_name(context, data->anchor_name,
-+ data->collection_name, data->collection_id,
-+ &data->primary_name);
-+ if (ret)
-+ goto error;
-+
-+ size = keyctl_read_alloc(data->collection_id, (void **)&data->keys);
-+ if (size == -1) {
-+ ret = errno;
-+ goto error;
-+ }
-+ data->num_keys = size / sizeof(key_serial_t);
-+ }
-
-- /* 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;
-+ *cursor_out = cursor;
-+ return 0;
-
-- /*
-- * 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;
-+error:
-+ krb5_krcc_ptcursor_free(context, &cursor);
-+ return ret;
-+}
-
-- 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;
-+static krb5_error_code KRB5_CALLCONV
-+krb5_krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
-+ krb5_ccache *cache_out)
-+{
-+ krb5_error_code ret;
-+ struct krcc_ptcursor_data *data;
-+ key_serial_t key, cache_id = 0;
-+ const char *first_name, *keytype, *sep, *subsidiary_name;
-+ size_t keytypelen;
-+ char *description = NULL;
-+
-+ *cache_out = NULL;
-+
-+ data = cursor->data;
-+
-+ /* No keyring available */
-+ if (data->collection_id == 0)
-+ return 0;
-+
-+ if (data->first) {
-+ /* Look for the primary cache for a collection cursor, or the
-+ * subsidiary cache for a subsidiary cursor. */
-+ data->first = FALSE;
-+ first_name = (data->primary_name != NULL) ? data->primary_name :
-+ data->subsidiary_name;
-+ cache_id = keyctl_search(data->collection_id, KRCC_KEY_TYPE_KEYRING,
-+ first_name, 0);
-+ if (cache_id != -1) {
-+ return make_cache(data->collection_id, cache_id, data->anchor_name,
-+ data->collection_name, first_name, cache_out);
-+ }
-+ }
-
-- val = sscanf(ids_buf, "%d:%d:%d", &session, &process, &thread);
-- if (val != 3)
-- goto out;
-+ /* A subsidiary cursor yields at most the first cache. */
-+ if (data->subsidiary_name != NULL)
-+ return 0;
-+
-+ keytype = KRCC_KEY_TYPE_KEYRING ";";
-+ keytypelen = strlen(keytype);
-+
-+ for (; data->next_key < data->num_keys; data->next_key++) {
-+ /* Free any previously retrieved key description. */
-+ free(description);
-+ description = NULL;
-+
-+ /*
-+ * Get the key description, which should have the form:
-+ * typename;UID;GID;permissions;description
-+ */
-+ key = data->keys[data->next_key];
-+ if (keyctl_describe_alloc(key, &description) < 0)
-+ continue;
-+ sep = strrchr(description, ';');
-+ if (sep == NULL)
-+ continue;
-+ subsidiary_name = sep + 1;
-+
-+ /* Skip this key if it isn't a keyring. */
-+ if (strncmp(description, keytype, keytypelen) != 0)
-+ continue;
-+
-+ /* Don't repeat the primary cache. */
-+ if (strcmp(subsidiary_name, data->primary_name) == 0)
-+ continue;
-+
-+ /* We found a valid key */
-+ data->next_key++;
-+ ret = make_cache(data->collection_id, key, data->anchor_name,
-+ data->collection_name, subsidiary_name, cache_out);
-+ free(description);
-+ return ret;
-+ }
-
-- p->session = session;
-- p->process = process;
-- p->thread = thread;
-+ free(description);
-+ return 0;
-+}
-
--out:
-- DEBUG_PRINT(("krb5_krcc_get_ring_ids: returning %d:%d:%d\n",
-- p->session, p->process, p->thread));
-+static krb5_error_code KRB5_CALLCONV
-+krb5_krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
-+{
-+ struct krcc_ptcursor_data *data = (*cursor)->data;
-+
-+ if (data != NULL) {
-+ free(data->anchor_name);
-+ free(data->collection_name);
-+ free(data->subsidiary_name);
-+ free(data->primary_name);
-+ free(data->keys);
-+ free(data);
-+ }
-+ free(*cursor);
-+ *cursor = NULL;
- return 0;
- }
-
-+static krb5_error_code KRB5_CALLCONV
-+krb5_krcc_switch_to(krb5_context context, krb5_ccache cache)
-+{
-+ krb5_krcc_data *data = cache->data;
-+ krb5_error_code ret;
-+ char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
-+ key_serial_t collection_id;
-+
-+ ret = parse_residual(data->name, &anchor_name, &collection_name,
-+ &subsidiary_name);
-+ if (ret)
-+ goto cleanup;
-+ ret = get_collection(anchor_name, collection_name, &collection_id);
-+ if (ret)
-+ goto cleanup;
-+ ret = set_primary_name(context, collection_id, subsidiary_name);
-+cleanup:
-+ free(anchor_name);
-+ free(collection_name);
-+ free(subsidiary_name);
-+ return ret;
-+}
-+
- /*
- * ===============================================================
- * INTERNAL functions to parse a credential from a key payload
-@@ -1271,8 +1830,8 @@ out:
- * 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)
-+krb5_krcc_parse(krb5_context context, krb5_pointer buf, unsigned int len,
-+ krb5_krcc_bc * bc)
- {
- DEBUG_PRINT(("krb5_krcc_parse: entered\n"));
-
-@@ -1290,8 +1849,8 @@ krb5_krcc_parse(krb5_context context, krb5_ccache id, krb5_pointer buf,
- * 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_krcc_parse_cred(krb5_context context, krb5_creds * creds, char *payload,
-+ int psize)
- {
- krb5_error_code kret;
- krb5_octet octet;
-@@ -1301,36 +1860,36 @@ krb5_krcc_parse_cred(krb5_context context, krb5_ccache id, krb5_creds * creds,
- /* Parse the pieces of the credential */
- bc.bpp = payload;
- bc.endp = bc.bpp + psize;
-- kret = krb5_krcc_parse_principal(context, id, &creds->client, &bc);
-+ kret = krb5_krcc_parse_principal(context, &creds->client, &bc);
- CHECK_N_GO(kret, out);
-
-- kret = krb5_krcc_parse_principal(context, id, &creds->server, &bc);
-+ kret = krb5_krcc_parse_principal(context, &creds->server, &bc);
- CHECK_N_GO(kret, cleanclient);
-
-- kret = krb5_krcc_parse_keyblock(context, id, &creds->keyblock, &bc);
-+ kret = krb5_krcc_parse_keyblock(context, &creds->keyblock, &bc);
- CHECK_N_GO(kret, cleanserver);
-
-- kret = krb5_krcc_parse_times(context, id, &creds->times, &bc);
-+ kret = krb5_krcc_parse_times(context, &creds->times, &bc);
- CHECK_N_GO(kret, cleanserver);
-
-- kret = krb5_krcc_parse_octet(context, id, &octet, &bc);
-+ kret = krb5_krcc_parse_octet(context, &octet, &bc);
- CHECK_N_GO(kret, cleanserver);
- creds->is_skey = octet;
-
-- kret = krb5_krcc_parse_int32(context, id, &int32, &bc);
-+ kret = krb5_krcc_parse_int32(context, &int32, &bc);
- CHECK_N_GO(kret, cleanserver);
- creds->ticket_flags = int32;
-
-- kret = krb5_krcc_parse_addrs(context, id, &creds->addresses, &bc);
-+ kret = krb5_krcc_parse_addrs(context, &creds->addresses, &bc);
- CHECK_N_GO(kret, cleanblock);
-
-- kret = krb5_krcc_parse_authdata(context, id, &creds->authdata, &bc);
-+ kret = krb5_krcc_parse_authdata(context, &creds->authdata, &bc);
- CHECK_N_GO(kret, cleanaddrs);
-
-- kret = krb5_krcc_parse_krb5data(context, id, &creds->ticket, &bc);
-+ kret = krb5_krcc_parse_krb5data(context, &creds->ticket, &bc);
- CHECK_N_GO(kret, cleanauthdata);
-
-- kret = krb5_krcc_parse_krb5data(context, id, &creds->second_ticket, &bc);
-+ kret = krb5_krcc_parse_krb5data(context, &creds->second_ticket, &bc);
- CHECK_N_GO(kret, cleanticket);
-
- kret = KRB5_OK;
-@@ -1355,8 +1914,8 @@ out:
- }
-
- static krb5_error_code
--krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
-- krb5_principal * princ, krb5_krcc_bc * bc)
-+krb5_krcc_parse_principal(krb5_context context, krb5_principal * princ,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- register krb5_principal tmpprinc;
-@@ -1364,12 +1923,12 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
- int i;
-
- /* Read principal type */
-- kret = krb5_krcc_parse_int32(context, id, &type, bc);
-+ kret = krb5_krcc_parse_int32(context, &type, bc);
- if (kret != KRB5_OK)
- return kret;
-
- /* Read the number of components */
-- kret = krb5_krcc_parse_int32(context, id, &length, bc);
-+ kret = krb5_krcc_parse_int32(context, &length, bc);
- if (kret != KRB5_OK)
- return kret;
-
-@@ -1380,12 +1939,7 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
- 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);
-+ tmpprinc->data = calloc(length, sizeof(krb5_data));
- if (tmpprinc->data == 0) {
- free(tmpprinc);
- return KRB5_CC_NOMEM;
-@@ -1396,15 +1950,12 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
- tmpprinc->length = length;
- tmpprinc->type = type;
-
-- kret = krb5_krcc_parse_krb5data(context, id,
-- krb5_princ_realm(context, tmpprinc), bc);
-+ kret = krb5_krcc_parse_krb5data(context, &tmpprinc->realm, 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);
-+ kret = krb5_krcc_parse_krb5data(context, &tmpprinc->data[i], bc);
- CHECK(kret);
- }
- *princ = tmpprinc;
-@@ -1412,16 +1963,16 @@ krb5_krcc_parse_principal(krb5_context context, krb5_ccache id,
-
- errout:
- while (--i >= 0)
-- free(krb5_princ_component(context, tmpprinc, i)->data);
-- free(krb5_princ_realm(context, tmpprinc)->data);
-+ free(tmpprinc->data[i].data);
-+ free(tmpprinc->realm.data);
- free(tmpprinc->data);
- free(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_krcc_parse_keyblock(krb5_context context, krb5_keyblock * keyblock,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_ui_2 ui2;
-@@ -1430,26 +1981,22 @@ krb5_krcc_parse_keyblock(krb5_context context, krb5_ccache id,
- keyblock->magic = KV5M_KEYBLOCK;
- keyblock->contents = 0;
-
-- kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
-+ kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
- CHECK(kret);
- keyblock->enctype = ui2;
-
-- kret = krb5_krcc_parse_int32(context, id, &int32, bc);
-+ kret = krb5_krcc_parse_int32(context, &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);
-+ keyblock->contents = malloc(keyblock->length);
- if (keyblock->contents == NULL)
- return KRB5_CC_NOMEM;
-
-- kret = krb5_krcc_parse(context, id, keyblock->contents,
-- keyblock->length, bc);
-+ kret = krb5_krcc_parse(context, keyblock->contents, keyblock->length, bc);
- CHECK(kret);
-
- return KRB5_OK;
-@@ -1460,25 +2007,25 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_times(krb5_context context, krb5_ccache id,
-- krb5_ticket_times * t, krb5_krcc_bc * bc)
-+krb5_krcc_parse_times(krb5_context context, krb5_ticket_times * t,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_int32 i;
-
-- kret = krb5_krcc_parse_int32(context, id, &i, bc);
-+ kret = krb5_krcc_parse_int32(context, &i, bc);
- CHECK(kret);
- t->authtime = i;
-
-- kret = krb5_krcc_parse_int32(context, id, &i, bc);
-+ kret = krb5_krcc_parse_int32(context, &i, bc);
- CHECK(kret);
- t->starttime = i;
-
-- kret = krb5_krcc_parse_int32(context, id, &i, bc);
-+ kret = krb5_krcc_parse_int32(context, &i, bc);
- CHECK(kret);
- t->endtime = i;
-
-- kret = krb5_krcc_parse_int32(context, id, &i, bc);
-+ kret = krb5_krcc_parse_int32(context, &i, bc);
- CHECK(kret);
- t->renew_till = i;
-
-@@ -1488,8 +2035,8 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
-- krb5_data * data, krb5_krcc_bc * bc)
-+krb5_krcc_parse_krb5data(krb5_context context, krb5_data * data,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_int32 len;
-@@ -1497,12 +2044,12 @@ krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
- data->magic = KV5M_DATA;
- data->data = 0;
-
-- kret = krb5_krcc_parse_int32(context, id, &len, bc);
-+ kret = krb5_krcc_parse_int32(context, &len, bc);
- CHECK(kret);
- if (len < 0)
- return KRB5_CC_NOMEM;
- data->length = len;
-- if (data->length != len || data->length + 1 == 0)
-+ if (data->length + 1 == 0)
- return KRB5_CC_NOMEM;
-
- if (data->length == 0) {
-@@ -1514,8 +2061,7 @@ krb5_krcc_parse_krb5data(krb5_context context, krb5_ccache id,
- if (data->data == NULL)
- return KRB5_CC_NOMEM;
-
-- kret = krb5_krcc_parse(context, id, data->data, (unsigned) data->length,
-- bc);
-+ kret = krb5_krcc_parse(context, data->data, (unsigned) data->length, bc);
- CHECK(kret);
-
- data->data[data->length] = 0; /* Null terminate, just in case.... */
-@@ -1527,13 +2073,12 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_int32(krb5_context context, krb5_ccache id, krb5_int32 * i,
-- krb5_krcc_bc * bc)
-+krb5_krcc_parse_int32(krb5_context context, krb5_int32 * i, krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- unsigned char buf[4];
-
-- kret = krb5_krcc_parse(context, id, buf, 4, bc);
-+ kret = krb5_krcc_parse(context, buf, 4, bc);
- if (kret)
- return kret;
- *i = load_32_be(buf);
-@@ -1541,15 +2086,14 @@ krb5_krcc_parse_int32(krb5_context context, krb5_ccache id, krb5_int32 * i,
- }
-
- static krb5_error_code
--krb5_krcc_parse_octet(krb5_context context, krb5_ccache id, krb5_octet * i,
-- krb5_krcc_bc * bc)
-+krb5_krcc_parse_octet(krb5_context context, krb5_octet * i, krb5_krcc_bc * bc)
- {
-- return krb5_krcc_parse(context, id, (krb5_pointer) i, 1, bc);
-+ return krb5_krcc_parse(context, (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_krcc_parse_addrs(krb5_context context, krb5_address *** addrs,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_int32 length;
-@@ -1559,18 +2103,17 @@ krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
- *addrs = 0;
-
- /* Read the number of components */
-- kret = krb5_krcc_parse_int32(context, id, &length, bc);
-+ kret = krb5_krcc_parse_int32(context, &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)
-+ msize = (size_t)length + 1;
-+ if (msize == 0 || length < 0)
- return KRB5_CC_NOMEM;
-- *addrs = ALLOC(msize, krb5_address *);
-+ *addrs = calloc(msize, sizeof(krb5_address *));
- if (*addrs == NULL)
- return KRB5_CC_NOMEM;
-
-@@ -1580,7 +2123,7 @@ krb5_krcc_parse_addrs(krb5_context context, krb5_ccache id,
- krb5_free_addresses(context, *addrs);
- return KRB5_CC_NOMEM;
- }
-- kret = krb5_krcc_parse_addr(context, id, (*addrs)[i], bc);
-+ kret = krb5_krcc_parse_addr(context, (*addrs)[i], bc);
- CHECK(kret);
- }
-
-@@ -1592,7 +2135,7 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
-+krb5_krcc_parse_addr(krb5_context context, krb5_address * addr,
- krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-@@ -1602,22 +2145,15 @@ krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
- addr->magic = KV5M_ADDRESS;
- addr->contents = 0;
-
-- kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
-+ kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
- CHECK(kret);
- addr->addrtype = ui2;
-
-- kret = krb5_krcc_parse_int32(context, id, &int32, bc);
-+ kret = krb5_krcc_parse_int32(context, &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;
-
-@@ -1625,7 +2161,7 @@ krb5_krcc_parse_addr(krb5_context context, krb5_ccache id, krb5_address * addr,
- if (addr->contents == NULL)
- return KRB5_CC_NOMEM;
-
-- kret = krb5_krcc_parse(context, id, addr->contents, addr->length, bc);
-+ kret = krb5_krcc_parse(context, addr->contents, addr->length, bc);
- CHECK(kret);
-
- return KRB5_OK;
-@@ -1636,8 +2172,8 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
-- krb5_authdata *** a, krb5_krcc_bc * bc)
-+krb5_krcc_parse_authdata(krb5_context context, krb5_authdata *** a,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_int32 length;
-@@ -1647,7 +2183,7 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
- *a = 0;
-
- /* Read the number of components */
-- kret = krb5_krcc_parse_int32(context, id, &length, bc);
-+ kret = krb5_krcc_parse_int32(context, &length, bc);
- CHECK(kret);
-
- if (length == 0)
-@@ -1657,11 +2193,10 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
- * 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)
-+ msize = (size_t)length + 1;
-+ if (msize == 0 || length < 0)
- return KRB5_CC_NOMEM;
-- *a = ALLOC(msize, krb5_authdata *);
-+ *a = calloc(msize, sizeof(krb5_authdata *));
- if (*a == NULL)
- return KRB5_CC_NOMEM;
-
-@@ -1672,7 +2207,7 @@ krb5_krcc_parse_authdata(krb5_context context, krb5_ccache id,
- *a = NULL;
- return KRB5_CC_NOMEM;
- }
-- kret = krb5_krcc_parse_authdatum(context, id, (*a)[i], bc);
-+ kret = krb5_krcc_parse_authdatum(context, (*a)[i], bc);
- CHECK(kret);
- }
-
-@@ -1686,8 +2221,8 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
-- krb5_authdata * a, krb5_krcc_bc * bc)
-+krb5_krcc_parse_authdatum(krb5_context context, krb5_authdata * a,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_int32 int32;
-@@ -1696,21 +2231,14 @@ krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
- a->magic = KV5M_AUTHDATA;
- a->contents = NULL;
-
-- kret = krb5_krcc_parse_ui_2(context, id, &ui2, bc);
-+ kret = krb5_krcc_parse_ui_2(context, &ui2, bc);
- CHECK(kret);
- a->ad_type = (krb5_authdatatype) ui2;
-- kret = krb5_krcc_parse_int32(context, id, &int32, bc);
-+ kret = krb5_krcc_parse_int32(context, &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;
-
-@@ -1718,7 +2246,7 @@ krb5_krcc_parse_authdatum(krb5_context context, krb5_ccache id,
- if (a->contents == NULL)
- return KRB5_CC_NOMEM;
-
-- kret = krb5_krcc_parse(context, id, a->contents, a->length, bc);
-+ kret = krb5_krcc_parse(context, a->contents, a->length, bc);
- CHECK(kret);
-
- return KRB5_OK;
-@@ -1730,13 +2258,12 @@ errout:
- }
-
- static krb5_error_code
--krb5_krcc_parse_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 * i,
-- krb5_krcc_bc * bc)
-+krb5_krcc_parse_ui_2(krb5_context context, 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);
-+ kret = krb5_krcc_parse(context, buf, 2, bc);
- if (kret)
- return kret;
- *i = load_16_be(buf);
-@@ -1756,9 +2283,15 @@ krb5_krcc_parse_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 * i,
- * 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)
-+krb5_krcc_unparse(krb5_context context, krb5_pointer buf, unsigned int len,
-+ krb5_krcc_bc * bc)
- {
-+ if (bc->bpp == NULL) {
-+ /* This is a dry run; just increase size and return. */
-+ bc->size += len;
-+ return KRB5_OK;
-+ }
-+
- if (bc->bpp + len > bc->endp)
- return KRB5_CC_WRITE;
-
-@@ -1769,29 +2302,26 @@ krb5_krcc_unparse(krb5_context context, krb5_ccache id, krb5_pointer buf,
- }
-
- static krb5_error_code
--krb5_krcc_unparse_principal(krb5_context context, krb5_ccache id,
-- krb5_principal princ, krb5_krcc_bc * bc)
-+krb5_krcc_unparse_principal(krb5_context context, 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);
-+ type = princ->type;
-+ tmp = length = princ->length;
-
-- kret = krb5_krcc_unparse_int32(context, id, type, bc);
-+ kret = krb5_krcc_unparse_int32(context, type, bc);
- CHECK_OUT(kret);
-
-- kret = krb5_krcc_unparse_int32(context, id, tmp, bc);
-+ kret = krb5_krcc_unparse_int32(context, tmp, bc);
- CHECK_OUT(kret);
-
-- kret = krb5_krcc_unparse_krb5data(context, id,
-- krb5_princ_realm(context, princ), bc);
-+ kret = krb5_krcc_unparse_krb5data(context, &princ->realm, bc);
- CHECK_OUT(kret);
-
- for (i = 0; i < length; i++) {
-- kret = krb5_krcc_unparse_krb5data(context, id,
-- krb5_princ_component(context, princ,
-- i), bc);
-+ kret = krb5_krcc_unparse_krb5data(context, &princ->data[i], bc);
- CHECK_OUT(kret);
- }
-
-@@ -1799,67 +2329,65 @@ krb5_krcc_unparse_principal(krb5_context context, krb5_ccache id,
- }
-
- static krb5_error_code
--krb5_krcc_unparse_keyblock(krb5_context context, krb5_ccache id,
-- krb5_keyblock * keyblock, krb5_krcc_bc * bc)
-+krb5_krcc_unparse_keyblock(krb5_context context, krb5_keyblock * keyblock,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-
-- kret = krb5_krcc_unparse_ui_2(context, id, keyblock->enctype, bc);
-+ kret = krb5_krcc_unparse_ui_2(context, keyblock->enctype, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_ui_4(context, id, keyblock->length, bc);
-+ kret = krb5_krcc_unparse_ui_4(context, keyblock->length, bc);
- CHECK_OUT(kret);
-- return krb5_krcc_unparse(context, id, (char *) keyblock->contents,
-+ return krb5_krcc_unparse(context, (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_krcc_unparse_times(krb5_context context, krb5_ticket_times * t,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-
-- kret = krb5_krcc_unparse_int32(context, id, t->authtime, bc);
-+ kret = krb5_krcc_unparse_int32(context, t->authtime, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_int32(context, id, t->starttime, bc);
-+ kret = krb5_krcc_unparse_int32(context, t->starttime, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_int32(context, id, t->endtime, bc);
-+ kret = krb5_krcc_unparse_int32(context, t->endtime, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_int32(context, id, t->renew_till, bc);
-+ kret = krb5_krcc_unparse_int32(context, 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_krcc_unparse_krb5data(krb5_context context, krb5_data * data,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-
-- kret = krb5_krcc_unparse_ui_4(context, id, data->length, bc);
-+ kret = krb5_krcc_unparse_ui_4(context, data->length, bc);
- CHECK_OUT(kret);
-- return krb5_krcc_unparse(context, id, data->data, data->length, bc);
-+ return krb5_krcc_unparse(context, 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)
-+krb5_krcc_unparse_int32(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
- {
-- return krb5_krcc_unparse_ui_4(context, id, (krb5_ui_4) i, bc);
-+ return krb5_krcc_unparse_ui_4(context, (krb5_ui_4) i, bc);
- }
-
- static krb5_error_code
--krb5_krcc_unparse_octet(krb5_context context, krb5_ccache id, krb5_int32 i,
-- krb5_krcc_bc * bc)
-+krb5_krcc_unparse_octet(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
- {
- krb5_octet ibuf;
-
- ibuf = (krb5_octet) i;
-- return krb5_krcc_unparse(context, id, (char *) &ibuf, 1, bc);
-+ return krb5_krcc_unparse(context, (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_krcc_unparse_addrs(krb5_context context, krb5_address ** addrs,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
- krb5_address **temp;
-@@ -1872,10 +2400,10 @@ krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
- length += 1;
- }
-
-- kret = krb5_krcc_unparse_int32(context, id, length, bc);
-+ kret = krb5_krcc_unparse_int32(context, length, bc);
- CHECK_OUT(kret);
- for (i = 0; i < length; i++) {
-- kret = krb5_krcc_unparse_addr(context, id, addrs[i], bc);
-+ kret = krb5_krcc_unparse_addr(context, addrs[i], bc);
- CHECK_OUT(kret);
- }
-
-@@ -1883,21 +2411,21 @@ krb5_krcc_unparse_addrs(krb5_context context, krb5_ccache id,
- }
-
- static krb5_error_code
--krb5_krcc_unparse_addr(krb5_context context, krb5_ccache id,
-- krb5_address * addr, krb5_krcc_bc * bc)
-+krb5_krcc_unparse_addr(krb5_context context, krb5_address * addr,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-
-- kret = krb5_krcc_unparse_ui_2(context, id, addr->addrtype, bc);
-+ kret = krb5_krcc_unparse_ui_2(context, addr->addrtype, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_ui_4(context, id, addr->length, bc);
-+ kret = krb5_krcc_unparse_ui_4(context, addr->length, bc);
- CHECK_OUT(kret);
-- return krb5_krcc_unparse(context, id, (char *) addr->contents,
-+ return krb5_krcc_unparse(context, (char *) addr->contents,
- addr->length, bc);
- }
-
- static krb5_error_code
--krb5_krcc_unparse_authdata(krb5_context context, krb5_ccache id,
-+krb5_krcc_unparse_authdata(krb5_context context,
- krb5_authdata ** a, krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-@@ -1909,47 +2437,45 @@ krb5_krcc_unparse_authdata(krb5_context context, krb5_ccache id,
- length++;
- }
-
-- kret = krb5_krcc_unparse_int32(context, id, length, bc);
-+ kret = krb5_krcc_unparse_int32(context, length, bc);
- CHECK_OUT(kret);
- for (i = 0; i < length; i++) {
-- kret = krb5_krcc_unparse_authdatum(context, id, a[i], bc);
-+ kret = krb5_krcc_unparse_authdatum(context, 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_krcc_unparse_authdatum(krb5_context context, krb5_authdata * a,
-+ krb5_krcc_bc * bc)
- {
- krb5_error_code kret;
-
-- kret = krb5_krcc_unparse_ui_2(context, id, a->ad_type, bc);
-+ kret = krb5_krcc_unparse_ui_2(context, a->ad_type, bc);
- CHECK_OUT(kret);
-- kret = krb5_krcc_unparse_ui_4(context, id, a->length, bc);
-+ kret = krb5_krcc_unparse_ui_4(context, a->length, bc);
- CHECK_OUT(kret);
-- return krb5_krcc_unparse(context, id, (krb5_pointer) a->contents,
-+ return krb5_krcc_unparse(context, (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)
-+krb5_krcc_unparse_ui_4(krb5_context context, krb5_ui_4 i, krb5_krcc_bc * bc)
- {
- unsigned char buf[4];
-
- store_32_be(i, buf);
-- return krb5_krcc_unparse(context, id, buf, 4, bc);
-+ return krb5_krcc_unparse(context, 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)
-+krb5_krcc_unparse_ui_2(krb5_context context, krb5_int32 i, krb5_krcc_bc * bc)
- {
- unsigned char buf[2];
-
- store_16_be(i, buf);
-- return krb5_krcc_unparse(context, id, buf, 2, bc);
-+ return krb5_krcc_unparse(context, buf, 2, bc);
- }
-
- /*
-@@ -1965,11 +2491,55 @@ krb5_krcc_unparse_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i,
- * 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_krcc_unparse_cred(krb5_context context, krb5_creds * creds,
-+ krb5_krcc_bc *bc)
- {
- krb5_error_code kret;
-- char *buf;
-+
-+ kret = krb5_krcc_unparse_principal(context, creds->client, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_principal(context, creds->server, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_keyblock(context, &creds->keyblock, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_times(context, &creds->times, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_octet(context, (krb5_int32) creds->is_skey, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_int32(context, creds->ticket_flags, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_addrs(context, creds->addresses, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_authdata(context, creds->authdata, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_krb5data(context, &creds->ticket, bc);
-+ CHECK_OUT(kret);
-+ CHECK(kret);
-+
-+ kret = krb5_krcc_unparse_krb5data(context, &creds->second_ticket, bc);
-+ CHECK_OUT(kret);
-+
-+ /* Success! */
-+ kret = KRB5_OK;
-+
-+errout:
-+ return kret;
-+}
-+
-+static krb5_error_code
-+krb5_krcc_unparse_cred_alloc(krb5_context context, krb5_creds * creds,
-+ char **datapp, unsigned int *lenptr)
-+{
-+ krb5_error_code kret;
-+ char *buf = NULL;
- krb5_krcc_bc bc;
-
- if (!creds || !datapp || !lenptr)
-@@ -1978,43 +2548,102 @@ krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
- *datapp = NULL;
- *lenptr = 0;
-
-- buf = malloc(GUESS_CRED_SIZE);
-+ /* Do a dry run first to calculate the size. */
-+ bc.bpp = bc.endp = NULL;
-+ bc.size = 0;
-+ kret = krb5_krcc_unparse_cred(context, creds, &bc);
-+ CHECK(kret);
-+ if (bc.size > MAX_CRED_SIZE)
-+ return KRB5_CC_WRITE;
-+
-+ /* Allocate a buffer and unparse for real. */
-+ buf = malloc(bc.size);
- if (buf == NULL)
- return KRB5_CC_NOMEM;
--
- bc.bpp = buf;
-- bc.endp = buf + GUESS_CRED_SIZE;
-+ bc.endp = buf + bc.size;
-+ kret = krb5_krcc_unparse_cred(context, creds, &bc);
-+ CHECK(kret);
-
-- kret = krb5_krcc_unparse_principal(context, id, creds->client, &bc);
-- CHECK_N_GO(kret, errout);
-+ /* Success! */
-+ *datapp = buf;
-+ *lenptr = bc.bpp - buf;
-+ buf = NULL;
-+ kret = KRB5_OK;
-
-- kret = krb5_krcc_unparse_principal(context, id, creds->server, &bc);
-- CHECK_N_GO(kret, errout);
-+errout:
-+ free(buf);
-+ return kret;
-+}
-
-- kret = krb5_krcc_unparse_keyblock(context, id, &creds->keyblock, &bc);
-- CHECK_N_GO(kret, errout);
-+static krb5_error_code
-+krb5_krcc_parse_index(krb5_context context, krb5_int32 *version,
-+ char **primary, void *payload, int psize)
-+{
-+ krb5_error_code kret;
-+ krb5_krcc_bc bc;
-+ krb5_data data;
-
-- kret = krb5_krcc_unparse_times(context, id, &creds->times, &bc);
-- CHECK_N_GO(kret, errout);
-+ bc.bpp = payload;
-+ bc.endp = bc.bpp + psize;
-
-- kret = krb5_krcc_unparse_octet(context, id, (krb5_int32) creds->is_skey,
-- &bc);
-- CHECK_N_GO(kret, errout);
-+ kret = krb5_krcc_parse_int32(context, version, &bc);
-+ CHECK_OUT(kret);
-
-- kret = krb5_krcc_unparse_int32(context, id, creds->ticket_flags, &bc);
-- CHECK_N_GO(kret, errout);
-+ kret = krb5_krcc_parse_krb5data(context, &data, &bc);
-+ CHECK_OUT(kret);
-
-- kret = krb5_krcc_unparse_addrs(context, id, creds->addresses, &bc);
-- CHECK_N_GO(kret, errout);
-+ *primary = (char *)data.data;
-+ return KRB5_OK;
-+}
-
-- kret = krb5_krcc_unparse_authdata(context, id, creds->authdata, &bc);
-- CHECK_N_GO(kret, errout);
-+static krb5_error_code
-+krb5_krcc_unparse_index_internal(krb5_context context, krb5_int32 version,
-+ const char *primary, krb5_krcc_bc *bc)
-+{
-+ krb5_error_code kret;
-+ krb5_data data;
-
-- kret = krb5_krcc_unparse_krb5data(context, id, &creds->ticket, &bc);
-- CHECK_N_GO(kret, errout);
-+ data.length = strlen(primary) + 1;
-+ data.data = (void *)primary;
-
-- kret = krb5_krcc_unparse_krb5data(context, id, &creds->second_ticket, &bc);
-- CHECK_N_GO(kret, errout);
-+ kret = krb5_krcc_unparse_int32(context, version, bc);
-+ CHECK_OUT(kret);
-+
-+ kret = krb5_krcc_unparse_krb5data(context, &data, bc);
-+ CHECK_OUT(kret);
-+
-+ return KRB5_OK;
-+}
-+
-+static krb5_error_code
-+krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
-+ const char *primary, void **datapp, int *lenptr)
-+{
-+ krb5_error_code kret;
-+ krb5_krcc_bc bc;
-+ char *buf;
-+
-+ if (!primary || !datapp || !lenptr)
-+ return EINVAL;
-+
-+ *datapp = NULL;
-+ *lenptr = 0;
-+
-+ /* Do a dry run first to calculate the size. */
-+ bc.bpp = bc.endp = NULL;
-+ bc.size = 0;
-+ kret = krb5_krcc_unparse_index_internal(context, version, primary, &bc);
-+ CHECK_OUT(kret);
-+
-+ buf = malloc(bc.size);
-+ if (buf == NULL)
-+ return ENOMEM;
-+
-+ bc.bpp = buf;
-+ bc.endp = buf + bc.size;
-+ kret = krb5_krcc_unparse_index_internal(context, version, primary, &bc);
-+ CHECK(kret);
-
- /* Success! */
- *datapp = buf;
-@@ -2022,6 +2651,8 @@ krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id,
- kret = KRB5_OK;
-
- errout:
-+ if (kret)
-+ free(buf);
- return kret;
- }
-
-@@ -2065,15 +2696,15 @@ const krb5_cc_ops krb5_krcc_ops = {
- krb5_krcc_remove_cred,
- krb5_krcc_set_flags,
- krb5_krcc_get_flags, /* added after 1.4 release */
-- NULL,
-- NULL,
-- NULL,
-+ krb5_krcc_ptcursor_new,
-+ krb5_krcc_ptcursor_next,
-+ krb5_krcc_ptcursor_free,
- NULL, /* move */
- krb5_krcc_last_change_time, /* lastchange */
- NULL, /* wasdefault */
- krb5_krcc_lock,
- krb5_krcc_unlock,
-- NULL, /* switch_to */
-+ krb5_krcc_switch_to,
- };
-
- #else /* !USE_KEYRING_CCACHE */
-diff --git a/src/lib/krb5/ccache/t_cc.c b/src/lib/krb5/ccache/t_cc.c
-index e14ae7f..6069cab 100644
---- a/src/lib/krb5/ccache/t_cc.c
-+++ b/src/lib/krb5/ccache/t_cc.c
-@@ -25,6 +25,7 @@
- */
-
- #include "k5-int.h"
-+#include "cc-int.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include "autoconf.h"
-@@ -331,14 +332,14 @@ check_registered(krb5_context context, const char *prefix)
- if(kret != KRB5_OK) {
- if(kret == KRB5_CC_UNKNOWN_TYPE)
- return 0;
-- com_err("Checking on credential type", kret,prefix);
-+ com_err("Checking on credential type", kret, "%s", prefix);
- fflush(stderr);
- return 0;
- }
-
- kret = krb5_cc_close(context, id);
- if(kret != KRB5_OK) {
-- com_err("Checking on credential type - closing", kret,prefix);
-+ com_err("Checking on credential type - closing", kret, "%s", prefix);
- fflush(stderr);
- }
-
-@@ -425,8 +426,8 @@ main(void)
- test_misc(context);
- do_test(context, "");
-
-- if(check_registered(context, "KEYRING:"))
-- do_test(context, "KEYRING:");
-+ if (check_registered(context, "KEYRING:process:"))
-+ do_test(context, "KEYRING:process:");
- else
- printf("Skiping KEYRING: test - unregistered type\n");
-
-diff --git a/src/lib/krb5/ccache/t_cccol.c b/src/lib/krb5/ccache/t_cccol.c
-new file mode 100644
-index 0000000..444806e
---- /dev/null
-+++ b/src/lib/krb5/ccache/t_cccol.c
-@@ -0,0 +1,363 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krb5/ccache/t_cccol.py - Test ccache collection via API */
-+/*
-+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
-+ * All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions
-+ * are met:
-+ *
-+ * * Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * * Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-+ * OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <krb5.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <assert.h>
-+
-+static krb5_context ctx;
-+
-+/* Check that code is 0. Display an error message first if it is not. */
-+static void
-+check(krb5_error_code code)
-+{
-+ const char *errmsg;
-+
-+ if (code != 0) {
-+ errmsg = krb5_get_error_message(ctx, code);
-+ fprintf(stderr, "%s\n", errmsg);
-+ krb5_free_error_message(ctx, errmsg);
-+ }
-+ assert(code == 0);
-+}
-+
-+/* Construct a list of the names of each credential cache in the collection. */
-+static void
-+get_collection_names(char ***list_out, size_t *count_out)
-+{
-+ krb5_cccol_cursor cursor;
-+ krb5_ccache cache;
-+ char **list = NULL;
-+ size_t count = 0;
-+ char *name;
-+
-+ check(krb5_cccol_cursor_new(ctx, &cursor));
-+ while (1) {
-+ check(krb5_cccol_cursor_next(ctx, cursor, &cache));
-+ if (cache == NULL)
-+ break;
-+ check(krb5_cc_get_full_name(ctx, cache, &name));
-+ krb5_cc_close(ctx, cache);
-+ list = realloc(list, (count + 1) * sizeof(*list));
-+ assert(list != NULL);
-+ list[count++] = name;
-+ }
-+ krb5_cccol_cursor_free(ctx, &cursor);
-+ *list_out = list;
-+ *count_out = count;
-+}
-+
-+/* Return true if list contains name. */
-+static krb5_boolean
-+in_list(char **list, size_t count, const char *name)
-+{
-+ size_t i;
-+
-+ for (i = 0; i < count; i++) {
-+ if (strcmp(list[i], name) == 0)
-+ return TRUE;
-+ }
-+ return FALSE;
-+}
-+
-+/* Release the memory for a list of credential cache names. */
-+static void
-+free_list(char **list, size_t count)
-+{
-+ size_t i;
-+
-+ for (i = 0; i < count; i++)
-+ krb5_free_string(ctx, list[i]);
-+ free(list);
-+}
-+
-+/*
-+ * Check that the cache names within the current collection begin with first
-+ * (unless first is NULL), that the other elements match the remaining
-+ * arguments in some order. others must be the number of additional cache
-+ * names.
-+ */
-+static void
-+check_collection(const char *first, size_t others, ...)
-+{
-+ va_list ap;
-+ char **list;
-+ size_t count, i;
-+ const char *name;
-+
-+ get_collection_names(&list, &count);
-+ if (first != NULL) {
-+ assert(strcmp(first, list[0]) == 0);
-+ assert(count == others + 1);
-+ } else {
-+ assert(count == others);
-+ }
-+ va_start(ap, others);
-+ for (i = 0; i < others; i++) {
-+ name = va_arg(ap, const char *);
-+ assert(in_list(list, count, name));
-+ }
-+ va_end(ap);
-+ free_list(list, count);
-+}
-+
-+/* Check that the name of cache matches expected_name. */
-+static void
-+check_name(krb5_ccache cache, const char *expected_name)
-+{
-+ char *name;
-+
-+ check(krb5_cc_get_full_name(ctx, cache, &name));
-+ assert(strcmp(name, expected_name) == 0);
-+ krb5_free_string(ctx, name);
-+}
-+
-+/* Check that when collection_name is resolved, the resulting cache's name
-+ * matches expected_name. */
-+static void
-+check_primary_name(const char *collection_name, const char *expected_name)
-+{
-+ krb5_ccache cache;
-+
-+ check(krb5_cc_resolve(ctx, collection_name, &cache));
-+ check_name(cache, expected_name);
-+ krb5_cc_close(ctx, cache);
-+}
-+
-+/* Check that when name is resolved, the resulting cache's principal matches
-+ * expected_princ, or has no principal if expected_princ is NULL. */
-+static void
-+check_princ(const char *name, krb5_principal expected_princ)
-+{
-+ krb5_ccache cache;
-+ krb5_principal princ;
-+
-+ check(krb5_cc_resolve(ctx, name, &cache));
-+ if (expected_princ != NULL) {
-+ check(krb5_cc_get_principal(ctx, cache, &princ));
-+ assert(krb5_principal_compare(ctx, princ, expected_princ));
-+ krb5_free_principal(ctx, princ);
-+ } else {
-+ assert(krb5_cc_get_principal(ctx, cache, &princ) != 0);
-+ }
-+ krb5_cc_close(ctx, cache);
-+}
-+
-+/* Check that krb5_cc_cache_match on princ returns a cache whose name matches
-+ * expected_name, or that the match fails if expected_name is NULL. */
-+static void
-+check_match(krb5_principal princ, const char *expected_name)
-+{
-+ krb5_ccache cache;
-+
-+ if (expected_name != NULL) {
-+ check(krb5_cc_cache_match(ctx, princ, &cache));
-+ check_name(cache, expected_name);
-+ krb5_cc_close(ctx, cache);
-+ } else {
-+ assert(krb5_cc_cache_match(ctx, princ, &cache) != 0);
-+ }
-+}
-+
-+int
-+main(int argc, char **argv)
-+{
-+ krb5_ccache ccinitial, ccu1, ccu2;
-+ krb5_principal princ1, princ2, princ3;
-+ const char *collection_name, *typename;
-+ char *initial_primary_name, *unique1_name, *unique2_name;
-+
-+ /*
-+ * Get the collection name from the command line. This is a ccache name
-+ * with collection semantics, like DIR:/path/to/directory. This test
-+ * program assumes that the collection is empty to start with.
-+ */
-+ assert(argc == 2);
-+ collection_name = argv[1];
-+
-+ /*
-+ * Set the default ccache for the context to be the collection name, so the
-+ * library can find the collection.
-+ */
-+ check(krb5_init_context(&ctx));
-+ check(krb5_cc_set_default_name(ctx, collection_name));
-+
-+ /*
-+ * Resolve the collection name. Since the collection is empty, this should
-+ * generate a subsidiary name of an uninitialized cache. Getting the name
-+ * of the resulting cache should give us the subsidiary name, not the
-+ * collection name. This resulting subsidiary name should be consistent if
-+ * we resolve the collection name again, and the collection should still be
-+ * empty since we haven't initialized the cache.
-+ */
-+ check(krb5_cc_resolve(ctx, collection_name, &ccinitial));
-+ check(krb5_cc_get_full_name(ctx, ccinitial, &initial_primary_name));
-+ assert(strcmp(initial_primary_name, collection_name) != 0);
-+ check_primary_name(collection_name, initial_primary_name);
-+ check_collection(NULL, 0);
-+ check_princ(collection_name, NULL);
-+ check_princ(initial_primary_name, NULL);
-+
-+ /*
-+ * Before initializing the primary ccache, generate and initialize two
-+ * unique caches of the collection's type. Check that the cache names
-+ * resolve to the generated caches and appear in the collection. (They
-+ * might appear before being initialized; that's not currently considered
-+ * important). The primary cache for the collection should remain as the
-+ * unitialized cache from the previous step.
-+ */
-+ typename = krb5_cc_get_type(ctx, ccinitial);
-+ check(krb5_cc_new_unique(ctx, typename, NULL, &ccu1));
-+ check(krb5_cc_get_full_name(ctx, ccu1, &unique1_name));
-+ check(krb5_parse_name(ctx, "princ1@X", &princ1));
-+ check(krb5_cc_initialize(ctx, ccu1, princ1));
-+ check_princ(unique1_name, princ1);
-+ check_match(princ1, unique1_name);
-+ check_collection(NULL, 1, unique1_name);
-+ check(krb5_cc_new_unique(ctx, typename, NULL, &ccu2));
-+ check(krb5_cc_get_full_name(ctx, ccu2, &unique2_name));
-+ check(krb5_parse_name(ctx, "princ2@X", &princ2));
-+ check(krb5_cc_initialize(ctx, ccu2, princ2));
-+ check_princ(unique2_name, princ2);
-+ check_match(princ1, unique1_name);
-+ check_match(princ2, unique2_name);
-+ check_collection(NULL, 2, unique1_name, unique2_name);
-+ assert(strcmp(unique1_name, initial_primary_name) != 0);
-+ assert(strcmp(unique1_name, collection_name) != 0);
-+ assert(strcmp(unique2_name, initial_primary_name) != 0);
-+ assert(strcmp(unique2_name, collection_name) != 0);
-+ assert(strcmp(unique2_name, unique1_name) != 0);
-+ check_primary_name(collection_name, initial_primary_name);
-+
-+ /*
-+ * Initialize the initial primary cache. Make sure it didn't change names,
-+ * that the previously retrieved name and the collection name both resolve
-+ * to the initialized cache, and that it now appears first in the
-+ * collection.
-+ */
-+ check(krb5_parse_name(ctx, "princ3@X", &princ3));
-+ check(krb5_cc_initialize(ctx, ccinitial, princ3));
-+ check_name(ccinitial, initial_primary_name);
-+ check_princ(initial_primary_name, princ3);
-+ check_princ(collection_name, princ3);
-+ check_match(princ3, initial_primary_name);
-+ check_collection(initial_primary_name, 2, unique1_name, unique2_name);
-+
-+ /*
-+ * Switch the primary cache to each cache we have open. One each switch,
-+ * check the primary name, check that the collection resolves to the
-+ * expected cache, and check that the new primary name appears first in the
-+ * collection.
-+ */
-+ check(krb5_cc_switch(ctx, ccu1));
-+ check_primary_name(collection_name, unique1_name);
-+ check_princ(collection_name, princ1);
-+ check_collection(unique1_name, 2, initial_primary_name, unique2_name);
-+ check(krb5_cc_switch(ctx, ccu2));
-+ check_primary_name(collection_name, unique2_name);
-+ check_princ(collection_name, princ2);
-+ check_collection(unique2_name, 2, initial_primary_name, unique1_name);
-+ check(krb5_cc_switch(ctx, ccinitial));
-+ check_primary_name(collection_name, initial_primary_name);
-+ check_princ(collection_name, princ3);
-+ check_collection(initial_primary_name, 2, unique1_name, unique2_name);
-+
-+ /*
-+ * Temporarily set the context default ccache to a subsidiary name, and
-+ * check that iterating over the collection yields that subsidiary cache
-+ * and no others.
-+ */
-+ check(krb5_cc_set_default_name(ctx, unique1_name));
-+ check_collection(unique1_name, 0);
-+ check(krb5_cc_set_default_name(ctx, collection_name));
-+
-+ /*
-+ * Destroy the primary cache. Make sure this causes both the initial
-+ * primary name and the collection name to resolve to an uninitialized
-+ * cache. Make sure the primary name doesn't change and doesn't appear in
-+ * the collection any more.
-+ */
-+ check(krb5_cc_destroy(ctx, ccinitial));
-+ check_princ(initial_primary_name, NULL);
-+ check_princ(collection_name, NULL);
-+ check_primary_name(collection_name, initial_primary_name);
-+ check_match(princ1, unique1_name);
-+ check_match(princ2, unique2_name);
-+ check_match(princ3, NULL);
-+ check_collection(NULL, 2, unique1_name, unique2_name);
-+
-+ /*
-+ * Switch to the first unique cache after destroying the primary cache.
-+ * Check that the collection name resolves to this cache and that the new
-+ * primary name appears first in the collection.
-+ */
-+ check(krb5_cc_switch(ctx, ccu1));
-+ check_primary_name(collection_name, unique1_name);
-+ check_princ(collection_name, princ1);
-+ check_collection(unique1_name, 1, unique2_name);
-+
-+ /*
-+ * Destroy the second unique cache (which is not the current primary),
-+ * check that it is on longer initialized, and check that it no longer
-+ * appears in the collection. Check that destroying the non-primary cache
-+ * doesn't affect the primary name.
-+ */
-+ check(krb5_cc_destroy(ctx, ccu2));
-+ check_princ(unique2_name, NULL);
-+ check_match(princ2, NULL);
-+ check_collection(unique1_name, 0);
-+ check_primary_name(collection_name, unique1_name);
-+ check_match(princ1, unique1_name);
-+ check_princ(collection_name, princ1);
-+
-+ /*
-+ * Destroy the first unique cache. Check that the collection is empty and
-+ * still has the same primary name.
-+ */
-+ check(krb5_cc_destroy(ctx, ccu1));
-+ check_princ(unique1_name, NULL);
-+ check_princ(collection_name, NULL);
-+ check_primary_name(collection_name, unique1_name);
-+ check_match(princ1, NULL);
-+ check_collection(NULL, 0);
-+
-+ krb5_free_string(ctx, initial_primary_name);
-+ krb5_free_string(ctx, unique1_name);
-+ krb5_free_string(ctx, unique2_name);
-+ krb5_free_principal(ctx, princ1);
-+ krb5_free_principal(ctx, princ2);
-+ krb5_free_principal(ctx, princ3);
-+ krb5_free_context(ctx);
-+ return 0;
-+}
-diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py
-index 8c459dd..e762625 100644
---- a/src/lib/krb5/ccache/t_cccol.py
-+++ b/src/lib/krb5/ccache/t_cccol.py
-@@ -1,6 +1,46 @@
- #!/usr/bin/python
- from k5test import *
-
-+realm = K5Realm(create_kdb=False)
-+
-+keyctl = which('keyctl')
-+out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1)
-+test_keyring = (keyctl is not None and
-+ 'Unknown credential cache type' not in out)
-+
-+# Run the collection test program against each collection-enabled type.
-+realm.run(['./t_cccol', 'DIR:' + os.path.join(realm.testdir, 'cc')])
-+if test_keyring:
-+ # Use the test directory as the collection name to avoid colliding
-+ # with other build trees.
-+ cname = realm.testdir
-+
-+ # Remove any keys left behind by previous failed test runs.
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-+ realm.run(['keyctl', 'purge', 'keyring', cname])
-+ out = realm.run(['keyctl', 'list', '@u'])
-+ if ('keyring: _krb_' + cname + '\n') in out:
-+ id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname])
-+ realm.run(['keyctl', 'unlink', id.strip(), '@u'])
-+
-+ # Run test program over each subtype, cleaning up as we go. Don't
-+ # test the persistent subtype, since it supports only one
-+ # collection and might be in actual use.
-+ realm.run(['./t_cccol', 'KEYRING:' + cname])
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-+ realm.run(['./t_cccol', 'KEYRING:legacy:' + cname])
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-+ realm.run(['./t_cccol', 'KEYRING:session:' + cname])
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-+ realm.run(['./t_cccol', 'KEYRING:user:' + cname])
-+ id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname])
-+ realm.run(['keyctl', 'unlink', id.strip(), '@u'])
-+ realm.run(['./t_cccol', 'KEYRING:process:abcd'])
-+ realm.run(['./t_cccol', 'KEYRING:thread:abcd'])
-+
-+realm.stop()
-+
-+# Test cursor semantics using real ccaches.
- realm = K5Realm(create_host=False)
-
- realm.addprinc('alice', password('alice'))
-@@ -11,12 +51,25 @@ dccname = 'DIR:%s' % ccdir
- duser = 'DIR::%s/tkt1' % ccdir
- dalice = 'DIR::%s/tkt2' % ccdir
- dbob = 'DIR::%s/tkt3' % ccdir
-+dnoent = 'DIR::%s/noent' % ccdir
- realm.kinit('user', password('user'), flags=['-c', duser])
- realm.kinit('alice', password('alice'), flags=['-c', dalice])
- realm.kinit('bob', password('bob'), flags=['-c', dbob])
-
-+if test_keyring:
-+ cname = realm.testdir
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-+ krccname = 'KEYRING:session:' + cname
-+ kruser = '%s:tkt1' % krccname
-+ kralice = '%s:tkt2' % krccname
-+ krbob = '%s:tkt3' % krccname
-+ krnoent = '%s:noent' % krccname
-+ realm.kinit('user', password('user'), flags=['-c', kruser])
-+ realm.kinit('alice', password('alice'), flags=['-c', kralice])
-+ realm.kinit('bob', password('bob'), flags=['-c', krbob])
-+
- def cursor_test(testname, args, expected):
-- outlines = realm.run_as_client(['./t_cccursor'] + args).splitlines()
-+ outlines = realm.run(['./t_cccursor'] + args).splitlines()
- outlines.sort()
- expected.sort()
- if outlines != expected:
-@@ -30,21 +83,33 @@ cursor_test('file-default2', [realm.ccache], [fccname])
- cursor_test('file-default3', [fccname], [fccname])
-
- cursor_test('dir', [dccname], [duser, dalice, dbob])
-+cursor_test('dir-subsidiary', [duser], [duser])
-+cursor_test('dir-nofile', [dnoent], [])
-+
-+if test_keyring:
-+ cursor_test('keyring', [krccname], [kruser, kralice, krbob])
-+ cursor_test('keyring-subsidiary', [kruser], [kruser])
-+ cursor_test('keyring-noent', [krnoent], [])
-
- mfoo = 'MEMORY:foo'
- mbar = 'MEMORY:bar'
- cursor_test('filemem', [fccname, mfoo, mbar], [fccname, mfoo, mbar])
- cursor_test('dirmem', [dccname, mfoo], [duser, dalice, dbob, mfoo])
-+if test_keyring:
-+ cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob, mfoo])
-
- # Test krb5_cccol_have_content.
--realm.run_as_client(['./t_cccursor', dccname, 'CONTENT'])
--realm.run_as_client(['./t_cccursor', fccname, 'CONTENT'])
--realm.run_as_client(['./t_cccursor', realm.ccache, 'CONTENT'])
--realm.run_as_client(['./t_cccursor', mfoo, 'CONTENT'], expected_code=1)
-+realm.run(['./t_cccursor', dccname, 'CONTENT'])
-+realm.run(['./t_cccursor', fccname, 'CONTENT'])
-+realm.run(['./t_cccursor', realm.ccache, 'CONTENT'])
-+realm.run(['./t_cccursor', mfoo, 'CONTENT'], expected_code=1)
-+if test_keyring:
-+ realm.run(['./t_cccursor', krccname, 'CONTENT'])
-+ realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname])
-
- # Make sure FILE doesn't yield a nonexistent default cache.
--realm.run_as_client([kdestroy])
-+realm.run([kdestroy])
- cursor_test('noexist', [], [])
--realm.run_as_client(['./t_cccursor', fccname, 'CONTENT'], expected_code=1)
-+realm.run(['./t_cccursor', fccname, 'CONTENT'], expected_code=1)
-
- success('Renewing credentials')
-diff --git a/src/util/k5test.py b/src/util/k5test.py
-index 3400154..aead832 100644
---- a/src/util/k5test.py
-+++ b/src/util/k5test.py
-@@ -142,6 +133,9 @@ Scripts may use the following functions and variables:
- added newline) in testlog, and write it to stdout if running
- verbosely.
-
-+* which(progname): Return the location of progname in the executable
-+ path, or None if it is not found.
-+
- * password(name): Return a weakly random password based on name. The
- password will be consistent across calls with the same name.
-
-@@ -388,6 +374,16 @@ def output(msg, force_verbose=False):
- sys.stdout.write(msg)
-
-
-+# Return the location of progname in the executable path, or None if
-+# it is not found.
-+def which(progname):
-+ for dir in os.environ["PATH"].split(os.pathsep):
-+ path = os.path.join(dir, progname)
-+ if os.access(path, os.X_OK):
-+ return path
-+ return None
-+
-+
- def password(name):
- """Choose a weakly random password from name, consistent across calls."""
- return name + str(os.getpid())
-@@ -880,6 +880,11 @@ class K5Realm(object):
- env['KPROP_PORT'] = str(self.portbase + 3)
- return env
-
-+ def run(self, args, env=None, **keywords):
-+ if env is None:
-+ env = self.env_client
-+ return _run_cmd(args, env, **keywords)
-+
- def run_as_client(self, args, **keywords):
- return _run_cmd(args, self.env_client, **keywords)
-
-diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in
-index f64226b..ad53e65 100644
---- a/src/lib/krb5/ccache/Makefile.in
-+++ b/src/lib/krb5/ccache/Makefile.in
-@@ -71,6 +66,7 @@ SRCS= $(srcdir)/ccbase.c \
-
- EXTRADEPSRCS= \
- $(srcdir)/t_cc.c \
-+ $(srcdir)/t_cccol.c \
- $(srcdir)/t_cccursor.c
-
- ##DOS##OBJS=$(OBJS) $(OUTPRE)ccfns.$(OBJEXT)
-@@ -108,6 +104,10 @@ T_CC_OBJS=t_cc.o
- t_cc: $(T_CC_OBJS) $(KRB5_BASE_DEPLIBS)
- $(CC_LINK) -o t_cc $(T_CC_OBJS) $(KRB5_BASE_LIBS)
-
-+T_CCCOL_OBJS = t_cccol.o
-+t_cccol: $(T_CCCOL_OBJS) $(KRB5_BASE_DEPLIBS)
-+ $(CC_LINK) -o $@ $(T_CCCOL_OBJS) $(KRB5_BASE_LIBS)
-+
- T_CCCURSOR_OBJS = t_cccursor.o
- t_cccursor: $(T_CCCURSOR_OBJS) $(KRB5_BASE_DEPLIBS)
- $(CC_LINK) -o $@ $(T_CCCURSOR_OBJS) $(KRB5_BASE_LIBS)
-@@ -116,11 +116,11 @@ check-unix:: t_cc
- KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\
- $(RUN_SETUP) $(VALGRIND) ./t_cc
-
--check-pytests:: t_cccursor
-+check-pytests:: t_cccursor t_cccol
- $(RUNPYTEST) $(srcdir)/t_cccol.py $(PYTESTFLAGS)
-
- clean-unix::
-- $(RM) t_cc t_cc.o t_cccursor t_cccursor.o
-+ $(RM) t_cc t_cc.o t_cccursor t_cccursor.o t_cccol t_cccol.o
-
- ##WIN32## $(OUTPRE)cc_mslsa.$(OBJEXT): cc_mslsa.c $(top_srcdir)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS)
-