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