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}
52
53
54static void * eap_tls_init(struct eap_sm *sm)
55{
56	struct eap_tls_data *data;
57
58	data = os_zalloc(sizeof(*data));
59	if (data == NULL)
60		return NULL;
61	data->state = START;
62
63	if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
64		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
65		eap_tls_reset(sm, data);
66		return NULL;
67	}
68
69	data->eap_type = EAP_TYPE_TLS;
70
71	return data;
72}
73
74
75#ifdef EAP_SERVER_UNAUTH_TLS
76static void * eap_unauth_tls_init(struct eap_sm *sm)
77{
78	struct eap_tls_data *data;
79
80	data = os_zalloc(sizeof(*data));
81	if (data == NULL)
82		return NULL;
83	data->state = START;
84
85	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
86		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
87		eap_tls_reset(sm, data);
88		return NULL;
89	}
90
91	data->eap_type = EAP_UNAUTH_TLS_TYPE;
92	return data;
93}
94#endif /* EAP_SERVER_UNAUTH_TLS */
95
96
97static void eap_tls_reset(struct eap_sm *sm, void *priv)
98{
99	struct eap_tls_data *data = priv;
100	if (data == NULL)
101		return;
102	eap_server_tls_ssl_deinit(sm, &data->ssl);
103	os_free(data);
104}
105
106
107static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
108					   struct eap_tls_data *data, u8 id)
109{
110	struct wpabuf *req;
111
112	req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
113	if (req == NULL) {
114		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
115			   "request");
116		eap_tls_state(data, FAILURE);
117		return NULL;
118	}
119
120	wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
121
122	eap_tls_state(data, CONTINUE);
123
124	return req;
125}
126
127
128static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
129{
130	struct eap_tls_data *data = priv;
131	struct wpabuf *res;
132
133	if (data->ssl.state == FRAG_ACK) {
134		return eap_server_tls_build_ack(id, data->eap_type, 0);
135	}
136
137	if (data->ssl.state == WAIT_FRAG_ACK) {
138		res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
139					       id);
140		goto check_established;
141	}
142
143	switch (data->state) {
144	case START:
145		return eap_tls_build_start(sm, data, id);
146	case CONTINUE:
147		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
148			data->established = 1;
149		break;
150	default:
151		wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
152			   __func__, data->state);
153		return NULL;
154	}
155
156	res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
157
158check_established:
159	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
160		/* TLS handshake has been completed and there are no more
161		 * fragments waiting to be sent out. */
162		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
163		eap_tls_state(data, SUCCESS);
164	}
165
166	return res;
167}
168
169
170static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
171			     struct wpabuf *respData)
172{
173	struct eap_tls_data *data = priv;
174	const u8 *pos;
175	size_t len;
176
177	if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
178		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
179				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
180				       &len);
181	else
182		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
183				       respData, &len);
184	if (pos == NULL || len < 1) {
185		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
186		return TRUE;
187	}
188
189	return FALSE;
190}
191
192
193static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
194				const struct wpabuf *respData)
195{
196	struct eap_tls_data *data = priv;
197	if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
198		wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
199			   "handshake message");
200		return;
201	}
202	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
203		eap_tls_state(data, FAILURE);
204}
205
206
207static void eap_tls_process(struct eap_sm *sm, void *priv,
208			    struct wpabuf *respData)
209{
210	struct eap_tls_data *data = priv;
211	if (eap_server_tls_process(sm, &data->ssl, respData, data,
212				   data->eap_type, NULL, eap_tls_process_msg) <
213	    0)
214		eap_tls_state(data, FAILURE);
215}
216
217
218static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
219{
220	struct eap_tls_data *data = priv;
221	return data->state == SUCCESS || data->state == FAILURE;
222}
223
224
225static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
226{
227	struct eap_tls_data *data = priv;
228	u8 *eapKeyData;
229
230	if (data->state != SUCCESS)
231		return NULL;
232
233	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
234					       "client EAP encryption",
235					       EAP_TLS_KEY_LEN);
236	if (eapKeyData) {
237		*len = EAP_TLS_KEY_LEN;
238		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
239			    eapKeyData, EAP_TLS_KEY_LEN);
240	} else {
241		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
242	}
243
244	return eapKeyData;
245}
246
247
248static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
249{
250	struct eap_tls_data *data = priv;
251	u8 *eapKeyData, *emsk;
252
253	if (data->state != SUCCESS)
254		return NULL;
255
256	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
257					       "client EAP encryption",
258					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
259	if (eapKeyData) {
260		emsk = os_malloc(EAP_EMSK_LEN);
261		if (emsk)
262			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
263				  EAP_EMSK_LEN);
264		os_free(eapKeyData);
265	} else
266		emsk = NULL;
267
268	if (emsk) {
269		*len = EAP_EMSK_LEN;
270		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
271			    emsk, EAP_EMSK_LEN);
272	} else {
273		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
274	}
275
276	return emsk;
277}
278
279
280static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
281{
282	struct eap_tls_data *data = priv;
283	return data->state == SUCCESS;
284}
285
286
287int eap_server_tls_register(void)
288{
289	struct eap_method *eap;
290	int ret;
291
292	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
293				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
294	if (eap == NULL)
295		return -1;
296
297	eap->init = eap_tls_init;
298	eap->reset = eap_tls_reset;
299	eap->buildReq = eap_tls_buildReq;
300	eap->check = eap_tls_check;
301	eap->process = eap_tls_process;
302	eap->isDone = eap_tls_isDone;
303	eap->getKey = eap_tls_getKey;
304	eap->isSuccess = eap_tls_isSuccess;
305	eap->get_emsk = eap_tls_get_emsk;
306
307	ret = eap_server_method_register(eap);
308	if (ret)
309		eap_server_method_free(eap);
310	return ret;
311}
312
313
314#ifdef EAP_SERVER_UNAUTH_TLS
315int eap_server_unauth_tls_register(void)
316{
317	struct eap_method *eap;
318	int ret;
319
320	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
321				      EAP_VENDOR_UNAUTH_TLS,
322				      EAP_VENDOR_TYPE_UNAUTH_TLS,
323				      "UNAUTH-TLS");
324	if (eap == NULL)
325		return -1;
326
327	eap->init = eap_unauth_tls_init;
328	eap->reset = eap_tls_reset;
329	eap->buildReq = eap_tls_buildReq;
330	eap->check = eap_tls_check;
331	eap->process = eap_tls_process;
332	eap->isDone = eap_tls_isDone;
333	eap->getKey = eap_tls_getKey;
334	eap->isSuccess = eap_tls_isSuccess;
335	eap->get_emsk = eap_tls_get_emsk;
336
337	ret = eap_server_method_register(eap);
338	if (ret)
339		eap_server_method_free(eap);
340	return ret;
341}
342#endif /* EAP_SERVER_UNAUTH_TLS */
343