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
17static void eap_tls_reset(struct eap_sm *sm, void *priv);
18
19
20struct 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
29static 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
46static 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
57static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
58{
59	struct wpabuf *buf;
60
61	if (!sm->cfg->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
72static 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
96static 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
118static 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
140static 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
150static 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
171static 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->cfg->ssl_ctx,
191					       data->ssl.conn))
192			data->established = 1;
193		break;
194	default:
195		wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
196			   __func__, data->state);
197		return NULL;
198	}
199
200	res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
201
202check_established:
203	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
204		/* TLS handshake has been completed and there are no more
205		 * fragments waiting to be sent out. */
206		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
207		eap_tls_state(data, SUCCESS);
208		eap_tls_valid_session(sm, data);
209		if (sm->serial_num) {
210			char user[128];
211			int user_len;
212
213			user_len = os_snprintf(user, sizeof(user), "cert-%s",
214					       sm->serial_num);
215			if (eap_user_get(sm, (const u8 *) user, user_len,
216					 data->phase2) < 0)
217				wpa_printf(MSG_DEBUG,
218					   "EAP-TLS: No user entry found based on the serial number of the client certificate ");
219			else
220				wpa_printf(MSG_DEBUG,
221					   "EAP-TLS: Updated user entry based on the serial number of the client certificate ");
222		}
223	}
224
225	return res;
226}
227
228
229static bool eap_tls_check(struct eap_sm *sm, void *priv,
230			  struct wpabuf *respData)
231{
232	struct eap_tls_data *data = priv;
233	const u8 *pos;
234	size_t len;
235
236	if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
237		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
238				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
239				       &len);
240	else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
241		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
242				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
243				       &len);
244	else
245		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
246				       respData, &len);
247	if (pos == NULL || len < 1) {
248		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
249		return true;
250	}
251
252	return false;
253}
254
255
256static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
257				const struct wpabuf *respData)
258{
259	struct eap_tls_data *data = priv;
260	if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
261		wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
262			   "handshake message");
263		return;
264	}
265	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
266		eap_tls_state(data, FAILURE);
267		return;
268	}
269}
270
271
272static void eap_tls_process(struct eap_sm *sm, void *priv,
273			    struct wpabuf *respData)
274{
275	struct eap_tls_data *data = priv;
276	const struct wpabuf *buf;
277	const u8 *pos;
278
279	if (eap_server_tls_process(sm, &data->ssl, respData, data,
280				   data->eap_type, NULL, eap_tls_process_msg) <
281	    0) {
282		eap_tls_state(data, FAILURE);
283		return;
284	}
285
286	if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
287	    !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
288		return;
289
290	buf = tls_connection_get_success_data(data->ssl.conn);
291	if (!buf || wpabuf_len(buf) < 1) {
292		wpa_printf(MSG_DEBUG,
293			   "EAP-TLS: No success data in resumed session - reject attempt");
294		eap_tls_state(data, FAILURE);
295		return;
296	}
297
298	pos = wpabuf_head(buf);
299	if (*pos != data->eap_type) {
300		wpa_printf(MSG_DEBUG,
301			   "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
302			   *pos);
303		eap_tls_state(data, FAILURE);
304		return;
305	}
306
307	wpa_printf(MSG_DEBUG,
308		   "EAP-TLS: Resuming previous session");
309	eap_tls_state(data, SUCCESS);
310	tls_connection_set_success_data_resumed(data->ssl.conn);
311	/* TODO: Cache serial number with session and update EAP user
312	 * information based on the cached serial number */
313}
314
315
316static bool eap_tls_isDone(struct eap_sm *sm, void *priv)
317{
318	struct eap_tls_data *data = priv;
319	return data->state == SUCCESS || data->state == FAILURE;
320}
321
322
323static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
324{
325	struct eap_tls_data *data = priv;
326	u8 *eapKeyData;
327	const char *label;
328	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
329	const u8 *context = NULL;
330	size_t context_len = 0;
331
332	if (data->state != SUCCESS)
333		return NULL;
334
335	if (data->ssl.tls_v13) {
336		label = "EXPORTER_EAP_TLS_Key_Material";
337		context = eap_tls13_context;
338		context_len = 1;
339	} else {
340		label = "client EAP encryption";
341	}
342	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
343					       context, context_len,
344					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
345	if (eapKeyData) {
346		*len = EAP_TLS_KEY_LEN;
347		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
348			    eapKeyData, EAP_TLS_KEY_LEN);
349		os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
350	} else {
351		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
352	}
353
354	return eapKeyData;
355}
356
357
358static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
359{
360	struct eap_tls_data *data = priv;
361	u8 *eapKeyData, *emsk;
362	const char *label;
363	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
364	const u8 *context = NULL;
365	size_t context_len = 0;
366
367	if (data->state != SUCCESS)
368		return NULL;
369
370	if (data->ssl.tls_v13) {
371		label = "EXPORTER_EAP_TLS_Key_Material";
372		context = eap_tls13_context;
373		context_len = 1;
374	} else {
375		label = "client EAP encryption";
376	}
377	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
378					       context, context_len,
379					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
380	if (eapKeyData) {
381		emsk = os_malloc(EAP_EMSK_LEN);
382		if (emsk)
383			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
384				  EAP_EMSK_LEN);
385		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
386	} else
387		emsk = NULL;
388
389	if (emsk) {
390		*len = EAP_EMSK_LEN;
391		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
392			    emsk, EAP_EMSK_LEN);
393	} else {
394		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
395	}
396
397	return emsk;
398}
399
400
401static bool eap_tls_isSuccess(struct eap_sm *sm, void *priv)
402{
403	struct eap_tls_data *data = priv;
404	return data->state == SUCCESS;
405}
406
407
408static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
409{
410	struct eap_tls_data *data = priv;
411
412	if (data->state != SUCCESS)
413		return NULL;
414
415	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
416						len);
417}
418
419
420int eap_server_tls_register(void)
421{
422	struct eap_method *eap;
423
424	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
425				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
426	if (eap == NULL)
427		return -1;
428
429	eap->init = eap_tls_init;
430	eap->reset = eap_tls_reset;
431	eap->buildReq = eap_tls_buildReq;
432	eap->check = eap_tls_check;
433	eap->process = eap_tls_process;
434	eap->isDone = eap_tls_isDone;
435	eap->getKey = eap_tls_getKey;
436	eap->isSuccess = eap_tls_isSuccess;
437	eap->get_emsk = eap_tls_get_emsk;
438	eap->getSessionId = eap_tls_get_session_id;
439
440	return eap_server_method_register(eap);
441}
442
443
444#ifdef EAP_SERVER_UNAUTH_TLS
445int eap_server_unauth_tls_register(void)
446{
447	struct eap_method *eap;
448
449	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
450				      EAP_VENDOR_UNAUTH_TLS,
451				      EAP_VENDOR_TYPE_UNAUTH_TLS,
452				      "UNAUTH-TLS");
453	if (eap == NULL)
454		return -1;
455
456	eap->init = eap_unauth_tls_init;
457	eap->reset = eap_tls_reset;
458	eap->buildReq = eap_tls_buildReq;
459	eap->check = eap_tls_check;
460	eap->process = eap_tls_process;
461	eap->isDone = eap_tls_isDone;
462	eap->getKey = eap_tls_getKey;
463	eap->isSuccess = eap_tls_isSuccess;
464	eap->get_emsk = eap_tls_get_emsk;
465
466	return eap_server_method_register(eap);
467}
468#endif /* EAP_SERVER_UNAUTH_TLS */
469
470
471#ifdef CONFIG_HS20
472int eap_server_wfa_unauth_tls_register(void)
473{
474	struct eap_method *eap;
475
476	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
477				      EAP_VENDOR_WFA_NEW,
478				      EAP_VENDOR_WFA_UNAUTH_TLS,
479				      "WFA-UNAUTH-TLS");
480	if (eap == NULL)
481		return -1;
482
483	eap->init = eap_wfa_unauth_tls_init;
484	eap->reset = eap_tls_reset;
485	eap->buildReq = eap_tls_buildReq;
486	eap->check = eap_tls_check;
487	eap->process = eap_tls_process;
488	eap->isDone = eap_tls_isDone;
489	eap->getKey = eap_tls_getKey;
490	eap->isSuccess = eap_tls_isSuccess;
491	eap->get_emsk = eap_tls_get_emsk;
492
493	return eap_server_method_register(eap);
494}
495#endif /* CONFIG_HS20 */
496