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