vendor/wpa_supplicant: upgrade from 2.1 to 2.9
[dragonfly.git] / contrib / wpa_supplicant / 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         int phase2;
26 };
27
28
29 static const char * eap_tls_state_txt(int state)
30 {
31         switch (state) {
32         case START:
33                 return "START";
34         case CONTINUE:
35                 return "CONTINUE";
36         case SUCCESS:
37                 return "SUCCESS";
38         case FAILURE:
39                 return "FAILURE";
40         default:
41                 return "Unknown?!";
42         }
43 }
44
45
46 static void eap_tls_state(struct eap_tls_data *data, int state)
47 {
48         wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
49                    eap_tls_state_txt(data->state),
50                    eap_tls_state_txt(state));
51         data->state = state;
52         if (state == FAILURE)
53                 tls_connection_remove_session(data->ssl.conn);
54 }
55
56
57 static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
58 {
59         struct wpabuf *buf;
60
61         if (!sm->tls_session_lifetime)
62                 return;
63
64         buf = wpabuf_alloc(1);
65         if (!buf)
66                 return;
67         wpabuf_put_u8(buf, data->eap_type);
68         tls_connection_set_success_data(data->ssl.conn, buf);
69 }
70
71
72 static void * eap_tls_init(struct eap_sm *sm)
73 {
74         struct eap_tls_data *data;
75
76         data = os_zalloc(sizeof(*data));
77         if (data == NULL)
78                 return NULL;
79         data->state = START;
80
81         if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
82                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
83                 eap_tls_reset(sm, data);
84                 return NULL;
85         }
86
87         data->eap_type = EAP_TYPE_TLS;
88
89         data->phase2 = sm->init_phase2;
90
91         return data;
92 }
93
94
95 #ifdef EAP_SERVER_UNAUTH_TLS
96 static void * eap_unauth_tls_init(struct eap_sm *sm)
97 {
98         struct eap_tls_data *data;
99
100         data = os_zalloc(sizeof(*data));
101         if (data == NULL)
102                 return NULL;
103         data->state = START;
104
105         if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
106                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
107                 eap_tls_reset(sm, data);
108                 return NULL;
109         }
110
111         data->eap_type = EAP_UNAUTH_TLS_TYPE;
112         return data;
113 }
114 #endif /* EAP_SERVER_UNAUTH_TLS */
115
116
117 #ifdef CONFIG_HS20
118 static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
119 {
120         struct eap_tls_data *data;
121
122         data = os_zalloc(sizeof(*data));
123         if (data == NULL)
124                 return NULL;
125         data->state = START;
126
127         if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
128                                     EAP_WFA_UNAUTH_TLS_TYPE)) {
129                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
130                 eap_tls_reset(sm, data);
131                 return NULL;
132         }
133
134         data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
135         return data;
136 }
137 #endif /* CONFIG_HS20 */
138
139
140 static void eap_tls_reset(struct eap_sm *sm, void *priv)
141 {
142         struct eap_tls_data *data = priv;
143         if (data == NULL)
144                 return;
145         eap_server_tls_ssl_deinit(sm, &data->ssl);
146         os_free(data);
147 }
148
149
150 static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
151                                            struct eap_tls_data *data, u8 id)
152 {
153         struct wpabuf *req;
154
155         req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
156         if (req == NULL) {
157                 wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
158                            "request");
159                 eap_tls_state(data, FAILURE);
160                 return NULL;
161         }
162
163         wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
164
165         eap_tls_state(data, CONTINUE);
166
167         return req;
168 }
169
170
171 static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
172 {
173         struct eap_tls_data *data = priv;
174         struct wpabuf *res;
175
176         if (data->ssl.state == FRAG_ACK) {
177                 return eap_server_tls_build_ack(id, data->eap_type, 0);
178         }
179
180         if (data->ssl.state == WAIT_FRAG_ACK) {
181                 res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
182                                                id);
183                 goto check_established;
184         }
185
186         switch (data->state) {
187         case START:
188                 return eap_tls_build_start(sm, data, id);
189         case CONTINUE:
190                 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
191                         data->established = 1;
192                 break;
193         default:
194                 wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
195                            __func__, data->state);
196                 return NULL;
197         }
198
199         res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
200
201 check_established:
202         if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
203                 /* TLS handshake has been completed and there are no more
204                  * fragments waiting to be sent out. */
205                 wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
206                 eap_tls_state(data, SUCCESS);
207                 eap_tls_valid_session(sm, data);
208                 if (sm->serial_num) {
209                         char user[128];
210                         int user_len;
211
212                         user_len = os_snprintf(user, sizeof(user), "cert-%s",
213                                                sm->serial_num);
214                         if (eap_user_get(sm, (const u8 *) user, user_len,
215                                          data->phase2) < 0)
216                                 wpa_printf(MSG_DEBUG,
217                                            "EAP-TLS: No user entry found based on the serial number of the client certificate ");
218                         else
219                                 wpa_printf(MSG_DEBUG,
220                                            "EAP-TLS: Updated user entry based on the serial number of the client certificate ");
221                 }
222         }
223
224         return res;
225 }
226
227
228 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
229                              struct wpabuf *respData)
230 {
231         struct eap_tls_data *data = priv;
232         const u8 *pos;
233         size_t len;
234
235         if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
236                 pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
237                                        EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
238                                        &len);
239         else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
240                 pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
241                                        EAP_VENDOR_WFA_UNAUTH_TLS, respData,
242                                        &len);
243         else
244                 pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
245                                        respData, &len);
246         if (pos == NULL || len < 1) {
247                 wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
248                 return TRUE;
249         }
250
251         return FALSE;
252 }
253
254
255 static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
256                                 const struct wpabuf *respData)
257 {
258         struct eap_tls_data *data = priv;
259         if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
260                 wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
261                            "handshake message");
262                 return;
263         }
264         if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
265                 eap_tls_state(data, FAILURE);
266                 return;
267         }
268
269         if (data->ssl.tls_v13 &&
270             tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
271                 struct wpabuf *plain, *encr;
272
273                 wpa_printf(MSG_DEBUG,
274                            "EAP-TLS: Send empty application data to indicate end of exchange");
275                 /* FIX: This should be an empty application data based on
276                  * draft-ietf-emu-eap-tls13-05, but OpenSSL does not allow zero
277                  * length payload (SSL_write() documentation explicitly
278                  * describes this as not allowed), so work around that for now
279                  * by sending out a payload of one octet. Hopefully the draft
280                  * specification will change to allow this so that no crypto
281                  * library changes are needed. */
282                 plain = wpabuf_alloc(1);
283                 if (!plain)
284                         return;
285                 wpabuf_put_u8(plain, 0);
286                 encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
287                 wpabuf_free(plain);
288                 if (!encr)
289                         return;
290                 if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
291                         wpa_printf(MSG_INFO,
292                                    "EAP-TLS: Failed to resize output buffer");
293                         wpabuf_free(encr);
294                         return;
295                 }
296                 wpabuf_put_buf(data->ssl.tls_out, encr);
297                 wpa_hexdump_buf(MSG_DEBUG,
298                                 "EAP-TLS: Data appended to the message", encr);
299                 wpabuf_free(encr);
300         }
301 }
302
303
304 static void eap_tls_process(struct eap_sm *sm, void *priv,
305                             struct wpabuf *respData)
306 {
307         struct eap_tls_data *data = priv;
308         const struct wpabuf *buf;
309         const u8 *pos;
310
311         if (eap_server_tls_process(sm, &data->ssl, respData, data,
312                                    data->eap_type, NULL, eap_tls_process_msg) <
313             0) {
314                 eap_tls_state(data, FAILURE);
315                 return;
316         }
317
318         if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
319             !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
320                 return;
321
322         buf = tls_connection_get_success_data(data->ssl.conn);
323         if (!buf || wpabuf_len(buf) < 1) {
324                 wpa_printf(MSG_DEBUG,
325                            "EAP-TLS: No success data in resumed session - reject attempt");
326                 eap_tls_state(data, FAILURE);
327                 return;
328         }
329
330         pos = wpabuf_head(buf);
331         if (*pos != data->eap_type) {
332                 wpa_printf(MSG_DEBUG,
333                            "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
334                            *pos);
335                 eap_tls_state(data, FAILURE);
336                 return;
337         }
338
339         wpa_printf(MSG_DEBUG,
340                    "EAP-TLS: Resuming previous session");
341         eap_tls_state(data, SUCCESS);
342         tls_connection_set_success_data_resumed(data->ssl.conn);
343         /* TODO: Cache serial number with session and update EAP user
344          * information based on the cached serial number */
345 }
346
347
348 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
349 {
350         struct eap_tls_data *data = priv;
351         return data->state == SUCCESS || data->state == FAILURE;
352 }
353
354
355 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
356 {
357         struct eap_tls_data *data = priv;
358         u8 *eapKeyData;
359         const char *label;
360         const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
361         const u8 *context = NULL;
362         size_t context_len = 0;
363
364         if (data->state != SUCCESS)
365                 return NULL;
366
367         if (data->ssl.tls_v13) {
368                 label = "EXPORTER_EAP_TLS_Key_Material";
369                 context = eap_tls13_context;
370                 context_len = 1;
371         } else {
372                 label = "client EAP encryption";
373         }
374         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
375                                                context, context_len,
376                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
377         if (eapKeyData) {
378                 *len = EAP_TLS_KEY_LEN;
379                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
380                             eapKeyData, EAP_TLS_KEY_LEN);
381                 os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
382         } else {
383                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
384         }
385
386         return eapKeyData;
387 }
388
389
390 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
391 {
392         struct eap_tls_data *data = priv;
393         u8 *eapKeyData, *emsk;
394         const char *label;
395         const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
396         const u8 *context = NULL;
397         size_t context_len = 0;
398
399         if (data->state != SUCCESS)
400                 return NULL;
401
402         if (data->ssl.tls_v13) {
403                 label = "EXPORTER_EAP_TLS_Key_Material";
404                 context = eap_tls13_context;
405                 context_len = 1;
406         } else {
407                 label = "client EAP encryption";
408         }
409         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
410                                                context, context_len,
411                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
412         if (eapKeyData) {
413                 emsk = os_malloc(EAP_EMSK_LEN);
414                 if (emsk)
415                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
416                                   EAP_EMSK_LEN);
417                 bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
418         } else
419                 emsk = NULL;
420
421         if (emsk) {
422                 *len = EAP_EMSK_LEN;
423                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
424                             emsk, EAP_EMSK_LEN);
425         } else {
426                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
427         }
428
429         return emsk;
430 }
431
432
433 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
434 {
435         struct eap_tls_data *data = priv;
436         return data->state == SUCCESS;
437 }
438
439
440 static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
441 {
442         struct eap_tls_data *data = priv;
443
444         if (data->state != SUCCESS)
445                 return NULL;
446
447         return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
448                                                 len);
449 }
450
451
452 int eap_server_tls_register(void)
453 {
454         struct eap_method *eap;
455
456         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
457                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
458         if (eap == NULL)
459                 return -1;
460
461         eap->init = eap_tls_init;
462         eap->reset = eap_tls_reset;
463         eap->buildReq = eap_tls_buildReq;
464         eap->check = eap_tls_check;
465         eap->process = eap_tls_process;
466         eap->isDone = eap_tls_isDone;
467         eap->getKey = eap_tls_getKey;
468         eap->isSuccess = eap_tls_isSuccess;
469         eap->get_emsk = eap_tls_get_emsk;
470         eap->getSessionId = eap_tls_get_session_id;
471
472         return eap_server_method_register(eap);
473 }
474
475
476 #ifdef EAP_SERVER_UNAUTH_TLS
477 int eap_server_unauth_tls_register(void)
478 {
479         struct eap_method *eap;
480
481         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
482                                       EAP_VENDOR_UNAUTH_TLS,
483                                       EAP_VENDOR_TYPE_UNAUTH_TLS,
484                                       "UNAUTH-TLS");
485         if (eap == NULL)
486                 return -1;
487
488         eap->init = eap_unauth_tls_init;
489         eap->reset = eap_tls_reset;
490         eap->buildReq = eap_tls_buildReq;
491         eap->check = eap_tls_check;
492         eap->process = eap_tls_process;
493         eap->isDone = eap_tls_isDone;
494         eap->getKey = eap_tls_getKey;
495         eap->isSuccess = eap_tls_isSuccess;
496         eap->get_emsk = eap_tls_get_emsk;
497
498         return eap_server_method_register(eap);
499 }
500 #endif /* EAP_SERVER_UNAUTH_TLS */
501
502
503 #ifdef CONFIG_HS20
504 int eap_server_wfa_unauth_tls_register(void)
505 {
506         struct eap_method *eap;
507
508         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
509                                       EAP_VENDOR_WFA_NEW,
510                                       EAP_VENDOR_WFA_UNAUTH_TLS,
511                                       "WFA-UNAUTH-TLS");
512         if (eap == NULL)
513                 return -1;
514
515         eap->init = eap_wfa_unauth_tls_init;
516         eap->reset = eap_tls_reset;
517         eap->buildReq = eap_tls_buildReq;
518         eap->check = eap_tls_check;
519         eap->process = eap_tls_process;
520         eap->isDone = eap_tls_isDone;
521         eap->getKey = eap_tls_getKey;
522         eap->isSuccess = eap_tls_isSuccess;
523         eap->get_emsk = eap_tls_get_emsk;
524
525         return eap_server_method_register(eap);
526 }
527 #endif /* CONFIG_HS20 */