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