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