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