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