eap_leap.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/*
2 * EAP peer method: LEAP
3 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16
17#include "common.h"
18#include "crypto/ms_funcs.h"
19#include "crypto/crypto.h"
20#include "crypto/random.h"
21#include "eap_i.h"
22
23#define LEAP_VERSION 1
24#define LEAP_CHALLENGE_LEN 8
25#define LEAP_RESPONSE_LEN 24
26#define LEAP_KEY_LEN 16
27
28
29struct eap_leap_data {
30	enum {
31		LEAP_WAIT_CHALLENGE,
32		LEAP_WAIT_SUCCESS,
33		LEAP_WAIT_RESPONSE,
34		LEAP_DONE
35	} state;
36
37	u8 peer_challenge[LEAP_CHALLENGE_LEN];
38	u8 peer_response[LEAP_RESPONSE_LEN];
39
40	u8 ap_challenge[LEAP_CHALLENGE_LEN];
41	u8 ap_response[LEAP_RESPONSE_LEN];
42};
43
44
45static void * eap_leap_init(struct eap_sm *sm)
46{
47	struct eap_leap_data *data;
48
49	data = os_zalloc(sizeof(*data));
50	if (data == NULL)
51		return NULL;
52	data->state = LEAP_WAIT_CHALLENGE;
53
54	sm->leap_done = FALSE;
55	return data;
56}
57
58
59static void eap_leap_deinit(struct eap_sm *sm, void *priv)
60{
61	os_free(priv);
62}
63
64
65static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
66						struct eap_method_ret *ret,
67						const struct wpabuf *reqData)
68{
69	struct eap_leap_data *data = priv;
70	struct wpabuf *resp;
71	const u8 *pos, *challenge, *identity, *password;
72	u8 challenge_len, *rpos;
73	size_t identity_len, password_len, len;
74	int pwhash;
75
76	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
77
78	identity = eap_get_config_identity(sm, &identity_len);
79	password = eap_get_config_password2(sm, &password_len, &pwhash);
80	if (identity == NULL || password == NULL)
81		return NULL;
82
83	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
84	if (pos == NULL || len < 3) {
85		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
86		ret->ignore = TRUE;
87		return NULL;
88	}
89
90	if (*pos != LEAP_VERSION) {
91		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
92			   "%d", *pos);
93		ret->ignore = TRUE;
94		return NULL;
95	}
96	pos++;
97
98	pos++; /* skip unused byte */
99
100	challenge_len = *pos++;
101	if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
102		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
103			   "(challenge_len=%d reqDataLen=%lu)",
104			   challenge_len, (unsigned long) wpabuf_len(reqData));
105		ret->ignore = TRUE;
106		return NULL;
107	}
108	challenge = pos;
109	os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
110	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
111		    challenge, LEAP_CHALLENGE_LEN);
112
113	wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
114
115	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
116			     3 + LEAP_RESPONSE_LEN + identity_len,
117			     EAP_CODE_RESPONSE, eap_get_id(reqData));
118	if (resp == NULL)
119		return NULL;
120	wpabuf_put_u8(resp, LEAP_VERSION);
121	wpabuf_put_u8(resp, 0); /* unused */
122	wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
123	rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
124	if (pwhash)
125		challenge_response(challenge, password, rpos);
126	else
127		nt_challenge_response(challenge, password, password_len, rpos);
128	os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
129	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
130		    rpos, LEAP_RESPONSE_LEN);
131	wpabuf_put_data(resp, identity, identity_len);
132
133	data->state = LEAP_WAIT_SUCCESS;
134
135	return resp;
136}
137
138
139static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
140						struct eap_method_ret *ret,
141						const struct wpabuf *reqData)
142{
143	struct eap_leap_data *data = priv;
144	struct wpabuf *resp;
145	u8 *pos;
146	const u8 *identity;
147	size_t identity_len;
148
149	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
150
151	identity = eap_get_config_identity(sm, &identity_len);
152	if (identity == NULL)
153		return NULL;
154
155	if (data->state != LEAP_WAIT_SUCCESS) {
156		wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
157			   "unexpected state (%d) - ignored", data->state);
158		ret->ignore = TRUE;
159		return NULL;
160	}
161
162	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
163			     3 + LEAP_CHALLENGE_LEN + identity_len,
164			     EAP_CODE_REQUEST, eap_get_id(reqData));
165	if (resp == NULL)
166		return NULL;
167	wpabuf_put_u8(resp, LEAP_VERSION);
168	wpabuf_put_u8(resp, 0); /* unused */
169	wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
170	pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
171	if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
172		wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
173			   "for challenge");
174		wpabuf_free(resp);
175		ret->ignore = TRUE;
176		return NULL;
177	}
178	os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
179	wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
180		    LEAP_CHALLENGE_LEN);
181	wpabuf_put_data(resp, identity, identity_len);
182
183	data->state = LEAP_WAIT_RESPONSE;
184
185	return resp;
186}
187
188
189static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
190						 struct eap_method_ret *ret,
191						 const struct wpabuf *reqData)
192{
193	struct eap_leap_data *data = priv;
194	const u8 *pos, *password;
195	u8 response_len, pw_hash[16], pw_hash_hash[16],
196		expected[LEAP_RESPONSE_LEN];
197	size_t password_len, len;
198	int pwhash;
199
200	wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
201
202	password = eap_get_config_password2(sm, &password_len, &pwhash);
203	if (password == NULL)
204		return NULL;
205
206	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
207	if (pos == NULL || len < 3) {
208		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
209		ret->ignore = TRUE;
210		return NULL;
211	}
212
213	if (*pos != LEAP_VERSION) {
214		wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
215			   "%d", *pos);
216		ret->ignore = TRUE;
217		return NULL;
218	}
219	pos++;
220
221	pos++; /* skip unused byte */
222
223	response_len = *pos++;
224	if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
225		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
226			   "(response_len=%d reqDataLen=%lu)",
227			   response_len, (unsigned long) wpabuf_len(reqData));
228		ret->ignore = TRUE;
229		return NULL;
230	}
231
232	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
233		    pos, LEAP_RESPONSE_LEN);
234	os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
235
236	if (pwhash) {
237		if (hash_nt_password_hash(password, pw_hash_hash)) {
238			ret->ignore = TRUE;
239			return NULL;
240		}
241	} else {
242		if (nt_password_hash(password, password_len, pw_hash) ||
243		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
244			ret->ignore = TRUE;
245			return NULL;
246		}
247	}
248	challenge_response(data->ap_challenge, pw_hash_hash, expected);
249
250	ret->methodState = METHOD_DONE;
251	ret->allowNotifications = FALSE;
252
253	if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
254		wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
255			   "response - authentication failed");
256		wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
257			    expected, LEAP_RESPONSE_LEN);
258		ret->decision = DECISION_FAIL;
259		return NULL;
260	}
261
262	ret->decision = DECISION_UNCOND_SUCC;
263
264	/* LEAP is somewhat odd method since it sends EAP-Success in the middle
265	 * of the authentication. Use special variable to transit EAP state
266	 * machine to SUCCESS state. */
267	sm->leap_done = TRUE;
268	data->state = LEAP_DONE;
269
270	/* No more authentication messages expected; AP will send EAPOL-Key
271	 * frames if encryption is enabled. */
272	return NULL;
273}
274
275
276static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
277					struct eap_method_ret *ret,
278					const struct wpabuf *reqData)
279{
280	const struct eap_hdr *eap;
281	size_t password_len;
282	const u8 *password;
283
284	password = eap_get_config_password(sm, &password_len);
285	if (password == NULL) {
286		wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
287		eap_sm_request_password(sm);
288		ret->ignore = TRUE;
289		return NULL;
290	}
291
292	/*
293	 * LEAP needs to be able to handle EAP-Success frame which does not
294	 * include Type field. Consequently, eap_hdr_validate() cannot be used
295	 * here. This validation will be done separately for EAP-Request and
296	 * EAP-Response frames.
297	 */
298	eap = wpabuf_head(reqData);
299	if (wpabuf_len(reqData) < sizeof(*eap) ||
300	    be_to_host16(eap->length) > wpabuf_len(reqData)) {
301		wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
302		ret->ignore = TRUE;
303		return NULL;
304	}
305
306	ret->ignore = FALSE;
307	ret->allowNotifications = TRUE;
308	ret->methodState = METHOD_MAY_CONT;
309	ret->decision = DECISION_FAIL;
310
311	sm->leap_done = FALSE;
312
313	switch (eap->code) {
314	case EAP_CODE_REQUEST:
315		return eap_leap_process_request(sm, priv, ret, reqData);
316	case EAP_CODE_SUCCESS:
317		return eap_leap_process_success(sm, priv, ret, reqData);
318	case EAP_CODE_RESPONSE:
319		return eap_leap_process_response(sm, priv, ret, reqData);
320	default:
321		wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
322			   "ignored", eap->code);
323		ret->ignore = TRUE;
324		return NULL;
325	}
326}
327
328
329static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
330{
331	struct eap_leap_data *data = priv;
332	return data->state == LEAP_DONE;
333}
334
335
336static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
337{
338	struct eap_leap_data *data = priv;
339	u8 *key, pw_hash_hash[16], pw_hash[16];
340	const u8 *addr[5], *password;
341	size_t elen[5], password_len;
342	int pwhash;
343
344	if (data->state != LEAP_DONE)
345		return NULL;
346
347	password = eap_get_config_password2(sm, &password_len, &pwhash);
348	if (password == NULL)
349		return NULL;
350
351	key = os_malloc(LEAP_KEY_LEN);
352	if (key == NULL)
353		return NULL;
354
355	if (pwhash) {
356		if (hash_nt_password_hash(password, pw_hash_hash)) {
357			os_free(key);
358			return NULL;
359		}
360	} else {
361		if (nt_password_hash(password, password_len, pw_hash) ||
362		    hash_nt_password_hash(pw_hash, pw_hash_hash)) {
363			os_free(key);
364			return NULL;
365		}
366	}
367	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
368			pw_hash_hash, 16);
369	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
370		    data->peer_challenge, LEAP_CHALLENGE_LEN);
371	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
372		    data->peer_response, LEAP_RESPONSE_LEN);
373	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
374		    data->ap_challenge, LEAP_CHALLENGE_LEN);
375	wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
376		    data->ap_response, LEAP_RESPONSE_LEN);
377
378	addr[0] = pw_hash_hash;
379	elen[0] = 16;
380	addr[1] = data->ap_challenge;
381	elen[1] = LEAP_CHALLENGE_LEN;
382	addr[2] = data->ap_response;
383	elen[2] = LEAP_RESPONSE_LEN;
384	addr[3] = data->peer_challenge;
385	elen[3] = LEAP_CHALLENGE_LEN;
386	addr[4] = data->peer_response;
387	elen[4] = LEAP_RESPONSE_LEN;
388	md5_vector(5, addr, elen, key);
389	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
390	*len = LEAP_KEY_LEN;
391
392	return key;
393}
394
395
396int eap_peer_leap_register(void)
397{
398	struct eap_method *eap;
399	int ret;
400
401	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
402				    EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
403	if (eap == NULL)
404		return -1;
405
406	eap->init = eap_leap_init;
407	eap->deinit = eap_leap_deinit;
408	eap->process = eap_leap_process;
409	eap->isKeyAvailable = eap_leap_isKeyAvailable;
410	eap->getKey = eap_leap_getKey;
411
412	ret = eap_peer_method_register(eap);
413	if (ret)
414		eap_peer_method_free(eap);
415	return ret;
416}
417