X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=util_radius.cpp;h=4e2f6e079724311dd6304f9ca620f8050cac262e;hb=e063ba4e45d12dbc1a397653f9e77228835e4a2b;hp=588a5588c0b023b5115551cf26ad19ea5d83db30;hpb=857a83ed4a05dc17e57ba15a5dc4febfc639e4e0;p=mech_eap.orig diff --git a/util_radius.cpp b/util_radius.cpp index 588a558..4e2f6e0 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,6 +30,10 @@ * SUCH DAMAGE. */ +/* + * RADIUS attribute provider implementation. + */ + #include "gssapiP_eap.h" /* stuff that should be provided by libradsec/libfreeradius-radius */ @@ -116,14 +120,14 @@ alreadyAddedAttributeP(std::vector &attrs, VALUE_PAIR *vp) static bool 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; @@ -132,7 +136,7 @@ isSecretAttributeP(uint16_t attrid, uint16_t vendor) break; } - return ret; + return bSecretAttribute; } static bool @@ -142,28 +146,28 @@ isSecretAttributeP(uint32_t attribute) } static bool -isHiddenAttributeP(uint16_t attrid, uint16_t vendor) +isInternalAttributeP(uint16_t attrid, uint16_t vendor) { - bool ret = false; + 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 -isHiddenAttributeP(uint32_t attribute) +isInternalAttributeP(uint32_t attribute) { - return isHiddenAttributeP(ATTRID(attribute), VENDOR(attribute)); + return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute)); } /* @@ -196,7 +200,8 @@ copyAvps(const VALUE_PAIR *src) } 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; @@ -205,7 +210,8 @@ gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addA gss_buffer_desc attribute; char attrid[64]; - if (isHiddenAttributeP(vp->attribute)) + /* Don't advertise attributes that are internal to the GSS-EAP mechanism */ + if (isInternalAttributeP(vp->attribute)) continue; if (alreadyAddedAttributeP(seen, vp)) @@ -217,7 +223,7 @@ gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addA attribute.value = attrid; attribute.length = strlen(attrid); - if (!addAttribute(this, &attribute, data)) + if (!addAttribute(m_manager, this, &attribute, data)) return false; seen.push_back(std::string(vp->name)); @@ -226,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 @@ -246,34 +325,12 @@ 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; uint32_t attrid; - char *s; - - 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); } @@ -291,9 +348,6 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, *more = 0; - if (isHiddenAttributeP(attrid)) - return false; - if (i == -1) i = 0; @@ -374,7 +428,7 @@ gss_eap_radius_attr_provider::getAttribute(uint16_t attribute, 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; @@ -383,18 +437,42 @@ gss_eap_radius_attr_provider::mapToAny(int authenticated, } 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); + VALUE_PAIR *vp = (VALUE_PAIR *)input; + pairfree(&vp); } bool gss_eap_radius_attr_provider::init(void) { - gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, - "urn:ietf:params:gss-eap:radius-avp", - gss_eap_radius_attr_provider::createAttrContext); + struct rs_context *radContext; + + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, 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) != 0) + return false; + + if (rs_context_read_config(radContext, RS_CONFIG_FILE) != 0) { + rs_context_destroy(radContext); + return false; + } + + if (rs_context_init_freeradius_dict(radContext, NULL)) { + rs_context_destroy(radContext); + return false; + } + + rs_context_destroy(radContext); +#endif + return true; } @@ -415,7 +493,7 @@ gssEapRadiusAddAvp(OM_uint32 *minor, VALUE_PAIR **vps, uint16_t attribute, uint16_t vendor, - gss_buffer_t buffer) + const gss_buffer_t buffer) { uint32_t attrid = VENDORATTR(vendor, attribute); unsigned char *p = (unsigned char *)buffer->value; @@ -425,8 +503,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) { @@ -456,8 +538,12 @@ gssEapRadiusGetRawAvp(OM_uint32 *minor, 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 @@ -476,8 +562,10 @@ gssEapRadiusGetAvp(OM_uint32 *minor, 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; @@ -514,208 +602,200 @@ gssEapRadiusFreeAvps(OM_uint32 *minor, OM_uint32 gssEapRadiusAttrProviderInit(OM_uint32 *minor) { - return gss_eap_radius_attr_provider::init() - ? GSS_S_COMPLETE : GSS_S_FAILURE; + if (!gss_eap_radius_attr_provider::init()) { + *minor = GSSEAP_RADSEC_INIT_FAILURE; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; } OM_uint32 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor) { gss_eap_radius_attr_provider::finalize(); + + *minor = 0; return GSS_S_COMPLETE; } -/* - * Encoding is: - * 4 octet NBO attribute ID | 4 octet attribute length | attribute data - */ -static size_t -avpSize(const VALUE_PAIR *vp) +static json_t * +avpToJson(const VALUE_PAIR *vp) { - size_t size = 4 + 1; - - if (vp != NULL) - size += vp->length; - - return size; -} + json_t *obj = json_object(); -static bool -avpExport(const VALUE_PAIR *vp, - unsigned char **pBuffer, - size_t *pRemain) -{ - unsigned char *p = *pBuffer; - size_t remain = *pRemain; + if (obj == NULL) { + throw new std::bad_alloc; + return NULL; + } - assert(remain >= avpSize(vp)); + /* FIXME check json_object_set_new return value */ + json_object_set_new(obj, "type", json_integer(vp->attribute)); - store_uint32_be(vp->attribute, p); + assert(vp->length <= MAX_STRING_LEN); switch (vp->type) { case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: - p[4] = 4; - store_uint32_be(vp->lvalue, p + 5); + json_object_set_new(obj, "value", json_integer(vp->lvalue)); break; - default: - assert(vp->length <= MAX_STRING_LEN); - p[4] = (uint8_t)vp->length; - memcpy(p + 5, vp->vp_octets, vp->length); + case PW_TYPE_STRING: + json_object_set_new(obj, "value", json_string(vp->vp_strvalue)); break; - } + default: { + char *b64; - *pBuffer += 5 + p[4]; - *pRemain -= 5 + p[4]; + if (base64Encode(vp->vp_octets, vp->length, &b64) < 0) { + json_decref(obj); + throw new std::bad_alloc; + } - return true; + json_object_set_new(obj, "value", json_string(b64)); + GSSEAP_FREE(b64); + break; + } + } + return obj; } static bool -avpImport(VALUE_PAIR **pVp, - unsigned char **pBuffer, - size_t *pRemain) +jsonToAvp(VALUE_PAIR **pVp, json_t *obj) { - unsigned char *p = *pBuffer; - size_t remain = *pRemain; VALUE_PAIR *vp = NULL; DICT_ATTR *da; uint32_t attrid; + json_t *type, *value; - if (remain < avpSize(NULL)) + type = json_object_get(obj, "type"); + value = json_object_get(obj, "value"); + if (type == NULL || value == NULL) goto fail; - attrid = load_uint32_be(p); - p += 4; - remain -= 4; - + attrid = json_integer_value(type); 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; } - if (remain < p[0]) - goto fail; - switch (vp->type) { case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: - if (p[0] != 4) - goto fail; - vp->length = 4; - vp->lvalue = load_uint32_be(p + 1); - p += 5; - remain -= 5; + vp->lvalue = json_integer_value(value); break; - case PW_TYPE_STRING: - /* check enough room to NUL terminate */ - if (p[0] == MAX_STRING_LEN) - goto fail; - else - /* fallthrough */ - default: - if (p[0] > MAX_STRING_LEN) + case PW_TYPE_STRING: { + const char *str = json_string_value(value); + size_t len; + + if (str == NULL || (len = strlen(str)) >= MAX_STRING_LEN) goto fail; - vp->length = (uint32_t)p[0]; - memcpy(vp->vp_octets, p + 1, vp->length); + vp->length = len; + memcpy(vp->vp_strvalue, str, len + 1); + break; + } + case PW_TYPE_OCTETS: + default: { + const char *str = json_string_value(value); + int len; + + /* this optimization requires base64Decode only understand packed encoding */ + if (str == NULL || + strlen(str) >= BASE64_EXPAND(MAX_STRING_LEN)) + goto fail; - if (vp->type == PW_TYPE_STRING) - vp->vp_strvalue[vp->length] = '\0'; + len = base64Decode(str, vp->vp_octets); + if (len < 0) + goto fail; - p += 1 + vp->length; - remain -= 1 + vp->length; + vp->length = len; + vp->vp_octets[len] = '\0'; break; } + } *pVp = vp; - *pBuffer = p; - *pRemain = remain; return true; fail: - pairbasicfree(vp); + if (vp != NULL) + pairbasicfree(vp); + *pVp = NULL; return false; } +const char * +gss_eap_radius_attr_provider::name(void) const +{ + return "radius"; +} + bool -gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, - const gss_buffer_t buffer) +gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx, + json_t *obj) { - unsigned char *p = (unsigned char *)buffer->value; - size_t remain = buffer->length; - uint32_t count; VALUE_PAIR **pNext = &m_vps; + json_t *attrs; + size_t i; - if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) - return false; - - if (remain < 4) + if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj)) return false; - count = load_uint32_be(p); - p += 4; - remain -= 4; + attrs = json_object_get(obj, "attributes"); - do { - VALUE_PAIR *attr; + for (i = 0; i < json_array_size(attrs); i++) { + json_t *attr = json_array_get(attrs, i); + VALUE_PAIR *vp; - if (!avpImport(&attr, &p, &remain)) + if (!jsonToAvp(&vp, attr)) return false; - *pNext = attr; - pNext = &attr->next; - - count--; - } while (remain != 0); - - if (count != 0) - return false; + *pNext = vp; + pNext = &vp->next; + } return true; } -void -gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const +const char * +gss_eap_radius_attr_provider::prefix(void) const { - uint32_t count = 0; - VALUE_PAIR *vp; - unsigned char *p; - size_t remain = 4; + return "urn:ietf:params:gss-eap:radius-avp"; +} - for (vp = m_vps; vp != NULL; vp = vp->next) { - remain += avpSize(vp); - count++; - } +json_t * +gss_eap_radius_attr_provider::jsonRepresentation(void) const +{ + json_t *obj, *attrs; - buffer->value = GSSEAP_MALLOC(remain); - if (buffer->value == NULL) { + attrs = json_array(); + if (attrs == NULL) throw new std::bad_alloc; - return; - } - buffer->length = remain; - - p = (unsigned char *)buffer->value; - store_uint32_be(count, p); - p += 4; - remain -= 4; + for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) { + json_t *attr = avpToJson(vp); + json_array_append_new(attrs, attr); + } - for (vp = m_vps; vp != NULL; vp = vp->next) { - avpExport(vp, &p, &remain); + obj = json_object(); + if (obj == NULL) { + json_decref(attrs); + throw new std::bad_alloc; } - assert(remain == 0); + json_object_set_new(obj, "attributes", attrs); + + return obj; } time_t @@ -730,29 +810,25 @@ gss_eap_radius_attr_provider::getExpiryTime(void) const return time(NULL) + vp->lvalue; } -/* 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) { - int code = RSE_OK; + int code; - if (err != NULL) - code = rs_err_code(err, 0); - else - code = RSE_SOME_ERROR; + assert(err != NULL); - *minor = RS_TO_COM_ERR(code); + code = rs_err_code(err, 0); - gssEapSaveStatusInfo(*minor, "radsec: %s", rs_err_msg(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; }