X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=util_radius.cpp;h=d1ff877bb2c683ae31a0e25cd55f2c194f1758f6;hb=530b6b8e5bf392e22962e1e406ee85d7f9561b52;hp=2c55bffbabf5b55c692f9a6399d90e5b190a8cd0;hpb=b47c10882ea3409098780eb9608d3008c91f076d;p=mech_eap.git diff --git a/util_radius.cpp b/util_radius.cpp index 2c55bff..d1ff877 100644 --- a/util_radius.cpp +++ b/util_radius.cpp @@ -32,27 +32,46 @@ #include "gssapiP_eap.h" -VALUE_PAIR * -gss_eap_radius_attr_provider::copyAvps(const VALUE_PAIR *src) +#define VENDORATTR(vendor, attr) ((vendor) << 16 | (attr)) + +#ifndef ATTRID +#define ATTRID(attr) ((attr) & 0xFFFF) +#endif + +static gss_buffer_desc radiusUrnPrefix = { + sizeof("urn:x-radius:") - 1, + (void *)"urn:x-radius:" +}; + +static struct rs_error * +radiusAllocHandle(const char *configFile, + rs_handle **pHandle) { - const VALUE_PAIR *vp; - VALUE_PAIR *dst = NULL, **pDst = &dst; + rs_handle *rh; + struct rs_alloc_scheme ralloc; - for (vp = src; vp != NULL; vp = vp->next) { - VALUE_PAIR *vp2; + *pHandle = NULL; - vp2 = (VALUE_PAIR *)GSSEAP_CALLOC(1, sizeof(*vp2)); - if (vp2 == NULL) { - rc_avpair_free(dst); - return NULL; - } - memcpy(vp2, vp, sizeof(*vp)); - vp2->next = NULL; - *pDst = vp2; - pDst = &vp2->next; + 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); } - return dst; + *pHandle = rh; + return NULL; } gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void) @@ -65,17 +84,27 @@ gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void) gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void) { if (m_rh != NULL) - rc_config_free(m_rh); + rs_context_destroy(m_rh); if (m_avps != NULL) - rc_avpair_free(m_avps); + pairfree(&m_avps); } bool -gss_eap_radius_attr_provider::initFromGssCred(const gss_cred_id_t cred) +gss_eap_radius_attr_provider::allocRadHandle(const std::string &configFile) { - OM_uint32 minor; + m_configFile.assign(configFile); - return !GSS_ERROR(gssEapRadiusAllocHandle(&minor, cred, &m_rh)); + /* + * 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 @@ -87,13 +116,13 @@ gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *ma if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx)) return false; - if (!initFromGssCred(GSS_C_NO_CREDENTIAL)) + radius = static_cast(ctx); + + if (!allocRadHandle(radius->m_configFile)) return false; - radius = static_cast(ctx); - if (radius->m_avps != NULL) { - m_avps = copyAvps(radius->getAvps()); - } + if (radius->m_avps != NULL) + m_avps = paircopy(const_cast(radius->getAvps())); return true; } @@ -103,15 +132,20 @@ 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 (!initFromGssCred(gssCred)) + 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.avps != NULL) { - m_avps = copyAvps(gssCtx->acceptorCtx.avps); + m_avps = paircopy(gssCtx->acceptorCtx.avps); if (m_avps == NULL) return false; } @@ -134,20 +168,23 @@ alreadyAddedAttributeP(std::vector &attrs, VALUE_PAIR *vp) } static bool -isSecretAttributeP(int attrid, int vendor) +isHiddenAttributeP(int attrid, uint16_t vendor) { bool ret = false; switch (vendor) { - case RADIUS_VENDOR_ID_MICROSOFT: + case VENDORPEC_MS: switch (attrid) { - case RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY: - case RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY: + case PW_MS_MPPE_SEND_KEY: + case PW_MS_MPPE_RECV_KEY: ret = true; break; default: break; } + case VENDORPEC_UKERNA: + ret = true; + break; default: break; } @@ -163,15 +200,18 @@ gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addA for (vp = m_avps; vp != NULL; vp = vp->next) { gss_buffer_desc attribute; - - if (isSecretAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute))) + char attrid[64]; + if (isHiddenAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute))) continue; if (alreadyAddedAttributeP(seen, vp)) continue; - attribute.value = (void *)vp->name; - attribute.length = strlen(vp->name); + snprintf(attrid, sizeof(attrid), "%s%d", + (char *)radiusUrnPrefix.value, vp->attribute); + + attribute.value = attrid; + attribute.length = strlen(attrid); if (!addAttribute(this, &attribute, data)) return false; @@ -204,24 +244,28 @@ gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr, { OM_uint32 tmpMinor; gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER; - DICT_ATTR *d; + DICT_ATTR *da; int attrid; char *s; - /* XXX vendor */ - duplicateBuffer(*attr, &strAttr); s = (char *)strAttr.value; - if (isdigit(((char *)strAttr.value)[0])) { + if (attr->length < radiusUrnPrefix.length || + memcmp(s, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0) + return false; + + s += radiusUrnPrefix.length; + + if (isdigit(*s)) { attrid = strtoul(s, NULL, 10); } else { - d = rc_dict_findattr(m_rh, (char *)s); - if (d == NULL) { + da = dict_attrbyname(s); + if (da == NULL) { gss_release_buffer(&tmpMinor, &strAttr); return false; } - attrid = d->value; + attrid = da->attr; } gss_release_buffer(&tmpMinor, &strAttr); @@ -230,80 +274,56 @@ gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr, value, display_value, more); } -static bool -isPrintableAttributeP(VALUE_PAIR *vp) -{ - size_t i; - - for (i = 0; i < sizeof(vp->strvalue); i++) { - if (!isprint(vp->strvalue[i])) - return false; - } - - return true; -} - bool -gss_eap_radius_attr_provider::getAttribute(int attrid, - int vendor, +gss_eap_radius_attr_provider::getAttribute(uint16_t vattrid, + uint16_t vendor, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) const { - OM_uint32 tmpMinor; + uint32_t attrid = VENDORATTR(vendor, vattrid); VALUE_PAIR *vp; - int i = *more; - int max = 0; - char name[NAME_LENGTH + 1]; - char displayString[AUTH_STRING_LEN + 1]; - gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER; - gss_buffer_desc displayBuf = GSS_C_EMPTY_BUFFER; + int i = *more, count = 0; *more = 0; - if (isSecretAttributeP(attrid, vendor)) - return false; - - vp = rc_avpair_get(m_avps, attrid, vendor); - if (vp == NULL) + if (isHiddenAttributeP(attrid, vendor)) return false; if (i == -1) i = 0; - do { - if (i == max) + for (vp = pairfind(m_avps, attrid); + vp != NULL; + vp = pairfind(vp->next, attrid)) { + if (count++ == i) { + if (pairfind(vp->next, attrid) != NULL) + *more = count; break; + } + } - max++; - } while ((vp = rc_avpair_get(vp->next, attrid, vendor)) != NULL); - - if (i > max) + if (vp == NULL && *more == 0) return false; - if (vp->type == PW_TYPE_STRING) { - valueBuf.value = (void *)vp->strvalue; - valueBuf.length = vp->lvalue; - } else { - valueBuf.value = (void *)&vp->lvalue; - valueBuf.length = 4; - } + if (value != GSS_C_NO_BUFFER) { + gss_buffer_desc valueBuf; + + valueBuf.value = (void *)vp->vp_octets; + valueBuf.length = vp->length; - if (value != GSS_C_NO_BUFFER) duplicateBuffer(valueBuf, value); + } - if (display_value != GSS_C_NO_BUFFER && - isPrintableAttributeP(vp)) { - if (rc_avpair_tostr(m_rh, vp, name, NAME_LENGTH, - displayString, AUTH_STRING_LEN) != 0) { - gss_release_buffer(&tmpMinor, value); - return false; - } + if (display_value != GSS_C_NO_BUFFER) { + char displayString[MAX_STRING_LEN]; + gss_buffer_desc displayBuf; + displayBuf.length = vp_prints_value(displayString, + sizeof(displayString), vp, 0); displayBuf.value = (void *)displayString; - displayBuf.length = strlen(displayString); duplicateBuffer(displayBuf, display_value); } @@ -313,14 +333,30 @@ gss_eap_radius_attr_provider::getAttribute(int attrid, if (complete != NULL) *complete = true; - if (max > i) - *more = i; - return true; } bool -gss_eap_radius_attr_provider::getAttribute(int attrid, +gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute, + uint16_t vendor, + int *authenticated, + int *complete, + gss_buffer_t value) const +{ + OM_uint32 major, minor; + + major = gssEapRadiusGetAvp(&minor, m_avps, attribute, vendor, value, TRUE); + + if (authenticated != NULL) + *authenticated = m_authenticated; + if (complete != NULL) + *complete = true; + + return !GSS_ERROR(major); +} + +bool +gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, int *authenticated, int *complete, gss_buffer_t value, @@ -340,34 +376,14 @@ gss_eap_radius_attr_provider::mapToAny(int authenticated, if (authenticated && !m_authenticated) return (gss_any_t)NULL; - return (gss_any_t)copyAvps(m_avps); + return (gss_any_t)paircopy(m_avps); } void gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, gss_any_t input) const { - rc_avpair_free((VALUE_PAIR *)input); -} - -void -gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const -{ - buffer->length = 0; - buffer->value = NULL; -} - -bool -gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, - const gss_buffer_t buffer) -{ - if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) - return false; - - if (!initFromGssCred(GSS_C_NO_CREDENTIAL)) - return false; - - return true; + pairfree((VALUE_PAIR **)&input); } bool @@ -392,39 +408,76 @@ gss_eap_radius_attr_provider::createAttrContext(void) } OM_uint32 -addAvpFromBuffer(OM_uint32 *minor, - rc_handle *rh, - VALUE_PAIR **vp, - int type, - gss_buffer_t buffer) +gssEapRadiusAddAvp(OM_uint32 *minor, + rs_handle *rh, + VALUE_PAIR **vps, + uint16_t vattrid, + uint16_t vendor, + gss_buffer_t buffer) { - if (rc_avpair_add(rh, vp, type, buffer->value, buffer->length, 0) == NULL) { - return GSS_S_FAILURE; - } + uint16_t attrid = VENDORATTR(vendor, vattrid); + unsigned char *p = (unsigned char *)buffer->value; + size_t remain = buffer->length; + + do { + VALUE_PAIR *vp; + size_t n = remain; + + if (n > MAX_STRING_LEN) + n = MAX_STRING_LEN; + + vp = paircreate(attrid, PW_TYPE_OCTETS); + if (vp == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy(vp->vp_octets, p, n); + pairadd(vps, vp); + + p += n; + remain -= n; + } while (remain != 0); return GSS_S_COMPLETE; } OM_uint32 -getBufferFromAvps(OM_uint32 *minor, - VALUE_PAIR *vps, - int type, - gss_buffer_t buffer, - int concat) +gssEapRadiusGetRawAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t type, + uint16_t vendor, + VALUE_PAIR **vp) +{ + uint16_t attr = VENDORATTR(vendor, type); + + *vp = pairfind(vps, attr); + + return (*vp == NULL) ? GSS_S_UNAVAILABLE : GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusGetAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t type, + uint16_t vendor, + gss_buffer_t buffer, + int concat) { VALUE_PAIR *vp; unsigned char *p; + uint32_t attr = VENDORATTR(vendor, type); buffer->length = 0; buffer->value = NULL; - vp = rc_avpair_get(vps, type, 0); + vp = pairfind(vps, attr); if (vp == NULL) return GSS_S_UNAVAILABLE; do { - buffer->length += vp->lvalue; - } while (concat && (vp = rc_avpair_get(vp->next, type, 0)) != NULL); + buffer->length += vp->length; + } while (concat && (vp = pairfind(vp->next, attr)) != NULL); buffer->value = GSSEAP_MALLOC(buffer->length); if (buffer->value == NULL) { @@ -434,11 +487,11 @@ getBufferFromAvps(OM_uint32 *minor, p = (unsigned char *)buffer->value; - for (vp = rc_avpair_get(vps, type, 0); + for (vp = pairfind(vps, attr); concat && vp != NULL; - vp = rc_avpair_get(vp->next, type, 0)) { - memcpy(p, vp->strvalue, vp->lvalue); - p += vp->lvalue; + vp = pairfind(vp->next, attr)) { + memcpy(p, vp->vp_octets, vp->length); + p += vp->length; } *minor = 0; @@ -460,30 +513,275 @@ gssEapRadiusAttrProviderFinalize(OM_uint32 *minor) } OM_uint32 -gssEapRadiusAllocHandle(OM_uint32 *minor, - const gss_cred_id_t cred, - rc_handle **pHandle) +gssEapRadiusMapError(OM_uint32 *minor, + struct rs_error *err) { - rc_handle *rh; - const char *config = RC_CONFIG_FILE; + if (err != NULL) + rs_err_code(err, 1); - *pHandle = NULL; + return GSS_S_FAILURE; +} + +OM_uint32 +gssEapRadiusAllocConn(OM_uint32 *minor, + const gss_cred_id_t cred, + gss_ctx_id_t ctx) +{ + 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) - config = cred->radiusConfigFile; + configFile = cred->radiusConfigFile; - rh = rc_read_config((char *)config); - if (rh == NULL) { - *minor = errno; - rc_config_free(rh); - return GSS_S_FAILURE; + err = radiusAllocHandle(configFile, &actx->radHandle); + if (err != NULL || actx->radHandle == NULL) { + return gssEapRadiusMapError(minor, err); } - if (rc_read_dictionary(rh, rc_conf_str(rh, (char *)"dictionary")) != 0) { - *minor = errno; - return GSS_S_FAILURE; + if (rs_conn_create(actx->radHandle, &actx->radConn, "gss-eap") != 0) { + return gssEapRadiusMapError(minor, rs_err_conn_pop(actx->radConn)); } - *pHandle = rh; + /* 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 + + *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) +{ + size_t size = 4 + 1; + + if (vp != NULL) + size += vp->length; + + return size; +} + +static bool +avpExport(rs_handle *rh, + const VALUE_PAIR *vp, + unsigned char **pBuffer, + size_t *pRemain) +{ + unsigned char *p = *pBuffer; + size_t remain = *pRemain; + + assert(remain >= avpSize(vp)); + + store_uint32_be(vp->attribute, p); + + 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); + break; + default: + assert(vp->length <= MAX_STRING_LEN); + p[4] = (uint8_t)vp->length; + memcpy(p + 5, vp->vp_octets, vp->length); + break; + } + + *pBuffer += 5 + p[4]; + *pRemain -= 5 + p[4]; + + return true; + +} + +static bool +avpImport(rs_handle *rh, + VALUE_PAIR **pVp, + unsigned char **pBuffer, + size_t *pRemain) +{ + unsigned char *p = *pBuffer; + size_t remain = *pRemain; + VALUE_PAIR *vp = NULL; + DICT_ATTR *da; + OM_uint32 attrid; + + if (remain < avpSize(NULL)) + goto fail; + + attrid = load_uint32_be(p); + p += 4; + remain -= 4; + + da = dict_attrbyvalue(attrid); + if (da == NULL) + goto fail; + + vp = pairalloc(da); + 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; + break; + case PW_TYPE_STRING: + /* check enough room to NUL terminate */ + if (p[0] >= MAX_STRING_LEN) + goto fail; + /* fallthrough */ + default: + vp->length = (uint32_t)p[0]; + memcpy(vp->vp_octets, p + 1, vp->length); + + if (vp->type == PW_TYPE_STRING) + vp->vp_strvalue[vp->length] = '\0'; + + p += 1 + vp->length; + remain -= 1 + vp->length; + break; + } + + *pVp = vp; + *pBuffer = p; + *pRemain = remain; + + return true; + +fail: + pairbasicfree(vp); + return false; +} + +bool +gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer) +{ + unsigned char *p = (unsigned char *)buffer->value; + size_t remain = buffer->length; + OM_uint32 configFileLen, count; + VALUE_PAIR **pNext = &m_avps; + + 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)) + 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; + + for (vp = m_avps; vp != NULL; vp = vp->next) { + remain += avpSize(vp); + count++; + } + + buffer->value = GSSEAP_MALLOC(remain); + if (buffer->value == NULL) { + throw new std::bad_alloc; + return; + } + buffer->length = remain; + + 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_avps; vp != NULL; vp = vp->next) { + avpExport(m_rh, vp, &p, &remain); + } + + assert(remain == 0); +} + +time_t +gss_eap_radius_attr_provider::getExpiryTime(void) const +{ + VALUE_PAIR *vp; + + vp = pairfind(m_avps, PW_SESSION_TIMEOUT); + if (vp == NULL || vp->lvalue == 0) + return 0; + + return time(NULL) + vp->lvalue; +}