gssapi: credential store extensions (#451)
Implement the GSS-API credential store API extensions defined by MIT here: https://k5wiki.kerberos.org/wiki/Projects/Credential_Store_extensions Note: we kill off gss_acquire_cred_ext() here. This was never a public API, although mechanisms could have implemented it and I briefly used it in my BrowserID prototype mechanism. gss_acquire_cred_ext_from() occupies the place in the dispatch table where gss_acquire_cred_ext() used to, but this structure was never visible outside Heimdal (i.e. it is only used by internal mechanisms); (Mechanisms that need to accept arbitrary key/value dictionaries from applications should now implement gss_acquire_cred_from().)
This commit is contained in:

committed by
Nico Williams

parent
a7d42cdf6b
commit
e0bb9c10ca
@@ -33,6 +33,44 @@
|
||||
|
||||
#include "gsskrb5_locl.h"
|
||||
|
||||
/*
|
||||
* Find an element in a cred store. Returns GSS_S_COMPLETE if the cred store
|
||||
* is absent or well formed, irrespective of whether the element exists. The
|
||||
* caller should check for *value != NULL before using; values are typically
|
||||
* optional, hence this behavior. (The caller should validate the return
|
||||
* value at least once though, to check it is well-formed.)
|
||||
*/
|
||||
OM_uint32
|
||||
__gsskrb5_cred_store_find(OM_uint32 *minor_status,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
const char *key,
|
||||
const char **value)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
*value = NULL;
|
||||
|
||||
if (cred_store == GSS_C_NO_CRED_STORE)
|
||||
return GSS_S_COMPLETE;
|
||||
else if (cred_store->count == 0) {
|
||||
*minor_status = GSS_KRB5_S_G_BAD_USAGE;
|
||||
return GSS_S_NO_CRED;
|
||||
}
|
||||
|
||||
for (i = 0; i < cred_store->count; i++) {
|
||||
if (strcmp(key, cred_store->elements[i].key) == 0) {
|
||||
if (*value) {
|
||||
*value = NULL;
|
||||
*minor_status = GSS_KRB5_S_G_BAD_USAGE;
|
||||
return GSS_S_DUPLICATE_ELEMENT;
|
||||
}
|
||||
*value = cred_store->elements[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32
|
||||
__gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
|
||||
krb5_context context,
|
||||
@@ -58,13 +96,21 @@ __gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
get_system_keytab(krb5_context context, krb5_keytab *keytab)
|
||||
get_system_keytab(krb5_context context,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
krb5_keytab *keytab)
|
||||
{
|
||||
krb5_error_code kret;
|
||||
const char *cs_ktname;
|
||||
OM_uint32 tmp;
|
||||
|
||||
__gsskrb5_cred_store_find(&tmp, cred_store, "keytab", &cs_ktname);
|
||||
|
||||
HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
|
||||
|
||||
if (_gsskrb5_keytab != NULL) {
|
||||
if (cs_ktname)
|
||||
kret = krb5_kt_resolve(context, cs_ktname, keytab);
|
||||
else if (_gsskrb5_keytab != NULL) {
|
||||
char *name = NULL;
|
||||
|
||||
kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
|
||||
@@ -82,15 +128,26 @@ get_system_keytab(krb5_context context, krb5_keytab *keytab)
|
||||
|
||||
static krb5_error_code
|
||||
get_client_keytab(krb5_context context,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
krb5_const_principal principal,
|
||||
krb5_keytab *keytab)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *name = NULL;
|
||||
const char *cs_ktname;
|
||||
OM_uint32 tmp;
|
||||
|
||||
__gsskrb5_cred_store_find(&tmp, cred_store, "client_keytab", &cs_ktname);
|
||||
|
||||
if (cs_ktname)
|
||||
ret = krb5_kt_resolve(context, cs_ktname, keytab);
|
||||
else {
|
||||
char *name = NULL;
|
||||
ret = _krb5_kt_client_default_name(context, &name);
|
||||
if (ret == 0)
|
||||
ret = krb5_kt_resolve(context, name, keytab);
|
||||
krb5_xfree(name);
|
||||
}
|
||||
|
||||
ret = _krb5_kt_client_default_name(context, &name);
|
||||
if (ret == 0)
|
||||
ret = krb5_kt_resolve(context, name, keytab);
|
||||
if (ret == 0 && principal) {
|
||||
krb5_keytab_entry entry;
|
||||
|
||||
@@ -99,14 +156,31 @@ get_client_keytab(krb5_context context,
|
||||
if (ret == 0)
|
||||
krb5_kt_free_entry(context, &entry);
|
||||
}
|
||||
krb5_xfree(name);
|
||||
|
||||
if (ret)
|
||||
ret = get_system_keytab(context, keytab);
|
||||
ret = get_system_keytab(context, GSS_C_NO_CRED_STORE, keytab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_boolean
|
||||
is_valid_password_cred_store(gss_const_key_value_set_t cred_store)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (cred_store == GSS_C_NO_CRED_STORE)
|
||||
return TRUE;
|
||||
|
||||
/* XXX don't check keytab, someday we will allow password+acceptor creds */
|
||||
for (i = 0; i < cred_store->count; i++) {
|
||||
if (strcmp(cred_store->elements[i].key, "ccache") == 0 ||
|
||||
strcmp(cred_store->elements[i].key, "client_keytab") == 0)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function produces a cred with a MEMORY ccache containing a TGT
|
||||
* acquired with a password.
|
||||
@@ -116,8 +190,9 @@ acquire_cred_with_password(OM_uint32 *minor_status,
|
||||
krb5_context context,
|
||||
const char *password,
|
||||
OM_uint32 time_req,
|
||||
gss_const_OID desired_mech,
|
||||
gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
gsskrb5_cred handle)
|
||||
{
|
||||
OM_uint32 ret = GSS_S_FAILURE;
|
||||
@@ -128,6 +203,11 @@ acquire_cred_with_password(OM_uint32 *minor_status,
|
||||
time_t now;
|
||||
OM_uint32 left;
|
||||
|
||||
if (!is_valid_password_cred_store(cred_store)) {
|
||||
*minor_status = GSS_KRB5_S_G_BAD_PASSWORD_CRED_STORE;
|
||||
return GSS_S_NO_CRED;
|
||||
}
|
||||
|
||||
if (cred_usage == GSS_C_ACCEPT) {
|
||||
/*
|
||||
* TODO: Here we should eventually support user2user (when we get
|
||||
@@ -212,11 +292,12 @@ static OM_uint32
|
||||
acquire_initiator_cred(OM_uint32 *minor_status,
|
||||
krb5_context context,
|
||||
OM_uint32 time_req,
|
||||
gss_const_OID desired_mech,
|
||||
gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
gsskrb5_cred handle)
|
||||
{
|
||||
OM_uint32 ret = GSS_S_FAILURE;
|
||||
OM_uint32 ret;
|
||||
krb5_creds cred;
|
||||
krb5_get_init_creds_opt *opt;
|
||||
krb5_principal def_princ = NULL;
|
||||
@@ -225,11 +306,19 @@ acquire_initiator_cred(OM_uint32 *minor_status,
|
||||
krb5_keytab keytab = NULL;
|
||||
krb5_error_code kret = 0;
|
||||
OM_uint32 left;
|
||||
const char *cs_ccache_name;
|
||||
time_t lifetime = 0;
|
||||
time_t now;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
ret = __gsskrb5_cred_store_find(minor_status, cred_store,
|
||||
"ccache", &cs_ccache_name);
|
||||
if (GSS_ERROR(ret))
|
||||
return ret;
|
||||
|
||||
ret = GSS_S_FAILURE;
|
||||
|
||||
/*
|
||||
* Get current time early so we can set handle->endtime to a value that
|
||||
* cannot accidentally be past the real endtime. We need a variant of
|
||||
@@ -239,7 +328,8 @@ acquire_initiator_cred(OM_uint32 *minor_status,
|
||||
|
||||
/*
|
||||
* First look for a ccache that has the desired_name (which may be
|
||||
* the default credential name).
|
||||
* the default credential name), unless a specific credential cache
|
||||
* was included in cred_store.
|
||||
*
|
||||
* If we don't have an unexpired credential, acquire one with a
|
||||
* keytab.
|
||||
@@ -250,7 +340,7 @@ acquire_initiator_cred(OM_uint32 *minor_status,
|
||||
* If we don't have any such ccache, then use a MEMORY ccache.
|
||||
*/
|
||||
|
||||
if (handle->principal != NULL) {
|
||||
if (handle->principal != NULL && cs_ccache_name == NULL) {
|
||||
/*
|
||||
* Not default credential case. See if we can find a ccache in
|
||||
* the cccol for the desired_name.
|
||||
@@ -277,7 +367,10 @@ acquire_initiator_cred(OM_uint32 *minor_status,
|
||||
* Either desired_name was GSS_C_NO_NAME (default cred) or
|
||||
* krb5_cc_cache_match() failed (or found expired).
|
||||
*/
|
||||
kret = krb5_cc_default(context, &def_ccache);
|
||||
if (cs_ccache_name)
|
||||
kret = krb5_cc_resolve(context, cs_ccache_name, &def_ccache);
|
||||
else
|
||||
kret = krb5_cc_default(context, &def_ccache);
|
||||
if (kret != 0)
|
||||
goto try_keytab;
|
||||
kret = krb5_cc_get_lifetime(context, def_ccache, &lifetime);
|
||||
@@ -319,7 +412,7 @@ try_keytab:
|
||||
if (kret)
|
||||
goto end;
|
||||
}
|
||||
kret = get_client_keytab(context, handle->principal, &keytab);
|
||||
kret = get_client_keytab(context, cred_store, handle->principal, &keytab);
|
||||
if (kret)
|
||||
goto end;
|
||||
|
||||
@@ -398,8 +491,9 @@ static OM_uint32
|
||||
acquire_acceptor_cred(OM_uint32 * minor_status,
|
||||
krb5_context context,
|
||||
OM_uint32 time_req,
|
||||
gss_const_OID desired_mech,
|
||||
gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
gsskrb5_cred handle)
|
||||
{
|
||||
OM_uint32 ret;
|
||||
@@ -407,7 +501,7 @@ acquire_acceptor_cred(OM_uint32 * minor_status,
|
||||
|
||||
ret = GSS_S_FAILURE;
|
||||
|
||||
kret = get_system_keytab(context, &handle->keytab);
|
||||
kret = get_system_keytab(context, cred_store, &handle->keytab);
|
||||
if (kret)
|
||||
goto end;
|
||||
|
||||
@@ -449,18 +543,23 @@ end:
|
||||
return (ret);
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_from
|
||||
(OM_uint32 * minor_status,
|
||||
gss_const_name_t desired_name,
|
||||
OM_uint32 time_req,
|
||||
const gss_OID_set desired_mechs,
|
||||
gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_const_key_value_set_t cred_store,
|
||||
gss_cred_id_t * output_cred_handle,
|
||||
gss_OID_set * actual_mechs,
|
||||
OM_uint32 * time_rec
|
||||
gss_OID_set *actual_mechs,
|
||||
OM_uint32 *time_rec
|
||||
)
|
||||
{
|
||||
krb5_context context;
|
||||
gsskrb5_cred handle;
|
||||
OM_uint32 ret;
|
||||
const char *password = NULL;
|
||||
|
||||
if (desired_mechs) {
|
||||
int present = 0;
|
||||
@@ -475,43 +574,6 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
|
||||
}
|
||||
}
|
||||
|
||||
ret = _gsskrb5_acquire_cred_ext(minor_status,
|
||||
desired_name,
|
||||
GSS_C_NO_OID,
|
||||
NULL,
|
||||
time_req,
|
||||
GSS_KRB5_MECHANISM,
|
||||
cred_usage,
|
||||
output_cred_handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle,
|
||||
NULL, time_rec, NULL, actual_mechs);
|
||||
if (ret) {
|
||||
OM_uint32 tmp;
|
||||
_gsskrb5_release_cred(&tmp, output_cred_handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
(OM_uint32 * minor_status,
|
||||
gss_const_name_t desired_name,
|
||||
gss_const_OID credential_type,
|
||||
const void *credential_data,
|
||||
OM_uint32 time_req,
|
||||
gss_const_OID desired_mech,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_cred_id_t * output_cred_handle
|
||||
)
|
||||
{
|
||||
krb5_context context;
|
||||
gsskrb5_cred handle;
|
||||
OM_uint32 ret;
|
||||
|
||||
cred_usage &= GSS_C_OPTION_MASK;
|
||||
|
||||
if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE &&
|
||||
@@ -520,6 +582,11 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
ret = __gsskrb5_cred_store_find(minor_status, cred_store,
|
||||
"password", &password);
|
||||
if (GSS_ERROR(ret))
|
||||
return ret;
|
||||
|
||||
GSSAPI_KRB5_INIT(&context);
|
||||
|
||||
*output_cred_handle = GSS_C_NO_CREDENTIAL;
|
||||
@@ -542,57 +609,24 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
}
|
||||
}
|
||||
|
||||
if (credential_type != GSS_C_NO_OID &&
|
||||
gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
|
||||
/* Acquire a cred with a password */
|
||||
gss_const_buffer_t pwbuf = credential_data;
|
||||
char *pw;
|
||||
|
||||
if (pwbuf == NULL) {
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
free(handle);
|
||||
*minor_status = KRB5_NOCREDS_SUPPLIED; /* see below */
|
||||
return GSS_S_CALL_INACCESSIBLE_READ;
|
||||
}
|
||||
|
||||
/* NUL-terminate the password, if it wasn't already */
|
||||
pw = strndup(pwbuf->value, pwbuf->length);
|
||||
if (pw == NULL) {
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
free(handle);
|
||||
*minor_status = krb5_enomem(context);
|
||||
return GSS_S_CALL_INACCESSIBLE_READ;
|
||||
}
|
||||
ret = acquire_cred_with_password(minor_status, context, pw, time_req,
|
||||
desired_mech, cred_usage, handle);
|
||||
free(pw);
|
||||
if (password) {
|
||||
ret = acquire_cred_with_password(minor_status, context, password, time_req,
|
||||
desired_mechs, cred_usage, cred_store, handle);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
krb5_free_principal(context, handle->principal);
|
||||
free(handle);
|
||||
return (ret);
|
||||
}
|
||||
} else if (credential_type != GSS_C_NO_OID) {
|
||||
/*
|
||||
* _gss_acquire_cred_ext() called with something other than a password.
|
||||
*
|
||||
* Not supported.
|
||||
*
|
||||
* _gss_acquire_cred_ext() is not a supported public interface, so
|
||||
* we don't have to try too hard as to minor status codes here.
|
||||
*/
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
free(handle);
|
||||
*minor_status = ENOTSUP;
|
||||
return GSS_S_FAILURE;
|
||||
} else {
|
||||
/*
|
||||
* Acquire a credential from the background credential store (ccache,
|
||||
* keytab).
|
||||
* Acquire a credential from the specified or background credential
|
||||
* store (ccache, keytab).
|
||||
*/
|
||||
if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
|
||||
ret = acquire_initiator_cred(minor_status, context, time_req,
|
||||
desired_mech, cred_usage, handle);
|
||||
desired_mechs, cred_usage,
|
||||
cred_store, handle);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
krb5_free_principal(context, handle->principal);
|
||||
@@ -602,7 +636,8 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
}
|
||||
if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
|
||||
ret = acquire_acceptor_cred(minor_status, context, time_req,
|
||||
desired_mech, cred_usage, handle);
|
||||
desired_mechs, cred_usage,
|
||||
cred_store, handle);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
|
||||
krb5_free_principal(context, handle->principal);
|
||||
@@ -615,6 +650,10 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
|
||||
&handle->mechanisms);
|
||||
handle->usage = cred_usage;
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle,
|
||||
NULL, time_rec, NULL, actual_mechs);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
if (handle->mechanisms != NULL)
|
||||
gss_release_oid_set(NULL, &handle->mechanisms);
|
||||
@@ -623,7 +662,6 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
|
||||
free(handle);
|
||||
return (ret);
|
||||
}
|
||||
handle->usage = cred_usage;
|
||||
*minor_status = 0;
|
||||
*output_cred_handle = (gss_cred_id_t)handle;
|
||||
return (GSS_S_COMPLETE);
|
||||
|
Reference in New Issue
Block a user