Merge branch 'master' of github.com:FreeRADIUS/freeradius-server
[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         if (a < b) return -1;
114         if (a > b) return +1;
115         return 0;
116 }
117
118
119 /*
120  * read the config section and load all the eap authentication types present.
121  */
122 static int eap_instantiate(CONF_SECTION *cs, void **instance)
123 {
124         int             i, eap_type;
125         int             num_types;
126         CONF_SECTION    *scs;
127         rlm_eap_t       *inst;
128
129         inst = (rlm_eap_t *) malloc(sizeof(*inst));
130         if (!inst) {
131                 return -1;
132         }
133         memset(inst, 0, sizeof(*inst));
134         if (cf_section_parse(cs, inst, module_config) < 0) {
135                 eap_detach(inst);
136                 return -1;
137         }
138
139         /*
140          *      Create our own random pool.
141          */
142         for (i = 0; i < 256; i++) {
143                 inst->rand_pool.randrsl[i] = fr_rand();
144         }
145         fr_randinit(&inst->rand_pool, 1);
146         inst->rand_pool.randcnt = 0;
147
148         inst->xlat_name = cf_section_name2(cs);
149         if (!inst->xlat_name) inst->xlat_name = "EAP";
150
151         /* Load all the configured EAP-Types */
152         num_types = 0;
153         for(scs=cf_subsection_find_next(cs, NULL, NULL);
154                 scs != NULL;
155                 scs=cf_subsection_find_next(cs, scs, NULL)) {
156
157                 const char      *auth_type;
158
159                 auth_type = cf_section_name1(scs);
160
161                 if (!auth_type)  continue;
162
163                 if (!strcmp(auth_type, TLS_CONFIG_SECTION))  continue;
164
165                 eap_type = eaptype_name2type(auth_type);
166                 if (eap_type < 0) {
167                         radlog(L_ERR, "rlm_eap: Unknown EAP type %s",
168                                auth_type);
169                         eap_detach(inst);
170                         return -1;
171                 }
172
173 #ifndef HAVE_OPENSSL_SSL_H
174                 /*
175                  *      This allows the default configuration to be
176                  *      shipped with EAP-TLS, etc. enabled.  If the
177                  *      system doesn't have OpenSSL, they will be
178                  *      ignored.
179                  *
180                  *      If the system does have OpenSSL, then this
181                  *      code will not be used.  The administrator will
182                  *      then have to delete the tls,
183                  *      etc. configurations from eap.conf in order to
184                  *      have EAP without the TLS types.
185                  */
186                 if ((eap_type == PW_EAP_TLS) ||
187                     (eap_type == PW_EAP_TTLS) ||
188                     (eap_type == PW_EAP_PEAP)) {
189                         DEBUG2("Ignoring EAP-Type/%s because we do not have OpenSSL support.", auth_type);
190                         continue;
191                 }
192 #endif
193
194                 /*
195                  *      Load the type.
196                  */
197                 if (eaptype_load(&inst->types[eap_type], eap_type, scs) < 0) {
198                         eap_detach(inst);
199                         return -1;
200                 }
201
202                 num_types++;    /* successfully loaded one more types */
203         }
204
205         if (num_types == 0) {
206                 radlog(L_ERR|L_CONS, "rlm_eap: No EAP type configured, module cannot do anything.");
207                 eap_detach(inst);
208                 return -1;
209         }
210
211         /*
212          *      Ensure that the default EAP type is loaded.
213          */
214         eap_type = eaptype_name2type(inst->default_eap_type_name);
215         if (eap_type < 0) {
216                 radlog(L_ERR|L_CONS, "rlm_eap: Unknown default EAP type %s",
217                        inst->default_eap_type_name);
218                 eap_detach(inst);
219                 return -1;
220         }
221
222         if (inst->types[eap_type] == NULL) {
223                 radlog(L_ERR|L_CONS, "rlm_eap: No such sub-type for default EAP type %s",
224                        inst->default_eap_type_name);
225                 eap_detach(inst);
226                 return -1;
227         }
228         inst->default_eap_type = eap_type; /* save the numerical type */
229
230         /*
231          *      List of sessions are set to NULL by the memset
232          *      of 'inst', above.
233          */
234
235         /*
236          *      Lookup sessions in the tree.  We don't free them in
237          *      the tree, as that's taken care of elsewhere...
238          */
239         inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0);
240         if (!inst->session_tree) {
241                 radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree");
242                 eap_detach(inst);
243                 return -1;
244         }
245
246         if (fr_debug_flag) {
247                 inst->handler_tree = rbtree_create(eap_handler_ptr_cmp, NULL, 0);
248                 if (!inst->handler_tree) {
249                         radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree");
250                         eap_detach(inst);
251                         return -1;
252                 }
253
254 #ifdef HAVE_PTHREAD_H
255                 if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) {
256                         radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno));
257                         eap_detach(inst);
258                         return -1;
259                 }
260 #endif
261         }
262
263 #ifdef HAVE_PTHREAD_H
264         if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
265                 radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno));
266                 eap_detach(inst);
267                 return -1;
268         }
269 #endif
270
271         *instance = inst;
272         return 0;
273 }
274
275
276 /*
277  *      For backwards compatibility.
278  */
279 static int eap_authenticate(void *instance, REQUEST *request)
280 {
281         rlm_eap_t       *inst;
282         EAP_HANDLER     *handler;
283         eap_packet_t    *eap_packet;
284         int             rcode;
285
286         inst = (rlm_eap_t *) instance;
287
288         if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
289                 RDEBUG("ERROR: You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!");
290                 return RLM_MODULE_INVALID;
291         }
292
293         /*
294          *      Get the eap packet  to start with
295          */
296         eap_packet = eap_vp2packet(request->packet->vps);
297         if (eap_packet == NULL) {
298                 radlog_request(L_ERR, 0, request, "Malformed EAP Message");
299                 return RLM_MODULE_FAIL;
300         }
301
302         /*
303          *      Create the eap handler.  The eap_packet will end up being
304          *      "swallowed" into the handler, so we can't access it after
305          *      this call.
306          */
307         handler = eap_handler(inst, &eap_packet, request);
308         if (handler == NULL) {
309                 RDEBUG2("Failed in handler");
310                 return RLM_MODULE_INVALID;
311         }
312
313         /*
314          *      Select the appropriate eap_type or default to the
315          *      configured one
316          */
317         rcode = eaptype_select(inst, handler);
318
319         /*
320          *      If it failed, die.
321          */
322         if (rcode == EAP_INVALID) {
323                 eap_fail(handler);
324                 eap_handler_free(inst, handler);
325                 RDEBUG2("Failed in EAP select");
326                 return RLM_MODULE_INVALID;
327         }
328
329 #ifdef WITH_PROXY
330         /*
331          *      If we're doing horrible tunneling work, remember it.
332          */
333         if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
334                 RDEBUG2("  Not-EAP proxy set.  Not composing EAP");
335                 /*
336                  *      Add the handle to the proxied list, so that we
337                  *      can retrieve it in the post-proxy stage, and
338                  *      send a response.
339                  */
340                 handler->inst_holder = inst;
341                 rcode = request_data_add(request,
342                                          inst, REQUEST_DATA_EAP_HANDLER,
343                                          handler,
344                                          (void *) eap_opaque_free);
345                 rad_assert(rcode == 0);
346
347                 return RLM_MODULE_HANDLED;
348         }
349 #endif
350
351 #ifdef WITH_PROXY
352         /*
353          *      Maybe the request was marked to be proxied.  If so,
354          *      proxy it.
355          */
356         if (request->proxy != NULL) {
357                 VALUE_PAIR *vp = NULL;
358
359                 rad_assert(request->proxy_reply == NULL);
360
361                 /*
362                  *      Add the handle to the proxied list, so that we
363                  *      can retrieve it in the post-proxy stage, and
364                  *      send a response.
365                  */
366                 handler->inst_holder = inst;
367                 rcode = request_data_add(request,
368                                          inst, REQUEST_DATA_EAP_HANDLER,
369                                          handler,
370                                          (void *) eap_opaque_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, TAG_ANY);
378                 if (vp) {
379                         vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
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, TAG_ANY);
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                         RDEBUG("Failed adding handler to the list");
438                         eap_fail(handler);
439                         eap_handler_free(inst, handler);
440                         return RLM_MODULE_FAIL;
441                 }
442
443         } else {
444                 RDEBUG2("Freeing handler");
445                 /* handler is not required any more, free it now */
446                 eap_handler_free(inst, handler);
447         }
448
449         /*
450          *      If it's an Access-Accept, RFC 2869, Section 2.3.1
451          *      says that we MUST include a User-Name attribute in the
452          *      Access-Accept.
453          */
454         if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
455             request->username) {
456                 VALUE_PAIR *vp;
457
458                 /*
459                  *      Doesn't exist, add it in.
460                  */
461                 vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
462                 if (!vp) {
463                         vp = pairmake("User-Name", "",
464                                       T_OP_EQ);
465                         strlcpy(vp->vp_strvalue, request->username->vp_strvalue,
466                                 sizeof(vp->vp_strvalue));
467                         vp->length = request->username->length;
468                         rad_assert(vp != NULL);
469                         pairadd(&(request->reply->vps), vp);
470                 }
471
472                 /*
473                  *      Cisco AP1230 has a bug and needs a zero
474                  *      terminated string in Access-Accept.
475                  */
476                 if ((inst->cisco_accounting_username_bug) &&
477                     (vp->length < (int) sizeof(vp->vp_strvalue))) {
478                         vp->vp_strvalue[vp->length] = '\0';
479                         vp->length++;
480                 }
481         }
482
483         return rcode;
484 }
485
486 /*
487  * EAP authorization DEPENDS on other rlm authorizations,
488  * to check for user existance & get their configured values.
489  * It Handles EAP-START Messages, User-Name initilization.
490  */
491 static int eap_authorize(void *instance, REQUEST *request)
492 {
493         rlm_eap_t       *inst;
494         int             status;
495         VALUE_PAIR      *vp;
496
497         inst = (rlm_eap_t *)instance;
498
499 #ifdef WITH_PROXY
500         /*
501          *      We don't do authorization again, once we've seen the
502          *      proxy reply (or the proxied packet)
503          */
504         if (request->proxy != NULL)
505                 return RLM_MODULE_NOOP;
506 #endif
507
508         /*
509          *      For EAP_START, send Access-Challenge with EAP Identity
510          *      request.  even when we have to proxy this request
511          *
512          *      RFC 2869, Section 2.3.1 notes that the "domain" of the
513          *      user, (i.e. where to proxy him) comes from the EAP-Identity,
514          *      so we CANNOT proxy the user, until we know his identity.
515          *
516          *      We therefore send an EAP Identity request.
517          */
518         status = eap_start(inst, request);
519         switch(status) {
520         case EAP_NOOP:
521                 return RLM_MODULE_NOOP;
522         case EAP_FAIL:
523                 return RLM_MODULE_FAIL;
524         case EAP_FOUND:
525                 return RLM_MODULE_HANDLED;
526         case EAP_OK:
527         case EAP_NOTFOUND:
528         default:
529                 break;
530         }
531
532         /*
533          *      RFC 2869, Section 2.3.1.  If a NAS sends an EAP-Identity,
534          *      it MUST copy the identity into the User-Name attribute.
535          *
536          *      But we don't worry about that too much.  We depend on
537          *      each EAP sub-module to look for handler->request->username,
538          *      and to get excited if it doesn't appear.
539          */
540
541         vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
542         if ((!vp) ||
543             (vp->vp_integer != PW_AUTHTYPE_REJECT)) {
544                 vp = pairmake("Auth-Type", inst->xlat_name, T_OP_EQ);
545                 if (!vp) {
546                         RDEBUG2("Failed to create Auth-Type %s: %s\n",
547                                 inst->xlat_name, fr_strerror());
548                         return RLM_MODULE_FAIL;
549                 }
550                 pairadd(&request->config_items, vp);
551         } else {
552                 RDEBUG2("WARNING: Auth-Type already set.  Not setting to EAP");
553         }
554
555         if (status == EAP_OK) return RLM_MODULE_OK;
556
557         return RLM_MODULE_UPDATED;
558 }
559
560
561 #ifdef WITH_PROXY
562 /*
563  *      If we're proxying EAP, then there may be magic we need
564  *      to do.
565  */
566 static int eap_post_proxy(void *inst, REQUEST *request)
567 {
568         size_t          i;
569         size_t          len;
570         VALUE_PAIR      *vp;
571         EAP_HANDLER     *handler;
572
573         /*
574          *      Just in case the admin lists EAP in post-proxy-type Fail.
575          */
576         if (!request->proxy_reply) return RLM_MODULE_NOOP;
577
578         /*
579          *      If there was a handler associated with this request,
580          *      then it's a tunneled request which was proxied...
581          */
582         handler = request_data_get(request, inst, REQUEST_DATA_EAP_HANDLER);
583         if (handler != NULL) {
584                 int             rcode;
585                 eap_tunnel_data_t *data;
586
587                 /*
588                  *      Grab the tunnel callbacks from the request.
589                  */
590                 data = (eap_tunnel_data_t *) request_data_get(request,
591                                                               request->proxy,
592                                                               REQUEST_DATA_EAP_TUNNEL_CALLBACK);
593                 if (!data) {
594                         radlog_request(L_ERR, 0, request, "Failed to retrieve callback for tunneled session!");
595                         eap_handler_free(inst, handler);
596                         return RLM_MODULE_FAIL;
597                 }
598
599                 /*
600                  *      Do the callback...
601                  */
602                 RDEBUG2("Doing post-proxy callback");
603                 rcode = data->callback(handler, data->tls_session);
604                 free(data);
605                 if (rcode == 0) {
606                         RDEBUG2("Failed in post-proxy callback");
607                         eap_fail(handler);
608                         eap_handler_free(inst, handler);
609                         return RLM_MODULE_REJECT;
610                 }
611
612                 /*
613                  *      We are done, wrap the EAP-request in RADIUS to send
614                  *      with all other required radius attributes
615                  */
616                 eap_compose(handler);
617
618                 /*
619                  *      Add to the list only if it is EAP-Request, OR if
620                  *      it's LEAP, and a response.
621                  */
622                 if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
623                     (handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
624                         if (!eaplist_add(inst, handler)) {
625                                 eap_fail(handler);
626                                 eap_handler_free(inst, handler);
627                                 return RLM_MODULE_FAIL;
628                         }
629                         
630                 } else {        /* couldn't have been LEAP, there's no tunnel */
631                         RDEBUG2("Freeing handler");
632                         /* handler is not required any more, free it now */
633                         eap_handler_free(inst, handler);
634                 }
635
636                 /*
637                  *      If it's an Access-Accept, RFC 2869, Section 2.3.1
638                  *      says that we MUST include a User-Name attribute in the
639                  *      Access-Accept.
640                  */
641                 if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
642                     request->username) {
643                         /*
644                          *      Doesn't exist, add it in.
645                          */
646                         vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
647                         if (!vp) {
648                                 vp = pairmake("User-Name", request->username->vp_strvalue,
649                                               T_OP_EQ);
650                                 rad_assert(vp != NULL);
651                                 pairadd(&(request->reply->vps), vp);
652                         }
653                 }
654
655                 return RLM_MODULE_OK;
656         } else {
657                 RDEBUG2("No pre-existing handler found");
658         }
659
660         /*
661          *      There may be more than one Cisco-AVPair.
662          *      Ensure we find the one with the LEAP attribute.
663          */
664         vp = request->proxy_reply->vps;
665         for (;;) {
666                 /*
667                  *      Hmm... there's got to be a better way to
668                  *      discover codes for vendor attributes.
669                  *
670                  *      This is vendor Cisco (9), Cisco-AVPair
671                  *      attribute (1)
672                  */
673                 vp = pairfind(vp, 1, 9, TAG_ANY);
674                 if (!vp) {
675                         return RLM_MODULE_NOOP;
676                 }
677
678                 /*
679                  *      If it's "leap:session-key", then stop.
680                  *
681                  *      The format is VERY specific!
682                  */
683                 if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
684                         break;
685                 }
686
687                 /*
688                  *      Not this AV-pair.  Go to the next one.
689                  */
690                 vp = vp->next;
691         }
692
693         /*
694          *      The format is very specific.
695          */
696         if (vp->length != 17 + 34) {
697                 RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
698                        vp->length, 17 + 34);
699                 return RLM_MODULE_NOOP;
700         }
701
702         /*
703          *      Decrypt the session key, using the proxy data.
704          */
705         i = 34;                 /* starts off with 34 octets */
706         len = rad_tunnel_pwdecode(vp->vp_octets + 17, &i,
707                                   request->home_server->secret,
708                                   request->proxy->vector);
709
710         /*
711          *      FIXME: Assert that i == 16.
712          */
713
714         /*
715          *      Encrypt the session key again, using the request data.
716          */
717         rad_tunnel_pwencode(vp->vp_strvalue + 17, &len,
718                             request->client->secret,
719                             request->packet->vector);
720
721         return RLM_MODULE_UPDATED;
722 }
723 #endif
724
725 static int eap_post_auth(void *instance, REQUEST *request)
726 {
727         rlm_eap_t       *inst = instance;
728         VALUE_PAIR      *vp;
729         EAP_HANDLER     *handler;
730         eap_packet_t    *eap_packet;
731         
732         /*
733          * Only build a failure message if something previously rejected the request
734          */
735         vp = pairfind(request->config_items, PW_POSTAUTHTYPE, 0, TAG_ANY);
736
737         if (!vp || (vp->vp_integer != PW_POSTAUTHTYPE_REJECT)) return RLM_MODULE_NOOP;
738         
739         if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
740                 RDEBUG2("Request didn't contain an EAP-Message, not inserting EAP-Failure");
741                 return RLM_MODULE_NOOP;
742         }
743         
744         if (pairfind(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
745                 RDEBUG2("Reply already contained an EAP-Message, not inserting EAP-Failure");
746                 return RLM_MODULE_NOOP;
747         }
748         
749         eap_packet = eap_vp2packet(request->packet->vps);
750         if (eap_packet == NULL) {
751                 radlog_request(L_ERR, 0, request, "Malformed EAP Message");
752                 return RLM_MODULE_FAIL;
753         }
754
755         handler = eap_handler(inst, &eap_packet, request);
756         if (handler == NULL) {
757                 RDEBUG2("Failed to get handler, probably already removed, not inserting EAP-Failure");
758                 return RLM_MODULE_NOOP;
759         }
760
761         RDEBUG2("Request was previously rejected, inserting EAP-Failure");
762         eap_fail(handler);
763         eap_handler_free(inst, handler);
764         
765         /*
766          * Make sure there's a message authenticator attribute in the response
767          * RADIUS protocol code will calculate the correct value later...
768          */
769         vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
770         if (!vp) {
771                 vp = pairmake("Message-Authenticator",
772                                   "0x00", T_OP_EQ);
773                 rad_assert(vp != NULL);
774                 pairadd(&(request->reply->vps), vp);
775         }
776
777         return RLM_MODULE_UPDATED;
778 }
779
780 /*
781  *      The module name should be the only globally exported symbol.
782  *      That is, everything else should be 'static'.
783  */
784 module_t rlm_eap = {
785         RLM_MODULE_INIT,
786         "eap",
787         RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
788         eap_instantiate,                /* instantiation */
789         eap_detach,                     /* detach */
790         {
791                 eap_authenticate,       /* authentication */
792                 eap_authorize,          /* authorization */
793                 NULL,                   /* preaccounting */
794                 NULL,                   /* accounting */
795                 NULL,                   /* checksimul */
796                 NULL,                   /* pre-proxy */
797 #ifdef WITH_PROXY
798                 eap_post_proxy,         /* post-proxy */
799 #else
800                 NULL,
801 #endif
802                 eap_post_auth           /* post-auth */
803         },
804 };