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->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->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
201check_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
228static 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
255static 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
304static 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
348static 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
355static 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
390static 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
433static 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
440static 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
452int 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
477int 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
504int 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 */
528