eap_server_mschapv2.c revision 337817
1214501Srpaulo/*
2214501Srpaulo * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3214501Srpaulo * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "crypto/ms_funcs.h"
13252726Srpaulo#include "crypto/random.h"
14214501Srpaulo#include "eap_i.h"
15214501Srpaulo
16214501Srpaulo
17214501Srpaulostruct eap_mschapv2_hdr {
18214501Srpaulo	u8 op_code; /* MSCHAPV2_OP_* */
19214501Srpaulo	u8 mschapv2_id; /* must be changed for challenges, but not for
20214501Srpaulo			 * success/failure */
21214501Srpaulo	u8 ms_length[2]; /* Note: misaligned; length - 5 */
22214501Srpaulo	/* followed by data */
23214501Srpaulo} STRUCT_PACKED;
24214501Srpaulo
25214501Srpaulo#define MSCHAPV2_OP_CHALLENGE 1
26214501Srpaulo#define MSCHAPV2_OP_RESPONSE 2
27214501Srpaulo#define MSCHAPV2_OP_SUCCESS 3
28214501Srpaulo#define MSCHAPV2_OP_FAILURE 4
29214501Srpaulo#define MSCHAPV2_OP_CHANGE_PASSWORD 7
30214501Srpaulo
31214501Srpaulo#define MSCHAPV2_RESP_LEN 49
32214501Srpaulo
33214501Srpaulo#define ERROR_RESTRICTED_LOGON_HOURS 646
34214501Srpaulo#define ERROR_ACCT_DISABLED 647
35214501Srpaulo#define ERROR_PASSWD_EXPIRED 648
36214501Srpaulo#define ERROR_NO_DIALIN_PERMISSION 649
37214501Srpaulo#define ERROR_AUTHENTICATION_FAILURE 691
38214501Srpaulo#define ERROR_CHANGING_PASSWORD 709
39214501Srpaulo
40214501Srpaulo#define PASSWD_CHANGE_CHAL_LEN 16
41214501Srpaulo#define MSCHAPV2_KEY_LEN 16
42214501Srpaulo
43214501Srpaulo
44214501Srpaulo#define CHALLENGE_LEN 16
45214501Srpaulo
46214501Srpaulostruct eap_mschapv2_data {
47214501Srpaulo	u8 auth_challenge[CHALLENGE_LEN];
48214501Srpaulo	int auth_challenge_from_tls;
49214501Srpaulo	u8 *peer_challenge;
50214501Srpaulo	u8 auth_response[20];
51214501Srpaulo	enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
52214501Srpaulo	u8 resp_mschapv2_id;
53214501Srpaulo	u8 master_key[16];
54214501Srpaulo	int master_key_valid;
55214501Srpaulo};
56214501Srpaulo
57214501Srpaulo
58214501Srpaulostatic void * eap_mschapv2_init(struct eap_sm *sm)
59214501Srpaulo{
60214501Srpaulo	struct eap_mschapv2_data *data;
61214501Srpaulo
62214501Srpaulo	data = os_zalloc(sizeof(*data));
63214501Srpaulo	if (data == NULL)
64214501Srpaulo		return NULL;
65214501Srpaulo	data->state = CHALLENGE;
66214501Srpaulo
67214501Srpaulo	if (sm->auth_challenge) {
68214501Srpaulo		os_memcpy(data->auth_challenge, sm->auth_challenge,
69214501Srpaulo			  CHALLENGE_LEN);
70214501Srpaulo		data->auth_challenge_from_tls = 1;
71214501Srpaulo	}
72214501Srpaulo
73214501Srpaulo	if (sm->peer_challenge) {
74214501Srpaulo		data->peer_challenge = os_malloc(CHALLENGE_LEN);
75214501Srpaulo		if (data->peer_challenge == NULL) {
76214501Srpaulo			os_free(data);
77214501Srpaulo			return NULL;
78214501Srpaulo		}
79214501Srpaulo		os_memcpy(data->peer_challenge, sm->peer_challenge,
80214501Srpaulo			  CHALLENGE_LEN);
81214501Srpaulo	}
82214501Srpaulo
83214501Srpaulo	return data;
84214501Srpaulo}
85214501Srpaulo
86214501Srpaulo
87214501Srpaulostatic void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
88214501Srpaulo{
89214501Srpaulo	struct eap_mschapv2_data *data = priv;
90214501Srpaulo	if (data == NULL)
91214501Srpaulo		return;
92214501Srpaulo
93214501Srpaulo	os_free(data->peer_challenge);
94281806Srpaulo	bin_clear_free(data, sizeof(*data));
95214501Srpaulo}
96214501Srpaulo
97214501Srpaulo
98214501Srpaulostatic struct wpabuf * eap_mschapv2_build_challenge(
99214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
100214501Srpaulo{
101214501Srpaulo	struct wpabuf *req;
102214501Srpaulo	struct eap_mschapv2_hdr *ms;
103214501Srpaulo	size_t ms_len;
104214501Srpaulo
105214501Srpaulo	if (!data->auth_challenge_from_tls &&
106252726Srpaulo	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
107214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
108214501Srpaulo			   "data");
109214501Srpaulo		data->state = FAILURE;
110214501Srpaulo		return NULL;
111214501Srpaulo	}
112214501Srpaulo
113281806Srpaulo	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
114214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
115214501Srpaulo			    EAP_CODE_REQUEST, id);
116214501Srpaulo	if (req == NULL) {
117214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
118214501Srpaulo			   " for request");
119214501Srpaulo		data->state = FAILURE;
120214501Srpaulo		return NULL;
121214501Srpaulo	}
122214501Srpaulo
123214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
124214501Srpaulo	ms->op_code = MSCHAPV2_OP_CHALLENGE;
125214501Srpaulo	ms->mschapv2_id = id;
126214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
127214501Srpaulo
128214501Srpaulo	wpabuf_put_u8(req, CHALLENGE_LEN);
129214501Srpaulo	if (!data->auth_challenge_from_tls)
130214501Srpaulo		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
131214501Srpaulo	else
132214501Srpaulo		wpabuf_put(req, CHALLENGE_LEN);
133214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
134214501Srpaulo		    data->auth_challenge, CHALLENGE_LEN);
135281806Srpaulo	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
136214501Srpaulo
137214501Srpaulo	return req;
138214501Srpaulo}
139214501Srpaulo
140214501Srpaulo
141214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req(
142214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
143214501Srpaulo{
144214501Srpaulo	struct wpabuf *req;
145214501Srpaulo	struct eap_mschapv2_hdr *ms;
146214501Srpaulo	u8 *msg;
147214501Srpaulo	char *message = "OK";
148214501Srpaulo	size_t ms_len;
149214501Srpaulo
150214501Srpaulo	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
151214501Srpaulo		os_strlen(message);
152214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
153214501Srpaulo			    EAP_CODE_REQUEST, id);
154214501Srpaulo	if (req == NULL) {
155214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
156214501Srpaulo			   " for request");
157214501Srpaulo		data->state = FAILURE;
158214501Srpaulo		return NULL;
159214501Srpaulo	}
160214501Srpaulo
161214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
162214501Srpaulo	ms->op_code = MSCHAPV2_OP_SUCCESS;
163214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
164214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
165214501Srpaulo	msg = (u8 *) (ms + 1);
166214501Srpaulo
167214501Srpaulo	wpabuf_put_u8(req, 'S');
168214501Srpaulo	wpabuf_put_u8(req, '=');
169214501Srpaulo	wpa_snprintf_hex_uppercase(
170214501Srpaulo		wpabuf_put(req, sizeof(data->auth_response) * 2),
171214501Srpaulo		sizeof(data->auth_response) * 2 + 1,
172214501Srpaulo		data->auth_response, sizeof(data->auth_response));
173214501Srpaulo	wpabuf_put_u8(req, ' ');
174214501Srpaulo	wpabuf_put_u8(req, 'M');
175214501Srpaulo	wpabuf_put_u8(req, '=');
176214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
177214501Srpaulo
178214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
179214501Srpaulo			  msg, ms_len - sizeof(*ms));
180214501Srpaulo
181214501Srpaulo	return req;
182214501Srpaulo}
183214501Srpaulo
184214501Srpaulo
185214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req(
186214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
187214501Srpaulo{
188214501Srpaulo	struct wpabuf *req;
189214501Srpaulo	struct eap_mschapv2_hdr *ms;
190214501Srpaulo	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
191214501Srpaulo		"M=FAILED";
192214501Srpaulo	size_t ms_len;
193214501Srpaulo
194214501Srpaulo	ms_len = sizeof(*ms) + os_strlen(message);
195214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
196214501Srpaulo			    EAP_CODE_REQUEST, id);
197214501Srpaulo	if (req == NULL) {
198214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
199214501Srpaulo			   " for request");
200214501Srpaulo		data->state = FAILURE;
201214501Srpaulo		return NULL;
202214501Srpaulo	}
203214501Srpaulo
204214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
205214501Srpaulo	ms->op_code = MSCHAPV2_OP_FAILURE;
206214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
207214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
208214501Srpaulo
209214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
210214501Srpaulo
211214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
212214501Srpaulo			  (u8 *) message, os_strlen(message));
213214501Srpaulo
214214501Srpaulo	return req;
215214501Srpaulo}
216214501Srpaulo
217214501Srpaulo
218214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
219214501Srpaulo					     u8 id)
220214501Srpaulo{
221214501Srpaulo	struct eap_mschapv2_data *data = priv;
222214501Srpaulo
223214501Srpaulo	switch (data->state) {
224214501Srpaulo	case CHALLENGE:
225214501Srpaulo		return eap_mschapv2_build_challenge(sm, data, id);
226214501Srpaulo	case SUCCESS_REQ:
227214501Srpaulo		return eap_mschapv2_build_success_req(sm, data, id);
228214501Srpaulo	case FAILURE_REQ:
229214501Srpaulo		return eap_mschapv2_build_failure_req(sm, data, id);
230214501Srpaulo	default:
231214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
232214501Srpaulo			   "buildReq", data->state);
233214501Srpaulo		break;
234214501Srpaulo	}
235214501Srpaulo	return NULL;
236214501Srpaulo}
237214501Srpaulo
238214501Srpaulo
239214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
240214501Srpaulo				  struct wpabuf *respData)
241214501Srpaulo{
242214501Srpaulo	struct eap_mschapv2_data *data = priv;
243214501Srpaulo	struct eap_mschapv2_hdr *resp;
244214501Srpaulo	const u8 *pos;
245214501Srpaulo	size_t len;
246214501Srpaulo
247214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
248214501Srpaulo			       &len);
249214501Srpaulo	if (pos == NULL || len < 1) {
250214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
251214501Srpaulo		return TRUE;
252214501Srpaulo	}
253214501Srpaulo
254214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
255214501Srpaulo	if (data->state == CHALLENGE &&
256214501Srpaulo	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
257214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
258214501Srpaulo			   "ignore op %d", resp->op_code);
259214501Srpaulo		return TRUE;
260214501Srpaulo	}
261214501Srpaulo
262214501Srpaulo	if (data->state == SUCCESS_REQ &&
263214501Srpaulo	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
264214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
265214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
266214501Srpaulo			   "Failure - ignore op %d", resp->op_code);
267214501Srpaulo		return TRUE;
268214501Srpaulo	}
269214501Srpaulo
270214501Srpaulo	if (data->state == FAILURE_REQ &&
271214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
272214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
273214501Srpaulo			   "- ignore op %d", resp->op_code);
274214501Srpaulo		return TRUE;
275214501Srpaulo	}
276214501Srpaulo
277214501Srpaulo	return FALSE;
278214501Srpaulo}
279214501Srpaulo
280214501Srpaulo
281214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm,
282214501Srpaulo					  struct eap_mschapv2_data *data,
283214501Srpaulo					  struct wpabuf *respData)
284214501Srpaulo{
285214501Srpaulo	struct eap_mschapv2_hdr *resp;
286214501Srpaulo	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
287214501Srpaulo	u8 flags;
288214501Srpaulo	size_t len, name_len, i;
289214501Srpaulo	u8 expected[24];
290214501Srpaulo	const u8 *username, *user;
291214501Srpaulo	size_t username_len, user_len;
292214501Srpaulo	int res;
293281806Srpaulo	char *buf;
294214501Srpaulo
295214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
296214501Srpaulo			       &len);
297214501Srpaulo	if (pos == NULL || len < 1)
298214501Srpaulo		return; /* Should not happen - frame already validated */
299214501Srpaulo
300214501Srpaulo	end = pos + len;
301214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
302214501Srpaulo	pos = (u8 *) (resp + 1);
303214501Srpaulo
304214501Srpaulo	if (len < sizeof(*resp) + 1 + 49 ||
305214501Srpaulo	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
306214501Srpaulo	    pos[0] != 49) {
307214501Srpaulo		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
308214501Srpaulo				respData);
309214501Srpaulo		data->state = FAILURE;
310214501Srpaulo		return;
311214501Srpaulo	}
312214501Srpaulo	data->resp_mschapv2_id = resp->mschapv2_id;
313214501Srpaulo	pos++;
314214501Srpaulo	peer_challenge = pos;
315214501Srpaulo	pos += 16 + 8;
316214501Srpaulo	nt_response = pos;
317214501Srpaulo	pos += 24;
318214501Srpaulo	flags = *pos++;
319214501Srpaulo	name = pos;
320214501Srpaulo	name_len = end - name;
321214501Srpaulo
322214501Srpaulo	if (data->peer_challenge) {
323214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
324214501Srpaulo			   "Peer-Challenge");
325214501Srpaulo		peer_challenge = data->peer_challenge;
326214501Srpaulo	}
327214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
328214501Srpaulo		    peer_challenge, 16);
329214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
330214501Srpaulo	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
331214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
332214501Srpaulo
333281806Srpaulo	buf = os_malloc(name_len * 4 + 1);
334281806Srpaulo	if (buf) {
335281806Srpaulo		printf_encode(buf, name_len * 4 + 1, name, name_len);
336281806Srpaulo		eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
337281806Srpaulo		os_free(buf);
338281806Srpaulo	}
339281806Srpaulo
340214501Srpaulo	/* MSCHAPv2 does not include optional domain name in the
341214501Srpaulo	 * challenge-response calculation, so remove domain prefix
342214501Srpaulo	 * (if present). */
343214501Srpaulo	username = sm->identity;
344214501Srpaulo	username_len = sm->identity_len;
345214501Srpaulo	for (i = 0; i < username_len; i++) {
346214501Srpaulo		if (username[i] == '\\') {
347214501Srpaulo			username_len -= i + 1;
348214501Srpaulo			username += i + 1;
349214501Srpaulo			break;
350214501Srpaulo		}
351214501Srpaulo	}
352214501Srpaulo
353214501Srpaulo	user = name;
354214501Srpaulo	user_len = name_len;
355214501Srpaulo	for (i = 0; i < user_len; i++) {
356214501Srpaulo		if (user[i] == '\\') {
357214501Srpaulo			user_len -= i + 1;
358214501Srpaulo			user += i + 1;
359214501Srpaulo			break;
360214501Srpaulo		}
361214501Srpaulo	}
362214501Srpaulo
363289549Srpaulo#ifdef CONFIG_TESTING_OPTIONS
364289549Srpaulo	{
365289549Srpaulo		u8 challenge[8];
366289549Srpaulo
367289549Srpaulo		if (challenge_hash(peer_challenge, data->auth_challenge,
368289549Srpaulo				   username, username_len, challenge) == 0) {
369289549Srpaulo			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
370289549Srpaulo						      username, username_len,
371289549Srpaulo						      challenge, nt_response);
372289549Srpaulo		}
373289549Srpaulo	}
374289549Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
375289549Srpaulo
376214501Srpaulo	if (username_len != user_len ||
377214501Srpaulo	    os_memcmp(username, user, username_len) != 0) {
378214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
379214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
380214501Srpaulo				  "name", username, username_len);
381214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
382214501Srpaulo				  "name", user, user_len);
383214501Srpaulo		data->state = FAILURE;
384214501Srpaulo		return;
385214501Srpaulo	}
386214501Srpaulo
387214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
388214501Srpaulo			  username, username_len);
389214501Srpaulo
390214501Srpaulo	if (sm->user->password_hash) {
391214501Srpaulo		res = generate_nt_response_pwhash(data->auth_challenge,
392214501Srpaulo						  peer_challenge,
393214501Srpaulo						  username, username_len,
394214501Srpaulo						  sm->user->password,
395214501Srpaulo						  expected);
396214501Srpaulo	} else {
397214501Srpaulo		res = generate_nt_response(data->auth_challenge,
398214501Srpaulo					   peer_challenge,
399214501Srpaulo					   username, username_len,
400214501Srpaulo					   sm->user->password,
401214501Srpaulo					   sm->user->password_len,
402214501Srpaulo					   expected);
403214501Srpaulo	}
404214501Srpaulo	if (res) {
405214501Srpaulo		data->state = FAILURE;
406214501Srpaulo		return;
407214501Srpaulo	}
408214501Srpaulo
409281806Srpaulo	if (os_memcmp_const(nt_response, expected, 24) == 0) {
410214501Srpaulo		const u8 *pw_hash;
411214501Srpaulo		u8 pw_hash_buf[16], pw_hash_hash[16];
412214501Srpaulo
413214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
414214501Srpaulo		data->state = SUCCESS_REQ;
415214501Srpaulo
416214501Srpaulo		/* Authenticator response is not really needed yet, but
417214501Srpaulo		 * calculate it here so that peer_challenge and username need
418214501Srpaulo		 * not be saved. */
419214501Srpaulo		if (sm->user->password_hash) {
420214501Srpaulo			pw_hash = sm->user->password;
421214501Srpaulo		} else {
422252726Srpaulo			if (nt_password_hash(sm->user->password,
423252726Srpaulo					     sm->user->password_len,
424252726Srpaulo					     pw_hash_buf) < 0) {
425252726Srpaulo				data->state = FAILURE;
426252726Srpaulo				return;
427252726Srpaulo			}
428214501Srpaulo			pw_hash = pw_hash_buf;
429214501Srpaulo		}
430281806Srpaulo		if (generate_authenticator_response_pwhash(
431281806Srpaulo			    pw_hash, peer_challenge, data->auth_challenge,
432281806Srpaulo			    username, username_len, nt_response,
433281806Srpaulo			    data->auth_response) < 0 ||
434281806Srpaulo		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
435281806Srpaulo		    get_master_key(pw_hash_hash, nt_response,
436281806Srpaulo				   data->master_key)) {
437281806Srpaulo			data->state = FAILURE;
438281806Srpaulo			return;
439281806Srpaulo		}
440214501Srpaulo		data->master_key_valid = 1;
441214501Srpaulo		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
442214501Srpaulo				data->master_key, MSCHAPV2_KEY_LEN);
443214501Srpaulo	} else {
444214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
445214501Srpaulo			    expected, 24);
446214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
447214501Srpaulo		data->state = FAILURE_REQ;
448214501Srpaulo	}
449214501Srpaulo}
450214501Srpaulo
451214501Srpaulo
452214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm,
453214501Srpaulo					      struct eap_mschapv2_data *data,
454214501Srpaulo					      struct wpabuf *respData)
455214501Srpaulo{
456214501Srpaulo	struct eap_mschapv2_hdr *resp;
457214501Srpaulo	const u8 *pos;
458214501Srpaulo	size_t len;
459214501Srpaulo
460214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
461214501Srpaulo			       &len);
462214501Srpaulo	if (pos == NULL || len < 1)
463214501Srpaulo		return; /* Should not happen - frame already validated */
464214501Srpaulo
465214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
466214501Srpaulo
467214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
468214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
469214501Srpaulo			   " - authentication completed successfully");
470214501Srpaulo		data->state = SUCCESS;
471214501Srpaulo	} else {
472214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
473214501Srpaulo			   "Response - peer rejected authentication");
474214501Srpaulo		data->state = FAILURE;
475214501Srpaulo	}
476214501Srpaulo}
477214501Srpaulo
478214501Srpaulo
479214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
480214501Srpaulo					      struct eap_mschapv2_data *data,
481214501Srpaulo					      struct wpabuf *respData)
482214501Srpaulo{
483214501Srpaulo	struct eap_mschapv2_hdr *resp;
484214501Srpaulo	const u8 *pos;
485214501Srpaulo	size_t len;
486214501Srpaulo
487214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
488214501Srpaulo			       &len);
489214501Srpaulo	if (pos == NULL || len < 1)
490214501Srpaulo		return; /* Should not happen - frame already validated */
491214501Srpaulo
492214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
493214501Srpaulo
494214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
495214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
496214501Srpaulo			   " - authentication failed");
497214501Srpaulo	} else {
498214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
499214501Srpaulo			   "Response - authentication failed");
500214501Srpaulo	}
501214501Srpaulo
502214501Srpaulo	data->state = FAILURE;
503214501Srpaulo}
504214501Srpaulo
505214501Srpaulo
506214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv,
507214501Srpaulo				 struct wpabuf *respData)
508214501Srpaulo{
509214501Srpaulo	struct eap_mschapv2_data *data = priv;
510214501Srpaulo
511214501Srpaulo	if (sm->user == NULL || sm->user->password == NULL) {
512214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
513214501Srpaulo		data->state = FAILURE;
514214501Srpaulo		return;
515214501Srpaulo	}
516214501Srpaulo
517214501Srpaulo	switch (data->state) {
518214501Srpaulo	case CHALLENGE:
519214501Srpaulo		eap_mschapv2_process_response(sm, data, respData);
520214501Srpaulo		break;
521214501Srpaulo	case SUCCESS_REQ:
522214501Srpaulo		eap_mschapv2_process_success_resp(sm, data, respData);
523214501Srpaulo		break;
524214501Srpaulo	case FAILURE_REQ:
525214501Srpaulo		eap_mschapv2_process_failure_resp(sm, data, respData);
526214501Srpaulo		break;
527214501Srpaulo	default:
528214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
529214501Srpaulo			   "process", data->state);
530214501Srpaulo		break;
531214501Srpaulo	}
532214501Srpaulo}
533214501Srpaulo
534214501Srpaulo
535214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
536214501Srpaulo{
537214501Srpaulo	struct eap_mschapv2_data *data = priv;
538214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
539214501Srpaulo}
540214501Srpaulo
541214501Srpaulo
542214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
543214501Srpaulo{
544214501Srpaulo	struct eap_mschapv2_data *data = priv;
545214501Srpaulo	u8 *key;
546214501Srpaulo
547214501Srpaulo	if (data->state != SUCCESS || !data->master_key_valid)
548214501Srpaulo		return NULL;
549214501Srpaulo
550214501Srpaulo	*len = 2 * MSCHAPV2_KEY_LEN;
551214501Srpaulo	key = os_malloc(*len);
552214501Srpaulo	if (key == NULL)
553214501Srpaulo		return NULL;
554214501Srpaulo	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
555214501Srpaulo	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
556214501Srpaulo	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557214501Srpaulo				MSCHAPV2_KEY_LEN, 1, 1);
558214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
559214501Srpaulo
560214501Srpaulo	return key;
561214501Srpaulo}
562214501Srpaulo
563214501Srpaulo
564214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
565214501Srpaulo{
566214501Srpaulo	struct eap_mschapv2_data *data = priv;
567214501Srpaulo	return data->state == SUCCESS;
568214501Srpaulo}
569214501Srpaulo
570214501Srpaulo
571214501Srpauloint eap_server_mschapv2_register(void)
572214501Srpaulo{
573214501Srpaulo	struct eap_method *eap;
574214501Srpaulo
575214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
576214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
577214501Srpaulo				      "MSCHAPV2");
578214501Srpaulo	if (eap == NULL)
579214501Srpaulo		return -1;
580214501Srpaulo
581214501Srpaulo	eap->init = eap_mschapv2_init;
582214501Srpaulo	eap->reset = eap_mschapv2_reset;
583214501Srpaulo	eap->buildReq = eap_mschapv2_buildReq;
584214501Srpaulo	eap->check = eap_mschapv2_check;
585214501Srpaulo	eap->process = eap_mschapv2_process;
586214501Srpaulo	eap->isDone = eap_mschapv2_isDone;
587214501Srpaulo	eap->getKey = eap_mschapv2_getKey;
588214501Srpaulo	eap->isSuccess = eap_mschapv2_isSuccess;
589214501Srpaulo
590337817Scy	return eap_server_method_register(eap);
591214501Srpaulo}
592