use urn:x-radius: namespace
[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  * This is a super-inefficient coding but the API is going to change
513  * as are the data structures, so not putting a lot of work in now.
514  */
515 static size_t
516 avpSize(const VALUE_PAIR *vp)
517 {
518     return NAME_LENGTH + 1 + 12 + AUTH_STRING_LEN + 1;
519 }
520
521 static bool
522 avpExport(const VALUE_PAIR *vp,
523           unsigned char **pBuffer,
524           size_t *pRemain)
525 {
526     unsigned char *p = *pBuffer;
527     size_t remain = *pRemain;
528
529     assert(remain >= avpSize(vp));
530
531     memcpy(p, vp->name, NAME_LENGTH + 1);
532     p += NAME_LENGTH + 1;
533     remain -= NAME_LENGTH + 1;
534
535     store_uint32_be(vp->attribute, &p[0]);
536     store_uint32_be(vp->type,      &p[4]);
537     store_uint32_be(vp->lvalue,    &p[8]);
538
539     p += 12;
540     remain -= 12;
541
542     memcpy(p, vp->strvalue, AUTH_STRING_LEN + 1);
543     p += AUTH_STRING_LEN + 1;
544     remain -= AUTH_STRING_LEN + 1;
545
546     *pBuffer = p;
547     *pRemain = remain;
548
549     return true;
550
551 }
552
553 static bool
554 avpImport(VALUE_PAIR **pVp,
555           unsigned char **pBuffer,
556           size_t *pRemain)
557 {
558     unsigned char *p = *pBuffer;
559     size_t remain = *pRemain;
560     VALUE_PAIR *vp;
561
562     if (remain < avpSize(NULL)) {
563         return false;
564     }
565
566     vp = (VALUE_PAIR *)GSSEAP_CALLOC(1, sizeof(*vp));
567     if (vp == NULL) {
568         throw new std::bad_alloc;
569         return false;
570     }
571     vp->next = NULL;
572
573     memcpy(vp->name, p, NAME_LENGTH + 1);
574     p += NAME_LENGTH + 1;
575     remain -= NAME_LENGTH + 1;
576
577     vp->attribute = load_uint32_be(&p[0]);
578     vp->type      = load_uint32_be(&p[4]);
579     vp->lvalue    = load_uint32_be(&p[8]);
580
581     p += 12;
582     remain -= 12;
583
584     memcpy(vp->strvalue, p, AUTH_STRING_LEN + 1);
585     p += AUTH_STRING_LEN + 1;
586     remain -= AUTH_STRING_LEN + 1;
587
588     *pVp = vp;
589     *pBuffer = p;
590     *pRemain = remain;
591
592     return true;
593 }
594
595 bool
596 gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
597                                              const gss_buffer_t buffer)
598 {
599     unsigned char *p = (unsigned char *)buffer->value;
600     size_t remain = buffer->length;
601     OM_uint32 count;
602     VALUE_PAIR **pNext = &m_avps;
603
604     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
605         return false;
606
607     if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
608         return false;
609
610     if (remain < 4)
611         return false;
612
613     count = load_uint32_be(p);
614     p += 4;
615     remain -= 4;
616
617     do {
618         VALUE_PAIR *attr;
619
620         if (!avpImport(&attr, &p, &remain))
621             return false;
622
623         *pNext = attr;
624         pNext = &attr->next;
625
626         count--;
627     } while (remain != 0);
628
629     if (count != 0)
630         return false;
631
632     return true;
633 }
634
635 void
636 gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const
637 {
638     OM_uint32 count = 0;
639     VALUE_PAIR *vp;
640     unsigned char *p;
641     size_t remain = 4;
642
643     for (vp = m_avps; vp != NULL; vp = vp->next) {
644         remain += avpSize(vp);
645         count++;
646     }
647
648     buffer->value = GSSEAP_MALLOC(remain);
649     if (buffer->value == NULL) {
650         throw new std::bad_alloc;
651         return;
652     }
653     buffer->length = remain;
654
655     p = (unsigned char *)buffer->value;
656
657     store_uint32_be(count, p);
658     p += 4;
659     remain -= 4;
660
661     for (vp = m_avps; vp != NULL; vp = vp->next) {
662         avpExport(vp, &p, &remain);
663     }
664
665     assert(remain == 0);
666 }
667
668 time_t
669 gss_eap_radius_attr_provider::getExpiryTime(void) const
670 {
671     VALUE_PAIR *vp;
672
673     vp = rc_avpair_get(m_avps, PW_SESSION_TIMEOUT, 0);
674     if (vp == NULL || vp->lvalue == 0)
675         return 0;
676
677     return time(NULL) + vp->lvalue;
678 }