/*
- * Copyright (c) 2010, JANET(UK)
+ * Copyright (c) 2011, JANET(UK)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
+/*
+ * Attribute provider mechanism.
+ */
+
#include "gssapiP_eap.h"
#include <typeinfo>
#include <exception>
#include <new>
+/* lazy initialisation */
+static GSSEAP_THREAD_ONCE gssEapAttrProvidersInitOnce = GSSEAP_ONCE_INITIALIZER;
+static OM_uint32 gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
+
+static void
+gssEapAttrProvidersInitInternal(void)
+{
+ OM_uint32 major, minor;
+
+ assert(gssEapAttrProvidersInitStatus == GSS_S_UNAVAILABLE);
+
+ major = gssEapRadiusAttrProviderInit(&minor);
+ if (major == GSS_S_COMPLETE)
+ major = gssEapSamlAttrProvidersInit(&minor);
+ if (major == GSS_S_COMPLETE)
+ major = gssEapLocalAttrProviderInit(&minor);
+
+#ifdef GSSEAP_DEBUG
+ assert(major == GSS_S_COMPLETE);
+#endif
+
+ gssEapAttrProvidersInitStatus = major;
+}
+
+static OM_uint32
+gssEapAttrProvidersInit(OM_uint32 *minor)
+{
+ GSSEAP_ONCE(&gssEapAttrProvidersInitOnce, gssEapAttrProvidersInitInternal);
+
+ if (GSS_ERROR(gssEapAttrProvidersInitStatus))
+ *minor = GSSEAP_NO_ATTR_PROVIDERS;
+
+ return gssEapAttrProvidersInitStatus;
+}
+
+OM_uint32
+gssEapAttrProvidersFinalize(OM_uint32 *minor)
+{
+ OM_uint32 major = GSS_S_COMPLETE;
+
+ if (gssEapAttrProvidersInitStatus == GSS_S_COMPLETE) {
+ major = gssEapLocalAttrProviderFinalize(minor);
+ if (major == GSS_S_COMPLETE)
+ major = gssEapSamlAttrProvidersFinalize(minor);
+ if (major == GSS_S_COMPLETE)
+ major = gssEapRadiusAttrProviderFinalize(minor);
+
+ gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
+ }
+
+ return major;
+}
+
static gss_eap_attr_create_provider gssEapAttrFactories[ATTR_TYPE_MAX + 1];
static gss_buffer_desc gssEapAttrPrefixes[ATTR_TYPE_MAX + 1];
*/
gss_eap_attr_ctx::gss_eap_attr_ctx(void)
{
+ m_flags = 0;
+
for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
gss_eap_attr_provider *provider;
return &gssEapAttrPrefixes[type];
}
+bool
+gss_eap_attr_ctx::providerEnabled(unsigned int type) const
+{
+ if (type == ATTR_TYPE_LOCAL &&
+ (m_flags & ATTR_FLAG_DISABLE_LOCAL))
+ return false;
+
+ if (m_providers[type] == NULL)
+ return false;
+
+ return true;
+}
+
+void
+gss_eap_attr_ctx::releaseProvider(unsigned int type)
+{
+ delete m_providers[type];
+ m_providers[type] = NULL;
+}
+
/*
* Initialize a context from an existing context.
*/
{
bool ret = true;
+ m_flags = manager->m_flags;
+
for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
- gss_eap_attr_provider *provider = m_providers[i];
+ gss_eap_attr_provider *provider;
- if (provider == NULL)
+ if (!providerEnabled(i)) {
+ releaseProvider(i);
continue;
+ }
+
+ provider = m_providers[i];
ret = provider->initFromExistingContext(this,
manager->m_providers[i]);
if (ret == false) {
- delete provider;
- m_providers[i] = NULL;
+ releaseProvider(i);
break;
}
}
{
bool ret = true;
+ if (cred != GSS_C_NO_CREDENTIAL &&
+ (cred->flags & GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG)) {
+ m_flags |= ATTR_FLAG_DISABLE_LOCAL;
+ }
+
for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
- gss_eap_attr_provider *provider = m_providers[i];
+ gss_eap_attr_provider *provider;
- if (provider == NULL)
+ if (!providerEnabled(i)) {
+ releaseProvider(i);
continue;
+ }
+
+ provider = m_providers[i];
ret = provider->initFromGssContext(this, cred, ctx);
if (ret == false) {
- delete provider;
- m_providers[i] = NULL;
+ releaseProvider(i);
break;
}
}
return ret;
}
+#define UPDATE_REMAIN(n) do { \
+ p += (n); \
+ remain -= (n); \
+ } while (0)
+
+#define CHECK_REMAIN(n) do { \
+ if (remain < (n)) { \
+ return false; \
+ } \
+ } while (0)
+
/*
* Initialize a context from an exported context or name token
*/
bool
gss_eap_attr_ctx::initFromBuffer(const gss_buffer_t buffer)
{
- bool ret;
- gss_eap_attr_provider *primaryProvider = getPrimaryProvider();
+ bool ret = false;
+ size_t remain = buffer->length;
+ unsigned char *p = (unsigned char *)buffer->value;
+ bool didInit[ATTR_TYPE_MAX + 1];
+ unsigned int type;
- ret = primaryProvider->initFromBuffer(this, buffer);
- if (ret == false)
- return ret;
+ for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++)
+ didInit[type] = false;
- for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
- gss_eap_attr_provider *provider = m_providers[i];
+ /* flags */
+ CHECK_REMAIN(4);
+ m_flags = load_uint32_be(p);
+ UPDATE_REMAIN(4);
+
+ while (remain) {
+ OM_uint32 type;
+ gss_buffer_desc providerToken;
+ gss_eap_attr_provider *provider;
+
+ /* TLV encoding of provider type, length, value */
+ CHECK_REMAIN(4);
+ type = load_uint32_be(p);
+ UPDATE_REMAIN(4);
+
+ CHECK_REMAIN(4);
+ providerToken.length = load_uint32_be(p);
+ UPDATE_REMAIN(4);
+
+ CHECK_REMAIN(providerToken.length);
+ providerToken.value = p;
+ UPDATE_REMAIN(providerToken.length);
+
+ if (type < ATTR_TYPE_MIN || type > ATTR_TYPE_MAX ||
+ didInit[type])
+ return false;
+
+ if (!providerEnabled(type)) {
+ releaseProvider(type);
+ continue;
+ }
+
+ provider = m_providers[type];
- if (provider == primaryProvider)
+ ret = provider->initFromBuffer(this, &providerToken);
+ if (ret == false) {
+ releaseProvider(type);
+ break;
+ }
+ didInit[type] = true;
+ }
+
+ /*
+ * The call the initFromGssContext methods for attribute
+ * providers that can initialize themselves from other
+ * providers.
+ */
+ for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++) {
+ gss_eap_attr_provider *provider;
+
+ if (didInit[type])
continue;
+ provider = m_providers[type];
+
ret = provider->initFromGssContext(this,
GSS_C_NO_CREDENTIAL,
GSS_C_NO_CONTEXT);
if (ret == false) {
- delete provider;
- m_providers[i] = NULL;
+ releaseProvider(type);
break;
}
}
/*
* Set an attribute
*/
-void
+bool
gss_eap_attr_ctx::setAttribute(int complete,
const gss_buffer_t attr,
const gss_buffer_t value)
gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
unsigned int type;
gss_eap_attr_provider *provider;
+ bool ret = false;
decomposeAttributeName(attr, &type, &suffix);
provider = m_providers[type];
if (provider != NULL) {
- provider->setAttribute(complete,
- (type == ATTR_TYPE_LOCAL) ? attr : &suffix,
- value);
- } else {
- /* XXX TODO throw exception */
+ ret = provider->setAttribute(complete,
+ (type == ATTR_TYPE_LOCAL) ? attr : &suffix,
+ value);
}
+
+ return ret;
}
/*
* Delete an attrbiute
*/
-void
+bool
gss_eap_attr_ctx::deleteAttribute(const gss_buffer_t attr)
{
gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
unsigned int type;
gss_eap_attr_provider *provider;
+ bool ret = false;
decomposeAttributeName(attr, &type, &suffix);
provider = m_providers[type];
- if (provider != NULL)
- provider->deleteAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix);
+ if (provider != NULL) {
+ ret = provider->deleteAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix);
+ }
+
+ return ret;
}
/*
};
static bool
-addAttribute(const gss_eap_attr_provider *provider,
+addAttribute(const gss_eap_attr_provider *provider GSSEAP_UNUSED,
const gss_buffer_t attribute,
void *data)
{
void
gss_eap_attr_ctx::exportToBuffer(gss_buffer_t buffer) const
{
- const gss_eap_attr_provider *primaryProvider = getPrimaryProvider();
+ OM_uint32 tmpMinor;
+ gss_buffer_desc providerTokens[ATTR_TYPE_MAX + 1];
+ size_t length = 4; /* m_flags */
+ unsigned char *p;
+ unsigned int i;
- primaryProvider->exportToBuffer(buffer);
+ for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
+ providerTokens[i].length = 0;
+ providerTokens[i].value = NULL;
+ }
+
+ for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
+ gss_eap_attr_provider *provider = m_providers[i];
+
+ if (provider == NULL)
+ continue;
+
+ provider->exportToBuffer(&providerTokens[i]);
+
+ if (providerTokens[i].value != NULL)
+ length += 8 + providerTokens[i].length;
+ }
+
+ buffer->length = length;
+ buffer->value = GSSEAP_MALLOC(length);
+ if (buffer->value == NULL)
+ throw new std::bad_alloc;
+
+ p = (unsigned char *)buffer->value;
+ store_uint32_be(m_flags, p);
+ p += 4;
+
+ for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
+ if (providerTokens[i].value == NULL)
+ continue;
+
+ store_uint32_be(i, p);
+ p += 4;
+ store_uint32_be(providerTokens[i].length, p);
+ p += 4;
+ memcpy(p, providerTokens[i].value, providerTokens[i].length);
+ p += providerTokens[i].length;
+
+ gss_release_buffer(&tmpMinor, &providerTokens[i]);
+ }
}
/*
return expiryTime;
}
-/*
- * Map C++ exception to GSS status
- */
-static OM_uint32
-mapException(OM_uint32 *minor, std::exception &e)
+OM_uint32
+gss_eap_attr_ctx::mapException(OM_uint32 *minor, std::exception &e) const
{
- OM_uint32 major = GSS_S_FAILURE;
+ unsigned int i;
+ OM_uint32 major;
- /* XXX TODO implement other mappings */
- if (typeid(e) == typeid(std::bad_alloc))
+ /* Errors we handle ourselves */
+ major = GSS_S_FAILURE;
+
+ if (typeid(e) == typeid(std::bad_alloc)) {
*minor = ENOMEM;
- else
- *minor = 0;
+ goto cleanup;
+ }
-#ifdef GSSEAP_DEBUG
+ /* Errors we delegate to providers */
+ major = GSS_S_CONTINUE_NEEDED;
+
+ for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
+ gss_eap_attr_provider *provider = m_providers[i];
+
+ if (provider == NULL)
+ continue;
+
+ major = provider->mapException(minor, e);
+ if (major != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+
+ if (major == GSS_S_CONTINUE_NEEDED) {
+ *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
+ major = GSS_S_FAILURE;
+ }
+
+cleanup:
+#if 0
/* rethrow for now for debugging */
throw e;
#endif
+ assert(GSS_ERROR(major));
+
return major;
}
gss_OID *MN_mech,
gss_buffer_set_t *attrs)
{
- if (name->attrCtx == NULL)
+ OM_uint32 major;
+
+ if (name_is_MN != NULL)
+ *name_is_MN = (name->mechanismUsed != GSS_C_NULL_OID);
+
+ if (MN_mech != NULL) {
+ major = gssEapCanonicalizeOid(minor, name->mechanismUsed,
+ OID_FLAG_NULL_VALID, MN_mech);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
return GSS_S_UNAVAILABLE;
+ }
try {
- if (!name->attrCtx->getAttributeTypes(attrs))
+ if (!name->attrCtx->getAttributeTypes(attrs)) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
return GSS_S_UNAVAILABLE;
+ }
} catch (std::exception &e) {
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
display_value->value = NULL;
}
- if (name->attrCtx == NULL)
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
return GSS_S_UNAVAILABLE;
+ }
try {
if (!name->attrCtx->getAttribute(attr, authenticated, complete,
- value, display_value, more))
+ value, display_value, more)) {
+ *minor = GSSEAP_NO_SUCH_ATTR;
+ gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
+ (int)attr->length, (char *)attr->value);
return GSS_S_UNAVAILABLE;
+ }
} catch (std::exception &e) {
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
gss_name_t name,
gss_buffer_t attr)
{
- if (name->attrCtx == NULL)
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
return GSS_S_UNAVAILABLE;
try {
- name->attrCtx->deleteAttribute(attr);
- } catch (std::exception &ex) {
- return mapException(minor, ex);
+ if (!name->attrCtx->deleteAttribute(attr)) {
+ *minor = GSSEAP_NO_SUCH_ATTR;
+ gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
+ (int)attr->length, (char *)attr->value);
+ return GSS_S_UNAVAILABLE;
+ }
+ } catch (std::exception &e) {
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
gss_buffer_t attr,
gss_buffer_t value)
{
- if (name->attrCtx == NULL)
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
return GSS_S_UNAVAILABLE;
try {
- name->attrCtx->setAttribute(complete, attr, value);
- } catch (std::exception &ex) {
- return mapException(minor, ex);
+ if (!name->attrCtx->setAttribute(complete, attr, value)) {
+ *minor = GSSEAP_NO_SUCH_ATTR;
+ gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
+ (int)attr->length, (char *)attr->value);
+ return GSS_S_UNAVAILABLE;
+ }
+ } catch (std::exception &e) {
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
return GSS_S_COMPLETE;
}
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
+ return GSS_S_UNAVAILABLE;
+
try {
name->attrCtx->exportToBuffer(buffer);
} catch (std::exception &e) {
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
assert(name->attrCtx == NULL);
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
+ return GSS_S_UNAVAILABLE;
+
if (buffer->length != 0) {
try {
ctx = new gss_eap_attr_ctx();
if (!ctx->initFromBuffer(buffer)) {
delete ctx;
+ *minor = GSSEAP_BAD_ATTR_TOKEN;
return GSS_S_DEFECTIVE_TOKEN;
}
name->attrCtx = ctx;
} catch (std::exception &e) {
delete ctx;
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
}
assert(out->attrCtx == NULL);
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
+ return GSS_S_UNAVAILABLE;
+
try {
if (in->attrCtx != NULL) {
ctx = new gss_eap_attr_ctx();
if (!ctx->initFromExistingContext(in->attrCtx)) {
delete ctx;
+ *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
return GSS_S_FAILURE;
}
out->attrCtx = ctx;
}
} catch (std::exception &e) {
delete ctx;
- return mapException(minor, e);
+ return in->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
gss_buffer_t type_id,
gss_any_t *output)
{
- if (name->attrCtx == NULL)
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
return GSS_S_UNAVAILABLE;
try {
*output = name->attrCtx->mapToAny(authenticated, type_id);
} catch (std::exception &e) {
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
gss_buffer_t type_id,
gss_any_t *input)
{
- if (name->attrCtx == NULL)
+ if (name->attrCtx == NULL) {
+ *minor = GSSEAP_NO_ATTR_CONTEXT;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
return GSS_S_UNAVAILABLE;
try {
name->attrCtx->releaseAnyNameMapping(type_id, *input);
*input = NULL;
} catch (std::exception &e) {
- return mapException(minor, e);
+ return name->attrCtx->mapException(minor, e);
}
return GSS_S_COMPLETE;
if (name->attrCtx != NULL)
delete name->attrCtx;
+ *minor = 0;
return GSS_S_COMPLETE;
}
* Public accessor for initialisng a context from a GSS context. Also
* sets expiry time on GSS context as a side-effect.
*/
-struct gss_eap_attr_ctx *
-gssEapCreateAttrContext(gss_cred_id_t gssCred,
- gss_ctx_id_t gssCtx)
+OM_uint32
+gssEapCreateAttrContext(OM_uint32 *minor,
+ gss_cred_id_t gssCred,
+ gss_ctx_id_t gssCtx,
+ struct gss_eap_attr_ctx **pAttrContext,
+ time_t *pExpiryTime)
{
- gss_eap_attr_ctx *ctx;
+ gss_eap_attr_ctx *ctx = NULL;
+ OM_uint32 major;
assert(gssCtx != GSS_C_NO_CONTEXT);
- ctx = new gss_eap_attr_ctx();
- if (!ctx->initFromGssContext(gssCred, gssCtx)) {
- delete ctx;
- return NULL;
+ major = gssEapAttrProvidersInit(minor);
+ if (GSS_ERROR(major))
+ return major;
+
+ *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
+ major = GSS_S_FAILURE;
+
+ try {
+ ctx = new gss_eap_attr_ctx();
+ if (ctx->initFromGssContext(gssCred, gssCtx)) {
+ *minor = 0;
+ major = GSS_S_COMPLETE;
+ } else {
+ delete ctx;
+ }
+ } catch (std::exception &e) {
+ if (ctx != NULL)
+ major = ctx->mapException(minor, e);
}
- gssCtx->expiryTime = ctx->getExpiryTime();
+ if (major == GSS_S_COMPLETE) {
+ *pAttrContext = ctx;
+ *pExpiryTime = ctx->getExpiryTime();
+ }
- return ctx;
+ return major;
}