#define KCMCACHE(X) ((kcm_ccache)(X)->data.data)
#define CACHENAME(X) (KCMCACHE(X)->name)
-static const char *
+static krb5_error_code
kcmss_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **col,
+ const char **sub)
{
- return CACHENAME(id);
+ if (name)
+ *name = CACHENAME(id);
+ if (col)
+ *col = NULL;
+ if (name)
+ *sub = CACHENAME(id);
+ return 0;
}
static krb5_error_code
-kcmss_resolve(krb5_context context, krb5_ccache *id, const char *res)
+kcmss_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
{
return KRB5_FCC_INTERNAL;
}
free(def_realm);
}
-static krb5_error_code
-get_switched_ccache(krb5_context context,
- const char * type,
- krb5_principal principal,
- krb5_ccache *ccache)
-{
- krb5_error_code ret;
-
-#ifdef _WIN32
- if (strcmp(type, "API") == 0) {
- /*
- * Windows stores the default ccache name in the
- * registry which is shared across multiple logon
- * sessions for the same user. The API credential
- * cache provides a unique name space per logon
- * session. Therefore there is no need to generate
- * a unique ccache name. Instead use the principal
- * name. This provides a friendlier user experience.
- */
- char * unparsed_name;
- char * cred_cache;
-
- ret = krb5_unparse_name(context, principal,
- &unparsed_name);
- if (ret)
- krb5_err(context, 1, ret,
- N_("unparsing principal name", ""));
-
- ret = asprintf(&cred_cache, "API:%s", unparsed_name);
- krb5_free_unparsed_name(context, unparsed_name);
- if (ret == -1 || cred_cache == NULL)
- krb5_err(context, 1, ret,
- N_("building credential cache name", ""));
-
- ret = krb5_cc_resolve(context, cred_cache, ccache);
- free(cred_cache);
- } else if (strcmp(type, "MSLSA") == 0) {
- /*
- * The Windows MSLSA cache when it is writeable
- * stores tickets for multiple client principals
- * in a single credential cache.
- */
- ret = krb5_cc_resolve(context, "MSLSA:", ccache);
- } else {
- ret = krb5_cc_new_unique(context, type, NULL, ccache);
- }
-#else /* !_WIN32 */
- ret = krb5_cc_new_unique(context, type, NULL, ccache);
-#endif /* _WIN32 */
-
- return ret;
-}
-
int
main(int argc, char **argv)
{
krb5_error_code ret;
krb5_context context;
- krb5_ccache ccache;
+ krb5_ccache ccache = NULL;
krb5_principal principal = NULL;
int optidx = 0;
krb5_deltat ticket_life = 0;
if (cred_cache)
ret = krb5_cc_resolve(context, cred_cache, &ccache);
- else {
- if (argc > 1) {
- char s[1024];
- ret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
- if (ret)
- krb5_err(context, 1, ret, "creating cred cache");
- snprintf(s, sizeof(s), "%s:%s",
- krb5_cc_get_type(context, ccache),
- krb5_cc_get_name(context, ccache));
- setenv("KRB5CCNAME", s, 1);
- unique_ccache = TRUE;
- } else {
- ret = krb5_cc_cache_match(context, principal, &ccache);
- if (ret) {
- const char *type;
- ret = krb5_cc_default(context, &ccache);
- if (ret)
- krb5_err(context, 1, ret,
- N_("resolving credentials cache", ""));
-
- /*
- * Check if the type support switching, and we do,
- * then do that instead over overwriting the current
- * default credential
- */
- type = krb5_cc_get_type(context, ccache);
- if (krb5_cc_support_switch(context, type)) {
- krb5_cc_close(context, ccache);
- ret = get_switched_ccache(context, type, principal,
- &ccache);
- if (ret == 0)
- unique_ccache = TRUE;
- }
- }
- }
- }
+ else
+ ret = krb5_cc_default_for(context, principal, &ccache);
if (ret)
krb5_err(context, 1, ret, N_("resolving credentials cache", ""));
gss_cred_usage_t * /* cred_usage_stored */
);
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_store_cred_into2(
+ OM_uint32 * /* minor_status */,
+ gss_const_cred_id_t /* input_cred_handle */,
+ gss_cred_usage_t /* input_usage */,
+ const gss_OID /* desired_mech */,
+ OM_uint32 /* store_cred_flags */,
+ gss_const_key_value_set_t /* cred_store */,
+ gss_OID_set * /* elements_stored */,
+ gss_cred_usage_t * /* cred_usage_stored */,
+ gss_buffer_set_t * /* env */
+ );
+
+enum gss_store_cred_flags {
+ GSS_C_STORE_CRED_DEFAULT = 1,
+ GSS_C_STORE_CRED_OVERWRITE = 2,
+ GSS_C_STORE_CRED_SET_PROCESS = 4,
+};
+#define GSS_C_STORE_CRED_DEFAULT GSS_C_STORE_CRED_DEFAULT
+#define GSS_C_STORE_CRED_OVERWRITE GSS_C_STORE_CRED_OVERWRITE
+#define GSS_C_STORE_CRED_SET_PROCESS GSS_C_STORE_CRED_SET_PROCESS
+
+
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_CALLCONV
gss_set_neg_mechs(
OM_uint32 * /* minor_status */,
gss_OID_set *elements_stored,
gss_cred_usage_t *cred_usage_stored);
+typedef OM_uint32 GSSAPI_CALLCONV
+_gss_store_cred_into2_t(OM_uint32 *minor_status,
+ gss_const_cred_id_t input_cred_handle,
+ gss_cred_usage_t input_usage,
+ gss_OID desired_mech,
+ OM_uint32 store_cred_flags,
+ gss_const_key_value_set_t cred_store,
+ gss_OID_set *elements_stored,
+ gss_cred_usage_t *cred_usage_stored,
+ gss_buffer_set_t *env);
+
typedef OM_uint32 GSSAPI_CALLCONV
_gss_set_neg_mechs_t(OM_uint32 *minor_status,
gss_cred_id_t cred_handle,
_gss_query_mechanism_info_t *gm_query_mechanism_info;
_gss_query_meta_data_t *gm_query_meta_data;
_gss_exchange_meta_data_t *gm_exchange_meta_data;
+ _gss_store_cred_into2_t *gm_store_cred_into2;
struct gss_mech_compat_desc_struct *gm_compat;
} gssapi_mech_interface_desc, *gssapi_mech_interface;
}
*delegated_cred_handle = NULL;
- kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
- NULL, &ccache);
+ kret = krb5_cc_resolve(context, "MEMORY:anonymous", &ccache);
if (kret) {
ctx->flags &= ~GSS_C_DELEG_FLAG;
goto out;
gsskrb5_cred handle;
ret = _gsskrb5_krb5_import_cred(minor_status,
- ccache,
+ &ccache,
NULL,
NULL,
delegated_cred_handle);
goto out;
handle = (gsskrb5_cred) *delegated_cred_handle;
-
handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
- krb5_cc_close(context, ccache);
- ccache = NULL;
}
out:
#endif
+/*
+ * WARNING: Takes ownership of `id'. Because MEMORY:anonymous is now not
+ * linked into to the MEMORY ccache namespace, we can't use krb5_cc_resolve()
+ * with the cache's name anymore. We need a krb5_cc_clone() or some such, with
+ * attendant new method for ccops. Or we could create a new MEMORY:anonymous
+ * ccache and copy all the creds from `id' into it. But we know callers of
+ * this function don't need `id' after calling it, so for now we'll just take
+ * ownershipd of it.
+ */
OM_uint32
_gsskrb5_krb5_import_cred(OM_uint32 *minor_status,
- krb5_ccache id,
+ krb5_ccache *id,
krb5_principal keytab_principal,
krb5_keytab keytab,
gss_cred_id_t *cred)
handle->usage = 0;
- if (id) {
+ if (*id) {
time_t now;
OM_uint32 left;
- char *str;
handle->usage |= GSS_C_INITIATE;
- kret = krb5_cc_get_principal(context, id,
+ kret = krb5_cc_get_principal(context, *id,
&handle->principal);
if (kret) {
free(handle);
krb5_timeofday(context, &now);
ret = __gsskrb5_ccache_lifetime(minor_status,
context,
- id,
+ *id,
handle->principal,
&left);
if (ret != GSS_S_COMPLETE) {
}
handle->endtime = now + left;
- kret = krb5_cc_get_full_name(context, id, &str);
- if (kret)
- goto out;
-
- kret = krb5_cc_resolve(context, str, &handle->ccache);
- free(str);
+ handle->ccache = *id;
+ *id = NULL;
if (kret)
goto out;
}
NULL, /* gm_query_mechanism_info */
NULL, /* gm_query_meta_data */
NULL, /* gm_exchange_meta_data */
+ _gsskrb5_store_cred_into2,
NULL /* gm_compat */
};
free(str);
str = NULL;
- major_stat = _gsskrb5_krb5_import_cred(minor_status, id, keytab_principal,
+ major_stat = _gsskrb5_krb5_import_cred(minor_status, &id, keytab_principal,
keytab, cred_handle);
out:
if (id)
return same;
}
-/*
- * Like krb5_cc_cache_match(), but only looking in the default collection.
- *
- * We need this to avoid looking for MEMORY ccaches, which risks matching the
- * same credential that we're storing. We could make sure that MEMORY ccaches
- * are searched for last in krb5_cc_cache_match(), then ignore any MEMORY
- * ccaches we find there, but, if we might then store in a ccache that will not
- * be found later as the default ccache, then it's not worth it.
- *
- * XXX In order to remove this, we'll first need to make sure that
- * krb5_cc_default() searches all collections when KRB5CCNAME is not set,
- * then we'll need to make sure that krb5_cc_cache_match() searches MEMORY
- * ccaches last (or else introduce a new ccache type like MEMORY but which
- * is never searched or searchable), then make sure that the caller below
- * treat finding a MEMORY the same as not finding a ccache at all.
- */
-static krb5_error_code
-ccache_match(krb5_context context,
- krb5_principal princ,
- const char *cctype,
- krb5_ccache *id)
+static OM_uint32
+add_env(OM_uint32 *minor,
+ gss_buffer_set_t *env,
+ const char *var,
+ const char *val)
{
- krb5_cc_cache_cursor cursor = NULL;
- krb5_error_code ret;
+ OM_uint32 major;
+ gss_buffer_desc b;
+ char *varval = NULL;
- *id = NULL;
- ret = krb5_cc_cache_get_first(context, cctype, &cursor);
- if (ret)
- return ret;
+ if (asprintf(&varval, "%s=%s", var, val) == -1 || varval == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
- while (krb5_cc_cache_next(context, cursor, id) == 0) {
- krb5_principal p = NULL;
+ b.value = varval;
+ b.length = strlen(varval) + 1;
+ major = gss_add_buffer_set_member(minor, &b, env);
+ free(varval);
+ return major;
+}
- ret = krb5_cc_get_principal(context, *id, &p);
- if (ret == 0 &&
- krb5_principal_compare(context, princ, p)) {
- krb5_free_principal(context, p);
- krb5_cc_cache_end_seq_get(context, cursor);
- return 0;
- }
- if (*id)
- krb5_cc_close(context, *id);
- *id = NULL;
- }
- krb5_cc_cache_end_seq_get(context, cursor);
- return KRB5_CC_END;
+static OM_uint32
+set_proc(OM_uint32 *minor, gss_buffer_set_t env)
+{
+ size_t i;
+
+ /*
+ * XXX On systems with setpag(), call setpag(). On WIN32... create a
+ * session, set the access token, ...?
+ */
+#ifndef WIN32
+ for (i = 0; i < env->count; i++)
+ putenv(env->elements[i].value);
+#endif
+ return GSS_S_COMPLETE;
}
OM_uint32 GSSAPI_CALLCONV
-_gsskrb5_store_cred_into(OM_uint32 *minor_status,
- gss_const_cred_id_t input_cred_handle,
- gss_cred_usage_t cred_usage,
- const gss_OID desired_mech,
- OM_uint32 overwrite_cred,
- OM_uint32 default_cred,
- gss_const_key_value_set_t cred_store,
- gss_OID_set *elements_stored,
- gss_cred_usage_t *cred_usage_stored)
+_gsskrb5_store_cred_into2(OM_uint32 *minor_status,
+ gss_const_cred_id_t input_cred_handle,
+ gss_cred_usage_t cred_usage,
+ const gss_OID desired_mech,
+ OM_uint32 store_cred_flags,
+ gss_const_key_value_set_t cred_store,
+ gss_OID_set *elements_stored,
+ gss_cred_usage_t *cred_usage_stored,
+ gss_buffer_set_t *envp)
{
krb5_context context;
krb5_error_code ret;
krb5_ccache id = NULL;
time_t exp_current;
time_t exp_new;
+ gss_buffer_set_t env = GSS_C_NO_BUFFER_SET;
const char *cs_ccache_name = NULL;
- OM_uint32 major_status;
+ OM_uint32 major_status, junk;
+ OM_uint32 overwrite_cred = store_cred_flags & GSS_C_STORE_CRED_OVERWRITE;
+ OM_uint32 default_cred = store_cred_flags & GSS_C_STORE_CRED_DEFAULT;
*minor_status = 0;
}
if (cs_ccache_name) {
- /*
- * Not the default ccache.
- *
- * Therefore not a collection type cache.
- *
- * Therefore there's no question of switching the primary ccache.
- *
- * Therefore we reset default_cred.
- *
- * XXX Perhaps we should fail in this case if default_cred is true.
- */
default_cred = 0;
- ret = krb5_cc_resolve(context, cs_ccache_name, &id);
+ ret = krb5_cc_resolve(context, cs_ccache_name, &id);
} else {
- const char *cctype = NULL;
-
/*
* Use the default ccache, and if it's a collection, switch it if
* default_cred is true.
*/
- ret = krb5_cc_default(context, &id);
- if (ret == 0) {
- cctype = krb5_cc_get_type(context, id);
- if (krb5_cc_support_switch(context, cctype)) {
- /* The default ccache is a collection type */
-
- krb5_cc_close(context, id);
- id = NULL;
-
- /* Find a matching ccache or create a new one */
- ret = ccache_match(context, input_cred->principal,
- cctype, &id);
- if (ret || id == NULL) {
- /* Since the ccache is new, just store unconditionally */
- overwrite_cred = 1;
- ret = krb5_cc_new_unique(context, cctype, NULL, &id);
- }
- }
- }
+ ret = krb5_cc_default_for(context, input_cred->principal, &id);
+ if (ret == 0 &&
+ krb5_cc_support_switch(context, krb5_cc_get_type(context, id)))
+ overwrite_cred = 1;
}
if (ret || id == NULL) {
return ret == 0 ? GSS_S_NO_CRED : GSS_S_FAILURE;
}
- /*
- * If the new creds are for a different principal than we had before,
- * overwrite.
- */
- if (!overwrite_cred && !same_princ(context, id, input_cred->ccache))
- overwrite_cred = 1;
-
- if (!overwrite_cred) {
+ if (!overwrite_cred && same_princ(context, id, input_cred->ccache)) {
/*
* If current creds are for the same princ as we already had creds for,
* and the new creds live longer than the old, overwrite.
NULL);
if (ret == 0 && default_cred)
krb5_cc_switch(context, id);
+
+ if ((store_cred_flags & GSS_C_STORE_CRED_SET_PROCESS) && envp == NULL)
+ envp = &env;
+ if (envp != NULL) {
+ char *fullname = NULL;
+
+ if ((ret = krb5_cc_get_full_name(context, id, &fullname)) == 0) {
+ major_status = add_env(minor_status, envp, "KRB5CCNAME", fullname);
+ free(fullname);
+ if (major_status)
+ ret = *minor_status;
+ }
+ }
(void) krb5_cc_close(context, id);
HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
+ if (ret == 0 && (store_cred_flags & GSS_C_STORE_CRED_SET_PROCESS) &&
+ (major_status = set_proc(minor_status, *envp)) != GSS_S_COMPLETE)
+ ret = *minor_status;
+ (void) gss_release_buffer_set(&junk, &env);
*minor_status = ret;
return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
}
+
+OM_uint32 GSSAPI_CALLCONV
+_gsskrb5_store_cred_into(OM_uint32 *minor_status,
+ gss_const_cred_id_t input_cred_handle,
+ gss_cred_usage_t cred_usage,
+ const gss_OID desired_mech,
+ OM_uint32 overwrite_cred,
+ OM_uint32 default_cred,
+ gss_const_key_value_set_t cred_store,
+ gss_OID_set *elements_stored,
+ gss_cred_usage_t *cred_usage_stored)
+{
+ OM_uint32 store_cred_flags =
+ (overwrite_cred ? GSS_C_STORE_CRED_OVERWRITE : 0) |
+ (default_cred ? GSS_C_STORE_CRED_DEFAULT : 0);
+
+ return _gsskrb5_store_cred_into2(minor_status, input_cred_handle,
+ cred_usage, desired_mech,
+ store_cred_flags, cred_store,
+ elements_stored, cred_usage_stored, NULL);
+}
gss_sign
gss_store_cred
gss_store_cred_into
+ gss_store_cred_into2
gss_test_oid_set_member
gss_unseal
gss_unwrap
gssapi_mech_interface m,
const struct _gss_mechanism_cred *mc,
gss_cred_usage_t input_usage,
- OM_uint32 overwrite_cred,
- OM_uint32 default_cred,
+ OM_uint32 store_cred_flags,
gss_const_key_value_set_t cred_store,
- gss_cred_usage_t *usage_stored)
+ gss_cred_usage_t *usage_stored,
+ gss_buffer_set_t *env)
{
OM_uint32 major_status;
-
- if (m->gm_store_cred_into)
+ OM_uint32 overwrite_cred =
+ !!(store_cred_flags & GSS_C_STORE_CRED_OVERWRITE);
+ OM_uint32 default_cred = !!(store_cred_flags & GSS_C_STORE_CRED_DEFAULT);
+
+ if (m->gm_store_cred_into2)
+ major_status = m->gm_store_cred_into2(minor_status, mc->gmc_cred,
+ input_usage, &m->gm_mech_oid,
+ store_cred_flags, cred_store,
+ NULL, usage_stored,
+ env);
+ else if (m->gm_store_cred_into)
major_status = m->gm_store_cred_into(minor_status, mc->gmc_cred,
input_usage, &m->gm_mech_oid,
overwrite_cred, default_cred,
* const key/value hashmap-like thing that specifies a credential store in a
* mechanism- and implementation-specific way, though Heimdal and MIT agree on
* at least the following keys for the Kerberos mechanism: ccache, keytab, and
- * client_keytab.
+ * client_keytab. A set of environment variables may be output as well
*/
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
-gss_store_cred_into(OM_uint32 *minor_status,
- gss_const_cred_id_t input_cred_handle,
- gss_cred_usage_t input_usage,
- const gss_OID desired_mech,
- OM_uint32 overwrite_cred,
- OM_uint32 default_cred,
- gss_const_key_value_set_t cred_store,
- gss_OID_set *elements_stored,
- gss_cred_usage_t *cred_usage_stored)
+gss_store_cred_into2(OM_uint32 *minor_status,
+ gss_const_cred_id_t input_cred_handle,
+ gss_cred_usage_t input_usage,
+ const gss_OID desired_mech,
+ OM_uint32 store_cred_flags,
+ gss_const_key_value_set_t cred_store,
+ gss_OID_set *elements_stored,
+ gss_cred_usage_t *cred_usage_stored,
+ gss_buffer_set_t *env)
{
- struct _gss_cred *cred = (struct _gss_cred *) input_cred_handle;
+ struct _gss_cred *cred = (struct _gss_cred *)input_cred_handle;
struct _gss_mechanism_cred *mc;
OM_uint32 major_status;
OM_uint32 minor;
size_t successes;
+ if (env != NULL)
+ *env = NULL;
+
if (input_cred_handle == NULL)
return GSS_S_CALL_INACCESSIBLE_READ;
!gss_oid_equal(&m->gm_mech_oid, desired_mech))
continue;
- major_status = store_mech_cred(minor_status, m, mc,
- input_usage, overwrite_cred,
- default_cred, cred_store,
- cred_usage_stored);
+ major_status = store_mech_cred(minor_status, m, mc, input_usage,
+ store_cred_flags, cred_store,
+ cred_usage_stored, env);
if (major_status == GSS_S_COMPLETE) {
if (elements_stored && desired_mech != GSS_C_NO_OID)
gss_add_oid_set_member(&minor, desired_mech, elements_stored);
return major_status;
}
+
+/*
+ * See RFC5588 for gss_store_cred(). This function is a variant that takes a
+ * const key/value hashmap-like thing that specifies a credential store in a
+ * mechanism- and implementation-specific way, though Heimdal and MIT agree on
+ * at least the following keys for the Kerberos mechanism: ccache, keytab, and
+ * client_keytab.
+ */
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_store_cred_into(OM_uint32 *minor_status,
+ gss_const_cred_id_t input_cred_handle,
+ gss_cred_usage_t input_usage,
+ const gss_OID desired_mech,
+ OM_uint32 overwrite_cred,
+ OM_uint32 default_cred,
+ gss_const_key_value_set_t cred_store,
+ gss_OID_set *elements_stored,
+ gss_cred_usage_t *cred_usage_stored)
+{
+ OM_uint32 store_cred_flags =
+ (overwrite_cred ? GSS_C_STORE_CRED_OVERWRITE : 0) |
+ (default_cred ? GSS_C_STORE_CRED_DEFAULT : 0);
+ return gss_store_cred_into2(minor_status, input_cred_handle, input_usage,
+ desired_mech, store_cred_flags, cred_store,
+ elements_stored, cred_usage_stored, NULL);
+}
NULL, /* gm_query_mechanism_info */
NULL, /* gm_query_meta_data */
NULL, /* gm_exchange_meta_data */
+ NULL, /* gm_store_cred_into2 */
NULL, /* gm_compat */
};
NULL, /* gm_query_mechanism_info */
NULL, /* gm_query_meta_data */
NULL, /* gm_exchange_meta_data */
+ NULL, /* gm_store_cred_into2 */
NULL /* gm_compat */
};
exit(exitval);
}
-static int version_flag = 0;
-static int help_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+static int env_flag = 0;
+static int def_flag = 0;
+static int overwrite_flag = 0;
static struct getargs args[] = {
{"version", 0, arg_flag, &version_flag, "print version", NULL },
- {"help", 0, arg_flag, &help_flag, NULL, NULL }
+ {"help", 0, arg_flag, &help_flag, NULL, NULL },
+ {"env", 'e', arg_flag, &env_flag,
+ "output env settings", NULL },
+ {"default", 0, arg_flag, &def_flag,
+ "switch credential store default principal", NULL },
+ {"overwrite", 0, arg_flag, &overwrite_flag,
+ "overwrite matching credential", NULL },
};
static void
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
gss_key_value_element_desc from_elements, to_elements;
gss_key_value_set_desc from, to;
+ gss_buffer_set_t env = GSS_C_NO_BUFFER_SET;
+ OM_uint32 store_flags = 0;
int optidx = 0;
setprogname(argv[0]);
exit(0);
}
+ if (def_flag)
+ store_flags |= GSS_C_STORE_CRED_DEFAULT;
+ if (overwrite_flag)
+ store_flags |= GSS_C_STORE_CRED_OVERWRITE;
+
argc -= optidx;
argv += optidx;
gss_err(1, major, minor, GSS_KRB5_MECHANISM,
"failed to acquire creds from %s", argv[0]);
- major = gss_store_cred_into(&minor, from_cred, GSS_C_INITIATE,
- GSS_KRB5_MECHANISM, 1, 1, &to, NULL, NULL);
+ major = gss_store_cred_into2(&minor, from_cred, GSS_C_INITIATE,
+ GSS_KRB5_MECHANISM, store_flags, &to, NULL,
+ NULL, env_flag ? &env : NULL);
if (major != GSS_S_COMPLETE)
gss_err(1, major, minor, GSS_KRB5_MECHANISM,
"failed to store creds into %s", argv[1]);
+ if (env_flag) {
+ size_t i;
+ int got_krb5ccname = 0;
+
+ if (env == GSS_C_NO_BUFFER_SET)
+ warnx("No environment settings");
+
+ for (i = 0; env != GSS_C_NO_BUFFER_SET && i < env->count; i++) {
+ got_krb5ccname = got_krb5ccname ||
+ (env->elements[i].length > sizeof("KRB5CCNAME=") &&
+ strncmp((const char *)env->elements[i].value, "KRB5CCNAME=",
+ sizeof("KRB5CCNAME=") - 1) == 0);
+ printf("%.*s\n", (int)env->elements[i].length,
+ (const char *)env->elements[i].value);
+ }
+ (void) gss_release_buffer_set(&minor, &env);
+
+ if (!got_krb5ccname)
+ errx(1, "KRB5CCNAME environment variable not set by "
+ "gss_store_cred_into2()");
+ }
+
(void) gss_release_cred(&minor, &from_cred);
(void) gss_release_cred(&minor, &to_cred);
gss_sign;
gss_store_cred;
gss_store_cred_into;
+ gss_store_cred_into2;
gss_test_oid_set_member;
gss_unseal;
gss_unwrap;
typedef struct krb5_acc {
char *cache_name;
+ char *cache_subsidiary;
cc_context_t context;
cc_ccache_t ccache;
} krb5_acc;
}
-static const char* KRB5_CALLCONV
+static krb5_error_code KRB5_CALLCONV
acc_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **colname,
+ const char **subsidiary)
{
+ krb5_error_code ret;
krb5_acc *a = ACACHE(id);
int32_t error;
- if (a->cache_name == NULL) {
- krb5_error_code ret;
- krb5_principal principal;
- char *name;
+ if (name)
+ *name = NULL;
+ if (colname)
+ *colname = NULL;
+ if (subsidiary)
+ *subsidiary = NULL;
+ if (a->cache_subsidiary == NULL) {
+ krb5_principal principal = NULL;
ret = _krb5_get_default_principal_local(context, &principal);
- if (ret)
- return NULL;
-
- ret = krb5_unparse_name(context, principal, &name);
+ if (ret == 0)
+ ret = krb5_unparse_name(context, principal, &a->cache_subsidiary);
krb5_free_principal(context, principal);
if (ret)
- return NULL;
-
- error = (*a->context->func->create_new_ccache)(a->context,
- cc_credentials_v5,
- name,
- &a->ccache);
- krb5_xfree(name);
- if (error)
- return NULL;
-
- error = get_cc_name(a);
- if (error)
- return NULL;
+ return ret;
}
- return a->cache_name;
+ if (a->cache_name == NULL) {
+ error = (*a->context->func->create_new_ccache)(a->context,
+ cc_credentials_v5,
+ a->cache_subsidiary,
+ &a->ccache);
+ if (error == ccNoError)
+ error = get_cc_name(a);
+ if (error != ccNoError)
+ ret = translate_cc_error(context, error);
+ }
+ if (name)
+ *name = a->cache_name;
+ if (colname)
+ *colname = "";
+ if (subsidiary)
+ *subsidiary = a->cache_subsidiary;
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
}
a = ACACHE(*id);
+ a->cache_subsidiary = NULL;
+ a->cache_name = NULL;
+ a->context = NULL;
+ a->ccache = NULL;
error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
if (error) {
return translate_cc_error(context, error);
}
- a->cache_name = NULL;
-
return 0;
}
static krb5_error_code KRB5_CALLCONV
-acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+acc_resolve(krb5_context context, krb5_ccache *id, const char *res, const char *sub)
{
krb5_error_code ret;
+ cc_time_t offset;
cc_int32 error;
krb5_acc *a;
+ char *s = NULL;
ret = acc_alloc(context, id);
if (ret)
a = ACACHE(*id);
- error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
- if (error == ccNoError) {
- cc_time_t offset;
- error = get_cc_name(a);
- if (error != ccNoError) {
+ if (sub) {
+ if (asprintf(&s, "%s:%s", res, sub) == -1 || s == NULL ||
+ (a->cache_subsidiary = strdup(sub)) == NULL) {
acc_close(context, *id);
- *id = NULL;
- return translate_cc_error(context, error);
- }
-
- error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
- cc_credentials_v5,
- &offset);
- if (error == 0)
- context->kdc_sec_offset = offset;
+ free(s);
+ return krb5_enomem(context);
+ }
+ res = s;
+ /*
+ * XXX With a bit of extra refactoring we could use the collection name
+ * as the path to the shared object implementing CCAPI... For now we
+ * ignore the collection name.
+ */
+ }
- } else if (error == ccErrCCacheNotFound) {
- a->ccache = NULL;
- a->cache_name = NULL;
- } else {
- *id = NULL;
- return translate_cc_error(context, error);
+ error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
+ if (error == ccNoError)
+ error = get_cc_name(a);
+ if (error != ccNoError) {
+ acc_close(context, *id);
+ *id = NULL;
+ free(s);
+ return translate_cc_error(context, error);
}
+ error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
+ cc_credentials_v5,
+ &offset);
+ if (error == 0)
+ context->kdc_sec_offset = offset;
+ free(s);
return 0;
}
static krb5_error_code KRB5_CALLCONV
acc_gen_new(krb5_context context, krb5_ccache *id)
{
- krb5_error_code ret;
- krb5_acc *a;
-
- ret = acc_alloc(context, id);
- if (ret)
- return ret;
-
- a = ACACHE(*id);
-
- a->ccache = NULL;
- a->cache_name = NULL;
-
- return 0;
+ return acc_alloc(context, id);
}
static krb5_error_code KRB5_CALLCONV
* Add a new ccache type with operations `ops', overwriting any
* existing one if `override'.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param ops type of plugin symbol
* @param override flag to select if the registration is to overide
* an existing ops with the same name.
*/
static krb5_error_code
-allocate_ccache (krb5_context context,
- const krb5_cc_ops *ops,
- const char *residual,
- krb5_ccache *id)
+allocate_ccache(krb5_context context,
+ const krb5_cc_ops *ops,
+ const char *residual,
+ const char *subsidiary,
+ krb5_ccache *id)
{
krb5_error_code ret;
#ifdef KRB5_USE_PATH_TOKENS
return ret;
}
- ret = (*id)->ops->resolve(context, id, residual);
+ ret = (*id)->ops->resolve(context, id, residual, subsidiary);
if(ret) {
free(*id);
*id = NULL;
* Find and allocate a ccache in `id' from the specification in `residual'.
* If the ccache name doesn't contain any colon, interpret it as a file name.
*
- * @param context a Keberos context.
+ * @param context a Kerberos context.
* @param name string name of a credential cache.
* @param id return pointer to a found credential cache.
*
if(strncmp(context->cc_ops[i]->prefix, name, prefix_len) == 0
&& name[prefix_len] == ':') {
return allocate_ccache (context, context->cc_ops[i],
- name + prefix_len + 1,
+ name + prefix_len + 1, NULL,
id);
}
}
if (is_possible_path_name(name))
- return allocate_ccache (context, &krb5_fcc_ops, name, id);
+ return allocate_ccache (context, &krb5_fcc_ops, name, NULL, id);
else {
krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
N_("unknown ccache type %s", "name"), name);
}
}
+static const char *
+get_default_cc_type(krb5_context context, int simple)
+{
+ const char *def_ccname;
+ const char *def_cctype =
+ krb5_config_get_string_default(context, NULL,
+ secure_getenv("KRB5CCTYPE"),
+ "libdefaults", "default_cc_type", NULL);
+
+ if (!simple &&
+ (def_ccname = krb5_cc_default_name(context))) {
+ size_t i;
+
+ for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
+ size_t prefix_len = strlen(context->cc_ops[i]->prefix);
+
+ if (!strncmp(context->cc_ops[i]->prefix, def_ccname, prefix_len) &&
+ def_ccname[prefix_len] == ':')
+ return context->cc_ops[i]->prefix;
+ }
+ if (is_possible_path_name(def_ccname))
+ return "FILE";
+ }
+ return def_cctype ? def_cctype : "DIR";
+}
+
+/**
+ * Find and allocate a ccache in `id' for the subsidiary cache named by
+ * `subsidiary' in the collection named by `collection'.
+ *
+ * @param context a Kerberos context.
+ * @param cctype string name of a credential cache collection type.
+ * @param collection string name of a credential cache collection.
+ * @param subsidiary string name of a credential cache in a collection.
+ * @param id return pointer to a found credential cache.
+ *
+ * @return Return 0 or an error code. In case of an error, id is set
+ * to NULL, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cc_resolve_sub(krb5_context context,
+ const char *cctype,
+ const char *collection,
+ const char *subsidiary,
+ krb5_ccache *id)
+{
+ size_t i;
+
+ *id = NULL;
+
+ if (!cctype && collection) {
+ /* Get the cctype from the collection, maybe */
+ for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
+ size_t plen = strlen(context->cc_ops[i]->prefix);
+
+ if ((strncmp(context->cc_ops[i]->prefix, collection, plen) ||
+ collection[plen] != ':'))
+ continue;
+ cctype = context->cc_ops[i]->prefix;
+ collection += plen + 1;
+ break;
+ }
+ }
+
+
+ if (!cctype) {
+ const char *def_cctype = get_default_cc_type(context, 0);
+ int might_be_path = collection && is_possible_path_name(collection);
+
+ if (def_cctype)
+ cctype = def_cctype;
+ else if (might_be_path && subsidiary)
+ cctype = "DIR"; /* Default to DIR */
+ else if (might_be_path && !subsidiary)
+ cctype = "FILE"; /* Default to FILE */
+ }
+
+ /* If either `cctype' is not NULL or `collection' starts with TYPE: */
+ if (cctype || collection) {
+ for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
+ size_t plen = strlen(context->cc_ops[i]->prefix);
+
+ if (cctype && strcmp(context->cc_ops[i]->prefix, cctype))
+ continue;
+ if (!cctype &&
+ (strncmp(context->cc_ops[i]->prefix, collection, plen) ||
+ collection[plen] != ':'))
+ continue;
+
+ return allocate_ccache(context, context->cc_ops[i],
+ cctype ? collection : collection + plen + 1,
+ subsidiary, id);
+ }
+ }
+
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ N_("unknown ccache type %s", ""),
+ cctype ? cctype : collection);
+ return KRB5_CC_UNKNOWN_TYPE;
+}
+
+
+/**
+ * Find and allocate a ccache in `id' from the specification in `residual', but
+ * specific to the given principal `principal' by using the principal name as
+ * the name of a "subsidiary" credentials cache in the collection named by
+ * `name'. If the ccache name doesn't contain any colon, interpret it as a
+ * file name.
+ *
+ * @param context a Kerberos context.
+ * @param name string name of a credential cache.
+ * @param principal principal name of desired credentials.
+ * @param id return pointer to a found credential cache.
+ *
+ * @return Return 0 or an error code. In case of an error, id is set
+ * to NULL, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cc_resolve_for(krb5_context context,
+ const char *cctype,
+ const char *name,
+ krb5_const_principal principal,
+ krb5_ccache *id)
+{
+ krb5_error_code ret;
+ char *p, *s;
+
+ *id = NULL;
+
+ ret = krb5_unparse_name(context, principal, &p);
+ if (ret)
+ return ret;
+ /* Subsidiary components cannot have ':'s in them */
+ for (s = strchr(p, ':'); s; s = strchr(s + 1, ':'))
+ *s = '-';
+ ret = krb5_cc_resolve_sub(context, cctype, name, p, id);
+ free(p);
+ return ret;
+}
+
/**
* Generates a new unique ccache of `type` in `id'. If `type' is NULL,
* the library chooses the default credential cache type. The supplied
krb5_cc_get_name(krb5_context context,
krb5_ccache id)
{
- return id->ops->get_name(context, id);
+ const char *name;
+
+ (void) id->ops->get_name(context, id, &name, NULL, NULL);
+ return name;
+}
+
+/**
+ * Return the name of the ccache collection associated with `id'
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_cc_get_collection(krb5_context context, krb5_ccache id)
+{
+ const char *name;
+
+ (void) id->ops->get_name(context, id, NULL, &name, NULL);
+ return name;
+}
+
+/**
+ * Return the name of the subsidiary ccache of `id'
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_cc_get_subsidiary(krb5_context context, krb5_ccache id)
+{
+ const char *name;
+
+ (void) id->ops->get_name(context, id, NULL, NULL, &name);
+ return name;
}
/**
/**
* Return the complete resolvable name the cache
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param id return pointer to a found credential cache
* @param str the returned name of a credential cache, free with krb5_xfree()
*
}
/* Else try a configured default ccache type's default */
- cfg = krb5_config_get_string(context, NULL, "libdefaults",
- "default_cc_type", NULL);
+ cfg = get_default_cc_type(context, 1);
if (cfg) {
const krb5_cc_ops *ops;
* @ingroup krb5_ccache
*/
-
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_default(krb5_context context,
krb5_ccache *id)
{
const char *p = krb5_cc_default_name(context);
+ *id = NULL;
if (p == NULL)
return krb5_enomem(context);
return krb5_cc_resolve(context, p, id);
}
+/**
+ * Open the named subsidiary cache from the default ccache collection in `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cc_default_sub(krb5_context context,
+ const char *subsidiary,
+ krb5_ccache *id)
+{
+ return krb5_cc_resolve_sub(context, get_default_cc_type(context, 0), NULL,
+ subsidiary, id);
+}
+
+/**
+ * Open the default ccache in `id' that corresponds to the given principal.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cc_default_for(krb5_context context,
+ krb5_const_principal principal,
+ krb5_ccache *id)
+{
+ return krb5_cc_resolve_for(context, get_default_cc_type(context, 0), NULL,
+ principal, id);
+}
+
/**
* Create a new ccache in `id' for `primary_principal'.
*
* Move the content from one credential cache to another. The
* operation is an atomic switch.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param from the credential cache to move the content from
* @param to the credential cache to move the content to
* principal (generated part of krb5_cc_set_config()). Returns FALSE
* (zero) if not a configuration principal.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param principal principal to check if it a configuration principal
*
* @ingroup krb5_ccache
* Store some configuration for the credential cache in the cache.
* Existing configuration under the same name is over-written.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param id the credential cache to store the data for
* @param principal configuration for a specific principal, if
* NULL, global for the whole cache.
/**
* Get some configuration for the credential cache in the cache.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param id the credential cache to store the data for
* @param principal configuration for a specific principal, if
* NULL, global for the whole cache.
* Get a new cache interation cursor that will interate over all
* credentials caches independent of type.
*
- * @param context a Keberos context
+ * @param context a Kerberos context
* @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
*
* @return Returns 0 or and error code, see krb5_get_error_message().
return KRB5_CC_END;
}
- return 0;
+ return ret;
}
/**
typedef struct krb5_dcache{
krb5_ccache fcache;
- char *dir;
char *name;
+ char *dir;
+ char *sub;
+ unsigned int default_candidate:1;
} krb5_dcache;
#define DCACHE(X) ((krb5_dcache*)(X)->data.data)
static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
+static krb5_error_code KRB5_CALLCONV dcc_set_default(krb5_context, krb5_ccache);
+/*
+ * Make subsidiary filesystem safe by mapping / and : to -. If the subsidiary
+ * is longer than 128 bytes, then truncate.
+ * In all cases, "tkt." is prefixed to be compatible with the DIR requirement
+ * that subsidiary ccache files be named tkt*.
+ *
+ * Thus host/foo.bar.baz@BAR.BAZ -> tkt.host-foo.bar.baz@BAR.BAZ.
+ *
+ * In particular, no filesystem component separators will be emitted, and . and
+ * .. will never be traversed.
+ */
+static krb5_error_code
+fs_encode_subsidiary(krb5_context context,
+ krb5_dcache *dc,
+ const char *subsidiary,
+ char **res)
+{
+ size_t len = strlen(subsidiary);
+ size_t i;
+
+ *res = NULL;
+ if (asprintf(res, "tkt.%s", subsidiary) == -1 || *res == NULL)
+ return krb5_enomem(context);
+ for (i = sizeof("tkt.") - 1; i < len; i++) {
+ switch ((*res)[i]) {
+#ifdef WIN32
+ case '\\': (*res)[0] = '-'; break;
+#endif
+ case '/': (*res)[0] = '-'; break;
+ case ':': (*res)[0] = '-'; break;
+ default: break;
+ }
+ }
+
+ /* Hopefully this will work on all filesystems */
+ if (len > 128 - sizeof("tkt.") - 1)
+ (*res)[127] = '\0';
+ return 0;
+}
static char *
primary_create(krb5_dcache *dc)
static int
is_filename_cacheish(const char *name)
{
- return strncmp(name, "tkt", 3) == 0;
-
+ size_t i;
+
+ if (strncmp(name, "tkt", sizeof("tkt") - 1))
+ return 0;
+ for (i = sizeof("tkt") - 1; name[i]; i++)
+ if (ISPATHSEP(name[i]))
+ return 0;
+ return 1;
}
static krb5_error_code
int fd = -1;
int asprintf_ret;
- if (!is_filename_cacheish(residual)) {
- krb5_set_error_message(context, KRB5_CC_FORMAT,
- "name %s is not a cache (doesn't start with tkt)", residual);
- return KRB5_CC_FORMAT;
- }
-
asprintf_ret = asprintf(&path, "%s/primary-XXXXXX", dc->dir);
if (asprintf_ret == -1 || path == NULL) {
return krb5_enomem(context);
}
static krb5_error_code
-get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
+get_default_cache(krb5_context context, krb5_dcache *dc,
+ const char *subsidiary, char **residual)
{
krb5_error_code ret;
char buf[MAXPATHLEN];
- char *primary;
+ char *primary = NULL;
FILE *f;
*residual = NULL;
+ if (subsidiary)
+ return fs_encode_subsidiary(context, dc, subsidiary, residual);
+
primary = primary_create(dc);
if (primary == NULL)
return krb5_enomem(context);
-static const char* KRB5_CALLCONV
+static krb5_error_code KRB5_CALLCONV
dcc_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **dir,
+ const char **sub)
{
krb5_dcache *dc = DCACHE(id);
- return dc->name;
+
+ if (name)
+ *name = dc->name;
+ if (dir)
+ *dir = dc->dir;
+ if (sub)
+ *sub = dc->sub;
+ return 0;
}
{
struct stat sb;
+ if (!path[0]) {
+ krb5_set_error_message(context, EINVAL,
+ N_("DIR empty directory component", ""));
+ return EINVAL;
+ }
+
if (stat(path, &sb) != 0) {
if (errno == ENOENT) {
/* XXX should use mkdirx_np() */
{
if (dc->fcache)
krb5_cc_close(context, dc->fcache);
- if (dc->dir)
- free(dc->dir);
- if (dc->name)
- free(dc->name);
+ free(dc->sub);
+ free(dc->dir);
+ free(dc->name);
memset(dc, 0, sizeof(*dc));
free(dc);
}
-static krb5_error_code KRB5_CALLCONV
-dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+static krb5_error_code
+get_default_dir(krb5_context context, char **res)
{
- char *filename = NULL;
krb5_error_code ret;
- krb5_dcache *dc;
- const char *p;
- int asprintf_ret;
+ char *s;
- p = res;
- do {
- p = strstr(p, "..");
- if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
- krb5_set_error_message(context, KRB5_CC_FORMAT,
- N_("Path contains a .. component", ""));
- return KRB5_CC_FORMAT;
- }
- if (p)
- p += 3;
- } while (p);
-
- dc = calloc(1, sizeof(*dc));
- if (dc == NULL) {
- krb5_set_error_message(context, KRB5_CC_NOMEM,
- N_("malloc: out of memory", ""));
- return KRB5_CC_NOMEM;
+ if ((ret = dcc_get_default_name(context, &s)))
+ return ret;
+ if (strncmp(s, "DIR:", sizeof("DIR:") - 1)) {
+ *res = s;
+ s = NULL;
+ } else if ((*res = strdup(s + sizeof("DIR:") - 1)) == NULL) {
+ ret = krb5_enomem(context);
}
-
- /* check for explicit component */
- if (res[0] == ':') {
- char *q;
+ free(s);
+ return ret;
+}
- dc->dir = strdup(&res[1]);
-#ifdef _WIN32
- q = strrchr(dc->dir, '\\');
- if (q == NULL)
+static krb5_error_code KRB5_CALLCONV
+dcc_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
+{
+ krb5_error_code ret;
+ krb5_dcache *dc = NULL;
+ char *filename = NULL;
+ size_t len;
+ int has_pathsep = 0;
+
+ if (sub) {
+ /*
+ * Here `res' has the directory name (or, if NULL, refers to the
+ * default DIR cccol), and `sub' has the "subsidiary" name, to which
+ * we'll prefix "tkt." (though we will insist only on "tkt" later).
+ */
+ if ((dc = calloc(1, sizeof(*dc))) == NULL ||
+ asprintf(&dc->sub, "tkt.%s", sub) == -1 || dc->sub == NULL) {
+ free(dc);
+ return krb5_enomem(context);
+ }
+ if (res && res[0] && (dc->dir = strdup(res)) == NULL) {
+ free(dc->sub);
+ free(dc);
+ return krb5_enomem(context);
+ } else if ((!res || !res[0]) && (ret = get_default_dir(context, &dc->dir))) {
+ free(dc->sub);
+ free(dc);
+ return ret;
+ }
+ } else {
+ const char *p;
+ int is_drive_letter_colon = 0;
+
+ /*
+ * Here `res' has whatever string followed "DIR:", and we need to parse
+ * it into `dc->dir' and `dc->sub'.
+ *
+ * Conventions we support for DIR cache naming:
+ *
+ * - DIR:path:NAME ---> FILE:path/tktNAME
+ * - DIR::path/tktNAME ---> FILE:path/tktNAME
+ * - DIR::NAME ---> FILE:${default_DIR_cccol_path}/tktNAME
+ * \-> FILE:/tmp/krb5cc_${uid}_dir/tktNAME
+ * - DIR:path ---> FILE:path/$(cat primary) or FILE:path/tkt
+ *
+ */
+
+ if (*res == '\0' || (res[0] == ':' && res[1] == '\0')) {
+ /* XXX Why not? */
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("\"DIR:\" is not a valid ccache name", ""));
+ return KRB5_CC_FORMAT;
+ }
+
+#ifdef WIN32
+ has_pathsep = strchr(res, '\\') != NULL;
#endif
- q = strrchr(dc->dir, '/');
- if (q) {
- *q++ = '\0';
- } else {
- krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
- dcc_release(context, dc);
- return KRB5_CC_FORMAT;
- }
-
- if (!is_filename_cacheish(q)) {
- krb5_set_error_message(context, KRB5_CC_FORMAT,
- N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
- dcc_release(context, dc);
- return KRB5_CC_FORMAT;
- }
-
- ret = verify_directory(context, dc->dir);
- if (ret) {
- dcc_release(context, dc);
- return ret;
- }
+ has_pathsep |= strchr(res, '/') != NULL;
- dc->name = strdup(res);
- if (dc->name == NULL) {
- dcc_release(context, dc);
- return krb5_enomem(context);
- }
-
- } else {
- char *residual;
- size_t len;
+ if ((dc = calloc(1, sizeof(*dc))) == NULL)
+ return krb5_enomem(context);
- dc->dir = strdup(res);
- if (dc->dir == NULL) {
- dcc_release(context, dc);
- return krb5_enomem(context);
- }
+ p = strrchr(res, ':');
+#ifdef WIN32
+ is_drive_letter_colon =
+ p && ((res[0] == ':' && res[1] != ':' && p - res == 2) ||
+ (res[0] != ':' && p - res == 1));
+#endif
- len = strlen(dc->dir);
+ if (res[0] != ':' && p && !is_drive_letter_colon) {
+ /* DIR:path:NAME */
+ if ((dc->dir = strndup(res, (p - res))) == NULL ||
+ asprintf(&dc->sub, "tkt.%s", p + 1) < 0 || dc->sub == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+ } else if (res[0] == ':' && has_pathsep) {
+ char *q;
+
+ /* DIR::path/tktNAME (the "tkt" must be there; we'll check) */
+ if ((dc->dir = strdup(&res[1])) == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+#ifdef _WIN32
+ q = strrchr(dc->dir, '\\');
+ if (q == NULL || ((p = strrchr(dc->dir, '/')) && q < p))
+#endif
+ q = strrchr(dc->dir, '/');
+ *q++ = '\0';
+ if ((dc->sub = strdup(q)) == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+ } else if (res[0] == ':') {
+ /* DIR::NAME -- no path component separators in NAME */
+ if ((ret = get_default_dir(context, &dc->dir))) {
+ dcc_release(context, dc);
+ return ret;
+ }
+ if (asprintf(&dc->sub, "tkt.%s", res + 1) < 0 || dc->sub == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+ } else {
+ /* DIR:path */
+ if ((dc->dir = strdup(res)) == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+
+ if ((ret = get_default_cache(context, dc, NULL, &dc->sub))) {
+ dcc_release(context, dc);
+ return ret;
+ }
+ }
+ }
- if (ISPATHSEP(dc->dir[len - 1]))
- dc->dir[len - 1] = '\0';
+ /* Strip off extra slashes on the end */
+ for (len = strlen(dc->dir);
+ len && ISPATHSEP(dc->dir[len - 1]);
+ len -= len ? 1 : 0)
+ dc->dir[len - 1] = '\0';
- ret = verify_directory(context, dc->dir);
- if (ret) {
- dcc_release(context, dc);
- return ret;
- }
+ /* If we got here then `dc->dir' and `dc->sub' must both be set */
- ret = get_default_cache(context, dc, &residual);
- if (ret) {
- dcc_release(context, dc);
- return ret;
- }
- asprintf_ret = asprintf(&dc->name, ":%s/%s", dc->dir, residual);
- free(residual);
- if (asprintf_ret == -1 || dc->name == NULL) {
- dc->name = NULL;
- dcc_release(context, dc);
- return krb5_enomem(context);
- }
+ if ((ret = verify_directory(context, dc->dir))) {
+ dcc_release(context, dc);
+ return ret;
}
-
- asprintf_ret = asprintf(&filename, "FILE%s", dc->name);
- if (asprintf_ret == -1 || filename == NULL) {
- dcc_release(context, dc);
- return krb5_enomem(context);
+ if (!is_filename_cacheish(dc->sub)) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Name %s is not a cache "
+ "(doesn't start with tkt)", ""), dc->sub);
+ dcc_release(context, dc);
+ return KRB5_CC_FORMAT;
+ }
+ if (asprintf(&dc->name, ":%s/%s", dc->dir, dc->sub) == -1 ||
+ dc->name == NULL ||
+ asprintf(&filename, "FILE%s", dc->name) == -1 || filename == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
}
ret = krb5_cc_resolve(context, filename, &dc->fcache);
return ret;
}
-
+ dc->default_candidate = 1;
(*id)->data.data = dc;
(*id)->data.length = sizeof(*dc);
return 0;
}
-static char *
-copy_default_dcc_cache(krb5_context context)
-{
- const char *defname;
- krb5_error_code ret;
- char *name = NULL;
- size_t len;
-
- len = strlen(krb5_dcc_ops.prefix);
-
- defname = krb5_cc_default_name(context);
- if (defname == NULL ||
- strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
- defname[len] != ':')
- {
- ret = dcc_get_default_name(context, &name);
- if (ret)
- return NULL;
-
- return name;
- } else {
- return strdup(&defname[len + 1]);
- }
-}
-
-
static krb5_error_code KRB5_CALLCONV
dcc_gen_new(krb5_context context, krb5_ccache *id)
{
krb5_error_code ret;
+ char *def_dir = NULL;
char *name = NULL;
- krb5_dcache *dc;
- int fd;
- size_t len;
- int asprintf_ret;
-
- name = copy_default_dcc_cache(context);
- if (name == NULL) {
- krb5_set_error_message(context, KRB5_CC_FORMAT,
- N_("Can't generate DIR caches unless its the default type", ""));
- return KRB5_CC_FORMAT;
- }
+ int fd = -1;
- len = strlen(krb5_dcc_ops.prefix);
- if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
- ++len;
- else
- len = 0;
+ ret = get_default_dir(context, &def_dir);
+ if (ret == 0)
+ ret = verify_directory(context, def_dir);
+ if (ret == 0 &&
+ (asprintf(&name, "DIR::%s/tktXXXXXX", def_dir) == -1 || name == NULL))
+ ret = krb5_enomem(context);
+ if (ret == 0 && (fd = mkstemp(name + sizeof("DIR::") - 1)) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = dcc_resolve(context, id, name + sizeof("DIR:") - 1, NULL);
- ret = dcc_resolve(context, id, name + len);
+ free(def_dir);
free(name);
- name = NULL;
- if (ret)
- return ret;
-
- dc = DCACHE((*id));
-
- asprintf_ret = asprintf(&name, ":%s/tktXXXXXX", dc->dir);
- if (asprintf_ret == -1 || name == NULL) {
- dcc_close(context, *id);
- return krb5_enomem(context);
- }
-
- fd = mkstemp(&name[1]);
- if (fd < 0) {
- dcc_close(context, *id);
- return krb5_enomem(context);
- }
- close(fd);
-
- free(dc->name);
- dc->name = name;
-
- return 0;
+ if (fd != -1)
+ close(fd);
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
dcc_close(krb5_context context,
krb5_ccache id)
{
+ krb5_dcache *dc = DCACHE(id);
+ krb5_principal p = NULL;
+ struct stat st;
+ char *primary = NULL;
+
+ /*
+ * If there's no default cache, but we're closing one, and the one we're
+ * closing has been initialized, then make it the default. This makes the
+ * first cache created the default.
+ *
+ * FIXME We should check if `D2FCACHE(dc)' has live credentials.
+ */
+ if (dc->default_candidate && D2FCACHE(dc) &&
+ krb5_cc_get_principal(context, D2FCACHE(dc), &p) == 0 &&
+ (primary = primary_create(dc)) &&
+ (stat(primary, &st) == -1 || !S_ISREG(st.st_mode) || st.st_size == 0))
+ dcc_set_default(context, id);
+ krb5_free_principal(context, p);
+ free(primary);
dcc_release(context, DCACHE(id));
return 0;
}
}
struct dcache_iter {
- int first;
+ char *primary;
krb5_dcache *dc;
+ DIR *d;
+ unsigned int first:1;
};
static krb5_error_code KRB5_CALLCONV
dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
{
- struct dcache_iter *iter;
- krb5_error_code ret;
- char *name;
+ struct dcache_iter *iter = NULL;
+ const char *name = krb5_cc_default_name(context);
+ size_t len;
+ char *p;
*cursor = NULL;
- iter = calloc(1, sizeof(*iter));
- if (iter == NULL)
- return krb5_enomem(context);
- iter->first = 1;
- name = copy_default_dcc_cache(context);
- if (name == NULL) {
- free(iter);
+ if (strncmp(name, "DIR:", sizeof("DIR:") - 1) != 0) {
krb5_set_error_message(context, KRB5_CC_FORMAT,
- N_("Can't generate DIR caches unless its the default type", ""));
+ N_("Can't list DIR caches unless its the default type", ""));
return KRB5_CC_FORMAT;
}
- ret = dcc_resolve(context, NULL, name);
- free(name);
- if (ret) {
+ if ((iter = calloc(1, sizeof(*iter))) == NULL ||
+ (iter->dc = calloc(1, sizeof(iter->dc[0]))) == NULL ||
+ (iter->dc->dir = strdup(name + sizeof("DIR:") - 1)) == NULL) {
+ if (iter)
+ free(iter->dc);
free(iter);
- return ret;
+ return krb5_enomem(context);
+ }
+ iter->first = 1;
+ p = strrchr(iter->dc->dir, ':');
+#ifdef WIN32
+ if (p == iter->dc->dir + 1)
+ p = NULL;
+#endif
+ if (p)
+ *p = '\0';
+
+ /* Strip off extra slashes on the end */
+ for (len = strlen(iter->dc->dir);
+ len && ISPATHSEP(iter->dc->dir[len - 1]);
+ len -= len ? 1 : 0) {
+ iter->dc->dir[len - 1] = '\0';
}
- /* XXX We need to opendir() here */
+ if ((iter->d = opendir(iter->dc->dir)) == NULL) {
+ free(iter->dc->dir);
+ free(iter->dc);
+ free(iter);
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Can't open DIR %s: %s", ""),
+ iter->dc->dir, strerror(errno));
+ return KRB5_CC_FORMAT;
+ }
*cursor = iter;
return 0;
dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
{
struct dcache_iter *iter = cursor;
+ krb5_error_code ret;
+ struct stat st;
+ struct dirent *dentry;
+ char *p = NULL;
+ *id = NULL;
if (iter == NULL)
return krb5_einval(context, 2);
- if (!iter->first) {
- krb5_clear_error_message(context);
- return KRB5_CC_END;
+ /* Emit primary subsidiary first */
+ if (iter->first &&
+ (ret = get_default_cache(context, iter->dc, NULL, &iter->primary)) == 0 &&
+ is_filename_cacheish(iter->primary)) {
+ iter->first = 0;
+ ret = KRB5_CC_END;
+ if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, iter->primary) > -1 && p != NULL &&
+ stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
+ ret = krb5_cc_resolve(context, p, id);
+ if (p == NULL)
+ return krb5_enomem(context);
+ free(p);
+ if (ret == 0)
+ return ret;
+ p = NULL;
}
- /* XXX We need to readdir() here */
iter->first = 0;
-
+ for (dentry = readdir(iter->d); dentry; dentry = readdir(iter->d)) {
+ if (!is_filename_cacheish(dentry->d_name) ||
+ (iter->primary && strcmp(dentry->d_name, iter->primary) == 0))
+ continue;
+ p = NULL;
+ ret = KRB5_CC_END;
+ if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, dentry->d_name) > -1 &&
+ p != NULL &&
+ stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
+ ret = krb5_cc_resolve(context, p, id);
+ free(p);
+ if (p == NULL)
+ return krb5_enomem(context);
+ if (ret == 0)
+ return ret;
+ }
return KRB5_CC_END;
}
if (iter == NULL)
return krb5_einval(context, 2);
- /* XXX We need to closedir() here */
- if (iter->dc)
- dcc_release(context, iter->dc);
+ (void) closedir(iter->d);
+ free(iter->dc->dir);
+ free(iter->dc);
+ free(iter->primary);
free(iter);
return 0;
}
{
krb5_dcache *dcfrom = DCACHE(from);
krb5_dcache *dcto = DCACHE(to);
+
+ dcfrom->default_candidate = 0;
+ dcto->default_candidate = 1;
return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
}
static krb5_error_code KRB5_CALLCONV
dcc_get_default_name(krb5_context context, char **str)
{
- return _krb5_expand_default_cc_name(context,
- KRB5_DEFAULT_CCNAME_DIR,
+ const char *def_cc_colname =
+ krb5_config_get_string_default(context, NULL, KRB5_DEFAULT_CCNAME_DIR,
+ "libdefaults", "default_cc_collection",
+ NULL);
+
+ /* What if def_cc_colname does not start with DIR:? We tolerate it. */
+ return _krb5_expand_default_cc_name(context, def_cc_colname,
str);
}
dcc_set_default(krb5_context context, krb5_ccache id)
{
krb5_dcache *dc = DCACHE(id);
- const char *name;
- name = krb5_cc_get_name(context, D2FCACHE(dc));
- if (name == NULL)
+ if (dc->sub == NULL)
return ENOENT;
-
- return set_default_cache(context, dc, name);
+ return set_default_cache(context, dc, dc->sub);
}
static krb5_error_code KRB5_CALLCONV
#define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
-static const char* KRB5_CALLCONV
+static krb5_error_code KRB5_CALLCONV
fcc_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **colname,
+ const char **sub)
{
if (FCACHE(id) == NULL)
- return NULL;
-
- return FILENAME(id);
+ return KRB5_CC_NOTFOUND;
+
+ if (name)
+ *name = FILENAME(id);
+ if (colname)
+ *colname = FILENAME(id);
+ if (sub)
+ *sub = NULL;
+ return 0;
}
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
fcc_lock(krb5_context context, krb5_ccache id,
int fd, krb5_boolean exclusive)
{
+ krb5_error_code ret;
+ const char *name;
+
if (exclusive == FALSE)
return 0;
- return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
+ ret = fcc_get_name(context, id, &name, NULL, NULL);
+ if (ret == 0)
+ ret = _krb5_xlock(context, fd, exclusive, name);
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
-fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+fcc_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
{
krb5_fcache *f;
+
+ if (sub && *sub) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("FILE ccache type is not a collection "
+ "type", ""));
+ return KRB5_CC_NOSUPP;
+ }
+
f = calloc(1, sizeof(*f));
if(f == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
f->version = 0;
(*id)->data.data = f;
(*id)->data.length = sizeof(*f);
+
return 0;
}
if (FCACHE(id) == NULL)
return krb5_einval(context, 2);
- if (TMPFILENAME(id)) {
+ if (TMPFILENAME(id))
(void) _krb5_erase_file(context, TMPFILENAME(id));
- free(TMPFILENAME(id));
- TMPFILENAME(id) = NULL;
- }
return _krb5_erase_file(context, FILENAME(id));
}
}
static krb5_error_code
-kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
+kcm_alloc(krb5_context context,
+ const char *name,
+ krb5_ccache *id)
{
krb5_kcmcache *k;
}
}
-static const char *
+static krb5_error_code KRB5_CALLCONV
kcm_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **col,
+ const char **sub)
{
- return CACHENAME(id);
+ /*
+ * TODO:
+ *
+ * - name should be <IPC-name>:<cache-name>
+ * - col should be <IPC-name>
+ * - sub should be <cache-name>
+ */
+ if (name)
+ *name = CACHENAME(id);
+ if (col)
+ *col = NULL;
+ if (sub)
+ *sub = CACHENAME(id);
+ return 0;
}
static krb5_error_code
-kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
+kcm_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
{
- return kcm_alloc(context, res, id);
+ /*
+ * For now, for KCM the `res' is the `sub'.
+ *
+ * TODO: We should use `res' as the IPC name instead of the one currently
+ * hard-coded in `kcm_ipc_name'.
+ */
+ return kcm_alloc(context, sub && *sub ? sub : res, id);
}
/*
typedef struct krb5_cc_cache_cursor_data *krb5_cc_cache_cursor;
-#define KRB5_CC_OPS_VERSION 3
+#define KRB5_CC_OPS_VERSION 4
typedef struct krb5_cc_ops {
int version;
const char *prefix;
- const char* (KRB5_CALLCONV * get_name)(krb5_context, krb5_ccache);
- krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, krb5_ccache *, const char *);
+ krb5_error_code (KRB5_CALLCONV * get_name)(krb5_context, krb5_ccache,
+ const char **, const char **,
+ const char **);
+ krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, krb5_ccache *, const char *,
+ const char *);
krb5_error_code (KRB5_CALLCONV * gen_new)(krb5_context, krb5_ccache *);
krb5_error_code (KRB5_CALLCONV * init)(krb5_context, krb5_ccache, krb5_principal);
krb5_error_code (KRB5_CALLCONV * destroy)(krb5_context, krb5_ccache);
*/
typedef struct _krb5_krcache {
char *krc_name; /* Name for this credentials cache */
+ char *krc_collection;
+ char *krc_subsidiary;
krb5_timestamp krc_changetime; /* update time, does not decrease (mutable) */
krb5_krcache_and_princ_id krc_id; /* cache and principal IDs (mutable) */
#define krc_cache_and_principal_id krc_id.krcu_cache_and_princ_id
collection_name = strdup(residual);
if (collection_name == NULL)
goto nomem;
-
- subsidiary_name = NULL;
} else {
collection_name = strndup(residual, sep - residual);
if (collection_name == NULL)
static krb5_error_code KRB5_CALLCONV
krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
{
+ krb5_krcache *data = KRCACHE(id);
krb5_error_code ret;
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
if (princ == NULL)
return KRB5_CC_BADNAME;
ret = initialize_internal(context, id, princ);
if (ret == 0)
- update_change_time(context, 0, KRCACHE(id));
+ update_change_time(context, 0, data);
return ret;
}
if (data == NULL)
return krb5_einval(context, 2);
+ free(data->krc_subsidiary);
+ free(data->krc_collection);
free(data->krc_name);
krb5_data_free(&id->data);
/* Create a keyring ccache handle for the given residual string. */
static krb5_error_code KRB5_CALLCONV
-krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
+krcc_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *residual,
+ const char *sub)
{
krb5_error_code ret;
key_serial_t collection_id, cache_id;
char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
ret = parse_residual(context, residual, &anchor_name, &collection_name,
- &subsidiary_name);
+ &subsidiary_name);
if (ret)
goto cleanup;
+ if (sub) {
+ free(subsidiary_name);
+ if ((subsidiary_name = strdup(sub)) == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ }
ret = get_collection(context, anchor_name, collection_name, &collection_id);
if (ret)
ret = make_subsidiary_residual(context, anchor_name, collection_name,
subsidiary_name, &data->krc_name);
- if (ret) {
+ if (ret ||
+ (data->krc_collection = strdup(collection_name)) == NULL ||
+ (data->krc_subsidiary = strdup(subsidiary_name)) == NULL) {
+ if (data) {
+ free(data->krc_collection);
+ free(data->krc_name);
+ }
free(data);
+ if (ret == 0)
+ ret = krb5_enomem(context);
return ret;
}
}
/* Return an alias to the residual string of the cache. */
-static const char *KRB5_CALLCONV
-krcc_get_name(krb5_context context, krb5_ccache id)
+static krb5_error_code KRB5_CALLCONV
+krcc_get_name(krb5_context context,
+ krb5_ccache id,
+ const char **name,
+ const char **collection_name,
+ const char **subsidiary_name)
{
- return KRCACHE(id)->krc_name;
+ krb5_krcache *data = KRCACHE(id);
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ if (name)
+ *name = data->krc_name;
+ if (collection_name)
+ *collection_name = data->krc_collection;
+ if (subsidiary_name)
+ *subsidiary_name = data->krc_subsidiary;
+ return 0;
}
/* Retrieve a copy of the default principal, if the cache is initialized. */
key_serial_t cache_id;
krb5_error_code ret;
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
heim_base_exchange_32(&cache_id, data->krc_cache_id);
ret = save_time_offsets(context, cache_id, (int32_t)offset, 0);
krb5_cc_copy_creds ;!
krb5_cc_copy_match_f
krb5_cc_default
+ krb5_cc_default_sub
+ krb5_cc_default_for
krb5_cc_default_name
krb5_cc_destroy
krb5_cc_end_seq_get
krb5_cc_register
krb5_cc_remove_cred
krb5_cc_resolve
+ krb5_cc_resolve_sub
+ krb5_cc_resolve_for
krb5_cc_retrieve_cred
krb5_cc_set_config
krb5_cc_set_default_name
typedef struct krb5_mcache {
char *name;
unsigned int refcnt;
- int dead;
+ unsigned int anonymous:1;
+ unsigned int dead:1;
krb5_principal primary_principal;
struct link {
krb5_creds cred;
#define MISDEAD(X) ((X)->dead)
-static const char* KRB5_CALLCONV
+static krb5_error_code KRB5_CALLCONV
mcc_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **col,
+ const char **sub)
{
- return MCACHE(id)->name;
+ if (name)
+ *name = MCACHE(id)->name;
+ if (col)
+ *col = NULL;
+ if (sub)
+ *sub = MCACHE(id)->name;
+ return 0;
}
-static krb5_mcache * KRB5_CALLCONV
-mcc_alloc(const char *name)
+static krb5_error_code
+mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
{
krb5_mcache *m, *m_c;
+ size_t counter = 0;
int ret = 0;
+ *out = NULL;
ALLOC(m, 1);
if(m == NULL)
- return NULL;
+ return krb5_enomem(context);
+
+again:
+ if (counter > 3) {
+ free(m->name);
+ free(m);
+ return EAGAIN; /* XXX */
+ }
if(name == NULL)
- ret = asprintf(&m->name, "%p", m);
+ ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
else
m->name = strdup(name);
if(ret < 0 || m->name == NULL) {
free(m);
- return NULL;
+ return krb5_enomem(context);
+ }
+ if (strcmp(m->name, "anonymous") == 0) {
+ m->anonymous = 1;
+ m->dead = 0;
+ m->refcnt = 1;
+ m->primary_principal = NULL;
+ m->creds = NULL;
+ m->mtime = time(NULL);
+ m->kdc_offset = 0;
+ m->next = NULL;
+ *out = m;
+ return 0;
}
+
/* check for dups first */
HEIMDAL_MUTEX_lock(&mcc_mutex);
for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
- if (strcmp(m->name, m_c->name) == 0)
- break;
+ if (strcmp(m->name, m_c->name) == 0)
+ break;
if (m_c) {
- free(m->name);
- free(m);
- HEIMDAL_MUTEX_unlock(&mcc_mutex);
- return NULL;
+ free(m->name);
+ free(m);
+ if (name) {
+ /* We raced with another thread to create this cache */
+ m = m_c;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ m->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ } else {
+ /* How likely are we to conflict on new_unique anyways?? */
+ counter++;
+ free(m->name);
+ m->name = NULL;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ goto again;
+ }
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ *out = m;
+ return 0;
}
+ m->anonymous = 0;
m->dead = 0;
m->refcnt = 1;
m->primary_principal = NULL;
HEIMDAL_MUTEX_init(&(m->mutex));
mcc_head = m;
HEIMDAL_MUTEX_unlock(&mcc_mutex);
- return m;
+ *out = m;
+ return 0;
}
static krb5_error_code KRB5_CALLCONV
-mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+mcc_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
{
+ krb5_error_code ret;
krb5_mcache *m;
- HEIMDAL_MUTEX_lock(&mcc_mutex);
- for (m = mcc_head; m != NULL; m = m->next)
- if (strcmp(m->name, res) == 0)
- break;
- HEIMDAL_MUTEX_unlock(&mcc_mutex);
-
- if (m != NULL) {
- HEIMDAL_MUTEX_lock(&(m->mutex));
- m->refcnt++;
- HEIMDAL_MUTEX_unlock(&(m->mutex));
- (*id)->data.data = m;
- (*id)->data.length = sizeof(*m);
- return 0;
- }
-
- m = mcc_alloc(res);
- if (m == NULL) {
- krb5_set_error_message(context, KRB5_CC_NOMEM,
- N_("malloc: out of memory", ""));
- return KRB5_CC_NOMEM;
- }
+ if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
+ return ret;
(*id)->data.data = m;
(*id)->data.length = sizeof(*m);
static krb5_error_code KRB5_CALLCONV
mcc_gen_new(krb5_context context, krb5_ccache *id)
{
+ krb5_error_code ret;
krb5_mcache *m;
- m = mcc_alloc(NULL);
-
- if (m == NULL) {
- krb5_set_error_message(context, KRB5_CC_NOMEM,
- N_("malloc: out of memory", ""));
- return KRB5_CC_NOMEM;
- }
+ if ((ret = mcc_alloc(context, NULL, &m)))
+ return ret;
(*id)->data.data = m;
(*id)->data.length = sizeof(*m);
return 0;
}
if (MISDEAD(m)) {
- free (m->name);
+ free(m->name);
HEIMDAL_MUTEX_unlock(&(m->mutex));
return 1;
}
{
krb5_mcache **n, *m = MCACHE(id);
+ if (m->anonymous) {
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (m->refcnt == 0) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ krb5_abortx(context, "mcc_destroy: refcnt already 0");
+ }
+ if (!MISDEAD(m))
+ mcc_destroy_internal(context, m);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+ }
+
HEIMDAL_MUTEX_lock(&mcc_mutex);
HEIMDAL_MUTEX_lock(&(m->mutex));
if (m->refcnt == 0)
}
l = malloc (sizeof(*l));
- if (l == NULL) {
- krb5_set_error_message(context, KRB5_CC_NOMEM,
- N_("malloc: out of memory", ""));
- HEIMDAL_MUTEX_unlock(&(m->mutex));
- return KRB5_CC_NOMEM;
- }
+ if (l == NULL)
+ return krb5_enomem(context);
l->next = m->creds;
m->creds = l;
memset (&l->cred, 0, sizeof(l->cred));
typedef struct krb5_scache {
char *name;
char *file;
+ char *sub;
sqlite3 *db;
sqlite_uint64 cid;
#else
#define KRB5_SCACHE_DB "/tmp/krb5scc_%{uid}"
#endif
-#define KRB5_SCACHE_NAME "SCC:" SCACHE_DEF_NAME ":" KRB5_SCACHE_DB
+#define KRB5_SCACHE_NAME "SCC:" KRB5_SCACHE_DB ":" SCACHE_DEF_NAME
#define SCACHE_INVALID_CID ((sqlite_uint64)-1)
#define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
#define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
#define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
-#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?"
+#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=? OR " \
+ "(PRINCIPAL IS NOT NULL AND PRINCIPAL=?)"
#define SQL_CCREDS "" \
"CREATE TABLE credentials (" \
static void
scc_free(krb5_scache *s)
{
+ if (!s)
+ return;
if (s->file)
free(s->file);
+ if (s->sub)
+ free(s->sub);
if (s->name)
free(s->name);
}
static krb5_error_code
-default_db(krb5_context context, sqlite3 **db)
+default_db(krb5_context context, const char *name, sqlite3 **db, char **file)
{
- char *name;
+ char *s = NULL;
+ char *f = NULL;
int ret;
- ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name);
- if (ret)
- return ret;
+ if (file)
+ *file = NULL;
+
+ if (name == NULL) {
+ if ((name = krb5_cc_default_name(context))) {
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+ }
+ if (name == NULL) {
+ ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s);
+ if (ret)
+ return ret;
+ name = s;
+ }
+ }
- ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL);
- free(name);
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+
+ if ((f = strdup(name)) == NULL) {
+ free(s);
+ return krb5_enomem(context);
+ }
+ free(s);
+
+ if ((s = strrchr(f, ':')))
+ *s = '\0';
+
+ ret = sqlite3_open_v2(f, db, SQLITE_OPEN_READWRITE, NULL);
if (ret != SQLITE_OK) {
+ sqlite3_close_v2(*db);
krb5_clear_error_message(context);
+ free(f);
return ENOENT;
}
+ if (file)
+ *file = f;
+ else
+ free(f);
+
#ifdef TRACEME
sqlite3_trace(*db, trace, NULL);
#endif
}
static krb5_error_code
-get_def_name(krb5_context context, char **str)
+get_def_name(krb5_context context, char *filein, char **str, char **file)
{
krb5_error_code ret;
sqlite3_stmt *stmt;
const char *name;
sqlite3 *db;
- ret = default_db(context, &db);
+ ret = default_db(context, filein, &db, file);
if (ret)
return ret;
static krb5_scache * KRB5_CALLCONV
-scc_alloc(krb5_context context, const char *name)
+scc_alloc(krb5_context context,
+ const char *name,
+ const char *sub,
+ int new_unique)
{
- krb5_error_code ret;
+ krb5_error_code ret = 0;
krb5_scache *s;
+ char *freeme = NULL;
+ char *subsidiary;
ALLOC(s, 1);
if(s == NULL)
s->cid = SCACHE_INVALID_CID;
- if (name) {
- char *file;
-
- if (*name == '\0') {
- ret = get_def_name(context, &s->name);
- if (ret)
- s->name = strdup(SCACHE_DEF_NAME);
- } else
- s->name = strdup(name);
-
- file = strrchr(s->name, ':');
- if (file) {
- *file++ = '\0';
- s->file = strdup(file);
- ret = 0;
- } else {
- ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
- }
+ if (name && *name && sub && *sub) {
+ if ((s->sub = strdup(sub)) == NULL ||
+ (s->file = strdup(name)) == NULL) {
+ free(s->file);
+ free(s);
+ (void) krb5_enomem(context);
+ return NULL;
+ }
} else {
- _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file);
- ret = asprintf(&s->name, "unique-%p", s);
+ s->sub = NULL;
+ s->file = NULL;
+ s->name = NULL;
+
+ if (name == NULL)
+ name = krb5_cc_default_name(context);
+ if (name == NULL) {
+ ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB,
+ &freeme);
+ if (ret) {
+ free(s);
+ return NULL;
+ }
+ name = freeme;
+ }
+
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+
+ if ((s->file = strdup(name)) == NULL) {
+ (void) krb5_enomem(context);
+ scc_free(s);
+ free(freeme);
+ return NULL;
+ }
+
+ if ((subsidiary = strrchr(s->file, ':'))) {
+#ifdef WIN32
+ if (subsidiary == &s->file + 1)
+ subsidiary = NULL;
+ else
+#endif
+ *(subsidiary++) = '\0';
+ }
+
+ if (new_unique) {
+ ret = asprintf(&s->sub, "unique-%p", s) < 0 || s->sub == NULL ?
+ krb5_enomem(context) : 0;
+ } else if (subsidiary == NULL || *subsidiary == '\0') {
+ ret = get_def_name(context, s->file, &s->sub, NULL);
+ if (ret) {
+ if ((s->sub = strdup(SCACHE_DEF_NAME)) == NULL)
+ ret = krb5_enomem(context);
+ else
+ ret = 0;
+ }
+ } else if ((s->sub = strdup(subsidiary)) == NULL) {
+ ret = krb5_enomem(context);
+ }
}
- if (ret < 0 || s->file == NULL || s->name == NULL) {
+
+ if (ret == 0 && s->file && s->sub &&
+ (asprintf(&s->name, "%s:%s", s->file, s->sub) < 0 || s->name == NULL))
+ ret = krb5_enomem(context);
+ if (ret || s->file == NULL || s->sub == NULL || s->name == NULL) {
scc_free(s);
+ free(freeme);
return NULL;
}
-
return s;
}
static krb5_error_code
open_database(krb5_context context, krb5_scache *s, int flags)
{
+ struct stat st;
int ret;
+
+ if (!(flags & SQLITE_OPEN_CREATE) && stat(s->file, &st) == 0 &&
+ st.st_size == 0)
+ return ENOENT;
ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
if (ret) {
if (s->db) {
{
int ret;
- sqlite3_bind_text(s->icache, 1, s->name, -1, NULL);
+ sqlite3_bind_text(s->icache, 1, s->sub, -1, NULL);
do {
ret = sqlite3_step(s->icache);
} while (ret == SQLITE_ROW);
*
*/
-static const char* KRB5_CALLCONV
+static krb5_error_code KRB5_CALLCONV
scc_get_name(krb5_context context,
- krb5_ccache id)
+ krb5_ccache id,
+ const char **name,
+ const char **file,
+ const char **sub)
{
- return SCACHE(id)->name;
+ if (name)
+ *name = SCACHE(id)->name;
+ if (file)
+ *file = SCACHE(id)->file;
+ if (sub)
+ *sub = SCACHE(id)->sub;
+ return 0;
}
static krb5_error_code KRB5_CALLCONV
-scc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+scc_resolve(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
{
+ krb5_error_code ret;
krb5_scache *s;
- int ret;
- s = scc_alloc(context, res);
+ s = scc_alloc(context, res, sub, 0);
if (s == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", ""));
return ret;
}
- ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL);
+ ret = sqlite3_bind_text(s->scache_name, 1, s->sub, -1, NULL);
if (ret != SQLITE_OK) {
- krb5_set_error_message(context, ENOMEM,
- "bind name: %s", sqlite3_errmsg(s->db));
- scc_free(s);
- return ENOMEM;
+ krb5_set_error_message(context, ENOMEM,
+ "bind principal: %s", sqlite3_errmsg(s->db));
+ scc_free(s);
+ return ENOMEM;
}
if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
{
krb5_scache *s;
- s = scc_alloc(context, NULL);
+ s = scc_alloc(context, NULL, NULL, 1);
if (s == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
static krb5_error_code KRB5_CALLCONV
scc_initialize(krb5_context context,
krb5_ccache id,
- krb5_principal primary_principal)
+ krb5_principal principal)
{
krb5_scache *s = SCACHE(id);
krb5_error_code ret;
}
}
- ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal);
+ ret = bind_principal(context, s->db, s->ucachep, 1, principal);
if (ret)
goto rollback;
sqlite3_bind_int(s->ucachep, 2, s->cid);
if (sqlite3_step(s->scache) != SQLITE_ROW) {
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
- N_("No principal for cache SCC:%s:%s", ""),
- s->name, s->file);
+ N_("No principal for cache SCC:%s", ""),
+ s->name);
return KRB5_CC_END;
}
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
N_("Principal data of wrong type "
- "for SCC:%s:%s", ""),
- s->name, s->file);
+ "for SCC:%s", ""),
+ s->name);
return KRB5_CC_END;
}
if (str == NULL) {
sqlite3_reset(s->scache);
krb5_set_error_message(context, KRB5_CC_END,
- N_("Principal not set for SCC:%s:%s", ""),
- s->name, s->file);
+ N_("Principal not set for SCC:%s", ""),
+ s->name);
return KRB5_CC_END;
}
if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
krb5_set_error_message(context, KRB5_CC_END,
- N_("credential of wrong type for SCC:%s:%s", ""),
- s->name, s->file);
+ N_("credential of wrong type for SCC:%s", ""),
+ s->name);
sqlite3_reset(ctx->credstmt);
return KRB5_CC_END;
}
ret = KRB5_CC_END;
krb5_set_error_message(context, ret,
N_("Credential of wrong type "
- "for SCC:%s:%s", ""),
- s->name, s->file);
+ "for SCC:%s", ""),
+ s->name);
break;
}
struct cache_iter {
char *drop;
+ char *file;
sqlite3 *db;
sqlite3_stmt *stmt;
};
if (ctx == NULL)
return krb5_enomem(context);
- ret = default_db(context, &ctx->db);
- if (ctx->db == NULL) {
+ ret = default_db(context, NULL, &ctx->db, &ctx->file);
+ if (ret) {
free(ctx);
return ret;
}
ret = asprintf(&name, "cacheIteration%pPid%d",
ctx, (int)getpid());
if (ret < 0 || name == NULL) {
- sqlite3_close(ctx->db);
- free(ctx);
- return krb5_enomem(context);
+ sqlite3_close(ctx->db);
+ free(ctx);
+ return krb5_enomem(context);
}
ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
if (ret < 0 || ctx->drop == NULL) {
- sqlite3_close(ctx->db);
- free(name);
- free(ctx);
- return krb5_enomem(context);
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx);
+ return krb5_enomem(context);
}
ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
- name);
+ name);
if (ret < 0 || str == NULL) {
- sqlite3_close(ctx->db);
- free(name);
- free(ctx->drop);
- free(ctx);
- return krb5_enomem(context);
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return krb5_enomem(context);
}
ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
free(str);
str = NULL;
if (ret) {
- sqlite3_close(ctx->db);
- free(name);
- free(ctx->drop);
- free(ctx);
- return ret;
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return ret;
}
ret = asprintf(&str, "SELECT name FROM %s", name);
if (ret < 0 || str == NULL) {
- exec_stmt(context, ctx->db, ctx->drop, 0);
- sqlite3_close(ctx->db);
- free(name);
- free(ctx->drop);
- free(ctx);
- return krb5_enomem(context);
+ exec_stmt(context, ctx->db, ctx->drop, 0);
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return krb5_enomem(context);
}
free(name);
goto again;
ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
- if (ret)
- return ret;
-
- return scc_resolve(context, id, name);
+ if (ret == 0)
+ ret = scc_resolve(context, id, ctx->file, name);
+ if (ret) {
+ free(*id);
+ *id = NULL;
+ }
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
exec_stmt(context, ctx->db, ctx->drop, 0);
sqlite3_finalize(ctx->stmt);
sqlite3_close(ctx->db);
+ free(ctx->file);
free(ctx->drop);
free(ctx);
return 0;
}
}
- sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL);
+ sqlite3_bind_text(sfrom->ucachen, 1, sto->sub, -1, NULL);
sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
do {
static krb5_error_code KRB5_CALLCONV
scc_get_default_name(krb5_context context, char **str)
{
- krb5_error_code ret;
- char *name;
-
*str = NULL;
-
- ret = get_def_name(context, &name);
- if (ret)
- return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
-
- ret = asprintf(str, "SCC:%s", name);
- free(name);
- if (ret < 0 || *str == NULL)
- return krb5_enomem(context);
- return 0;
+ return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
}
static krb5_error_code KRB5_CALLCONV
return KRB5_CC_IO;
}
- ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL);
+ ret = sqlite3_bind_text(s->umaster, 1, s->sub, -1, NULL);
if (ret) {
sqlite3_reset(s->umaster);
krb5_set_error_message(context, KRB5_CC_IO,
krb5_free_principal(context, p);
}
+static krb5_error_code
+test_cccol(krb5_context context, const char *def_cccol, const char **what)
+{
+ krb5_cc_cache_cursor cursor;
+ krb5_error_code ret;
+ krb5_principal p1, p2;
+ krb5_ccache id, id1, id2;
+ krb5_creds cred1, cred2;
+ size_t match1 = 0;
+ size_t match2 = 0;
+
+ memset(&cred1, 0, sizeof(cred1));
+ memset(&cred2, 0, sizeof(cred2));
+
+ *what = "krb5_parse_name";
+ ret = krb5_parse_name(context, "krbtgt/SU.SE@SU.SE", &cred1.server);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "lha@SU.SE", &cred1.client);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "krbtgt/H5L.SE@H5L.SE", &cred2.server);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "lha@H5L.SE", &cred2.client);
+ if (ret) return ret;
+ *what = "krb5_cc_set_default_name";
+ ret = krb5_cc_set_default_name(context, def_cccol);
+ if (ret) return ret;
+ *what = "krb5_cc_default";
+ ret = krb5_cc_default(context, &id1);
+ if (ret) return ret;
+ *what = "krb5_cc_initialize";
+ ret = krb5_cc_initialize(context, id1, cred1.client);
+ if (ret) return ret;
+ *what = "krb5_cc_store_cred";
+ ret = krb5_cc_store_cred(context, id1, &cred1);
+ if (ret) return ret;
+ *what = "krb5_cc_resolve";
+ ret = krb5_cc_resolve_for(context, NULL, def_cccol, cred2.client, &id2);
+ if (ret) return ret;
+ *what = "krb5_cc_initialize";
+ ret = krb5_cc_initialize(context, id2, cred2.client);
+ if (ret) return ret;
+ *what = "krb5_cc_store_cred";
+ ret = krb5_cc_store_cred(context, id2, &cred2);
+ if (ret) return ret;
+
+ krb5_cc_close(context, id1);
+ krb5_cc_close(context, id2);
+ id1 = id2 = NULL;
+
+ *what = "krb5_cc_default";
+ ret = krb5_cc_default(context, &id1);
+ if (ret) return ret;
+ *what = "krb5_cc_resolve";
+ ret = krb5_cc_resolve_for(context, NULL, def_cccol, cred2.client, &id2);
+ if (ret) return ret;
+
+ *what = "krb5_cc_get_principal";
+ ret = krb5_cc_get_principal(context, id1, &p1);
+ if (ret) return ret;
+ ret = krb5_cc_get_principal(context, id2, &p2);
+ if (ret) return ret;
+
+ if (!krb5_principal_compare(context, p1, cred1.client)) {
+ char *u1 = NULL;
+ char *u2 = NULL;
+
+ (void) krb5_unparse_name(context, p1, &u1);
+ (void) krb5_unparse_name(context, cred1.client, &u2);
+ warnx("Inconsistent principals for ccaches in %s: %s vs %s "
+ "(expected lha@SU.SE)", def_cccol, u1, u2);
+ return EINVAL;
+ }
+ if (!krb5_principal_compare(context, p2, cred2.client)) {
+ char *u1 = NULL;
+ char *u2 = NULL;
+
+ (void) krb5_unparse_name(context, p2, &u1);
+ (void) krb5_unparse_name(context, cred2.client, &u2);
+ warnx("Inconsistent principals for ccaches in %s: %s and %s "
+ "(expected lha@H5L.SE)", def_cccol, u1, u2);
+ return EINVAL;
+ }
+ krb5_free_principal(context, p1);
+ krb5_free_principal(context, p2);
+
+ *what = "krb5_cc_cache_get_first";
+ ret = krb5_cc_cache_get_first(context, NULL, &cursor);
+ if (ret) return ret;
+ *what = "krb5_cc_cache_next";
+ while (krb5_cc_cache_next(context, cursor, &id) == 0) {
+ krb5_principal p;
+
+ *what = "krb5_cc_get_principal";
+ ret = krb5_cc_get_principal(context, id, &p);
+ if (ret) return ret;
+ if (krb5_principal_compare(context, p, cred1.client))
+ match1++;
+ else if (krb5_principal_compare(context, p, cred2.client))
+ match2++;
+ krb5_free_principal(context, p);
+ krb5_cc_close(context, id);
+ }
+ (void) krb5_cc_cache_end_seq_get(context, cursor);
+
+ *what = "cccol iteration inconsistency";
+ if (match1 != 1 || match2 != 1) return EINVAL;
+
+ krb5_cc_close(context, id1);
+ krb5_cc_close(context, id2);
+
+ krb5_free_cred_contents(context, &cred1);
+ krb5_free_cred_contents(context, &cred2);
+
+ return 0;
+}
+
+static void
+test_cccol_dcache(krb5_context context)
+{
+ krb5_error_code ret;
+ char template[sizeof("DIR:dcache-XXXXXX")];
+ char *s;
+ const char *what;
+
+ memcpy(template, "DIR:dcache-XXXXXX", sizeof("DIR:dcache-XXXXXX"));
+ if (mkdtemp(template + sizeof("DIR:") - 1) == NULL)
+ krb5_err(context, 1, errno, "mkdtemp");
+
+ ret = test_cccol(context, template, &what);
+
+ if (asprintf(&s, "%s/primary", template + sizeof("DIR:") - 1) > 0) {
+ (void) unlink(s);
+ free(s);
+ }
+ if (asprintf(&s, "%s/tkt", template + sizeof("DIR:") - 1) > 0) {
+ (void) unlink(s);
+ free(s);
+ }
+ if (asprintf(&s, "%s/tkt.lha@H5L.SE", template + sizeof("DIR:") - 1) > 0) {
+ (void) unlink(s);
+ free(s);
+ }
+ if (asprintf(&s, "%s/tkt.lha@SU.SE", template + sizeof("DIR:") - 1) > 0) {
+ (void) unlink(s);
+ free(s);
+ }
+ (void) rmdir(template + sizeof("DIR:") - 1); /* XXX Check that this succeeds */
+ if (ret)
+ krb5_err(context, 1, errno, "%s", what);
+}
+
+static void
+test_cccol_scache(krb5_context context)
+{
+ krb5_error_code ret;
+ char template[sizeof("SCC:scache-XXXXXX")];
+ const char *what;
+ int fd;
+
+ memcpy(template, "SCC:scache-XXXXXX", sizeof("SCC:scache-XXXXXX"));
+ if ((fd = mkstemp(template + sizeof("SCC:") - 1)) == -1)
+ krb5_err(context, 1, errno, "mkstemp");
+ (void) close(fd);
+
+ ret = test_cccol(context, template, &what);
+ (void) unlink(template + sizeof("SCC:") - 1);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+}
+
static struct getargs args[] = {
{"debug", 'd', arg_flag, &debug_flag,
test_default_name(context);
test_mcache(context);
+ /*
+ * XXX Make sure to set default ccache names for each cc type!
+ * Otherwise we clobber the user's ccaches.
+ */
test_init_vs_destroy(context, krb5_cc_type_memory);
test_init_vs_destroy(context, krb5_cc_type_file);
#if 0
test_cache_find(context, "lha@SU.SE", 1);
test_cache_find(context, "hulabundulahotentot@SU.SE", 0);
+ /*
+ * XXX We should compose and krb5_cc_set_default_name() a default ccache
+ * for each cc type that we test with test_cache_iter(), and we should do
+ * that inside test_cache_iter().
+ *
+ * Alternatively we should remove test_cache_iter() in favor of
+ * test_cccol(), which is a much more complete test.
+ */
test_cache_iter(context, krb5_cc_type_memory, 0);
test_cache_iter(context, krb5_cc_type_memory, 1);
test_cache_iter(context, krb5_cc_type_memory, 0);
test_cc_config(context, "MEMORY", "bar", 1000); /* 1000 because fast */
test_cc_config(context, "FILE", "/tmp/foocc", 30); /* 30 because slower */
+ test_cccol_dcache(context);
+ test_cccol_scache(context);
+#ifdef HAVE_KEYUTILS_H
+ {
+ const char *what;
+
+ ret = test_cccol(context, "KEYRING:legacy:fooccol", &what);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+
+ ret = test_cccol(context, "MEMORY:fooccol", &what);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+ }
+#endif /* HAVE_KEYUTILS_H */
+
krb5_free_context(context);
#if 0
{ "default_client_keytab_name", krb5_config_string, NULL, 0 },
{ "default_cc_name", krb5_config_string, NULL, 0 },
{ "default_cc_type", krb5_config_string, NULL, 0 },
+ { "default_cc_collection", krb5_config_string, NULL, 0 },
{ "default_etypes", krb5_config_string, NULL, 0 },
{ "default_etypes_des", krb5_config_string, NULL, 0 },
{ "default_keytab_modify_name", krb5_config_string, NULL, 0 },
krb5_cc_copy_cache;
krb5_cc_copy_match_f;
krb5_cc_default;
+ krb5_cc_default_for;
+ krb5_cc_default_sub;
krb5_cc_default_name;
krb5_cc_destroy;
krb5_cc_end_seq_get;
krb5_cc_register;
krb5_cc_remove_cred;
krb5_cc_resolve;
+ krb5_cc_resolve_for;
+ krb5_cc_resolve_sub;
krb5_cc_retrieve_cred;
krb5_cc_set_config;
krb5_cc_set_default_name;
${kinit} -c ${cache} --password-file=${objdir}/foopassword user@${R} || exitcode=1
echo "copy ccache with gss_store_cred"
-${test_add_store_cred} ${cache} ${cache2} || exit 1
+${test_add_store_cred} --default --overwrite --env ${cache} ${cache2} || exit 1
${klist} -c ${cache2} || exit 1
echo "keytab"
cache="FILE:krb5ccfile"
kinit="${TESTS_ENVIRONMENT} ../../kuser/kinit -c $cache ${afs_no_afslog}"
+kdestroy="${TESTS_ENVIRONMENT} ../../kuser/kdestroy -c $cache"
klist="${TESTS_ENVIRONMENT} ../../kuser/heimtools klist -c $cache"
kgetcred="${TESTS_ENVIRONMENT} ../../kuser/kgetcred -c $cache"
kadmin="${TESTS_ENVIRONMENT} ../../kadmin/kadmin -l -r $R"
testfailed="echo test failed; cat messages.log; exit 1"
echo "Test gss_acquire_cred_with_password" ; > messages.log
+${kdestroy}
${context} --client-name=user1@${R} --client-password=u1 --mech-type=krb5 \
host@lucid.test.h5l.se || { eval "$testfailed"; }
+${klist} && { eval "$testfailed"; }
# These must fail (because wrong password)
${context} --client-name=user1@${R} --client-password=u2 --mech-type=krb5 \
host@lucid.test.h5l.se && { eval "$testfailed"; }
+${klist} && { eval "$testfailed"; }
${context} --client-name=user1@${R} --client-password=u2 --mech-type='' \
--mech-types=krb5 host@lucid.test.h5l.se && { eval "$testfailed"; }
+${klist} && { eval "$testfailed"; }
${context} --client-name=user1@${R} --client-password=u2 --mech-type=krb5 \
--mech-types=krb5 host@lucid.test.h5l.se && { eval "$testfailed"; }
+${klist} && { eval "$testfailed"; }
${context} --client-name=user1@${R} --client-password=u2 --mech-type=all \
--mech-types=krb5 host@lucid.test.h5l.se && { eval "$testfailed"; }
+${klist} && { eval "$testfailed"; }
${context} --client-name=user1@${R} --client-password=u2 \
--mech-type=krb5,ntlm --mech-types=krb5 host@lucid.test.h5l.se \
&& { eval "$testfailed"; }
an2ln-db.txt \
kdc-tester4.json \
krb5.conf \
+ krb5-cccol.conf \
krb5-authz.conf \
krb5-authz2.conf \
krb5-canon.conf \
-e 's,[@]kdc[@],,g' < $(srcdir)/krb5.conf.in > krb5.conf.tmp && \
mv krb5.conf.tmp krb5.conf
+krb5-cccol.conf: krb5-cccol.conf.in Makefile
+ $(do_subst) \
+ -e 's,[@]WEAK[@],false,g' \
+ -e 's,[@]dk[@],,g' \
+ -e 's,[@]kdc[@],,g' < $(srcdir)/krb5-cccol.conf.in > krb5-cccol.conf.tmp && \
+ mv krb5-cccol.conf.tmp krb5-cccol.conf
+
krb5-authz.conf: krb5-authz.conf.in Makefile
$(do_subst) < $(srcdir)/krb5-authz.conf.in > krb5-authz.conf.tmp && \
mv krb5-authz.conf.tmp krb5-authz.conf
. ${env_setup}
-KRB5_CONFIG="${objdir}/krb5-cc.conf"
+KRB5_CONFIG="${objdir}/krb5.conf"
export KRB5_CONFIG
unset KRB5CCNAME
> messages.log
-cp "${objdir}/krb5.conf" "${objdir}/krb5-cc.conf"
-
echo Creating database
${kadmin} \
init \
ec=0
-(cat ${objdir}/krb5.conf ; \
- echo '' ; \
- echo '[libdefaults]' ; \
- echo " default_cc_type = SCC" ; \
- echo '' ) \
- > ${objdir}/krb5-cc.conf
+export KRB5CCNAME=SCC:${objdir}/sdb
${kswitch} -p foo@${R} 2>/dev/null && ${kdestroy}
${kswitch} -p foo@${R} 2>/dev/null && ${kdestroy}
${kdestroy}
echo "getting both tickets"; > messages.log
-${kinit} -c SCC:1 foo@${R} || { ec=1 ; eval "${testfailed}"; }
-${kinit} -c SCC:2 bar@${R} || { ec=1 ; eval "${testfailed}"; }
+${kinit} -c ${KRB5CCNAME}:1 foo@${R} || { ec=1 ; eval "${testfailed}"; }
+${kinit} -c ${KRB5CCNAME}:2 bar@${R} || { ec=1 ; eval "${testfailed}"; }
echo "switch foo"
${kswitch} -p foo@${R} || { ec=1 ; eval "${testfailed}"; }
${klist} | head -2 | grep foo@ >/dev/null || { ec=1 ; eval "${testfailed}"; }
echo "check that bar is gone"
${klist} -l | grep bar@ >/dev/null && { ec=1 ; eval "${testfailed}"; }
+echo "getting tickets (DIR)"; > messages.log
+KRB5_CONFIG="${objdir}/krb5-cccol.conf"
+export KRB5_CONFIG
+unset KRB5CCNAME
+rm -rf ${objdir}/kt ${objdir}/cc_dir
+mkdir ${objdir}/cc_dir || { ec=1 ; eval "${testfailed}"; }
+${kinit} foo@${R} || { ec=1 ; eval "${testfailed}"; }
+${kinit} --no-change-default bar@${R} || { ec=1 ; eval "${testfailed}"; }
+primary=`cat ${objdir}/cc_dir/primary`
+[ "x$primary" = xtkt.foo@${R} ] || { ec=1 ; eval "${testfailed}"; }
+${klist} -l |
+ grep "foo@TEST.H5L.SE.*FILE:${objdir}/cc_dir/tkt.foo@TEST.H5L.SE" > /dev/null ||
+ { ec=1 ; eval "${testfailed}"; }
+${klist} -l |
+ grep "bar@TEST.H5L.SE.*FILE:${objdir}/cc_dir/tkt.bar@TEST.H5L.SE" > /dev/null ||
+ { ec=1 ; eval "${testfailed}"; }
echo "killing kdc (${kdcpid})"
sh ${leaks_kill} kdc $kdcpid || exit 1
--- /dev/null
+[libdefaults]
+ default_realm = TEST.H5L.SE TEST2.H5L.SE
+ default_cc_collection = DIR:@objdir@/cc_dir/
+ no-addresses = TRUE
+ allow_weak_crypto = @WEAK@
+ dns_lookup_kdc = no
+ dns_lookup_realm = no
+
+
+[appdefaults]
+ pkinit_anchors = FILE:@srcdir@/../../lib/hx509/data/ca.crt
+ reconnect-min = 2s
+ reconnect-backoff = 2s
+ reconnect-max = 10s
+
+[realms]
+ TEST.H5L.SE = {
+ kdc = localhost:@port@
+ admin_server = localhost:@admport@
+ kpasswd_server = localhost:@pwport@
+ }
+ SUB.TEST.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ TEST2.H5L.SE = {
+ kdc = localhost:@port@
+ kpasswd_server = localhost:@pwport@
+ }
+ TEST3.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ TEST4.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ SOME-REALM5.FR = {
+ kdc = localhost:@port@
+ }
+ SOME-REALM6.US = {
+ kdc = localhost:@port@
+ }
+ SOME-REALM7.UK = {
+ kdc = localhost:@port@
+ }
+ SOME-REALM8.UK = {
+ kdc = localhost:@port@
+ }
+ TEST-HTTP.H5L.SE = {
+ kdc = http/localhost:@port@
+ }
+ H1.TEST.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ H2.TEST.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ H3.H2.TEST.H5L.SE = {
+ kdc = localhost:@port@
+ }
+ H4.H2.TEST.H5L.SE = {
+ kdc = localhost:@port@
+ }
+
+[domain_realm]
+ .test.h5l.se = TEST.H5L.SE
+ .sub.test.h5l.se = SUB.TEST.H5L.SE
+ .h1.test.h5l.se = H1.TEST.H5L.SE
+ .h2.test.h5l.se = H2.TEST.H5L.SE
+ .h3.h2.test.h5l.se = H3.H2.TEST.H5L.SE
+ .h4.h2.test.h5l.se = H4.H2.TEST.H5L.SE
+ .example.com = TEST2.H5L.SE
+ localhost = TEST.H5L.SE
+ .localdomain = TEST.H5L.SE
+ localdomain = TEST.H5L.SE
+ .localdomain6 = TEST.H5L.SE
+ localdomain6 = TEST.H5L.SE
+
+
+[kdc]
+ enable-digest = true
+ allow-anonymous = true
+ digests_allowed = chap-md5,digest-md5,ntlm-v1,ntlm-v1-session,ntlm-v2,ms-chap-v2
+ strict-nametypes = true
+
+ enable-http = true
+
+ enable-pkinit = true
+ pkinit_identity = FILE:@srcdir@/../../lib/hx509/data/kdc.crt,@srcdir@/../../lib/hx509/data/kdc.key
+ pkinit_anchors = FILE:@srcdir@/../../lib/hx509/data/ca.crt
+ pkinit_pool = FILE:@srcdir@/../../lib/hx509/data/sub-ca.crt
+# pkinit_revoke = CRL:@srcdir@/../../lib/hx509/data/crl1.crl
+ pkinit_mappings_file = @srcdir@/pki-mapping
+ pkinit_allow_proxy_certificate = true
+
+ database = {
+ label = {
+ dbname = @db_type@:@objdir@/current-db@kdc@
+ realm = TEST.H5L.SE
+ mkey_file = @objdir@/mkey.file
+ acl_file = @srcdir@/heimdal.acl
+ log_file = @objdir@/current@kdc@.log
+ }
+ label2 = {
+ dbname = @db_type@:@objdir@/current-db@kdc@
+ realm = TEST2.H5L.SE
+ mkey_file = @objdir@/mkey.file
+ acl_file = @srcdir@/heimdal.acl
+ log_file = @objdir@/current@kdc@.log
+ }
+ label3 = {
+ dbname = sqlite:@objdir@/current-db@kdc@.sqlite3
+ realm = SOME-REALM5.FR
+ mkey_file = @objdir@/mkey.file
+ acl_file = @srcdir@/heimdal.acl
+ log_file = @objdir@/current@kdc@.log
+ }
+ }
+
+ signal_socket = @objdir@/signal
+ iprop-stats = @objdir@/iprop-stats
+ iprop-acl = @srcdir@/iprop-acl
+ log-max-size = 40000
+
+[hdb]
+ db-dir = @objdir@
+
+[logging]
+ kdc = 0-/FILE:@objdir@/messages.log
+ krb5 = 0-/FILE:@objdir@/messages.log
+ default = 0-/FILE:@objdir@/messages.log
+
+# If you are doing preformance measurements on OSX you want to change
+# the kdc LOG line from = to - below to keep the FILE open and avoid
+# open/write/close which is blocking (rdar:// ) on OSX.
+# kdc = 0-/FILE=@objdir@/messages.log
+
+[kadmin]
+ save-password = true
+ default_key_rules = {
+ */des3-only@* = des3-cbc-sha1:pw-salt
+ */aes-only@* = aes256-cts-hmac-sha1-96:pw-salt
+ }
+ @dk@
+
+[capaths]
+ TEST.H5L.SE = {
+ TEST2.H5L.SE = .
+ SOME-REALM5.FR = 1
+ TEST3.H5L.SE = TEST2.H5L.SE
+ TEST4.H5L.SE = TEST2.H5L.SE
+ TEST4.H5L.SE = TEST3.H5L.SE
+ SOME-REALM6.US = SOME-REALM5.FR
+ SOME-REALM7.UK = SOME-REALM6.US
+ SOME-REALM7.UK = SOME-REALM5.FR
+ SOME-REALM8.UK = SOME-REALM6.US
+ }
+ H4.H2.TEST.H5L.SE = {
+ H1.TEST.H5L.SE = H3.H2.TEST.H5L.SE
+ H1.TEST.H5L.SE = H2.TEST.H5L.SE
+ H1.TEST.H5L.SE = TEST.H5L.SE
+
+ TEST.H5L.SE = H3.H2.TEST.H5L.SE
+ TEST.H5L.SE = H2.TEST.H5L.SE
+
+ H2.TEST.H5L.SE = H3.H2.TEST.H5L.SE
+ }