From b1f558a0f5adc777ba60395ae41f99800b538fcb Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 15 Oct 2013 10:53:33 -0400 Subject: [PATCH] Drop backported patch --- krb5.spec | 3 +- persistent_keyring.patch | 3321 -------------------------------------- 2 files changed, 1 insertion(+), 3323 deletions(-) delete mode 100644 persistent_keyring.patch diff --git a/krb5.spec b/krb5.spec index 20f62c6..0053886 100644 --- a/krb5.spec +++ b/krb5.spec @@ -114,7 +114,6 @@ Patch201: krb5-1.11.2-keycheck.patch Patch202: krb5-1.11.2-otp.patch # Patches for kernel-persistent-keyring support (backport) -Patch301: persistent_keyring.patch Patch302: krb5-master-kinit-cccol.patch Patch303: krb5-keyring-strtol.patch @@ -311,7 +310,6 @@ certificate. %setup -q -n %{name}-%{version}-alpha1 -a 3 -a 100 ln -s NOTICE LICENSE -%patch301 -p1 -b .persistent-keyring %patch302 -p1 -b .kinit-cccol %patch303 -p1 -b .keyring-strtol @@ -1041,6 +1039,7 @@ exit 0 * Thu Oct 17 2013 Nalin Dahyabhai - 1.12-alpha1.0 - initial update to alpha1 + - drop backport of persistent keyring support * Wed Oct 16 2013 Nalin Dahyabhai - 1.11.3-26 - create and own /etc/gss (#1019937) 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_" 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 -- * :: -- */ --#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 ? "" : 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 - #include - #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 -+#include -+#include -+#include -+#include -+ -+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) -