Move EAP types (sub-modules) to use talloc
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_peap / rlm_eap_peap.c
1 /*
2  * rlm_eap_peap.c  contains the interfaces that are called from eap
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 2003 Alan DeKok <aland@freeradius.org>
21  * Copyright 2006 The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include "eap_peap.h"
28
29 typedef struct rlm_eap_peap_t {
30         /*
31          *      TLS configuration
32          */
33         char    *tls_conf_name;
34         fr_tls_server_conf_t *tls_conf;
35
36         /*
37          *      Default tunneled EAP type
38          */
39         char    *default_eap_type_name;
40         int     default_eap_type;
41
42         /*
43          *      Use the reply attributes from the tunneled session in
44          *      the non-tunneled reply to the client.
45          */
46         int     use_tunneled_reply;
47
48         /*
49          *      Use SOME of the request attributes from outside of the
50          *      tunneled session in the tunneled request
51          */
52         int     copy_request_to_tunnel;
53
54 #ifdef WITH_PROXY
55         /*
56          *      Proxy tunneled session as EAP, or as de-capsulated
57          *      protocol.
58          */
59         int     proxy_tunneled_request_as_eap;
60 #endif
61
62         /*
63          *      Virtual server for inner tunnel session.
64          */
65         char    *virtual_server;
66
67         /*
68          *      Do we do SoH request?
69          */
70         int     soh;
71         char    *soh_virtual_server;
72
73         /*
74          *      Do we do require a client cert?
75          */
76         int     req_client_cert;
77 } rlm_eap_peap_t;
78
79
80 static CONF_PARSER module_config[] = {
81         { "tls", PW_TYPE_STRING_PTR,
82           offsetof(rlm_eap_peap_t, tls_conf_name), NULL, NULL },
83
84         { "default_eap_type", PW_TYPE_STRING_PTR,
85           offsetof(rlm_eap_peap_t, default_eap_type_name), NULL, "mschapv2" },
86
87         { "copy_request_to_tunnel", PW_TYPE_BOOLEAN,
88           offsetof(rlm_eap_peap_t, copy_request_to_tunnel), NULL, "no" },
89
90         { "use_tunneled_reply", PW_TYPE_BOOLEAN,
91           offsetof(rlm_eap_peap_t, use_tunneled_reply), NULL, "no" },
92
93 #ifdef WITH_PROXY
94         { "proxy_tunneled_request_as_eap", PW_TYPE_BOOLEAN,
95           offsetof(rlm_eap_peap_t, proxy_tunneled_request_as_eap), NULL, "yes" },
96 #endif
97
98         { "virtual_server", PW_TYPE_STRING_PTR,
99           offsetof(rlm_eap_peap_t, virtual_server), NULL, NULL },
100
101         { "soh", PW_TYPE_BOOLEAN,
102           offsetof(rlm_eap_peap_t, soh), NULL, "no" },
103
104         { "require_client_cert", PW_TYPE_BOOLEAN,
105           offsetof(rlm_eap_peap_t, req_client_cert), NULL, "no" },
106
107         { "soh_virtual_server", PW_TYPE_STRING_PTR,
108           offsetof(rlm_eap_peap_t, soh_virtual_server), NULL, NULL },
109
110         { NULL, -1, 0, NULL, NULL }           /* end the list */
111 };
112
113
114 /*
115  *      Attach the module.
116  */
117 static int eappeap_attach(CONF_SECTION *cs, void **instance)
118 {
119         rlm_eap_peap_t          *inst;
120
121         *instance = inst = talloc_zero(cs, rlm_eap_peap_t);
122         if (!inst) return -1;
123
124         /*
125          *      Parse the configuration attributes.
126          */
127         if (cf_section_parse(cs, inst, module_config) < 0) {
128                 return -1;
129         }
130
131         /*
132          *      Convert the name to an integer, to make it easier to
133          *      handle.
134          */
135         inst->default_eap_type = eaptype_name2type(inst->default_eap_type_name);
136         if (inst->default_eap_type < 0) {
137                 radlog(L_ERR, "rlm_eap_peap: Unknown EAP type %s",
138                        inst->default_eap_type_name);
139                 return -1;
140         }
141
142         /*
143          *      Read tls configuration, either from group given by 'tls'
144          *      option, or from the eap-tls configuration.
145          */
146         inst->tls_conf = eaptls_conf_parse(cs, "tls");
147
148         if (!inst->tls_conf) {
149                 radlog(L_ERR, "rlm_eap_peap: Failed initializing SSL context");
150                 return -1;
151         }
152
153         return 0;
154 }
155
156 /*
157  *      Free the PEAP per-session data
158  */
159 static void peap_free(void *p)
160 {
161         peap_tunnel_t *t = (peap_tunnel_t *) p;
162
163         if (!t) return;
164
165         pairfree(&t->username);
166         pairfree(&t->state);
167         pairfree(&t->accept_vps);
168         pairfree(&t->soh_reply_vps);
169
170         free(t);
171 }
172
173
174 /*
175  *      Free the PEAP per-session data
176  */
177 static peap_tunnel_t *peap_alloc(rlm_eap_peap_t *inst)
178 {
179         peap_tunnel_t *t;
180
181         t = rad_malloc(sizeof(*t));
182         memset(t, 0, sizeof(*t));
183
184         t->default_eap_type = inst->default_eap_type;
185         t->copy_request_to_tunnel = inst->copy_request_to_tunnel;
186         t->use_tunneled_reply = inst->use_tunneled_reply;
187 #ifdef WITH_PROXY
188         t->proxy_tunneled_request_as_eap = inst->proxy_tunneled_request_as_eap;
189 #endif
190         t->virtual_server = inst->virtual_server;
191         t->soh = inst->soh;
192         t->soh_virtual_server = inst->soh_virtual_server;
193         t->session_resumption_state = PEAP_RESUMPTION_MAYBE;
194
195         return t;
196 }
197
198 /*
199  *      Send an initial eap-tls request to the peer, using the libeap functions.
200  */
201 static int eappeap_initiate(void *type_arg, EAP_HANDLER *handler)
202 {
203         int             status;
204         tls_session_t   *ssn;
205         rlm_eap_peap_t  *inst;
206         VALUE_PAIR      *vp;
207         int             client_cert = FALSE;
208         REQUEST         *request = handler->request;
209
210         inst = type_arg;
211
212         handler->tls = TRUE;
213         handler->finished = FALSE;
214
215         /*
216          *      Check if we need a client certificate.
217          */
218         client_cert = inst->req_client_cert;
219
220         /*
221          * EAP-TLS-Require-Client-Cert attribute will override
222          * the require_client_cert configuration option.
223          */
224         vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
225         if (vp) {
226                 client_cert = vp->vp_integer;
227         }
228
229         ssn = eaptls_session(inst->tls_conf, handler, client_cert);
230         if (!ssn) {
231                 return 0;
232         }
233
234         handler->opaque = ((void *)ssn);
235         handler->free_opaque = session_free;
236
237         /*
238          *      Set up type-specific information.
239          */
240         ssn->prf_label = "client EAP encryption";
241
242         /*
243          *      As it is a poorly designed protocol, PEAP uses
244          *      bits in the TLS header to indicate PEAP
245          *      version numbers.  For now, we only support
246          *      PEAP version 0, so it doesn't matter too much.
247          *      However, if we support later versions of PEAP,
248          *      we will need this flag to indicate which
249          *      version we're currently dealing with.
250          */
251         ssn->peap_flag = 0x00;
252
253         /*
254          *      PEAP version 0 requires 'include_length = no',
255          *      so rather than hoping the user figures it out,
256          *      we force it here.
257          */
258         ssn->length_flag = 0;
259
260         /*
261          *      TLS session initialization is over.  Now handle TLS
262          *      related handshaking or application data.
263          */
264         status = eaptls_start(handler->eap_ds, ssn->peap_flag);
265         RDEBUG2("Start returned %d", status);
266         if (status == 0) {
267                 return 0;
268         }
269
270         /*
271          *      The next stage to process the packet.
272          */
273         handler->stage = AUTHENTICATE;
274
275         return 1;
276 }
277
278 /*
279  *      Do authentication, by letting EAP-TLS do most of the work.
280  */
281 static int eappeap_authenticate(void *arg, EAP_HANDLER *handler)
282 {
283         int rcode;
284         fr_tls_status_t status;
285         rlm_eap_peap_t *inst = (rlm_eap_peap_t *) arg;
286         tls_session_t *tls_session = (tls_session_t *) handler->opaque;
287         peap_tunnel_t *peap = tls_session->opaque;
288         REQUEST *request = handler->request;
289
290         /*
291          *      Session resumption requires the storage of data, so
292          *      allocate it if it doesn't already exist.
293          */
294         if (!tls_session->opaque) {
295                 peap = tls_session->opaque = peap_alloc(inst);
296                 tls_session->free_opaque = peap_free;
297         }
298
299         status = eaptls_process(handler);
300         RDEBUG2("eaptls_process returned %d\n", status);
301         switch (status) {
302                 /*
303                  *      EAP-TLS handshake was successful, tell the
304                  *      client to keep talking.
305                  *
306                  *      If this was EAP-TLS, we would just return
307                  *      an EAP-TLS-Success packet here.
308                  */
309         case FR_TLS_SUCCESS:
310                 RDEBUG2("FR_TLS_SUCCESS");
311                 peap->status = PEAP_STATUS_TUNNEL_ESTABLISHED;
312                 break;
313
314                 /*
315                  *      The TLS code is still working on the TLS
316                  *      exchange, and it's a valid TLS request.
317                  *      do nothing.
318                  */
319         case FR_TLS_HANDLED:
320           /*
321            *    FIXME: If the SSL session is established, grab the state
322            *    and EAP id from the inner tunnel, and update it with
323            *    the expected EAP id!
324            */
325                 RDEBUG2("FR_TLS_HANDLED");
326                 return 1;
327
328                 /*
329                  *      Handshake is done, proceed with decoding tunneled
330                  *      data.
331                  */
332         case FR_TLS_OK:
333                 RDEBUG2("FR_TLS_OK");
334                 break;
335
336                 /*
337                  *      Anything else: fail.
338                  */
339         default:
340                 RDEBUG2("FR_TLS_OTHERS");
341                 return 0;
342         }
343
344         /*
345          *      Session is established, proceed with decoding
346          *      tunneled data.
347          */
348         RDEBUG2("Session established.  Decoding tunneled attributes.");
349
350         /*
351          *      We may need PEAP data associated with the session, so
352          *      allocate it here, if it wasn't already alloacted.
353          */
354         if (!tls_session->opaque) {
355                 tls_session->opaque = peap_alloc(inst);
356                 tls_session->free_opaque = peap_free;
357         }
358
359         /*
360          *      Process the PEAP portion of the request.
361          */
362         rcode = eappeap_process(handler, tls_session);
363         switch (rcode) {
364         case RLM_MODULE_REJECT:
365                 eaptls_fail(handler, 0);
366                 return 0;
367
368         case RLM_MODULE_HANDLED:
369                 eaptls_request(handler->eap_ds, tls_session);
370                 return 1;
371
372         case RLM_MODULE_OK:
373                 /*
374                  *      Move the saved VP's from the Access-Accept to
375                  *      our Access-Accept.
376                  */
377                 peap = tls_session->opaque;
378                 if (peap->soh_reply_vps) {
379                         RDEBUG2("Using saved attributes from the SoH reply");
380                         debug_pair_list(peap->soh_reply_vps);
381                         pairadd(&handler->request->reply->vps, peap->soh_reply_vps);
382                         peap->soh_reply_vps = NULL;
383                 }
384                 if (peap->accept_vps) {
385                         RDEBUG2("Using saved attributes from the original Access-Accept");
386                         debug_pair_list(peap->accept_vps);
387                         pairadd(&handler->request->reply->vps, peap->accept_vps);
388                         peap->accept_vps = NULL;
389                 }
390
391                 /*
392                  *      Success: Automatically return MPPE keys.
393                  */
394                 return eaptls_success(handler, 0);
395
396                 /*
397                  *      No response packet, MUST be proxying it.
398                  *      The main EAP module will take care of discovering
399                  *      that the request now has a "proxy" packet, and
400                  *      will proxy it, rather than returning an EAP packet.
401                  */
402         case RLM_MODULE_UPDATED:
403 #ifdef WITH_PROXY
404                 rad_assert(handler->request->proxy != NULL);
405 #endif
406                 return 1;
407                 break;
408
409         default:
410                 break;
411         }
412
413         eaptls_fail(handler, 0);
414         return 0;
415 }
416
417
418 /*
419  *      The module name should be the only globally exported symbol.
420  *      That is, everything else should be 'static'.
421  */
422 EAP_TYPE rlm_eap_peap = {
423         "eap_peap",
424         eappeap_attach,                 /* attach */
425         eappeap_initiate,               /* Start the initial request */
426         NULL,                           /* authorization */
427         eappeap_authenticate,           /* authentication */
428         NULL                            /* detach */
429 };