use a more efficient encoding of RADIUS AVPs
[mech_eap.git] / 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 static gss_buffer_desc radiusUrnPrefix = {
36     sizeof("urn:x-radius:") - 1,
37     (void *)"urn:x-radius:"
38 };
39
40 VALUE_PAIR *
41 gss_eap_radius_attr_provider::copyAvps(const VALUE_PAIR *src)
42 {
43     const VALUE_PAIR *vp;
44     VALUE_PAIR *dst = NULL, **pDst = &dst;
45
46     for (vp = src; vp != NULL; vp = vp->next) {
47         VALUE_PAIR *vp2;
48
49         vp2 = (VALUE_PAIR *)GSSEAP_CALLOC(1, sizeof(*vp2));
50         if (vp2 == NULL) {
51             rc_avpair_free(dst);
52             return NULL;
53         }
54         memcpy(vp2, vp, sizeof(*vp));
55         vp2->next = NULL;
56         *pDst = vp2;
57         pDst = &vp2->next;
58     }
59
60     return dst;
61 }
62
63 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
64 {
65     m_rh = NULL;
66     m_avps = NULL;
67     m_authenticated = false;
68 }
69
70 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
71 {
72     if (m_rh != NULL)
73         rc_config_free(m_rh);
74     if (m_avps != NULL)
75         rc_avpair_free(m_avps);
76 }
77
78 bool
79 gss_eap_radius_attr_provider::initFromGssCred(const gss_cred_id_t cred)
80 {
81     OM_uint32 minor;
82
83     return !GSS_ERROR(gssEapRadiusAllocHandle(&minor, cred, &m_rh));
84 }
85
86 bool
87 gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager,
88                                                       const gss_eap_attr_provider *ctx)
89 {
90     const gss_eap_radius_attr_provider *radius;
91
92     if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx))
93         return false;
94
95     if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
96         return false;
97
98     radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
99     if (radius->m_avps != NULL) {
100         m_avps = copyAvps(radius->getAvps());
101     }
102
103     return true;
104 }
105
106 bool
107 gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager,
108                                                  const gss_cred_id_t gssCred,
109                                                  const gss_ctx_id_t gssCtx)
110 {
111     if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx))
112         return false;
113
114     if (!initFromGssCred(gssCred))
115         return false;
116
117     if (gssCtx != GSS_C_NO_CONTEXT) {
118         if (gssCtx->acceptorCtx.avps != NULL) {
119             m_avps = copyAvps(gssCtx->acceptorCtx.avps);
120             if (m_avps == NULL)
121                 return false;
122         }
123     }
124
125     return true;
126 }
127
128 static bool
129 alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
130 {
131     for (std::vector<std::string>::const_iterator a = attrs.begin();
132          a != attrs.end();
133          ++a) {
134         if (strcmp(vp->name, (*a).c_str()) == 0)
135             return true;
136     }
137
138     return false;
139 }
140
141 static bool
142 isHiddenAttributeP(int attrid, int vendor)
143 {
144     bool ret = false;
145
146     switch (vendor) {
147     case VENDOR_ID_MICROSOFT:
148         switch (attrid) {
149         case VENDOR_ATTR_MS_MPPE_SEND_KEY:
150         case VENDOR_ATTR_MS_MPPE_RECV_KEY:
151             ret = true;
152             break;
153         default:
154             break;
155         }
156     case VENDOR_ID_UKERNA:
157         ret = true;
158         break;
159     default:
160         break;
161     }
162
163     return ret;
164 }
165
166 bool
167 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, void *data) const
168 {
169     VALUE_PAIR *vp;
170     std::vector <std::string> seen;
171
172     for (vp = m_avps; vp != NULL; vp = vp->next) {
173         gss_buffer_desc attribute;
174         char attrid[64];
175         if (isHiddenAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute)))
176             continue;
177
178         if (alreadyAddedAttributeP(seen, vp))
179             continue;
180
181         snprintf(attrid, sizeof(attrid), "%s%d",
182             (char *)radiusUrnPrefix.value, vp->attribute);
183
184         attribute.value = attrid;
185         attribute.length = strlen(attrid);
186
187         if (!addAttribute(this, &attribute, data))
188             return false;
189
190         seen.push_back(std::string(vp->name));
191     }
192
193     return true;
194 }
195
196 void
197 gss_eap_radius_attr_provider::setAttribute(int complete,
198                                            const gss_buffer_t attr,
199                                            const gss_buffer_t value)
200 {
201 }
202
203 void
204 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t value)
205 {
206 }
207
208 bool
209 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
210                                            int *authenticated,
211                                            int *complete,
212                                            gss_buffer_t value,
213                                            gss_buffer_t display_value,
214                                            int *more) const
215 {
216     OM_uint32 tmpMinor;
217     gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
218     DICT_ATTR *d;
219     int attrid;
220     char *s;
221
222     /* XXX vendor */
223     duplicateBuffer(*attr, &strAttr);
224     s = (char *)strAttr.value;
225
226     if (attr->length < radiusUrnPrefix.length ||
227         memcmp(s, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
228         return false;
229
230     s += radiusUrnPrefix.length;
231
232     if (isdigit(*s)) {
233         attrid = strtoul(s, NULL, 10);
234     } else {
235         d = rc_dict_findattr(m_rh, (char *)s);
236         if (d == NULL) {
237             gss_release_buffer(&tmpMinor, &strAttr);
238             return false;
239         }
240         attrid = d->value;
241     }
242
243     gss_release_buffer(&tmpMinor, &strAttr);
244
245     return getAttribute(attrid, authenticated, complete,
246                         value, display_value, more);
247 }
248
249 static bool
250 isPrintableAttributeP(VALUE_PAIR *vp)
251 {
252     size_t i;
253     int gotChar = 0;
254
255     for (i = 0; i < sizeof(vp->strvalue); i++) {
256         if (gotChar && vp->strvalue[i] == '\0')
257             return true;
258
259         if (!isprint(vp->strvalue[i]))
260             return false;
261
262         if (!gotChar)
263             gotChar++;
264     }
265
266     return true;
267 }
268
269 bool
270 gss_eap_radius_attr_provider::getAttribute(int attrid,
271                                            int vendor,
272                                            int *authenticated,
273                                            int *complete,
274                                            gss_buffer_t value,
275                                            gss_buffer_t display_value,
276                                            int *more) const
277 {
278     OM_uint32 tmpMinor;
279     VALUE_PAIR *vp;
280     int i = *more, count = 0;
281     char name[NAME_LENGTH + 1];
282     char displayString[AUTH_STRING_LEN + 1];
283     gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER;
284     gss_buffer_desc displayBuf = GSS_C_EMPTY_BUFFER;
285
286     *more = 0;
287
288     if (isHiddenAttributeP(attrid, vendor))
289         return false;
290
291     if (i == -1)
292         i = 0;
293
294     for (vp = rc_avpair_get(m_avps, attrid, vendor);
295          vp != NULL;
296          vp = rc_avpair_get(vp->next, attrid, vendor)) {
297         if (count++ == i) {
298             if (rc_avpair_get(vp->next, attrid, vendor) != NULL)
299                 *more = count;
300             break;
301         }
302     }
303
304     if (vp == NULL && *more == 0)
305         return false;
306
307     if (vp->type == PW_TYPE_STRING) {
308         valueBuf.value = (void *)vp->strvalue;
309         valueBuf.length = vp->lvalue;
310     } else {
311         valueBuf.value = (void *)&vp->lvalue;
312         valueBuf.length = 4;
313     }
314
315     if (value != GSS_C_NO_BUFFER)
316         duplicateBuffer(valueBuf, value);
317
318     if (display_value != GSS_C_NO_BUFFER &&
319         isPrintableAttributeP(vp)) {
320         if (rc_avpair_tostr(m_rh, vp, name, NAME_LENGTH,
321                             displayString, AUTH_STRING_LEN) != 0) {
322             gss_release_buffer(&tmpMinor, value);
323             return false;
324         }
325
326         displayBuf.value = (void *)displayString;
327         displayBuf.length = strlen(displayString);
328
329         duplicateBuffer(displayBuf, display_value);
330     }
331
332     if (authenticated != NULL)
333         *authenticated = m_authenticated;
334     if (complete != NULL)
335         *complete = true;
336
337     return true;
338 }
339
340 bool
341 gss_eap_radius_attr_provider::getFragmentedAttribute(int attribute,
342                                                      int vendor,
343                                                      int *authenticated,
344                                                      int *complete,
345                                                      gss_buffer_t value) const
346 {
347     OM_uint32 major, minor;
348
349     major = getBufferFromAvps(&minor, m_avps, attribute, vendor, value, TRUE);
350
351     if (authenticated != NULL)
352         *authenticated = m_authenticated;
353     if (complete != NULL)
354         *complete = true;
355
356     return !GSS_ERROR(major);
357 }
358
359 bool
360 gss_eap_radius_attr_provider::getAttribute(int attrid,
361                                            int *authenticated,
362                                            int *complete,
363                                            gss_buffer_t value,
364                                            gss_buffer_t display_value,
365                                            int *more) const
366 {
367
368     return getAttribute(ATTRID(attrid), VENDOR(attrid),
369                         authenticated, complete,
370                         value, display_value, more);
371 }
372
373 gss_any_t
374 gss_eap_radius_attr_provider::mapToAny(int authenticated,
375                                        gss_buffer_t type_id) const
376 {
377     if (authenticated && !m_authenticated)
378         return (gss_any_t)NULL;
379
380     return (gss_any_t)copyAvps(m_avps);
381 }
382
383 void
384 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
385                                                     gss_any_t input) const
386 {
387     rc_avpair_free((VALUE_PAIR *)input);
388 }
389
390 bool
391 gss_eap_radius_attr_provider::init(void)
392 {
393     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
394                                        "urn:ietf:params:gss-eap:radius-avp",
395                                        gss_eap_radius_attr_provider::createAttrContext);
396     return true;
397 }
398
399 void
400 gss_eap_radius_attr_provider::finalize(void)
401 {
402     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
403 }
404
405 gss_eap_attr_provider *
406 gss_eap_radius_attr_provider::createAttrContext(void)
407 {
408     return new gss_eap_radius_attr_provider;
409 }
410
411 OM_uint32
412 addAvpFromBuffer(OM_uint32 *minor,
413                  rc_handle *rh,
414                  VALUE_PAIR **vp,
415                  int type,
416                  int vendor,
417                  gss_buffer_t buffer)
418 {
419     if (rc_avpair_add(rh, vp, type,
420                       buffer->value, buffer->length, vendor) == NULL) {
421         return GSS_S_FAILURE;
422     }
423
424     return GSS_S_COMPLETE;
425 }
426
427 OM_uint32
428 getBufferFromAvps(OM_uint32 *minor,
429                   VALUE_PAIR *vps,
430                   int type,
431                   int vendor,
432                   gss_buffer_t buffer,
433                   int concat)
434 {
435     VALUE_PAIR *vp;
436     unsigned char *p;
437
438     buffer->length = 0;
439     buffer->value = NULL;
440
441     vp = rc_avpair_get(vps, type, vendor);
442     if (vp == NULL)
443         return GSS_S_UNAVAILABLE;
444
445     do {
446         buffer->length += vp->lvalue;
447     } while (concat && (vp = rc_avpair_get(vp->next, type, vendor)) != NULL);
448
449     buffer->value = GSSEAP_MALLOC(buffer->length);
450     if (buffer->value == NULL) {
451         *minor = ENOMEM;
452         return GSS_S_FAILURE;
453     }
454
455     p = (unsigned char *)buffer->value;
456
457     for (vp = rc_avpair_get(vps, type, vendor);
458          concat && vp != NULL;
459          vp = rc_avpair_get(vp->next, type, vendor)) {
460         memcpy(p, vp->strvalue, vp->lvalue);
461         p += vp->lvalue;
462     }
463
464     *minor = 0;
465     return GSS_S_COMPLETE;
466 }
467
468 OM_uint32
469 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
470 {
471     return gss_eap_radius_attr_provider::init()
472         ? GSS_S_COMPLETE : GSS_S_FAILURE;
473 }
474
475 OM_uint32
476 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
477 {
478     gss_eap_radius_attr_provider::finalize();
479     return GSS_S_COMPLETE;
480 }
481
482 OM_uint32
483 gssEapRadiusAllocHandle(OM_uint32 *minor,
484                         const gss_cred_id_t cred,
485                         rc_handle **pHandle)
486 {
487     rc_handle *rh;
488     const char *config = RC_CONFIG_FILE;
489
490     *pHandle = NULL;
491
492     if (cred != GSS_C_NO_CREDENTIAL && cred->radiusConfigFile != NULL)
493         config = cred->radiusConfigFile;
494
495     rh = rc_read_config((char *)config);
496     if (rh == NULL) {
497         *minor = errno;
498         rc_config_free(rh);
499         return GSS_S_FAILURE;
500     }
501
502     if (rc_read_dictionary(rh, rc_conf_str(rh, (char *)"dictionary")) != 0) {
503         *minor = errno;
504         return GSS_S_FAILURE;
505     }
506
507     *pHandle = rh;
508     return GSS_S_COMPLETE;
509 }
510
511 /*
512  * Encoding is:
513  * 4 octet NBO attribute ID | 4 octet attribute length | attribute data
514  */
515 static size_t
516 avpSize(const VALUE_PAIR *vp)
517 {
518     size_t size = 4 + 1;
519
520     if (vp != NULL)
521         size += (vp->type == PW_TYPE_STRING) ? vp->lvalue : 4;
522
523     return size;
524 }
525
526 static bool
527 avpExport(rc_handle *rh,
528           const VALUE_PAIR *vp,
529           unsigned char **pBuffer,
530           size_t *pRemain)
531 {
532     unsigned char *p = *pBuffer;
533     size_t remain = *pRemain;
534
535     assert(remain >= avpSize(vp));
536
537     store_uint32_be(vp->attribute, p);
538
539     if (vp->type == PW_TYPE_STRING) {
540         assert(vp->lvalue <= AUTH_STRING_LEN);
541         p[4] = (uint8_t)vp->lvalue;
542         memcpy(p + 5, vp->strvalue, vp->lvalue);
543     } else {
544         p[4] = 4;
545         store_uint32_be(vp->lvalue, p + 5);
546     }
547
548     *pBuffer += 5 + p[4];
549     *pRemain -= 5 + p[4];
550
551     return true;
552
553 }
554
555 static bool
556 avpImport(rc_handle *rh,
557           VALUE_PAIR **pVp,
558           unsigned char **pBuffer,
559           size_t *pRemain)
560 {
561     unsigned char *p = *pBuffer;
562     size_t remain = *pRemain;
563     VALUE_PAIR *vp;
564     DICT_ATTR *d;
565
566     if (remain < avpSize(NULL))
567         return false;
568
569     vp = (VALUE_PAIR *)GSSEAP_CALLOC(1, sizeof(*vp));
570     if (vp == NULL) {
571         throw new std::bad_alloc;
572         return false;
573     }
574
575     vp->attribute = load_uint32_be(p);
576     p += 4;
577     remain -= 4;
578
579     d = rc_dict_getattr(rh, vp->attribute);
580     if (d == NULL)
581         goto fail;
582
583     assert(sizeof(vp->name) == sizeof(d->name));
584     strcpy(vp->name, d->name);
585     vp->type = d->type;
586
587     if (remain < p[0])
588         goto fail;
589
590     if (vp->type == PW_TYPE_STRING) {
591         if (p[0] > AUTH_STRING_LEN)
592             goto fail;
593
594         vp->lvalue = (uint32_t)p[0];
595         memcpy(vp->strvalue, p + 1, vp->lvalue);
596         vp->strvalue[vp->lvalue] = '\0';
597         p += 1 + vp->lvalue;
598         remain -= 1 + vp->lvalue;
599     } else {
600         if (p[0] != 4)
601             goto fail;
602
603         vp->lvalue = load_uint32_be(p + 1);
604         p += 5;
605         remain -= 5;
606     }
607
608     *pVp = vp;
609     *pBuffer = p;
610     *pRemain = remain;
611
612     return true;
613
614 fail:
615     GSSEAP_FREE(vp);
616     return false;
617 }
618
619 bool
620 gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
621                                              const gss_buffer_t buffer)
622 {
623     unsigned char *p = (unsigned char *)buffer->value;
624     size_t remain = buffer->length;
625     OM_uint32 count;
626     VALUE_PAIR **pNext = &m_avps;
627
628     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
629         return false;
630
631     if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
632         return false;
633
634     if (remain < 4)
635         return false;
636
637     count = load_uint32_be(p);
638     p += 4;
639     remain -= 4;
640
641     do {
642         VALUE_PAIR *attr;
643
644         if (!avpImport(m_rh, &attr, &p, &remain))
645             return false;
646
647         *pNext = attr;
648         pNext = &attr->next;
649
650         count--;
651     } while (remain != 0);
652
653     if (count != 0)
654         return false;
655
656     return true;
657 }
658
659 void
660 gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const
661 {
662     OM_uint32 count = 0;
663     VALUE_PAIR *vp;
664     unsigned char *p;
665     size_t remain = 4;
666
667     for (vp = m_avps; vp != NULL; vp = vp->next) {
668         remain += avpSize(vp);
669         count++;
670     }
671
672     buffer->value = GSSEAP_MALLOC(remain);
673     if (buffer->value == NULL) {
674         throw new std::bad_alloc;
675         return;
676     }
677     buffer->length = remain;
678
679     p = (unsigned char *)buffer->value;
680
681     store_uint32_be(count, p);
682     p += 4;
683     remain -= 4;
684
685     for (vp = m_avps; vp != NULL; vp = vp->next) {
686         avpExport(m_rh, vp, &p, &remain);
687     }
688
689     assert(remain == 0);
690 }
691
692 time_t
693 gss_eap_radius_attr_provider::getExpiryTime(void) const
694 {
695     VALUE_PAIR *vp;
696
697     vp = rc_avpair_get(m_avps, PW_SESSION_TIMEOUT, 0);
698     if (vp == NULL || vp->lvalue == 0)
699         return 0;
700
701     return time(NULL) + vp->lvalue;
702 }