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