Exemplo n.º 1
0
const char * KRB5_CALLCONV
krb5_cc_default_name(krb5_context context)
{
    krb5_error_code err = 0;
    krb5_os_context os_ctx = NULL;

    if (!context || context->magic != KV5M_CONTEXT) { err = KV5M_CONTEXT; }

    if (!err) {
        os_ctx = &context->os_context;

        if (os_ctx->default_ccname == NULL) {
            /* Default ccache name has not been set yet */
            char *new_ccname = NULL;
            char new_ccbuf[1024];

            /* try the environment variable first */
            new_ccname = getenv(KRB5_ENV_CCNAME);

            if (new_ccname == NULL) {
                /* fall back on the default ccache name for the OS */
                new_ccname = new_ccbuf;
                err = get_from_os (new_ccbuf, sizeof (new_ccbuf));
            }

            if (!err) {
                err = krb5_cc_set_default_name (context, new_ccname);
            }
        }
    }

    return err ? NULL : os_ctx->default_ccname;
}
Exemplo n.º 2
0
static void
test_default_name(krb5_context context)
{
    krb5_error_code ret;
    const char *p, *test_cc_name = TEST_CC_NAME;
    char *p1, *p2, *p3;

    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 1 failed");
    p1 = estrdup(p);

    ret = krb5_cc_set_default_name(context, NULL);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_set_default_name failed");

    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
    p2 = estrdup(p);

    if (strcmp(p1, p2) != 0)
	krb5_errx (context, 1, "krb5_cc_default_name no longer same");
	
    ret = krb5_cc_set_default_name(context, test_cc_name);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");

    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
    p3 = estrdup(p);

#ifndef KRB5_USE_PATH_TOKENS
    /* If we are using path tokens, we don't expect the p3 and
       test_cc_name to match since p3 is going to have expanded
       tokens. */
    if (strcmp(p3, test_cc_name) != 0)
	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
#endif

    free(p1);
    free(p2);
    free(p3);
}
Exemplo n.º 3
0
Arquivo: sss_krb5.c Projeto: 3van/sssd
char * sss_get_ccache_name_for_principal(TALLOC_CTX *mem_ctx,
                                         krb5_context ctx,
                                         krb5_principal principal,
                                         const char *location)
{
#ifdef HAVE_KRB5_CC_COLLECTION
    krb5_error_code kerr;
    krb5_ccache tmp_cc = NULL;
    char *tmp_ccname = NULL;
    char *ret_ccname = NULL;

    DEBUG(SSSDBG_TRACE_ALL,
          "Location: [%s]\n", location);

    kerr = krb5_cc_set_default_name(ctx, location);
    if (kerr != 0) {
        KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        return NULL;
    }

    kerr = krb5_cc_cache_match(ctx, principal, &tmp_cc);
    if (kerr != 0) {
        const char *err_msg = sss_krb5_get_error_message(ctx, kerr);
        DEBUG(SSSDBG_TRACE_INTERNAL,
              "krb5_cc_cache_match failed: [%d][%s]\n", kerr, err_msg);
        sss_krb5_free_error_message(ctx, err_msg);
        return NULL;
    }

    kerr = krb5_cc_get_full_name(ctx, tmp_cc, &tmp_ccname);
    if (kerr != 0) {
        KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        goto done;
    }

    DEBUG(SSSDBG_TRACE_ALL,
          "tmp_ccname: [%s]\n", tmp_ccname);

    ret_ccname = talloc_strdup(mem_ctx, tmp_ccname);
    if (ret_ccname == NULL) {
        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed (ENOMEM).\n");
    }

done:
    if (tmp_cc != NULL) {
        kerr = krb5_cc_close(ctx, tmp_cc);
        if (kerr != 0) {
            KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        }
    }
    krb5_free_string(ctx, tmp_ccname);

    return ret_ccname;
#else
    return NULL;
#endif /* HAVE_KRB5_CC_COLLECTION */
}
Exemplo n.º 4
0
/*
 * Set the default ccache name for all processes for the current user
 * (and the current context)
 */
krb5_error_code KRB5_CALLCONV
krb5int_cc_user_set_default_name(krb5_context context, const char *name)
{
    krb5_error_code code = 0;
    if ((code = krb5_cc_set_default_name(context, name)))
        return code;
    set_for_os(name);
    return code;
}
Exemplo n.º 5
0
/*
 * Set the default ccache name for all processes for the current user
 * (and the current context)
 */
krb5_error_code KRB5_CALLCONV
krb5int_cc_user_set_default_name(krb5_context context, const char *name)
{
    krb5_error_code err;

    err = krb5_cc_set_default_name(context, name);
    if (err)
        return err;
    set_for_os(name);
    return 0;
}
Exemplo n.º 6
0
static void
test_set_default_ccname(krb5_context ctx, char *ccname)
{
    krb5_error_code retval;

    retval = krb5_cc_set_default_name(ctx, ccname);
    if (retval) {
        com_err("krb5_set_default_ccname", retval, 0);
        return;
    }
    printf("krb5_set_default_ccname(%s)\n", ccname);
}
Exemplo n.º 7
0
int
main(int argc, char **argv)
{
    krb5_context ctx, ctx2;
    krb5_plugin_initvt_fn *mods;
    const krb5_enctype etypes1[] = { ENCTYPE_DES3_CBC_SHA1, 0 };
    const krb5_enctype etypes2[] = { ENCTYPE_AES128_CTS_HMAC_SHA1_96,
                                     ENCTYPE_AES256_CTS_HMAC_SHA1_96, 0 };
    krb5_prompt_type ptypes[] = { KRB5_PROMPT_TYPE_PASSWORD };

    /* Copy a default context and verify the result. */
    check(krb5_init_context(&ctx) == 0);
    check(krb5_copy_context(ctx, &ctx2) == 0);
    check_context(ctx2, ctx);
    krb5_free_context(ctx2);

    /* Set non-default values for all of the propagated fields in ctx. */
    ctx->allow_weak_crypto = TRUE;
    check(krb5_set_default_in_tkt_ktypes(ctx, etypes1) == 0);
    check(krb5_set_default_tgs_enctypes(ctx, etypes2) == 0);
    check(krb5_set_debugging_time(ctx, 1234, 5678) == 0);
    check(krb5_cc_set_default_name(ctx, "defccname") == 0);
    check(krb5_set_default_realm(ctx, "defrealm") == 0);
    ctx->clockskew = 18;
    ctx->kdc_req_sumtype = CKSUMTYPE_NIST_SHA;
    ctx->default_ap_req_sumtype = CKSUMTYPE_HMAC_SHA1_96_AES128;
    ctx->default_safe_sumtype = CKSUMTYPE_HMAC_SHA1_96_AES256;
    ctx->kdc_default_options = KDC_OPT_FORWARDABLE;
    ctx->library_options = 0;
    ctx->profile_secure = TRUE;
    ctx->udp_pref_limit = 2345;
    ctx->use_conf_ktypes = TRUE;
    ctx->ignore_acceptor_hostname = TRUE;
    ctx->dns_canonicalize_hostname = CANONHOST_FALSE;
    free(ctx->plugin_base_dir);
    check((ctx->plugin_base_dir = strdup("/a/b/c/d")) != NULL);

    /* Also set some of the non-propagated fields. */
    ctx->prompt_types = ptypes;
    check(k5_plugin_load_all(ctx, PLUGIN_INTERFACE_PWQUAL, &mods) == 0);
    k5_plugin_free_modules(ctx, mods);
    k5_setmsg(ctx, ENOMEM, "nooooooooo");
    krb5_set_trace_callback(ctx, trace, ctx);

    /* Copy the intentionally messy context and verify the result. */
    check(krb5_copy_context(ctx, &ctx2) == 0);
    check_context(ctx2, ctx);
    krb5_free_context(ctx2);

    krb5_free_context(ctx);
    return 0;
}
Exemplo n.º 8
0
static void
test_default_name(krb5_context context)
{
    krb5_error_code ret;
    const char *p, *test_cc_name = "/tmp/krb5-cc-test-foo";
    char *p1, *p2, *p3;

    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 1 failed");
    p1 = estrdup(p);

    ret = krb5_cc_set_default_name(context, NULL);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_set_default_name failed");

    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
    p2 = estrdup(p);

    if (strcmp(p1, p2) != 0)
	krb5_errx (context, 1, "krb5_cc_default_name no longer same");
	
    ret = krb5_cc_set_default_name(context, test_cc_name);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
    
    p = krb5_cc_default_name(context);
    if (p == NULL)
	krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
    p3 = estrdup(p);
    
    if (strcmp(p3, test_cc_name) != 0)
	krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
}
Exemplo n.º 9
0
int
main(int argc, char **argv)
{
    krb5_error_code ret;
    krb5_context ctx;
    krb5_cccol_cursor cursor;
    krb5_ccache cache, hold[64];
    int i;
    char *name;

    assert(krb5_init_context(&ctx) == 0);
    if (argc > 1)
        assert(krb5_cc_set_default_name(ctx, argv[1]) == 0);

    if (argc > 2) {
        assert(argc < 60);
        for (i = 2; i < argc; i++) {
            if (strcmp(argv[i], "CONTENT") == 0) {
                ret = krb5_cccol_have_content(ctx);
                krb5_free_context(ctx);
                return ret != 0;
            }
            assert(krb5_cc_resolve(ctx, argv[i], &hold[i - 2]) == 0);
        }
    }

    assert(krb5_cccol_cursor_new(ctx, &cursor) == 0);
    while (1) {
        assert(krb5_cccol_cursor_next(ctx, cursor, &cache) == 0);
        if (cache == NULL)
            break;
        assert(krb5_cc_get_full_name(ctx, cache, &name) == 0);
        printf("%s\n", name);
        krb5_free_string(ctx, name);
        krb5_cc_close(ctx, cache);
    }
    assert(krb5_cccol_cursor_free(ctx, &cursor) == 0);

    for (i = 2; i < argc; i++)
        krb5_cc_close(ctx, hold[i - 2]);

    krb5_free_context(ctx);
    return 0;
}
Exemplo n.º 10
0
krb5_error_code
krb5_os_init_context(krb5_context ctx, krb5_boolean kdc)
{
    krb5_os_context os_ctx;
    krb5_error_code    retval = 0;
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
#endif /* _WIN32 */

    os_ctx = &ctx->os_context;
    os_ctx->magic = KV5M_OS_CONTEXT;
    os_ctx->time_offset = 0;
    os_ctx->usec_offset = 0;
    os_ctx->os_flags = 0;
    os_ctx->default_ccname = 0;

    ctx->vtbl = 0;
    PLUGIN_DIR_INIT(&ctx->libkrb5_plugins);
    PLUGIN_DIR_INIT(&ctx->preauth_plugins);
    ctx->preauth_context = NULL;

    retval = os_init_paths(ctx, kdc);
    /*
     * If there's an error in the profile, return an error.  Just
     * ignoring the error is a Bad Thing (tm).
     */

    if (!retval) {
        krb5_cc_set_default_name(ctx, NULL);

#ifdef _WIN32
        /* We initialize winsock to version 1.1 but
         * we do not care if we succeed or fail.
         */
        wVersionRequested = 0x0101;
        WSAStartup (wVersionRequested, &wsaData);
#endif /* _WIN32 */
    }
    return retval;
}
Exemplo n.º 11
0
OM_uint32
gss_krb5_ccache_name(OM_uint32 *minor_status, 
		     const char *name,
		     const char **out_name)
{
    krb5_error_code kret;

    *minor_status = 0;

    GSSAPI_KRB5_INIT();

    if (out_name) {
	const char *name;

	if (last_out_name) {
	    free(last_out_name);
	    last_out_name = NULL;
	}

	name = krb5_cc_default_name(gssapi_krb5_context);
	if (name == NULL) {
	    *minor_status = ENOMEM;
	    gssapi_krb5_set_error_string ();
	    return GSS_S_FAILURE;
	}
	last_out_name = strdup(name);
	if (last_out_name == NULL) {
	    *minor_status = ENOMEM;
	    return GSS_S_FAILURE;
	}
	*out_name = last_out_name;
    }

    kret = krb5_cc_set_default_name(gssapi_krb5_context, name);
    if (kret) {
	*minor_status = kret;
	gssapi_krb5_set_error_string ();
	return GSS_S_FAILURE;
    }
    return GSS_S_COMPLETE;
}
Exemplo n.º 12
0
static void
test_mcc_default(void)
{
    krb5_context context;
    krb5_error_code ret;
    krb5_ccache id, id2;
    int i;

    for (i = 0; i < 10; i++) {

	ret = krb5_init_context(&context);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_init_context");

	ret = krb5_cc_set_default_name(context, "MEMORY:foo");
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_set_default_name");

	ret = krb5_cc_default(context, &id);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_default");

	ret = krb5_cc_default(context, &id2);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_default");

	ret = krb5_cc_close(context, id);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_close");

	ret = krb5_cc_close(context, id2);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_close");

	krb5_free_context(context);
    }
}
Exemplo n.º 13
0
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_set_sec_context_option
           (OM_uint32 *minor_status,
            gss_ctx_id_t *context_handle,
            const gss_OID desired_object,
            const gss_buffer_t value)
{
    krb5_context context;
    OM_uint32 maj_stat;

    GSSAPI_KRB5_INIT (&context);

    if (value == GSS_C_NO_BUFFER) {
	*minor_status = EINVAL;
	return GSS_S_FAILURE;
    }

    if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) {
	gsskrb5_ctx ctx;
	int flag;

	if (*context_handle == GSS_C_NO_CONTEXT) {
	    *minor_status = EINVAL;
	    return GSS_S_NO_CONTEXT;
	}

	maj_stat = get_bool(minor_status, value, &flag);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;

	ctx = (gsskrb5_ctx)*context_handle;
	HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
	if (flag)
	    ctx->more_flags |= COMPAT_OLD_DES3;
	else
	    ctx->more_flags &= ~COMPAT_OLD_DES3;
	ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
	return GSS_S_COMPLETE;
    } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) {
	int flag;

	maj_stat = get_bool(minor_status, value, &flag);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;

	krb5_set_dns_canonicalize_hostname(context, flag);
	return GSS_S_COMPLETE;

    } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) {
	char *str;

	maj_stat = get_string(minor_status, value, &str);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;

	maj_stat = _gsskrb5_register_acceptor_identity(minor_status, str);
	free(str);

	return maj_stat;

    } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) {
	char *str;

	maj_stat = get_string(minor_status, value, &str);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;
	if (str == NULL) {
	    *minor_status = 0;
	    return GSS_S_CALL_INACCESSIBLE_READ;
	}

	krb5_set_default_realm(context, str);
	free(str);

	*minor_status = 0;
	return GSS_S_COMPLETE;

    } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) {

	*minor_status = EINVAL;
	return GSS_S_FAILURE;

    } else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) {
	char *str;

	maj_stat = get_string(minor_status, value, &str);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;
	if (str == NULL) {
	    *minor_status = 0;
	    return GSS_S_CALL_INACCESSIBLE_READ;
	}

	*minor_status = krb5_cc_set_default_name(context, str);
	free(str);
	if (*minor_status)
	    return GSS_S_FAILURE;

	return GSS_S_COMPLETE;
    } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_TIME_OFFSET_X)) {
	OM_uint32 offset;
	time_t t;

	maj_stat = get_int32(minor_status, value, &offset);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;

	t = time(NULL) + offset;

	krb5_set_real_time(context, t, 0);

	*minor_status = 0;
	return GSS_S_COMPLETE;
    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_TIME_OFFSET_X)) {
	krb5_timestamp sec;
	int32_t usec;
	time_t t;

	t = time(NULL);

	krb5_us_timeofday (context, &sec, &usec);

	maj_stat = set_int32(minor_status, value, sec - t);
	if (maj_stat != GSS_S_COMPLETE)
	    return maj_stat;

	*minor_status = 0;
	return GSS_S_COMPLETE;
    } else if (gss_oid_equal(desired_object, GSS_KRB5_PLUGIN_REGISTER_X)) {
	struct gsskrb5_krb5_plugin c;

	if (value->length != sizeof(c)) {
	    *minor_status = EINVAL;
	    return GSS_S_FAILURE;
	}
	memcpy(&c, value->value, sizeof(c));
	krb5_plugin_register(context, c.type, c.name, c.symbol);

	*minor_status = 0;
	return GSS_S_COMPLETE;
    }

    *minor_status = EINVAL;
    return GSS_S_FAILURE;
}
Exemplo n.º 14
0
/*
 * VmDirLoginUser Creates a TGT cache for the Process to
 * communicate with the VmDir
*/
DWORD
VmDirKrb5LoginUser(
    PCSTR pszUserName,
    PCSTR pszPassword,
    PCSTR pszKrb5ConfPath /* Optional */
)
{
    DWORD dwError = 0;
    krb5_context context = NULL;
    krb5_get_init_creds_opt *opt = NULL;
    krb5_principal principal = NULL;
    krb5_ccache ccache = NULL;
    krb5_creds creds = { 0 };
    PSTR pszCacheName = NULL;

    if (!IsNullOrEmptyString(pszKrb5ConfPath))
    {
        setenv(KRB5_CONF_PATH, pszKrb5ConfPath, 1);
    }

    dwError = VmDirMapKrbError(
                  krb5_init_context(&context)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    dwError = VmDirMapKrbError(
                  krb5_get_init_creds_opt_alloc(context, &opt)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    krb5_get_init_creds_opt_set_tkt_life(opt, 8 * 60 * 60); //8 hours ticket
    krb5_get_init_creds_opt_set_forwardable(opt, 1);

    // Creates a File based Credential cache based on defaults
    dwError = VmDirMapKrbError(
                  krb5_cc_new_unique(
                      context,
                      "FILE",
                      "hint",
                      &ccache)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    // it is assumed that pszUserName is in user@REALM format.
    dwError = VmDirMapKrbError(
                  krb5_parse_name(context, pszUserName, &principal)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    // Let us get the Creds from the Kerberos Server
    dwError =  VmDirMapKrbError(
                   krb5_get_init_creds_password(
                       context,
                       &creds,
                       principal,
                       (PSTR) pszPassword,
                       NULL,
                       NULL,
                       0,
                       NULL,
                       opt)
               );
    BAIL_ON_VMDIR_ERROR(dwError);

    dwError = VmDirMapKrbError(
                  krb5_cc_initialize(context, ccache, principal)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    dwError = VmDirMapKrbError(
                  krb5_cc_store_cred(context, ccache, &creds)
              );
    BAIL_ON_VMDIR_ERROR(dwError);

    pszCacheName = (PSTR)krb5_cc_get_name(context, ccache);
    if ( pszCacheName == NULL)
    {
        dwError = ERROR_NO_CRED_CACHE_NAME;
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    // let us set the Value to the Env. Variable so GSSAPI can find it.
    setenv(KRB5_CC_NAME, pszCacheName, 1);

    krb5_cc_set_default_name(context, pszCacheName);

error:

    if (principal != NULL)
    {
        krb5_free_principal(context, principal);
    }

    if (opt != NULL)
    {
        krb5_get_init_creds_opt_free(context,opt);
    }

    krb5_free_cred_contents(context, &creds);

    if (context != NULL)
    {
        krb5_free_context(context);
    }

    return dwError;
}
Exemplo n.º 15
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;
}