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 rs_avp *copyAvps(rs_const_avp *src);
44 gssEapRadiusGetAvp(OM_uint32 *minor,
46 const gss_eap_attrid &attrid,
51 gssEapRadiusAddAvp(OM_uint32 *minor,
53 const gss_eap_attrid &attrid,
54 const gss_buffer_t buffer);
57 avpToAttrId(rs_const_avp *vp)
59 gss_eap_attrid attrid;
61 rs_avp_attrid(vp, &attrid.second, &attrid.first);
66 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
69 m_authenticated = false;
72 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
79 gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
80 const gss_eap_attr_provider *ctx)
82 const gss_eap_radius_attr_provider *radius;
84 if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
87 radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
89 if (radius->m_vps != NULL)
90 m_vps = copyAvps(radius->getAvps());
92 m_authenticated = radius->m_authenticated;
98 gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
99 const gss_cred_id_t gssCred,
100 const gss_ctx_id_t gssCtx)
102 if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
105 if (gssCtx != GSS_C_NO_CONTEXT) {
106 if (gssCtx->acceptorCtx.vps != NULL) {
107 m_vps = copyAvps(gssCtx->acceptorCtx.vps);
111 /* We assume libradsec validated this for us */
112 GSSEAP_ASSERT(rs_avp_find(m_vps, PW_MESSAGE_AUTHENTICATOR, 0) != NULL);
113 m_authenticated = true;
121 alreadyAddedAttributeP(std::vector <gss_eap_attrid> &attrs,
122 gss_eap_attrid &attrid)
124 for (std::vector<gss_eap_attrid>::const_iterator a = attrs.begin();
127 if (attrid.first == (*a).first &&
128 attrid.second == (*a).second)
136 isSecretAttributeP(const gss_eap_attrid &attrid)
138 bool bSecretAttribute = false;
140 switch (attrid.first) {
141 case VENDORPEC_MICROSOFT:
142 switch (attrid.second) {
143 case PW_MS_MPPE_SEND_KEY:
144 case PW_MS_MPPE_RECV_KEY:
145 bSecretAttribute = true;
154 return bSecretAttribute;
158 isSecretAttributeP(rs_const_avp *vp)
160 return isSecretAttributeP(avpToAttrId(vp));
164 isInternalAttributeP(const gss_eap_attrid &attrid)
166 bool bInternalAttribute = false;
168 /* should have been filtered */
169 GSSEAP_ASSERT(!isSecretAttributeP(attrid));
171 switch (attrid.first) {
172 case VENDORPEC_UKERNA:
173 switch (attrid.second) {
174 case PW_GSS_ACCEPTOR_SERVICE_NAME:
175 case PW_GSS_ACCEPTOR_HOST_NAME:
176 case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
177 case PW_GSS_ACCEPTOR_REALM_NAME:
178 case PW_SAML_AAA_ASSERTION:
179 bInternalAttribute = true;
189 return bInternalAttribute;
193 isInternalAttributeP(rs_const_avp *vp)
195 return isInternalAttributeP(avpToAttrId(vp));
199 isFragmentedAttributeP(const gss_eap_attrid &attrid)
201 /* A bit of a hack for the PAC for now. Should be configurable. */
202 return (attrid.first == VENDORPEC_UKERNA) &&
203 !isInternalAttributeP(attrid);
207 * Copy AVP list, same as paircopy except it filters out attributes
211 copyAvps(rs_const_avp *src)
216 for (vp = src; vp != NULL; vp = rs_avp_next_const(vp)) {
219 if (isSecretAttributeP(vp))
222 vpcopy = rs_avp_dup(vp);
223 if (vpcopy == NULL) {
225 throw std::bad_alloc();
228 rs_avp_append(&dst, vpcopy);
235 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
239 std::vector <gss_eap_attrid> seen;
241 for (vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
242 gss_buffer_desc desc;
243 gss_eap_attrid attrid;
246 /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
247 if (isInternalAttributeP(vp))
250 rs_avp_attrid(vp, &attrid.second, &attrid.first);
252 if (alreadyAddedAttributeP(seen, attrid))
255 /* TODO support draft-ietf-radext-radius-extensions */
256 if (attrid.first != 0) {
257 snprintf(buf, sizeof(buf), "26.%u.%u", attrid.first, attrid.second);
259 snprintf(buf, sizeof(buf), "%u", attrid.second);
263 desc.length = strlen(buf);
265 if (!addAttribute(m_manager, this, &desc, data))
268 seen.push_back(attrid);
275 getAttributeId(const gss_buffer_t desc,
276 gss_eap_attrid *attrid)
279 gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
283 /* need to duplicate because attr may not be NUL terminated */
284 duplicateBuffer(*desc, &strAttr);
285 s = (char *)strAttr.value;
287 /* TODO support draft-ietf-radext-radius-extensions */
289 unsigned int tmp = strtoul(s, &s, 10);
295 case PW_VENDOR_SPECIFIC:
296 /* attribute name formatted as 26.Vendor.Attribute */
297 attrid->first = strtoul(s, &s, 10);
300 attrid->second = strtoul(s, &s, 10);
307 } else if (*s == '\0') {
308 /* Non-vendor attrbiute */
310 attrid->second = tmp;
315 ret = (rs_attr_find(s, &attrid->second, &attrid->first) == RSE_OK);
318 gss_release_buffer(&tmpMinor, &strAttr);
324 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
325 const gss_eap_attrid &attrid,
326 const gss_buffer_t value)
328 OM_uint32 major = GSS_S_UNAVAILABLE, minor;
330 if (!isSecretAttributeP(attrid) &&
331 !isInternalAttributeP(attrid)) {
332 deleteAttribute(attrid);
334 major = gssEapRadiusAddAvp(&minor, &m_vps, attrid, value);
337 return !GSS_ERROR(major);
341 gss_eap_radius_attr_provider::setAttribute(int complete,
342 const gss_buffer_t attr,
343 const gss_buffer_t value)
345 gss_eap_attrid attrid;
347 if (!getAttributeId(attr, &attrid))
350 return setAttribute(complete, attrid, value);
354 gss_eap_radius_attr_provider::deleteAttribute(const gss_eap_attrid &attrid)
356 if (isSecretAttributeP(attrid) ||
357 isInternalAttributeP(attrid) ||
358 rs_avp_find(m_vps, attrid.second, attrid.first) == NULL)
361 return (rs_avp_delete(&m_vps, attrid.second, attrid.first) == RSE_OK);
365 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
367 gss_eap_attrid attrid;
369 if (!getAttributeId(attr, &attrid))
372 return deleteAttribute(attrid);
376 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
380 gss_buffer_t display_value,
383 gss_eap_attrid attrid;
385 if (!getAttributeId(attr, &attrid))
388 return getAttribute(attrid,
389 authenticated, complete,
390 value, display_value, more);
394 gss_eap_radius_attr_provider::getAttribute(const gss_eap_attrid &attrid,
398 gss_buffer_t display_value,
402 int i = *more, count = 0;
409 if (isSecretAttributeP(attrid) ||
410 isInternalAttributeP(attrid)) {
412 } else if (isFragmentedAttributeP(attrid)) {
413 return getFragmentedAttribute(attrid,
419 for (vp = rs_avp_find_const(m_vps, attrid.second, attrid.first);
421 vp = rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first)) {
423 if (rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first) != NULL)
429 if (vp == NULL && *more == 0)
432 if (value != GSS_C_NO_BUFFER) {
433 gss_buffer_desc valueBuf;
435 rs_avp_octets_value_byref((rs_avp *)vp,
436 (unsigned char **)&valueBuf.value,
439 duplicateBuffer(valueBuf, value);
442 if (display_value != GSS_C_NO_BUFFER &&
443 !rs_avp_is_octets(vp)) {
444 char displayString[RS_MAX_STRING_LEN];
445 gss_buffer_desc displayBuf;
447 displayBuf.length = rs_avp_display_value(vp, displayString,
448 sizeof(displayString));
449 displayBuf.value = (void *)displayString;
451 duplicateBuffer(displayBuf, display_value);
454 if (authenticated != NULL)
455 *authenticated = m_authenticated;
456 if (complete != NULL)
463 gss_eap_radius_attr_provider::getFragmentedAttribute(const gss_eap_attrid &attrid,
466 gss_buffer_t value) const
468 OM_uint32 major, minor;
470 major = gssEapRadiusGetAvp(&minor, m_vps, attrid, value, TRUE);
472 if (authenticated != NULL)
473 *authenticated = m_authenticated;
474 if (complete != NULL)
477 return !GSS_ERROR(major);
481 gss_eap_radius_attr_provider::mapToAny(int authenticated,
482 gss_buffer_t type_id GSSEAP_UNUSED) const
484 if (authenticated && !m_authenticated)
485 return (gss_any_t)NULL;
487 return (gss_any_t)copyAvps(m_vps);
491 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
492 gss_any_t input) const
494 rs_avp *vp = (rs_avp *)input;
499 gss_eap_radius_attr_provider::init(void)
501 gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, createAttrContext);
507 gss_eap_radius_attr_provider::finalize(void)
509 gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
512 gss_eap_attr_provider *
513 gss_eap_radius_attr_provider::createAttrContext(void)
515 return new gss_eap_radius_attr_provider;
519 gssEapRadiusAddAvp(OM_uint32 *minor,
521 const gss_eap_attrid &attrid,
522 const gss_buffer_t buffer)
524 unsigned char *p = (unsigned char *)buffer->value;
525 size_t remain = buffer->length;
532 * There's an extra byte of padding; RADIUS AVPs can only
535 if (n >= RS_MAX_STRING_LEN)
536 n = RS_MAX_STRING_LEN - 1;
538 vp = rs_avp_alloc(attrid.second, attrid.first);
541 return GSS_S_FAILURE;
544 rs_avp_octets_set(vp, p, n);
546 rs_avp_append(vps, vp);
550 } while (remain != 0);
552 return GSS_S_COMPLETE;
556 gssEapRadiusAddAvp(OM_uint32 *minor,
557 struct rs_packet *pkt,
558 unsigned int attribute,
560 const gss_buffer_t buffer)
562 gss_eap_attrid attrid(vendor, attribute);
565 code = rs_packet_append_avp(pkt, attrid.second, attrid.first,
566 buffer->value, buffer->length);
567 if (code != RSE_OK) {
568 *minor = RS_MAP_ERROR(code);
569 return GSS_S_FAILURE;
573 return GSS_S_COMPLETE;
577 gssEapRadiusGetRawAvp(OM_uint32 *minor,
579 unsigned int attribute,
583 *vp = rs_avp_find_const(vps, attribute, vendor);
585 *minor = GSSEAP_NO_SUCH_ATTR;
586 return GSS_S_UNAVAILABLE;
589 return GSS_S_COMPLETE;
593 gssEapRadiusGetAvp(OM_uint32 *minor,
595 const gss_eap_attrid &attrid,
602 if (buffer != GSS_C_NO_BUFFER) {
604 buffer->value = NULL;
607 vp = rs_avp_find_const(vps, attrid.second, attrid.first);
609 *minor = GSSEAP_NO_SUCH_ATTR;
610 return GSS_S_UNAVAILABLE;
613 if (buffer != GSS_C_NO_BUFFER) {
615 rs_avp_fragmented_value(vp, NULL, &buffer->length);
617 buffer->length = rs_avp_length(vp);
619 buffer->value = GSSEAP_MALLOC(buffer->length);
620 if (buffer->value == NULL) {
622 return GSS_S_FAILURE;
626 err = rs_avp_fragmented_value(vp, (unsigned char *)buffer->value, &buffer->length);
628 err = rs_avp_octets_value(vp, (unsigned char *)buffer->value, &buffer->length);
631 *minor = RS_MAP_ERROR(err);
632 return GSS_S_FAILURE;
637 return GSS_S_COMPLETE;
641 gssEapRadiusGetAvp(OM_uint32 *minor,
642 struct rs_packet *pkt,
643 unsigned int attribute,
649 gss_eap_attrid attrid(vendor, attribute);
651 rs_packet_avps(pkt, &vps);
653 return gssEapRadiusGetAvp(minor, *vps, attrid, buffer, concat);
657 gssEapRadiusFreeAvps(OM_uint32 *minor,
662 return GSS_S_COMPLETE;
666 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
668 if (!gss_eap_radius_attr_provider::init()) {
669 *minor = GSSEAP_RADSEC_INIT_FAILURE;
670 return GSS_S_FAILURE;
673 return GSS_S_COMPLETE;
677 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
679 gss_eap_radius_attr_provider::finalize();
682 return GSS_S_COMPLETE;
686 avpToJson(rs_const_avp *vp)
689 gss_eap_attrid attrid;
691 GSSEAP_ASSERT(rs_avp_length(vp) <= RS_MAX_STRING_LEN);
693 switch (rs_avp_typeof(vp)) {
694 case RS_TYPE_INTEGER:
695 obj.set("value", rs_avp_integer_value(vp));
698 obj.set("value", rs_avp_date_value(vp));
701 obj.set("value", rs_avp_string_value(vp));
706 if (base64Encode(rs_avp_octets_value_const_ptr(vp),
707 rs_avp_length(vp), &b64) < 0)
708 throw std::bad_alloc();
710 obj.set("value", b64);
716 attrid = avpToAttrId(vp);
718 obj.set("type", attrid.second);
719 if (attrid.first != 0)
720 obj.set("vendor", attrid.first);
726 jsonToAvp(rs_avp **pVp, JSONObject &obj)
729 gss_eap_attrid attrid;
731 JSONObject type = obj["type"];
732 JSONObject vendor = obj["vendor"];
733 JSONObject value = obj["value"];
735 if (!type.isInteger())
737 attrid.second = type.integer();
739 if (!vendor.isNull()) {
740 if (!vendor.isInteger())
742 attrid.first = vendor.integer();
747 vp = rs_avp_alloc(attrid.second, attrid.first);
749 throw std::bad_alloc();
751 switch (rs_avp_typeof(vp)) {
752 case RS_TYPE_INTEGER:
755 if (!value.isInteger())
758 if (rs_avp_integer_set(vp, value.integer()) != RSE_OK)
762 case RS_TYPE_STRING: {
763 if (!value.isString())
766 if (rs_avp_string_set(vp, value.string()) != RSE_OK)
773 unsigned char buf[RS_MAX_STRING_LEN];
775 if (!value.isString())
778 const char *str = value.string();
779 ssize_t len = strlen(str);
781 /* this optimization requires base64Decode only understand packed encoding */
782 if (len >= BASE64_EXPAND(RS_MAX_STRING_LEN))
785 len = base64Decode(str, buf);
789 if (rs_avp_octets_set(vp, buf, len) != RSE_OK)
808 gss_eap_radius_attr_provider::name(void) const
814 gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
817 if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
820 JSONObject attrs = obj["attributes"];
821 size_t nelems = attrs.size();
823 for (size_t i = 0; i < nelems; i++) {
824 JSONObject attr = attrs[i];
827 if (!jsonToAvp(&vp, attr))
830 rs_avp_append(&m_vps, vp);
833 m_authenticated = obj["authenticated"].integer() ? true : false;
839 gss_eap_radius_attr_provider::prefix(void) const
841 return "urn:ietf:params:gssapi:aaa-radius";
845 gss_eap_radius_attr_provider::jsonRepresentation(void) const
847 JSONObject obj, attrs = JSONObject::array();
849 for (rs_avp *vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
850 JSONObject attr = avpToJson(vp);
854 obj.set("attributes", attrs);
856 obj.set("authenticated", m_authenticated);
862 gss_eap_radius_attr_provider::getExpiryTime(void) const
867 vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
871 value = rs_avp_integer_value(vp);
875 return time(NULL) + value;
879 gssEapRadiusMapError(OM_uint32 *minor,
880 struct rs_error *err)
884 GSSEAP_ASSERT(err != NULL);
886 code = rs_err_code(err, 0);
888 if (code == RSE_OK) {
890 return GSS_S_COMPLETE;
893 *minor = RS_MAP_ERROR(code);
895 gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
898 return GSS_S_FAILURE;
902 gssEapCreateRadiusContext(OM_uint32 *minor,
904 struct rs_context **pRadContext)
906 const char *configFile = RS_CONFIG_FILE;
907 struct rs_context *radContext;
908 struct rs_alloc_scheme ralloc;
909 struct rs_error *err;
914 if (rs_context_create(&radContext) != 0) {
915 *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
916 return GSS_S_FAILURE;
919 if (cred->radiusConfigFile.value != NULL)
920 configFile = (const char *)cred->radiusConfigFile.value;
922 ralloc.calloc = GSSEAP_CALLOC;
923 ralloc.malloc = GSSEAP_MALLOC;
924 ralloc.free = GSSEAP_FREE;
925 ralloc.realloc = GSSEAP_REALLOC;
927 rs_context_set_alloc_scheme(radContext, &ralloc);
929 if (rs_context_read_config(radContext, configFile) != 0) {
930 err = rs_err_ctx_pop(radContext);
934 *pRadContext = radContext;
937 return GSS_S_COMPLETE;
940 major = gssEapRadiusMapError(minor, err);
941 rs_context_destroy(radContext);