a272759df24c0e2a9f5654c725a85098946ff74b
[mech_eap.orig] / 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(m_manager, 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, createAttrContext);
455
456 #if 1
457     /*
458      * This hack is necessary in order to force the loading of the global
459      * dictionary, otherwise accepting reauthentication tokens fails unless
460      * the acceptor has already accepted a normal authentication token.
461      */
462     if (rs_context_create(&radContext) != 0)
463         return false;
464
465     if (rs_context_read_config(radContext, RS_CONFIG_FILE) != 0) {
466         rs_context_destroy(radContext);
467         return false;
468     }
469
470     if (rs_context_init_freeradius_dict(radContext, NULL)) {
471         rs_context_destroy(radContext);
472         return false;
473     }
474
475     rs_context_destroy(radContext);
476 #endif
477
478     return true;
479 }
480
481 void
482 gss_eap_radius_attr_provider::finalize(void)
483 {
484     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
485 }
486
487 gss_eap_attr_provider *
488 gss_eap_radius_attr_provider::createAttrContext(void)
489 {
490     return new gss_eap_radius_attr_provider;
491 }
492
493 OM_uint32
494 gssEapRadiusAddAvp(OM_uint32 *minor,
495                    VALUE_PAIR **vps,
496                    uint16_t attribute,
497                    uint16_t vendor,
498                    const gss_buffer_t buffer)
499 {
500     uint32_t attrid = VENDORATTR(vendor, attribute);
501     unsigned char *p = (unsigned char *)buffer->value;
502     size_t remain = buffer->length;
503
504     do {
505         VALUE_PAIR *vp;
506         size_t n = remain;
507
508         /*
509          * There's an extra byte of padding; RADIUS AVPs can only
510          * be 253 octets.
511          */
512         if (n >= MAX_STRING_LEN)
513             n = MAX_STRING_LEN - 1;
514
515         vp = paircreate(attrid, PW_TYPE_OCTETS);
516         if (vp == NULL) {
517             *minor = ENOMEM;
518             return GSS_S_FAILURE;
519         }
520
521         memcpy(vp->vp_octets, p, n);
522         vp->length = n;
523
524         pairadd(vps, vp);
525
526         p += n;
527         remain -= n;
528     } while (remain != 0);
529
530     return GSS_S_COMPLETE;
531 }
532
533 OM_uint32
534 gssEapRadiusGetRawAvp(OM_uint32 *minor,
535                       VALUE_PAIR *vps,
536                       uint16_t attribute,
537                       uint16_t vendor,
538                       VALUE_PAIR **vp)
539 {
540     uint32_t attr = VENDORATTR(vendor, attribute);
541
542     *vp = pairfind(vps, attr);
543     if (*vp == NULL) {
544         *minor = GSSEAP_NO_SUCH_ATTR;
545         return GSS_S_UNAVAILABLE;
546     }
547
548     return GSS_S_COMPLETE;
549 }
550
551 OM_uint32
552 gssEapRadiusGetAvp(OM_uint32 *minor,
553                    VALUE_PAIR *vps,
554                    uint16_t attribute,
555                    uint16_t vendor,
556                    gss_buffer_t buffer,
557                    int concat)
558 {
559     VALUE_PAIR *vp;
560     unsigned char *p;
561     uint32_t attr = VENDORATTR(vendor, attribute);
562
563     buffer->length = 0;
564     buffer->value = NULL;
565
566     vp = pairfind(vps, attr);
567     if (vp == NULL) {
568         *minor = GSSEAP_NO_SUCH_ATTR;
569         return GSS_S_UNAVAILABLE;
570     }
571
572     do {
573         buffer->length += vp->length;
574     } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
575
576     buffer->value = GSSEAP_MALLOC(buffer->length);
577     if (buffer->value == NULL) {
578         *minor = ENOMEM;
579         return GSS_S_FAILURE;
580     }
581
582     p = (unsigned char *)buffer->value;
583
584     for (vp = pairfind(vps, attr);
585          concat && vp != NULL;
586          vp = pairfind(vp->next, attr)) {
587         memcpy(p, vp->vp_octets, vp->length);
588         p += vp->length;
589     }
590
591     *minor = 0;
592     return GSS_S_COMPLETE;
593 }
594
595 OM_uint32
596 gssEapRadiusFreeAvps(OM_uint32 *minor,
597                      VALUE_PAIR **vps)
598 {
599     pairfree(vps);
600     *minor = 0;
601     return GSS_S_COMPLETE;
602 }
603
604 OM_uint32
605 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
606 {
607     if (!gss_eap_radius_attr_provider::init()) {
608         *minor = GSSEAP_RADSEC_INIT_FAILURE;
609         return GSS_S_FAILURE;
610     }
611
612     return GSS_S_COMPLETE;
613 }
614
615 OM_uint32
616 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
617 {
618     gss_eap_radius_attr_provider::finalize();
619
620     *minor = 0;
621     return GSS_S_COMPLETE;
622 }
623
624 static DDF
625 avpMarshall(const VALUE_PAIR *vp)
626 {
627     DDF obj(NULL);
628
629     obj.addmember("type").integer(vp->attribute);
630
631     assert(vp->length <= MAX_STRING_LEN);
632
633     switch (vp->type) {
634     case PW_TYPE_INTEGER:
635     case PW_TYPE_IPADDR:
636     case PW_TYPE_DATE:
637         obj.addmember("value").integer(vp->lvalue);
638         break;
639     case PW_TYPE_STRING:
640         obj.addmember("value").string(vp->vp_strvalue);
641         break;
642     default: {
643         XMLSize_t len;
644         XMLByte *b64 = xercesc::Base64::encode(vp->vp_octets, vp->length, &len);
645
646         if (b64[len - 1] == '\n')
647             b64[--len] = '\0'; /* XXX there may be embedded newlines */
648
649         obj.addmember("value").string((char *)b64);
650         delete b64;
651         break;
652     }
653     }
654
655     return obj;
656 }
657
658 static bool
659 avpUnmarshall(VALUE_PAIR **pVp, DDF &obj)
660 {
661     VALUE_PAIR *vp = NULL;
662     DICT_ATTR *da;
663     uint32_t attrid;
664
665     attrid = obj["type"].integer();
666
667     da = dict_attrbyvalue(attrid);
668     if (da != NULL) {
669         vp = pairalloc(da);
670     } else {
671         vp = paircreate(attrid, PW_TYPE_STRING);
672     }
673     if (vp == NULL) {
674         throw new std::bad_alloc;
675         goto fail;
676     }
677
678     switch (vp->type) {
679     case PW_TYPE_INTEGER:
680     case PW_TYPE_IPADDR:
681     case PW_TYPE_DATE:
682         vp->length = 4;
683         vp->lvalue = obj["value"].integer();;
684         break;
685     case PW_TYPE_STRING: {
686         const char *str = obj["value"].string();
687         size_t len = strlen(str);
688         if (str == NULL || len >= MAX_STRING_LEN)
689             goto fail;
690
691         vp->length = len;
692         memcpy(vp->vp_strvalue, str, len + 1);
693         break;
694     }
695     case PW_TYPE_OCTETS:
696     default: {
697         XMLSize_t len;
698         const XMLByte *b64 = (const XMLByte *)obj["value"].string();
699         XMLByte *data = xercesc::Base64::decode(b64, &len);
700         if (data == NULL || len >= MAX_STRING_LEN) {
701             delete data;
702             goto fail;
703         }
704
705         vp->length = len;
706         memcpy(vp->vp_octets, data, len);
707         vp->vp_octets[len] = '\0';
708         delete data;
709         break;
710     }
711     }
712
713     *pVp = vp;
714
715     return true;
716
717 fail:
718     if (vp != NULL)
719         pairbasicfree(vp);
720     *pVp = NULL;
721     return false;
722 }
723
724 const char *
725 gss_eap_radius_attr_provider::marshallingKey(void) const
726 {
727     return "radius";
728 }
729
730 bool
731 gss_eap_radius_attr_provider::unmarshallAndInit(const gss_eap_attr_ctx *ctx,
732                                                 DDF &obj)
733 {
734     VALUE_PAIR **pNext = &m_vps;
735
736     if (!gss_eap_attr_provider::unmarshallAndInit(ctx, obj))
737         return false;
738
739     DDF attrs = obj["attributes"];
740     DDF attr = attrs.first();
741
742     while (!attr.isnull()) {
743         VALUE_PAIR *vp;
744
745         if (!avpUnmarshall(&vp, attr))
746             return false;
747
748         *pNext = vp;
749         pNext = &vp->next;
750
751         attr = attrs.next();
752     }
753
754     return true;
755 }
756
757 const char *
758 gss_eap_radius_attr_provider::prefix(void) const
759 {
760     return "urn:ietf:params:gss-eap:radius-avp";
761 }
762
763 DDF
764 gss_eap_radius_attr_provider::marshall(void) const
765 {
766     DDF obj(NULL);
767     DDF attrs = obj.structure().addmember("attributes").list();
768
769     for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) {
770         DDF attr = avpMarshall(vp);
771         attrs.add(attr);
772     }
773
774     return obj;
775 }
776
777 time_t
778 gss_eap_radius_attr_provider::getExpiryTime(void) const
779 {
780     VALUE_PAIR *vp;
781
782     vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
783     if (vp == NULL || vp->lvalue == 0)
784         return 0;
785
786     return time(NULL) + vp->lvalue;
787 }
788
789 OM_uint32
790 gssEapRadiusMapError(OM_uint32 *minor,
791                      struct rs_error *err)
792 {
793     int code;
794
795     assert(err != NULL);
796
797     code = rs_err_code(err, 0);
798
799     if (code == RSE_OK) {
800         *minor = 0;
801         return GSS_S_COMPLETE;
802     }
803
804     *minor = ERROR_TABLE_BASE_rse + code;
805
806     gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
807     rs_err_free(err);
808
809     return GSS_S_FAILURE;
810 }