eap_server_tls.c revision 214501
1214501Srpaulo/*
2214501Srpaulo * hostapd / EAP-TLS (RFC 2716)
3214501Srpaulo * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5214501Srpaulo * This program is free software; you can redistribute it and/or modify
6214501Srpaulo * it under the terms of the GNU General Public License version 2 as
7214501Srpaulo * published by the Free Software Foundation.
8214501Srpaulo *
9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214501Srpaulo * license.
11214501Srpaulo *
12214501Srpaulo * See README and COPYING for more details.
13214501Srpaulo */
14214501Srpaulo
15214501Srpaulo#include "includes.h"
16214501Srpaulo
17214501Srpaulo#include "common.h"
18214501Srpaulo#include "eap_i.h"
19214501Srpaulo#include "eap_tls_common.h"
20214501Srpaulo#include "crypto/tls.h"
21214501Srpaulo
22214501Srpaulo
23214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv);
24214501Srpaulo
25214501Srpaulo
26214501Srpaulostruct eap_tls_data {
27214501Srpaulo	struct eap_ssl_data ssl;
28214501Srpaulo	enum { START, CONTINUE, SUCCESS, FAILURE } state;
29214501Srpaulo	int established;
30214501Srpaulo};
31214501Srpaulo
32214501Srpaulo
33214501Srpaulostatic const char * eap_tls_state_txt(int state)
34214501Srpaulo{
35214501Srpaulo	switch (state) {
36214501Srpaulo	case START:
37214501Srpaulo		return "START";
38214501Srpaulo	case CONTINUE:
39214501Srpaulo		return "CONTINUE";
40214501Srpaulo	case SUCCESS:
41214501Srpaulo		return "SUCCESS";
42214501Srpaulo	case FAILURE:
43214501Srpaulo		return "FAILURE";
44214501Srpaulo	default:
45214501Srpaulo		return "Unknown?!";
46214501Srpaulo	}
47214501Srpaulo}
48214501Srpaulo
49214501Srpaulo
50214501Srpaulostatic void eap_tls_state(struct eap_tls_data *data, int state)
51214501Srpaulo{
52214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
53214501Srpaulo		   eap_tls_state_txt(data->state),
54214501Srpaulo		   eap_tls_state_txt(state));
55214501Srpaulo	data->state = state;
56214501Srpaulo}
57214501Srpaulo
58214501Srpaulo
59214501Srpaulostatic void * eap_tls_init(struct eap_sm *sm)
60214501Srpaulo{
61214501Srpaulo	struct eap_tls_data *data;
62214501Srpaulo
63214501Srpaulo	data = os_zalloc(sizeof(*data));
64214501Srpaulo	if (data == NULL)
65214501Srpaulo		return NULL;
66214501Srpaulo	data->state = START;
67214501Srpaulo
68214501Srpaulo	if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
69214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
70214501Srpaulo		eap_tls_reset(sm, data);
71214501Srpaulo		return NULL;
72214501Srpaulo	}
73214501Srpaulo
74214501Srpaulo	return data;
75214501Srpaulo}
76214501Srpaulo
77214501Srpaulo
78214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv)
79214501Srpaulo{
80214501Srpaulo	struct eap_tls_data *data = priv;
81214501Srpaulo	if (data == NULL)
82214501Srpaulo		return;
83214501Srpaulo	eap_server_tls_ssl_deinit(sm, &data->ssl);
84214501Srpaulo	os_free(data);
85214501Srpaulo}
86214501Srpaulo
87214501Srpaulo
88214501Srpaulostatic struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
89214501Srpaulo					   struct eap_tls_data *data, u8 id)
90214501Srpaulo{
91214501Srpaulo	struct wpabuf *req;
92214501Srpaulo
93214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
94214501Srpaulo			    id);
95214501Srpaulo	if (req == NULL) {
96214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
97214501Srpaulo			   "request");
98214501Srpaulo		eap_tls_state(data, FAILURE);
99214501Srpaulo		return NULL;
100214501Srpaulo	}
101214501Srpaulo
102214501Srpaulo	wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
103214501Srpaulo
104214501Srpaulo	eap_tls_state(data, CONTINUE);
105214501Srpaulo
106214501Srpaulo	return req;
107214501Srpaulo}
108214501Srpaulo
109214501Srpaulo
110214501Srpaulostatic struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
111214501Srpaulo{
112214501Srpaulo	struct eap_tls_data *data = priv;
113214501Srpaulo	struct wpabuf *res;
114214501Srpaulo
115214501Srpaulo	if (data->ssl.state == FRAG_ACK) {
116214501Srpaulo		return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
117214501Srpaulo	}
118214501Srpaulo
119214501Srpaulo	if (data->ssl.state == WAIT_FRAG_ACK) {
120214501Srpaulo		res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
121214501Srpaulo					       id);
122214501Srpaulo		goto check_established;
123214501Srpaulo	}
124214501Srpaulo
125214501Srpaulo	switch (data->state) {
126214501Srpaulo	case START:
127214501Srpaulo		return eap_tls_build_start(sm, data, id);
128214501Srpaulo	case CONTINUE:
129214501Srpaulo		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
130214501Srpaulo			data->established = 1;
131214501Srpaulo		break;
132214501Srpaulo	default:
133214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
134214501Srpaulo			   __func__, data->state);
135214501Srpaulo		return NULL;
136214501Srpaulo	}
137214501Srpaulo
138214501Srpaulo	res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
139214501Srpaulo
140214501Srpaulocheck_established:
141214501Srpaulo	if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
142214501Srpaulo		/* TLS handshake has been completed and there are no more
143214501Srpaulo		 * fragments waiting to be sent out. */
144214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
145214501Srpaulo		eap_tls_state(data, SUCCESS);
146214501Srpaulo	}
147214501Srpaulo
148214501Srpaulo	return res;
149214501Srpaulo}
150214501Srpaulo
151214501Srpaulo
152214501Srpaulostatic Boolean eap_tls_check(struct eap_sm *sm, void *priv,
153214501Srpaulo			     struct wpabuf *respData)
154214501Srpaulo{
155214501Srpaulo	const u8 *pos;
156214501Srpaulo	size_t len;
157214501Srpaulo
158214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
159214501Srpaulo	if (pos == NULL || len < 1) {
160214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
161214501Srpaulo		return TRUE;
162214501Srpaulo	}
163214501Srpaulo
164214501Srpaulo	return FALSE;
165214501Srpaulo}
166214501Srpaulo
167214501Srpaulo
168214501Srpaulostatic void eap_tls_process_msg(struct eap_sm *sm, void *priv,
169214501Srpaulo				const struct wpabuf *respData)
170214501Srpaulo{
171214501Srpaulo	struct eap_tls_data *data = priv;
172214501Srpaulo	if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
173214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
174214501Srpaulo			   "handshake message");
175214501Srpaulo		return;
176214501Srpaulo	}
177214501Srpaulo	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
178214501Srpaulo		eap_tls_state(data, FAILURE);
179214501Srpaulo}
180214501Srpaulo
181214501Srpaulo
182214501Srpaulostatic void eap_tls_process(struct eap_sm *sm, void *priv,
183214501Srpaulo			    struct wpabuf *respData)
184214501Srpaulo{
185214501Srpaulo	struct eap_tls_data *data = priv;
186214501Srpaulo	if (eap_server_tls_process(sm, &data->ssl, respData, data,
187214501Srpaulo				   EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
188214501Srpaulo	    0)
189214501Srpaulo		eap_tls_state(data, FAILURE);
190214501Srpaulo}
191214501Srpaulo
192214501Srpaulo
193214501Srpaulostatic Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
194214501Srpaulo{
195214501Srpaulo	struct eap_tls_data *data = priv;
196214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
197214501Srpaulo}
198214501Srpaulo
199214501Srpaulo
200214501Srpaulostatic u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
201214501Srpaulo{
202214501Srpaulo	struct eap_tls_data *data = priv;
203214501Srpaulo	u8 *eapKeyData;
204214501Srpaulo
205214501Srpaulo	if (data->state != SUCCESS)
206214501Srpaulo		return NULL;
207214501Srpaulo
208214501Srpaulo	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
209214501Srpaulo					       "client EAP encryption",
210214501Srpaulo					       EAP_TLS_KEY_LEN);
211214501Srpaulo	if (eapKeyData) {
212214501Srpaulo		*len = EAP_TLS_KEY_LEN;
213214501Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
214214501Srpaulo			    eapKeyData, EAP_TLS_KEY_LEN);
215214501Srpaulo	} else {
216214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
217214501Srpaulo	}
218214501Srpaulo
219214501Srpaulo	return eapKeyData;
220214501Srpaulo}
221214501Srpaulo
222214501Srpaulo
223214501Srpaulostatic u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
224214501Srpaulo{
225214501Srpaulo	struct eap_tls_data *data = priv;
226214501Srpaulo	u8 *eapKeyData, *emsk;
227214501Srpaulo
228214501Srpaulo	if (data->state != SUCCESS)
229214501Srpaulo		return NULL;
230214501Srpaulo
231214501Srpaulo	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
232214501Srpaulo					       "client EAP encryption",
233214501Srpaulo					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
234214501Srpaulo	if (eapKeyData) {
235214501Srpaulo		emsk = os_malloc(EAP_EMSK_LEN);
236214501Srpaulo		if (emsk)
237214501Srpaulo			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
238214501Srpaulo				  EAP_EMSK_LEN);
239214501Srpaulo		os_free(eapKeyData);
240214501Srpaulo	} else
241214501Srpaulo		emsk = NULL;
242214501Srpaulo
243214501Srpaulo	if (emsk) {
244214501Srpaulo		*len = EAP_EMSK_LEN;
245214501Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
246214501Srpaulo			    emsk, EAP_EMSK_LEN);
247214501Srpaulo	} else {
248214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
249214501Srpaulo	}
250214501Srpaulo
251214501Srpaulo	return emsk;
252214501Srpaulo}
253214501Srpaulo
254214501Srpaulo
255214501Srpaulostatic Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
256214501Srpaulo{
257214501Srpaulo	struct eap_tls_data *data = priv;
258214501Srpaulo	return data->state == SUCCESS;
259214501Srpaulo}
260214501Srpaulo
261214501Srpaulo
262214501Srpauloint eap_server_tls_register(void)
263214501Srpaulo{
264214501Srpaulo	struct eap_method *eap;
265214501Srpaulo	int ret;
266214501Srpaulo
267214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
268214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
269214501Srpaulo	if (eap == NULL)
270214501Srpaulo		return -1;
271214501Srpaulo
272214501Srpaulo	eap->init = eap_tls_init;
273214501Srpaulo	eap->reset = eap_tls_reset;
274214501Srpaulo	eap->buildReq = eap_tls_buildReq;
275214501Srpaulo	eap->check = eap_tls_check;
276214501Srpaulo	eap->process = eap_tls_process;
277214501Srpaulo	eap->isDone = eap_tls_isDone;
278214501Srpaulo	eap->getKey = eap_tls_getKey;
279214501Srpaulo	eap->isSuccess = eap_tls_isSuccess;
280214501Srpaulo	eap->get_emsk = eap_tls_get_emsk;
281214501Srpaulo
282214501Srpaulo	ret = eap_server_method_register(eap);
283214501Srpaulo	if (ret)
284214501Srpaulo		eap_server_method_free(eap);
285214501Srpaulo	return ret;
286214501Srpaulo}
287