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