Fixes to build without PTHREADs
[freeradius.git] / src / modules / rlm_eap / rlm_eap.c
1 /*
2  * rlm_eap.c  contains handles that are called from modules.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2003,2006  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  * Copyright 2003  Alan DeKok <aland@freeradius.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/modules.h>
30
31 #include "rlm_eap.h"
32
33 static const CONF_PARSER module_config[] = {
34         { "default_eap_type", PW_TYPE_STRING_PTR,
35           offsetof(rlm_eap_t, default_eap_type_name), NULL, "md5" },
36         { "timer_expire", PW_TYPE_INTEGER,
37           offsetof(rlm_eap_t, timer_limit), NULL, "60"},
38         { "ignore_unknown_eap_types", PW_TYPE_BOOLEAN,
39           offsetof(rlm_eap_t, ignore_unknown_eap_types), NULL, "no" },
40         { "cisco_accounting_username_bug", PW_TYPE_BOOLEAN,
41           offsetof(rlm_eap_t, cisco_accounting_username_bug), NULL, "no" },
42         { "max_sessions", PW_TYPE_INTEGER,
43           offsetof(rlm_eap_t, max_sessions), NULL, "2048"},
44
45         { NULL, -1, 0, NULL, NULL }           /* end the list */
46 };
47
48 /*
49  * delete all the allocated space by eap module
50  */
51 static int eap_detach(void *instance)
52 {
53         rlm_eap_t *inst;
54         int i;
55
56         inst = (rlm_eap_t *)instance;
57
58 #ifdef HAVE_PTHREAD_H
59         pthread_mutex_destroy(&(inst->session_mutex));
60         if (inst->handler_tree) pthread_mutex_destroy(&(inst->handler_mutex));
61 #endif
62
63         rbtree_free(inst->session_tree);
64         if (inst->handler_tree) rbtree_free(inst->handler_tree);
65         inst->session_tree = NULL;
66         eaplist_free(inst);
67
68         for (i = 0; i < PW_EAP_MAX_TYPES; i++) {
69                 if (inst->types[i]) eaptype_free(inst->types[i]);
70                 inst->types[i] = NULL;
71         }
72
73         free(inst);
74
75         return 0;
76 }
77
78
79 /*
80  *      Compare two handlers.
81  */
82 static int eap_handler_cmp(const void *a, const void *b)
83 {
84         int rcode;
85         const EAP_HANDLER *one = a;
86         const EAP_HANDLER *two = b;
87
88         if (one->eap_id < two->eap_id) return -1;
89         if (one->eap_id > two->eap_id) return +1;
90
91         rcode = memcmp(one->state, two->state, sizeof(one->state));
92         if (rcode != 0) return rcode;
93
94         /*
95          *      As of 2.1.8, we don't key off of source IP.  This
96          *      a NAS to send packets load-balanced (or fail-over)
97          *      across multiple intermediate proxies, and still have
98          *      EAP work.
99          */
100         if (fr_ipaddr_cmp(&one->src_ipaddr, &two->src_ipaddr) != 0) {
101                 DEBUG("WARNING: EAP packets are arriving from two different upstream servers.  Has there been a proxy fail-over?");
102         }
103
104         return 0;
105 }
106
107
108 /*
109  *      Compare two handler pointers
110  */
111 static int eap_handler_ptr_cmp(const void *a, const void *b)
112 {
113   return (((uint8_t *) a) - ((uint8_t *) b));
114 }
115
116
117 /*
118  * read the config section and load all the eap authentication types present.
119  */
120 static int eap_instantiate(CONF_SECTION *cs, void **instance)
121 {
122         int             i, eap_type;
123         int             num_types;
124         CONF_SECTION    *scs;
125         rlm_eap_t       *inst;
126
127         inst = (rlm_eap_t *) malloc(sizeof(*inst));
128         if (!inst) {
129                 return -1;
130         }
131         memset(inst, 0, sizeof(*inst));
132         if (cf_section_parse(cs, inst, module_config) < 0) {
133                 eap_detach(inst);
134                 return -1;
135         }
136
137         /*
138          *      Create our own random pool.
139          */
140         for (i = 0; i < 256; i++) {
141                 inst->rand_pool.randrsl[i] = fr_rand();
142         }
143         fr_randinit(&inst->rand_pool, 1);
144         inst->rand_pool.randcnt = 0;
145
146         inst->xlat_name = cf_section_name2(cs);
147         if (!inst->xlat_name) inst->xlat_name = "EAP";
148
149         /* Load all the configured EAP-Types */
150         num_types = 0;
151         for(scs=cf_subsection_find_next(cs, NULL, NULL);
152                 scs != NULL;
153                 scs=cf_subsection_find_next(cs, scs, NULL)) {
154
155                 const char      *auth_type;
156
157                 auth_type = cf_section_name1(scs);
158
159                 if (!auth_type)  continue;
160
161                 eap_type = eaptype_name2type(auth_type);
162                 if (eap_type < 0) {
163                         radlog(L_ERR, "rlm_eap: Unknown EAP type %s",
164                                auth_type);
165                         eap_detach(inst);
166                         return -1;
167                 }
168
169 #ifndef HAVE_OPENSSL_SSL_H
170                 /*
171                  *      This allows the default configuration to be
172                  *      shipped with EAP-TLS, etc. enabled.  If the
173                  *      system doesn't have OpenSSL, they will be
174                  *      ignored.
175                  *
176                  *      If the system does have OpenSSL, then this
177                  *      code will not be used.  The administrator will
178                  *      then have to delete the tls,
179                  *      etc. configurations from eap.conf in order to
180                  *      have EAP without the TLS types.
181                  */
182                 if ((eap_type == PW_EAP_TLS) ||
183                     (eap_type == PW_EAP_TTLS) ||
184                     (eap_type == PW_EAP_PEAP)) {
185                         DEBUG2("Ignoring EAP-Type/%s because we do not have OpenSSL support.", auth_type);
186                         continue;
187                 }
188 #endif
189
190                 /*
191                  *      If we're asked to load TTLS or PEAP, ensure
192                  *      that we've first loaded TLS.
193                  */
194                 if (((eap_type == PW_EAP_TTLS) ||
195                      (eap_type == PW_EAP_PEAP)) &&
196                     (inst->types[PW_EAP_TLS] == NULL)) {
197                         radlog(L_ERR, "rlm_eap: Unable to load EAP-Type/%s, as EAP-Type/TLS is required first.",
198                                auth_type);
199                         return -1;
200                 }
201
202                 /*
203                  *      Load the type.
204                  */
205                 if (eaptype_load(&inst->types[eap_type], eap_type, scs) < 0) {
206                         eap_detach(inst);
207                         return -1;
208                 }
209
210                 num_types++;    /* successfully loaded one more types */
211         }
212
213         if (num_types == 0) {
214                 radlog(L_ERR|L_CONS, "rlm_eap: No EAP type configured, module cannot do anything.");
215                 eap_detach(inst);
216                 return -1;
217         }
218
219         /*
220          *      Ensure that the default EAP type is loaded.
221          */
222         eap_type = eaptype_name2type(inst->default_eap_type_name);
223         if (eap_type < 0) {
224                 radlog(L_ERR|L_CONS, "rlm_eap: Unknown default EAP type %s",
225                        inst->default_eap_type_name);
226                 eap_detach(inst);
227                 return -1;
228         }
229
230         if (inst->types[eap_type] == NULL) {
231                 radlog(L_ERR|L_CONS, "rlm_eap: No such sub-type for default EAP type %s",
232                        inst->default_eap_type_name);
233                 eap_detach(inst);
234                 return -1;
235         }
236         inst->default_eap_type = eap_type; /* save the numerical type */
237
238         /*
239          *      List of sessions are set to NULL by the memset
240          *      of 'inst', above.
241          */
242
243         /*
244          *      Lookup sessions in the tree.  We don't free them in
245          *      the tree, as that's taken care of elsewhere...
246          */
247         inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0);
248         if (!inst->session_tree) {
249                 radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree");
250                 eap_detach(inst);
251                 return -1;
252         }
253
254         if (fr_debug_flag) {
255                 inst->handler_tree = rbtree_create(eap_handler_ptr_cmp, NULL, 0);
256                 if (!inst->handler_tree) {
257                         radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree");
258                         eap_detach(inst);
259                         return -1;
260                 }
261
262 #ifdef HAVE_PTHREAD_H
263                 if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) {
264                         radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno));
265                         eap_detach(inst);
266                         return -1;
267                 }
268 #endif
269         }
270
271 #ifdef HAVE_PTHREAD_H
272         if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
273                 radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno));
274                 eap_detach(inst);
275                 return -1;
276         }
277 #endif
278
279         *instance = inst;
280         return 0;
281 }
282
283
284 /*
285  *      For backwards compatibility.
286  */
287 static int eap_authenticate(void *instance, REQUEST *request)
288 {
289         rlm_eap_t       *inst;
290         EAP_HANDLER     *handler;
291         eap_packet_t    *eap_packet;
292         int             rcode;
293
294         inst = (rlm_eap_t *) instance;
295
296         if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0)) {
297                 RDEBUG("ERROR: You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!");
298                 return RLM_MODULE_INVALID;
299         }
300
301         /*
302          *      Get the eap packet  to start with
303          */
304         eap_packet = eap_vp2packet(request->packet->vps);
305         if (eap_packet == NULL) {
306                 radlog_request(L_ERR, 0, request, "Malformed EAP Message");
307                 return RLM_MODULE_FAIL;
308         }
309
310         /*
311          *      Create the eap handler.  The eap_packet will end up being
312          *      "swallowed" into the handler, so we can't access it after
313          *      this call.
314          */
315         handler = eap_handler(inst, &eap_packet, request);
316         if (handler == NULL) {
317                 RDEBUG2("Failed in handler");
318                 return RLM_MODULE_INVALID;
319         }
320
321         /*
322          *      Select the appropriate eap_type or default to the
323          *      configured one
324          */
325         rcode = eaptype_select(inst, handler);
326
327         /*
328          *      If it failed, die.
329          */
330         if (rcode == EAP_INVALID) {
331                 eap_fail(handler);
332                 eap_handler_free(inst, handler);
333                 RDEBUG2("Failed in EAP select");
334                 return RLM_MODULE_INVALID;
335         }
336
337 #ifdef WITH_PROXY
338         /*
339          *      If we're doing horrible tunneling work, remember it.
340          */
341         if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
342                 RDEBUG2("  Not-EAP proxy set.  Not composing EAP");
343                 /*
344                  *      Add the handle to the proxied list, so that we
345                  *      can retrieve it in the post-proxy stage, and
346                  *      send a response.
347                  */
348                 rcode = request_data_add(request,
349                                          inst, REQUEST_DATA_EAP_HANDLER,
350                                          handler,
351                                          (void *) eap_handler_free);
352                 rad_assert(rcode == 0);
353
354                 return RLM_MODULE_HANDLED;
355         }
356 #endif
357
358 #ifdef WITH_PROXY
359         /*
360          *      Maybe the request was marked to be proxied.  If so,
361          *      proxy it.
362          */
363         if (request->proxy != NULL) {
364                 VALUE_PAIR *vp = NULL;
365
366                 rad_assert(request->proxy_reply == NULL);
367
368                 /*
369                  *      Add the handle to the proxied list, so that we
370                  *      can retrieve it in the post-proxy stage, and
371                  *      send a response.
372                  */
373                 rcode = request_data_add(request,
374                                          inst, REQUEST_DATA_EAP_HANDLER,
375                                          handler,
376                                          (void *) eap_handler_free);
377                 rad_assert(rcode == 0);
378
379                 /*
380                  *      Some simple sanity checks.  These should really
381                  *      be handled by the radius library...
382                  */
383                 vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE, 0);
384                 if (vp) {
385                         vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0);
386                         if (!vp) {
387                                 vp = pairmake("Message-Authenticator",
388                                               "0x00", T_OP_EQ);
389                                 rad_assert(vp != NULL);
390                                 pairadd(&(request->proxy->vps), vp);
391                         }
392                 }
393
394                 /*
395                  *      Delete the "proxied to" attribute, as it's
396                  *      set to 127.0.0.1 for tunneled requests, and
397                  *      we don't want to tell the world that...
398                  */
399                 pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS);
400
401                 RDEBUG2("  Tunneled session will be proxied.  Not doing EAP.");
402                 return RLM_MODULE_HANDLED;
403         }
404 #endif
405
406         /*
407          *      We are done, wrap the EAP-request in RADIUS to send
408          *      with all other required radius attributes
409          */
410         rcode = eap_compose(handler);
411
412         /*
413          *      Add to the list only if it is EAP-Request, OR if
414          *      it's LEAP, and a response.
415          */
416         if (((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
417             (handler->eap_ds->request->type.type >= PW_EAP_MD5)) ||
418
419                 /*
420                  *      LEAP is a little different.  At Stage 4,
421                  *      it sends an EAP-Success message, but we still
422                  *      need to keep the State attribute & session
423                  *      data structure around for the AP Challenge.
424                  *
425                  *      At stage 6, LEAP sends an EAP-Response, which
426                  *      isn't put into the list.
427                  */
428             ((handler->eap_ds->response->code == PW_EAP_RESPONSE) &&
429              (handler->eap_ds->response->type.type == PW_EAP_LEAP) &&
430              (handler->eap_ds->request->code == PW_EAP_SUCCESS) &&
431              (handler->eap_ds->request->type.type == 0))) {
432
433                 /*
434                  *      Return FAIL if we can't remember the handler.
435                  *      This is actually disallowed by the
436                  *      specification, as unexpected FAILs could have
437                  *      been forged.  However, we want to signal to
438                  *      everyone else involved that we are
439                  *      intentionally failing the session, as opposed
440                  *      to accidentally failing it.
441                  */
442                 if (!eaplist_add(inst, handler)) {
443                         eap_fail(handler);
444                         eap_handler_free(inst, handler);
445                         return RLM_MODULE_FAIL;
446                 }
447
448         } else {
449                 RDEBUG2("Freeing handler");
450                 /* handler is not required any more, free it now */
451                 eap_handler_free(inst, handler);
452         }
453
454         /*
455          *      If it's an Access-Accept, RFC 2869, Section 2.3.1
456          *      says that we MUST include a User-Name attribute in the
457          *      Access-Accept.
458          */
459         if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
460             request->username) {
461                 VALUE_PAIR *vp;
462
463                 /*
464                  *      Doesn't exist, add it in.
465                  */
466                 vp = pairfind(request->reply->vps, PW_USER_NAME, 0);
467                 if (!vp) {
468                         vp = pairmake("User-Name", "",
469                                       T_OP_EQ);
470                         strlcpy(vp->vp_strvalue, request->username->vp_strvalue,
471                                 sizeof(vp->vp_strvalue));
472                         vp->length = request->username->length;
473                         rad_assert(vp != NULL);
474                         pairadd(&(request->reply->vps), vp);
475                 }
476
477                 /*
478                  *      Cisco AP1230 has a bug and needs a zero
479                  *      terminated string in Access-Accept.
480                  */
481                 if ((inst->cisco_accounting_username_bug) &&
482                     (vp->length < (int) sizeof(vp->vp_strvalue))) {
483                         vp->vp_strvalue[vp->length] = '\0';
484                         vp->length++;
485                 }
486         }
487
488         return rcode;
489 }
490
491 /*
492  * EAP authorization DEPENDS on other rlm authorizations,
493  * to check for user existance & get their configured values.
494  * It Handles EAP-START Messages, User-Name initilization.
495  */
496 static int eap_authorize(void *instance, REQUEST *request)
497 {
498         rlm_eap_t       *inst;
499         int             status;
500         VALUE_PAIR      *vp;
501
502         inst = (rlm_eap_t *)instance;
503
504 #ifdef WITH_PROXY
505         /*
506          *      We don't do authorization again, once we've seen the
507          *      proxy reply (or the proxied packet)
508          */
509         if (request->proxy != NULL)
510                 return RLM_MODULE_NOOP;
511 #endif
512
513         /*
514          *      For EAP_START, send Access-Challenge with EAP Identity
515          *      request.  even when we have to proxy this request
516          *
517          *      RFC 2869, Section 2.3.1 notes that the "domain" of the
518          *      user, (i.e. where to proxy him) comes from the EAP-Identity,
519          *      so we CANNOT proxy the user, until we know his identity.
520          *
521          *      We therefore send an EAP Identity request.
522          */
523         status = eap_start(inst, request);
524         switch(status) {
525         case EAP_NOOP:
526                 return RLM_MODULE_NOOP;
527         case EAP_FAIL:
528                 return RLM_MODULE_FAIL;
529         case EAP_FOUND:
530                 return RLM_MODULE_HANDLED;
531         case EAP_OK:
532         case EAP_NOTFOUND:
533         default:
534                 break;
535         }
536
537         /*
538          *      RFC 2869, Section 2.3.1.  If a NAS sends an EAP-Identity,
539          *      it MUST copy the identity into the User-Name attribute.
540          *
541          *      But we don't worry about that too much.  We depend on
542          *      each EAP sub-module to look for handler->request->username,
543          *      and to get excited if it doesn't appear.
544          */
545
546         vp = pairfind(request->config_items, PW_AUTH_TYPE, 0);
547         if ((!vp) ||
548             (vp->vp_integer != PW_AUTHTYPE_REJECT)) {
549                 vp = pairmake("Auth-Type", inst->xlat_name, T_OP_EQ);
550                 if (!vp) {
551                         RDEBUG2("Failed to create Auth-Type %s: %s\n",
552                                 inst->xlat_name, fr_strerror());
553                         return RLM_MODULE_FAIL;
554                 }
555                 pairadd(&request->config_items, vp);
556         } else {
557                 RDEBUG2("WARNING: Auth-Type already set.  Not setting to EAP");
558         }
559
560         if (status == EAP_OK) return RLM_MODULE_OK;
561
562         return RLM_MODULE_UPDATED;
563 }
564
565
566 #ifdef WITH_PROXY
567 /*
568  *      If we're proxying EAP, then there may be magic we need
569  *      to do.
570  */
571 static int eap_post_proxy(void *inst, REQUEST *request)
572 {
573         size_t          i;
574         size_t          len;
575         VALUE_PAIR      *vp;
576         EAP_HANDLER     *handler;
577
578         /*
579          *      Just in case the admin lists EAP in post-proxy-type Fail.
580          */
581         if (!request->proxy_reply) return RLM_MODULE_NOOP;
582
583         /*
584          *      If there was a handler associated with this request,
585          *      then it's a tunneled request which was proxied...
586          */
587         handler = request_data_get(request, inst, REQUEST_DATA_EAP_HANDLER);
588         if (handler != NULL) {
589                 int             rcode;
590                 eap_tunnel_data_t *data;
591
592                 /*
593                  *      Grab the tunnel callbacks from the request.
594                  */
595                 data = (eap_tunnel_data_t *) request_data_get(request,
596                                                               request->proxy,
597                                                               REQUEST_DATA_EAP_TUNNEL_CALLBACK);
598                 if (!data) {
599                         radlog_request(L_ERR, 0, request, "Failed to retrieve callback for tunneled session!");
600                         eap_handler_free(inst, handler);
601                         return RLM_MODULE_FAIL;
602                 }
603
604                 /*
605                  *      Do the callback...
606                  */
607                 RDEBUG2("Doing post-proxy callback");
608                 rcode = data->callback(handler, data->tls_session);
609                 free(data);
610                 if (rcode == 0) {
611                         RDEBUG2("Failed in post-proxy callback");
612                         eap_fail(handler);
613                         eap_handler_free(inst, handler);
614                         return RLM_MODULE_REJECT;
615                 }
616
617                 /*
618                  *      We are done, wrap the EAP-request in RADIUS to send
619                  *      with all other required radius attributes
620                  */
621                 eap_compose(handler);
622
623                 /*
624                  *      Add to the list only if it is EAP-Request, OR if
625                  *      it's LEAP, and a response.
626                  */
627                 if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
628                     (handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
629                         if (!eaplist_add(inst, handler)) {
630                                 eap_fail(handler);
631                                 eap_handler_free(inst, handler);
632                                 return RLM_MODULE_FAIL;
633                         }
634                         
635                 } else {        /* couldn't have been LEAP, there's no tunnel */
636                         RDEBUG2("Freeing handler");
637                         /* handler is not required any more, free it now */
638                         eap_handler_free(inst, handler);
639                 }
640
641                 /*
642                  *      If it's an Access-Accept, RFC 2869, Section 2.3.1
643                  *      says that we MUST include a User-Name attribute in the
644                  *      Access-Accept.
645                  */
646                 if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
647                     request->username) {
648                         /*
649                          *      Doesn't exist, add it in.
650                          */
651                         vp = pairfind(request->reply->vps, PW_USER_NAME, 0);
652                         if (!vp) {
653                                 vp = pairmake("User-Name", request->username->vp_strvalue,
654                                               T_OP_EQ);
655                                 rad_assert(vp != NULL);
656                                 pairadd(&(request->reply->vps), vp);
657                         }
658                 }
659
660                 return RLM_MODULE_OK;
661         } else {
662                 RDEBUG2("No pre-existing handler found");
663         }
664
665         /*
666          *      There may be more than one Cisco-AVPair.
667          *      Ensure we find the one with the LEAP attribute.
668          */
669         vp = request->proxy_reply->vps;
670         for (;;) {
671                 /*
672                  *      Hmm... there's got to be a better way to
673                  *      discover codes for vendor attributes.
674                  *
675                  *      This is vendor Cisco (9), Cisco-AVPair
676                  *      attribute (1)
677                  */
678                 vp = pairfind(vp, 1, 9);
679                 if (!vp) {
680                         return RLM_MODULE_NOOP;
681                 }
682
683                 /*
684                  *      If it's "leap:session-key", then stop.
685                  *
686                  *      The format is VERY specific!
687                  */
688                 if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
689                         break;
690                 }
691
692                 /*
693                  *      Not this AV-pair.  Go to the next one.
694                  */
695                 vp = vp->next;
696         }
697
698         /*
699          *      The format is very specific.
700          */
701         if (vp->length != 17 + 34) {
702                 RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
703                        vp->length, 17 + 34);
704                 return RLM_MODULE_NOOP;
705         }
706
707         /*
708          *      Decrypt the session key, using the proxy data.
709          */
710         i = 34;                 /* starts off with 34 octets */
711         len = rad_tunnel_pwdecode(vp->vp_octets + 17, &i,
712                                   request->home_server->secret,
713                                   request->proxy->vector);
714
715         /*
716          *      FIXME: Assert that i == 16.
717          */
718
719         /*
720          *      Encrypt the session key again, using the request data.
721          */
722         rad_tunnel_pwencode(vp->vp_strvalue + 17, &len,
723                             request->client->secret,
724                             request->packet->vector);
725
726         return RLM_MODULE_UPDATED;
727 }
728 #endif
729
730 /*
731  *      The module name should be the only globally exported symbol.
732  *      That is, everything else should be 'static'.
733  */
734 module_t rlm_eap = {
735         RLM_MODULE_INIT,
736         "eap",
737         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
738         eap_instantiate,                /* instantiation */
739         eap_detach,                     /* detach */
740         {
741                 eap_authenticate,       /* authentication */
742                 eap_authorize,          /* authorization */
743                 NULL,                   /* preaccounting */
744                 NULL,                   /* accounting */
745                 NULL,                   /* checksimul */
746                 NULL,                   /* pre-proxy */
747 #ifdef WITH_PROXY
748                 eap_post_proxy,         /* post-proxy */
749 #else
750                 NULL,
751 #endif
752                 NULL                    /* post-auth */
753         },
754 };