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