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