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