xref: /netbsd-src/external/bsd/wpa/dist/src/eap_peer/eap_leap.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
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 
29 struct 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 
45 static 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 
59 static void eap_leap_deinit(struct eap_sm *sm, void *priv)
60 {
61 	os_free(priv);
62 }
63 
64 
65 static 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 
139 static 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 
189 static 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 
276 static 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 
329 static 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 
336 static 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 
396 int 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