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