7bb0b945d155295cd582592e92148b0ff476bcc1
[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,2001  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  */
23
24 #include "autoconf.h"
25 #include "eap.h"
26
27 static CONF_PARSER module_config[] = {
28         { "default_eap_type", PW_TYPE_STRING_PTR, offsetof(EAP_CONF, default_eap_type), NULL, "md5" },
29         { "timer_expire", PW_TYPE_INTEGER, offsetof(EAP_CONF, timer_limit), NULL, "60"},
30
31         { NULL, -1, 0, NULL, NULL }           /* end the list */
32 };
33
34 static int eap_init(void)
35 {
36         return 0;
37 }
38
39
40 /*
41  * read the config section and load all the eap authentication types present.
42  */
43 static int eap_instantiate(CONF_SECTION *cs, void **instance)
44 {
45         char            *auth_name;
46         CONF_SECTION    *scs;
47         EAP_TYPES       *types;
48         EAP_CONF        *conf;
49         rlm_eap_t       **eap_stuff;
50         
51         eap_stuff = (rlm_eap_t **)instance;
52         types    = NULL;
53         conf     = NULL;
54         auth_name = NULL;
55
56         conf = (EAP_CONF *)malloc(sizeof(EAP_CONF));
57         if (conf == NULL) {
58                 radlog(L_ERR, "rlm_eap: out of memory");
59                 return -1;
60         }
61         if (cf_section_parse(cs, conf, module_config) < 0) {
62                 free(conf);
63                 return -1;
64         }
65
66         for(scs=cf_subsection_find_next(cs, NULL, NULL);
67                         scs != NULL;
68                         scs=cf_subsection_find_next(cs, scs, NULL)) {
69                 auth_name = cf_section_name1(scs);
70
71                 if (!auth_name)  continue;
72                 eaptype_load(&types, auth_name, scs);
73         }
74
75         if (!types) {
76                 free(conf->default_eap_type);
77                 conf->default_eap_type = NULL;
78                 free(conf);
79                 conf = NULL;
80                 return -1;
81         }
82
83         *eap_stuff = (rlm_eap_t *)malloc(sizeof(rlm_eap_t));
84         if (*eap_stuff) {
85                 (*eap_stuff)->typelist = types;
86                 (*eap_stuff)->echolist = NULL;
87                 (*eap_stuff)->conf = conf;
88         }  else {
89                 radlog(L_ERR, "rlm_eap: out of memory");
90                 eaptype_freelist(&types);
91                 return -1;
92         }
93
94         /* Generate a state key, specific to eap */
95         generate_key();
96         return 0;
97 }
98
99 /*
100  * delete all the allocated space by eap module
101  */
102 static int eap_detach(void *instance)
103 {
104         rlm_eap_t *t;
105         t = (rlm_eap_t *)instance;
106
107         eaplist_free(&(t->echolist));
108         eaptype_freelist(&(t->typelist));
109
110         free(t->conf->default_eap_type);
111         free(t->conf);
112
113         free(t);
114
115         return 0;
116 }
117
118 /*
119  * Assumption: Any one of the Authorization module should
120  *      get the configured password for any valid user.
121  *      If not, Authentication fails to validate.
122  *
123  * All EAP types will be handled in their respective sub modules.
124  *
125  * To Handle EAP-response, we keep track of the EAP-request we send.
126  * When Success or Failure or when timed out, we delete them.
127  */
128 static int eap_authenticate(void *instance, REQUEST *request)
129 {
130         EAP_HANDLER     *handler;
131         rlm_eap_t       *eap_stuff;
132         eap_packet_t    *eap_packet;
133         int             status;
134
135         eap_stuff = (rlm_eap_t *)instance;
136
137         /* 
138          * Always, clean the list first as it is not timer based
139          * FIXME: Appropriate cleaning mechanism.
140          */
141         eaplist_clean(&(eap_stuff->echolist), (time_t)eap_stuff->conf->timer_limit);
142
143         /*
144          * Incase if EAP is not configured in autz block
145          * or eap_authorize is not invoked
146          */
147         status = eap_start(request);
148         switch(status) {
149         case EAP_NOOP:
150                 return RLM_MODULE_NOOP;
151         case EAP_FAIL:
152                 return RLM_MODULE_FAIL;
153         case EAP_FOUND:
154                 return RLM_MODULE_OK;
155         case EAP_NOTFOUND:
156         default:
157                 break;
158         }
159
160         /* get the eap packet  to start with */
161         eap_packet = eap_attribute(request->packet->vps);
162         if (eap_packet == NULL) {
163                 radlog(L_ERR, "rlm_eap: Malformed EAP Message");
164                 return RLM_MODULE_FAIL;
165         }
166
167         /*
168          * create the eap handler 
169          */
170         handler = eap_handler(&(eap_stuff->echolist), &eap_packet, request);
171         if (handler == NULL) {
172                 return RLM_MODULE_INVALID;
173         }
174
175         /*
176          * No User-Name, No authentication
177          */
178         if (request->username == NULL) {
179                 radlog(L_ERR, "rlm_eap: Unknown User, authentication failed");
180                 return RLM_MODULE_REJECT;
181         }
182
183         /*
184          * Always get the configured values, for each user.
185          * to pass it to the specific EAP-Type
186          */
187         handler->configured = paircopy(request->config_items);
188         if (handler->configured == NULL) {
189                 radlog(L_INFO, "rlm_eap: No configured information for this user");
190
191                 /*
192                  * FIXME: I think at least one item should be configured for any user
193                  *      If nothing is configured, then atleast 
194                  *      config_items should provide the same username
195                  *      if the user is present in the database
196                  */
197                 /*
198                 eap_fail(request, handler->eap_ds->request);
199                 eap_handler_free(&handler);
200                 return RLM_MODULE_INVALID;
201                 */
202         }
203
204
205         /*
206          * Select the appropriate eap_type or default to the configured one
207          */
208         if (eaptype_select(eap_stuff->typelist, handler,
209                 eap_stuff->conf->default_eap_type) == EAP_INVALID) {
210
211                 eap_fail(request, handler->eap_ds->request);
212                 eap_handler_free(&handler);
213                 return RLM_MODULE_INVALID;
214         }
215
216         /*
217          * We are done, wrap the EAP-request in RADIUS to send
218          * with all other required radius attributes
219          */
220         eap_compose(request, handler->eap_ds->request);
221
222         /*
223          * Add to the list only if it is EAP-Request
224          */
225         if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
226                 (handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
227
228                 handler->id = eap_generateid(request, (u_char)handler->eap_ds->request->id);
229                 if (handler->id == NULL) {
230                         radlog(L_ERR, "rlm_eap: problem in generating ID, Present EAP is no more Valid");
231                         eap_handler_free(&handler);
232                 } else {
233                         eaplist_add(&(eap_stuff->echolist), handler);
234                 }
235         } else {
236                 /* handler is no more required, free it now */
237                 eap_handler_free(&handler);
238         }
239         return RLM_MODULE_OK;
240 }
241
242 /*
243  * EAP authorization DEPENDS on other rlm authorizations,
244  * to check for user existance & get their configured values.
245  * It Handles EAP-START Messages, User-Name initilization.
246  */
247 static int eap_authorize(void *instance, REQUEST *request)
248 {
249         VALUE_PAIR      *atype, *vp;
250         rlm_eap_t       *eap_stuff;
251         eap_packet_t    *eap_packet;
252         int             status;
253         unsigned char   *id;
254         
255         eap_stuff = (rlm_eap_t *)instance;
256
257         /* Authorization not valid for proxies */
258         if (request->proxy != NULL)
259                 return RLM_MODULE_NOOP;
260
261         /*
262          * For EAP_START, send Access-Challenge with EAP Identity request.
263          * even when we have to proxy this request
264          */
265         status = eap_start(request);
266         switch(status) {
267         case EAP_NOOP:
268                 return RLM_MODULE_NOOP;
269         case EAP_FAIL:
270                 return RLM_MODULE_FAIL;
271         case EAP_FOUND:
272                 return RLM_MODULE_HANDLED;
273         case EAP_NOTFOUND:
274         default:
275                 break;
276         }
277         
278         /*
279          * We should have User-Name to proceed further
280          */
281         if (request->username == NULL) {
282
283                 /* get the eap packet */
284                 eap_packet = eap_attribute(request->packet->vps);
285                 if (eap_packet == NULL) {
286                         radlog(L_ERR, "rlm_eap: Malformed EAP Message");
287                         return RLM_MODULE_FAIL;
288                 }
289
290                 id = eap_regenerateid(request, eap_packet->id);
291                 if (id == NULL) {
292                         radlog(L_ERR, "rlm_eap: User-Name cannot be obtained");
293                         free(eap_packet);
294                         return RLM_MODULE_FAIL;
295                 }
296
297                 request->username = eap_useridentity(eap_stuff->echolist, eap_packet, id);
298                 if (request->username == NULL) {
299                         radlog(L_ERR, "rlm_eap: Unknown User, authorization failed");
300                         free(eap_packet);
301                         free(id);
302                         return RLM_MODULE_FAIL;
303                 }
304                 free(eap_packet);
305                 free(id);
306         }
307
308         /*
309          * Enforce EAP authentication
310
311          * Auth-type(s) already set?  overide it with EAP
312          * If EAP-Message is present in RADIUS, then EAP authentication is MUST.
313
314          * TODO: When Multiple authentications are supported in RADIUS, 
315          *     then prioritize EAP by prepending it before all Auth-Types
316          */
317
318         atype = pairfind(request->config_items, PW_AUTHTYPE);
319         if ((atype == NULL) || 
320                 ((atype->lvalue != PW_AUTHTYPE_EAP) &&
321                 (atype->lvalue != PW_AUTHTYPE_ACCEPT) &&
322                 (atype->lvalue != PW_AUTHTYPE_REJECT))) {
323
324                 vp = pairmake("Auth-Type", "EAP", T_OP_EQ);
325                 if (vp == NULL) {
326                         return RLM_MODULE_FAIL;
327                 }
328                 /* to overide */
329                 pairdelete(&request->config_items, PW_AUTHTYPE);
330                 pairadd(&request->config_items, vp);
331
332                 /* To prioritize
333                 vp->next = request->config_items;
334                 request->config_items = vp;
335                 */
336         }
337
338         return RLM_MODULE_UPDATED;
339 }
340
341 /*
342  *      The module name should be the only globally exported symbol.
343  *      That is, everything else should be 'static'.
344  */
345 module_t rlm_eap = {
346         "eap",
347         RLM_TYPE_THREAD_UNSAFE,         /* type */
348         eap_init,                       /* initialization */
349         eap_instantiate,                /* instantiation */
350         {
351                 eap_authenticate,       /* authentication */
352                 eap_authorize,          /* authorization */
353                 NULL,                   /* preaccounting */
354                 NULL,                   /* accounting */
355                 NULL                    /* checksimul */
356         },
357         eap_detach,                     /* detach */
358         NULL,                           /* destroy */
359 };