eap_server_tls.c revision 281806
1214501Srpaulo/*
2214501Srpaulo * hostapd / EAP-TLS (RFC 2716)
3214501Srpaulo * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "eap_i.h"
13214501Srpaulo#include "eap_tls_common.h"
14214501Srpaulo#include "crypto/tls.h"
15214501Srpaulo
16214501Srpaulo
17214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv);
18214501Srpaulo
19214501Srpaulo
20214501Srpaulostruct eap_tls_data {
21214501Srpaulo	struct eap_ssl_data ssl;
22214501Srpaulo	enum { START, CONTINUE, SUCCESS, FAILURE } state;
23214501Srpaulo	int established;
24252726Srpaulo	u8 eap_type;
25214501Srpaulo};
26214501Srpaulo
27214501Srpaulo
28214501Srpaulostatic const char * eap_tls_state_txt(int state)
29214501Srpaulo{
30214501Srpaulo	switch (state) {
31214501Srpaulo	case START:
32214501Srpaulo		return "START";
33214501Srpaulo	case CONTINUE:
34214501Srpaulo		return "CONTINUE";
35214501Srpaulo	case SUCCESS:
36214501Srpaulo		return "SUCCESS";
37214501Srpaulo	case FAILURE:
38214501Srpaulo		return "FAILURE";
39214501Srpaulo	default:
40214501Srpaulo		return "Unknown?!";
41214501Srpaulo	}
42214501Srpaulo}
43214501Srpaulo
44214501Srpaulo
45214501Srpaulostatic void eap_tls_state(struct eap_tls_data *data, int state)
46214501Srpaulo{
47214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
48214501Srpaulo		   eap_tls_state_txt(data->state),
49214501Srpaulo		   eap_tls_state_txt(state));
50214501Srpaulo	data->state = state;
51214501Srpaulo}
52214501Srpaulo
53214501Srpaulo
54214501Srpaulostatic void * eap_tls_init(struct eap_sm *sm)
55214501Srpaulo{
56214501Srpaulo	struct eap_tls_data *data;
57214501Srpaulo
58214501Srpaulo	data = os_zalloc(sizeof(*data));
59214501Srpaulo	if (data == NULL)
60214501Srpaulo		return NULL;
61214501Srpaulo	data->state = START;
62214501Srpaulo
63214501Srpaulo	if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
64214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
65214501Srpaulo		eap_tls_reset(sm, data);
66214501Srpaulo		return NULL;
67214501Srpaulo	}
68214501Srpaulo
69252726Srpaulo	data->eap_type = EAP_TYPE_TLS;
70252726Srpaulo
71214501Srpaulo	return data;
72214501Srpaulo}
73214501Srpaulo
74214501Srpaulo
75252726Srpaulo#ifdef EAP_SERVER_UNAUTH_TLS
76252726Srpaulostatic void * eap_unauth_tls_init(struct eap_sm *sm)
77252726Srpaulo{
78252726Srpaulo	struct eap_tls_data *data;
79252726Srpaulo
80252726Srpaulo	data = os_zalloc(sizeof(*data));
81252726Srpaulo	if (data == NULL)
82252726Srpaulo		return NULL;
83252726Srpaulo	data->state = START;
84252726Srpaulo
85252726Srpaulo	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
86252726Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
87252726Srpaulo		eap_tls_reset(sm, data);
88252726Srpaulo		return NULL;
89252726Srpaulo	}
90252726Srpaulo
91252726Srpaulo	data->eap_type = EAP_UNAUTH_TLS_TYPE;
92252726Srpaulo	return data;
93252726Srpaulo}
94252726Srpaulo#endif /* EAP_SERVER_UNAUTH_TLS */
95252726Srpaulo
96252726Srpaulo
97281806Srpaulo#ifdef CONFIG_HS20
98281806Srpaulostatic void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
99281806Srpaulo{
100281806Srpaulo	struct eap_tls_data *data;
101281806Srpaulo
102281806Srpaulo	data = os_zalloc(sizeof(*data));
103281806Srpaulo	if (data == NULL)
104281806Srpaulo		return NULL;
105281806Srpaulo	data->state = START;
106281806Srpaulo
107281806Srpaulo	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
108281806Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
109281806Srpaulo		eap_tls_reset(sm, data);
110281806Srpaulo		return NULL;
111281806Srpaulo	}
112281806Srpaulo
113281806Srpaulo	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
114281806Srpaulo	return data;
115281806Srpaulo}
116281806Srpaulo#endif /* CONFIG_HS20 */
117281806Srpaulo
118281806Srpaulo
119214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv)
120214501Srpaulo{
121214501Srpaulo	struct eap_tls_data *data = priv;
122214501Srpaulo	if (data == NULL)
123214501Srpaulo		return;
124214501Srpaulo	eap_server_tls_ssl_deinit(sm, &data->ssl);
125214501Srpaulo	os_free(data);
126214501Srpaulo}
127214501Srpaulo
128214501Srpaulo
129214501Srpaulostatic struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
130214501Srpaulo					   struct eap_tls_data *data, u8 id)
131214501Srpaulo{
132214501Srpaulo	struct wpabuf *req;
133214501Srpaulo
134252726Srpaulo	req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
135214501Srpaulo	if (req == NULL) {
136214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
137214501Srpaulo			   "request");
138214501Srpaulo		eap_tls_state(data, FAILURE);
139214501Srpaulo		return NULL;
140214501Srpaulo	}
141214501Srpaulo
142214501Srpaulo	wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
143214501Srpaulo
144214501Srpaulo	eap_tls_state(data, CONTINUE);
145214501Srpaulo
146214501Srpaulo	return req;
147214501Srpaulo}
148214501Srpaulo
149214501Srpaulo
150214501Srpaulostatic struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
151214501Srpaulo{
152214501Srpaulo	struct eap_tls_data *data = priv;
153214501Srpaulo	struct wpabuf *res;
154214501Srpaulo
155214501Srpaulo	if (data->ssl.state == FRAG_ACK) {
156252726Srpaulo		return eap_server_tls_build_ack(id, data->eap_type, 0);
157214501Srpaulo	}
158214501Srpaulo
159214501Srpaulo	if (data->ssl.state == WAIT_FRAG_ACK) {
160252726Srpaulo		res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
161214501Srpaulo					       id);
162214501Srpaulo		goto check_established;
163214501Srpaulo	}
164214501Srpaulo
165214501Srpaulo	switch (data->state) {
166214501Srpaulo	case START:
167214501Srpaulo		return eap_tls_build_start(sm, data, id);
168214501Srpaulo	case CONTINUE:
169214501Srpaulo		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
170214501Srpaulo			data->established = 1;
171214501Srpaulo		break;
172214501Srpaulo	default:
173214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
174214501Srpaulo			   __func__, data->state);
175214501Srpaulo		return NULL;
176214501Srpaulo	}
177214501Srpaulo
178252726Srpaulo	res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
179214501Srpaulo
180214501Srpaulocheck_established:
181214501Srpaulo	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
182214501Srpaulo		/* TLS handshake has been completed and there are no more
183214501Srpaulo		 * fragments waiting to be sent out. */
184214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
185214501Srpaulo		eap_tls_state(data, SUCCESS);
186214501Srpaulo	}
187214501Srpaulo
188214501Srpaulo	return res;
189214501Srpaulo}
190214501Srpaulo
191214501Srpaulo
192214501Srpaulostatic Boolean eap_tls_check(struct eap_sm *sm, void *priv,
193214501Srpaulo			     struct wpabuf *respData)
194214501Srpaulo{
195252726Srpaulo	struct eap_tls_data *data = priv;
196214501Srpaulo	const u8 *pos;
197214501Srpaulo	size_t len;
198214501Srpaulo
199252726Srpaulo	if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
200252726Srpaulo		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
201252726Srpaulo				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
202252726Srpaulo				       &len);
203281806Srpaulo	else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
204281806Srpaulo		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
205281806Srpaulo				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
206281806Srpaulo				       &len);
207252726Srpaulo	else
208252726Srpaulo		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
209252726Srpaulo				       respData, &len);
210214501Srpaulo	if (pos == NULL || len < 1) {
211214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
212214501Srpaulo		return TRUE;
213214501Srpaulo	}
214214501Srpaulo
215214501Srpaulo	return FALSE;
216214501Srpaulo}
217214501Srpaulo
218214501Srpaulo
219214501Srpaulostatic void eap_tls_process_msg(struct eap_sm *sm, void *priv,
220214501Srpaulo				const struct wpabuf *respData)
221214501Srpaulo{
222214501Srpaulo	struct eap_tls_data *data = priv;
223214501Srpaulo	if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
224214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
225214501Srpaulo			   "handshake message");
226214501Srpaulo		return;
227214501Srpaulo	}
228214501Srpaulo	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
229214501Srpaulo		eap_tls_state(data, FAILURE);
230214501Srpaulo}
231214501Srpaulo
232214501Srpaulo
233214501Srpaulostatic void eap_tls_process(struct eap_sm *sm, void *priv,
234214501Srpaulo			    struct wpabuf *respData)
235214501Srpaulo{
236214501Srpaulo	struct eap_tls_data *data = priv;
237214501Srpaulo	if (eap_server_tls_process(sm, &data->ssl, respData, data,
238252726Srpaulo				   data->eap_type, NULL, eap_tls_process_msg) <
239214501Srpaulo	    0)
240214501Srpaulo		eap_tls_state(data, FAILURE);
241214501Srpaulo}
242214501Srpaulo
243214501Srpaulo
244214501Srpaulostatic Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
245214501Srpaulo{
246214501Srpaulo	struct eap_tls_data *data = priv;
247214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
248214501Srpaulo}
249214501Srpaulo
250214501Srpaulo
251214501Srpaulostatic u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
252214501Srpaulo{
253214501Srpaulo	struct eap_tls_data *data = priv;
254214501Srpaulo	u8 *eapKeyData;
255214501Srpaulo
256214501Srpaulo	if (data->state != SUCCESS)
257214501Srpaulo		return NULL;
258214501Srpaulo
259214501Srpaulo	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
260214501Srpaulo					       "client EAP encryption",
261214501Srpaulo					       EAP_TLS_KEY_LEN);
262214501Srpaulo	if (eapKeyData) {
263214501Srpaulo		*len = EAP_TLS_KEY_LEN;
264214501Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
265214501Srpaulo			    eapKeyData, EAP_TLS_KEY_LEN);
266214501Srpaulo	} else {
267214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
268214501Srpaulo	}
269214501Srpaulo
270214501Srpaulo	return eapKeyData;
271214501Srpaulo}
272214501Srpaulo
273214501Srpaulo
274214501Srpaulostatic u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
275214501Srpaulo{
276214501Srpaulo	struct eap_tls_data *data = priv;
277214501Srpaulo	u8 *eapKeyData, *emsk;
278214501Srpaulo
279214501Srpaulo	if (data->state != SUCCESS)
280214501Srpaulo		return NULL;
281214501Srpaulo
282214501Srpaulo	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
283214501Srpaulo					       "client EAP encryption",
284214501Srpaulo					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
285214501Srpaulo	if (eapKeyData) {
286214501Srpaulo		emsk = os_malloc(EAP_EMSK_LEN);
287214501Srpaulo		if (emsk)
288214501Srpaulo			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
289214501Srpaulo				  EAP_EMSK_LEN);
290281806Srpaulo		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
291214501Srpaulo	} else
292214501Srpaulo		emsk = NULL;
293214501Srpaulo
294214501Srpaulo	if (emsk) {
295214501Srpaulo		*len = EAP_EMSK_LEN;
296214501Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
297214501Srpaulo			    emsk, EAP_EMSK_LEN);
298214501Srpaulo	} else {
299214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
300214501Srpaulo	}
301214501Srpaulo
302214501Srpaulo	return emsk;
303214501Srpaulo}
304214501Srpaulo
305214501Srpaulo
306214501Srpaulostatic Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
307214501Srpaulo{
308214501Srpaulo	struct eap_tls_data *data = priv;
309214501Srpaulo	return data->state == SUCCESS;
310214501Srpaulo}
311214501Srpaulo
312214501Srpaulo
313281806Srpaulostatic u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
314281806Srpaulo{
315281806Srpaulo	struct eap_tls_data *data = priv;
316281806Srpaulo
317281806Srpaulo	if (data->state != SUCCESS)
318281806Srpaulo		return NULL;
319281806Srpaulo
320281806Srpaulo	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
321281806Srpaulo						len);
322281806Srpaulo}
323281806Srpaulo
324281806Srpaulo
325214501Srpauloint eap_server_tls_register(void)
326214501Srpaulo{
327214501Srpaulo	struct eap_method *eap;
328214501Srpaulo	int ret;
329214501Srpaulo
330214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
331214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
332214501Srpaulo	if (eap == NULL)
333214501Srpaulo		return -1;
334214501Srpaulo
335214501Srpaulo	eap->init = eap_tls_init;
336214501Srpaulo	eap->reset = eap_tls_reset;
337214501Srpaulo	eap->buildReq = eap_tls_buildReq;
338214501Srpaulo	eap->check = eap_tls_check;
339214501Srpaulo	eap->process = eap_tls_process;
340214501Srpaulo	eap->isDone = eap_tls_isDone;
341214501Srpaulo	eap->getKey = eap_tls_getKey;
342214501Srpaulo	eap->isSuccess = eap_tls_isSuccess;
343214501Srpaulo	eap->get_emsk = eap_tls_get_emsk;
344281806Srpaulo	eap->getSessionId = eap_tls_get_session_id;
345214501Srpaulo
346214501Srpaulo	ret = eap_server_method_register(eap);
347214501Srpaulo	if (ret)
348214501Srpaulo		eap_server_method_free(eap);
349214501Srpaulo	return ret;
350214501Srpaulo}
351252726Srpaulo
352252726Srpaulo
353252726Srpaulo#ifdef EAP_SERVER_UNAUTH_TLS
354252726Srpauloint eap_server_unauth_tls_register(void)
355252726Srpaulo{
356252726Srpaulo	struct eap_method *eap;
357252726Srpaulo	int ret;
358252726Srpaulo
359252726Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
360252726Srpaulo				      EAP_VENDOR_UNAUTH_TLS,
361252726Srpaulo				      EAP_VENDOR_TYPE_UNAUTH_TLS,
362252726Srpaulo				      "UNAUTH-TLS");
363252726Srpaulo	if (eap == NULL)
364252726Srpaulo		return -1;
365252726Srpaulo
366252726Srpaulo	eap->init = eap_unauth_tls_init;
367252726Srpaulo	eap->reset = eap_tls_reset;
368252726Srpaulo	eap->buildReq = eap_tls_buildReq;
369252726Srpaulo	eap->check = eap_tls_check;
370252726Srpaulo	eap->process = eap_tls_process;
371252726Srpaulo	eap->isDone = eap_tls_isDone;
372252726Srpaulo	eap->getKey = eap_tls_getKey;
373252726Srpaulo	eap->isSuccess = eap_tls_isSuccess;
374252726Srpaulo	eap->get_emsk = eap_tls_get_emsk;
375252726Srpaulo
376252726Srpaulo	ret = eap_server_method_register(eap);
377252726Srpaulo	if (ret)
378252726Srpaulo		eap_server_method_free(eap);
379252726Srpaulo	return ret;
380252726Srpaulo}
381252726Srpaulo#endif /* EAP_SERVER_UNAUTH_TLS */
382281806Srpaulo
383281806Srpaulo
384281806Srpaulo#ifdef CONFIG_HS20
385281806Srpauloint eap_server_wfa_unauth_tls_register(void)
386281806Srpaulo{
387281806Srpaulo	struct eap_method *eap;
388281806Srpaulo	int ret;
389281806Srpaulo
390281806Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
391281806Srpaulo				      EAP_VENDOR_WFA_NEW,
392281806Srpaulo				      EAP_VENDOR_WFA_UNAUTH_TLS,
393281806Srpaulo				      "WFA-UNAUTH-TLS");
394281806Srpaulo	if (eap == NULL)
395281806Srpaulo		return -1;
396281806Srpaulo
397281806Srpaulo	eap->init = eap_wfa_unauth_tls_init;
398281806Srpaulo	eap->reset = eap_tls_reset;
399281806Srpaulo	eap->buildReq = eap_tls_buildReq;
400281806Srpaulo	eap->check = eap_tls_check;
401281806Srpaulo	eap->process = eap_tls_process;
402281806Srpaulo	eap->isDone = eap_tls_isDone;
403281806Srpaulo	eap->getKey = eap_tls_getKey;
404281806Srpaulo	eap->isSuccess = eap_tls_isSuccess;
405281806Srpaulo	eap->get_emsk = eap_tls_get_emsk;
406281806Srpaulo
407281806Srpaulo	ret = eap_server_method_register(eap);
408281806Srpaulo	if (ret)
409281806Srpaulo		eap_server_method_free(eap);
410281806Srpaulo	return ret;
411281806Srpaulo}
412281806Srpaulo#endif /* CONFIG_HS20 */
413