eap_server_tls.c revision 337817
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};
26
27
28static 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
45static 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
56static 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
71static 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
93static 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
115static 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
137static 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
147static 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
168static 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
198check_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
211static 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
238static 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
252static 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
294static 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
301static 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
324static 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
356static 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
363static 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
375int eap_server_tls_register(void)
376{
377	struct eap_method *eap;
378
379	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
380				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
381	if (eap == NULL)
382		return -1;
383
384	eap->init = eap_tls_init;
385	eap->reset = eap_tls_reset;
386	eap->buildReq = eap_tls_buildReq;
387	eap->check = eap_tls_check;
388	eap->process = eap_tls_process;
389	eap->isDone = eap_tls_isDone;
390	eap->getKey = eap_tls_getKey;
391	eap->isSuccess = eap_tls_isSuccess;
392	eap->get_emsk = eap_tls_get_emsk;
393	eap->getSessionId = eap_tls_get_session_id;
394
395	return eap_server_method_register(eap);
396}
397
398
399#ifdef EAP_SERVER_UNAUTH_TLS
400int eap_server_unauth_tls_register(void)
401{
402	struct eap_method *eap;
403
404	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
405				      EAP_VENDOR_UNAUTH_TLS,
406				      EAP_VENDOR_TYPE_UNAUTH_TLS,
407				      "UNAUTH-TLS");
408	if (eap == NULL)
409		return -1;
410
411	eap->init = eap_unauth_tls_init;
412	eap->reset = eap_tls_reset;
413	eap->buildReq = eap_tls_buildReq;
414	eap->check = eap_tls_check;
415	eap->process = eap_tls_process;
416	eap->isDone = eap_tls_isDone;
417	eap->getKey = eap_tls_getKey;
418	eap->isSuccess = eap_tls_isSuccess;
419	eap->get_emsk = eap_tls_get_emsk;
420
421	return eap_server_method_register(eap);
422}
423#endif /* EAP_SERVER_UNAUTH_TLS */
424
425
426#ifdef CONFIG_HS20
427int eap_server_wfa_unauth_tls_register(void)
428{
429	struct eap_method *eap;
430
431	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
432				      EAP_VENDOR_WFA_NEW,
433				      EAP_VENDOR_WFA_UNAUTH_TLS,
434				      "WFA-UNAUTH-TLS");
435	if (eap == NULL)
436		return -1;
437
438	eap->init = eap_wfa_unauth_tls_init;
439	eap->reset = eap_tls_reset;
440	eap->buildReq = eap_tls_buildReq;
441	eap->check = eap_tls_check;
442	eap->process = eap_tls_process;
443	eap->isDone = eap_tls_isDone;
444	eap->getKey = eap_tls_getKey;
445	eap->isSuccess = eap_tls_isSuccess;
446	eap->get_emsk = eap_tls_get_emsk;
447
448	return eap_server_method_register(eap);
449}
450#endif /* CONFIG_HS20 */
451