use urn:radius: prefix
[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:radius:") - 1,
37     (void *)"urn: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 RADIUS_VENDOR_ID_MICROSOFT:
148         switch (attrid) {
149         case RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY:
150         case RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY:
151             ret = true;
152             break;
153         default:
154             break;
155         }
156     case RADIUS_VENDOR_ID_GSS_EAP:
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 #ifndef RADIUS_STRING_ATTRS
175         char attrid[64];
176 #endif
177         if (isHiddenAttributeP(ATTRID(vp->attribute), VENDOR(vp->attribute)))
178             continue;
179
180         if (alreadyAddedAttributeP(seen, vp))
181             continue;
182
183 #ifdef RADIUS_STRING_ATTRS
184         attribute.value = (void *)vp->name;
185         attribute.length = strlen(vp->name);
186 #else
187         snprintf(attrid, sizeof(attrid), "%s%d",
188             (char *)radiusUrnPrefix.value, vp->attribute);
189
190         attribute.value = attrid;
191         attribute.length = strlen(attrid);
192 #endif /* RADIUS_STRING_ATTRS */
193
194         if (!addAttribute(this, &attribute, data))
195             return false;
196
197         seen.push_back(std::string(vp->name));
198     }
199
200     return true;
201 }
202
203 void
204 gss_eap_radius_attr_provider::setAttribute(int complete,
205                                            const gss_buffer_t attr,
206                                            const gss_buffer_t value)
207 {
208 }
209
210 void
211 gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t value)
212 {
213 }
214
215 bool
216 gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
217                                            int *authenticated,
218                                            int *complete,
219                                            gss_buffer_t value,
220                                            gss_buffer_t display_value,
221                                            int *more) const
222 {
223     OM_uint32 tmpMinor;
224     gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
225     DICT_ATTR *d;
226     int attrid;
227     char *s;
228
229     /* XXX vendor */
230     duplicateBuffer(*attr, &strAttr);
231     s = (char *)strAttr.value;
232
233     if (attr->length >= radiusUrnPrefix.length &&
234         memcmp(s, radiusUrnPrefix.value, radiusUrnPrefix.length) == 0) {
235         s += radiusUrnPrefix.length;
236         attrid = strtoul(s, NULL, 10);
237     } else {
238         d = rc_dict_findattr(m_rh, (char *)s);
239         if (d == NULL) {
240             gss_release_buffer(&tmpMinor, &strAttr);
241             return false;
242         }
243         attrid = d->value;
244     }
245
246     gss_release_buffer(&tmpMinor, &strAttr);
247
248     return getAttribute(attrid, authenticated, complete,
249                         value, display_value, more);
250 }
251
252 static bool
253 isPrintableAttributeP(VALUE_PAIR *vp)
254 {
255     size_t i;
256     int gotChar = 0;
257
258     for (i = 0; i < sizeof(vp->strvalue); i++) {
259         if (gotChar && vp->strvalue[i] == '\0')
260             return true;
261
262         if (!isprint(vp->strvalue[i]))
263             return false;
264
265         if (!gotChar)
266             gotChar++;
267     }
268
269     return true;
270 }
271
272 bool
273 gss_eap_radius_attr_provider::getAttribute(int attrid,
274                                            int vendor,
275                                            int *authenticated,
276                                            int *complete,
277                                            gss_buffer_t value,
278                                            gss_buffer_t display_value,
279                                            int *more) const
280 {
281     OM_uint32 tmpMinor;
282     VALUE_PAIR *vp;
283     int i = *more, count = 0;
284     char name[NAME_LENGTH + 1];
285     char displayString[AUTH_STRING_LEN + 1];
286     gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER;
287     gss_buffer_desc displayBuf = GSS_C_EMPTY_BUFFER;
288
289     *more = 0;
290
291     if (isHiddenAttributeP(attrid, vendor))
292         return false;
293
294     if (i == -1)
295         i = 0;
296
297     for (vp = rc_avpair_get(m_avps, attrid, vendor);
298          vp != NULL;
299          vp = rc_avpair_get(vp->next, attrid, vendor)) {
300         if (count++ == i) {
301             if (rc_avpair_get(vp->next, attrid, vendor) != NULL)
302                 *more = count;
303             break;
304         }
305     }
306
307     if (vp == NULL && *more == 0)
308         return false;
309
310     if (vp->type == PW_TYPE_STRING) {
311         valueBuf.value = (void *)vp->strvalue;
312         valueBuf.length = vp->lvalue;
313     } else {
314         valueBuf.value = (void *)&vp->lvalue;
315         valueBuf.length = 4;
316     }
317
318     if (value != GSS_C_NO_BUFFER)
319         duplicateBuffer(valueBuf, value);
320
321     if (display_value != GSS_C_NO_BUFFER &&
322         isPrintableAttributeP(vp)) {
323         if (rc_avpair_tostr(m_rh, vp, name, NAME_LENGTH,
324                             displayString, AUTH_STRING_LEN) != 0) {
325             gss_release_buffer(&tmpMinor, value);
326             return false;
327         }
328
329         displayBuf.value = (void *)displayString;
330         displayBuf.length = strlen(displayString);
331
332         duplicateBuffer(displayBuf, display_value);
333     }
334
335     if (authenticated != NULL)
336         *authenticated = m_authenticated;
337     if (complete != NULL)
338         *complete = true;
339
340     return true;
341 }
342
343 bool
344 gss_eap_radius_attr_provider::getFragmentedAttribute(int attribute,
345                                                      int vendor,
346                                                      int *authenticated,
347                                                      int *complete,
348                                                      gss_buffer_t value) const
349 {
350     OM_uint32 major, minor;
351
352     major = getBufferFromAvps(&minor, m_avps, attribute, vendor, value, TRUE);
353
354     if (authenticated != NULL)
355         *authenticated = m_authenticated;
356     if (complete != NULL)
357         *complete = true;
358
359     return !GSS_ERROR(major);
360 }
361
362 bool
363 gss_eap_radius_attr_provider::getAttribute(int attrid,
364                                            int *authenticated,
365                                            int *complete,
366                                            gss_buffer_t value,
367                                            gss_buffer_t display_value,
368                                            int *more) const
369 {
370
371     return getAttribute(ATTRID(attrid), VENDOR(attrid),
372                         authenticated, complete,
373                         value, display_value, more);
374 }
375
376 gss_any_t
377 gss_eap_radius_attr_provider::mapToAny(int authenticated,
378                                        gss_buffer_t type_id) const
379 {
380     if (authenticated && !m_authenticated)
381         return (gss_any_t)NULL;
382
383     return (gss_any_t)copyAvps(m_avps);
384 }
385
386 void
387 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
388                                                     gss_any_t input) const
389 {
390     rc_avpair_free((VALUE_PAIR *)input);
391 }
392
393 bool
394 gss_eap_radius_attr_provider::init(void)
395 {
396     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
397                                        "urn:ietf:params:gss-eap:radius-avp",
398                                        gss_eap_radius_attr_provider::createAttrContext);
399     return true;
400 }
401
402 void
403 gss_eap_radius_attr_provider::finalize(void)
404 {
405     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
406 }
407
408 gss_eap_attr_provider *
409 gss_eap_radius_attr_provider::createAttrContext(void)
410 {
411     return new gss_eap_radius_attr_provider;
412 }
413
414 OM_uint32
415 addAvpFromBuffer(OM_uint32 *minor,
416                  rc_handle *rh,
417                  VALUE_PAIR **vp,
418                  int type,
419                  int vendor,
420                  gss_buffer_t buffer)
421 {
422     if (rc_avpair_add(rh, vp, type,
423                       buffer->value, buffer->length, vendor) == NULL) {
424         return GSS_S_FAILURE;
425     }
426
427     return GSS_S_COMPLETE;
428 }
429
430 OM_uint32
431 getBufferFromAvps(OM_uint32 *minor,
432                   VALUE_PAIR *vps,
433                   int type,
434                   int vendor,
435                   gss_buffer_t buffer,
436                   int concat)
437 {
438     VALUE_PAIR *vp;
439     unsigned char *p;
440
441     buffer->length = 0;
442     buffer->value = NULL;
443
444     vp = rc_avpair_get(vps, type, vendor);
445     if (vp == NULL)
446         return GSS_S_UNAVAILABLE;
447
448     do {
449         buffer->length += vp->lvalue;
450     } while (concat && (vp = rc_avpair_get(vp->next, type, vendor)) != NULL);
451
452     buffer->value = GSSEAP_MALLOC(buffer->length);
453     if (buffer->value == NULL) {
454         *minor = ENOMEM;
455         return GSS_S_FAILURE;
456     }
457
458     p = (unsigned char *)buffer->value;
459
460     for (vp = rc_avpair_get(vps, type, vendor);
461          concat && vp != NULL;
462          vp = rc_avpair_get(vp->next, type, vendor)) {
463         memcpy(p, vp->strvalue, vp->lvalue);
464         p += vp->lvalue;
465     }
466
467     *minor = 0;
468     return GSS_S_COMPLETE;
469 }
470
471 OM_uint32
472 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
473 {
474     return gss_eap_radius_attr_provider::init()
475         ? GSS_S_COMPLETE : GSS_S_FAILURE;
476 }
477
478 OM_uint32
479 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
480 {
481     gss_eap_radius_attr_provider::finalize();
482     return GSS_S_COMPLETE;
483 }
484
485 OM_uint32
486 gssEapRadiusAllocHandle(OM_uint32 *minor,
487                         const gss_cred_id_t cred,
488                         rc_handle **pHandle)
489 {
490     rc_handle *rh;
491     const char *config = RC_CONFIG_FILE;
492
493     *pHandle = NULL;
494
495     if (cred != GSS_C_NO_CREDENTIAL && cred->radiusConfigFile != NULL)
496         config = cred->radiusConfigFile;
497
498     rh = rc_read_config((char *)config);
499     if (rh == NULL) {
500         *minor = errno;
501         rc_config_free(rh);
502         return GSS_S_FAILURE;
503     }
504
505     if (rc_read_dictionary(rh, rc_conf_str(rh, (char *)"dictionary")) != 0) {
506         *minor = errno;
507         return GSS_S_FAILURE;
508     }
509
510     *pHandle = rh;
511     return GSS_S_COMPLETE;
512 }
513
514 /*
515  * This is a super-inefficient coding but the API is going to change
516  * as are the data structures, so not putting a lot of work in now.
517  */
518 static size_t
519 avpSize(const VALUE_PAIR *vp)
520 {
521     return NAME_LENGTH + 1 + 12 + AUTH_STRING_LEN + 1;
522 }
523
524 static bool
525 avpExport(const VALUE_PAIR *vp,
526           unsigned char **pBuffer,
527           size_t *pRemain)
528 {
529     unsigned char *p = *pBuffer;
530     size_t remain = *pRemain;
531
532     assert(remain >= avpSize(vp));
533
534     memcpy(p, vp->name, NAME_LENGTH + 1);
535     p += NAME_LENGTH + 1;
536     remain -= NAME_LENGTH + 1;
537
538     store_uint32_be(vp->attribute, &p[0]);
539     store_uint32_be(vp->type,      &p[4]);
540     store_uint32_be(vp->lvalue,    &p[8]);
541
542     p += 12;
543     remain -= 12;
544
545     memcpy(p, vp->strvalue, AUTH_STRING_LEN + 1);
546     p += AUTH_STRING_LEN + 1;
547     remain -= AUTH_STRING_LEN + 1;
548
549     *pBuffer = p;
550     *pRemain = remain;
551
552     return true;
553
554 }
555
556 static bool
557 avpImport(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
565     if (remain < avpSize(NULL)) {
566         return false;
567     }
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     vp->next = NULL;
575
576     memcpy(vp->name, p, NAME_LENGTH + 1);
577     p += NAME_LENGTH + 1;
578     remain -= NAME_LENGTH + 1;
579
580     vp->attribute = load_uint32_be(&p[0]);
581     vp->type      = load_uint32_be(&p[4]);
582     vp->lvalue    = load_uint32_be(&p[8]);
583
584     p += 12;
585     remain -= 12;
586
587     memcpy(vp->strvalue, p, AUTH_STRING_LEN + 1);
588     p += AUTH_STRING_LEN + 1;
589     remain -= AUTH_STRING_LEN + 1;
590
591     *pVp = vp;
592     *pBuffer = p;
593     *pRemain = remain;
594
595     return true;
596 }
597
598 bool
599 gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
600                                              const gss_buffer_t buffer)
601 {
602     unsigned char *p = (unsigned char *)buffer->value;
603     size_t remain = buffer->length;
604     OM_uint32 count;
605     VALUE_PAIR **pNext = &m_avps;
606
607     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
608         return false;
609
610     if (!initFromGssCred(GSS_C_NO_CREDENTIAL))
611         return false;
612
613     if (remain < 4)
614         return false;
615
616     count = load_uint32_be(p);
617     p += 4;
618     remain -= 4;
619
620     do {
621         VALUE_PAIR *attr;
622
623         if (!avpImport(&attr, &p, &remain))
624             return false;
625
626         *pNext = attr;
627         pNext = &attr->next;
628
629         count--;
630     } while (remain != 0);
631
632     if (count != 0)
633         return false;
634
635     return true;
636 }
637
638 void
639 gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const
640 {
641     OM_uint32 count = 0;
642     VALUE_PAIR *vp;
643     unsigned char *p;
644     size_t remain = 4;
645
646     for (vp = m_avps; vp != NULL; vp = vp->next) {
647         remain += avpSize(vp);
648         count++;
649     }
650
651     buffer->value = GSSEAP_MALLOC(remain);
652     if (buffer->value == NULL) {
653         throw new std::bad_alloc;
654         return;
655     }
656     buffer->length = remain;
657
658     p = (unsigned char *)buffer->value;
659
660     store_uint32_be(count, p);
661     p += 4;
662     remain -= 4;
663
664     for (vp = m_avps; vp != NULL; vp = vp->next) {
665         avpExport(vp, &p, &remain);
666     }
667
668     assert(remain == 0);
669 }