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