861e97bd8ae87477948edccad6f632bf60939abc
[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 /* stuff that should be provided by libradsec/libfreeradius-radius */
36 #define VENDORATTR(vendor, attr)            (((vendor) << 16) | (attr))
37
38 #ifndef ATTRID
39 #define ATTRID(attr)                        ((attr) & 0xFFFF)
40 #endif
41
42 static gss_buffer_desc radiusUrnPrefix = {
43     sizeof("urn:x-radius:") - 1,
44     (void *)"urn:x-radius:"
45 };
46
47 static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
48
49 gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
50 {
51     m_vps = NULL;
52     m_authenticated = false;
53 }
54
55 gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
56 {
57     if (m_vps != NULL)
58         pairfree(&m_vps);
59 }
60
61 bool
62 gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager,
63                                                       const gss_eap_attr_provider *ctx)
64 {
65     const gss_eap_radius_attr_provider *radius;
66
67     if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx))
68         return false;
69
70     radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
71
72     if (radius->m_vps != NULL)
73         m_vps = copyAvps(const_cast<VALUE_PAIR *>(radius->getAvps()));
74
75     m_authenticated = radius->m_authenticated;
76
77     return true;
78 }
79
80 bool
81 gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager,
82                                                  const gss_cred_id_t gssCred,
83                                                  const gss_ctx_id_t gssCtx)
84 {
85     if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx))
86         return false;
87
88     if (gssCtx != GSS_C_NO_CONTEXT) {
89         if (gssCtx->acceptorCtx.vps != NULL) {
90             m_vps = copyAvps(gssCtx->acceptorCtx.vps);
91             if (m_vps == NULL)
92                 return false;
93
94             /* We assume libradsec validated this for us */
95             assert(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL);
96             m_authenticated = true;
97         }
98     }
99
100     return true;
101 }
102
103 static bool
104 alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
105 {
106     for (std::vector<std::string>::const_iterator a = attrs.begin();
107          a != attrs.end();
108          ++a) {
109         if (strcmp(vp->name, (*a).c_str()) == 0)
110             return true;
111     }
112
113     return false;
114 }
115
116 static bool
117 isSecretAttributeP(uint16_t attrid, uint16_t vendor)
118 {
119     bool ret = false;
120
121     switch (vendor) {
122     case VENDORPEC_MS:
123         switch (attrid) {
124         case PW_MS_MPPE_SEND_KEY:
125         case PW_MS_MPPE_RECV_KEY:
126             ret = true;
127             break;
128         default:
129             break;
130         }
131     default:
132         break;
133     }
134
135     return ret;
136 }
137
138 static bool
139 isSecretAttributeP(uint32_t attribute)
140 {
141     return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute));
142 }
143
144 static bool
145 isHiddenAttributeP(uint16_t attrid, uint16_t vendor)
146 {
147     bool ret = false;
148
149     /* should have been filtered */
150     assert(!isSecretAttributeP(attrid, vendor));
151
152     switch (vendor) {
153     case VENDORPEC_UKERNA:
154         ret = true;
155         break;
156     default:
157         break;
158     }
159
160     return ret;
161 }
162
163 static bool
164 isHiddenAttributeP(uint32_t attribute)
165 {
166     return isHiddenAttributeP(ATTRID(attribute), VENDOR(attribute));
167 }
168
169 /*
170  * Copy AVP list, same as paircopy except it filters out attributes
171  * containing keys.
172  */
173 static VALUE_PAIR *
174 copyAvps(const VALUE_PAIR *src)
175 {
176     const VALUE_PAIR *vp;
177     VALUE_PAIR *dst = NULL, **pDst = &dst;
178
179     for (vp = src; vp != NULL; vp = vp->next) {
180         VALUE_PAIR *vpcopy;
181
182         if (isSecretAttributeP(vp->attribute))
183             continue;
184
185         vpcopy = paircopyvp(vp);
186         if (vpcopy == NULL) {
187             pairfree(&dst);
188             throw new std::bad_alloc;
189             return NULL;
190         }
191         *pDst = vpcopy;
192         pDst = &vpcopy->next;
193      }
194
195     return dst;
196 }
197
198 bool
199 gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, void *data) const
200 {
201     VALUE_PAIR *vp;
202     std::vector <std::string> seen;
203
204     for (vp = m_vps; vp != NULL; vp = vp->next) {
205         gss_buffer_desc attribute;
206         char attrid[64];
207
208         if (isHiddenAttributeP(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 *da;
252     uint32_t attrid;
253     char *s;
254
255     duplicateBuffer(*attr, &strAttr);
256     s = (char *)strAttr.value;
257
258     if (attr->length < radiusUrnPrefix.length ||
259         memcmp(s, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
260         return false;
261
262     s += radiusUrnPrefix.length;
263
264     if (isdigit(*s)) {
265         attrid = strtoul(s, NULL, 10);
266     } else {
267         da = dict_attrbyname(s);
268         if (da == NULL) {
269             gss_release_buffer(&tmpMinor, &strAttr);
270             return false;
271         }
272         attrid = da->attr;
273     }
274
275     gss_release_buffer(&tmpMinor, &strAttr);
276
277     return getAttribute(attrid, authenticated, complete,
278                         value, display_value, more);
279 }
280
281 bool
282 gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
283                                            int *authenticated,
284                                            int *complete,
285                                            gss_buffer_t value,
286                                            gss_buffer_t display_value,
287                                            int *more) const
288 {
289     VALUE_PAIR *vp;
290     int i = *more, count = 0;
291
292     *more = 0;
293
294     if (isHiddenAttributeP(attrid))
295         return false;
296
297     if (i == -1)
298         i = 0;
299
300     for (vp = pairfind(m_vps, attrid);
301          vp != NULL;
302          vp = pairfind(vp->next, attrid)) {
303         if (count++ == i) {
304             if (pairfind(vp->next, attrid) != NULL)
305                 *more = count;
306             break;
307         }
308     }
309
310     if (vp == NULL && *more == 0)
311         return false;
312
313     if (value != GSS_C_NO_BUFFER) {
314         gss_buffer_desc valueBuf;
315
316         valueBuf.value = (void *)vp->vp_octets;
317         valueBuf.length = vp->length;
318
319         duplicateBuffer(valueBuf, value);
320     }
321
322     if (display_value != GSS_C_NO_BUFFER) {
323         char displayString[MAX_STRING_LEN];
324         gss_buffer_desc displayBuf;
325
326         displayBuf.length = vp_prints_value(displayString,
327                                             sizeof(displayString), vp, 0);
328         displayBuf.value = (void *)displayString;
329
330         duplicateBuffer(displayBuf, display_value);
331     }
332
333     if (authenticated != NULL)
334         *authenticated = m_authenticated;
335     if (complete != NULL)
336         *complete = true;
337
338     return true;
339 }
340
341 bool
342 gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
343                                                      uint16_t vendor,
344                                                      int *authenticated,
345                                                      int *complete,
346                                                      gss_buffer_t value) const
347 {
348     OM_uint32 major, minor;
349
350     major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE);
351
352     if (authenticated != NULL)
353         *authenticated = m_authenticated;
354     if (complete != NULL)
355         *complete = true;
356
357     return !GSS_ERROR(major);
358 }
359
360 bool
361 gss_eap_radius_attr_provider::getAttribute(uint16_t attribute,
362                                            uint16_t vendor,
363                                            int *authenticated,
364                                            int *complete,
365                                            gss_buffer_t value,
366                                            gss_buffer_t display_value,
367                                            int *more) const
368 {
369
370     return getAttribute(VENDORATTR(attribute, vendor),
371                         authenticated, complete,
372                         value, display_value, more);
373 }
374
375 gss_any_t
376 gss_eap_radius_attr_provider::mapToAny(int authenticated,
377                                        gss_buffer_t type_id) const
378 {
379     if (authenticated && !m_authenticated)
380         return (gss_any_t)NULL;
381
382     return (gss_any_t)copyAvps(m_vps);
383 }
384
385 void
386 gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
387                                                     gss_any_t input) const
388 {
389     pairfree((VALUE_PAIR **)&input);
390 }
391
392 bool
393 gss_eap_radius_attr_provider::init(void)
394 {
395     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
396                                        "urn:ietf:params:gss-eap:radius-avp",
397                                        gss_eap_radius_attr_provider::createAttrContext);
398     return true;
399 }
400
401 void
402 gss_eap_radius_attr_provider::finalize(void)
403 {
404     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
405 }
406
407 gss_eap_attr_provider *
408 gss_eap_radius_attr_provider::createAttrContext(void)
409 {
410     return new gss_eap_radius_attr_provider;
411 }
412
413 OM_uint32
414 gssEapRadiusAddAvp(OM_uint32 *minor,
415                    VALUE_PAIR **vps,
416                    uint16_t attribute,
417                    uint16_t vendor,
418                    gss_buffer_t buffer)
419 {
420     uint32_t attrid = VENDORATTR(vendor, attribute);
421     unsigned char *p = (unsigned char *)buffer->value;
422     size_t remain = buffer->length;
423
424     do {
425         VALUE_PAIR *vp;
426         size_t n = remain;
427
428         if (n > MAX_STRING_LEN)
429             n = MAX_STRING_LEN;
430
431         vp = paircreate(attrid, PW_TYPE_OCTETS);
432         if (vp == NULL) {
433             *minor = ENOMEM;
434             return GSS_S_FAILURE;
435         }
436
437         memcpy(vp->vp_octets, p, n);
438         vp->length = n;
439
440         pairadd(vps, vp);
441
442         p += n;
443         remain -= n;
444     } while (remain != 0);
445
446     return GSS_S_COMPLETE;
447 }
448
449 OM_uint32
450 gssEapRadiusGetRawAvp(OM_uint32 *minor,
451                       VALUE_PAIR *vps,
452                       uint16_t attribute,
453                       uint16_t vendor,
454                       VALUE_PAIR **vp)
455 {
456     uint32_t attr = VENDORATTR(vendor, attribute);
457
458     *vp = pairfind(vps, attr);
459
460     return (*vp == NULL) ? GSS_S_UNAVAILABLE : GSS_S_COMPLETE;
461 }
462
463 OM_uint32
464 gssEapRadiusGetAvp(OM_uint32 *minor,
465                    VALUE_PAIR *vps,
466                    uint16_t attribute,
467                    uint16_t vendor,
468                    gss_buffer_t buffer,
469                    int concat)
470 {
471     VALUE_PAIR *vp;
472     unsigned char *p;
473     uint32_t attr = VENDORATTR(vendor, attribute);
474
475     buffer->length = 0;
476     buffer->value = NULL;
477
478     vp = pairfind(vps, attr);
479     if (vp == NULL)
480         return GSS_S_UNAVAILABLE;
481
482     do {
483         buffer->length += vp->length;
484     } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
485
486     buffer->value = GSSEAP_MALLOC(buffer->length);
487     if (buffer->value == NULL) {
488         *minor = ENOMEM;
489         return GSS_S_FAILURE;
490     }
491
492     p = (unsigned char *)buffer->value;
493
494     for (vp = pairfind(vps, attr);
495          concat && vp != NULL;
496          vp = pairfind(vp->next, attr)) {
497         memcpy(p, vp->vp_octets, vp->length);
498         p += vp->length;
499     }
500
501     *minor = 0;
502     return GSS_S_COMPLETE;
503 }
504
505 OM_uint32
506 gssEapRadiusFreeAvps(OM_uint32 *minor,
507                      VALUE_PAIR **vps)
508 {
509     pairfree(vps);
510     *minor = 0;
511     return GSS_S_COMPLETE;
512 }
513
514 OM_uint32
515 gssEapRadiusAttrProviderInit(OM_uint32 *minor)
516 {
517     return gss_eap_radius_attr_provider::init()
518         ? GSS_S_COMPLETE : GSS_S_FAILURE;
519 }
520
521 OM_uint32
522 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
523 {
524     gss_eap_radius_attr_provider::finalize();
525     return GSS_S_COMPLETE;
526 }
527
528 /* partition error namespace so it does not conflict with krb5 */
529 #define ERROR_TABLE_BASE_rse (46882560L)
530
531 #define RS_TO_COM_ERR(rse)                  ((rse) == RSE_OK ? 0 : (rse) + ERROR_TABLE_BASE_rse)
532 #define COM_TO_RS_ERR(err)                  ((err) > ERROR_TABLE_BASE_rse && \
533                                              (err) <= (ERROR_TABLE_BASE_rse + RSE_SOME_ERROR) ? \
534                                              (err) - ERROR_TABLE_BASE_rse : RSE_SOME_ERROR)
535
536 OM_uint32
537 gssEapRadiusMapError(OM_uint32 *minor,
538                      struct rs_error *err)
539 {
540     int code = RSE_OK;
541
542     if (err != NULL)
543         code = rs_err_code(err, 0);
544     else
545         code = RSE_SOME_ERROR;
546
547     *minor = RS_TO_COM_ERR(code);
548
549     gssEapSaveStatusInfo(*minor, "radsec: %s", rs_err_msg(err, 0));
550
551     rs_err_free(err);
552     return GSS_S_FAILURE;
553 }
554
555 OM_uint32
556 gssEapRadiusAllocConn(OM_uint32 *minor,
557                       const gss_cred_id_t cred,
558                       gss_ctx_id_t ctx)
559 {
560     struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx;
561     const char *configFile = RS_CONFIG_FILE;
562     const char *configStanza = "gss-eap";
563     struct rs_alloc_scheme ralloc;
564     struct rs_error *err;
565
566     assert(actx->radHandle == NULL);
567     assert(actx->radConn == NULL);
568
569     if (rs_context_create(&actx->radHandle, RS_DICT_FILE) != 0)
570         return GSS_S_FAILURE;
571
572     if (cred != GSS_C_NO_CREDENTIAL) {
573         if (cred->radiusConfigFile != NULL)
574             configFile = cred->radiusConfigFile;
575         if (cred->radiusConfigStanza != NULL)
576             configStanza = cred->radiusConfigStanza;
577     }
578
579     ralloc.calloc  = GSSEAP_CALLOC;
580     ralloc.malloc  = GSSEAP_MALLOC;
581     ralloc.free    = GSSEAP_FREE;
582     ralloc.realloc = GSSEAP_REALLOC;
583
584     rs_context_set_alloc_scheme(actx->radHandle, &ralloc);
585
586     if (rs_context_read_config(actx->radHandle, configFile) != 0) {
587         err = rs_err_ctx_pop(actx->radHandle);
588         goto fail;
589     }
590
591     if (rs_conn_create(actx->radHandle, &actx->radConn, configStanza) != 0) {
592         err = rs_err_conn_pop(actx->radConn);
593         goto fail;
594     }
595
596     /* XXX TODO rs_conn_select_server does not exist yet */
597 #if 0
598     if (actx->radServer != NULL) {
599         if (rs_conn_select_server(actx->radConn, actx->radServer) != 0) {
600             err = rs_err_conn_pop(actx->radConn);
601             goto fail;
602         }
603     }
604 #endif
605
606     *minor = 0;
607     return GSS_S_COMPLETE;
608
609 fail:
610     OM_uint32 major = gssEapRadiusMapError(minor, err);
611
612     if (actx->radConn != NULL) {
613         rs_conn_destroy(actx->radConn);
614         actx->radConn = NULL;
615     }
616
617     if (actx->radHandle != NULL) {
618         rs_context_destroy(actx->radHandle);
619         actx->radHandle = NULL;
620     }
621
622     return major;
623 }
624
625 /*
626  * Encoding is:
627  * 4 octet NBO attribute ID | 4 octet attribute length | attribute data
628  */
629 static size_t
630 avpSize(const VALUE_PAIR *vp)
631 {
632     size_t size = 4 + 1;
633
634     if (vp != NULL)
635         size += vp->length;
636
637     return size;
638 }
639
640 static bool
641 avpExport(const VALUE_PAIR *vp,
642           unsigned char **pBuffer,
643           size_t *pRemain)
644 {
645     unsigned char *p = *pBuffer;
646     size_t remain = *pRemain;
647
648     assert(remain >= avpSize(vp));
649
650     store_uint32_be(vp->attribute, p);
651
652     switch (vp->type) {
653     case PW_TYPE_INTEGER:
654     case PW_TYPE_IPADDR:
655     case PW_TYPE_DATE:
656         p[4] = 4;
657         store_uint32_be(vp->lvalue, p + 5);
658         break;
659     default:
660         assert(vp->length <= MAX_STRING_LEN);
661         p[4] = (uint8_t)vp->length;
662         memcpy(p + 5, vp->vp_octets, vp->length);
663         break;
664     }
665
666     *pBuffer += 5 + p[4];
667     *pRemain -= 5 + p[4];
668
669     return true;
670
671 }
672
673 static bool
674 avpImport(VALUE_PAIR **pVp,
675           unsigned char **pBuffer,
676           size_t *pRemain)
677 {
678     unsigned char *p = *pBuffer;
679     size_t remain = *pRemain;
680     VALUE_PAIR *vp = NULL;
681     DICT_ATTR *da;
682     uint32_t attrid;
683
684     if (remain < avpSize(NULL))
685         goto fail;
686
687     attrid = load_uint32_be(p);
688     p += 4;
689     remain -= 4;
690
691     da = dict_attrbyvalue(attrid);
692     if (da == NULL)
693         goto fail;
694
695     vp = pairalloc(da);
696     if (vp == NULL) {
697         throw new std::bad_alloc;
698         goto fail;
699     }
700
701     if (remain < p[0])
702         goto fail;
703
704     switch (vp->type) {
705     case PW_TYPE_INTEGER:
706     case PW_TYPE_IPADDR:
707     case PW_TYPE_DATE:
708         if (p[0] != 4)
709             goto fail;
710
711         vp->length = 4;
712         vp->lvalue = load_uint32_be(p + 1);
713         p += 5;
714         remain -= 5;
715         break;
716     case PW_TYPE_STRING:
717         /* check enough room to NUL terminate */
718         if (p[0] == MAX_STRING_LEN)
719             goto fail;
720         else
721         /* fallthrough */
722     default:
723         if (p[0] > MAX_STRING_LEN)
724             goto fail;
725
726         vp->length = (uint32_t)p[0];
727         memcpy(vp->vp_octets, p + 1, vp->length);
728
729         if (vp->type == PW_TYPE_STRING)
730             vp->vp_strvalue[vp->length] = '\0';
731
732         p += 1 + vp->length;
733         remain -= 1 + vp->length;
734         break;
735     }
736
737     *pVp = vp;
738     *pBuffer = p;
739     *pRemain = remain;
740
741     return true;
742
743 fail:
744     pairbasicfree(vp);
745     return false;
746 }
747
748 bool
749 gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
750                                              const gss_buffer_t buffer)
751 {
752     unsigned char *p = (unsigned char *)buffer->value;
753     size_t remain = buffer->length;
754     uint32_t count;
755     VALUE_PAIR **pNext = &m_vps;
756
757     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
758         return false;
759
760     if (remain < 4)
761         return false;
762
763     count = load_uint32_be(p);
764     p += 4;
765     remain -= 4;
766
767     do {
768         VALUE_PAIR *attr;
769
770         if (!avpImport(&attr, &p, &remain))
771             return false;
772
773         *pNext = attr;
774         pNext = &attr->next;
775
776         count--;
777     } while (remain != 0);
778
779     if (count != 0)
780         return false;
781
782     return true;
783 }
784
785 void
786 gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const
787 {
788     uint32_t count = 0;
789     VALUE_PAIR *vp;
790     unsigned char *p;
791     size_t remain = 4;
792
793     for (vp = m_vps; vp != NULL; vp = vp->next) {
794         remain += avpSize(vp);
795         count++;
796     }
797
798     buffer->value = GSSEAP_MALLOC(remain);
799     if (buffer->value == NULL) {
800         throw new std::bad_alloc;
801         return;
802     }
803     buffer->length = remain;
804
805     p = (unsigned char *)buffer->value;
806
807     store_uint32_be(count, p);
808     p += 4;
809     remain -= 4;
810
811     for (vp = m_vps; vp != NULL; vp = vp->next) {
812         avpExport(vp, &p, &remain);
813     }
814
815     assert(remain == 0);
816 }
817
818 time_t
819 gss_eap_radius_attr_provider::getExpiryTime(void) const
820 {
821     VALUE_PAIR *vp;
822
823     vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
824     if (vp == NULL || vp->lvalue == 0)
825         return 0;
826
827     return time(NULL) + vp->lvalue;
828 }