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