Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / eap_server / eap_server_tls.c
1 /*
2  * hostapd / EAP-TLS (RFC 2716)
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "eap_i.h"
13 #include "eap_tls_common.h"
14 #include "crypto/tls.h"
15
16
17 static void eap_tls_reset(struct eap_sm *sm, void *priv);
18
19
20 struct eap_tls_data {
21         struct eap_ssl_data ssl;
22         enum { START, CONTINUE, SUCCESS, FAILURE } state;
23         int established;
24         u8 eap_type;
25 };
26
27
28 static const char * eap_tls_state_txt(int state)
29 {
30         switch (state) {
31         case START:
32                 return "START";
33         case CONTINUE:
34                 return "CONTINUE";
35         case SUCCESS:
36                 return "SUCCESS";
37         case FAILURE:
38                 return "FAILURE";
39         default:
40                 return "Unknown?!";
41         }
42 }
43
44
45 static void eap_tls_state(struct eap_tls_data *data, int state)
46 {
47         wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
48                    eap_tls_state_txt(data->state),
49                    eap_tls_state_txt(state));
50         data->state = state;
51         if (state == FAILURE)
52                 tls_connection_remove_session(data->ssl.conn);
53 }
54
55
56 static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
57 {
58         struct wpabuf *buf;
59
60         if (!sm->tls_session_lifetime)
61                 return;
62
63         buf = wpabuf_alloc(1);
64         if (!buf)
65                 return;
66         wpabuf_put_u8(buf, data->eap_type);
67         tls_connection_set_success_data(data->ssl.conn, buf);
68 }
69
70
71 static void * eap_tls_init(struct eap_sm *sm)
72 {
73         struct eap_tls_data *data;
74
75         data = os_zalloc(sizeof(*data));
76         if (data == NULL)
77                 return NULL;
78         data->state = START;
79
80         if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
81                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
82                 eap_tls_reset(sm, data);
83                 return NULL;
84         }
85
86         data->eap_type = EAP_TYPE_TLS;
87
88         return data;
89 }
90
91
92 #ifdef EAP_SERVER_UNAUTH_TLS
93 static void * eap_unauth_tls_init(struct eap_sm *sm)
94 {
95         struct eap_tls_data *data;
96
97         data = os_zalloc(sizeof(*data));
98         if (data == NULL)
99                 return NULL;
100         data->state = START;
101
102         if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
103                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
104                 eap_tls_reset(sm, data);
105                 return NULL;
106         }
107
108         data->eap_type = EAP_UNAUTH_TLS_TYPE;
109         return data;
110 }
111 #endif /* EAP_SERVER_UNAUTH_TLS */
112
113
114 #ifdef CONFIG_HS20
115 static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
116 {
117         struct eap_tls_data *data;
118
119         data = os_zalloc(sizeof(*data));
120         if (data == NULL)
121                 return NULL;
122         data->state = START;
123
124         if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
125                                     EAP_WFA_UNAUTH_TLS_TYPE)) {
126                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
127                 eap_tls_reset(sm, data);
128                 return NULL;
129         }
130
131         data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
132         return data;
133 }
134 #endif /* CONFIG_HS20 */
135
136
137 static void eap_tls_reset(struct eap_sm *sm, void *priv)
138 {
139         struct eap_tls_data *data = priv;
140         if (data == NULL)
141                 return;
142         eap_server_tls_ssl_deinit(sm, &data->ssl);
143         os_free(data);
144 }
145
146
147 static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
148                                            struct eap_tls_data *data, u8 id)
149 {
150         struct wpabuf *req;
151
152         req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
153         if (req == NULL) {
154                 wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
155                            "request");
156                 eap_tls_state(data, FAILURE);
157                 return NULL;
158         }
159
160         wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
161
162         eap_tls_state(data, CONTINUE);
163
164         return req;
165 }
166
167
168 static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
169 {
170         struct eap_tls_data *data = priv;
171         struct wpabuf *res;
172
173         if (data->ssl.state == FRAG_ACK) {
174                 return eap_server_tls_build_ack(id, data->eap_type, 0);
175         }
176
177         if (data->ssl.state == WAIT_FRAG_ACK) {
178                 res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
179                                                id);
180                 goto check_established;
181         }
182
183         switch (data->state) {
184         case START:
185                 return eap_tls_build_start(sm, data, id);
186         case CONTINUE:
187                 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
188                         data->established = 1;
189                 break;
190         default:
191                 wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
192                            __func__, data->state);
193                 return NULL;
194         }
195
196         res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
197
198 check_established:
199         if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
200                 /* TLS handshake has been completed and there are no more
201                  * fragments waiting to be sent out. */
202                 wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
203                 eap_tls_state(data, SUCCESS);
204                 eap_tls_valid_session(sm, data);
205         }
206
207         return res;
208 }
209
210
211 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
212                              struct wpabuf *respData)
213 {
214         struct eap_tls_data *data = priv;
215         const u8 *pos;
216         size_t len;
217
218         if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
219                 pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
220                                        EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
221                                        &len);
222         else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
223                 pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
224                                        EAP_VENDOR_WFA_UNAUTH_TLS, respData,
225                                        &len);
226         else
227                 pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
228                                        respData, &len);
229         if (pos == NULL || len < 1) {
230                 wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
231                 return TRUE;
232         }
233
234         return FALSE;
235 }
236
237
238 static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
239                                 const struct wpabuf *respData)
240 {
241         struct eap_tls_data *data = priv;
242         if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
243                 wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
244                            "handshake message");
245                 return;
246         }
247         if (eap_server_tls_phase1(sm, &data->ssl) < 0)
248                 eap_tls_state(data, FAILURE);
249 }
250
251
252 static void eap_tls_process(struct eap_sm *sm, void *priv,
253                             struct wpabuf *respData)
254 {
255         struct eap_tls_data *data = priv;
256         const struct wpabuf *buf;
257         const u8 *pos;
258
259         if (eap_server_tls_process(sm, &data->ssl, respData, data,
260                                    data->eap_type, NULL, eap_tls_process_msg) <
261             0) {
262                 eap_tls_state(data, FAILURE);
263                 return;
264         }
265
266         if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
267             !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
268                 return;
269
270         buf = tls_connection_get_success_data(data->ssl.conn);
271         if (!buf || wpabuf_len(buf) < 1) {
272                 wpa_printf(MSG_DEBUG,
273                            "EAP-TLS: No success data in resumed session - reject attempt");
274                 eap_tls_state(data, FAILURE);
275                 return;
276         }
277
278         pos = wpabuf_head(buf);
279         if (*pos != data->eap_type) {
280                 wpa_printf(MSG_DEBUG,
281                            "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
282                            *pos);
283                 eap_tls_state(data, FAILURE);
284                 return;
285         }
286
287         wpa_printf(MSG_DEBUG,
288                    "EAP-TLS: Resuming previous session");
289         eap_tls_state(data, SUCCESS);
290         tls_connection_set_success_data_resumed(data->ssl.conn);
291 }
292
293
294 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
295 {
296         struct eap_tls_data *data = priv;
297         return data->state == SUCCESS || data->state == FAILURE;
298 }
299
300
301 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
302 {
303         struct eap_tls_data *data = priv;
304         u8 *eapKeyData;
305
306         if (data->state != SUCCESS)
307                 return NULL;
308
309         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
310                                                "client EAP encryption",
311                                                EAP_TLS_KEY_LEN);
312         if (eapKeyData) {
313                 *len = EAP_TLS_KEY_LEN;
314                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
315                             eapKeyData, EAP_TLS_KEY_LEN);
316         } else {
317                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
318         }
319
320         return eapKeyData;
321 }
322
323
324 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
325 {
326         struct eap_tls_data *data = priv;
327         u8 *eapKeyData, *emsk;
328
329         if (data->state != SUCCESS)
330                 return NULL;
331
332         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
333                                                "client EAP encryption",
334                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
335         if (eapKeyData) {
336                 emsk = os_malloc(EAP_EMSK_LEN);
337                 if (emsk)
338                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
339                                   EAP_EMSK_LEN);
340                 bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
341         } else
342                 emsk = NULL;
343
344         if (emsk) {
345                 *len = EAP_EMSK_LEN;
346                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
347                             emsk, EAP_EMSK_LEN);
348         } else {
349                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
350         }
351
352         return emsk;
353 }
354
355
356 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
357 {
358         struct eap_tls_data *data = priv;
359         return data->state == SUCCESS;
360 }
361
362
363 static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
364 {
365         struct eap_tls_data *data = priv;
366
367         if (data->state != SUCCESS)
368                 return NULL;
369
370         return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
371                                                 len);
372 }
373
374
375 int eap_server_tls_register(void)
376 {
377         struct eap_method *eap;
378         int ret;
379
380         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
381                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
382         if (eap == NULL)
383                 return -1;
384
385         eap->init = eap_tls_init;
386         eap->reset = eap_tls_reset;
387         eap->buildReq = eap_tls_buildReq;
388         eap->check = eap_tls_check;
389         eap->process = eap_tls_process;
390         eap->isDone = eap_tls_isDone;
391         eap->getKey = eap_tls_getKey;
392         eap->isSuccess = eap_tls_isSuccess;
393         eap->get_emsk = eap_tls_get_emsk;
394         eap->getSessionId = eap_tls_get_session_id;
395
396         ret = eap_server_method_register(eap);
397         if (ret)
398                 eap_server_method_free(eap);
399         return ret;
400 }
401
402
403 #ifdef EAP_SERVER_UNAUTH_TLS
404 int eap_server_unauth_tls_register(void)
405 {
406         struct eap_method *eap;
407         int ret;
408
409         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
410                                       EAP_VENDOR_UNAUTH_TLS,
411                                       EAP_VENDOR_TYPE_UNAUTH_TLS,
412                                       "UNAUTH-TLS");
413         if (eap == NULL)
414                 return -1;
415
416         eap->init = eap_unauth_tls_init;
417         eap->reset = eap_tls_reset;
418         eap->buildReq = eap_tls_buildReq;
419         eap->check = eap_tls_check;
420         eap->process = eap_tls_process;
421         eap->isDone = eap_tls_isDone;
422         eap->getKey = eap_tls_getKey;
423         eap->isSuccess = eap_tls_isSuccess;
424         eap->get_emsk = eap_tls_get_emsk;
425
426         ret = eap_server_method_register(eap);
427         if (ret)
428                 eap_server_method_free(eap);
429         return ret;
430 }
431 #endif /* EAP_SERVER_UNAUTH_TLS */
432
433
434 #ifdef CONFIG_HS20
435 int eap_server_wfa_unauth_tls_register(void)
436 {
437         struct eap_method *eap;
438         int ret;
439
440         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
441                                       EAP_VENDOR_WFA_NEW,
442                                       EAP_VENDOR_WFA_UNAUTH_TLS,
443                                       "WFA-UNAUTH-TLS");
444         if (eap == NULL)
445                 return -1;
446
447         eap->init = eap_wfa_unauth_tls_init;
448         eap->reset = eap_tls_reset;
449         eap->buildReq = eap_tls_buildReq;
450         eap->check = eap_tls_check;
451         eap->process = eap_tls_process;
452         eap->isDone = eap_tls_isDone;
453         eap->getKey = eap_tls_getKey;
454         eap->isSuccess = eap_tls_isSuccess;
455         eap->get_emsk = eap_tls_get_emsk;
456
457         ret = eap_server_method_register(eap);
458         if (ret)
459                 eap_server_method_free(eap);
460         return ret;
461 }
462 #endif /* CONFIG_HS20 */