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