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:
20
kcm/glue.c
20
kcm/glue.c
@@ -44,15 +44,27 @@ RCSID("$Id$");
|
||||
#define KCMCACHE(X) ((kcm_ccache)(X)->data.data)
|
||||
#define CACHENAME(X) (KCMCACHE(X)->name)
|
||||
|
||||
static const char *
|
||||
static krb5_error_code
|
||||
kcmss_get_name(krb5_context context,
|
||||
krb5_ccache id)
|
||||
krb5_ccache id,
|
||||
const char **name,
|
||||
const char **col,
|
||||
const char **sub)
|
||||
{
|
||||
return CACHENAME(id);
|
||||
if (name)
|
||||
*name = CACHENAME(id);
|
||||
if (col)
|
||||
*col = NULL;
|
||||
if (name)
|
||||
*sub = CACHENAME(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
kcmss_resolve(krb5_context context, krb5_ccache *id, const char *res)
|
||||
kcmss_resolve(krb5_context context,
|
||||
krb5_ccache *id,
|
||||
const char *res,
|
||||
const char *sub)
|
||||
{
|
||||
return KRB5_FCC_INTERNAL;
|
||||
}
|
||||
|
@@ -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", ""));
|
||||
|
||||
|
@@ -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 */,
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
256
lib/krb5/cache.c
256
lib/krb5/cache.c
@@ -104,7 +104,7 @@ main (int argc, char **argv)
|
||||
* Add a new ccache type with operations `ops', overwriting any
|
||||
* existing one if `override'.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param ops type of plugin symbol
|
||||
* @param override flag to select if the registration is to overide
|
||||
* an existing ops with the same name.
|
||||
@@ -180,10 +180,11 @@ _krb5_cc_allocate(krb5_context context,
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
allocate_ccache (krb5_context context,
|
||||
const krb5_cc_ops *ops,
|
||||
const char *residual,
|
||||
krb5_ccache *id)
|
||||
allocate_ccache(krb5_context context,
|
||||
const krb5_cc_ops *ops,
|
||||
const char *residual,
|
||||
const char *subsidiary,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
#ifdef KRB5_USE_PATH_TOKENS
|
||||
@@ -210,7 +211,7 @@ allocate_ccache (krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = (*id)->ops->resolve(context, id, residual);
|
||||
ret = (*id)->ops->resolve(context, id, residual, subsidiary);
|
||||
if(ret) {
|
||||
free(*id);
|
||||
*id = NULL;
|
||||
@@ -247,7 +248,7 @@ is_possible_path_name(const char * name)
|
||||
* Find and allocate a ccache in `id' from the specification in `residual'.
|
||||
* If the ccache name doesn't contain any colon, interpret it as a file name.
|
||||
*
|
||||
* @param context a Keberos context.
|
||||
* @param context a Kerberos context.
|
||||
* @param name string name of a credential cache.
|
||||
* @param id return pointer to a found credential cache.
|
||||
*
|
||||
@@ -273,12 +274,12 @@ krb5_cc_resolve(krb5_context context,
|
||||
if(strncmp(context->cc_ops[i]->prefix, name, prefix_len) == 0
|
||||
&& name[prefix_len] == ':') {
|
||||
return allocate_ccache (context, context->cc_ops[i],
|
||||
name + prefix_len + 1,
|
||||
name + prefix_len + 1, NULL,
|
||||
id);
|
||||
}
|
||||
}
|
||||
if (is_possible_path_name(name))
|
||||
return allocate_ccache (context, &krb5_fcc_ops, name, id);
|
||||
return allocate_ccache (context, &krb5_fcc_ops, name, NULL, id);
|
||||
else {
|
||||
krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
|
||||
N_("unknown ccache type %s", "name"), name);
|
||||
@@ -286,6 +287,153 @@ krb5_cc_resolve(krb5_context context,
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_default_cc_type(krb5_context context, int simple)
|
||||
{
|
||||
const char *def_ccname;
|
||||
const char *def_cctype =
|
||||
krb5_config_get_string_default(context, NULL,
|
||||
secure_getenv("KRB5CCTYPE"),
|
||||
"libdefaults", "default_cc_type", NULL);
|
||||
|
||||
if (!simple &&
|
||||
(def_ccname = krb5_cc_default_name(context))) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
|
||||
size_t prefix_len = strlen(context->cc_ops[i]->prefix);
|
||||
|
||||
if (!strncmp(context->cc_ops[i]->prefix, def_ccname, prefix_len) &&
|
||||
def_ccname[prefix_len] == ':')
|
||||
return context->cc_ops[i]->prefix;
|
||||
}
|
||||
if (is_possible_path_name(def_ccname))
|
||||
return "FILE";
|
||||
}
|
||||
return def_cctype ? def_cctype : "DIR";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and allocate a ccache in `id' for the subsidiary cache named by
|
||||
* `subsidiary' in the collection named by `collection'.
|
||||
*
|
||||
* @param context a Kerberos context.
|
||||
* @param cctype string name of a credential cache collection type.
|
||||
* @param collection string name of a credential cache collection.
|
||||
* @param subsidiary string name of a credential cache in a collection.
|
||||
* @param id return pointer to a found credential cache.
|
||||
*
|
||||
* @return Return 0 or an error code. In case of an error, id is set
|
||||
* to NULL, see krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_cc_resolve_sub(krb5_context context,
|
||||
const char *cctype,
|
||||
const char *collection,
|
||||
const char *subsidiary,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
*id = NULL;
|
||||
|
||||
if (!cctype && collection) {
|
||||
/* Get the cctype from the collection, maybe */
|
||||
for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
|
||||
size_t plen = strlen(context->cc_ops[i]->prefix);
|
||||
|
||||
if ((strncmp(context->cc_ops[i]->prefix, collection, plen) ||
|
||||
collection[plen] != ':'))
|
||||
continue;
|
||||
cctype = context->cc_ops[i]->prefix;
|
||||
collection += plen + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!cctype) {
|
||||
const char *def_cctype = get_default_cc_type(context, 0);
|
||||
int might_be_path = collection && is_possible_path_name(collection);
|
||||
|
||||
if (def_cctype)
|
||||
cctype = def_cctype;
|
||||
else if (might_be_path && subsidiary)
|
||||
cctype = "DIR"; /* Default to DIR */
|
||||
else if (might_be_path && !subsidiary)
|
||||
cctype = "FILE"; /* Default to FILE */
|
||||
}
|
||||
|
||||
/* If either `cctype' is not NULL or `collection' starts with TYPE: */
|
||||
if (cctype || collection) {
|
||||
for (i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
|
||||
size_t plen = strlen(context->cc_ops[i]->prefix);
|
||||
|
||||
if (cctype && strcmp(context->cc_ops[i]->prefix, cctype))
|
||||
continue;
|
||||
if (!cctype &&
|
||||
(strncmp(context->cc_ops[i]->prefix, collection, plen) ||
|
||||
collection[plen] != ':'))
|
||||
continue;
|
||||
|
||||
return allocate_ccache(context, context->cc_ops[i],
|
||||
cctype ? collection : collection + plen + 1,
|
||||
subsidiary, id);
|
||||
}
|
||||
}
|
||||
|
||||
krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
|
||||
N_("unknown ccache type %s", ""),
|
||||
cctype ? cctype : collection);
|
||||
return KRB5_CC_UNKNOWN_TYPE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and allocate a ccache in `id' from the specification in `residual', but
|
||||
* specific to the given principal `principal' by using the principal name as
|
||||
* the name of a "subsidiary" credentials cache in the collection named by
|
||||
* `name'. If the ccache name doesn't contain any colon, interpret it as a
|
||||
* file name.
|
||||
*
|
||||
* @param context a Kerberos context.
|
||||
* @param name string name of a credential cache.
|
||||
* @param principal principal name of desired credentials.
|
||||
* @param id return pointer to a found credential cache.
|
||||
*
|
||||
* @return Return 0 or an error code. In case of an error, id is set
|
||||
* to NULL, see krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_cc_resolve_for(krb5_context context,
|
||||
const char *cctype,
|
||||
const char *name,
|
||||
krb5_const_principal principal,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *p, *s;
|
||||
|
||||
*id = NULL;
|
||||
|
||||
ret = krb5_unparse_name(context, principal, &p);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Subsidiary components cannot have ':'s in them */
|
||||
for (s = strchr(p, ':'); s; s = strchr(s + 1, ':'))
|
||||
*s = '-';
|
||||
ret = krb5_cc_resolve_sub(context, cctype, name, p, id);
|
||||
free(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new unique ccache of `type` in `id'. If `type' is NULL,
|
||||
* the library chooses the default credential cache type. The supplied
|
||||
@@ -334,7 +482,42 @@ KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
|
||||
krb5_cc_get_name(krb5_context context,
|
||||
krb5_ccache id)
|
||||
{
|
||||
return id->ops->get_name(context, id);
|
||||
const char *name;
|
||||
|
||||
(void) id->ops->get_name(context, id, &name, NULL, NULL);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the ccache collection associated with `id'
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
|
||||
krb5_cc_get_collection(krb5_context context, krb5_ccache id)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
(void) id->ops->get_name(context, id, NULL, &name, NULL);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the subsidiary ccache of `id'
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
|
||||
krb5_cc_get_subsidiary(krb5_context context, krb5_ccache id)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
(void) id->ops->get_name(context, id, NULL, NULL, &name);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +537,7 @@ krb5_cc_get_type(krb5_context context,
|
||||
/**
|
||||
* Return the complete resolvable name the cache
|
||||
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param id return pointer to a found credential cache
|
||||
* @param str the returned name of a credential cache, free with krb5_xfree()
|
||||
*
|
||||
@@ -620,8 +803,7 @@ krb5_cc_configured_default_name(krb5_context context)
|
||||
}
|
||||
|
||||
/* Else try a configured default ccache type's default */
|
||||
cfg = krb5_config_get_string(context, NULL, "libdefaults",
|
||||
"default_cc_type", NULL);
|
||||
cfg = get_default_cc_type(context, 1);
|
||||
if (cfg) {
|
||||
const krb5_cc_ops *ops;
|
||||
|
||||
@@ -687,18 +869,52 @@ krb5_cc_configured_default_name(krb5_context context)
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_cc_default(krb5_context context,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
const char *p = krb5_cc_default_name(context);
|
||||
|
||||
*id = NULL;
|
||||
if (p == NULL)
|
||||
return krb5_enomem(context);
|
||||
return krb5_cc_resolve(context, p, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the named subsidiary cache from the default ccache collection in `id'.
|
||||
*
|
||||
* @return Return an error code or 0, see krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_cc_default_sub(krb5_context context,
|
||||
const char *subsidiary,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
return krb5_cc_resolve_sub(context, get_default_cc_type(context, 0), NULL,
|
||||
subsidiary, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the default ccache in `id' that corresponds to the given principal.
|
||||
*
|
||||
* @return Return an error code or 0, see krb5_get_error_message().
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_cc_default_for(krb5_context context,
|
||||
krb5_const_principal principal,
|
||||
krb5_ccache *id)
|
||||
{
|
||||
return krb5_cc_resolve_for(context, get_default_cc_type(context, 0), NULL,
|
||||
principal, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ccache in `id' for `primary_principal'.
|
||||
*
|
||||
@@ -1424,7 +1640,7 @@ krb5_cc_cache_match (krb5_context context,
|
||||
* Move the content from one credential cache to another. The
|
||||
* operation is an atomic switch.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param from the credential cache to move the content from
|
||||
* @param to the credential cache to move the content to
|
||||
|
||||
@@ -1515,7 +1731,7 @@ build_conf_principals(krb5_context context, krb5_ccache id,
|
||||
* principal (generated part of krb5_cc_set_config()). Returns FALSE
|
||||
* (zero) if not a configuration principal.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param principal principal to check if it a configuration principal
|
||||
*
|
||||
* @ingroup krb5_ccache
|
||||
@@ -1539,7 +1755,7 @@ krb5_is_config_principal(krb5_context context,
|
||||
* Store some configuration for the credential cache in the cache.
|
||||
* Existing configuration under the same name is over-written.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param id the credential cache to store the data for
|
||||
* @param principal configuration for a specific principal, if
|
||||
* NULL, global for the whole cache.
|
||||
@@ -1586,7 +1802,7 @@ out:
|
||||
/**
|
||||
* Get some configuration for the credential cache in the cache.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param id the credential cache to store the data for
|
||||
* @param principal configuration for a specific principal, if
|
||||
* NULL, global for the whole cache.
|
||||
@@ -1637,7 +1853,7 @@ struct krb5_cccol_cursor_data {
|
||||
* Get a new cache interation cursor that will interate over all
|
||||
* credentials caches independent of type.
|
||||
*
|
||||
* @param context a Keberos context
|
||||
* @param context a Kerberos context
|
||||
* @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
|
||||
*
|
||||
* @return Returns 0 or and error code, see krb5_get_error_message().
|
||||
@@ -1711,7 +1927,7 @@ krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
|
||||
return KRB5_CC_END;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -37,8 +37,10 @@
|
||||
|
||||
typedef struct krb5_dcache{
|
||||
krb5_ccache fcache;
|
||||
char *dir;
|
||||
char *name;
|
||||
char *dir;
|
||||
char *sub;
|
||||
unsigned int default_candidate:1;
|
||||
} krb5_dcache;
|
||||
|
||||
#define DCACHE(X) ((krb5_dcache*)(X)->data.data)
|
||||
@@ -46,7 +48,47 @@ typedef struct krb5_dcache{
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
|
||||
static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
|
||||
static krb5_error_code KRB5_CALLCONV dcc_set_default(krb5_context, krb5_ccache);
|
||||
|
||||
/*
|
||||
* Make subsidiary filesystem safe by mapping / and : to -. If the subsidiary
|
||||
* is longer than 128 bytes, then truncate.
|
||||
* In all cases, "tkt." is prefixed to be compatible with the DIR requirement
|
||||
* that subsidiary ccache files be named tkt*.
|
||||
*
|
||||
* Thus host/foo.bar.baz@BAR.BAZ -> tkt.host-foo.bar.baz@BAR.BAZ.
|
||||
*
|
||||
* In particular, no filesystem component separators will be emitted, and . and
|
||||
* .. will never be traversed.
|
||||
*/
|
||||
static krb5_error_code
|
||||
fs_encode_subsidiary(krb5_context context,
|
||||
krb5_dcache *dc,
|
||||
const char *subsidiary,
|
||||
char **res)
|
||||
{
|
||||
size_t len = strlen(subsidiary);
|
||||
size_t i;
|
||||
|
||||
*res = NULL;
|
||||
if (asprintf(res, "tkt.%s", subsidiary) == -1 || *res == NULL)
|
||||
return krb5_enomem(context);
|
||||
for (i = sizeof("tkt.") - 1; i < len; i++) {
|
||||
switch ((*res)[i]) {
|
||||
#ifdef WIN32
|
||||
case '\\': (*res)[0] = '-'; break;
|
||||
#endif
|
||||
case '/': (*res)[0] = '-'; break;
|
||||
case ':': (*res)[0] = '-'; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hopefully this will work on all filesystems */
|
||||
if (len > 128 - sizeof("tkt.") - 1)
|
||||
(*res)[127] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
primary_create(krb5_dcache *dc)
|
||||
@@ -63,8 +105,14 @@ primary_create(krb5_dcache *dc)
|
||||
static int
|
||||
is_filename_cacheish(const char *name)
|
||||
{
|
||||
return strncmp(name, "tkt", 3) == 0;
|
||||
|
||||
size_t i;
|
||||
|
||||
if (strncmp(name, "tkt", sizeof("tkt") - 1))
|
||||
return 0;
|
||||
for (i = sizeof("tkt") - 1; name[i]; i++)
|
||||
if (ISPATHSEP(name[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -77,12 +125,6 @@ set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
|
||||
int fd = -1;
|
||||
int asprintf_ret;
|
||||
|
||||
if (!is_filename_cacheish(residual)) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
"name %s is not a cache (doesn't start with tkt)", residual);
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
asprintf_ret = asprintf(&path, "%s/primary-XXXXXX", dc->dir);
|
||||
if (asprintf_ret == -1 || path == NULL) {
|
||||
return krb5_enomem(context);
|
||||
@@ -141,14 +183,18 @@ set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
|
||||
get_default_cache(krb5_context context, krb5_dcache *dc,
|
||||
const char *subsidiary, char **residual)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char buf[MAXPATHLEN];
|
||||
char *primary;
|
||||
char *primary = NULL;
|
||||
FILE *f;
|
||||
|
||||
*residual = NULL;
|
||||
if (subsidiary)
|
||||
return fs_encode_subsidiary(context, dc, subsidiary, residual);
|
||||
|
||||
primary = primary_create(dc);
|
||||
if (primary == NULL)
|
||||
return krb5_enomem(context);
|
||||
@@ -197,12 +243,22 @@ get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
|
||||
|
||||
|
||||
|
||||
static const char* KRB5_CALLCONV
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_get_name(krb5_context context,
|
||||
krb5_ccache id)
|
||||
krb5_ccache id,
|
||||
const char **name,
|
||||
const char **dir,
|
||||
const char **sub)
|
||||
{
|
||||
krb5_dcache *dc = DCACHE(id);
|
||||
return dc->name;
|
||||
|
||||
if (name)
|
||||
*name = dc->name;
|
||||
if (dir)
|
||||
*dir = dc->dir;
|
||||
if (sub)
|
||||
*sub = dc->sub;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +267,12 @@ verify_directory(krb5_context context, const char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (!path[0]) {
|
||||
krb5_set_error_message(context, EINVAL,
|
||||
N_("DIR empty directory component", ""));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (stat(path, &sb) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
/* XXX should use mkdirx_np() */
|
||||
@@ -241,118 +303,176 @@ dcc_release(krb5_context context, krb5_dcache *dc)
|
||||
{
|
||||
if (dc->fcache)
|
||||
krb5_cc_close(context, dc->fcache);
|
||||
if (dc->dir)
|
||||
free(dc->dir);
|
||||
if (dc->name)
|
||||
free(dc->name);
|
||||
free(dc->sub);
|
||||
free(dc->dir);
|
||||
free(dc->name);
|
||||
memset(dc, 0, sizeof(*dc));
|
||||
free(dc);
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
|
||||
static krb5_error_code
|
||||
get_default_dir(krb5_context context, char **res)
|
||||
{
|
||||
char *filename = NULL;
|
||||
krb5_error_code ret;
|
||||
krb5_dcache *dc;
|
||||
const char *p;
|
||||
int asprintf_ret;
|
||||
char *s;
|
||||
|
||||
p = res;
|
||||
do {
|
||||
p = strstr(p, "..");
|
||||
if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Path contains a .. component", ""));
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
if (p)
|
||||
p += 3;
|
||||
} while (p);
|
||||
|
||||
dc = calloc(1, sizeof(*dc));
|
||||
if (dc == NULL) {
|
||||
krb5_set_error_message(context, KRB5_CC_NOMEM,
|
||||
N_("malloc: out of memory", ""));
|
||||
return KRB5_CC_NOMEM;
|
||||
if ((ret = dcc_get_default_name(context, &s)))
|
||||
return ret;
|
||||
if (strncmp(s, "DIR:", sizeof("DIR:") - 1)) {
|
||||
*res = s;
|
||||
s = NULL;
|
||||
} else if ((*res = strdup(s + sizeof("DIR:") - 1)) == NULL) {
|
||||
ret = krb5_enomem(context);
|
||||
}
|
||||
|
||||
/* check for explicit component */
|
||||
if (res[0] == ':') {
|
||||
char *q;
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dc->dir = strdup(&res[1]);
|
||||
#ifdef _WIN32
|
||||
q = strrchr(dc->dir, '\\');
|
||||
if (q == NULL)
|
||||
#endif
|
||||
q = strrchr(dc->dir, '/');
|
||||
if (q) {
|
||||
*q++ = '\0';
|
||||
} else {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
|
||||
dcc_release(context, dc);
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
if (!is_filename_cacheish(q)) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
|
||||
dcc_release(context, dc);
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
ret = verify_directory(context, dc->dir);
|
||||
if (ret) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dc->name = strdup(res);
|
||||
if (dc->name == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_resolve(krb5_context context,
|
||||
krb5_ccache *id,
|
||||
const char *res,
|
||||
const char *sub)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_dcache *dc = NULL;
|
||||
char *filename = NULL;
|
||||
size_t len;
|
||||
int has_pathsep = 0;
|
||||
|
||||
if (sub) {
|
||||
/*
|
||||
* Here `res' has the directory name (or, if NULL, refers to the
|
||||
* default DIR cccol), and `sub' has the "subsidiary" name, to which
|
||||
* we'll prefix "tkt." (though we will insist only on "tkt" later).
|
||||
*/
|
||||
if ((dc = calloc(1, sizeof(*dc))) == NULL ||
|
||||
asprintf(&dc->sub, "tkt.%s", sub) == -1 || dc->sub == NULL) {
|
||||
free(dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
if (res && res[0] && (dc->dir = strdup(res)) == NULL) {
|
||||
free(dc->sub);
|
||||
free(dc);
|
||||
return krb5_enomem(context);
|
||||
} else if ((!res || !res[0]) && (ret = get_default_dir(context, &dc->dir))) {
|
||||
free(dc->sub);
|
||||
free(dc);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
char *residual;
|
||||
size_t len;
|
||||
const char *p;
|
||||
int is_drive_letter_colon = 0;
|
||||
|
||||
dc->dir = strdup(res);
|
||||
if (dc->dir == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
/*
|
||||
* Here `res' has whatever string followed "DIR:", and we need to parse
|
||||
* it into `dc->dir' and `dc->sub'.
|
||||
*
|
||||
* Conventions we support for DIR cache naming:
|
||||
*
|
||||
* - DIR:path:NAME ---> FILE:path/tktNAME
|
||||
* - DIR::path/tktNAME ---> FILE:path/tktNAME
|
||||
* - DIR::NAME ---> FILE:${default_DIR_cccol_path}/tktNAME
|
||||
* \-> FILE:/tmp/krb5cc_${uid}_dir/tktNAME
|
||||
* - DIR:path ---> FILE:path/$(cat primary) or FILE:path/tkt
|
||||
*
|
||||
*/
|
||||
|
||||
len = strlen(dc->dir);
|
||||
if (*res == '\0' || (res[0] == ':' && res[1] == '\0')) {
|
||||
/* XXX Why not? */
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("\"DIR:\" is not a valid ccache name", ""));
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
if (ISPATHSEP(dc->dir[len - 1]))
|
||||
dc->dir[len - 1] = '\0';
|
||||
#ifdef WIN32
|
||||
has_pathsep = strchr(res, '\\') != NULL;
|
||||
#endif
|
||||
has_pathsep |= strchr(res, '/') != NULL;
|
||||
|
||||
ret = verify_directory(context, dc->dir);
|
||||
if (ret) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
if ((dc = calloc(1, sizeof(*dc))) == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
ret = get_default_cache(context, dc, &residual);
|
||||
if (ret) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
asprintf_ret = asprintf(&dc->name, ":%s/%s", dc->dir, residual);
|
||||
free(residual);
|
||||
if (asprintf_ret == -1 || dc->name == NULL) {
|
||||
dc->name = NULL;
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
p = strrchr(res, ':');
|
||||
#ifdef WIN32
|
||||
is_drive_letter_colon =
|
||||
p && ((res[0] == ':' && res[1] != ':' && p - res == 2) ||
|
||||
(res[0] != ':' && p - res == 1));
|
||||
#endif
|
||||
|
||||
if (res[0] != ':' && p && !is_drive_letter_colon) {
|
||||
/* DIR:path:NAME */
|
||||
if ((dc->dir = strndup(res, (p - res))) == NULL ||
|
||||
asprintf(&dc->sub, "tkt.%s", p + 1) < 0 || dc->sub == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
} else if (res[0] == ':' && has_pathsep) {
|
||||
char *q;
|
||||
|
||||
/* DIR::path/tktNAME (the "tkt" must be there; we'll check) */
|
||||
if ((dc->dir = strdup(&res[1])) == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
q = strrchr(dc->dir, '\\');
|
||||
if (q == NULL || ((p = strrchr(dc->dir, '/')) && q < p))
|
||||
#endif
|
||||
q = strrchr(dc->dir, '/');
|
||||
*q++ = '\0';
|
||||
if ((dc->sub = strdup(q)) == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
} else if (res[0] == ':') {
|
||||
/* DIR::NAME -- no path component separators in NAME */
|
||||
if ((ret = get_default_dir(context, &dc->dir))) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
if (asprintf(&dc->sub, "tkt.%s", res + 1) < 0 || dc->sub == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
} else {
|
||||
/* DIR:path */
|
||||
if ((dc->dir = strdup(res)) == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
|
||||
if ((ret = get_default_cache(context, dc, NULL, &dc->sub))) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asprintf_ret = asprintf(&filename, "FILE%s", dc->name);
|
||||
if (asprintf_ret == -1 || filename == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
/* Strip off extra slashes on the end */
|
||||
for (len = strlen(dc->dir);
|
||||
len && ISPATHSEP(dc->dir[len - 1]);
|
||||
len -= len ? 1 : 0)
|
||||
dc->dir[len - 1] = '\0';
|
||||
|
||||
/* If we got here then `dc->dir' and `dc->sub' must both be set */
|
||||
|
||||
if ((ret = verify_directory(context, dc->dir))) {
|
||||
dcc_release(context, dc);
|
||||
return ret;
|
||||
}
|
||||
if (!is_filename_cacheish(dc->sub)) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Name %s is not a cache "
|
||||
"(doesn't start with tkt)", ""), dc->sub);
|
||||
dcc_release(context, dc);
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
if (asprintf(&dc->name, ":%s/%s", dc->dir, dc->sub) == -1 ||
|
||||
dc->name == NULL ||
|
||||
asprintf(&filename, "FILE%s", dc->name) == -1 || filename == NULL) {
|
||||
dcc_release(context, dc);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
|
||||
ret = krb5_cc_resolve(context, filename, &dc->fcache);
|
||||
@@ -362,86 +482,36 @@ dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
dc->default_candidate = 1;
|
||||
(*id)->data.data = dc;
|
||||
(*id)->data.length = sizeof(*dc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
copy_default_dcc_cache(krb5_context context)
|
||||
{
|
||||
const char *defname;
|
||||
krb5_error_code ret;
|
||||
char *name = NULL;
|
||||
size_t len;
|
||||
|
||||
len = strlen(krb5_dcc_ops.prefix);
|
||||
|
||||
defname = krb5_cc_default_name(context);
|
||||
if (defname == NULL ||
|
||||
strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
|
||||
defname[len] != ':')
|
||||
{
|
||||
ret = dcc_get_default_name(context, &name);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
return name;
|
||||
} else {
|
||||
return strdup(&defname[len + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_gen_new(krb5_context context, krb5_ccache *id)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *def_dir = NULL;
|
||||
char *name = NULL;
|
||||
krb5_dcache *dc;
|
||||
int fd;
|
||||
size_t len;
|
||||
int asprintf_ret;
|
||||
int fd = -1;
|
||||
|
||||
name = copy_default_dcc_cache(context);
|
||||
if (name == NULL) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Can't generate DIR caches unless its the default type", ""));
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
ret = get_default_dir(context, &def_dir);
|
||||
if (ret == 0)
|
||||
ret = verify_directory(context, def_dir);
|
||||
if (ret == 0 &&
|
||||
(asprintf(&name, "DIR::%s/tktXXXXXX", def_dir) == -1 || name == NULL))
|
||||
ret = krb5_enomem(context);
|
||||
if (ret == 0 && (fd = mkstemp(name + sizeof("DIR::") - 1)) == -1)
|
||||
ret = errno;
|
||||
if (ret == 0)
|
||||
ret = dcc_resolve(context, id, name + sizeof("DIR:") - 1, NULL);
|
||||
|
||||
len = strlen(krb5_dcc_ops.prefix);
|
||||
if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
|
||||
++len;
|
||||
else
|
||||
len = 0;
|
||||
|
||||
ret = dcc_resolve(context, id, name + len);
|
||||
free(def_dir);
|
||||
free(name);
|
||||
name = NULL;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dc = DCACHE((*id));
|
||||
|
||||
asprintf_ret = asprintf(&name, ":%s/tktXXXXXX", dc->dir);
|
||||
if (asprintf_ret == -1 || name == NULL) {
|
||||
dcc_close(context, *id);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
|
||||
fd = mkstemp(&name[1]);
|
||||
if (fd < 0) {
|
||||
dcc_close(context, *id);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
free(dc->name);
|
||||
dc->name = name;
|
||||
|
||||
return 0;
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
@@ -457,6 +527,25 @@ static krb5_error_code KRB5_CALLCONV
|
||||
dcc_close(krb5_context context,
|
||||
krb5_ccache id)
|
||||
{
|
||||
krb5_dcache *dc = DCACHE(id);
|
||||
krb5_principal p = NULL;
|
||||
struct stat st;
|
||||
char *primary = NULL;
|
||||
|
||||
/*
|
||||
* If there's no default cache, but we're closing one, and the one we're
|
||||
* closing has been initialized, then make it the default. This makes the
|
||||
* first cache created the default.
|
||||
*
|
||||
* FIXME We should check if `D2FCACHE(dc)' has live credentials.
|
||||
*/
|
||||
if (dc->default_candidate && D2FCACHE(dc) &&
|
||||
krb5_cc_get_principal(context, D2FCACHE(dc), &p) == 0 &&
|
||||
(primary = primary_create(dc)) &&
|
||||
(stat(primary, &st) == -1 || !S_ISREG(st.st_mode) || st.st_size == 0))
|
||||
dcc_set_default(context, id);
|
||||
krb5_free_principal(context, p);
|
||||
free(primary);
|
||||
dcc_release(context, DCACHE(id));
|
||||
return 0;
|
||||
}
|
||||
@@ -545,39 +634,61 @@ dcc_get_version(krb5_context context,
|
||||
}
|
||||
|
||||
struct dcache_iter {
|
||||
int first;
|
||||
char *primary;
|
||||
krb5_dcache *dc;
|
||||
DIR *d;
|
||||
unsigned int first:1;
|
||||
};
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
|
||||
{
|
||||
struct dcache_iter *iter;
|
||||
krb5_error_code ret;
|
||||
char *name;
|
||||
struct dcache_iter *iter = NULL;
|
||||
const char *name = krb5_cc_default_name(context);
|
||||
size_t len;
|
||||
char *p;
|
||||
|
||||
*cursor = NULL;
|
||||
iter = calloc(1, sizeof(*iter));
|
||||
if (iter == NULL)
|
||||
return krb5_enomem(context);
|
||||
iter->first = 1;
|
||||
|
||||
name = copy_default_dcc_cache(context);
|
||||
if (name == NULL) {
|
||||
free(iter);
|
||||
if (strncmp(name, "DIR:", sizeof("DIR:") - 1) != 0) {
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Can't generate DIR caches unless its the default type", ""));
|
||||
N_("Can't list DIR caches unless its the default type", ""));
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
ret = dcc_resolve(context, NULL, name);
|
||||
free(name);
|
||||
if (ret) {
|
||||
if ((iter = calloc(1, sizeof(*iter))) == NULL ||
|
||||
(iter->dc = calloc(1, sizeof(iter->dc[0]))) == NULL ||
|
||||
(iter->dc->dir = strdup(name + sizeof("DIR:") - 1)) == NULL) {
|
||||
if (iter)
|
||||
free(iter->dc);
|
||||
free(iter);
|
||||
return ret;
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
iter->first = 1;
|
||||
p = strrchr(iter->dc->dir, ':');
|
||||
#ifdef WIN32
|
||||
if (p == iter->dc->dir + 1)
|
||||
p = NULL;
|
||||
#endif
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
/* Strip off extra slashes on the end */
|
||||
for (len = strlen(iter->dc->dir);
|
||||
len && ISPATHSEP(iter->dc->dir[len - 1]);
|
||||
len -= len ? 1 : 0) {
|
||||
iter->dc->dir[len - 1] = '\0';
|
||||
}
|
||||
|
||||
/* XXX We need to opendir() here */
|
||||
if ((iter->d = opendir(iter->dc->dir)) == NULL) {
|
||||
free(iter->dc->dir);
|
||||
free(iter->dc);
|
||||
free(iter);
|
||||
krb5_set_error_message(context, KRB5_CC_FORMAT,
|
||||
N_("Can't open DIR %s: %s", ""),
|
||||
iter->dc->dir, strerror(errno));
|
||||
return KRB5_CC_FORMAT;
|
||||
}
|
||||
|
||||
*cursor = iter;
|
||||
return 0;
|
||||
@@ -587,18 +698,49 @@ static krb5_error_code KRB5_CALLCONV
|
||||
dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
|
||||
{
|
||||
struct dcache_iter *iter = cursor;
|
||||
krb5_error_code ret;
|
||||
struct stat st;
|
||||
struct dirent *dentry;
|
||||
char *p = NULL;
|
||||
|
||||
*id = NULL;
|
||||
if (iter == NULL)
|
||||
return krb5_einval(context, 2);
|
||||
|
||||
if (!iter->first) {
|
||||
krb5_clear_error_message(context);
|
||||
return KRB5_CC_END;
|
||||
/* Emit primary subsidiary first */
|
||||
if (iter->first &&
|
||||
(ret = get_default_cache(context, iter->dc, NULL, &iter->primary)) == 0 &&
|
||||
is_filename_cacheish(iter->primary)) {
|
||||
iter->first = 0;
|
||||
ret = KRB5_CC_END;
|
||||
if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, iter->primary) > -1 && p != NULL &&
|
||||
stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
|
||||
ret = krb5_cc_resolve(context, p, id);
|
||||
if (p == NULL)
|
||||
return krb5_enomem(context);
|
||||
free(p);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
/* XXX We need to readdir() here */
|
||||
iter->first = 0;
|
||||
|
||||
for (dentry = readdir(iter->d); dentry; dentry = readdir(iter->d)) {
|
||||
if (!is_filename_cacheish(dentry->d_name) ||
|
||||
(iter->primary && strcmp(dentry->d_name, iter->primary) == 0))
|
||||
continue;
|
||||
p = NULL;
|
||||
ret = KRB5_CC_END;
|
||||
if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, dentry->d_name) > -1 &&
|
||||
p != NULL &&
|
||||
stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
|
||||
ret = krb5_cc_resolve(context, p, id);
|
||||
free(p);
|
||||
if (p == NULL)
|
||||
return krb5_enomem(context);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
}
|
||||
return KRB5_CC_END;
|
||||
}
|
||||
|
||||
@@ -610,9 +752,10 @@ dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
|
||||
if (iter == NULL)
|
||||
return krb5_einval(context, 2);
|
||||
|
||||
/* XXX We need to closedir() here */
|
||||
if (iter->dc)
|
||||
dcc_release(context, iter->dc);
|
||||
(void) closedir(iter->d);
|
||||
free(iter->dc->dir);
|
||||
free(iter->dc);
|
||||
free(iter->primary);
|
||||
free(iter);
|
||||
return 0;
|
||||
}
|
||||
@@ -622,14 +765,22 @@ dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
|
||||
{
|
||||
krb5_dcache *dcfrom = DCACHE(from);
|
||||
krb5_dcache *dcto = DCACHE(to);
|
||||
|
||||
dcfrom->default_candidate = 0;
|
||||
dcto->default_candidate = 1;
|
||||
return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
dcc_get_default_name(krb5_context context, char **str)
|
||||
{
|
||||
return _krb5_expand_default_cc_name(context,
|
||||
KRB5_DEFAULT_CCNAME_DIR,
|
||||
const char *def_cc_colname =
|
||||
krb5_config_get_string_default(context, NULL, KRB5_DEFAULT_CCNAME_DIR,
|
||||
"libdefaults", "default_cc_collection",
|
||||
NULL);
|
||||
|
||||
/* What if def_cc_colname does not start with DIR:? We tolerate it. */
|
||||
return _krb5_expand_default_cc_name(context, def_cc_colname,
|
||||
str);
|
||||
}
|
||||
|
||||
@@ -637,13 +788,10 @@ static krb5_error_code KRB5_CALLCONV
|
||||
dcc_set_default(krb5_context context, krb5_ccache id)
|
||||
{
|
||||
krb5_dcache *dc = DCACHE(id);
|
||||
const char *name;
|
||||
|
||||
name = krb5_cc_get_name(context, D2FCACHE(dc));
|
||||
if (name == NULL)
|
||||
if (dc->sub == NULL)
|
||||
return ENOENT;
|
||||
|
||||
return set_default_cache(context, dc, name);
|
||||
return set_default_cache(context, dc, dc->sub);
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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 },
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -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"; }
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
165
tests/kdc/krb5-cccol.conf.in
Normal file
165
tests/kdc/krb5-cccol.conf.in
Normal 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
|
||||
}
|
Reference in New Issue
Block a user