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