X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=util_radius.cpp;h=083b3e70426434d6712797a9a653da1adac5cb0a;hp=b84dc738399cb9b4d0c2250584575c1e8d316ac3;hb=ae79fdae047f980d01b2b4e84ccea52e24d8c7a0;hpb=9af21373654beae5e003dcc05806abc577a50b68 diff --git a/util_radius.cpp b/util_radius.cpp index b84dc73..083b3e7 100644 --- a/util_radius.cpp +++ b/util_radius.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, JANET(UK) + * Copyright (c) 2011, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,14 @@ * SUCH DAMAGE. */ +/* + * RADIUS attribute provider implementation. + */ + #include "gssapiP_eap.h" /* stuff that should be provided by libradsec/libfreeradius-radius */ -#define VENDORATTR(vendor, attr) ((vendor) << 16 | (attr)) +#define VENDORATTR(vendor, attr) (((vendor) << 16) | (attr)) #ifndef ATTRID #define ATTRID(attr) ((attr) & 0xFFFF) @@ -44,71 +48,21 @@ static gss_buffer_desc radiusUrnPrefix = { (void *)"urn:x-radius:" }; -static struct rs_error * -radiusAllocHandle(const char *configFile, - rs_handle **pHandle) -{ - rs_handle *rh; - struct rs_alloc_scheme ralloc; - - *pHandle = NULL; - - if (configFile == NULL || configFile[0] == '\0') - configFile = RS_CONFIG_FILE; - - if (rs_context_create(&rh, RS_DICT_FILE) != 0) - return NULL; - - ralloc.calloc = gssEapCalloc; - ralloc.malloc = gssEapMalloc; - ralloc.free = gssEapFree; - ralloc.realloc = gssEapRealloc; - - rs_context_set_alloc_scheme(rh, &ralloc); - - if (rs_context_read_config(rh, configFile) != 0) { - rs_context_destroy(rh); - return rs_err_ctx_pop(rh); - } - - *pHandle = rh; - return NULL; -} +static VALUE_PAIR *copyAvps(const VALUE_PAIR *src); gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void) { - m_rh = NULL; m_vps = NULL; m_authenticated = false; } gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void) { - if (m_rh != NULL) - rs_context_destroy(m_rh); if (m_vps != NULL) pairfree(&m_vps); } bool -gss_eap_radius_attr_provider::allocRadHandle(const std::string &configFile) -{ - m_configFile.assign(configFile); - - /* - * Currently none of the FreeRADIUS functions we use here actually take - * a handle, so we may as well leave it as NULL. - */ -#if 0 - radiusAllocHandle(m_configFile.c_str(), &m_rh); - - return (m_rh != NULL); -#else - return true; -#endif -} - -bool gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager, const gss_eap_attr_provider *ctx) { @@ -119,11 +73,10 @@ gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *ma radius = static_cast(ctx); - if (!allocRadHandle(radius->m_configFile)) - return false; - if (radius->m_vps != NULL) - m_vps = paircopy(const_cast(radius->getAvps())); + m_vps = copyAvps(const_cast(radius->getAvps())); + + m_authenticated = radius->m_authenticated; return true; } @@ -133,22 +86,18 @@ gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager const gss_cred_id_t gssCred, const gss_ctx_id_t gssCtx) { - std::string configFile(RS_CONFIG_FILE); - if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx)) return false; - if (gssCred != GSS_C_NO_CREDENTIAL && gssCred->radiusConfigFile != NULL) - configFile.assign(gssCred->radiusConfigFile); - - if (!allocRadHandle(configFile)) - return false; - if (gssCtx != GSS_C_NO_CONTEXT) { if (gssCtx->acceptorCtx.vps != NULL) { - m_vps = paircopy(gssCtx->acceptorCtx.vps); + m_vps = copyAvps(gssCtx->acceptorCtx.vps); if (m_vps == NULL) return false; + + /* We assume libradsec validated this for us */ + assert(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL); + m_authenticated = true; } } @@ -169,32 +118,90 @@ alreadyAddedAttributeP(std::vector &attrs, VALUE_PAIR *vp) } static bool -isHiddenAttributeP(int attrid, uint16_t vendor) +isSecretAttributeP(uint16_t attrid, uint16_t vendor) { - bool ret = false; + bool bSecretAttribute = false; switch (vendor) { case VENDORPEC_MS: switch (attrid) { case PW_MS_MPPE_SEND_KEY: case PW_MS_MPPE_RECV_KEY: - ret = true; + bSecretAttribute = true; break; default: break; } + default: + break; + } + + return bSecretAttribute; +} + +static bool +isSecretAttributeP(uint32_t attribute) +{ + return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute)); +} + +static bool +isInternalAttributeP(uint16_t attrid, uint16_t vendor) +{ + bool bInternalAttribute = false; + + /* should have been filtered */ + assert(!isSecretAttributeP(attrid, vendor)); + + switch (vendor) { case VENDORPEC_UKERNA: - ret = true; + bInternalAttribute = true; break; default: break; } - return ret; + return bInternalAttribute; +} + +static bool +isInternalAttributeP(uint32_t attribute) +{ + return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute)); +} + +/* + * Copy AVP list, same as paircopy except it filters out attributes + * containing keys. + */ +static VALUE_PAIR * +copyAvps(const VALUE_PAIR *src) +{ + const VALUE_PAIR *vp; + VALUE_PAIR *dst = NULL, **pDst = &dst; + + for (vp = src; vp != NULL; vp = vp->next) { + VALUE_PAIR *vpcopy; + + if (isSecretAttributeP(vp->attribute)) + continue; + + vpcopy = paircopyvp(vp); + if (vpcopy == NULL) { + pairfree(&dst); + throw new std::bad_alloc; + return NULL; + } + *pDst = vpcopy; + pDst = &vpcopy->next; + } + + return dst; } bool -gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, void *data) const +gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, + void *data) const { VALUE_PAIR *vp; std::vector seen; @@ -202,7 +209,9 @@ gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addA for (vp = m_vps; vp != NULL; vp = vp->next) { gss_buffer_desc attribute; char attrid[64]; - if (isHiddenAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute))) + + /* Don't advertise attributes that are internal to the GSS-EAP mechanism */ + if (isInternalAttributeP(vp->attribute)) continue; if (alreadyAddedAttributeP(seen, vp)) @@ -223,16 +232,89 @@ gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addA return true; } -void +uint32_t +getAttributeId(const gss_buffer_t attr) +{ + OM_uint32 tmpMinor; + gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER; + DICT_ATTR *da; + char *s; + uint32_t attrid = 0; + + if (attr->length < radiusUrnPrefix.length || + memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0) + return 0; + + /* need to duplicate because attr may not be NUL terminated */ + duplicateBuffer(*attr, &strAttr); + s = (char *)strAttr.value + radiusUrnPrefix.length; + + if (isdigit(*s)) { + attrid = strtoul(s, NULL, 10); + } else { + da = dict_attrbyname(s); + if (da != NULL) + attrid = da->attr; + } + + gss_release_buffer(&tmpMinor, &strAttr); + + return attrid; +} + +bool +gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED, + uint32_t attrid, + const gss_buffer_t value) +{ + OM_uint32 major = GSS_S_UNAVAILABLE, minor; + + if (!isSecretAttributeP(attrid) && + !isInternalAttributeP(attrid)) { + deleteAttribute(attrid); + + major = gssEapRadiusAddAvp(&minor, &m_vps, + ATTRID(attrid), VENDOR(attrid), + value); + } + + return !GSS_ERROR(major); +} + +bool gss_eap_radius_attr_provider::setAttribute(int complete, const gss_buffer_t attr, const gss_buffer_t value) { + uint32_t attrid = getAttributeId(attr); + + if (!attrid) + return false; + + return setAttribute(complete, attrid, value); } -void -gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t value) +bool +gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid) +{ + if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) || + pairfind(m_vps, attrid) == NULL) + return false; + + pairdelete(&m_vps, attrid); + + return true; +} + +bool +gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr) { + uint32_t attrid = getAttributeId(attr); + + if (!attrid) + return false; + + return deleteAttribute(attrid); } bool @@ -243,56 +325,29 @@ gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr, gss_buffer_t display_value, int *more) const { - OM_uint32 tmpMinor; - gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER; - DICT_ATTR *da; - int attrid; - char *s; + uint32_t attrid; - duplicateBuffer(*attr, &strAttr); - s = (char *)strAttr.value; - - if (attr->length < radiusUrnPrefix.length || - memcmp(s, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0) + attrid = getAttributeId(attr); + if (!attrid) return false; - s += radiusUrnPrefix.length; - - if (isdigit(*s)) { - attrid = strtoul(s, NULL, 10); - } else { - da = dict_attrbyname(s); - if (da == NULL) { - gss_release_buffer(&tmpMinor, &strAttr); - return false; - } - attrid = da->attr; - } - - gss_release_buffer(&tmpMinor, &strAttr); - return getAttribute(attrid, authenticated, complete, value, display_value, more); } bool -gss_eap_radius_attr_provider::getAttribute(uint16_t vattrid, - uint16_t vendor, +gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) const { - uint32_t attrid = VENDORATTR(vendor, vattrid); VALUE_PAIR *vp; int i = *more, count = 0; *more = 0; - if (isHiddenAttributeP(attrid, vendor)) - return false; - if (i == -1) i = 0; @@ -357,7 +412,8 @@ gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute, } bool -gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, +gss_eap_radius_attr_provider::getAttribute(uint16_t attribute, + uint16_t vendor, int *authenticated, int *complete, gss_buffer_t value, @@ -365,23 +421,23 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, int *more) const { - return getAttribute(ATTRID(attrid), VENDOR(attrid), + return getAttribute(VENDORATTR(attribute, vendor), authenticated, complete, value, display_value, more); } gss_any_t gss_eap_radius_attr_provider::mapToAny(int authenticated, - gss_buffer_t type_id) const + gss_buffer_t type_id GSSEAP_UNUSED) const { if (authenticated && !m_authenticated) return (gss_any_t)NULL; - return (gss_any_t)paircopy(m_vps); + return (gss_any_t)copyAvps(m_vps); } void -gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, +gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED, gss_any_t input) const { pairfree((VALUE_PAIR **)&input); @@ -390,9 +446,25 @@ gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, bool gss_eap_radius_attr_provider::init(void) { + struct rs_context *radContext; + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, "urn:ietf:params:gss-eap:radius-avp", - gss_eap_radius_attr_provider::createAttrContext); + createAttrContext); + +#if 1 + /* + * This hack is necessary in order to force the loading of the global + * dictionary, otherwise accepting reauthentication tokens fails unless + * the acceptor has already accepted a normal authentication token. + */ + if (rs_context_create(&radContext, RS_DICT_FILE) != 0) { + return false; + } + + rs_context_destroy(radContext); +#endif + return true; } @@ -410,13 +482,12 @@ gss_eap_radius_attr_provider::createAttrContext(void) OM_uint32 gssEapRadiusAddAvp(OM_uint32 *minor, - rs_handle *rh, VALUE_PAIR **vps, - uint16_t vattrid, + uint16_t attribute, uint16_t vendor, - gss_buffer_t buffer) + const gss_buffer_t buffer) { - uint16_t attrid = VENDORATTR(vendor, vattrid); + uint32_t attrid = VENDORATTR(vendor, attribute); unsigned char *p = (unsigned char *)buffer->value; size_t remain = buffer->length; @@ -424,8 +495,12 @@ gssEapRadiusAddAvp(OM_uint32 *minor, VALUE_PAIR *vp; size_t n = remain; - if (n > MAX_STRING_LEN) - n = MAX_STRING_LEN; + /* + * There's an extra byte of padding; RADIUS AVPs can only + * be 253 octets. + */ + if (n >= MAX_STRING_LEN) + n = MAX_STRING_LEN - 1; vp = paircreate(attrid, PW_TYPE_OCTETS); if (vp == NULL) { @@ -434,6 +509,8 @@ gssEapRadiusAddAvp(OM_uint32 *minor, } memcpy(vp->vp_octets, p, n); + vp->length = n; + pairadd(vps, vp); p += n; @@ -446,35 +523,41 @@ gssEapRadiusAddAvp(OM_uint32 *minor, OM_uint32 gssEapRadiusGetRawAvp(OM_uint32 *minor, VALUE_PAIR *vps, - uint16_t type, + uint16_t attribute, uint16_t vendor, VALUE_PAIR **vp) { - uint16_t attr = VENDORATTR(vendor, type); + uint32_t attr = VENDORATTR(vendor, attribute); *vp = pairfind(vps, attr); + if (*vp == NULL) { + *minor = GSSEAP_NO_SUCH_ATTR; + return GSS_S_UNAVAILABLE; + } - return (*vp == NULL) ? GSS_S_UNAVAILABLE : GSS_S_COMPLETE; + return GSS_S_COMPLETE; } OM_uint32 gssEapRadiusGetAvp(OM_uint32 *minor, VALUE_PAIR *vps, - uint16_t type, + uint16_t attribute, uint16_t vendor, gss_buffer_t buffer, int concat) { VALUE_PAIR *vp; unsigned char *p; - uint32_t attr = VENDORATTR(vendor, type); + uint32_t attr = VENDORATTR(vendor, attribute); buffer->length = 0; buffer->value = NULL; vp = pairfind(vps, attr); - if (vp == NULL) + if (vp == NULL) { + *minor = GSSEAP_NO_SUCH_ATTR; return GSS_S_UNAVAILABLE; + } do { buffer->length += vp->length; @@ -500,77 +583,29 @@ gssEapRadiusGetAvp(OM_uint32 *minor, } OM_uint32 -gssEapRadiusAttrProviderInit(OM_uint32 *minor) +gssEapRadiusFreeAvps(OM_uint32 *minor, + VALUE_PAIR **vps) { - return gss_eap_radius_attr_provider::init() - ? GSS_S_COMPLETE : GSS_S_FAILURE; -} - -OM_uint32 -gssEapRadiusAttrProviderFinalize(OM_uint32 *minor) -{ - gss_eap_radius_attr_provider::finalize(); + pairfree(vps); + *minor = 0; return GSS_S_COMPLETE; } -/* partition error namespace so it does not conflict with krb5 */ -#define ERROR_TABLE_BASE_rse (46882560L) - -#define RS_TO_COM_ERR(rse) ((rse) == RSE_OK ? 0 : (rse) + ERROR_TABLE_BASE_rse) -#define COM_TO_RS_ERR(err) ((err) > ERROR_TABLE_BASE_rse && \ - (err) <= (ERROR_TABLE_BASE_rse + RSE_SOME_ERROR) ? \ - (err) - ERROR_TABLE_BASE_rse : RSE_SOME_ERROR) - OM_uint32 -gssEapRadiusMapError(OM_uint32 *minor, - struct rs_error *err) +gssEapRadiusAttrProviderInit(OM_uint32 *minor) { - int code = RSE_OK; - - if (err != NULL) - code = rs_err_code(err, 0); - else - code = RSE_SOME_ERROR; - - *minor = RS_TO_COM_ERR(code); - - gssEapSaveStatusInfo(*minor, "radsec: %s", rs_err_msg(err, 0)); + if (!gss_eap_radius_attr_provider::init()) { + *minor = GSSEAP_RADSEC_INIT_FAILURE; + return GSS_S_FAILURE; + } - rs_err_free(err); - return GSS_S_FAILURE; + return GSS_S_COMPLETE; } OM_uint32 -gssEapRadiusAllocConn(OM_uint32 *minor, - const gss_cred_id_t cred, - gss_ctx_id_t ctx) +gssEapRadiusAttrProviderFinalize(OM_uint32 *minor) { - struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx; - const char *configFile = NULL; - struct rs_error *err; - - assert(actx->radHandle == NULL); - assert(actx->radConn == NULL); - - if (cred != GSS_C_NO_CREDENTIAL && cred->radiusConfigFile != NULL) - configFile = cred->radiusConfigFile; - - err = radiusAllocHandle(configFile, &actx->radHandle); - if (err != NULL || actx->radHandle == NULL) { - return gssEapRadiusMapError(minor, err); - } - - if (rs_conn_create(actx->radHandle, &actx->radConn, "gss-eap") != 0) { - return gssEapRadiusMapError(minor, rs_err_conn_pop(actx->radConn)); - } - - /* XXX TODO rs_conn_select_server does not exist yet */ -#if 0 - if (actx->radServer != NULL) { - if (rs_conn_select_server(actx->radConn, actx->radServer) != 0) - return gssEapRadiusMapError(minor, rs_err_conn_pop(actx->radConn)); - } -#endif + gss_eap_radius_attr_provider::finalize(); *minor = 0; return GSS_S_COMPLETE; @@ -592,8 +627,7 @@ avpSize(const VALUE_PAIR *vp) } static bool -avpExport(rs_handle *rh, - const VALUE_PAIR *vp, +avpExport(const VALUE_PAIR *vp, unsigned char **pBuffer, size_t *pRemain) { @@ -626,8 +660,7 @@ avpExport(rs_handle *rh, } static bool -avpImport(rs_handle *rh, - VALUE_PAIR **pVp, +avpImport(VALUE_PAIR **pVp, unsigned char **pBuffer, size_t *pRemain) { @@ -635,7 +668,7 @@ avpImport(rs_handle *rh, size_t remain = *pRemain; VALUE_PAIR *vp = NULL; DICT_ATTR *da; - OM_uint32 attrid; + uint32_t attrid; if (remain < avpSize(NULL)) goto fail; @@ -645,10 +678,11 @@ avpImport(rs_handle *rh, remain -= 4; da = dict_attrbyvalue(attrid); - if (da == NULL) - goto fail; - - vp = pairalloc(da); + if (da != NULL) { + vp = pairalloc(da); + } else { + vp = paircreate(attrid, PW_TYPE_STRING); + } if (vp == NULL) { throw new std::bad_alloc; goto fail; @@ -670,11 +704,10 @@ avpImport(rs_handle *rh, remain -= 5; break; case PW_TYPE_STRING: - /* check enough room to NUL terminate */ + default: if (p[0] >= MAX_STRING_LEN) goto fail; - /* fallthrough */ - default: + vp->length = (uint32_t)p[0]; memcpy(vp->vp_octets, p + 1, vp->length); @@ -693,7 +726,9 @@ avpImport(rs_handle *rh, return true; fail: - pairbasicfree(vp); + if (vp != NULL) + pairbasicfree(vp); + *pVp = NULL; return false; } @@ -703,65 +738,33 @@ gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, { unsigned char *p = (unsigned char *)buffer->value; size_t remain = buffer->length; - OM_uint32 configFileLen, count; VALUE_PAIR **pNext = &m_vps; if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) return false; - if (remain < 4) - return false; - - configFileLen = load_uint32_be(p); - p += 4; - remain -= 4; - - if (remain < configFileLen) - return false; - - std::string configFile((char *)p, configFileLen); - p += configFileLen; - remain -= configFileLen; - - if (!allocRadHandle(configFile)) - return false; - - if (remain < 4) - return false; - - count = load_uint32_be(p); - p += 4; - remain -= 4; - do { VALUE_PAIR *attr; - if (!avpImport(m_rh, &attr, &p, &remain)) + if (!avpImport(&attr, &p, &remain)) return false; *pNext = attr; pNext = &attr->next; - - count--; } while (remain != 0); - if (count != 0) - return false; - return true; } void gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const { - OM_uint32 count = 0; VALUE_PAIR *vp; unsigned char *p; - size_t remain = 4 + m_configFile.length() + 4; + size_t remain = 0; for (vp = m_vps; vp != NULL; vp = vp->next) { remain += avpSize(vp); - count++; } buffer->value = GSSEAP_MALLOC(remain); @@ -773,20 +776,8 @@ gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const p = (unsigned char *)buffer->value; - store_uint32_be(m_configFile.length(), p); - p += 4; - remain -= 4; - - memcpy(p, m_configFile.c_str(), m_configFile.length()); - p += m_configFile.length(); - remain -= m_configFile.length(); - - store_uint32_be(count, p); - p += 4; - remain -= 4; - for (vp = m_vps; vp != NULL; vp = vp->next) { - avpExport(m_rh, vp, &p, &remain); + avpExport(vp, &p, &remain); } assert(remain == 0); @@ -803,3 +794,26 @@ gss_eap_radius_attr_provider::getExpiryTime(void) const return time(NULL) + vp->lvalue; } + +OM_uint32 +gssEapRadiusMapError(OM_uint32 *minor, + struct rs_error *err) +{ + int code; + + assert(err != NULL); + + code = rs_err_code(err, 0); + + if (code == RSE_OK) { + *minor = 0; + return GSS_S_COMPLETE; + } + + *minor = ERROR_TABLE_BASE_rse + code; + + gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err)); + rs_err_free(err); + + return GSS_S_FAILURE; +}