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"
39 #define RS_MAP_ERROR(code) (ERROR_TABLE_BASE_rse + (code))
41 static gss_buffer_desc radiusUrnPrefix = {
42 sizeof("urn:x-radius:") - 1,
43 (void *)"urn:x-radius:"
46 static rs_avp *copyAvps(rs_const_avp *src);
49 gssEapRadiusGetAvp(OM_uint32 *minor,
51 const gss_eap_attrid &attrid,
56 gssEapRadiusAddAvp(OM_uint32 *minor,
58 const gss_eap_attrid &attrid,
59 const gss_buffer_t buffer);
62 avpToAttrId(rs_const_avp *vp)
64 gss_eap_attrid attrid;
66 rs_avp_attrid(vp, &attrid.second, &attrid.first);
71 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
74 m_authenticated = false;
77 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
84 gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
85 const gss_eap_attr_provider *ctx)
87 const gss_eap_radius_attr_provider *radius;
89 if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
92 radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
94 if (radius->m_vps != NULL)
95 m_vps = copyAvps(radius->getAvps());
97 m_authenticated = radius->m_authenticated;
103 gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
104 const gss_cred_id_t gssCred,
105 const gss_ctx_id_t gssCtx)
107 if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
110 if (gssCtx != GSS_C_NO_CONTEXT) {
111 if (gssCtx->acceptorCtx.vps != NULL) {
112 m_vps = copyAvps(gssCtx->acceptorCtx.vps);
116 /* We assume libradsec validated this for us */
117 GSSEAP_ASSERT(rs_avp_find(m_vps, PW_MESSAGE_AUTHENTICATOR, 0) != NULL);
118 m_authenticated = true;
126 alreadyAddedAttributeP(std::vector <gss_eap_attrid> &attrs,
127 gss_eap_attrid &attrid)
129 for (std::vector<gss_eap_attrid>::const_iterator a = attrs.begin();
132 if (attrid.first == (*a).first &&
133 attrid.second == (*a).second)
141 isSecretAttributeP(const gss_eap_attrid &attrid)
143 bool bSecretAttribute = false;
145 switch (attrid.first) {
146 case VENDORPEC_MICROSOFT:
147 switch (attrid.second) {
148 case PW_MS_MPPE_SEND_KEY:
149 case PW_MS_MPPE_RECV_KEY:
150 bSecretAttribute = true;
159 return bSecretAttribute;
163 isSecretAttributeP(rs_const_avp *vp)
165 return isSecretAttributeP(avpToAttrId(vp));
169 isInternalAttributeP(const gss_eap_attrid &attrid)
171 bool bInternalAttribute = false;
173 /* should have been filtered */
174 GSSEAP_ASSERT(!isSecretAttributeP(attrid));
176 switch (attrid.first) {
177 case VENDORPEC_UKERNA:
178 switch (attrid.second) {
179 case PW_GSS_ACCEPTOR_SERVICE_NAME:
180 case PW_GSS_ACCEPTOR_HOST_NAME:
181 case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
182 case PW_GSS_ACCEPTOR_REALM_NAME:
183 case PW_SAML_AAA_ASSERTION:
184 bInternalAttribute = true;
194 return bInternalAttribute;
198 isInternalAttributeP(rs_const_avp *vp)
200 return isInternalAttributeP(avpToAttrId(vp));
204 isFragmentedAttributeP(const gss_eap_attrid &attrid)
206 /* A bit of a hack for the PAC for now. Should be configurable. */
207 return (attrid.first == VENDORPEC_UKERNA) &&
208 !isInternalAttributeP(attrid);
212 * Copy AVP list, same as paircopy except it filters out attributes
216 copyAvps(rs_const_avp *src)
221 for (vp = src; vp != NULL; vp = rs_avp_next_const(vp)) {
224 if (isSecretAttributeP(vp))
227 vpcopy = rs_avp_dup(vp);
228 if (vpcopy == NULL) {
230 throw std::bad_alloc();
233 rs_avp_append(&dst, vpcopy);
240 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
244 std::vector <gss_eap_attrid> seen;
246 for (vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
247 gss_buffer_desc desc;
248 gss_eap_attrid attrid;
251 /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
252 if (isInternalAttributeP(vp))
255 rs_avp_attrid(vp, &attrid.second, &attrid.first);
257 if (alreadyAddedAttributeP(seen, attrid))
260 if (attrid.second != 0) {
261 snprintf(buf, sizeof(buf), "%s%u.%u",
262 (char *)radiusUrnPrefix.value, attrid.first, attrid.second);
264 snprintf(buf, sizeof(buf), "%s%u",
265 (char *)radiusUrnPrefix.value, attrid.second);
269 desc.length = strlen(buf);
271 if (!addAttribute(m_manager, this, &desc, data))
274 seen.push_back(attrid);
281 getAttributeId(const gss_buffer_t desc,
282 gss_eap_attrid *attrid)
285 gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
289 if (desc->length < radiusUrnPrefix.length ||
290 memcmp(desc->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
293 /* need to duplicate because attr may not be NUL terminated */
294 duplicateBuffer(*desc, &strAttr);
295 s = (char *)strAttr.value + radiusUrnPrefix.length;
299 ret = (rs_attr_find(s, &attrid->second, &attrid->first) == RSE_OK);
302 unsigned int tmp = strtoul(s, &s2, 10);
305 /* Non-vendor attrbiute */
307 attrid->second = tmp;
309 } else if (*s2 == '.') {
310 /* Vendor attributes formatted as Vendor.Attribute */
312 attrid->second = strtoul(s2 + 1, NULL, 10);
319 gss_release_buffer(&tmpMinor, &strAttr);
325 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
326 const gss_eap_attrid &attrid,
327 const gss_buffer_t value)
329 OM_uint32 major = GSS_S_UNAVAILABLE, minor;
331 if (!isSecretAttributeP(attrid) &&
332 !isInternalAttributeP(attrid)) {
333 deleteAttribute(attrid);
335 major = gssEapRadiusAddAvp(&minor, &m_vps, attrid, value);
338 return !GSS_ERROR(major);
342 gss_eap_radius_attr_provider::setAttribute(int complete,
343 const gss_buffer_t attr,
344 const gss_buffer_t value)
346 gss_eap_attrid attrid;
348 if (!getAttributeId(attr, &attrid))
351 return setAttribute(complete, attrid, value);
355 gss_eap_radius_attr_provider::deleteAttribute(const gss_eap_attrid &attrid)
357 if (isSecretAttributeP(attrid) ||
358 isInternalAttributeP(attrid) ||
359 rs_avp_find(m_vps, attrid.second, attrid.first) == NULL)
362 return (rs_avp_delete(&m_vps, attrid.second, attrid.first) == RSE_OK);
366 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
368 gss_eap_attrid attrid;
370 if (!getAttributeId(attr, &attrid))
373 return deleteAttribute(attrid);
377 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
381 gss_buffer_t display_value,
384 gss_eap_attrid attrid;
386 if (!getAttributeId(attr, &attrid))
389 return getAttribute(attrid,
390 authenticated, complete,
391 value, display_value, more);
395 gss_eap_radius_attr_provider::getAttribute(const gss_eap_attrid &attrid,
399 gss_buffer_t display_value,
403 int i = *more, count = 0;
410 if (isSecretAttributeP(attrid) ||
411 isInternalAttributeP(attrid)) {
413 } else if (isFragmentedAttributeP(attrid)) {
414 return getFragmentedAttribute(attrid,
420 for (vp = rs_avp_find_const(m_vps, attrid.second, attrid.first);
422 vp = rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first)) {
424 if (rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first) != NULL)
430 if (vp == NULL && *more == 0)
433 if (value != GSS_C_NO_BUFFER) {
434 gss_buffer_desc valueBuf;
436 rs_avp_octets_value_byref((rs_avp *)vp,
437 (unsigned char **)&valueBuf.value,
440 duplicateBuffer(valueBuf, value);
443 if (display_value != GSS_C_NO_BUFFER &&
444 !rs_avp_is_octets(vp)) {
445 char displayString[RS_MAX_STRING_LEN];
446 gss_buffer_desc displayBuf;
448 displayBuf.length = rs_avp_display_value(vp, displayString,
449 sizeof(displayString));
450 displayBuf.value = (void *)displayString;
452 duplicateBuffer(displayBuf, display_value);
455 if (authenticated != NULL)
456 *authenticated = m_authenticated;
457 if (complete != NULL)
464 gss_eap_radius_attr_provider::getFragmentedAttribute(const gss_eap_attrid &attrid,
467 gss_buffer_t value) const
469 OM_uint32 major, minor;
471 major = gssEapRadiusGetAvp(&minor, m_vps, attrid, value, TRUE);
473 if (authenticated != NULL)
474 *authenticated = m_authenticated;
475 if (complete != NULL)
478 return !GSS_ERROR(major);
482 gss_eap_radius_attr_provider::mapToAny(int authenticated,
483 gss_buffer_t type_id GSSEAP_UNUSED) const
485 if (authenticated && !m_authenticated)
486 return (gss_any_t)NULL;
488 return (gss_any_t)copyAvps(m_vps);
492 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
493 gss_any_t input) const
495 rs_avp *vp = (rs_avp *)input;
500 gss_eap_radius_attr_provider::init(void)
502 gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, createAttrContext);
508 gss_eap_radius_attr_provider::finalize(void)
510 gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
513 gss_eap_attr_provider *
514 gss_eap_radius_attr_provider::createAttrContext(void)
516 return new gss_eap_radius_attr_provider;
520 gssEapRadiusAddAvp(OM_uint32 *minor,
522 const gss_eap_attrid &attrid,
523 const gss_buffer_t buffer)
525 unsigned char *p = (unsigned char *)buffer->value;
526 size_t remain = buffer->length;
533 * There's an extra byte of padding; RADIUS AVPs can only
536 if (n >= RS_MAX_STRING_LEN)
537 n = RS_MAX_STRING_LEN - 1;
539 vp = rs_avp_alloc(attrid.second, attrid.first);
542 return GSS_S_FAILURE;
545 rs_avp_octets_set(vp, p, n);
547 rs_avp_append(vps, vp);
551 } while (remain != 0);
553 return GSS_S_COMPLETE;
557 gssEapRadiusAddAvp(OM_uint32 *minor,
558 struct rs_packet *pkt,
559 unsigned int attribute,
561 const gss_buffer_t buffer)
563 gss_eap_attrid attrid(vendor, attribute);
566 code = rs_packet_append_avp(pkt, attrid.second, attrid.first,
567 buffer->value, buffer->length);
568 if (code != RSE_OK) {
569 *minor = RS_MAP_ERROR(code);
570 return GSS_S_FAILURE;
574 return GSS_S_COMPLETE;
578 gssEapRadiusGetRawAvp(OM_uint32 *minor,
580 unsigned int attribute,
584 *vp = rs_avp_find_const(vps, attribute, vendor);
586 *minor = GSSEAP_NO_SUCH_ATTR;
587 return GSS_S_UNAVAILABLE;
590 return GSS_S_COMPLETE;
594 gssEapRadiusGetAvp(OM_uint32 *minor,
596 const gss_eap_attrid &attrid,
603 if (buffer != GSS_C_NO_BUFFER) {
605 buffer->value = NULL;
608 vp = rs_avp_find_const(vps, attrid.second, attrid.first);
610 *minor = GSSEAP_NO_SUCH_ATTR;
611 return GSS_S_UNAVAILABLE;
614 if (buffer != GSS_C_NO_BUFFER) {
616 rs_avp_fragmented_value(vp, NULL, &buffer->length);
618 buffer->length = rs_avp_length(vp);
620 buffer->value = GSSEAP_MALLOC(buffer->length);
621 if (buffer->value == NULL) {
623 return GSS_S_FAILURE;
627 err = rs_avp_fragmented_value(vp, (unsigned char *)buffer->value, &buffer->length);
629 err = rs_avp_octets_value(vp, (unsigned char *)buffer->value, &buffer->length);
632 *minor = RS_MAP_ERROR(err);
633 return GSS_S_FAILURE;
638 return GSS_S_COMPLETE;
642 gssEapRadiusGetAvp(OM_uint32 *minor,
643 struct rs_packet *pkt,
644 unsigned int attribute,
650 gss_eap_attrid attrid(vendor, attribute);
652 rs_packet_avps(pkt, &vps);
654 return gssEapRadiusGetAvp(minor, *vps, attrid, buffer, concat);
658 gssEapRadiusFreeAvps(OM_uint32 *minor,
663 return GSS_S_COMPLETE;
667 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
669 if (!gss_eap_radius_attr_provider::init()) {
670 *minor = GSSEAP_RADSEC_INIT_FAILURE;
671 return GSS_S_FAILURE;
674 return GSS_S_COMPLETE;
678 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
680 gss_eap_radius_attr_provider::finalize();
683 return GSS_S_COMPLETE;
687 avpToJson(rs_const_avp *vp)
690 gss_eap_attrid attrid;
692 GSSEAP_ASSERT(rs_avp_length(vp) <= RS_MAX_STRING_LEN);
694 switch (rs_avp_typeof(vp)) {
695 case RS_TYPE_INTEGER:
696 obj.set("value", rs_avp_integer_value(vp));
699 obj.set("value", rs_avp_date_value(vp));
702 obj.set("value", rs_avp_string_value(vp));
707 if (base64Encode(rs_avp_octets_value_const_ptr(vp),
708 rs_avp_length(vp), &b64) < 0)
709 throw std::bad_alloc();
711 obj.set("value", b64);
717 attrid = avpToAttrId(vp);
719 obj.set("type", attrid.second);
720 if (attrid.first != 0)
721 obj.set("vendor", attrid.first);
727 jsonToAvp(rs_avp **pVp, JSONObject &obj)
730 gss_eap_attrid attrid;
732 JSONObject type = obj["type"];
733 JSONObject vendor = obj["vendor"];
734 JSONObject value = obj["value"];
736 if (!type.isInteger())
738 attrid.second = type.integer();
740 if (!vendor.isNull()) {
741 if (!vendor.isInteger())
743 attrid.first = vendor.integer();
748 vp = rs_avp_alloc(attrid.second, attrid.first);
750 throw std::bad_alloc();
752 switch (rs_avp_typeof(vp)) {
753 case RS_TYPE_INTEGER:
756 if (!value.isInteger())
759 if (rs_avp_integer_set(vp, value.integer()) != RSE_OK)
763 case RS_TYPE_STRING: {
764 if (!value.isString())
767 if (rs_avp_string_set(vp, value.string()) != RSE_OK)
774 unsigned char buf[RS_MAX_STRING_LEN];
776 if (!value.isString())
779 const char *str = value.string();
780 ssize_t len = strlen(str);
782 /* this optimization requires base64Decode only understand packed encoding */
783 if (len >= BASE64_EXPAND(RS_MAX_STRING_LEN))
786 len = base64Decode(str, buf);
790 if (rs_avp_octets_set(vp, buf, len) != RSE_OK)
809 gss_eap_radius_attr_provider::name(void) const
815 gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
818 if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
821 JSONObject attrs = obj["attributes"];
822 size_t nelems = attrs.size();
824 for (size_t i = 0; i < nelems; i++) {
825 JSONObject attr = attrs[i];
828 if (!jsonToAvp(&vp, attr))
831 rs_avp_append(&m_vps, vp);
834 m_authenticated = obj["authenticated"].integer() ? true : false;
840 gss_eap_radius_attr_provider::prefix(void) const
842 return "urn:ietf:params:gss-eap:radius-avp";
846 gss_eap_radius_attr_provider::jsonRepresentation(void) const
848 JSONObject obj, attrs = JSONObject::array();
850 for (rs_avp *vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
851 JSONObject attr = avpToJson(vp);
855 obj.set("attributes", attrs);
857 obj.set("authenticated", m_authenticated);
863 gss_eap_radius_attr_provider::getExpiryTime(void) const
868 vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
872 value = rs_avp_integer_value(vp);
876 return time(NULL) + value;
880 gssEapRadiusMapError(OM_uint32 *minor,
881 struct rs_error *err)
885 GSSEAP_ASSERT(err != NULL);
887 code = rs_err_code(err, 0);
889 if (code == RSE_OK) {
891 return GSS_S_COMPLETE;
894 *minor = RS_MAP_ERROR(code);
896 gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
899 return GSS_S_FAILURE;
903 gssEapCreateRadiusContext(OM_uint32 *minor,
905 struct rs_context **pRadContext)
907 const char *configFile = RS_CONFIG_FILE;
908 struct rs_context *radContext;
909 struct rs_alloc_scheme ralloc;
910 struct rs_error *err;
915 if (rs_context_create(&radContext) != 0) {
916 *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
917 return GSS_S_FAILURE;
920 if (cred->radiusConfigFile.value != NULL)
921 configFile = (const char *)cred->radiusConfigFile.value;
923 ralloc.calloc = GSSEAP_CALLOC;
924 ralloc.malloc = GSSEAP_MALLOC;
925 ralloc.free = GSSEAP_FREE;
926 ralloc.realloc = GSSEAP_REALLOC;
928 rs_context_set_alloc_scheme(radContext, &ralloc);
930 if (rs_context_read_config(radContext, configFile) != 0) {
931 err = rs_err_ctx_pop(radContext);
935 *pRadContext = radContext;
938 return GSS_S_COMPLETE;
941 major = gssEapRadiusMapError(minor, err);
942 rs_context_destroy(radContext);