Remove redundant file from freeradius-abfab list.
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_ttls / rlm_eap_ttls.c
1 /*
2  * rlm_eap_ttls.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 USES_APPLE_DEPRECATED_API       /* OpenSSL API has been deprecated by Apple */
26
27 #include "eap_ttls.h"
28
29 typedef struct rlm_eap_ttls_t {
30         /*
31          *      TLS configuration
32          */
33         char const *tls_conf_name;
34         fr_tls_server_conf_t *tls_conf;
35
36         /*
37          *      Default tunneled EAP type
38          */
39         char const *default_method_name;
40         int default_method;
41
42         /*
43          *      Use the reply attributes from the tunneled session in
44          *      the non-tunneled reply to the client.
45          */
46         bool 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         bool copy_request_to_tunnel;
53
54         /*
55          *      RFC 5281 (TTLS) says that the length field MUST NOT be
56          *      in fragments after the first one.  However, we've done
57          *      it that way for years, and no one has complained.
58          *
59          *      In the interests of allowing the server to follow the
60          *      RFC, we add the option here.  If set to "no", it sends
61          *      the length field in ONLY the first fragment.
62          */
63         bool include_length;
64
65         /*
66          *      Virtual server for inner tunnel session.
67          */
68         char const *virtual_server;
69
70         /*
71          *      Do we do require a client cert?
72          */
73         bool req_client_cert;
74 } rlm_eap_ttls_t;
75
76
77 static CONF_PARSER module_config[] = {
78         { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, tls_conf_name), NULL },
79         { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, default_method_name), "md5" },
80         { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, copy_request_to_tunnel), "no" },
81         { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, use_tunneled_reply), "no" },
82         { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, virtual_server), NULL },
83         { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, include_length), "yes" },
84         { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, req_client_cert), "no" },
85         CONF_PARSER_TERMINATOR
86 };
87
88
89 /*
90  *      Attach the module.
91  */
92 static int mod_instantiate(CONF_SECTION *cs, void **instance)
93 {
94         rlm_eap_ttls_t          *inst;
95
96         *instance = inst = talloc_zero(cs, rlm_eap_ttls_t);
97         if (!inst) return -1;
98
99         /*
100          *      Parse the configuration attributes.
101          */
102         if (cf_section_parse(cs, inst, module_config) < 0) {
103                 return -1;
104         }
105
106         /*
107          *      Convert the name to an integer, to make it easier to
108          *      handle.
109          */
110         inst->default_method = eap_name2type(inst->default_method_name);
111         if (inst->default_method < 0) {
112                 ERROR("rlm_eap_ttls: Unknown EAP type %s",
113                        inst->default_method_name);
114                 return -1;
115         }
116
117         /*
118          *      Read tls configuration, either from group given by 'tls'
119          *      option, or from the eap-tls configuration.
120          */
121         inst->tls_conf = eaptls_conf_parse(cs, "tls");
122
123         if (!inst->tls_conf) {
124                 ERROR("rlm_eap_ttls: Failed initializing SSL context");
125                 return -1;
126         }
127
128         return 0;
129 }
130
131 /*
132  *      Allocate the TTLS per-session data
133  */
134 static ttls_tunnel_t *ttls_alloc(TALLOC_CTX *ctx, rlm_eap_ttls_t *inst)
135 {
136         ttls_tunnel_t *t;
137
138         t = talloc_zero(ctx, ttls_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         t->virtual_server = inst->virtual_server;
144         return t;
145 }
146
147
148 /*
149  *      Send an initial eap-tls request to the peer, using the libeap functions.
150  */
151 static int mod_session_init(void *type_arg, eap_handler_t *handler)
152 {
153         int             status;
154         tls_session_t   *ssn;
155         rlm_eap_ttls_t  *inst;
156         VALUE_PAIR      *vp;
157         bool            client_cert;
158         REQUEST         *request = handler->request;
159
160         inst = type_arg;
161
162         handler->tls = true;
163
164         /*
165          *      Check if we need a client certificate.
166          */
167
168         /*
169          * EAP-TLS-Require-Client-Cert attribute will override
170          * the require_client_cert configuration option.
171          */
172         vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
173         if (vp) {
174                 client_cert = vp->vp_integer ? true : false;
175         } else {
176                 client_cert = inst->req_client_cert;
177         }
178
179         ssn = eaptls_session(handler, inst->tls_conf, client_cert);
180         if (!ssn) {
181                 return 0;
182         }
183
184         handler->opaque = ((void *)ssn);
185
186         /*
187          *      Set up type-specific information.
188          */
189         ssn->prf_label = "ttls keying material";
190
191         /*
192          *      TLS session initialization is over.  Now handle TLS
193          *      related handshaking or application data.
194          */
195         status = eaptls_start(handler->eap_ds, ssn->peap_flag);
196         if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
197                 REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
198         } else {
199                 RDEBUG2("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
200         }
201         if (status == 0) return 0;
202
203         /*
204          *      The next stage to process the packet.
205          */
206         handler->stage = PROCESS;
207
208         return 1;
209 }
210
211
212 /*
213  *      Do authentication, by letting EAP-TLS do most of the work.
214  */
215 static int mod_process(void *arg, eap_handler_t *handler)
216 {
217         int rcode;
218         fr_tls_status_t status;
219         rlm_eap_ttls_t *inst = (rlm_eap_ttls_t *) arg;
220         tls_session_t *tls_session = (tls_session_t *) handler->opaque;
221         ttls_tunnel_t *t = (ttls_tunnel_t *) tls_session->opaque;
222         REQUEST *request = handler->request;
223
224         RDEBUG2("Authenticate");
225
226         tls_session->length_flag = inst->include_length;
227
228         /*
229          *      Process TLS layer until done.
230          */
231         status = eaptls_process(handler);
232         if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
233                 REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
234         } else {
235                 RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
236         }
237
238         switch (status) {
239         /*
240          *      EAP-TLS handshake was successful, tell the
241          *      client to keep talking.
242          *
243          *      If this was EAP-TLS, we would just return
244          *      an EAP-TLS-Success packet here.
245          */
246         case FR_TLS_SUCCESS:
247                 if (SSL_session_reused(tls_session->ssl)) {
248                         RDEBUG("Skipping Phase2 due to session resumption");
249                         goto do_keys;
250                 }
251
252                 if (t && t->authenticated) {
253                         if (t->accept_vps) {
254                                 RDEBUG2("Using saved attributes from the original Access-Accept");
255                                 rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL);
256                                 fr_pair_list_mcopy_by_num(handler->request->reply,
257                                            &handler->request->reply->vps,
258                                            &t->accept_vps, 0, 0, TAG_ANY);
259                         } else if (t->use_tunneled_reply) {
260                                 RDEBUG2("No saved attributes in the original Access-Accept");
261                         }
262
263                 do_keys:
264                         /*
265                          *      Success: Automatically return MPPE keys.
266                          */
267                         return eaptls_success(handler, 0);
268                 } else {
269                         eaptls_request(handler->eap_ds, tls_session);
270                 }
271                 return 1;
272
273         /*
274          *      The TLS code is still working on the TLS
275          *      exchange, and it's a valid TLS request.
276          *      do nothing.
277          */
278         case FR_TLS_HANDLED:
279                 return 1;
280
281         /*
282          *      Handshake is done, proceed with decoding tunneled
283          *      data.
284          */
285         case FR_TLS_OK:
286                 break;
287
288         /*
289          *      Anything else: fail.
290          */
291         default:
292                 return 0;
293         }
294
295         /*
296          *      Session is established, proceed with decoding
297          *      tunneled data.
298          */
299         RDEBUG2("Session established.  Proceeding to decode tunneled attributes");
300
301         /*
302          *      We may need TTLS data associated with the session, so
303          *      allocate it here, if it wasn't already alloacted.
304          */
305         if (!tls_session->opaque) {
306                 tls_session->opaque = ttls_alloc(tls_session, inst);
307         }
308
309         /*
310          *      Process the TTLS portion of the request.
311          */
312         rcode = eapttls_process(handler, tls_session);
313         switch (rcode) {
314         case PW_CODE_ACCESS_REJECT:
315                 eaptls_fail(handler, 0);
316                 return 0;
317
318                 /*
319                  *      Access-Challenge, continue tunneled conversation.
320                  */
321         case PW_CODE_ACCESS_CHALLENGE:
322                 eaptls_request(handler->eap_ds, tls_session);
323                 return 1;
324
325                 /*
326                  *      Success: Automatically return MPPE keys.
327                  */
328         case PW_CODE_ACCESS_ACCEPT:
329                 return eaptls_success(handler, 0);
330
331                 /*
332                  *      No response packet, MUST be proxying it.
333                  *      The main EAP module will take care of discovering
334                  *      that the request now has a "proxy" packet, and
335                  *      will proxy it, rather than returning an EAP packet.
336                  */
337         case PW_CODE_STATUS_CLIENT:
338 #ifdef WITH_PROXY
339                 rad_assert(handler->request->proxy != NULL);
340 #endif
341                 return 1;
342
343         default:
344                 break;
345         }
346
347         /*
348          *      Something we don't understand: Reject it.
349          */
350         eaptls_fail(handler, 0);
351         return 0;
352 }
353
354 /*
355  *      The module name should be the only globally exported symbol.
356  *      That is, everything else should be 'static'.
357  */
358 extern rlm_eap_module_t rlm_eap_ttls;
359 rlm_eap_module_t rlm_eap_ttls = {
360         .name           = "eap_ttls",
361         .instantiate    = mod_instantiate,      /* Create new submodule instance */
362         .session_init   = mod_session_init,     /* Initialise a new EAP session */
363         .process        = mod_process           /* Process next round of EAP method */
364 };