2 * Copyright (c) 2011, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * RADIUS attribute provider implementation.
37 #include "gssapiP_eap.h"
38 #include "util_radius.h"
39 #include "utils/radius_utils.h"
41 #ifdef GSSEAP_ENABLE_ACCEPTOR
43 /* stuff that should be provided by libradsec/libfreeradius-radius */
44 #define VENDORATTR(vendor, attr) (((vendor) << 16) | (attr))
47 #define ATTRID(attr) ((attr) & 0xFFFF)
50 static gss_buffer_desc radiusUrnPrefix = {
51 sizeof("urn:x-radius:") - 1,
52 (void *)"urn:x-radius:"
55 static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
57 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
60 m_authenticated = false;
63 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
70 gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
71 const gss_eap_attr_provider *ctx)
73 const gss_eap_radius_attr_provider *radius;
75 if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
78 radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
80 if (radius->m_vps != NULL)
81 m_vps = copyAvps(const_cast<VALUE_PAIR *>(radius->getAvps()));
83 m_authenticated = radius->m_authenticated;
89 gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
90 const gss_cred_id_t gssCred,
91 const gss_ctx_id_t gssCtx)
93 if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
96 if (gssCtx != GSS_C_NO_CONTEXT) {
97 if (gssCtx->acceptorCtx.vps != NULL) {
98 m_vps = copyAvps(gssCtx->acceptorCtx.vps);
102 /* We assume libradsec validated this for us */
103 GSSEAP_ASSERT(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL);
104 m_authenticated = true;
112 alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
114 for (std::vector<std::string>::const_iterator a = attrs.begin();
117 if (strcmp(vp->name, (*a).c_str()) == 0)
125 isSecretAttributeP(uint16_t attrid, uint16_t vendor)
127 bool bSecretAttribute = false;
132 case PW_MS_MPPE_SEND_KEY:
133 case PW_MS_MPPE_RECV_KEY:
134 bSecretAttribute = true;
143 return bSecretAttribute;
147 isSecretAttributeP(uint32_t attribute)
149 return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute));
153 isInternalAttributeP(uint16_t attrid, uint16_t vendor)
155 bool bInternalAttribute = false;
157 /* should have been filtered */
158 GSSEAP_ASSERT(!isSecretAttributeP(attrid, vendor));
161 case VENDORPEC_UKERNA:
163 case PW_GSS_ACCEPTOR_SERVICE_NAME:
164 case PW_GSS_ACCEPTOR_HOST_NAME:
165 case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
166 case PW_GSS_ACCEPTOR_REALM_NAME:
167 case PW_SAML_AAA_ASSERTION:
168 bInternalAttribute = true;
178 return bInternalAttribute;
182 isInternalAttributeP(uint32_t attribute)
184 return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute));
188 isFragmentedAttributeP(uint16_t attrid, uint16_t vendor)
190 /* A bit of a hack for the PAC for now. Should be configurable. */
191 return (vendor == VENDORPEC_UKERNA) &&
192 !isInternalAttributeP(attrid, vendor);
196 isFragmentedAttributeP(uint32_t attribute)
198 return isFragmentedAttributeP(ATTRID(attribute), VENDOR(attribute));
202 * Copy AVP list, same as paircopy except it filters out attributes
206 copyAvps(const VALUE_PAIR *src)
208 const VALUE_PAIR *vp;
209 VALUE_PAIR *dst = NULL, **pDst = &dst;
211 for (vp = src; vp != NULL; vp = vp->next) {
214 if (isSecretAttributeP(vp->attribute))
217 vpcopy = paircopyvp(vp);
218 if (vpcopy == NULL) {
220 throw std::bad_alloc();
223 pDst = &vpcopy->next;
230 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
234 std::vector <std::string> seen;
236 for (vp = m_vps; vp != NULL; vp = vp->next) {
237 gss_buffer_desc attribute;
240 /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
241 if (isInternalAttributeP(vp->attribute))
244 if (alreadyAddedAttributeP(seen, vp))
247 snprintf(attrid, sizeof(attrid), "%s%d",
248 (char *)radiusUrnPrefix.value, vp->attribute);
250 attribute.value = attrid;
251 attribute.length = strlen(attrid);
253 if (!addAttribute(m_manager, this, &attribute, data))
256 seen.push_back(std::string(vp->name));
263 getAttributeId(const gss_buffer_t attr)
266 gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
271 if (attr->length < radiusUrnPrefix.length ||
272 memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
275 /* need to duplicate because attr may not be NUL terminated */
276 duplicateBuffer(*attr, &strAttr);
277 s = (char *)strAttr.value + radiusUrnPrefix.length;
280 attrid = strtoul(s, NULL, 10);
282 da = dict_attrbyname(s);
287 gss_release_buffer(&tmpMinor, &strAttr);
293 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
295 const gss_buffer_t value)
297 OM_uint32 major = GSS_S_UNAVAILABLE, minor;
299 if (!isSecretAttributeP(attrid) &&
300 !isInternalAttributeP(attrid)) {
301 deleteAttribute(attrid);
303 major = gssEapRadiusAddAvp(&minor, &m_vps,
304 ATTRID(attrid), VENDOR(attrid),
308 return !GSS_ERROR(major);
312 gss_eap_radius_attr_provider::setAttribute(int complete,
313 const gss_buffer_t attr,
314 const gss_buffer_t value)
316 uint32_t attrid = getAttributeId(attr);
321 return setAttribute(complete, attrid, value);
325 gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid)
327 if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) ||
328 pairfind(m_vps, attrid) == NULL)
331 pairdelete(&m_vps, attrid);
337 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
339 uint32_t attrid = getAttributeId(attr);
344 return deleteAttribute(attrid);
348 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
352 gss_buffer_t display_value,
357 attrid = getAttributeId(attr);
361 return getAttribute(attrid, authenticated, complete,
362 value, display_value, more);
366 gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
370 gss_buffer_t display_value,
374 int i = *more, count = 0;
381 if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid)) {
383 } else if (isFragmentedAttributeP(attrid)) {
384 return getFragmentedAttribute(attrid,
390 for (vp = pairfind(m_vps, attrid);
392 vp = pairfind(vp->next, attrid)) {
394 if (pairfind(vp->next, attrid) != NULL)
400 if (vp == NULL && *more == 0)
403 if (value != GSS_C_NO_BUFFER) {
404 gss_buffer_desc valueBuf;
406 valueBuf.value = (void *)vp->vp_octets;
407 valueBuf.length = vp->length;
409 duplicateBuffer(valueBuf, value);
412 if (display_value != GSS_C_NO_BUFFER &&
413 vp->type != PW_TYPE_OCTETS) {
414 char displayString[MAX_STRING_LEN];
415 gss_buffer_desc displayBuf;
417 displayBuf.length = vp_prints_value(displayString,
418 sizeof(displayString), vp, 0);
419 displayBuf.value = (void *)displayString;
421 duplicateBuffer(displayBuf, display_value);
424 if (authenticated != NULL)
425 *authenticated = m_authenticated;
426 if (complete != NULL)
433 gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
437 gss_buffer_t value) const
439 OM_uint32 major, minor;
441 major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE);
443 if (authenticated != NULL)
444 *authenticated = m_authenticated;
445 if (complete != NULL)
448 return !GSS_ERROR(major);
452 gss_eap_radius_attr_provider::getFragmentedAttribute(uint32_t attrid,
455 gss_buffer_t value) const
457 return getFragmentedAttribute(ATTRID(attrid), VENDOR(attrid),
458 authenticated, complete, value);
462 gss_eap_radius_attr_provider::getAttribute(uint16_t attribute,
467 gss_buffer_t display_value,
471 return getAttribute(VENDORATTR(attribute, vendor),
472 authenticated, complete,
473 value, display_value, more);
477 gss_eap_radius_attr_provider::mapToAny(int authenticated,
478 gss_buffer_t type_id GSSEAP_UNUSED) const
480 if (authenticated && !m_authenticated)
481 return (gss_any_t)NULL;
483 return (gss_any_t)copyAvps(m_vps);
487 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
488 gss_any_t input) const
490 VALUE_PAIR *vp = (VALUE_PAIR *)input;
495 gss_eap_radius_attr_provider::init(void)
497 gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, createAttrContext);
503 gss_eap_radius_attr_provider::finalize(void)
505 gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
508 gss_eap_attr_provider *
509 gss_eap_radius_attr_provider::createAttrContext(void)
511 return new gss_eap_radius_attr_provider;
515 gssEapRadiusAddAvp(OM_uint32 *minor,
519 const gss_buffer_t buffer)
521 uint32_t attrid = VENDORATTR(vendor, attribute);
522 unsigned char *p = (unsigned char *)buffer->value;
523 size_t remain = buffer->length;
530 * There's an extra byte of padding; RADIUS AVPs can only
533 if (n >= MAX_STRING_LEN)
534 n = MAX_STRING_LEN - 1;
536 vp = paircreate(attrid, PW_TYPE_OCTETS);
539 return GSS_S_FAILURE;
542 memcpy(vp->vp_octets, p, n);
549 } while (remain != 0);
551 return GSS_S_COMPLETE;
555 gssEapRadiusGetRawAvp(OM_uint32 *minor,
561 uint32_t attr = VENDORATTR(vendor, attribute);
563 *vp = pairfind(vps, attr);
565 *minor = GSSEAP_NO_SUCH_ATTR;
566 return GSS_S_UNAVAILABLE;
569 return GSS_S_COMPLETE;
573 gssEapRadiusGetAvp(OM_uint32 *minor,
582 uint32_t attr = VENDORATTR(vendor, attribute);
584 if (buffer != GSS_C_NO_BUFFER) {
586 buffer->value = NULL;
589 vp = pairfind(vps, attr);
591 *minor = GSSEAP_NO_SUCH_ATTR;
592 return GSS_S_UNAVAILABLE;
595 if (buffer != GSS_C_NO_BUFFER) {
597 buffer->length += vp->length;
598 } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
600 buffer->value = GSSEAP_MALLOC(buffer->length);
601 if (buffer->value == NULL) {
603 return GSS_S_FAILURE;
606 p = (unsigned char *)buffer->value;
608 for (vp = pairfind(vps, attr);
609 concat && vp != NULL;
610 vp = pairfind(vp->next, attr)) {
611 memcpy(p, vp->vp_octets, vp->length);
617 return GSS_S_COMPLETE;
621 gssEapRadiusFreeAvps(OM_uint32 *minor,
626 return GSS_S_COMPLETE;
630 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
632 if (!gss_eap_radius_attr_provider::init()) {
633 *minor = GSSEAP_RADSEC_INIT_FAILURE;
634 return GSS_S_FAILURE;
637 return GSS_S_COMPLETE;
641 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
643 gss_eap_radius_attr_provider::finalize();
646 return GSS_S_COMPLETE;
650 avpToJson(const VALUE_PAIR *vp)
654 GSSEAP_ASSERT(vp->length <= MAX_STRING_LEN);
657 case PW_TYPE_INTEGER:
660 obj.set("value", vp->lvalue);
663 obj.set("value", vp->vp_strvalue);
668 if (base64Encode(vp->vp_octets, vp->length, &b64) < 0)
669 throw std::bad_alloc();
671 obj.set("value", b64);
677 obj.set("type", vp->attribute);
683 jsonToAvp(VALUE_PAIR **pVp, JSONObject &obj)
685 VALUE_PAIR *vp = NULL;
689 JSONObject type = obj["type"];
690 JSONObject value = obj["value"];
692 if (!type.isInteger())
695 attrid = type.integer();
696 da = dict_attrbyvalue(attrid);
700 int type = base64Valid(value.string()) ?
701 PW_TYPE_OCTETS : PW_TYPE_STRING;
702 vp = paircreate(attrid, type);
705 throw std::bad_alloc();
708 case PW_TYPE_INTEGER:
711 if (!value.isInteger())
715 vp->lvalue = value.integer();
717 case PW_TYPE_STRING: {
718 if (!value.isString())
721 const char *str = value.string();
722 size_t len = strlen(str);
724 if (len >= MAX_STRING_LEN)
728 memcpy(vp->vp_strvalue, str, len + 1);
733 if (!value.isString())
736 const char *str = value.string();
737 ssize_t len = strlen(str);
739 /* this optimization requires base64Decode only understand packed encoding */
740 if (len >= BASE64_EXPAND(MAX_STRING_LEN))
743 len = base64Decode(str, vp->vp_octets);
764 gss_eap_radius_attr_provider::name(void) const
770 gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
773 VALUE_PAIR **pNext = &m_vps;
775 if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
778 JSONObject attrs = obj["attributes"];
779 size_t nelems = attrs.size();
781 for (size_t i = 0; i < nelems; i++) {
782 JSONObject attr = attrs[i];
785 if (!jsonToAvp(&vp, attr))
792 m_authenticated = obj["authenticated"].integer() ? true : false;
798 gss_eap_radius_attr_provider::prefix(void) const
800 return "urn:ietf:params:gss-eap:radius-avp";
804 gss_eap_radius_attr_provider::jsonRepresentation(void) const
806 JSONObject obj, attrs = JSONObject::array();
808 for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) {
809 JSONObject attr = avpToJson(vp);
813 obj.set("attributes", attrs);
815 obj.set("authenticated", m_authenticated);
821 gss_eap_radius_attr_provider::getExpiryTime(void) const
825 vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
826 if (vp == NULL || vp->lvalue == 0)
829 return time(NULL) + vp->lvalue;
833 gssEapRadiusMapError(OM_uint32 *minor,
834 struct rs_error *err)
838 GSSEAP_ASSERT(err != NULL);
840 code = rs_err_code(err, 0);
842 if (code == RSE_OK) {
844 return GSS_S_COMPLETE;
847 *minor = ERROR_TABLE_BASE_rse + code;
849 gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
852 return GSS_S_FAILURE;
856 gssEapCreateRadiusContext(OM_uint32 *minor,
858 struct rs_context **pRadContext)
860 const char *configFile = RS_CONFIG_FILE;
861 struct rs_context *radContext;
862 struct rs_alloc_scheme ralloc;
863 struct rs_error *err;
868 if (rs_context_create(&radContext) != 0) {
869 *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
870 return GSS_S_FAILURE;
873 if (cred->radiusConfigFile.value != NULL)
874 configFile = (const char *)cred->radiusConfigFile.value;
876 ralloc.calloc = GSSEAP_CALLOC;
877 ralloc.malloc = GSSEAP_MALLOC;
878 ralloc.free = GSSEAP_FREE;
879 ralloc.realloc = GSSEAP_REALLOC;
881 rs_context_set_alloc_scheme(radContext, &ralloc);
883 if (rs_context_read_config(radContext, configFile) != 0) {
884 err = rs_err_ctx_pop(radContext);
888 if (rs_context_init_freeradius_dict(radContext, NULL) != 0) {
889 err = rs_err_ctx_pop(radContext);
893 *pRadContext = radContext;
896 return GSS_S_COMPLETE;
899 major = gssEapRadiusMapError(minor, err);
900 rs_context_destroy(radContext);
905 #endif /* GSSEAP_ENABLE_ACCEPTOR */
908 gssEapRadiusAddAttr(OM_uint32 *minor, struct wpabuf **buf, uint16_t attr,
909 uint16_t vendor, gss_buffer_t buffer)
911 if (radius_add_tlv(buf, attr, vendor, (u8 *)buffer->value,
912 buffer->length) < 0) {
913 *minor = ENOMEM; /* could be length too long, though */
914 return GSS_S_FAILURE;
916 return GSS_S_COMPLETE;