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