Polled from branch_1_1 fix for bug #348
[freeradius.git] / src / modules / rlm_eap / rlm_eap.c
1 /*
2  * rlm_eap.c  contains handles that are called from modules.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2003  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  * Copyright 2003  Alan DeKok <aland@freeradius.org>
23  */
24
25 #include <freeradius-devel/autoconf.h>
26 #include "rlm_eap.h"
27 #include <freeradius-devel/modules.h>
28
29 static const char rcsid[] = "$Id$";
30
31 static const CONF_PARSER module_config[] = {
32         { "default_eap_type", PW_TYPE_STRING_PTR,
33           offsetof(rlm_eap_t, default_eap_type_name), NULL, "md5" },
34         { "timer_expire", PW_TYPE_INTEGER,
35           offsetof(rlm_eap_t, timer_limit), NULL, "60"},
36         { "ignore_unknown_eap_types", PW_TYPE_BOOLEAN,
37           offsetof(rlm_eap_t, ignore_unknown_eap_types), NULL, "no" },
38         { "cisco_accounting_username_bug", PW_TYPE_BOOLEAN,
39           offsetof(rlm_eap_t, cisco_accounting_username_bug), NULL, "no" },
40
41         { NULL, -1, 0, NULL, NULL }           /* end the list */
42 };
43
44 /*
45  * delete all the allocated space by eap module
46  */
47 static int eap_detach(void *instance)
48 {
49         rlm_eap_t *inst;
50         int i;
51
52         inst = (rlm_eap_t *)instance;
53
54         rbtree_free(inst->session_tree);
55         inst->session_tree = NULL;
56         eaplist_free(inst);
57
58         for (i = 0; i < PW_EAP_MAX_TYPES; i++) {
59                 if (inst->types[i]) eaptype_free(inst->types[i]);
60                 inst->types[i] = NULL;
61         }
62
63         pthread_mutex_destroy(&(inst->session_mutex));
64
65         if (inst->default_eap_type_name) free(inst->default_eap_type_name);
66         free(inst);
67
68         return 0;
69 }
70
71
72 /*
73  *      Compare two handlers.
74  */
75 static int eap_handler_cmp(const void *a, const void *b)
76 {
77         int rcode;
78         const EAP_HANDLER *one = a;
79         const EAP_HANDLER *two = b;
80
81
82         if (one->src_ipaddr.af < two->src_ipaddr.af) return -1;
83         if (one->src_ipaddr.af > two->src_ipaddr.af) return +1;
84
85         if (one->eap_id < two->eap_id) return -1;
86         if (one->eap_id > two->eap_id) return +1;
87
88         switch (one->src_ipaddr.af) {
89         case AF_INET:
90                 rcode = memcmp(&one->src_ipaddr.ipaddr.ip4addr,
91                                &two->src_ipaddr.ipaddr.ip4addr,
92                                sizeof(one->src_ipaddr.ipaddr.ip4addr));
93                 break;
94         case AF_INET6:
95                 rcode = memcmp(&one->src_ipaddr.ipaddr.ip6addr,
96                                &two->src_ipaddr.ipaddr.ip6addr,
97                                sizeof(one->src_ipaddr.ipaddr.ip6addr));
98                 break;
99         default:
100                 return -1;      /* FIXME: die! */
101                 break;
102         }
103         /*
104          *      We could optimize this away, but the compiler should
105          *      do that work for us, and this coding style helps us
106          *      remember what to do if we add more checks later.
107          */
108         if (rcode != 0) return rcode;
109
110         return memcmp(one->state, two->state, sizeof(one->state));
111 }
112
113
114 /*
115  * read the config section and load all the eap authentication types present.
116  */
117 static int eap_instantiate(CONF_SECTION *cs, void **instance)
118 {
119         int             eap_type;
120         int             num_types;
121         CONF_SECTION    *scs;
122         rlm_eap_t       *inst;
123
124         inst = (rlm_eap_t *) malloc(sizeof(*inst));
125         if (!inst) {
126                 return -1;
127         }
128         memset(inst, 0, sizeof(*inst));
129         if (cf_section_parse(cs, inst, module_config) < 0) {
130                 eap_detach(inst);
131                 return -1;
132         }
133
134         /* Load all the configured EAP-Types */
135         num_types = 0;
136         for(scs=cf_subsection_find_next(cs, NULL, NULL);
137                 scs != NULL;
138                 scs=cf_subsection_find_next(cs, scs, NULL)) {
139
140                 char    *auth_type;
141
142                 auth_type = cf_section_name1(scs);
143
144                 if (!auth_type)  continue;
145
146                 eap_type = eaptype_name2type(auth_type);
147                 if (eap_type < 0) {
148                         radlog(L_ERR|L_CONS, "rlm_eap: Unknown EAP type %s",
149                                auth_type);
150                         eap_detach(inst);
151                         return -1;
152                 }
153
154                 /*
155                  *      If we're asked to load TTLS or PEAP, ensure
156                  *      that we've first loaded TLS.
157                  */
158                 if (((eap_type == PW_EAP_TTLS) ||
159                      (eap_type == PW_EAP_PEAP)) &&
160                     (inst->types[PW_EAP_TLS] == NULL)) {
161                         radlog(L_ERR, "rlm_eap: Unable to load EAP-Type/%s, as EAP-Type/TLS is required first.",
162                                auth_type);
163                         return -1;
164                 }
165
166                 /*
167                  *      Load the type.
168                  */
169                 if (eaptype_load(&inst->types[eap_type], eap_type, scs) < 0) {
170                         eap_detach(inst);
171                         return -1;
172                 }
173
174                 num_types++;    /* successfully loaded one more types */
175         }
176
177         if (num_types == 0) {
178                 radlog(L_ERR|L_CONS, "rlm_eap: No EAP type configured, module cannot do anything.");
179                 eap_detach(inst);
180                 return -1;
181         }
182
183         /*
184          *      Ensure that the default EAP type is loaded.
185          */
186         eap_type = eaptype_name2type(inst->default_eap_type_name);
187         if (eap_type < 0) {
188                 radlog(L_ERR|L_CONS, "rlm_eap: Unknown default EAP type %s",
189                        inst->default_eap_type_name);
190                 eap_detach(inst);
191                 return -1;
192         }
193
194         if (inst->types[eap_type] == NULL) {
195                 radlog(L_ERR|L_CONS, "rlm_eap: No such sub-type for default EAP type %s",
196                        inst->default_eap_type_name);
197                 eap_detach(inst);
198                 return -1;
199         }
200         inst->default_eap_type = eap_type; /* save the numerical type */
201
202         /*
203          *      List of sessions are set to NULL by the memset
204          *      of 'inst', above.
205          */
206
207         /* Generate a state key, specific to eap */
208         generate_key();
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(eap_handler_cmp, NULL, 0);
215         if (!inst->session_tree) {
216                 radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree");
217                 eap_detach(inst);
218                 return -1;
219         }
220
221         pthread_mutex_init(&(inst->session_mutex), NULL);
222
223         *instance = inst;
224         return 0;
225 }
226
227
228 /*
229  *      For backwards compatibility.
230  */
231 static int eap_authenticate(void *instance, REQUEST *request)
232 {
233         rlm_eap_t       *inst;
234         EAP_HANDLER     *handler;
235         eap_packet_t    *eap_packet;
236         int             rcode;
237
238         inst = (rlm_eap_t *) instance;
239
240         /*
241          *      Get the eap packet  to start with
242          */
243         eap_packet = eap_attribute(request->packet->vps);
244         if (eap_packet == NULL) {
245                 radlog(L_ERR, "rlm_eap: Malformed EAP Message");
246                 return RLM_MODULE_FAIL;
247         }
248
249         /*
250          *      Create the eap handler.  The eap_packet will end up being
251          *      "swallowed" into the handler, so we can't access it after
252          *      this call.
253          */
254         handler = eap_handler(inst, &eap_packet, request);
255         if (handler == NULL) {
256                 DEBUG2("  rlm_eap: Failed in handler");
257                 return RLM_MODULE_INVALID;
258         }
259
260         /*
261          *      If it's a recursive request, then disallow
262          *      TLS, TTLS, and PEAP, inside of the TLS tunnel.
263          */
264         if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
265                 switch(handler->eap_ds->response->type.type) {
266                 case PW_EAP_TLS:
267                 case PW_EAP_TTLS:
268                 case PW_EAP_PEAP:
269                         DEBUG2(" rlm_eap: Unable to tunnel TLS inside of TLS");
270                         eap_fail(handler);
271                         eap_handler_free(handler);
272                         return RLM_MODULE_INVALID;
273                         break;
274
275                 default:        /* It may be OK, allow it to proceed */
276                         break;
277
278                 }
279         }
280
281         /*
282          *      Select the appropriate eap_type or default to the
283          *      configured one
284          */
285         rcode = eaptype_select(inst, handler);
286
287         /*
288          *      If it failed, die.
289          */
290         if (rcode == EAP_INVALID) {
291                 eap_fail(handler);
292                 eap_handler_free(handler);
293                 DEBUG2("  rlm_eap: Failed in EAP select");
294                 return RLM_MODULE_INVALID;
295         }
296
297         /*
298          *      If we're doing horrible tunneling work, remember it.
299          */
300         if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
301                 DEBUG2("  Not-EAP proxy set.  Not composing EAP");
302                 /*
303                  *      Add the handle to the proxied list, so that we
304                  *      can retrieve it in the post-proxy stage, and
305                  *      send a response.
306                  */
307                 rcode = request_data_add(request,
308                                          inst, REQUEST_DATA_EAP_HANDLER,
309                                          handler, eap_handler_free);
310                 rad_assert(rcode == 0);
311
312                 return RLM_MODULE_HANDLED;
313         }
314
315
316         /*
317          *      Maybe the request was marked to be proxied.  If so,
318          *      proxy it.
319          */
320         if (request->proxy != NULL) {
321                 VALUE_PAIR *vp = NULL;
322
323                 rad_assert(request->proxy_reply == NULL);
324
325                 /*
326                  *      Add the handle to the proxied list, so that we
327                  *      can retrieve it in the post-proxy stage, and
328                  *      send a response.
329                  */
330                 rcode = request_data_add(request,
331                                          inst, REQUEST_DATA_EAP_HANDLER,
332                                          handler, eap_handler_free);
333                 rad_assert(rcode == 0);
334
335                 /*
336                  *      Some simple sanity checks.  These should really
337                  *      be handled by the radius library...
338                  */
339                 vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE);
340                 if (vp) {
341                         vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR);
342                         if (!vp) {
343                                 vp = pairmake("Message-Authenticator",
344                                               "0x00", T_OP_EQ);
345                                 rad_assert(vp != NULL);
346                                 pairadd(&(request->proxy->vps), vp);
347                         }
348                 }
349                         
350                 /*
351                  *      Delete the "proxied to" attribute, as it's
352                  *      set to 127.0.0.1 for tunneled requests, and
353                  *      we don't want to tell the world that...
354                  */
355                 pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO);
356
357                 DEBUG2("  Tunneled session will be proxied.  Not doing EAP.");
358                 return RLM_MODULE_HANDLED;
359         }
360
361         /*
362          *      We are done, wrap the EAP-request in RADIUS to send
363          *      with all other required radius attributes
364          */
365         rcode = eap_compose(handler);
366
367         /*
368          *      Add to the list only if it is EAP-Request, OR if
369          *      it's LEAP, and a response.
370          */
371         if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
372             (handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
373                 eaplist_add(inst, handler);
374
375                 /*
376                  *      LEAP is a little different.  At Stage 4,
377                  *      it sends an EAP-Success message, but we still
378                  *      need to keep the State attribute & session
379                  *      data structure around for the AP Challenge.
380                  *
381                  *      At stage 6, LEAP sends an EAP-Response, which
382                  *      isn't put into the list.
383                  */
384         } else if ((handler->eap_ds->response->code == PW_EAP_RESPONSE) &&
385                    (handler->eap_ds->response->type.type == PW_EAP_LEAP) &&
386                    (handler->eap_ds->request->code == PW_EAP_SUCCESS) &&
387                    (handler->eap_ds->request->type.type == 0)) {
388
389                 eaplist_add(inst, handler);
390
391         } else {
392                 DEBUG2("  rlm_eap: Freeing handler");
393                 /* handler is not required any more, free it now */
394                 eap_handler_free(handler);
395         }
396
397         /*
398          *      If it's an Access-Accept, RFC 2869, Section 2.3.1
399          *      says that we MUST include a User-Name attribute in the
400          *      Access-Accept.
401          */
402         if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
403             request->username) {
404                 VALUE_PAIR *vp;
405
406                 /*
407                  *      Doesn't exist, add it in.
408                  */
409                 vp = pairfind(request->reply->vps, PW_USER_NAME);
410                 if (!vp) {
411                         vp = pairmake("User-Name", request->username->vp_strvalue,
412                                       T_OP_EQ);
413                         rad_assert(vp != NULL);
414                         pairadd(&(request->reply->vps), vp);
415                 }
416
417                 /*
418                  *      Cisco AP1230 has a bug and needs a zero
419                  *      terminated string in Access-Accept.
420                  */
421                 if ((inst->cisco_accounting_username_bug) &&
422                     (vp->length < (int) sizeof(vp->vp_strvalue))) {
423                         vp->vp_strvalue[vp->length] = '\0';
424                         vp->length++;
425                 }
426         }
427
428         return rcode;
429 }
430
431 /*
432  * EAP authorization DEPENDS on other rlm authorizations,
433  * to check for user existance & get their configured values.
434  * It Handles EAP-START Messages, User-Name initilization.
435  */
436 static int eap_authorize(void *instance, REQUEST *request)
437 {
438         rlm_eap_t       *inst;
439         int             status;
440         VALUE_PAIR      *vp;
441
442         inst = (rlm_eap_t *)instance;
443
444         /*
445          *      We don't do authorization again, once we've seen the
446          *      proxy reply (or the proxied packet)
447          */
448         if (request->proxy != NULL)
449                 return RLM_MODULE_NOOP;
450
451         /*
452          *      For EAP_START, send Access-Challenge with EAP Identity
453          *      request.  even when we have to proxy this request
454          *
455          *      RFC 2869, Section 2.3.1 notes that the "domain" of the
456          *      user, (i.e. where to proxy him) comes from the EAP-Identity,
457          *      so we CANNOT proxy the user, until we know his identity.
458          *
459          *      We therefore send an EAP Identity request.
460          */
461         status = eap_start(inst, request);
462         switch(status) {
463         case EAP_NOOP:
464                 return RLM_MODULE_NOOP;
465         case EAP_FAIL:
466                 return RLM_MODULE_FAIL;
467         case EAP_FOUND:
468                 return RLM_MODULE_HANDLED;
469         case EAP_NOTFOUND:
470         default:
471                 break;
472         }
473
474         /*
475          *      RFC 2869, Section 2.3.1.  If a NAS sends an EAP-Identity,
476          *      it MUST copy the identity into the User-Name attribute.
477          *
478          *      But we don't worry about that too much.  We depend on
479          *      each EAP sub-module to look for handler->request->username,
480          *      and to get excited if it doesn't appear.
481          */
482
483         vp = pairfind(request->config_items, PW_AUTH_TYPE);
484         if ((!vp) ||
485             (vp->lvalue != PW_AUTHTYPE_REJECT)) {
486                 vp = pairmake("Auth-Type", "EAP", T_OP_EQ);
487                 if (!vp) {
488                         return RLM_MODULE_FAIL;
489                 }
490                 pairadd(&request->config_items, vp);
491         }
492
493         return RLM_MODULE_UPDATED;
494 }
495
496 /*
497  *      If we're proxying EAP, then there may be magic we need
498  *      to do.
499  */
500 static int eap_post_proxy(void *inst, REQUEST *request)
501 {
502         int             i, len;
503         VALUE_PAIR      *vp;
504         EAP_HANDLER     *handler;
505
506         /*
507          *      If there was a handler associated with this request,
508          *      then it's a tunneled request which was proxied...
509          */
510         handler = request_data_get(request, inst, REQUEST_DATA_EAP_HANDLER);
511         if (handler != NULL) {
512                 int             rcode;
513                 eap_tunnel_data_t *data;
514
515                 /*
516                  *      Grab the tunnel callbacks from the request.
517                  */
518                 data = (eap_tunnel_data_t *) request_data_get(request,
519                                                               request->proxy,
520                                                               REQUEST_DATA_EAP_TUNNEL_CALLBACK);
521                 if (!data) {
522                         radlog(L_ERR, "rlm_eap: Failed to retrieve callback for tunneled session!");
523                         eap_handler_free(handler);
524                         return RLM_MODULE_FAIL;
525                 }
526
527                 /*
528                  *      Do the callback...
529                  */
530                 rcode = data->callback(handler, data->tls_session);
531                 free(data);
532                 if (rcode == 0) {
533                         eap_fail(handler);
534                         eap_handler_free(handler);
535                         return RLM_MODULE_REJECT;
536                 }
537
538                 /*
539                  *      We are done, wrap the EAP-request in RADIUS to send
540                  *      with all other required radius attributes
541                  */
542                 rcode = eap_compose(handler);
543
544                 /*
545                  *      Add to the list only if it is EAP-Request, OR if
546                  *      it's LEAP, and a response.
547                  */
548                 if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
549                     (handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
550                         eaplist_add(inst, handler);
551
552                 } else {        /* couldn't have been LEAP, there's no tunnel */
553                         DEBUG2("  rlm_eap: Freeing handler");
554                         /* handler is not required any more, free it now */
555                         eap_handler_free(handler);
556                 }
557
558                 /*
559                  *      If it's an Access-Accept, RFC 2869, Section 2.3.1
560                  *      says that we MUST include a User-Name attribute in the
561                  *      Access-Accept.
562                  */
563                 if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
564                     request->username) {
565                         /*
566                          *      Doesn't exist, add it in.
567                          */
568                         vp = pairfind(request->reply->vps, PW_USER_NAME);
569                         if (!vp) {
570                                 vp = pairmake("User-Name", request->username->vp_strvalue,
571                                               T_OP_EQ);
572                                 rad_assert(vp != NULL);
573                                 pairadd(&(request->reply->vps), vp);
574                         }
575                 }
576
577                 return RLM_MODULE_OK;
578         }
579
580
581         /*
582          *      There may be more than one Cisco-AVPair.
583          *      Ensure we find the one with the LEAP attribute.
584          */
585         vp = request->proxy_reply->vps;
586         for (;;) {
587                 /*
588                  *      Hmm... there's got to be a better way to
589                  *      discover codes for vendor attributes.
590                  *
591                  *      This is vendor Cisco (9), Cisco-AVPair
592                  *      attribute (1)
593                  */
594                 vp = pairfind(vp, (9 << 16)  | 1);
595                 if (!vp) {
596                         return RLM_MODULE_NOOP;
597                 }
598
599                 /*
600                  *      If it's "leap:session-key", then stop.
601                  *
602                  *      The format is VERY specific!
603                  */
604                 if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
605                         break;
606                 }
607
608                 /*
609                  *      Not this AV-pair.  Go to the next one.
610                  */
611                 vp = vp->next;
612         }
613
614         /*
615          *      The format is very specific.
616          */
617         if (vp->length != 17 + 34) {
618                 DEBUG2("  rlm_eap: Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
619                        vp->length, 17 + 34);
620                 return RLM_MODULE_NOOP;
621         }
622
623         /*
624          *      Decrypt the session key, using the proxy data.
625          */
626         i = 34;                 /* starts off with 34 octets */
627         len = rad_tunnel_pwdecode(vp->vp_strvalue + 17, &i,
628                                   request->proxysecret,
629                                   request->proxy->vector);
630
631         /*
632          *      FIXME: Assert that i == 16.
633          */
634
635         /*
636          *      Encrypt the session key again, using the request data.
637          */
638         rad_tunnel_pwencode(vp->vp_strvalue + 17, &len,
639                             request->secret,
640                             request->packet->vector);
641
642         return RLM_MODULE_UPDATED;
643 }
644
645
646 /*
647  *      The module name should be the only globally exported symbol.
648  *      That is, everything else should be 'static'.
649  */
650 module_t rlm_eap = {
651         RLM_MODULE_INIT,
652         "eap",
653         RLM_TYPE_THREAD_SAFE,           /* type */
654         eap_instantiate,                /* instantiation */
655         eap_detach,                     /* detach */
656         {
657                 eap_authenticate,       /* authentication */
658                 eap_authorize,          /* authorization */
659                 NULL,                   /* preaccounting */
660                 NULL,                   /* accounting */
661                 NULL,                   /* checksimul */
662                 NULL,                   /* pre-proxy */
663                 eap_post_proxy,         /* post-proxy */
664                 NULL                    /* post-auth */
665         },
666 };