krb5: Improve cccol sub naming; add gss_store_cred_into2()

- Formalize the TYPE:collection_name:subsidiary_name naming scheme for
   ccaches in ccache collections
    - KEYRING: ccaches are weird because they have one more optional field: the
      "anchor", so rather than just assume a naming convention everywhere, we
      add new functions as well
 - Add krb5_cc_{resolve,default}_sub() that allows one to specify a
   "subsidiary" ccache name in a collection separately from the
   collection name
 - Add krb5_cc_{resolve,default}_for() which take a principal name,
   unparse it, and use it as the subsidiary ccache name (with colons
   replaced)
 - Make kinit use the new interfaces
 - Add missing DIR ccache iteration functionality
 - Revamps test_cc
 - Add krb5_cc_get_collection() and krb5_cc_get_subsidiary()
 - Bump the ccops SPI version number
 - Add gss_store_cred_into2()
 - Make MEMORY:anonymous not linked into the global MEMORY ccache
   collection, and uses this for delegated cred handles

TBD:

 - Split this up into a krb5 change and gss mech_krb5 change?
 - Add krb5_cc_init_and_store() utility, per Greg's suggestion?
This commit is contained in:
Nicolas Williams
2020-01-22 19:18:14 -06:00
parent a7359d6898
commit 7bf4d76e75
33 changed files with 1749 additions and 715 deletions

View File

@@ -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;
}

View File

@@ -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", ""));

View File

@@ -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 */,

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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 */
};

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 */
};

View File

@@ -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 */
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
/**

View File

@@ -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);
}
free(s);
return ret;
}
/* check for explicit component */
if (res[0] == ':') {
char *q;
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

View File

@@ -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));
}

View File

@@ -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 <IPC-name>:<cache-name>
* - col should be <IPC-name>
* - sub should be <cache-name>
*/
if (name)
*name = CACHENAME(id);
if (col)
*col = NULL;
if (sub)
*sub = CACHENAME(id);
return 0;
}
static krb5_error_code
kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
kcm_resolve(krb5_context context,
krb5_ccache *id,
const char *res,
const char *sub)
{
return kcm_alloc(context, res, id);
/*
* For now, for KCM the `res' is the `sub'.
*
* TODO: We should use `res' as the IPC name instead of the one currently
* hard-coded in `kcm_ipc_name'.
*/
return kcm_alloc(context, sub && *sub ? sub : res, id);
}
/*

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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));

View File

@@ -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,

View File

@@ -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

View File

@@ -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 },

View File

@@ -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;

View File

@@ -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"

View File

@@ -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"; }

View File

@@ -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

View File

@@ -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

View File

@@ -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
}