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