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