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 #define RS_MAP_ERROR(code) (ERROR_TABLE_BASE_rse + (code))
45 static rs_avp *copyAvps(rs_const_avp *src);
48 gssEapRadiusGetAvp(OM_uint32 *minor,
50 const gss_eap_attrid &attrid,
55 gssEapRadiusAddAvp(OM_uint32 *minor,
57 const gss_eap_attrid &attrid,
58 const gss_buffer_t buffer);
61 avpToAttrId(rs_const_avp *vp)
63 gss_eap_attrid attrid;
65 rs_avp_attrid(vp, &attrid.second, &attrid.first);
70 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
73 m_authenticated = false;
76 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
83 gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
84 const gss_eap_attr_provider *ctx)
86 const gss_eap_radius_attr_provider *radius;
88 if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
91 radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
93 if (radius->m_vps != NULL)
94 m_vps = copyAvps(radius->getAvps());
96 m_authenticated = radius->m_authenticated;
102 gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
103 const gss_cred_id_t gssCred,
104 const gss_ctx_id_t gssCtx)
106 if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
109 if (gssCtx != GSS_C_NO_CONTEXT) {
110 if (gssCtx->acceptorCtx.vps != NULL) {
111 m_vps = copyAvps(gssCtx->acceptorCtx.vps);
115 /* We assume libradsec validated this for us */
116 GSSEAP_ASSERT(rs_avp_find(m_vps, PW_MESSAGE_AUTHENTICATOR, 0) != NULL);
117 m_authenticated = true;
125 alreadyAddedAttributeP(std::vector <gss_eap_attrid> &attrs,
126 gss_eap_attrid &attrid)
128 for (std::vector<gss_eap_attrid>::const_iterator a = attrs.begin();
131 if (attrid.first == (*a).first &&
132 attrid.second == (*a).second)
140 isSecretAttributeP(const gss_eap_attrid &attrid)
142 bool bSecretAttribute = false;
144 switch (attrid.first) {
145 case VENDORPEC_MICROSOFT:
146 switch (attrid.second) {
147 case PW_MS_MPPE_SEND_KEY:
148 case PW_MS_MPPE_RECV_KEY:
149 bSecretAttribute = true;
158 return bSecretAttribute;
162 isSecretAttributeP(rs_const_avp *vp)
164 return isSecretAttributeP(avpToAttrId(vp));
168 isInternalAttributeP(const gss_eap_attrid &attrid)
170 bool bInternalAttribute = false;
172 /* should have been filtered */
173 GSSEAP_ASSERT(!isSecretAttributeP(attrid));
175 switch (attrid.first) {
176 case VENDORPEC_UKERNA:
177 switch (attrid.second) {
178 case PW_SAML_AAA_ASSERTION:
179 bInternalAttribute = true;
186 switch (attrid.second) {
187 case PW_GSS_ACCEPTOR_SERVICE_NAME:
188 case PW_GSS_ACCEPTOR_HOST_NAME:
189 case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
190 case PW_GSS_ACCEPTOR_REALM_NAME:
191 bInternalAttribute = true;
201 return bInternalAttribute;
205 isInternalAttributeP(rs_const_avp *vp)
207 return isInternalAttributeP(avpToAttrId(vp));
211 isFragmentedAttributeP(const gss_eap_attrid &attrid)
213 /* A bit of a hack for the PAC for now. Should be configurable. */
214 return (attrid.first == VENDORPEC_UKERNA) &&
215 !isInternalAttributeP(attrid);
219 * Copy AVP list, same as paircopy except it filters out attributes
223 copyAvps(rs_const_avp *src)
228 for (vp = src; vp != NULL; vp = rs_avp_next_const(vp)) {
231 if (isSecretAttributeP(vp))
234 vpcopy = rs_avp_dup(vp);
235 if (vpcopy == NULL) {
237 throw std::bad_alloc();
240 rs_avp_append(&dst, vpcopy);
247 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
251 std::vector <gss_eap_attrid> seen;
253 for (vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
254 gss_buffer_desc desc;
255 gss_eap_attrid attrid;
258 /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
259 if (isInternalAttributeP(vp))
262 rs_avp_attrid(vp, &attrid.second, &attrid.first);
264 if (alreadyAddedAttributeP(seen, attrid))
267 if (rs_attr_display_name(attrid.second, attrid.first,
268 buf, sizeof(buf), TRUE) != RSE_OK ||
269 strncmp(buf, "Attr-", 5) != 0)
272 desc.value = &buf[5];
273 desc.length = strlen((char *)desc.value);
275 if (!addAttribute(m_manager, this, &desc, data))
278 seen.push_back(attrid);
285 getAttributeId(const gss_buffer_t desc,
286 gss_eap_attrid *attrid)
291 if (desc->length == 0)
294 canon = isdigit(*(char *)desc->value);
296 /* need to duplicate because attr may not be NUL terminated */
297 strAttr = (char *)GSSEAP_MALLOC((canon ? 5 : 0) + desc->length + 1);
299 throw new std::bad_alloc();
304 memcpy(s, "Attr-", 5);
308 memcpy(s, desc->value, desc->length);
312 code = rs_attr_parse_name(strAttr, &attrid->second, &attrid->first);
314 GSSEAP_FREE(strAttr);
316 return (code == RSE_OK);
320 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
321 const gss_eap_attrid &attrid,
322 const gss_buffer_t value)
324 OM_uint32 major = GSS_S_UNAVAILABLE, minor;
326 if (!isSecretAttributeP(attrid) &&
327 !isInternalAttributeP(attrid)) {
328 deleteAttribute(attrid);
330 major = gssEapRadiusAddAvp(&minor, &m_vps, attrid, value);
333 return !GSS_ERROR(major);
337 gss_eap_radius_attr_provider::setAttribute(int complete,
338 const gss_buffer_t attr,
339 const gss_buffer_t value)
341 gss_eap_attrid attrid;
343 if (!getAttributeId(attr, &attrid))
346 return setAttribute(complete, attrid, value);
350 gss_eap_radius_attr_provider::deleteAttribute(const gss_eap_attrid &attrid)
352 if (isSecretAttributeP(attrid) ||
353 isInternalAttributeP(attrid) ||
354 rs_avp_find(m_vps, attrid.second, attrid.first) == NULL)
357 return (rs_avp_delete(&m_vps, attrid.second, attrid.first) == RSE_OK);
361 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
363 gss_eap_attrid attrid;
365 if (!getAttributeId(attr, &attrid))
368 return deleteAttribute(attrid);
372 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
376 gss_buffer_t display_value,
379 gss_eap_attrid attrid;
381 if (!getAttributeId(attr, &attrid))
384 return getAttribute(attrid,
385 authenticated, complete,
386 value, display_value, more);
390 gss_eap_radius_attr_provider::getAttribute(const gss_eap_attrid &attrid,
394 gss_buffer_t display_value,
398 int i = *more, count = 0;
405 if (isSecretAttributeP(attrid) ||
406 isInternalAttributeP(attrid)) {
408 } else if (isFragmentedAttributeP(attrid)) {
409 return getFragmentedAttribute(attrid,
415 for (vp = rs_avp_find_const(m_vps, attrid.second, attrid.first);
417 vp = rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first)) {
419 if (rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first) != NULL)
425 if (vp == NULL && *more == 0)
428 if (value != GSS_C_NO_BUFFER) {
429 gss_buffer_desc valueBuf;
431 rs_avp_octets_value_byref((rs_avp *)vp,
432 (unsigned char **)&valueBuf.value,
435 duplicateBuffer(valueBuf, value);
438 if (display_value != GSS_C_NO_BUFFER &&
439 !rs_avp_is_octets(vp)) {
440 char displayString[RS_MAX_STRING_LEN];
441 gss_buffer_desc displayBuf;
443 displayBuf.length = rs_avp_display_value(vp, displayString,
444 sizeof(displayString));
445 displayBuf.value = (void *)displayString;
447 duplicateBuffer(displayBuf, display_value);
450 if (authenticated != NULL)
451 *authenticated = m_authenticated;
452 if (complete != NULL)
459 gss_eap_radius_attr_provider::getFragmentedAttribute(const gss_eap_attrid &attrid,
462 gss_buffer_t value) const
464 OM_uint32 major, minor;
466 major = gssEapRadiusGetAvp(&minor, m_vps, attrid, value, TRUE);
468 if (authenticated != NULL)
469 *authenticated = m_authenticated;
470 if (complete != NULL)
473 return !GSS_ERROR(major);
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 rs_avp *vp = (rs_avp *)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,
517 const gss_eap_attrid &attrid,
518 const gss_buffer_t buffer)
520 unsigned char *p = (unsigned char *)buffer->value;
521 size_t remain = buffer->length;
528 * There's an extra byte of padding; RADIUS AVPs can only
531 if (n >= RS_MAX_STRING_LEN)
532 n = RS_MAX_STRING_LEN - 1;
534 vp = rs_avp_alloc(attrid.second, attrid.first);
537 return GSS_S_FAILURE;
540 rs_avp_octets_set(vp, p, n);
542 rs_avp_append(vps, vp);
546 } while (remain != 0);
548 return GSS_S_COMPLETE;
552 gssEapRadiusAddAvp(OM_uint32 *minor,
553 struct rs_packet *pkt,
554 unsigned int attribute,
556 const gss_buffer_t buffer)
558 gss_eap_attrid attrid(vendor, attribute);
561 code = rs_packet_append_avp(pkt, attrid.second, attrid.first,
562 buffer->value, buffer->length);
563 if (code != RSE_OK) {
564 *minor = RS_MAP_ERROR(code);
565 return GSS_S_FAILURE;
569 return GSS_S_COMPLETE;
573 gssEapRadiusGetRawAvp(OM_uint32 *minor,
575 unsigned int attribute,
579 *vp = rs_avp_find_const(vps, attribute, vendor);
581 *minor = GSSEAP_NO_SUCH_ATTR;
582 return GSS_S_UNAVAILABLE;
585 return GSS_S_COMPLETE;
589 gssEapRadiusGetAvp(OM_uint32 *minor,
591 const gss_eap_attrid &attrid,
598 if (buffer != GSS_C_NO_BUFFER) {
600 buffer->value = NULL;
603 vp = rs_avp_find_const(vps, attrid.second, attrid.first);
605 *minor = GSSEAP_NO_SUCH_ATTR;
606 return GSS_S_UNAVAILABLE;
609 if (buffer != GSS_C_NO_BUFFER) {
611 rs_avp_fragmented_value(vp, NULL, &buffer->length);
613 buffer->length = rs_avp_length(vp);
615 buffer->value = GSSEAP_MALLOC(buffer->length);
616 if (buffer->value == NULL) {
618 return GSS_S_FAILURE;
622 err = rs_avp_fragmented_value(vp, (unsigned char *)buffer->value, &buffer->length);
624 err = rs_avp_octets_value(vp, (unsigned char *)buffer->value, &buffer->length);
627 *minor = RS_MAP_ERROR(err);
628 return GSS_S_FAILURE;
633 return GSS_S_COMPLETE;
637 gssEapRadiusGetAvp(OM_uint32 *minor,
638 struct rs_packet *pkt,
639 unsigned int attribute,
645 gss_eap_attrid attrid(vendor, attribute);
647 rs_packet_avps(pkt, &vps);
649 return gssEapRadiusGetAvp(minor, *vps, attrid, buffer, concat);
653 gssEapRadiusFreeAvps(OM_uint32 *minor,
658 return GSS_S_COMPLETE;
662 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
664 if (!gss_eap_radius_attr_provider::init()) {
665 *minor = GSSEAP_RADSEC_INIT_FAILURE;
666 return GSS_S_FAILURE;
669 return GSS_S_COMPLETE;
673 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
675 gss_eap_radius_attr_provider::finalize();
678 return GSS_S_COMPLETE;
682 avpToJson(rs_const_avp *vp)
685 gss_eap_attrid attrid;
687 GSSEAP_ASSERT(rs_avp_length(vp) <= RS_MAX_STRING_LEN);
689 switch (rs_avp_typeof(vp)) {
690 case RS_TYPE_INTEGER:
691 obj.set("value", rs_avp_integer_value(vp));
694 obj.set("value", rs_avp_date_value(vp));
697 obj.set("value", rs_avp_string_value(vp));
702 if (base64Encode(rs_avp_octets_value_const_ptr(vp),
703 rs_avp_length(vp), &b64) < 0)
704 throw std::bad_alloc();
706 obj.set("value", b64);
712 attrid = avpToAttrId(vp);
714 obj.set("type", attrid.second);
715 if (attrid.first != 0)
716 obj.set("vendor", attrid.first);
722 jsonToAvp(rs_avp **pVp, JSONObject &obj)
725 gss_eap_attrid attrid;
727 JSONObject type = obj["type"];
728 JSONObject vendor = obj["vendor"];
729 JSONObject value = obj["value"];
731 if (!type.isInteger())
733 attrid.second = type.integer();
735 if (!vendor.isNull()) {
736 if (!vendor.isInteger())
738 attrid.first = vendor.integer();
743 vp = rs_avp_alloc(attrid.second, attrid.first);
745 throw std::bad_alloc();
747 switch (rs_avp_typeof(vp)) {
748 case RS_TYPE_INTEGER:
751 if (!value.isInteger())
754 if (rs_avp_integer_set(vp, value.integer()) != RSE_OK)
758 case RS_TYPE_STRING: {
759 if (!value.isString())
762 if (rs_avp_string_set(vp, value.string()) != RSE_OK)
769 unsigned char buf[RS_MAX_STRING_LEN];
771 if (!value.isString())
774 const char *str = value.string();
775 ssize_t len = strlen(str);
777 /* this optimization requires base64Decode only understand packed encoding */
778 if (len >= BASE64_EXPAND(RS_MAX_STRING_LEN))
781 len = base64Decode(str, buf);
785 if (rs_avp_octets_set(vp, buf, len) != RSE_OK)
804 gss_eap_radius_attr_provider::name(void) const
810 gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
813 if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
816 JSONObject attrs = obj["attributes"];
817 size_t nelems = attrs.size();
819 for (size_t i = 0; i < nelems; i++) {
820 JSONObject attr = attrs[i];
823 if (!jsonToAvp(&vp, attr))
826 rs_avp_append(&m_vps, vp);
829 m_authenticated = obj["authenticated"].integer() ? true : false;
835 gss_eap_radius_attr_provider::prefix(void) const
837 return "urn:ietf:params:gss:radius-attribute";
841 gss_eap_radius_attr_provider::jsonRepresentation(void) const
843 JSONObject obj, attrs = JSONObject::array();
845 for (rs_avp *vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
846 JSONObject attr = avpToJson(vp);
850 obj.set("attributes", attrs);
852 obj.set("authenticated", m_authenticated);
858 gss_eap_radius_attr_provider::getExpiryTime(void) const
863 vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
867 value = rs_avp_integer_value(vp);
871 return time(NULL) + value;
875 gssEapRadiusMapError(OM_uint32 *minor,
876 struct rs_error *err)
880 GSSEAP_ASSERT(err != NULL);
882 code = rs_err_code(err, 0);
884 if (code == RSE_OK) {
886 return GSS_S_COMPLETE;
889 *minor = RS_MAP_ERROR(code);
891 gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
894 return GSS_S_FAILURE;
898 gssEapCreateRadiusContext(OM_uint32 *minor,
900 struct rs_context **pRadContext)
902 const char *configFile = RS_CONFIG_FILE;
903 struct rs_context *radContext;
904 struct rs_alloc_scheme ralloc;
905 struct rs_error *err;
910 if (rs_context_create(&radContext) != 0) {
911 *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
912 return GSS_S_FAILURE;
915 if (cred->radiusConfigFile.value != NULL)
916 configFile = (const char *)cred->radiusConfigFile.value;
918 ralloc.calloc = GSSEAP_CALLOC;
919 ralloc.malloc = GSSEAP_MALLOC;
920 ralloc.free = GSSEAP_FREE;
921 ralloc.realloc = GSSEAP_REALLOC;
923 rs_context_set_alloc_scheme(radContext, &ralloc);
925 if (rs_context_read_config(radContext, configFile) != 0) {
926 err = rs_err_ctx_pop(radContext);
930 *pRadContext = radContext;
933 return GSS_S_COMPLETE;
936 major = gssEapRadiusMapError(minor, err);
937 rs_context_destroy(radContext);
942 #endif /* GSSEAP_ENABLE_ACCEPTOR */
945 gssEapRadiusAddAttr(OM_uint32 *minor, struct wpabuf **buf, uint16_t attr,
946 uint16_t vendor, gss_buffer_t buffer)
948 if (radius_add_tlv(buf, attr, vendor, (u8 *)buffer->value,
949 buffer->length) < 0) {
950 *minor = ENOMEM; /* could be length too long, though */
951 return GSS_S_FAILURE;
953 return GSS_S_COMPLETE;