diff --git a/kcm/glue.c b/kcm/glue.c index b4a131963..d087a26cf 100644 --- a/kcm/glue.c +++ b/kcm/glue.c @@ -44,15 +44,27 @@ RCSID("$Id$"); #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; } diff --git a/kuser/kinit.c b/kuser/kinit.c index d32be4316..32d8a36c4 100644 --- a/kuser/kinit.c +++ b/kuser/kinit.c @@ -1316,65 +1316,12 @@ get_princ_kt(krb5_context context, 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; @@ -1477,42 +1424,8 @@ main(int argc, char **argv) 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", "")); diff --git a/lib/gssapi/gssapi/gssapi.h b/lib/gssapi/gssapi/gssapi.h index 4f3777228..54e11ab4d 100644 --- a/lib/gssapi/gssapi/gssapi.h +++ b/lib/gssapi/gssapi/gssapi.h @@ -1187,6 +1187,29 @@ gss_store_cred_into( 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 */, diff --git a/lib/gssapi/gssapi_mech.h b/lib/gssapi/gssapi_mech.h index 68732f7f7..1199bb6b8 100644 --- a/lib/gssapi/gssapi_mech.h +++ b/lib/gssapi/gssapi_mech.h @@ -474,6 +474,17 @@ _gss_store_cred_into_t(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, @@ -623,6 +634,7 @@ typedef struct gssapi_mech_interface_desc { _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; diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c index d4680e9e8..9100c71b9 100644 --- a/lib/gssapi/krb5/accept_sec_context.c +++ b/lib/gssapi/krb5/accept_sec_context.c @@ -169,8 +169,7 @@ gsskrb5_accept_delegated_token } *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; @@ -204,7 +203,7 @@ gsskrb5_accept_delegated_token gsskrb5_cred handle; ret = _gsskrb5_krb5_import_cred(minor_status, - ccache, + &ccache, NULL, NULL, delegated_cred_handle); @@ -212,10 +211,7 @@ gsskrb5_accept_delegated_token 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: diff --git a/lib/gssapi/krb5/copy_ccache.c b/lib/gssapi/krb5/copy_ccache.c index 14296bccd..f32eec701 100644 --- a/lib/gssapi/krb5/copy_ccache.c +++ b/lib/gssapi/krb5/copy_ccache.c @@ -62,9 +62,18 @@ gss_krb5_copy_ccache(OM_uint32 *minor_status, #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) @@ -88,14 +97,13 @@ _gsskrb5_krb5_import_cred(OM_uint32 *minor_status, 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); @@ -121,7 +129,7 @@ _gsskrb5_krb5_import_cred(OM_uint32 *minor_status, krb5_timeofday(context, &now); ret = __gsskrb5_ccache_lifetime(minor_status, context, - id, + *id, handle->principal, &left); if (ret != GSS_S_COMPLETE) { @@ -131,12 +139,8 @@ _gsskrb5_krb5_import_cred(OM_uint32 *minor_status, } 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; } diff --git a/lib/gssapi/krb5/external.c b/lib/gssapi/krb5/external.c index 71fc3974d..0af3e3e59 100644 --- a/lib/gssapi/krb5/external.c +++ b/lib/gssapi/krb5/external.c @@ -404,6 +404,7 @@ static gssapi_mech_interface_desc krb5_mech = { NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ + _gsskrb5_store_cred_into2, NULL /* gm_compat */ }; diff --git a/lib/gssapi/krb5/set_cred_option.c b/lib/gssapi/krb5/set_cred_option.c index 1411f8665..ef177a0ef 100644 --- a/lib/gssapi/krb5/set_cred_option.c +++ b/lib/gssapi/krb5/set_cred_option.c @@ -105,7 +105,7 @@ import_cred(OM_uint32 *minor_status, 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) diff --git a/lib/gssapi/krb5/store_cred.c b/lib/gssapi/krb5/store_cred.c index c7c95b943..f18003853 100644 --- a/lib/gssapi/krb5/store_cred.c +++ b/lib/gssapi/krb5/store_cred.c @@ -51,64 +51,54 @@ same_princ(krb5_context context, krb5_ccache id1, krb5_ccache id2) 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; - - while (krb5_cc_cache_next(context, cursor, id) == 0) { - krb5_principal p = NULL; - - 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; + if (asprintf(&varval, "%s=%s", var, val) == -1 || varval == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; } - krb5_cc_cache_end_seq_get(context, cursor); - return KRB5_CC_END; + + b.value = varval; + b.length = strlen(varval) + 1; + major = gss_add_buffer_set_member(minor, &b, env); + free(varval); + return major; +} + +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; @@ -116,8 +106,11 @@ _gsskrb5_store_cred_into(OM_uint32 *minor_status, 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; @@ -168,45 +161,17 @@ _gsskrb5_store_cred_into(OM_uint32 *minor_status, } 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) { @@ -215,14 +180,7 @@ _gsskrb5_store_cred_into(OM_uint32 *minor_status, 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. @@ -246,9 +204,47 @@ _gsskrb5_store_cred_into(OM_uint32 *minor_status, 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); +} diff --git a/lib/gssapi/libgssapi-exports.def b/lib/gssapi/libgssapi-exports.def index 992e8eb3f..c699cad43 100644 --- a/lib/gssapi/libgssapi-exports.def +++ b/lib/gssapi/libgssapi-exports.def @@ -96,6 +96,7 @@ EXPORTS gss_sign gss_store_cred gss_store_cred_into + gss_store_cred_into2 gss_test_oid_set_member gss_unseal gss_unwrap diff --git a/lib/gssapi/mech/gss_store_cred_into.c b/lib/gssapi/mech/gss_store_cred_into.c index 3009fa4a2..1c739b056 100644 --- a/lib/gssapi/mech/gss_store_cred_into.c +++ b/lib/gssapi/mech/gss_store_cred_into.c @@ -38,14 +38,23 @@ store_mech_cred(OM_uint32 *minor_status, 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; + 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_into) + 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, @@ -66,25 +75,28 @@ store_mech_cred(OM_uint32 *minor_status, * 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; @@ -117,10 +129,9 @@ gss_store_cred_into(OM_uint32 *minor_status, !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); @@ -142,3 +153,29 @@ gss_store_cred_into(OM_uint32 *minor_status, 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); +} diff --git a/lib/gssapi/ntlm/external.c b/lib/gssapi/ntlm/external.c index 011627975..02590c96d 100644 --- a/lib/gssapi/ntlm/external.c +++ b/lib/gssapi/ntlm/external.c @@ -130,6 +130,7 @@ static gssapi_mech_interface_desc ntlm_mech = { NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ + NULL, /* gm_store_cred_into2 */ NULL, /* gm_compat */ }; diff --git a/lib/gssapi/spnego/external.c b/lib/gssapi/spnego/external.c index e0744f4b2..b1393f9d5 100644 --- a/lib/gssapi/spnego/external.c +++ b/lib/gssapi/spnego/external.c @@ -154,6 +154,7 @@ static gssapi_mech_interface_desc spnego_mech = { NULL, /* gm_query_mechanism_info */ NULL, /* gm_query_meta_data */ NULL, /* gm_exchange_meta_data */ + NULL, /* gm_store_cred_into2 */ NULL /* gm_compat */ }; diff --git a/lib/gssapi/test_add_store_cred.c b/lib/gssapi/test_add_store_cred.c index 95ef7440d..03c3dc95a 100644 --- a/lib/gssapi/test_add_store_cred.c +++ b/lib/gssapi/test_add_store_cred.c @@ -94,12 +94,21 @@ gss_err(int exitval, OM_uint32 major, OM_uint32 minor, gss_OID mech, 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 @@ -119,6 +128,8 @@ main(int argc, char **argv) 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]); @@ -133,6 +144,11 @@ main(int argc, char **argv) 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; @@ -159,12 +175,35 @@ main(int argc, char **argv) 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); diff --git a/lib/gssapi/version-script.map b/lib/gssapi/version-script.map index 635a7b09d..03ef28e4a 100644 --- a/lib/gssapi/version-script.map +++ b/lib/gssapi/version-script.map @@ -90,6 +90,7 @@ HEIMDAL_GSS_2.0 { gss_sign; gss_store_cred; gss_store_cred_into; + gss_store_cred_into2; gss_test_oid_set_member; gss_unseal; gss_unwrap; diff --git a/lib/krb5/acache.c b/lib/krb5/acache.c index 4f6fa3c0b..8d4f49c8a 100644 --- a/lib/krb5/acache.c +++ b/lib/krb5/acache.c @@ -49,6 +49,7 @@ static void *cc_handle; typedef struct krb5_acc { char *cache_name; + char *cache_subsidiary; cc_context_t context; cc_ccache_t ccache; } krb5_acc; @@ -442,41 +443,51 @@ get_cc_name(krb5_acc *a) } -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 @@ -497,6 +508,10 @@ acc_alloc(krb5_context context, krb5_ccache *id) } 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) { @@ -504,17 +519,17 @@ acc_alloc(krb5_context context, krb5_ccache *id) 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) @@ -522,49 +537,44 @@ acc_resolve(krb5_context context, krb5_ccache *id, const char *res) 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; - - } else if (error == ccErrCCacheNotFound) { - a->ccache = NULL; - a->cache_name = NULL; - } else { - *id = NULL; - return translate_cc_error(context, error); + 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. + */ } + 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 diff --git a/lib/krb5/cache.c b/lib/krb5/cache.c index 8a606529a..c46ef3b8e 100644 --- a/lib/krb5/cache.c +++ b/lib/krb5/cache.c @@ -104,7 +104,7 @@ main (int argc, char **argv) * 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. @@ -180,10 +180,11 @@ _krb5_cc_allocate(krb5_context context, */ 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 @@ -210,7 +211,7 @@ allocate_ccache (krb5_context context, return ret; } - ret = (*id)->ops->resolve(context, id, residual); + ret = (*id)->ops->resolve(context, id, residual, subsidiary); if(ret) { free(*id); *id = NULL; @@ -247,7 +248,7 @@ is_possible_path_name(const char * name) * 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. * @@ -273,12 +274,12 @@ krb5_cc_resolve(krb5_context context, 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); @@ -286,6 +287,153 @@ krb5_cc_resolve(krb5_context context, } } +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 @@ -334,7 +482,42 @@ KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 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; } /** @@ -354,7 +537,7 @@ krb5_cc_get_type(krb5_context context, /** * 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() * @@ -620,8 +803,7 @@ krb5_cc_configured_default_name(krb5_context context) } /* 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; @@ -687,18 +869,52 @@ krb5_cc_configured_default_name(krb5_context context) * @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'. * @@ -1424,7 +1640,7 @@ krb5_cc_cache_match (krb5_context context, * 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 @@ -1515,7 +1731,7 @@ build_conf_principals(krb5_context context, krb5_ccache id, * 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 @@ -1539,7 +1755,7 @@ krb5_is_config_principal(krb5_context context, * 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. @@ -1586,7 +1802,7 @@ out: /** * 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. @@ -1637,7 +1853,7 @@ struct krb5_cccol_cursor_data { * 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(). @@ -1711,7 +1927,7 @@ krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor, return KRB5_CC_END; } - return 0; + return ret; } /** diff --git a/lib/krb5/dcache.c b/lib/krb5/dcache.c index 2a87f5b08..4d4607061 100644 --- a/lib/krb5/dcache.c +++ b/lib/krb5/dcache.c @@ -37,8 +37,10 @@ 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) @@ -46,7 +48,47 @@ typedef struct krb5_dcache{ 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) @@ -63,8 +105,14 @@ 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 @@ -77,12 +125,6 @@ set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual) 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); @@ -141,14 +183,18 @@ set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual) } 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); @@ -197,12 +243,22 @@ get_default_cache(krb5_context context, krb5_dcache *dc, char **residual) -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; } @@ -211,6 +267,12 @@ verify_directory(krb5_context context, const char *path) { 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() */ @@ -241,118 +303,176 @@ dcc_release(krb5_context context, krb5_dcache *dc) { 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) -#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; - } - - dc->name = strdup(res); - if (dc->name == NULL) { - dcc_release(context, dc); - return krb5_enomem(context); - } +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 { - char *residual; - size_t len; + const char *p; + int is_drive_letter_colon = 0; - dc->dir = strdup(res); - if (dc->dir == NULL) { - dcc_release(context, dc); - return krb5_enomem(context); - } + /* + * 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 + * + */ - len = strlen(dc->dir); + 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; + } - if (ISPATHSEP(dc->dir[len - 1])) - dc->dir[len - 1] = '\0'; +#ifdef WIN32 + has_pathsep = strchr(res, '\\') != NULL; +#endif + has_pathsep |= strchr(res, '/') != NULL; - ret = verify_directory(context, dc->dir); - if (ret) { - dcc_release(context, dc); - return ret; - } + if ((dc = calloc(1, sizeof(*dc))) == NULL) + return krb5_enomem(context); - 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); - } + p = strrchr(res, ':'); +#ifdef WIN32 + is_drive_letter_colon = + p && ((res[0] == ':' && res[1] != ':' && p - res == 2) || + (res[0] != ':' && p - res == 1)); +#endif + + 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; + } + } } - asprintf_ret = asprintf(&filename, "FILE%s", dc->name); - if (asprintf_ret == -1 || filename == NULL) { - dcc_release(context, dc); - return krb5_enomem(context); + /* 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'; + + /* If we got here then `dc->dir' and `dc->sub' must both be set */ + + if ((ret = verify_directory(context, dc->dir))) { + dcc_release(context, dc); + return ret; + } + 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); @@ -362,86 +482,36 @@ dcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 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; + int fd = -1; - 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; - } + 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); - len = strlen(krb5_dcc_ops.prefix); - if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':') - ++len; - else - len = 0; - - 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 @@ -457,6 +527,25 @@ 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; } @@ -545,39 +634,61 @@ dcc_get_version(krb5_context context, } 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; @@ -587,18 +698,49 @@ static krb5_error_code KRB5_CALLCONV 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; } @@ -610,9 +752,10 @@ dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 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; } @@ -622,14 +765,22 @@ dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) { 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); } @@ -637,13 +788,10 @@ static krb5_error_code KRB5_CALLCONV 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 diff --git a/lib/krb5/fcache.c b/lib/krb5/fcache.c index bb34ba72d..f2ed1e2a4 100644 --- a/lib/krb5/fcache.c +++ b/lib/krb5/fcache.c @@ -62,14 +62,23 @@ struct fcc_cursor { #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 KRB5_CC_NOTFOUND; - return FILENAME(id); + if (name) + *name = FILENAME(id); + if (colname) + *colname = FILENAME(id); + if (sub) + *sub = NULL; + return 0; } KRB5_LIB_FUNCTION int KRB5_LIB_CALL @@ -178,15 +187,32 @@ static krb5_error_code KRB5_CALLCONV 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, @@ -204,6 +230,7 @@ fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) f->version = 0; (*id)->data.data = f; (*id)->data.length = sizeof(*f); + return 0; } @@ -647,11 +674,8 @@ fcc_destroy(krb5_context context, 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)); } diff --git a/lib/krb5/kcm.c b/lib/krb5/kcm.c index 6f48ff4ca..e7548a577 100644 --- a/lib/krb5/kcm.c +++ b/lib/krb5/kcm.c @@ -136,7 +136,9 @@ krb5_kcm_storage_request(krb5_context context, } 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; @@ -229,17 +231,42 @@ kcm_free(krb5_context context, krb5_ccache *id) } } -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 : + * - col should be + * - sub should be + */ + 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); } /* diff --git a/lib/krb5/krb5.h b/lib/krb5/krb5.h index 44553386a..53257fa37 100644 --- a/lib/krb5/krb5.h +++ b/lib/krb5/krb5.h @@ -491,13 +491,16 @@ typedef struct krb5_creds { 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); diff --git a/lib/krb5/krcache.c b/lib/krb5/krcache.c index 1d425ca3e..56be3567b 100644 --- a/lib/krb5/krcache.c +++ b/lib/krb5/krcache.c @@ -207,6 +207,8 @@ typedef union _krb5_krcache_and_princ_id { */ 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 @@ -384,8 +386,6 @@ parse_residual(krb5_context context, collection_name = strdup(residual); if (collection_name == NULL) goto nomem; - - subsidiary_name = NULL; } else { collection_name = strndup(residual, sep - residual); if (collection_name == NULL) @@ -918,14 +918,18 @@ initialize_internal(krb5_context context, 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; } @@ -939,6 +943,8 @@ krcc_close(krb5_context context, krb5_ccache id) 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); @@ -1048,16 +1054,26 @@ make_cache(krb5_context context, /* 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) @@ -1243,8 +1259,16 @@ alloc_cache(krb5_context context, 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; } @@ -1321,10 +1345,25 @@ cleanup: } /* 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. */ @@ -1641,6 +1680,9 @@ krcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset) 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); diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 561ca7ef8..341177550 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -87,6 +87,8 @@ EXPORTS 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 @@ -111,6 +113,8 @@ EXPORTS 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 diff --git a/lib/krb5/mcache.c b/lib/krb5/mcache.c index a8a638b16..1ce1df343 100644 --- a/lib/krb5/mcache.c +++ b/lib/krb5/mcache.c @@ -38,7 +38,8 @@ 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; @@ -57,42 +58,89 @@ static struct krb5_mcache *mcc_head; #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; @@ -103,35 +151,21 @@ mcc_alloc(const char *name) 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); @@ -143,15 +177,11 @@ mcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 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); @@ -221,7 +251,7 @@ mcc_close_internal(krb5_mcache *m) return 0; } if (MISDEAD(m)) { - free (m->name); + free(m->name); HEIMDAL_MUTEX_unlock(&(m->mutex)); return 1; } @@ -248,6 +278,18 @@ mcc_destroy(krb5_context context, { 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) @@ -290,12 +332,8 @@ mcc_store_cred(krb5_context context, } 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)); diff --git a/lib/krb5/scache.c b/lib/krb5/scache.c index d3b9764c0..d260cac27 100644 --- a/lib/krb5/scache.c +++ b/lib/krb5/scache.c @@ -40,6 +40,7 @@ typedef struct krb5_scache { char *name; char *file; + char *sub; sqlite3 *db; sqlite_uint64 cid; @@ -66,7 +67,7 @@ typedef struct krb5_scache { #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) @@ -103,7 +104,8 @@ typedef struct krb5_scache { #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 (" \ @@ -153,8 +155,12 @@ free_krb5(void *str) 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); @@ -225,22 +231,53 @@ exec_stmt(krb5_context context, sqlite3 *db, const char *str, } 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; - ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL); - free(name); + 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; + } + } + + 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 @@ -249,14 +286,14 @@ default_db(krb5_context context, sqlite3 **db) } 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; @@ -294,10 +331,15 @@ out: 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) @@ -305,41 +347,87 @@ scc_alloc(krb5_context context, const char *name) 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) { @@ -361,7 +449,7 @@ create_cache(krb5_context context, krb5_scache *s) { 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); @@ -477,20 +565,32 @@ bind_principal(krb5_context context, * */ -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", "")); @@ -503,12 +603,12 @@ scc_resolve(krb5_context context, krb5_ccache *id, const char *res) 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) { @@ -540,7 +640,7 @@ scc_gen_new(krb5_context context, krb5_ccache *id) { 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, @@ -557,7 +657,7 @@ scc_gen_new(krb5_context context, krb5_ccache *id) 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; @@ -589,7 +689,7 @@ scc_initialize(krb5_context context, } } - 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); @@ -827,8 +927,8 @@ scc_get_principal(krb5_context context, 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; } @@ -836,8 +936,8 @@ scc_get_principal(krb5_context context, 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; } @@ -845,8 +945,8 @@ scc_get_principal(krb5_context context, 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; } @@ -1001,8 +1101,8 @@ next: 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; } @@ -1079,8 +1179,8 @@ scc_remove_cred(krb5_context context, 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; } @@ -1134,6 +1234,7 @@ scc_set_flags(krb5_context context, struct cache_iter { char *drop; + char *file; sqlite3 *db; sqlite3_stmt *stmt; }; @@ -1151,8 +1252,8 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 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; } @@ -1160,48 +1261,48 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 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); @@ -1249,10 +1350,13 @@ again: 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 @@ -1263,6 +1367,7 @@ scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 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; @@ -1304,7 +1409,7 @@ scc_move(krb5_context context, krb5_ccache from, krb5_ccache to) } } - 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 { @@ -1334,20 +1439,8 @@ rollback: 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 @@ -1364,7 +1457,7 @@ scc_set_default(krb5_context context, krb5_ccache id) 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, diff --git a/lib/krb5/test_cc.c b/lib/krb5/test_cc.c index fce86c911..0a8145da4 100644 --- a/lib/krb5/test_cc.c +++ b/lib/krb5/test_cc.c @@ -669,6 +669,176 @@ test_cc_config(krb5_context context, const char *cc_type, 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, @@ -725,6 +895,10 @@ main(int argc, char **argv) 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 @@ -753,6 +927,14 @@ main(int argc, char **argv) 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); @@ -860,6 +1042,22 @@ main(int argc, char **argv) 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 diff --git a/lib/krb5/verify_krb5_conf.c b/lib/krb5/verify_krb5_conf.c index 128ebb270..1063ecdae 100644 --- a/lib/krb5/verify_krb5_conf.c +++ b/lib/krb5/verify_krb5_conf.c @@ -410,6 +410,7 @@ struct entry libdefaults_entries[] = { { "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 }, diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index d35fb3c04..18d27dc8b 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -87,6 +87,8 @@ HEIMDAL_KRB5_2.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; @@ -111,6 +113,8 @@ HEIMDAL_KRB5_2.0 { 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; diff --git a/tests/gss/check-basic.in b/tests/gss/check-basic.in index bd219b5e0..a4c58db7d 100644 --- a/tests/gss/check-basic.in +++ b/tests/gss/check-basic.in @@ -105,7 +105,7 @@ echo "initial ticket" ${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" diff --git a/tests/gss/check-context.in b/tests/gss/check-context.in index a192ae1c4..2f215ca7d 100644 --- a/tests/gss/check-context.in +++ b/tests/gss/check-context.in @@ -53,6 +53,7 @@ nokeytab="FILE:no-such-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" @@ -121,17 +122,23 @@ trap "kill ${kdcpid}; echo signal killing kdc; exit 1;" EXIT 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"; } diff --git a/tests/kdc/Makefile.am b/tests/kdc/Makefile.am index 4bb5dacc8..7efc86b8f 100644 --- a/tests/kdc/Makefile.am +++ b/tests/kdc/Makefile.am @@ -4,6 +4,7 @@ noinst_DATA = \ an2ln-db.txt \ kdc-tester4.json \ krb5.conf \ + krb5-cccol.conf \ krb5-authz.conf \ krb5-authz2.conf \ krb5-canon.conf \ @@ -183,6 +184,13 @@ krb5.conf: krb5.conf.in Makefile -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 diff --git a/tests/kdc/check-cc.in b/tests/kdc/check-cc.in index 1543db4f8..e69621523 100644 --- a/tests/kdc/check-cc.in +++ b/tests/kdc/check-cc.in @@ -36,7 +36,7 @@ objdir="@objdir@" . ${env_setup} -KRB5_CONFIG="${objdir}/krb5-cc.conf" +KRB5_CONFIG="${objdir}/krb5.conf" export KRB5_CONFIG unset KRB5CCNAME @@ -68,8 +68,6 @@ rm -f mkey.file* > messages.log -cp "${objdir}/krb5.conf" "${objdir}/krb5-cc.conf" - echo Creating database ${kadmin} \ init \ @@ -95,12 +93,7 @@ trap "kill -9 ${kdcpid}; echo signal killing kdc; exit 1;" EXIT 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} @@ -114,8 +107,8 @@ ${klist} -l | grep foo@ >/dev/null || { ec=1 ; eval "${testfailed}"; } ${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}"; } @@ -140,6 +133,22 @@ ${klist} -l | 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 diff --git a/tests/kdc/krb5-cccol.conf.in b/tests/kdc/krb5-cccol.conf.in new file mode 100644 index 000000000..819de8007 --- /dev/null +++ b/tests/kdc/krb5-cccol.conf.in @@ -0,0 +1,165 @@ +[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 + }