Update to use IETF RADIUS attributes
[mech_eap.git] / mech_eap / util_radius.cpp
1 /*
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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
30  * SUCH DAMAGE.
31  */
32
33 /*
34  * RADIUS attribute provider implementation.
35  */
36
37 #include "gssapiP_eap.h"
38
39 #define RS_MAP_ERROR(code)  (ERROR_TABLE_BASE_rse + (code))
40
41 static rs_avp *copyAvps(rs_const_avp *src);
42
43 static OM_uint32
44 gssEapRadiusGetAvp(OM_uint32 *minor,
45                    rs_avp *vps,
46                    const gss_eap_attrid &attrid,
47                    gss_buffer_t buffer,
48                    int concat);
49
50 static OM_uint32
51 gssEapRadiusAddAvp(OM_uint32 *minor,
52                    rs_avp **vps,
53                    const gss_eap_attrid &attrid,
54                    const gss_buffer_t buffer);
55
56 static gss_eap_attrid
57 avpToAttrId(rs_const_avp *vp)
58 {
59     gss_eap_attrid attrid;
60
61     rs_avp_attrid(vp, &attrid.second, &attrid.first);
62
63     return attrid;
64 }
65
66 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
67 {
68     m_vps = NULL;
69     m_authenticated = false;
70 }
71
72 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
73 {
74     if (m_vps != NULL)
75         rs_avp_free(&m_vps);
76 }
77
78 bool
79 gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
80                                                       const gss_eap_attr_provider *ctx)
81 {
82     const gss_eap_radius_attr_provider *radius;
83
84     if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
85         return false;
86
87     radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
88
89     if (radius->m_vps != NULL)
90         m_vps = copyAvps(radius->getAvps());
91
92     m_authenticated = radius->m_authenticated;
93
94     return true;
95 }
96
97 bool
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)
101 {
102     if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
103         return false;
104
105     if (gssCtx != GSS_C_NO_CONTEXT) {
106         if (gssCtx->acceptorCtx.vps != NULL) {
107             m_vps = copyAvps(gssCtx->acceptorCtx.vps);
108             if (m_vps == NULL)
109                 return false;
110
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;
114         }
115     }
116
117     return true;
118 }
119
120 static bool
121 alreadyAddedAttributeP(std::vector <gss_eap_attrid> &attrs,
122                        gss_eap_attrid &attrid)
123 {
124     for (std::vector<gss_eap_attrid>::const_iterator a = attrs.begin();
125          a != attrs.end();
126          ++a) {
127         if (attrid.first == (*a).first &&
128             attrid.second == (*a).second)
129             return true;
130     }
131
132     return false;
133 }
134
135 static bool
136 isSecretAttributeP(const gss_eap_attrid &attrid)
137 {
138     bool bSecretAttribute = false;
139
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;
146             break;
147         default:
148             break;
149         }
150     default:
151         break;
152     }
153
154     return bSecretAttribute;
155 }
156
157 static bool
158 isSecretAttributeP(rs_const_avp *vp)
159 {
160     return isSecretAttributeP(avpToAttrId(vp));
161 }
162
163 static bool
164 isInternalAttributeP(const gss_eap_attrid &attrid)
165 {
166     bool bInternalAttribute = false;
167
168     /* should have been filtered */
169     GSSEAP_ASSERT(!isSecretAttributeP(attrid));
170
171     switch (attrid.first) {
172     case VENDORPEC_UKERNA:
173         switch (attrid.second) {
174         case PW_SAML_AAA_ASSERTION:
175             bInternalAttribute = true;
176             break;
177         default:
178             break;
179         }
180         break;
181     case 0:
182         switch (attrid.second) {
183                     case PW_GSS_ACCEPTOR_SERVICE_NAME:
184         case PW_GSS_ACCEPTOR_HOST_NAME:
185         case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
186         case PW_GSS_ACCEPTOR_REALM_NAME:
187             bInternalAttribute = true;
188             break;
189         default:
190             break;
191         }
192         break;
193     default:
194         break;
195     }
196
197     return bInternalAttribute;
198 }
199
200 static bool
201 isInternalAttributeP(rs_const_avp *vp)
202 {
203     return isInternalAttributeP(avpToAttrId(vp));
204 }
205
206 static bool
207 isFragmentedAttributeP(const gss_eap_attrid &attrid)
208 {
209     /* A bit of a hack for the PAC for now. Should be configurable. */
210     return (attrid.first == VENDORPEC_UKERNA) &&
211         !isInternalAttributeP(attrid);
212 }
213
214 /*
215  * Copy AVP list, same as paircopy except it filters out attributes
216  * containing keys.
217  */
218 static rs_avp *
219 copyAvps(rs_const_avp *src)
220 {
221     rs_const_avp *vp;
222     rs_avp *dst = NULL;
223
224     for (vp = src; vp != NULL; vp = rs_avp_next_const(vp)) {
225         rs_avp *vpcopy;
226
227         if (isSecretAttributeP(vp))
228             continue;
229
230         vpcopy = rs_avp_dup(vp);
231         if (vpcopy == NULL) {
232             rs_avp_free(&dst);
233             throw std::bad_alloc();
234         }
235
236         rs_avp_append(&dst, vpcopy);
237      }
238
239     return dst;
240 }
241
242 bool
243 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
244                                                 void *data) const
245 {
246     rs_avp *vp;
247     std::vector <gss_eap_attrid> seen;
248
249     for (vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
250         gss_buffer_desc desc;
251         gss_eap_attrid attrid;
252         char buf[64];
253
254         /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
255         if (isInternalAttributeP(vp))
256             continue;
257
258         rs_avp_attrid(vp, &attrid.second, &attrid.first);
259
260         if (alreadyAddedAttributeP(seen, attrid))
261             continue;
262
263         if (rs_attr_display_name(attrid.second, attrid.first,
264                                  buf, sizeof(buf), TRUE) != RSE_OK ||
265             strncmp(buf, "Attr-", 5) != 0)
266             continue;
267
268         desc.value = &buf[5];
269         desc.length = strlen((char *)desc.value);
270
271         if (!addAttribute(m_manager, this, &desc, data))
272             return false;
273
274         seen.push_back(attrid);
275     }
276
277     return true;
278 }
279
280 static bool
281 getAttributeId(const gss_buffer_t desc,
282                gss_eap_attrid *attrid)
283 {
284     char *strAttr, *s;
285     int canon, code;
286
287     if (desc->length == 0)
288         return false;
289
290     canon = isdigit(*(char *)desc->value);
291
292     /* need to duplicate because attr may not be NUL terminated */
293     strAttr = (char *)GSSEAP_MALLOC((canon ? 5 : 0) + desc->length + 1);
294     if (strAttr == NULL)
295         throw new std::bad_alloc();
296
297     s = strAttr;
298
299     if (canon) {
300         memcpy(s, "Attr-", 5);
301         s += 5;
302     }
303
304     memcpy(s, desc->value, desc->length);
305     s += desc->length;
306     *s = '\0';
307
308     code = rs_attr_parse_name(strAttr, &attrid->second, &attrid->first);
309
310     GSSEAP_FREE(strAttr);
311
312     return (code == RSE_OK);
313 }
314
315 bool
316 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
317                                            const gss_eap_attrid &attrid,
318                                            const gss_buffer_t value)
319 {
320     OM_uint32 major = GSS_S_UNAVAILABLE, minor;
321
322     if (!isSecretAttributeP(attrid) &&
323         !isInternalAttributeP(attrid)) {
324         deleteAttribute(attrid);
325
326         major = gssEapRadiusAddAvp(&minor, &m_vps, attrid, value);
327     }
328
329     return !GSS_ERROR(major);
330 }
331
332 bool
333 gss_eap_radius_attr_provider::setAttribute(int complete,
334                                            const gss_buffer_t attr,
335                                            const gss_buffer_t value)
336 {
337     gss_eap_attrid attrid;
338
339     if (!getAttributeId(attr, &attrid))
340         return false;
341
342     return setAttribute(complete, attrid, value);
343 }
344
345 bool
346 gss_eap_radius_attr_provider::deleteAttribute(const gss_eap_attrid &attrid)
347 {
348     if (isSecretAttributeP(attrid) ||
349         isInternalAttributeP(attrid) ||
350         rs_avp_find(m_vps, attrid.second, attrid.first) == NULL)
351         return false;
352
353     return (rs_avp_delete(&m_vps, attrid.second, attrid.first) == RSE_OK);
354 }
355
356 bool
357 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
358 {
359     gss_eap_attrid attrid;
360
361     if (!getAttributeId(attr, &attrid))
362         return false;
363
364     return deleteAttribute(attrid);
365 }
366
367 bool
368 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
369                                            int *authenticated,
370                                            int *complete,
371                                            gss_buffer_t value,
372                                            gss_buffer_t display_value,
373                                            int *more) const
374 {
375     gss_eap_attrid attrid;
376
377     if (!getAttributeId(attr, &attrid))
378         return false;
379
380     return getAttribute(attrid,
381                         authenticated, complete,
382                         value, display_value, more);
383 }
384
385 bool
386 gss_eap_radius_attr_provider::getAttribute(const gss_eap_attrid &attrid,
387                                            int *authenticated,
388                                            int *complete,
389                                            gss_buffer_t value,
390                                            gss_buffer_t display_value,
391                                            int *more) const
392 {
393     rs_const_avp *vp;
394     int i = *more, count = 0;
395
396     *more = 0;
397
398     if (i == -1)
399         i = 0;
400
401     if (isSecretAttributeP(attrid) ||
402         isInternalAttributeP(attrid)) {
403         return false;
404     } else if (isFragmentedAttributeP(attrid)) {
405         return getFragmentedAttribute(attrid,
406                                       authenticated,
407                                       complete,
408                                       value);
409     }
410
411     for (vp = rs_avp_find_const(m_vps, attrid.second, attrid.first);
412          vp != NULL;
413          vp = rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first)) {
414         if (count++ == i) {
415             if (rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first) != NULL)
416                 *more = count;
417             break;
418         }
419     }
420
421     if (vp == NULL && *more == 0)
422         return false;
423
424     if (value != GSS_C_NO_BUFFER) {
425         gss_buffer_desc valueBuf;
426
427         rs_avp_octets_value_byref((rs_avp *)vp,
428                                   (unsigned char **)&valueBuf.value,
429                                   &valueBuf.length);
430
431         duplicateBuffer(valueBuf, value);
432     }
433
434     if (display_value != GSS_C_NO_BUFFER &&
435         !rs_avp_is_octets(vp)) {
436         char displayString[RS_MAX_STRING_LEN];
437         gss_buffer_desc displayBuf;
438
439         displayBuf.length = rs_avp_display_value(vp, displayString,
440                                                  sizeof(displayString));
441         displayBuf.value = (void *)displayString;
442
443         duplicateBuffer(displayBuf, display_value);
444     }
445
446     if (authenticated != NULL)
447         *authenticated = m_authenticated;
448     if (complete != NULL)
449         *complete = true;
450
451     return true;
452 }
453
454 bool
455 gss_eap_radius_attr_provider::getFragmentedAttribute(const gss_eap_attrid &attrid,
456                                                      int *authenticated,
457                                                      int *complete,
458                                                      gss_buffer_t value) const
459 {
460     OM_uint32 major, minor;
461
462     major = gssEapRadiusGetAvp(&minor, m_vps, attrid, value, TRUE);
463
464     if (authenticated != NULL)
465         *authenticated = m_authenticated;
466     if (complete != NULL)
467         *complete = true;
468
469     return !GSS_ERROR(major);
470 }
471
472 gss_any_t
473 gss_eap_radius_attr_provider::mapToAny(int authenticated,
474                                        gss_buffer_t type_id GSSEAP_UNUSED) const
475 {
476     if (authenticated && !m_authenticated)
477         return (gss_any_t)NULL;
478
479     return (gss_any_t)copyAvps(m_vps);
480 }
481
482 void
483 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
484                                                     gss_any_t input) const
485 {
486     rs_avp *vp = (rs_avp *)input;
487     rs_avp_free(&vp);
488 }
489
490 bool
491 gss_eap_radius_attr_provider::init(void)
492 {
493     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, createAttrContext);
494
495     return true;
496 }
497
498 void
499 gss_eap_radius_attr_provider::finalize(void)
500 {
501     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
502 }
503
504 gss_eap_attr_provider *
505 gss_eap_radius_attr_provider::createAttrContext(void)
506 {
507     return new gss_eap_radius_attr_provider;
508 }
509
510 static OM_uint32
511 gssEapRadiusAddAvp(OM_uint32 *minor,
512                    rs_avp **vps,
513                    const gss_eap_attrid &attrid,
514                    const gss_buffer_t buffer)
515 {
516     unsigned char *p = (unsigned char *)buffer->value;
517     size_t remain = buffer->length;
518
519     do {
520         rs_avp *vp;
521         size_t n = remain;
522
523         /*
524          * There's an extra byte of padding; RADIUS AVPs can only
525          * be 253 octets.
526          */
527         if (n >= RS_MAX_STRING_LEN)
528             n = RS_MAX_STRING_LEN - 1;
529
530         vp = rs_avp_alloc(attrid.second, attrid.first);
531         if (vp == NULL) {
532             *minor = ENOMEM;
533             return GSS_S_FAILURE;
534         }
535
536         rs_avp_octets_set(vp, p, n);
537
538         rs_avp_append(vps, vp);
539
540         p += n;
541         remain -= n;
542     } while (remain != 0);
543
544     return GSS_S_COMPLETE;
545 }
546
547 OM_uint32
548 gssEapRadiusAddAvp(OM_uint32 *minor,
549                    struct rs_packet *pkt,
550                    unsigned int attribute,
551                    unsigned int vendor,
552                    const gss_buffer_t buffer)
553 {
554     gss_eap_attrid attrid(vendor, attribute);
555     int code;
556
557     code = rs_packet_append_avp(pkt, attrid.second, attrid.first,
558                                 buffer->value, buffer->length);
559     if (code != RSE_OK) {
560         *minor = RS_MAP_ERROR(code);
561         return GSS_S_FAILURE;
562     }
563
564     *minor = 0;
565     return GSS_S_COMPLETE;
566 }
567
568 OM_uint32
569 gssEapRadiusGetRawAvp(OM_uint32 *minor,
570                       rs_const_avp *vps,
571                       unsigned int attribute,
572                       unsigned int vendor,
573                       rs_const_avp **vp)
574 {
575     *vp = rs_avp_find_const(vps, attribute, vendor);
576     if (*vp == NULL) {
577         *minor = GSSEAP_NO_SUCH_ATTR;
578         return GSS_S_UNAVAILABLE;
579     }
580
581     return GSS_S_COMPLETE;
582 }
583
584 static OM_uint32
585 gssEapRadiusGetAvp(OM_uint32 *minor,
586                    rs_avp *vps,
587                    const gss_eap_attrid &attrid,
588                    gss_buffer_t buffer,
589                    int concat)
590 {
591     rs_const_avp *vp;
592     int err;
593
594     if (buffer != GSS_C_NO_BUFFER) {
595         buffer->length = 0;
596         buffer->value = NULL;
597     }
598
599     vp = rs_avp_find_const(vps, attrid.second, attrid.first);
600     if (vp == NULL) {
601         *minor = GSSEAP_NO_SUCH_ATTR;
602         return GSS_S_UNAVAILABLE;
603     }
604
605     if (buffer != GSS_C_NO_BUFFER) {
606         if (concat)
607             rs_avp_fragmented_value(vp, NULL, &buffer->length);
608         else
609             buffer->length = rs_avp_length(vp);
610
611         buffer->value = GSSEAP_MALLOC(buffer->length);
612         if (buffer->value == NULL) {
613             *minor = ENOMEM;
614             return GSS_S_FAILURE;
615         }
616
617         if (concat)
618             err = rs_avp_fragmented_value(vp, (unsigned char *)buffer->value, &buffer->length);
619         else
620             err = rs_avp_octets_value(vp, (unsigned char *)buffer->value, &buffer->length);
621
622         if (err != 0) {
623             *minor = RS_MAP_ERROR(err);
624             return GSS_S_FAILURE;
625         }
626     }
627
628     *minor = 0;
629     return GSS_S_COMPLETE;
630 }
631
632 OM_uint32
633 gssEapRadiusGetAvp(OM_uint32 *minor,
634                    struct rs_packet *pkt,
635                    unsigned int attribute,
636                    unsigned int vendor,
637                    gss_buffer_t buffer,
638                    int concat)
639 {
640     rs_avp **vps;
641     gss_eap_attrid attrid(vendor, attribute);
642
643     rs_packet_avps(pkt, &vps);
644
645     return gssEapRadiusGetAvp(minor, *vps, attrid, buffer, concat);
646 }
647
648 OM_uint32
649 gssEapRadiusFreeAvps(OM_uint32 *minor,
650                      rs_avp **vps)
651 {
652     rs_avp_free(vps);
653     *minor = 0;
654     return GSS_S_COMPLETE;
655 }
656
657 OM_uint32
658 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
659 {
660     if (!gss_eap_radius_attr_provider::init()) {
661         *minor = GSSEAP_RADSEC_INIT_FAILURE;
662         return GSS_S_FAILURE;
663     }
664
665     return GSS_S_COMPLETE;
666 }
667
668 OM_uint32
669 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
670 {
671     gss_eap_radius_attr_provider::finalize();
672
673     *minor = 0;
674     return GSS_S_COMPLETE;
675 }
676
677 static JSONObject
678 avpToJson(rs_const_avp *vp)
679 {
680     JSONObject obj;
681     gss_eap_attrid attrid;
682
683     GSSEAP_ASSERT(rs_avp_length(vp) <= RS_MAX_STRING_LEN);
684
685     switch (rs_avp_typeof(vp)) {
686     case RS_TYPE_INTEGER:
687         obj.set("value", rs_avp_integer_value(vp));
688         break;
689     case RS_TYPE_DATE:
690         obj.set("value", rs_avp_date_value(vp));
691         break;
692     case RS_TYPE_STRING:
693         obj.set("value", rs_avp_string_value(vp));
694         break;
695     default: {
696         char *b64;
697
698         if (base64Encode(rs_avp_octets_value_const_ptr(vp),
699                          rs_avp_length(vp), &b64) < 0)
700             throw std::bad_alloc();
701
702         obj.set("value", b64);
703         GSSEAP_FREE(b64);
704         break;
705     }
706     }
707
708     attrid = avpToAttrId(vp);
709
710     obj.set("type", attrid.second);
711     if (attrid.first != 0)
712         obj.set("vendor", attrid.first);
713
714     return obj;
715 }
716
717 static bool
718 jsonToAvp(rs_avp **pVp, JSONObject &obj)
719 {
720     rs_avp *vp = NULL;
721     gss_eap_attrid attrid;
722
723     JSONObject type = obj["type"];
724     JSONObject vendor = obj["vendor"];
725     JSONObject value = obj["value"];
726
727     if (!type.isInteger())
728         goto fail;
729     attrid.second = type.integer();
730
731     if (!vendor.isNull()) {
732         if (!vendor.isInteger())
733             goto fail;
734         attrid.first = vendor.integer();
735     } else {
736         attrid.first = 0;
737     }
738
739     vp = rs_avp_alloc(attrid.second, attrid.first);
740     if (vp == NULL)
741         throw std::bad_alloc();
742
743     switch (rs_avp_typeof(vp)) {
744     case RS_TYPE_INTEGER:
745     case RS_TYPE_IPADDR:
746     case RS_TYPE_DATE:
747         if (!value.isInteger())
748             goto fail;
749
750         if (rs_avp_integer_set(vp, value.integer()) != RSE_OK)
751             goto fail;
752
753         break;
754     case RS_TYPE_STRING: {
755         if (!value.isString())
756             goto fail;
757
758         if (rs_avp_string_set(vp, value.string()) != RSE_OK)
759             goto fail;
760
761         break;
762     }
763     case RS_TYPE_OCTETS:
764     default: {
765         unsigned char buf[RS_MAX_STRING_LEN];
766
767         if (!value.isString())
768             goto fail;
769
770         const char *str = value.string();
771         ssize_t len = strlen(str);
772
773         /* this optimization requires base64Decode only understand packed encoding */
774         if (len >= BASE64_EXPAND(RS_MAX_STRING_LEN))
775             goto fail;
776
777         len = base64Decode(str, buf);
778         if (len < 0)
779             goto fail;
780
781         if (rs_avp_octets_set(vp, buf, len) != RSE_OK)
782             goto fail;
783
784         break;
785     }
786     }
787
788     *pVp = vp;
789
790     return true;
791
792 fail:
793     if (vp != NULL)
794         rs_avp_free(&vp);
795     *pVp = NULL;
796     return false;
797 }
798
799 const char *
800 gss_eap_radius_attr_provider::name(void) const
801 {
802     return "radius";
803 }
804
805 bool
806 gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
807                                                  JSONObject &obj)
808 {
809     if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
810         return false;
811
812     JSONObject attrs = obj["attributes"];
813     size_t nelems = attrs.size();
814
815     for (size_t i = 0; i < nelems; i++) {
816         JSONObject attr = attrs[i];
817         rs_avp *vp;
818
819         if (!jsonToAvp(&vp, attr))
820             return false;
821
822         rs_avp_append(&m_vps, vp);
823     }
824
825     m_authenticated = obj["authenticated"].integer() ? true : false;
826
827     return true;
828 }
829
830 const char *
831 gss_eap_radius_attr_provider::prefix(void) const
832 {
833     return "urn:ietf:params:gss:radius-attribute";
834 }
835
836 JSONObject
837 gss_eap_radius_attr_provider::jsonRepresentation(void) const
838 {
839     JSONObject obj, attrs = JSONObject::array();
840
841     for (rs_avp *vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
842         JSONObject attr = avpToJson(vp);
843         attrs.append(attr);
844     }
845
846     obj.set("attributes", attrs);
847
848     obj.set("authenticated", m_authenticated);
849
850     return obj;
851 }
852
853 time_t
854 gss_eap_radius_attr_provider::getExpiryTime(void) const
855 {
856     rs_const_avp *vp;
857     uint32_t value;
858
859     vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
860     if (vp == NULL)
861         return 0;
862
863     value = rs_avp_integer_value(vp);
864     if (value == 0)
865         return 0;
866
867     return time(NULL) + value;
868 }
869
870 OM_uint32
871 gssEapRadiusMapError(OM_uint32 *minor,
872                      struct rs_error *err)
873 {
874     int code;
875
876     GSSEAP_ASSERT(err != NULL);
877
878     code = rs_err_code(err, 0);
879
880     if (code == RSE_OK) {
881         *minor = 0;
882         return GSS_S_COMPLETE;
883     }
884
885     *minor = RS_MAP_ERROR(code);
886
887     gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
888     rs_err_free(err);
889
890     return GSS_S_FAILURE;
891 }
892
893 OM_uint32
894 gssEapCreateRadiusContext(OM_uint32 *minor,
895                           gss_cred_id_t cred,
896                           struct rs_context **pRadContext)
897 {
898     const char *configFile = RS_CONFIG_FILE;
899     struct rs_context *radContext;
900     struct rs_alloc_scheme ralloc;
901     struct rs_error *err;
902     OM_uint32 major;
903
904     *pRadContext = NULL;
905
906     if (rs_context_create(&radContext) != 0) {
907         *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
908         return GSS_S_FAILURE;
909     }
910
911     if (cred->radiusConfigFile.value != NULL)
912         configFile = (const char *)cred->radiusConfigFile.value;
913
914     ralloc.calloc  = GSSEAP_CALLOC;
915     ralloc.malloc  = GSSEAP_MALLOC;
916     ralloc.free    = GSSEAP_FREE;
917     ralloc.realloc = GSSEAP_REALLOC;
918
919     rs_context_set_alloc_scheme(radContext, &ralloc);
920
921     if (rs_context_read_config(radContext, configFile) != 0) {
922         err = rs_err_ctx_pop(radContext);
923         goto fail;
924     }
925
926     *pRadContext = radContext;
927
928     *minor = 0;
929     return GSS_S_COMPLETE;
930
931 fail:
932     major = gssEapRadiusMapError(minor, err);
933     rs_context_destroy(radContext);
934
935     return major;
936 }