remove debugging statement
[moonshot.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 #include <xercesc/util/Base64.hpp>
40
41 /* stuff that should be provided by libradsec/libfreeradius-radius */
42 #define VENDORATTR(vendor, attr)            (((vendor) << 16) | (attr))
43
44 #ifndef ATTRID
45 #define ATTRID(attr)                        ((attr) & 0xFFFF)
46 #endif
47
48 static gss_buffer_desc radiusUrnPrefix = {
49     sizeof("urn:x-radius:") - 1,
50     (void *)"urn:x-radius:"
51 };
52
53 static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
54
55 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
56 {
57     m_vps = NULL;
58     m_authenticated = false;
59 }
60
61 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
62 {
63     if (m_vps != NULL)
64         pairfree(&m_vps);
65 }
66
67 bool
68 gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager,
69                                                       const gss_eap_attr_provider *ctx)
70 {
71     const gss_eap_radius_attr_provider *radius;
72
73     if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx))
74         return false;
75
76     radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
77
78     if (radius->m_vps != NULL)
79         m_vps = copyAvps(const_cast<VALUE_PAIR *>(radius->getAvps()));
80
81     m_authenticated = radius->m_authenticated;
82
83     return true;
84 }
85
86 bool
87 gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager,
88                                                  const gss_cred_id_t gssCred,
89                                                  const gss_ctx_id_t gssCtx)
90 {
91     if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx))
92         return false;
93
94     if (gssCtx != GSS_C_NO_CONTEXT) {
95         if (gssCtx->acceptorCtx.vps != NULL) {
96             m_vps = copyAvps(gssCtx->acceptorCtx.vps);
97             if (m_vps == NULL)
98                 return false;
99
100             /* We assume libradsec validated this for us */
101             assert(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL);
102             m_authenticated = true;
103         }
104     }
105
106     return true;
107 }
108
109 static bool
110 alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
111 {
112     for (std::vector<std::string>::const_iterator a = attrs.begin();
113          a != attrs.end();
114          ++a) {
115         if (strcmp(vp->name, (*a).c_str()) == 0)
116             return true;
117     }
118
119     return false;
120 }
121
122 static bool
123 isSecretAttributeP(uint16_t attrid, uint16_t vendor)
124 {
125     bool bSecretAttribute = false;
126
127     switch (vendor) {
128     case VENDORPEC_MS:
129         switch (attrid) {
130         case PW_MS_MPPE_SEND_KEY:
131         case PW_MS_MPPE_RECV_KEY:
132             bSecretAttribute = true;
133             break;
134         default:
135             break;
136         }
137     default:
138         break;
139     }
140
141     return bSecretAttribute;
142 }
143
144 static bool
145 isSecretAttributeP(uint32_t attribute)
146 {
147     return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute));
148 }
149
150 static bool
151 isInternalAttributeP(uint16_t attrid, uint16_t vendor)
152 {
153     bool bInternalAttribute = false;
154
155     /* should have been filtered */
156     assert(!isSecretAttributeP(attrid, vendor));
157
158     switch (vendor) {
159     case VENDORPEC_UKERNA:
160         bInternalAttribute = true;
161         break;
162     default:
163         break;
164     }
165
166     return bInternalAttribute;
167 }
168
169 static bool
170 isInternalAttributeP(uint32_t attribute)
171 {
172     return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute));
173 }
174
175 /*
176  * Copy AVP list, same as paircopy except it filters out attributes
177  * containing keys.
178  */
179 static VALUE_PAIR *
180 copyAvps(const VALUE_PAIR *src)
181 {
182     const VALUE_PAIR *vp;
183     VALUE_PAIR *dst = NULL, **pDst = &dst;
184
185     for (vp = src; vp != NULL; vp = vp->next) {
186         VALUE_PAIR *vpcopy;
187
188         if (isSecretAttributeP(vp->attribute))
189             continue;
190
191         vpcopy = paircopyvp(vp);
192         if (vpcopy == NULL) {
193             pairfree(&dst);
194             throw new std::bad_alloc;
195             return NULL;
196         }
197         *pDst = vpcopy;
198         pDst = &vpcopy->next;
199      }
200
201     return dst;
202 }
203
204 bool
205 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
206                                                 void *data) const
207 {
208     VALUE_PAIR *vp;
209     std::vector <std::string> seen;
210
211     for (vp = m_vps; vp != NULL; vp = vp->next) {
212         gss_buffer_desc attribute;
213         char attrid[64];
214
215         /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
216         if (isInternalAttributeP(vp->attribute))
217             continue;
218
219         if (alreadyAddedAttributeP(seen, vp))
220             continue;
221
222         snprintf(attrid, sizeof(attrid), "%s%d",
223             (char *)radiusUrnPrefix.value, vp->attribute);
224
225         attribute.value = attrid;
226         attribute.length = strlen(attrid);
227
228         if (!addAttribute(this, &attribute, data))
229             return false;
230
231         seen.push_back(std::string(vp->name));
232     }
233
234     return true;
235 }
236
237 uint32_t
238 getAttributeId(const gss_buffer_t attr)
239 {
240     OM_uint32 tmpMinor;
241     gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
242     DICT_ATTR *da;
243     char *s;
244     uint32_t attrid = 0;
245
246     if (attr->length < radiusUrnPrefix.length ||
247         memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
248         return 0;
249
250     /* need to duplicate because attr may not be NUL terminated */
251     duplicateBuffer(*attr, &strAttr);
252     s = (char *)strAttr.value + radiusUrnPrefix.length;
253
254     if (isdigit(*s)) {
255         attrid = strtoul(s, NULL, 10);
256     } else {
257         da = dict_attrbyname(s);
258         if (da != NULL)
259             attrid = da->attr;
260     }
261
262     gss_release_buffer(&tmpMinor, &strAttr);
263
264     return attrid;
265 }
266
267 bool
268 gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
269                                            uint32_t attrid,
270                                            const gss_buffer_t value)
271 {
272     OM_uint32 major = GSS_S_UNAVAILABLE, minor;
273
274     if (!isSecretAttributeP(attrid) &&
275         !isInternalAttributeP(attrid)) {
276         deleteAttribute(attrid);
277
278         major = gssEapRadiusAddAvp(&minor, &m_vps,
279                                    ATTRID(attrid), VENDOR(attrid), 
280                                    value);
281     }
282
283     return !GSS_ERROR(major);
284 }
285
286 bool
287 gss_eap_radius_attr_provider::setAttribute(int complete,
288                                            const gss_buffer_t attr,
289                                            const gss_buffer_t value)
290 {
291     uint32_t attrid = getAttributeId(attr);
292
293     if (!attrid)
294         return false;
295
296     return setAttribute(complete, attrid, value);
297 }
298
299 bool
300 gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid)
301 {
302     if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) ||
303         pairfind(m_vps, attrid) == NULL)
304         return false;
305
306     pairdelete(&m_vps, attrid);
307
308     return true;
309 }
310
311 bool
312 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
313 {
314     uint32_t attrid = getAttributeId(attr);
315
316     if (!attrid)
317         return false;
318
319     return deleteAttribute(attrid);
320 }
321
322 bool
323 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
324                                            int *authenticated,
325                                            int *complete,
326                                            gss_buffer_t value,
327                                            gss_buffer_t display_value,
328                                            int *more) const
329 {
330     uint32_t attrid;
331
332     attrid = getAttributeId(attr);
333     if (!attrid)
334         return false;
335
336     return getAttribute(attrid, authenticated, complete,
337                         value, display_value, more);
338 }
339
340 bool
341 gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
342                                            int *authenticated,
343                                            int *complete,
344                                            gss_buffer_t value,
345                                            gss_buffer_t display_value,
346                                            int *more) const
347 {
348     VALUE_PAIR *vp;
349     int i = *more, count = 0;
350
351     *more = 0;
352
353     if (i == -1)
354         i = 0;
355
356     for (vp = pairfind(m_vps, attrid);
357          vp != NULL;
358          vp = pairfind(vp->next, attrid)) {
359         if (count++ == i) {
360             if (pairfind(vp->next, attrid) != NULL)
361                 *more = count;
362             break;
363         }
364     }
365
366     if (vp == NULL && *more == 0)
367         return false;
368
369     if (value != GSS_C_NO_BUFFER) {
370         gss_buffer_desc valueBuf;
371
372         valueBuf.value = (void *)vp->vp_octets;
373         valueBuf.length = vp->length;
374
375         duplicateBuffer(valueBuf, value);
376     }
377
378     if (display_value != GSS_C_NO_BUFFER) {
379         char displayString[MAX_STRING_LEN];
380         gss_buffer_desc displayBuf;
381
382         displayBuf.length = vp_prints_value(displayString,
383                                             sizeof(displayString), vp, 0);
384         displayBuf.value = (void *)displayString;
385
386         duplicateBuffer(displayBuf, display_value);
387     }
388
389     if (authenticated != NULL)
390         *authenticated = m_authenticated;
391     if (complete != NULL)
392         *complete = true;
393
394     return true;
395 }
396
397 bool
398 gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
399                                                      uint16_t vendor,
400                                                      int *authenticated,
401                                                      int *complete,
402                                                      gss_buffer_t value) const
403 {
404     OM_uint32 major, minor;
405
406     major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE);
407
408     if (authenticated != NULL)
409         *authenticated = m_authenticated;
410     if (complete != NULL)
411         *complete = true;
412
413     return !GSS_ERROR(major);
414 }
415
416 bool
417 gss_eap_radius_attr_provider::getAttribute(uint16_t attribute,
418                                            uint16_t vendor,
419                                            int *authenticated,
420                                            int *complete,
421                                            gss_buffer_t value,
422                                            gss_buffer_t display_value,
423                                            int *more) const
424 {
425
426     return getAttribute(VENDORATTR(attribute, vendor),
427                         authenticated, complete,
428                         value, display_value, more);
429 }
430
431 gss_any_t
432 gss_eap_radius_attr_provider::mapToAny(int authenticated,
433                                        gss_buffer_t type_id GSSEAP_UNUSED) const
434 {
435     if (authenticated && !m_authenticated)
436         return (gss_any_t)NULL;
437
438     return (gss_any_t)copyAvps(m_vps);
439 }
440
441 void
442 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
443                                                     gss_any_t input) const
444 {
445     VALUE_PAIR *vp = (VALUE_PAIR *)input;
446     pairfree(&vp);
447 }
448
449 bool
450 gss_eap_radius_attr_provider::init(void)
451 {
452     struct rs_context *radContext;
453
454     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
455                                        "urn:ietf:params:gss-eap:radius-avp",
456                                        createAttrContext);
457
458 #if 1
459     /*
460      * This hack is necessary in order to force the loading of the global
461      * dictionary, otherwise accepting reauthentication tokens fails unless
462      * the acceptor has already accepted a normal authentication token.
463      */
464     if (rs_context_create(&radContext) != 0)
465         return false;
466
467     if (rs_context_read_config(radContext, RS_CONFIG_FILE) != 0) {
468         rs_context_destroy(radContext);
469         return false;
470     }
471
472     if (rs_context_init_freeradius_dict(radContext, NULL)) {
473         rs_context_destroy(radContext);
474         return false;
475     }
476
477     rs_context_destroy(radContext);
478 #endif
479
480     return true;
481 }
482
483 void
484 gss_eap_radius_attr_provider::finalize(void)
485 {
486     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
487 }
488
489 gss_eap_attr_provider *
490 gss_eap_radius_attr_provider::createAttrContext(void)
491 {
492     return new gss_eap_radius_attr_provider;
493 }
494
495 OM_uint32
496 gssEapRadiusAddAvp(OM_uint32 *minor,
497                    VALUE_PAIR **vps,
498                    uint16_t attribute,
499                    uint16_t vendor,
500                    const gss_buffer_t buffer)
501 {
502     uint32_t attrid = VENDORATTR(vendor, attribute);
503     unsigned char *p = (unsigned char *)buffer->value;
504     size_t remain = buffer->length;
505
506     do {
507         VALUE_PAIR *vp;
508         size_t n = remain;
509
510         /*
511          * There's an extra byte of padding; RADIUS AVPs can only
512          * be 253 octets.
513          */
514         if (n >= MAX_STRING_LEN)
515             n = MAX_STRING_LEN - 1;
516
517         vp = paircreate(attrid, PW_TYPE_OCTETS);
518         if (vp == NULL) {
519             *minor = ENOMEM;
520             return GSS_S_FAILURE;
521         }
522
523         memcpy(vp->vp_octets, p, n);
524         vp->length = n;
525
526         pairadd(vps, vp);
527
528         p += n;
529         remain -= n;
530     } while (remain != 0);
531
532     return GSS_S_COMPLETE;
533 }
534
535 OM_uint32
536 gssEapRadiusGetRawAvp(OM_uint32 *minor,
537                       VALUE_PAIR *vps,
538                       uint16_t attribute,
539                       uint16_t vendor,
540                       VALUE_PAIR **vp)
541 {
542     uint32_t attr = VENDORATTR(vendor, attribute);
543
544     *vp = pairfind(vps, attr);
545     if (*vp == NULL) {
546         *minor = GSSEAP_NO_SUCH_ATTR;
547         return GSS_S_UNAVAILABLE;
548     }
549
550     return GSS_S_COMPLETE;
551 }
552
553 OM_uint32
554 gssEapRadiusGetAvp(OM_uint32 *minor,
555                    VALUE_PAIR *vps,
556                    uint16_t attribute,
557                    uint16_t vendor,
558                    gss_buffer_t buffer,
559                    int concat)
560 {
561     VALUE_PAIR *vp;
562     unsigned char *p;
563     uint32_t attr = VENDORATTR(vendor, attribute);
564
565     buffer->length = 0;
566     buffer->value = NULL;
567
568     vp = pairfind(vps, attr);
569     if (vp == NULL) {
570         *minor = GSSEAP_NO_SUCH_ATTR;
571         return GSS_S_UNAVAILABLE;
572     }
573
574     do {
575         buffer->length += vp->length;
576     } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
577
578     buffer->value = GSSEAP_MALLOC(buffer->length);
579     if (buffer->value == NULL) {
580         *minor = ENOMEM;
581         return GSS_S_FAILURE;
582     }
583
584     p = (unsigned char *)buffer->value;
585
586     for (vp = pairfind(vps, attr);
587          concat && vp != NULL;
588          vp = pairfind(vp->next, attr)) {
589         memcpy(p, vp->vp_octets, vp->length);
590         p += vp->length;
591     }
592
593     *minor = 0;
594     return GSS_S_COMPLETE;
595 }
596
597 OM_uint32
598 gssEapRadiusFreeAvps(OM_uint32 *minor,
599                      VALUE_PAIR **vps)
600 {
601     pairfree(vps);
602     *minor = 0;
603     return GSS_S_COMPLETE;
604 }
605
606 OM_uint32
607 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
608 {
609     if (!gss_eap_radius_attr_provider::init()) {
610         *minor = GSSEAP_RADSEC_INIT_FAILURE;
611         return GSS_S_FAILURE;
612     }
613
614     return GSS_S_COMPLETE;
615 }
616
617 OM_uint32
618 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
619 {
620     gss_eap_radius_attr_provider::finalize();
621
622     *minor = 0;
623     return GSS_S_COMPLETE;
624 }
625
626 static DDF
627 avpMarshall(const VALUE_PAIR *vp)
628 {
629     DDF obj(NULL);
630
631     obj.addmember("type").integer(vp->attribute);
632
633     assert(vp->length <= MAX_STRING_LEN);
634
635     switch (vp->type) {
636     case PW_TYPE_INTEGER:
637     case PW_TYPE_IPADDR:
638     case PW_TYPE_DATE:
639         obj.addmember("value").integer(vp->lvalue);
640         break;
641     case PW_TYPE_STRING:
642         obj.addmember("value").string(vp->vp_strvalue);
643         break;
644     default: {
645         XMLSize_t len;
646         XMLByte *b64 = xercesc::Base64::encode(vp->vp_octets, vp->length, &len);
647
648         if (b64[len - 1] == '\n')
649             b64[--len] = '\0'; /* XXX there may be embedded newlines */
650
651         obj.addmember("value").string((char *)b64);
652         delete b64;
653         break;
654     }
655     }
656
657     return obj;
658 }
659
660 static bool
661 avpUnmarshall(VALUE_PAIR **pVp, DDF &obj)
662 {
663     VALUE_PAIR *vp = NULL;
664     DICT_ATTR *da;
665     uint32_t attrid;
666
667     attrid = obj["type"].integer();
668
669     da = dict_attrbyvalue(attrid);
670     if (da != NULL) {
671         vp = pairalloc(da);
672     } else {
673         vp = paircreate(attrid, PW_TYPE_STRING);
674     }
675     if (vp == NULL) {
676         throw new std::bad_alloc;
677         goto fail;
678     }
679
680     switch (vp->type) {
681     case PW_TYPE_INTEGER:
682     case PW_TYPE_IPADDR:
683     case PW_TYPE_DATE:
684         vp->length = 4;
685         vp->lvalue = obj["value"].integer();;
686         break;
687     case PW_TYPE_STRING: {
688         const char *str = obj["value"].string();
689         size_t len = strlen(str);
690         if (str == NULL || len >= MAX_STRING_LEN)
691             goto fail;
692
693         vp->length = len;
694         memcpy(vp->vp_strvalue, str, len + 1);
695         break;
696     }
697     case PW_TYPE_OCTETS:
698     default: {
699         XMLSize_t len;
700         const XMLByte *b64 = (const XMLByte *)obj["value"].string();
701         XMLByte *data = xercesc::Base64::decode(b64, &len);
702         if (data == NULL || len >= MAX_STRING_LEN) {
703             delete data;
704             goto fail;
705         }
706
707         vp->length = len;
708         memcpy(vp->vp_octets, data, len);
709         vp->vp_octets[len] = '\0';
710         delete data;
711         break;
712     }
713     }
714
715     *pVp = vp;
716
717     return true;
718
719 fail:
720     if (vp != NULL)
721         pairbasicfree(vp);
722     *pVp = NULL;
723     return false;
724 }
725
726 const char *
727 gss_eap_radius_attr_provider::marshallingKey(void) const
728 {
729     return "radius";
730 }
731
732 bool
733 gss_eap_radius_attr_provider::unmarshallAndInit(const gss_eap_attr_ctx *ctx,
734                                                 DDF &obj)
735 {
736     VALUE_PAIR **pNext = &m_vps;
737
738     if (!gss_eap_attr_provider::unmarshallAndInit(ctx, obj))
739         return false;
740
741     DDF attrs = obj["attributes"];
742     DDF attr = attrs.first();
743
744     while (!attr.isnull()) {
745         VALUE_PAIR *vp;
746
747         if (!avpUnmarshall(&vp, attr))
748             return false;
749
750         *pNext = vp;
751         pNext = &vp->next;
752
753         attr = attrs.next();
754     }
755
756     return true;
757 }
758
759 DDF
760 gss_eap_radius_attr_provider::marshall(void) const
761 {
762     DDF obj(NULL);
763     DDF attrs = obj.structure().addmember("attributes").list();
764
765     for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) {
766         DDF attr = avpMarshall(vp);
767         attrs.add(attr);
768     }
769
770     return obj;
771 }
772
773 time_t
774 gss_eap_radius_attr_provider::getExpiryTime(void) const
775 {
776     VALUE_PAIR *vp;
777
778     vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
779     if (vp == NULL || vp->lvalue == 0)
780         return 0;
781
782     return time(NULL) + vp->lvalue;
783 }
784
785 OM_uint32
786 gssEapRadiusMapError(OM_uint32 *minor,
787                      struct rs_error *err)
788 {
789     int code;
790
791     assert(err != NULL);
792
793     code = rs_err_code(err, 0);
794
795     if (code == RSE_OK) {
796         *minor = 0;
797         return GSS_S_COMPLETE;
798     }
799
800     *minor = ERROR_TABLE_BASE_rse + code;
801
802     gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
803     rs_err_free(err);
804
805     return GSS_S_FAILURE;
806 }