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) {
74346981Scy		data->peer_challenge = os_memdup(sm->peer_challenge,
75346981Scy						 CHALLENGE_LEN);
76214501Srpaulo		if (data->peer_challenge == NULL) {
77214501Srpaulo			os_free(data);
78214501Srpaulo			return NULL;
79214501Srpaulo		}
80214501Srpaulo	}
81214501Srpaulo
82214501Srpaulo	return data;
83214501Srpaulo}
84214501Srpaulo
85214501Srpaulo
86214501Srpaulostatic void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
87214501Srpaulo{
88214501Srpaulo	struct eap_mschapv2_data *data = priv;
89214501Srpaulo	if (data == NULL)
90214501Srpaulo		return;
91214501Srpaulo
92214501Srpaulo	os_free(data->peer_challenge);
93281806Srpaulo	bin_clear_free(data, sizeof(*data));
94214501Srpaulo}
95214501Srpaulo
96214501Srpaulo
97214501Srpaulostatic struct wpabuf * eap_mschapv2_build_challenge(
98214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
99214501Srpaulo{
100214501Srpaulo	struct wpabuf *req;
101214501Srpaulo	struct eap_mschapv2_hdr *ms;
102214501Srpaulo	size_t ms_len;
103214501Srpaulo
104214501Srpaulo	if (!data->auth_challenge_from_tls &&
105252726Srpaulo	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
106214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
107214501Srpaulo			   "data");
108214501Srpaulo		data->state = FAILURE;
109214501Srpaulo		return NULL;
110214501Srpaulo	}
111214501Srpaulo
112281806Srpaulo	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
113214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
114214501Srpaulo			    EAP_CODE_REQUEST, id);
115214501Srpaulo	if (req == NULL) {
116214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
117214501Srpaulo			   " for request");
118214501Srpaulo		data->state = FAILURE;
119214501Srpaulo		return NULL;
120214501Srpaulo	}
121214501Srpaulo
122214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
123214501Srpaulo	ms->op_code = MSCHAPV2_OP_CHALLENGE;
124214501Srpaulo	ms->mschapv2_id = id;
125214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
126214501Srpaulo
127214501Srpaulo	wpabuf_put_u8(req, CHALLENGE_LEN);
128214501Srpaulo	if (!data->auth_challenge_from_tls)
129214501Srpaulo		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
130214501Srpaulo	else
131214501Srpaulo		wpabuf_put(req, CHALLENGE_LEN);
132214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
133214501Srpaulo		    data->auth_challenge, CHALLENGE_LEN);
134281806Srpaulo	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
135214501Srpaulo
136214501Srpaulo	return req;
137214501Srpaulo}
138214501Srpaulo
139214501Srpaulo
140214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req(
141214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
142214501Srpaulo{
143214501Srpaulo	struct wpabuf *req;
144214501Srpaulo	struct eap_mschapv2_hdr *ms;
145214501Srpaulo	u8 *msg;
146214501Srpaulo	char *message = "OK";
147214501Srpaulo	size_t ms_len;
148214501Srpaulo
149214501Srpaulo	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
150214501Srpaulo		os_strlen(message);
151214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
152214501Srpaulo			    EAP_CODE_REQUEST, id);
153214501Srpaulo	if (req == NULL) {
154214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
155214501Srpaulo			   " for request");
156214501Srpaulo		data->state = FAILURE;
157214501Srpaulo		return NULL;
158214501Srpaulo	}
159214501Srpaulo
160214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
161214501Srpaulo	ms->op_code = MSCHAPV2_OP_SUCCESS;
162214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
163214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
164214501Srpaulo	msg = (u8 *) (ms + 1);
165214501Srpaulo
166214501Srpaulo	wpabuf_put_u8(req, 'S');
167214501Srpaulo	wpabuf_put_u8(req, '=');
168214501Srpaulo	wpa_snprintf_hex_uppercase(
169214501Srpaulo		wpabuf_put(req, sizeof(data->auth_response) * 2),
170214501Srpaulo		sizeof(data->auth_response) * 2 + 1,
171214501Srpaulo		data->auth_response, sizeof(data->auth_response));
172214501Srpaulo	wpabuf_put_u8(req, ' ');
173214501Srpaulo	wpabuf_put_u8(req, 'M');
174214501Srpaulo	wpabuf_put_u8(req, '=');
175214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
176214501Srpaulo
177214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
178214501Srpaulo			  msg, ms_len - sizeof(*ms));
179214501Srpaulo
180214501Srpaulo	return req;
181214501Srpaulo}
182214501Srpaulo
183214501Srpaulo
184214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req(
185214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
186214501Srpaulo{
187214501Srpaulo	struct wpabuf *req;
188214501Srpaulo	struct eap_mschapv2_hdr *ms;
189214501Srpaulo	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
190214501Srpaulo		"M=FAILED";
191214501Srpaulo	size_t ms_len;
192214501Srpaulo
193214501Srpaulo	ms_len = sizeof(*ms) + os_strlen(message);
194214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
195214501Srpaulo			    EAP_CODE_REQUEST, id);
196214501Srpaulo	if (req == NULL) {
197214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
198214501Srpaulo			   " for request");
199214501Srpaulo		data->state = FAILURE;
200214501Srpaulo		return NULL;
201214501Srpaulo	}
202214501Srpaulo
203214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
204214501Srpaulo	ms->op_code = MSCHAPV2_OP_FAILURE;
205214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
206214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
207214501Srpaulo
208214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
209214501Srpaulo
210214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
211214501Srpaulo			  (u8 *) message, os_strlen(message));
212214501Srpaulo
213214501Srpaulo	return req;
214214501Srpaulo}
215214501Srpaulo
216214501Srpaulo
217214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
218214501Srpaulo					     u8 id)
219214501Srpaulo{
220214501Srpaulo	struct eap_mschapv2_data *data = priv;
221214501Srpaulo
222214501Srpaulo	switch (data->state) {
223214501Srpaulo	case CHALLENGE:
224214501Srpaulo		return eap_mschapv2_build_challenge(sm, data, id);
225214501Srpaulo	case SUCCESS_REQ:
226214501Srpaulo		return eap_mschapv2_build_success_req(sm, data, id);
227214501Srpaulo	case FAILURE_REQ:
228214501Srpaulo		return eap_mschapv2_build_failure_req(sm, data, id);
229214501Srpaulo	default:
230214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
231214501Srpaulo			   "buildReq", data->state);
232214501Srpaulo		break;
233214501Srpaulo	}
234214501Srpaulo	return NULL;
235214501Srpaulo}
236214501Srpaulo
237214501Srpaulo
238214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
239214501Srpaulo				  struct wpabuf *respData)
240214501Srpaulo{
241214501Srpaulo	struct eap_mschapv2_data *data = priv;
242214501Srpaulo	struct eap_mschapv2_hdr *resp;
243214501Srpaulo	const u8 *pos;
244214501Srpaulo	size_t len;
245214501Srpaulo
246214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
247214501Srpaulo			       &len);
248214501Srpaulo	if (pos == NULL || len < 1) {
249214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
250214501Srpaulo		return TRUE;
251214501Srpaulo	}
252214501Srpaulo
253214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
254214501Srpaulo	if (data->state == CHALLENGE &&
255214501Srpaulo	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
256214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
257214501Srpaulo			   "ignore op %d", resp->op_code);
258214501Srpaulo		return TRUE;
259214501Srpaulo	}
260214501Srpaulo
261214501Srpaulo	if (data->state == SUCCESS_REQ &&
262214501Srpaulo	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
263214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
264214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
265214501Srpaulo			   "Failure - ignore op %d", resp->op_code);
266214501Srpaulo		return TRUE;
267214501Srpaulo	}
268214501Srpaulo
269214501Srpaulo	if (data->state == FAILURE_REQ &&
270214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
271214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
272214501Srpaulo			   "- ignore op %d", resp->op_code);
273214501Srpaulo		return TRUE;
274214501Srpaulo	}
275214501Srpaulo
276214501Srpaulo	return FALSE;
277214501Srpaulo}
278214501Srpaulo
279214501Srpaulo
280214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm,
281214501Srpaulo					  struct eap_mschapv2_data *data,
282214501Srpaulo					  struct wpabuf *respData)
283214501Srpaulo{
284214501Srpaulo	struct eap_mschapv2_hdr *resp;
285214501Srpaulo	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
286214501Srpaulo	u8 flags;
287214501Srpaulo	size_t len, name_len, i;
288214501Srpaulo	u8 expected[24];
289214501Srpaulo	const u8 *username, *user;
290214501Srpaulo	size_t username_len, user_len;
291214501Srpaulo	int res;
292281806Srpaulo	char *buf;
293214501Srpaulo
294214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
295214501Srpaulo			       &len);
296214501Srpaulo	if (pos == NULL || len < 1)
297214501Srpaulo		return; /* Should not happen - frame already validated */
298214501Srpaulo
299214501Srpaulo	end = pos + len;
300214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
301214501Srpaulo	pos = (u8 *) (resp + 1);
302214501Srpaulo
303214501Srpaulo	if (len < sizeof(*resp) + 1 + 49 ||
304214501Srpaulo	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
305214501Srpaulo	    pos[0] != 49) {
306214501Srpaulo		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
307214501Srpaulo				respData);
308214501Srpaulo		data->state = FAILURE;
309214501Srpaulo		return;
310214501Srpaulo	}
311214501Srpaulo	data->resp_mschapv2_id = resp->mschapv2_id;
312214501Srpaulo	pos++;
313214501Srpaulo	peer_challenge = pos;
314214501Srpaulo	pos += 16 + 8;
315214501Srpaulo	nt_response = pos;
316214501Srpaulo	pos += 24;
317214501Srpaulo	flags = *pos++;
318214501Srpaulo	name = pos;
319214501Srpaulo	name_len = end - name;
320214501Srpaulo
321214501Srpaulo	if (data->peer_challenge) {
322214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
323214501Srpaulo			   "Peer-Challenge");
324214501Srpaulo		peer_challenge = data->peer_challenge;
325214501Srpaulo	}
326214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
327214501Srpaulo		    peer_challenge, 16);
328214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
329214501Srpaulo	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
330214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
331214501Srpaulo
332281806Srpaulo	buf = os_malloc(name_len * 4 + 1);
333281806Srpaulo	if (buf) {
334281806Srpaulo		printf_encode(buf, name_len * 4 + 1, name, name_len);
335281806Srpaulo		eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
336281806Srpaulo		os_free(buf);
337281806Srpaulo	}
338281806Srpaulo
339214501Srpaulo	/* MSCHAPv2 does not include optional domain name in the
340214501Srpaulo	 * challenge-response calculation, so remove domain prefix
341214501Srpaulo	 * (if present). */
342214501Srpaulo	username = sm->identity;
343214501Srpaulo	username_len = sm->identity_len;
344214501Srpaulo	for (i = 0; i < username_len; i++) {
345214501Srpaulo		if (username[i] == '\\') {
346214501Srpaulo			username_len -= i + 1;
347214501Srpaulo			username += i + 1;
348214501Srpaulo			break;
349214501Srpaulo		}
350214501Srpaulo	}
351214501Srpaulo
352214501Srpaulo	user = name;
353214501Srpaulo	user_len = name_len;
354214501Srpaulo	for (i = 0; i < user_len; i++) {
355214501Srpaulo		if (user[i] == '\\') {
356214501Srpaulo			user_len -= i + 1;
357214501Srpaulo			user += i + 1;
358214501Srpaulo			break;
359214501Srpaulo		}
360214501Srpaulo	}
361214501Srpaulo
362289549Srpaulo#ifdef CONFIG_TESTING_OPTIONS
363289549Srpaulo	{
364289549Srpaulo		u8 challenge[8];
365289549Srpaulo
366289549Srpaulo		if (challenge_hash(peer_challenge, data->auth_challenge,
367289549Srpaulo				   username, username_len, challenge) == 0) {
368289549Srpaulo			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
369289549Srpaulo						      username, username_len,
370289549Srpaulo						      challenge, nt_response);
371289549Srpaulo		}
372289549Srpaulo	}
373289549Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
374289549Srpaulo
375214501Srpaulo	if (username_len != user_len ||
376214501Srpaulo	    os_memcmp(username, user, username_len) != 0) {
377214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
378214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
379214501Srpaulo				  "name", username, username_len);
380214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
381214501Srpaulo				  "name", user, user_len);
382214501Srpaulo		data->state = FAILURE;
383214501Srpaulo		return;
384214501Srpaulo	}
385214501Srpaulo
386214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
387214501Srpaulo			  username, username_len);
388214501Srpaulo
389214501Srpaulo	if (sm->user->password_hash) {
390214501Srpaulo		res = generate_nt_response_pwhash(data->auth_challenge,
391214501Srpaulo						  peer_challenge,
392214501Srpaulo						  username, username_len,
393214501Srpaulo						  sm->user->password,
394214501Srpaulo						  expected);
395214501Srpaulo	} else {
396214501Srpaulo		res = generate_nt_response(data->auth_challenge,
397214501Srpaulo					   peer_challenge,
398214501Srpaulo					   username, username_len,
399214501Srpaulo					   sm->user->password,
400214501Srpaulo					   sm->user->password_len,
401214501Srpaulo					   expected);
402214501Srpaulo	}
403214501Srpaulo	if (res) {
404214501Srpaulo		data->state = FAILURE;
405214501Srpaulo		return;
406214501Srpaulo	}
407214501Srpaulo
408281806Srpaulo	if (os_memcmp_const(nt_response, expected, 24) == 0) {
409214501Srpaulo		const u8 *pw_hash;
410214501Srpaulo		u8 pw_hash_buf[16], pw_hash_hash[16];
411214501Srpaulo
412214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
413214501Srpaulo		data->state = SUCCESS_REQ;
414214501Srpaulo
415214501Srpaulo		/* Authenticator response is not really needed yet, but
416214501Srpaulo		 * calculate it here so that peer_challenge and username need
417214501Srpaulo		 * not be saved. */
418214501Srpaulo		if (sm->user->password_hash) {
419214501Srpaulo			pw_hash = sm->user->password;
420214501Srpaulo		} else {
421252726Srpaulo			if (nt_password_hash(sm->user->password,
422252726Srpaulo					     sm->user->password_len,
423252726Srpaulo					     pw_hash_buf) < 0) {
424252726Srpaulo				data->state = FAILURE;
425252726Srpaulo				return;
426252726Srpaulo			}
427214501Srpaulo			pw_hash = pw_hash_buf;
428214501Srpaulo		}
429281806Srpaulo		if (generate_authenticator_response_pwhash(
430281806Srpaulo			    pw_hash, peer_challenge, data->auth_challenge,
431281806Srpaulo			    username, username_len, nt_response,
432281806Srpaulo			    data->auth_response) < 0 ||
433281806Srpaulo		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
434281806Srpaulo		    get_master_key(pw_hash_hash, nt_response,
435281806Srpaulo				   data->master_key)) {
436281806Srpaulo			data->state = FAILURE;
437281806Srpaulo			return;
438281806Srpaulo		}
439214501Srpaulo		data->master_key_valid = 1;
440214501Srpaulo		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
441214501Srpaulo				data->master_key, MSCHAPV2_KEY_LEN);
442214501Srpaulo	} else {
443214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
444214501Srpaulo			    expected, 24);
445214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
446214501Srpaulo		data->state = FAILURE_REQ;
447214501Srpaulo	}
448214501Srpaulo}
449214501Srpaulo
450214501Srpaulo
451214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm,
452214501Srpaulo					      struct eap_mschapv2_data *data,
453214501Srpaulo					      struct wpabuf *respData)
454214501Srpaulo{
455214501Srpaulo	struct eap_mschapv2_hdr *resp;
456214501Srpaulo	const u8 *pos;
457214501Srpaulo	size_t len;
458214501Srpaulo
459214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
460214501Srpaulo			       &len);
461214501Srpaulo	if (pos == NULL || len < 1)
462214501Srpaulo		return; /* Should not happen - frame already validated */
463214501Srpaulo
464214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
465214501Srpaulo
466214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
467214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
468214501Srpaulo			   " - authentication completed successfully");
469214501Srpaulo		data->state = SUCCESS;
470214501Srpaulo	} else {
471214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
472214501Srpaulo			   "Response - peer rejected authentication");
473214501Srpaulo		data->state = FAILURE;
474214501Srpaulo	}
475214501Srpaulo}
476214501Srpaulo
477214501Srpaulo
478214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
479214501Srpaulo					      struct eap_mschapv2_data *data,
480214501Srpaulo					      struct wpabuf *respData)
481214501Srpaulo{
482214501Srpaulo	struct eap_mschapv2_hdr *resp;
483214501Srpaulo	const u8 *pos;
484214501Srpaulo	size_t len;
485214501Srpaulo
486214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
487214501Srpaulo			       &len);
488214501Srpaulo	if (pos == NULL || len < 1)
489214501Srpaulo		return; /* Should not happen - frame already validated */
490214501Srpaulo
491214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
492214501Srpaulo
493214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
494214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
495214501Srpaulo			   " - authentication failed");
496214501Srpaulo	} else {
497214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
498214501Srpaulo			   "Response - authentication failed");
499214501Srpaulo	}
500214501Srpaulo
501214501Srpaulo	data->state = FAILURE;
502214501Srpaulo}
503214501Srpaulo
504214501Srpaulo
505214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv,
506214501Srpaulo				 struct wpabuf *respData)
507214501Srpaulo{
508214501Srpaulo	struct eap_mschapv2_data *data = priv;
509214501Srpaulo
510214501Srpaulo	if (sm->user == NULL || sm->user->password == NULL) {
511214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
512214501Srpaulo		data->state = FAILURE;
513214501Srpaulo		return;
514214501Srpaulo	}
515214501Srpaulo
516214501Srpaulo	switch (data->state) {
517214501Srpaulo	case CHALLENGE:
518214501Srpaulo		eap_mschapv2_process_response(sm, data, respData);
519214501Srpaulo		break;
520214501Srpaulo	case SUCCESS_REQ:
521214501Srpaulo		eap_mschapv2_process_success_resp(sm, data, respData);
522214501Srpaulo		break;
523214501Srpaulo	case FAILURE_REQ:
524214501Srpaulo		eap_mschapv2_process_failure_resp(sm, data, respData);
525214501Srpaulo		break;
526214501Srpaulo	default:
527214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
528214501Srpaulo			   "process", data->state);
529214501Srpaulo		break;
530214501Srpaulo	}
531214501Srpaulo}
532214501Srpaulo
533214501Srpaulo
534214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
535214501Srpaulo{
536214501Srpaulo	struct eap_mschapv2_data *data = priv;
537214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
538214501Srpaulo}
539214501Srpaulo
540214501Srpaulo
541214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
542214501Srpaulo{
543214501Srpaulo	struct eap_mschapv2_data *data = priv;
544214501Srpaulo	u8 *key;
545214501Srpaulo
546214501Srpaulo	if (data->state != SUCCESS || !data->master_key_valid)
547214501Srpaulo		return NULL;
548214501Srpaulo
549214501Srpaulo	*len = 2 * MSCHAPV2_KEY_LEN;
550214501Srpaulo	key = os_malloc(*len);
551214501Srpaulo	if (key == NULL)
552214501Srpaulo		return NULL;
553214501Srpaulo	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554346981Scy	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
555346981Scy				    1) < 0 ||
556346981Scy	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557346981Scy				    MSCHAPV2_KEY_LEN, 1, 1) < 0) {
558346981Scy		os_free(key);
559346981Scy		return NULL;
560346981Scy	}
561214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
562214501Srpaulo
563214501Srpaulo	return key;
564214501Srpaulo}
565214501Srpaulo
566214501Srpaulo
567214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
568214501Srpaulo{
569214501Srpaulo	struct eap_mschapv2_data *data = priv;
570214501Srpaulo	return data->state == SUCCESS;
571214501Srpaulo}
572214501Srpaulo
573214501Srpaulo
574214501Srpauloint eap_server_mschapv2_register(void)
575214501Srpaulo{
576214501Srpaulo	struct eap_method *eap;
577214501Srpaulo
578214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
579214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
580214501Srpaulo				      "MSCHAPV2");
581214501Srpaulo	if (eap == NULL)
582214501Srpaulo		return -1;
583214501Srpaulo
584214501Srpaulo	eap->init = eap_mschapv2_init;
585214501Srpaulo	eap->reset = eap_mschapv2_reset;
586214501Srpaulo	eap->buildReq = eap_mschapv2_buildReq;
587214501Srpaulo	eap->check = eap_mschapv2_check;
588214501Srpaulo	eap->process = eap_mschapv2_process;
589214501Srpaulo	eap->isDone = eap_mschapv2_isDone;
590214501Srpaulo	eap->getKey = eap_mschapv2_getKey;
591214501Srpaulo	eap->isSuccess = eap_mschapv2_isSuccess;
592214501Srpaulo
593337817Scy	return eap_server_method_register(eap);
594214501Srpaulo}
595