From bf2b6cb4e7bc5ccdc4436588345830141b9cdede Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Fri, 6 Sep 2013 14:12:24 -0400 Subject: [PATCH] - incorporate backported persistent-keyring (Simo) - incorporate Simo's backport of his persistent-keyring changes (#991148) --- krb5.spec | 10 +- persistent_keyring.patch | 2797 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2806 insertions(+), 1 deletion(-) create mode 100644 persistent_keyring.patch diff --git a/krb5.spec b/krb5.spec index cabe052..5b5a70f 100644 --- a/krb5.spec +++ b/krb5.spec @@ -32,7 +32,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.11.3 -Release: 9%{?dist} +Release: 10%{?dist} # Maybe we should explode from the now-available-to-everybody tarball instead? # http://web.mit.edu/kerberos/dist/krb5/1.11/krb5-1.11.3-signed.tar Source0: krb5-%{version}.tar.gz @@ -101,6 +101,9 @@ Patch134: krb5-1.11-kpasswdtest.patch 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 + License: MIT URL: http://web.mit.edu/kerberos/www/ Group: System Environment/Libraries @@ -329,6 +332,8 @@ ln -s NOTICE LICENSE %patch201 -p1 -b .keycheck %patch202 -p1 -b .otp +%patch301 -p1 -b .persistent-keyring + # Take the execute bit off of documentation. chmod -x doc/krb5-protocol/*.txt @@ -911,6 +916,9 @@ exit 0 %{_sbindir}/uuserver %changelog +* Fri Sep 6 2013 Nalin Dahyabhai 1.11.3-10 +- incorporate Simo's backport of his persistent-keyring changes (#991148) + * Fri Aug 23 2013 Nalin Dahyabhai 1.11.3-9 - take another stab at accounting for UnversionedDocdirs for the -libs subpackage (spotted by ssorce) diff --git a/persistent_keyring.patch b/persistent_keyring.patch new file mode 100644 index 0000000..1f9be9b --- /dev/null +++ b/persistent_keyring.patch @@ -0,0 +1,2797 @@ +diff --git a/src/aclocal.m4 b/src/aclocal.m4 +index 2c17e46..abb3eb5 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,6 +1665,15 @@ 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, ++ [dnl Pre-reqs were found ++ AC_DEFINE(HAVE_PERSISTENT_KEYRING, 1, [Define if persistent keyrings are supported]) ++ ])) ++])dnl ++dnl + dnl + dnl Use PAM instead of local crypt() compare for checking local passwords, + dnl and perform PAM account, session management, and password-changing where +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index fd1bcec..2c52f73 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -66,7 +66,12 @@ + * 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 ++ * special key ++ * ++ * For collections: ++ * - A collection is a keyring containing multiple ccache keyrings ++ * - The primary ccache in use is referenced by a special key in the ++ * collection keyring (see below) + */ + + #include "cc-int.h" +@@ -101,9 +106,10 @@ debug_print(char *fmt, ...) + #endif + + /* +- * We always use "user" key type ++ * We use the "user" key type for labels and "big_key" for tickets + */ + #define KRCC_KEY_TYPE_USER "user" ++#define KRCC_KEY_TYPE_BIG_KEY "big_key" + + /* + * We create ccaches as separate keyrings +@@ -117,20 +123,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 +131,64 @@ debug_print(char *fmt, ...) + */ + #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__" + ++/* ++ * Special name that identifies the key that hold the reference to the ++ * current primary ccache in the collection ++ */ ++#define KRCC_COLLECTION_PRIMARY "krb_ccache:primary" ++ ++/* ++ * 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_CCCOL_PREFIX "_krb_" ++ ++#define KRCC_PERSIST_PREFIX "persistent:" ++#define KRCC_USER_PREFIX "user:" ++#define KRCC_SESSION_PREFIX "session:" ++#define KRCC_PROCESS_PREFIX "process:" ++#define KRCC_THREAD_PREFIX "thread:" ++ ++#define KRCC_HAS_PERSIST_PREFIX(r) \ ++ (strncmp(r, KRCC_PERSIST_PREFIX, sizeof(KRCC_PERSIST_PREFIX)-1) == 0) ++#define KRCC_HAS_USER_PREFIX(r) \ ++ (strncmp(r, KRCC_USER_PREFIX, sizeof(KRCC_USER_PREFIX)-1) == 0) ++#define KRCC_HAS_SESSION_PREFIX(r) \ ++ (strncmp(r, KRCC_SESSION_PREFIX, sizeof(KRCC_SESSION_PREFIX)-1) == 0) ++#define KRCC_HAS_PROCESS_PREFIX(r) \ ++ (strncmp(r, KRCC_PROCESS_PREFIX, sizeof(KRCC_PROCESS_PREFIX)-1) == 0) ++#define KRCC_HAS_THREAD_PREFIX(r) \ ++ (strncmp(r, KRCC_THREAD_PREFIX, sizeof(KRCC_THREAD_PREFIX)-1) == 0) ++ ++#define KRCC_IS_LEGACY_SESSION(r) \ ++ (!KRCC_HAS_PERSIST_PREFIX(r) && \ ++ !KRCC_HAS_USER_PREFIX(r) && \ ++ !KRCC_HAS_SESSION_PREFIX(r) && \ ++ !KRCC_HAS_PROCESS_PREFIX(r) && \ ++ !KRCC_HAS_THREAD_PREFIX(r)) ++ ++enum krcc_keyring_type { ++ KRCC_LEGACY_SESSION = 0, ++ KRCC_PERSIST, ++ KRCC_USER, ++ KRCC_SESSION, ++ KRCC_PROCESS, ++ KRCC_THREAD ++}; ++ ++ + #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; +@@ -180,9 +210,8 @@ typedef struct _krb5_krcc_data + key_serial_t parent_id; /* parent keyring of this ccache keyring */ + key_serial_t ring_id; /* keyring representing ccache */ + key_serial_t princ_id; /* key holding principal info */ +- int numkeys; /* # of keys in this ring +- * (does NOT include principal info) */ + krb5_timestamp changetime; ++ const char *key_type; + } krb5_krcc_data; + + /* Passed internally to assure we don't go past the bounds of our buffer */ +@@ -190,6 +219,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 +288,18 @@ 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); ++ + /* + * Internal utility functions + */ +@@ -275,103 +317,111 @@ 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); ++static krb5_error_code krb5_krcc_resolve_internal ++(key_serial_t ring_id, key_serial_t ccache_id, const char *residual, ++ krb5_ccache *cache_out); ++ ++static krb5_error_code krb5_krcc_get_keyring ++(krb5_context context, const char *full_residual, char **name, ++ krb5_boolean *subsidiary, key_serial_t *id); ++ ++static krb5_error_code krb5_krcc_default_keyring ++(krb5_context context, krb5_boolean *subsidiary, char **name, ++ key_serial_t *id); ++ ++static void krb5_krcc_destroy_primary ++(key_serial_t ring_id); ++ ++static const char * krb5_krcc_get_ring_name ++(const char *residual); ++ ++static char * krb5_krcc_new_residual ++(const char *residual, const char *ring_name); ++ ++static krb5_error_code krb5_krcc_unique_keyring ++(krb5_context context, key_serial_t parent_id, char **retname, ++ key_serial_t *keyring_id); ++ ++static krb5_error_code krb5_krcc_set_primary ++(krb5_context context, const char *name, key_serial_t ring_id); ++ ++static krb5_error_code krb5_krcc_get_primary ++(krb5_context context, key_serial_t ring_id, char **name); + + /* 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. +- */ +-static int KRB5_CALLCONV +-krb5_krcc_getkeycount(key_serial_t cred_ring) +-{ +- int res, nkeys; +- +- res = keyctl_read(cred_ring, NULL, 0); +- if (res > 0) +- nkeys = (res / sizeof(key_serial_t)) - 1; +- else +- nkeys = 0; +- return(nkeys); +-} +- +-/* + * Modifies: + * id + * +@@ -388,24 +438,81 @@ 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; ++ char *new_name = NULL; ++ char *residual; ++ const char *name; ++ key_serial_t key; + + 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->ring_id == 0) { ++ /* deferred initialization */ ++ name = krb5_krcc_get_ring_name(data->name); ++ ++ if (name[0] == '\0') { ++ /* empty initial primary key, geneate new unique one */ ++ kret = krb5_krcc_unique_keyring(context, data->parent_id, ++ &new_name, &key); ++ if (kret) ++ goto out; ++ ++ kret = krb5_krcc_set_primary(context, new_name, data->parent_id); ++ if (kret) ++ goto out; ++ ++ residual = krb5_krcc_new_residual(data->name, new_name); ++ if (!residual) { ++ kret = ENOMEM; ++ goto out; ++ } ++ free(data->name); ++ data->name = residual; ++ name = new_name; ++ } else { ++ /* either dangling primary key or legacy session cache */ ++ key = keyctl_search(data->parent_id, KRCC_KEY_TYPE_KEYRING, ++ name, 0); ++ if (key == -1) { ++ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, ++ data->parent_id); ++ if (key == -1) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_initialize: Error adding new " ++ "keyring '%s': %s\n", name, strerror(kret))); ++ goto out; ++ } ++ DEBUG_PRINT(("krb5_krcc_initialize: new keyring '%s', key %d, " ++ "added to keyring %d\n", name, key, ring_id)); ++ } ++ } ++ data->ring_id = key; ++ } ++ ++ /* If this is a legacy session make sure to link the ccache keyring ++ * directly in the session keyring too */ ++ if (KRCC_IS_LEGACY_SESSION(data->name)) { ++ /* legacy session */ ++ if (keyctl_link(data->ring_id, KEY_SPEC_SESSION_KEYRING) == -1) { ++ DEBUG_PRINT(("krb5_krcc_initialize: failed to link ccache " ++ "keyring to session keyring %d\n", errno)); ++ } ++ } ++ + 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); ++ free(new_name); + return kret; + } + +@@ -460,20 +567,33 @@ 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: ring_id %d, princ_id %d\n", ++ d->ring_id, d->princ_id)); + +- res = keyctl_clear(d->ring_id); +- if (res != 0) { +- return errno; ++ if (d->ring_id) { ++ res = keyctl_clear(d->ring_id); ++ if (res != 0) ++ return errno; + } +- d->numkeys = 0; + d->princ_id = 0; + krb5_krcc_update_change_time(d); + + return KRB5_OK; + } + ++static void krb5_krcc_destroy_primary(key_serial_t id) ++{ ++ key_serial_t key; ++ ++ key = keyctl_search(id, KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY, 0); ++ if (key != -1) { ++ if (keyctl_unlink(key, id) == -1) { ++ DEBUG_PRINT(("krb5_krcc_destroy_primary: unlinking key %d from " ++ "keyring %d: %s\n", key, id, error_message(errno))); ++ } ++ } ++} ++ + /* + * Effects: + * Destroys the contents of id. +@@ -484,7 +604,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,20 +612,22 @@ 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->ring_id) { ++ if (res == -1) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_destroy: unlinking key %d from keyring " ++ "%d: %s\n", d->ring_id, d->parent_id, ++ error_message(errno))); ++ } + } +-cleanup: ++ /* also remove primary key if any */ ++ krb5_krcc_destroy_primary(d->parent_id); ++ + k5_cc_mutex_unlock(context, &d->lock); + k5_cc_mutex_destroy(&d->lock); + free(d); +@@ -513,9 +635,131 @@ cleanup: + + krb5_change_cache(); + +- return KRB5_OK; ++ return kret; ++} ++ ++static krb5_error_code ++krb5_krcc_unique_keyring(krb5_context context, key_serial_t parent_id, ++ char **retname, key_serial_t *keyring_id) ++{ ++ key_serial_t keyring; ++ krb5_error_code kret; ++ char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS]; ++ int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1; ++ int tries; ++ ++/* XXX This values is platform-specific and should not be here! */ ++/* XXX There is a bug in FC5 where this is not included in errno.h */ ++#ifndef ENOKEY ++#define ENOKEY 126 /* Required key not available */ ++#endif ++ ++ memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX)); ++ /* ++ * Loop until we successfully create a new ccache keyring with ++ * a unique name, or we get an error. Limit to 100 tries. ++ */ ++ k5_cc_mutex_lock(context, &krb5int_krcc_mutex); ++ ++ tries = 100; ++ while (tries-- > 0) { ++ kret = krb5int_random_string(context, uniquename + prefixlen, ++ KRCC_NAME_RAND_CHARS); ++ if (kret) goto done; ++ ++ DEBUG_PRINT(("krb5_krcc_unique_keyring: searching for name '%s'\n", ++ uniquename)); ++ keyring = keyctl_search(parent_id, ++ KRCC_KEY_TYPE_KEYRING, uniquename, 0); ++ /*XXX*/ DEBUG_PRINT(("krb5_krcc_unique_keyring: after searching for '%s', keyring = %d, errno = %d\n", uniquename, keyring, errno)); ++ if (keyring < 0 && errno == ENOKEY) { ++ /* name does not already exist, create it to reserve the name */ ++ keyring = add_key(KRCC_KEY_TYPE_KEYRING, ++ uniquename, NULL, 0, parent_id); ++ if (keyring < 0) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_unique_keyring: '%s' trying to " ++ "create '%s'\n", strerror(errno), uniquename)); ++ goto done; ++ } ++ break; ++ } ++ } ++ ++ if (tries <= 0) { ++ kret = KRB5_CC_BADNAME; ++ goto done; ++ } ++ ++ *retname = strdup(uniquename); ++ if (!*retname) { ++ kret = ENOMEM; ++ goto done; ++ } ++ *keyring_id = keyring; ++ kret = KRB5_OK; ++done: ++ k5_cc_mutex_unlock(context, &krb5int_krcc_mutex); ++ return kret; ++} ++ ++static krb5_error_code ++krb5_krcc_resolve_internal(key_serial_t ring_id, key_serial_t ccache_id, ++ const char *residual, krb5_ccache *cache_out) ++{ ++ krb5_error_code kret; ++ krb5_ccache ccache = NULL; ++ krb5_krcc_data *d; ++ key_serial_t pkey = 0; ++ ++ /* Determine key containing principal information */ ++ pkey = keyctl_search(ccache_id, KRCC_KEY_TYPE_USER, ++ KRCC_SPEC_PRINC_KEYNAME, 0); ++ if (pkey < 0) { ++ DEBUG_PRINT(("krb5_krcc_resolve_internal: Error locating principal " ++ "info for existing ccache in ring %d: %s\n", ++ ccache_id, strerror(errno))); ++ pkey = 0; ++ } ++ ++ ccache = malloc(sizeof(struct _krb5_ccache)); ++ if (!ccache) ++ return ENOMEM; ++ ++ kret = krb5_krcc_new_data(residual, ccache_id, ring_id, &d); ++ if (kret) { ++ goto done; ++ } ++ ++ DEBUG_PRINT(("krb5_krcc_resolve_internal: ring_id %d, princ_id %d, " ++ "nkeys %d\n", ccache_id, pkey, nkeys)); ++ d->princ_id = pkey; ++ ccache->ops = &krb5_krcc_ops; ++ ccache->data = d; ++ ccache->magic = KV5M_CCACHE; ++ *cache_out = ccache; ++ kret = KRB5_OK; ++ ++done: ++ if (kret) { ++ free(ccache); ++ } ++ return kret; + } + ++static const char * ++krb5_krcc_get_ring_name(const char *residual) ++{ ++ const char *name; ++ ++ name = strrchr(residual, ':'); ++ if (name) { ++ name += 1; ++ return name; ++ } ++ ++ return residual; ++} + + /* + * Requires: +@@ -532,45 +776,28 @@ cleanup: + * A filled in krb5_ccache structure "id". + * + * Errors: +- * KRB5_CC_NOMEM - there was insufficient memory to allocate the +- * krb5_ccache. id is undefined. ++ * EINVAL - the residual name is invalid. ++ * ENOMEM - there was insufficient memory for allocations. ++ * id is undefined. + * permission errors + */ + + static krb5_error_code KRB5_CALLCONV + krb5_krcc_resolve(krb5_context context, krb5_ccache * id, const char *full_residual) + { +- krb5_ccache lid; + krb5_error_code kret; +- krb5_krcc_data *d; + key_serial_t key; +- key_serial_t pkey = 0; +- int nkeys = 0; +- int res; +- krb5_krcc_ring_ids_t ids; + key_serial_t ring_id; +- const char *residual; ++ char *residual; ++ const char *name; + + DEBUG_PRINT(("krb5_krcc_resolve: entered with name '%s'\n", + full_residual)); + +- res = krb5_krcc_get_ring_ids(&ids); +- if (res) { +- kret = EINVAL; +- DEBUG_PRINT(("krb5_krcc_resolve: Error getting ring id values!\n")); ++ kret = krb5_krcc_get_keyring(context, full_residual, ++ &residual, NULL, &ring_id); ++ if (kret) + return kret; +- } +- +- if (strncmp(full_residual, "thread:", 7) == 0) { +- residual = full_residual + 7; +- ring_id = ids.thread; +- } else if (strncmp(full_residual, "process:", 8) == 0) { +- residual = full_residual + 8; +- ring_id = ids.process; +- } else { +- residual = full_residual; +- ring_id = ids.session; +- } + + DEBUG_PRINT(("krb5_krcc_resolve: searching ring %d for residual '%s'\n", + ring_id, residual)); +@@ -584,55 +811,24 @@ krb5_krcc_resolve(krb5_context context, krb5_ccache * id, const char *full_resid + * 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)); ++ ++ name = krb5_krcc_get_ring_name(residual); ++ ++ key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, name, 0); ++ if (key == -1) { ++ DEBUG_PRINT(("krb5_krcc_resolve: primary ccache keyring %s " ++ "not found in keyring %d\n", name, ring_id)); ++ key = 0; + } 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); ++ key, name, ring_id)); + } + +- 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; +- } ++ kret = krb5_krcc_resolve_internal(ring_id, key, residual, id); + +- 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; ++ free(residual); ++ return kret; + } + + /* +@@ -652,48 +848,40 @@ static krb5_error_code KRB5_CALLCONV + krb5_krcc_start_seq_get(krb5_context context, krb5_ccache id, + krb5_cc_cursor * cursor) + { +- krb5_krcc_cursor krcursor; + krb5_error_code kret; ++ krb5_krcc_cursor krcursor; + 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)); ++ if (!d->ring_id) { ++ k5_cc_mutex_unlock(context, &d->lock); ++ return KRB5_FCC_NOFILE; ++ } + +- krcursor = (krb5_krcc_cursor) malloc(size); +- if (krcursor == NULL) { ++ size = keyctl_read_alloc(d->ring_id, &keys); ++ if (size == -1) { ++ kret = errno; ++ DEBUG_PRINT(("Error getting from keyring: %s\n", strerror(errno))); + k5_cc_mutex_unlock(context, &d->lock); +- return KRB5_CC_NOMEM; ++ return kret; + } + +- 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); ++ krcursor = calloc(1, sizeof(struct _krb5_krcc_cursor)); ++ if (krcursor == NULL) { ++ free(keys); + k5_cc_mutex_unlock(context, &d->lock); +- return KRB5_CC_IO; ++ return KRB5_CC_NOMEM; + } + +- krcursor->numkeys = d->numkeys; +- krcursor->currkey = 0; + 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 +929,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 +951,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,13 +975,33 @@ 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) { ++ free(krcursor->keys); ++ free(krcursor); ++ } ++ *cursor = NULL; + return KRB5_OK; + } + ++static const char * ++krb5_krcc_use_key_type(const char *residual) ++{ ++ if (KRCC_HAS_PERSIST_PREFIX(residual) ++ || KRCC_HAS_USER_PREFIX(residual) ++ || KRCC_HAS_SESSION_PREFIX(residual) ++ || KRCC_HAS_PROCESS_PREFIX(residual) ++ || KRCC_HAS_THREAD_PREFIX(residual)) { ++ /* new methods, try to use big_key */ ++ return KRCC_KEY_TYPE_BIG_KEY; ++ } else { ++ /* legacy session type, always use user type */ ++ return KRCC_KEY_TYPE_USER; ++ } ++} ++ + /* Utility routine: Creates the back-end data for a keyring cache. + + Call with the global list lock held. */ +@@ -823,14 +1031,54 @@ krb5_krcc_new_data(const char *name, key_serial_t ring, + d->princ_id = 0; + d->ring_id = ring; + d->parent_id = parent_ring; +- d->numkeys = 0; + d->changetime = 0; ++ d->key_type = krb5_krcc_use_key_type(name); + krb5_krcc_update_change_time(d); + + *datapp = d; + return 0; + } + ++static char * ++krb5_krcc_new_residual(const char *residual, const char *ring_name) ++{ ++ char *r, *p; ++ const char *resname; ++ size_t rl, rn; ++ ++ if (KRCC_HAS_PROCESS_PREFIX(residual)) { ++ resname = residual + sizeof(KRCC_PROCESS_PREFIX); ++ } else if (KRCC_HAS_THREAD_PREFIX(residual)) { ++ resname = residual + sizeof(KRCC_THREAD_PREFIX); ++ } else if (KRCC_HAS_PERSIST_PREFIX(residual)) { ++ resname = residual + sizeof(KRCC_PERSIST_PREFIX); ++ } else if (KRCC_HAS_USER_PREFIX(residual)) { ++ resname = residual + sizeof(KRCC_USER_PREFIX); ++ } else if (KRCC_HAS_SESSION_PREFIX(residual)) { ++ resname = residual + sizeof(KRCC_SESSION_PREFIX); ++ } else { ++ /* legacy session type, replace the full residual */ ++ return strdup(ring_name); ++ } ++ ++ /* the ':' after the type prefix is already skipped above */ ++ p = strrchr(resname, ':'); ++ if (p) ++ rl = p - residual; /* excludes subsidiary ':' */ ++ else ++ rl = strlen(residual); ++ rn = strlen(ring_name) + 1; /* includes \0 */ ++ ++ r = malloc(rl + 1 + rn); ++ if (!r) ++ return NULL; ++ ++ memcpy(r, residual, rl); ++ r[rl++] = ':'; ++ memcpy(&r[rl], ring_name, rn); ++ return r; ++} ++ + /* + * Effects: + * Creates a new keyring cred cache whose name is guaranteed to be +@@ -846,82 +1094,67 @@ 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; ++ char *uniquename = NULL; ++ char *residual = NULL; ++ char *new_res = NULL; + krb5_error_code kret; + krb5_krcc_data *d; +- key_serial_t ring_id = KEY_SPEC_SESSION_KEYRING; +- key_serial_t key; ++ krb5_boolean subsidiary; ++ key_serial_t ring_id; ++ key_serial_t key = 0; + + DEBUG_PRINT(("krb5_krcc_generate_new: entered\n")); + ++ kret = krb5_krcc_default_keyring(context, &subsidiary, ++ &residual, &ring_id); ++ if (kret) ++ return kret; ++ ++ if (subsidiary) { ++ 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 done; ++ } ++ + /* Allocate memory */ + lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); +- if (lid == NULL) +- return KRB5_CC_NOMEM; ++ if (lid == NULL) { ++ kret = ENOMEM; ++ goto done; ++ } + + lid->ops = &krb5_krcc_ops; + +- kret = k5_cc_mutex_lock(context, &krb5int_krcc_mutex); +- if (kret) { +- free(lid); +- return kret; +- } ++ kret = krb5_krcc_unique_keyring(context, ring_id, &uniquename, &key); ++ if (kret) ++ goto done; + +-/* 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 ++ new_res = krb5_krcc_new_residual(residual, uniquename); ++ if (!new_res) { ++ kret = ENOMEM; ++ goto done; ++ } + +- /* +- * 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(new_res, key, ring_id, &d); ++ if (kret) ++ goto done; + +- 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_OK; + +- kret = krb5_krcc_new_data(uniquename, key, ring_id, &d); +- k5_cc_mutex_unlock(context, &krb5int_krcc_mutex); ++done: ++ free(uniquename); ++ free(residual); ++ free(new_res); + if (kret) { + free(lid); + return kret; + } +- lid->data = d; + *id = lid; +- krb5_change_cache(); + return KRB5_OK; + } + +@@ -1001,8 +1234,9 @@ krb5_krcc_remove_cred(krb5_context context, krb5_ccache cache, + static krb5_error_code KRB5_CALLCONV + krb5_krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) + { ++ krb5_krcc_data *d = (krb5_krcc_data *)id->data; + DEBUG_PRINT(("krb5_krcc_set_flags: entered\n")); +- ++ if (d->ring_id == 0) return KRB5_FCC_NOFILE; + return KRB5_OK; + } + +@@ -1015,6 +1249,22 @@ krb5_krcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags * flags) + return KRB5_OK; + } + ++static key_serial_t ++krb5_krcc_add_key(const char *name, const void *payload, size_t plen, ++ krb5_krcc_data *data) ++{ ++#ifdef HAVE_PERSISTENT_KEYRING ++ key_serial_t key; ++ ++ /* try with big_key and if it fails fall back to user key */ ++ errno = 0; ++ key = add_key(data->key_type, name, payload, plen, data->ring_id); ++ if (key != -1 || errno != EINVAL) ++ return key; ++#endif ++ return add_key(KRCC_KEY_TYPE_USER, name, payload, plen, data->ring_id); ++} ++ + /* store: Save away creds in the ccache keyring. */ + static krb5_error_code KRB5_CALLCONV + krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) +@@ -1025,12 +1275,17 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) + unsigned int payloadlen; + key_serial_t newkey; + char *keyname = NULL; ++ long timeout; ++ long res; + + 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->ring_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,25 +1295,33 @@ 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); ++ newkey = krb5_krcc_add_key(keyname, payload, payloadlen, d); + 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); ++ goto errout; ++ } ++ ++ timeout = creds->times.endtime - time(NULL); ++ /* if it wraps do not try to set timestamp */ ++ if (timeout > 0) { ++ res = keyctl_set_timeout(d->ring_id, timeout); ++ if (res != 0) ++ DEBUG_PRINT(("krb5_krcc_store: Failed to set ccache timeout " ++ "[%ld]", timeout)); + } + ++ kret = KRB5_OK; ++ krb5_krcc_update_change_time(d); ++ + errout: + if (keyname) + krb5_free_unparsed_name(context, keyname); +@@ -1073,36 +1336,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 +1369,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 +1378,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 */ +@@ -1172,11 +1434,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->ring_id || !d->princ_id) { + princ = 0L; + kret = KRB5_FCC_NOFILE; + goto errout; +@@ -1191,7 +1451,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 +1460,537 @@ errout: + return kret; + } + +-static int +-krb5_krcc_get_ring_ids(krb5_krcc_ring_ids_t *p) ++static krb5_error_code ++krb5_krcc_set_primary(krb5_context context, const char *name, ++ key_serial_t ring_id) + { +- key_serial_t ids_key; +- char ids_buf[128]; +- key_serial_t session, process, thread; +- long val; ++ krb5_error_code kret; ++ key_serial_t key; ++ void *payload = NULL; ++ int payloadlen; + +- DEBUG_PRINT(("krb5_krcc_get_ring_ids: entered\n")); ++ kret = krb5_krcc_unparse_index(context, KRCC_COLLECTION_VERSION, ++ name, &payload, &payloadlen); ++ if (kret) { ++ DEBUG_PRINT(("krb5_krcc_set_primary: Error creating primary with " ++ "content [%d:%s]", KRCC_COLLECTION_VERSION, name)); ++ return kret; ++ } + +- if (!p) +- return EINVAL; ++ key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY, ++ payload, payloadlen, ring_id); ++ if (key == -1) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_set_primary: Error setting primary key: " ++ "%s\n", strerror(kret))); ++ goto done; ++ } + +- /* 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; ++ DEBUG_PRINT(("krb5_krcc_set_primary: created primary key %d, for " ++ "keyring %d (%s)\n", key, ring_id, name)); ++ kret = KRB5_OK; + +- /* +- * 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; ++done: ++ free(payload); ++ return kret; ++} + +- 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_krcc_get_primary(krb5_context context, key_serial_t ring_id, char **name) ++{ ++ krb5_error_code kret; ++ key_serial_t primary; ++ void *payload = NULL; ++ int payloadlen; ++ krb5_int32 version; ++ ++ primary = keyctl_search(ring_id, KRCC_KEY_TYPE_USER, ++ KRCC_COLLECTION_PRIMARY, 0); ++ if (primary == -1) { ++ DEBUG_PRINT(("krb5_krcc_get_primary: No primary key available\n")); ++ *name = NULL; ++ return ENOENT; ++ } ++ payloadlen = keyctl_read_alloc(primary, &payload); ++ if (payloadlen == -1) { ++ DEBUG_PRINT(("krb5_krcc_get_primary: Error reading primary key\n")); ++ kret = EINVAL; ++ goto done; ++ } + +- val = sscanf(ids_buf, "%d:%d:%d", &session, &process, &thread); +- if (val != 3) +- goto out; ++ kret = krb5_krcc_parse_index(context, &version, name, payload, payloadlen); ++ if (kret) { ++ DEBUG_PRINT(("krb5_krcc_get_primary: Error parsing primary key\n")); ++ goto done; ++ } ++ ++ if (version != KRCC_COLLECTION_VERSION) { ++ DEBUG_PRINT(("krb5_krcc_get_primary: Invalid version\n")); ++ kret = EINVAL; ++ goto done; ++ } + +- p->session = session; +- p->process = process; +- p->thread = thread; ++ DEBUG_PRINT(("krb5_krcc_get_primary: primary key %d, points to " ++ "keyring %s\n", primary, *name)); ++ kret = KRB5_OK; + +-out: +- DEBUG_PRINT(("krb5_krcc_get_ring_ids: returning %d:%d:%d\n", +- p->session, p->process, p->thread)); ++done: ++ if (kret) { ++ krb5_krcc_destroy_primary(ring_id); ++ } ++ free(payload); ++ return kret; ++} ++ ++static krb5_error_code ++krb5_krcc_get_keyring(krb5_context context, const char *full_residual, ++ char **name, krb5_boolean *subsidiary, key_serial_t *id) ++{ ++ const char *residual; ++ krb5_error_code kret; ++ key_serial_t ring_id = 0; ++ key_serial_t cccol_id = 0; ++ char *p = NULL; ++ char *ccname = NULL; ++ krb5_boolean sub = FALSE; ++ key_serial_t parent; ++ enum krcc_keyring_type type; ++ long long int luid; ++ int len, ret; ++ ++ if (KRCC_HAS_PROCESS_PREFIX(full_residual)) { ++ residual = full_residual + (sizeof(KRCC_PROCESS_PREFIX) - 1); ++ ring_id = KEY_SPEC_PROCESS_KEYRING; ++ type = KRCC_PROCESS; ++ } else if (KRCC_HAS_PERSIST_PREFIX(full_residual)) { ++ residual = full_residual + (sizeof(KRCC_PERSIST_PREFIX) - 1); ++ type = KRCC_PERSIST; ++ } else if (KRCC_HAS_USER_PREFIX(full_residual)) { ++ residual = full_residual + (sizeof(KRCC_USER_PREFIX) - 1); ++ ring_id = KEY_SPEC_USER_KEYRING; ++ type = KRCC_USER; ++ } else if (KRCC_HAS_SESSION_PREFIX(full_residual)) { ++ residual = full_residual + (sizeof(KRCC_SESSION_PREFIX) - 1); ++ ring_id = KEY_SPEC_SESSION_KEYRING; ++ type = KRCC_SESSION; ++ } else if (KRCC_HAS_THREAD_PREFIX(full_residual)) { ++ residual = full_residual + (sizeof(KRCC_THREAD_PREFIX) - 1); ++ ring_id = KEY_SPEC_THREAD_KEYRING; ++ type = KRCC_THREAD; ++ } else { ++ /* legacy backwards compat */ ++ residual = full_residual; ++ ring_id = KEY_SPEC_SESSION_KEYRING; ++ type = KRCC_LEGACY_SESSION; ++ } ++ ++ /* we are transforming classic KEYRING caches into collection enabled ++ * caches as well here, while maintaining compatibility with existing ++ * practices. ++ * We can't change semantics of the legacy session type as they may be ++ * shared between a new and an old version of the library. So for the ++ * legacy session keyring create the collection keyring using a prefixed ++ * residual name in order ro leave the original name free for the first ++ * ccache keyring which will be placed both in the collection keyring as ++ * well as linked directly in the session keyring. */ ++ switch (type) { ++ case KRCC_LEGACY_SESSION: ++ case KRCC_THREAD: ++ case KRCC_PROCESS: ++ case KRCC_SESSION: ++ case KRCC_USER: ++ /* check if this is a plain name or includes a subsidiary */ ++ p = strchr(residual, ':'); ++ if (p) { ++ sub = TRUE; ++ len = p - residual; ++ } else { ++ len = strlen(residual); ++ } ++ ret = asprintf(&ccname, KRCC_CCCOL_PREFIX"%.*s", len, residual); ++ if (ret == -1 || !ccname) { ++ return ENOMEM; ++ } ++ ++ cccol_id = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, ccname, 0); ++ if (cccol_id == -1) { ++ cccol_id = add_key(KRCC_KEY_TYPE_KEYRING, ++ ccname, NULL, 0, ring_id); ++ if (cccol_id == -1) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_get_keyring: Couldn't create [%s] " ++ "keyring, error %d (%s)\n", ++ ccname, kret, strerror(kret))); ++ free(ccname); ++ return kret; ++ } ++ } ++ free(ccname); ++ ccname = NULL; ++ ring_id = cccol_id; ++ break; ++ ++ case KRCC_PERSIST: ++ ++ switch (residual[0]) { ++ case ':': ++ /* residual present */ ++ sub = TRUE; ++ /* fall through */ ++ case '\0': ++ /* no uid specified, use user's own */ ++ luid = geteuid(); ++ break; ++ default: ++ errno = 0; ++ luid = strtoll(residual, &p, 10); ++ if (errno) ++ return EINVAL; ++ if (*p == ':') ++ sub = TRUE; ++ else if ((p == residual) || (*p != 0)) ++ return EINVAL; ++ } ++ ++#ifdef HAVE_PERSISTENT_KEYRING ++ parent = keyctl_get_persistent(luid, KEY_SPEC_PROCESS_KEYRING); ++ if (parent == -1) { ++ /* if the kernel does not support persistent keyrings, ++ * fall back to a standard user key ring */ ++ if (errno == ENOTSUP) ++ parent = KEY_SPEC_USER_KEYRING; ++ else ++ return errno; ++ } ++#else ++ parent = KEY_SPEC_USER_KEYRING; ++#endif ++ if ((parent == KEY_SPEC_USER_KEYRING) && (luid != geteuid())) ++ return EINVAL; ++ ring_id = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, "_krb", 0); ++ if (ring_id == -1) { ++ ring_id = add_key(KRCC_KEY_TYPE_KEYRING, "_krb", NULL, 0, parent); ++ if (ring_id == -1) { ++ kret = errno; ++ DEBUG_PRINT(("krb5_krcc_get_keyring: Couldn't create _krb " ++ "keyring for uid %ld, error %d (%s)\n", ++ (long)luid, kret, strerror(kret))); ++ return kret; ++ } ++ } ++ break; ++ } ++ ++ /* if a subsidiary is specified just return */ ++ if (sub == TRUE) { ++ *name = strdup(full_residual); ++ if (*name == NULL) ++ return ENOMEM; ++ goto done; ++ } ++ ++ /* Check to see if we have an index key by chance, if we do get the ++ * ccache pointed by it */ ++ kret = krb5_krcc_get_primary(context, ring_id, &ccname); ++ if (kret) { ++ DEBUG_PRINT(("krb5_krcc_get_keyring: Error reading primary key " ++ "from keyring %d: %s\n", ring_id, strerror(kret))); ++ } ++ ++ /* for backwards compatibility the first session keyring must ++ * be named after the residual, for all other types put in an ++ * empty name, it will be replaced at initialization time */ ++ if (type == KRCC_LEGACY_SESSION) { ++ if (!ccname) { ++ /* no primary yet */ ++ *name = strdup(full_residual); ++ if (*name == NULL) ++ return ENOMEM; ++ } else { ++ *name = ccname; ++ goto done; ++ } ++ } else { ++ kret = asprintf(name, "%s:%s", full_residual, ccname ? ccname : ""); ++ free(ccname); ++ if (kret == -1) ++ return ENOMEM; ++ } ++ ++done: ++ if (subsidiary) ++ *subsidiary = sub; ++ *id = ring_id; ++ return KRB5_OK; ++} ++ ++static krb5_error_code ++krb5_krcc_default_keyring(krb5_context context, krb5_boolean *subsidiary, ++ char **name, key_serial_t *id) ++{ ++ const char *defname; ++ ++ *subsidiary = FALSE; ++ if (name) *name = NULL; ++ *id = 0; ++ ++ defname = krb5_cc_default_name(context); ++ if (!defname || strncmp(defname, "KEYRING:", 8) != 0) { ++ /* return classic session type with empty name so that a ++ * random name will actually be generate at initialization time */ ++ return krb5_krcc_get_keyring(context, "", name, subsidiary, id); ++ } ++ return krb5_krcc_get_keyring(context, &defname[8], name, subsidiary, id); ++} ++ ++static krb5_boolean ++krb5_krcc_check_legacy_session(key_serial_t ring_id, const char *residual, ++ char **ccache_name, key_serial_t *ccache_id) ++{ ++ key_serial_t key; ++ char *name, *ep; ++ ++ if (!KRCC_IS_LEGACY_SESSION(residual)) { ++ return FALSE; ++ } ++ ++ /* a legacy session residual looks like this: ++ * : and we need */ ++ ep = strchr(residual, ':'); ++ if (!ep) /* something weird, just ignore */ ++ return FALSE; ++ ++ name = strndup(residual, ep - residual); ++ if (!name) /* too bad */ ++ return FALSE; ++ ++ key = keyctl_search(ring_id, KRCC_KEY_TYPE_KEYRING, name, 0); ++ if (key == -1) { ++ free(name); ++ return FALSE; ++ } ++ ++ *ccache_name = name; ++ *ccache_id = key; ++ return TRUE; ++} ++ ++struct krcc_ptcursor_data { ++ key_serial_t ring_id; ++ char *name; ++ krb5_boolean first; ++ krb5_boolean subsidiary; ++ long num_keys; ++ long next_key; ++ key_serial_t *keys; ++}; ++ ++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 = NULL; ++ krb5_error_code kret; ++ long size; ++ ++ *cursor_out = NULL; ++ ++ data = calloc(1, sizeof(struct krcc_ptcursor_data)); ++ if (!data) ++ return ENOMEM; ++ cursor = malloc(sizeof(struct krb5_cc_ptcursor_s)); ++ if (!cursor) { ++ free(data); ++ return ENOMEM; ++ } ++ cursor->ops = &krb5_krcc_ops; ++ cursor->data = data; ++ data->first = TRUE; ++ data->subsidiary = FALSE; ++ ++ /* If a keyring cannot be found or the default cache is a subsidiary ++ * then return an empty data set with only the primary/subsidiary set ++ * as the cache name */ ++ kret = krb5_krcc_default_keyring(context, &data->subsidiary, ++ &data->name, &data->ring_id); ++ if (kret || (data->ring_id == 0)) ++ goto done; ++ ++ size = keyctl_read_alloc(data->ring_id, (void **)&data->keys); ++ if (size == -1) { ++ kret = errno; ++ goto done; ++ } ++ data->num_keys = size / sizeof(key_serial_t); ++ ++ kret = KRB5_OK; ++ ++done: ++ if (kret) { ++ free(cursor->data); ++ free(cursor); ++ } else { ++ *cursor_out = cursor; ++ } ++ return kret; ++} ++ ++static krb5_error_code KRB5_CALLCONV ++krb5_krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor, ++ krb5_ccache *cache_out) ++{ ++ struct krcc_ptcursor_data *data; ++ key_serial_t ccache_id; ++ krb5_error_code kret; ++ const char *first_name; ++ const char *pref; ++ const char *type; ++ size_t preflen; ++ size_t typelen; ++ char *description = NULL; ++ char *residual; ++ char *name; ++ long cur_key; ++ long res; ++ ++ *cache_out = NULL; ++ ++ data = cursor->data; ++ ++ /* No keyring available */ ++ if (data->ring_id == 0) ++ return 0; ++ ++ /* if no cccol is available name will be set to "" for compatibility ++ * with legacy session keyrings. Return 0 in this case as we do not ++ * want to try to enumerate all keys in the general session keyring */ ++ if (data->name[0] == '\0') ++ return 0; ++ ++ /* if we already returned the named subsidiary just stop */ ++ if (data->subsidiary && !data->first) ++ return 0; ++ ++ first_name = krb5_krcc_get_ring_name(data->name); ++ ++ if (data->first && first_name[0] != '\0') { ++ /* first call in, search for a key matching data->name ++ * which is the primary or the named subsidiary */ ++ pref = first_name; ++ } else { ++ /* there is no first if the subsidiary is present but empty, this ++ * happens when the default ccache is not of type KEYRING and a ++ * program explicitly enumerates all cache collections */ ++ data->first = FALSE; ++ pref = KRCC_NAME_PREFIX; ++ } ++ preflen = strlen(pref); ++ ++ type = KRCC_KEY_TYPE_KEYRING ";"; ++ typelen = strlen(type); ++ ++ for (cur_key = data->next_key; cur_key < data->num_keys; cur_key++) { ++ /* free any previously returned description */ ++ free(description); ++ description = NULL; ++ ++ res = keyctl_describe_alloc(data->keys[cur_key], &description); ++ if (res == -1) { ++ DEBUG_PRINT(("krb5_krcc_ptcursor_next: Failed to get keyring " ++ "description for %ld\n", data->keys[cur_key])); ++ /* try the next */ ++ continue; ++ } ++ ++ name = strrchr(description, ';'); ++ if (!name) { ++ DEBUG_PRINT(("krb5_krcc_ptcursor_next: Keyring (%ld) description" ++ " [%s] has unknown format (no ';')!\n", ++ data->keys[cur_key], description)); ++ /* try the next */ ++ continue; ++ } ++ name++; ++ ++ if (strncmp(type, description, typelen) != 0) { ++ /* not a keyring, just skip */ ++ continue; ++ } ++ ++ if (strncmp(pref, name, preflen) == 0) { ++ /* found, but skip primary if not first */ ++ if (!data->first) { ++ if (strcmp(first_name, name) == 0) ++ continue; ++ } ++ ++ /* a valid one */ ++ ccache_id = data->keys[cur_key]; ++ break; ++ } ++ } ++ ++ if (cur_key >= data->num_keys) { ++ /* nothing found */ ++ free(description); ++ ++ if (!krb5_krcc_check_legacy_session(data->ring_id, data->name, ++ &name, &ccache_id)) ++ return 0; ++ ++ /* legacy session and found original cache, point description at name ++ *so that name will be properly freed later on */ ++ description = name; ++ } ++ ++ if (data->first) { ++ /* we searched for the primary/subsidiary, ++ * reset for the following searches */ ++ data->first = FALSE; ++ } else { ++ data->next_key = cur_key + 1; ++ } ++ ++ residual = krb5_krcc_new_residual(data->name, name); ++ free(description); /* we don't need 'name' anymore */ ++ if (!residual) ++ return ENOMEM; ++ ++ kret = krb5_krcc_resolve_internal(data->ring_id, ccache_id, ++ residual, cache_out); ++ ++ free(residual); ++ return kret; ++} ++ ++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) { ++ free(data->name); ++ free(data->keys); ++ free(data); ++ } ++ (*cursor)->data = NULL; ++ 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 kret; ++ const char *ring_name; ++ ++ ring_name = krb5_krcc_get_ring_name(data->name); ++ kret = krb5_krcc_set_primary(context, ring_name, data->parent_id); ++ return kret; ++} ++ + /* + * =============================================================== + * INTERNAL functions to parse a credential from a key payload +@@ -1271,8 +2011,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 +2030,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 +2041,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 +2095,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 +2104,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 +2120,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 +2131,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 +2144,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 +2162,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 +2188,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 +2216,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 +2225,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 +2242,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 +2254,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 +2267,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 +2284,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 +2304,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 +2316,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 +2326,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 +2342,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 +2353,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 +2364,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 +2374,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 +2388,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 +2402,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 +2412,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 +2427,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 +2439,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 +2464,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 +2483,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 +2510,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 +2581,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 +2592,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 +2618,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 +2672,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 +2729,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 +2832,8 @@ krb5_krcc_unparse_cred(krb5_context context, krb5_ccache id, + kret = KRB5_OK; + + errout: ++ if (kret) ++ free(buf); + return kret; + } + +@@ -2065,15 +2877,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, /* switch_to */ + }; + + #else /* !USE_KEYRING_CCACHE */