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