eap_server_tls.c revision 346981
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}
267
268
269static void eap_tls_process(struct eap_sm *sm, void *priv,
270			    struct wpabuf *respData)
271{
272	struct eap_tls_data *data = priv;
273	const struct wpabuf *buf;
274	const u8 *pos;
275
276	if (eap_server_tls_process(sm, &data->ssl, respData, data,
277				   data->eap_type, NULL, eap_tls_process_msg) <
278	    0) {
279		eap_tls_state(data, FAILURE);
280		return;
281	}
282
283	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
284	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
285		return;
286
287	buf = tls_connection_get_success_data(data->ssl.conn);
288	if (!buf || wpabuf_len(buf) < 1) {
289		wpa_printf(MSG_DEBUG,
290			   "EAP-TLS: No success data in resumed session - reject attempt");
291		eap_tls_state(data, FAILURE);
292		return;
293	}
294
295	pos = wpabuf_head(buf);
296	if (*pos != data->eap_type) {
297		wpa_printf(MSG_DEBUG,
298			   "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
299			   *pos);
300		eap_tls_state(data, FAILURE);
301		return;
302	}
303
304	wpa_printf(MSG_DEBUG,
305		   "EAP-TLS: Resuming previous session");
306	eap_tls_state(data, SUCCESS);
307	tls_connection_set_success_data_resumed(data->ssl.conn);
308	/* TODO: Cache serial number with session and update EAP user
309	 * information based on the cached serial number */
310}
311
312
313static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
314{
315	struct eap_tls_data *data = priv;
316	return data->state == SUCCESS || data->state == FAILURE;
317}
318
319
320static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
321{
322	struct eap_tls_data *data = priv;
323	u8 *eapKeyData;
324	const char *label;
325
326	if (data->state != SUCCESS)
327		return NULL;
328
329	if (data->ssl.tls_v13)
330		label = "EXPORTER_EAP_TLS_Key_Material";
331	else
332		label = "client EAP encryption";
333	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
334					       NULL, 0,
335					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
336	if (eapKeyData) {
337		*len = EAP_TLS_KEY_LEN;
338		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
339			    eapKeyData, EAP_TLS_KEY_LEN);
340		os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
341	} else {
342		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
343	}
344
345	return eapKeyData;
346}
347
348
349static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
350{
351	struct eap_tls_data *data = priv;
352	u8 *eapKeyData, *emsk;
353	const char *label;
354
355	if (data->state != SUCCESS)
356		return NULL;
357
358	if (data->ssl.tls_v13)
359		label = "EXPORTER_EAP_TLS_Key_Material";
360	else
361		label = "client EAP encryption";
362	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
363					       NULL, 0,
364					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
365	if (eapKeyData) {
366		emsk = os_malloc(EAP_EMSK_LEN);
367		if (emsk)
368			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
369				  EAP_EMSK_LEN);
370		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
371	} else
372		emsk = NULL;
373
374	if (emsk) {
375		*len = EAP_EMSK_LEN;
376		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
377			    emsk, EAP_EMSK_LEN);
378	} else {
379		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
380	}
381
382	return emsk;
383}
384
385
386static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
387{
388	struct eap_tls_data *data = priv;
389	return data->state == SUCCESS;
390}
391
392
393static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
394{
395	struct eap_tls_data *data = priv;
396
397	if (data->state != SUCCESS)
398		return NULL;
399
400	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
401						len);
402}
403
404
405int eap_server_tls_register(void)
406{
407	struct eap_method *eap;
408
409	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
410				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
411	if (eap == NULL)
412		return -1;
413
414	eap->init = eap_tls_init;
415	eap->reset = eap_tls_reset;
416	eap->buildReq = eap_tls_buildReq;
417	eap->check = eap_tls_check;
418	eap->process = eap_tls_process;
419	eap->isDone = eap_tls_isDone;
420	eap->getKey = eap_tls_getKey;
421	eap->isSuccess = eap_tls_isSuccess;
422	eap->get_emsk = eap_tls_get_emsk;
423	eap->getSessionId = eap_tls_get_session_id;
424
425	return eap_server_method_register(eap);
426}
427
428
429#ifdef EAP_SERVER_UNAUTH_TLS
430int eap_server_unauth_tls_register(void)
431{
432	struct eap_method *eap;
433
434	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
435				      EAP_VENDOR_UNAUTH_TLS,
436				      EAP_VENDOR_TYPE_UNAUTH_TLS,
437				      "UNAUTH-TLS");
438	if (eap == NULL)
439		return -1;
440
441	eap->init = eap_unauth_tls_init;
442	eap->reset = eap_tls_reset;
443	eap->buildReq = eap_tls_buildReq;
444	eap->check = eap_tls_check;
445	eap->process = eap_tls_process;
446	eap->isDone = eap_tls_isDone;
447	eap->getKey = eap_tls_getKey;
448	eap->isSuccess = eap_tls_isSuccess;
449	eap->get_emsk = eap_tls_get_emsk;
450
451	return eap_server_method_register(eap);
452}
453#endif /* EAP_SERVER_UNAUTH_TLS */
454
455
456#ifdef CONFIG_HS20
457int eap_server_wfa_unauth_tls_register(void)
458{
459	struct eap_method *eap;
460
461	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
462				      EAP_VENDOR_WFA_NEW,
463				      EAP_VENDOR_WFA_UNAUTH_TLS,
464				      "WFA-UNAUTH-TLS");
465	if (eap == NULL)
466		return -1;
467
468	eap->init = eap_wfa_unauth_tls_init;
469	eap->reset = eap_tls_reset;
470	eap->buildReq = eap_tls_buildReq;
471	eap->check = eap_tls_check;
472	eap->process = eap_tls_process;
473	eap->isDone = eap_tls_isDone;
474	eap->getKey = eap_tls_getKey;
475	eap->isSuccess = eap_tls_isSuccess;
476	eap->get_emsk = eap_tls_get_emsk;
477
478	return eap_server_method_register(eap);
479}
480#endif /* CONFIG_HS20 */
481