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