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