eap_server_mschapv2.c revision 346981
1226031Sstas/*
2226031Sstas * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3226031Sstas * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4226031Sstas *
5226031Sstas * This software may be distributed under the terms of the BSD license.
6226031Sstas * See README for more details.
7226031Sstas */
8226031Sstas
9226031Sstas#include "includes.h"
10226031Sstas
11226031Sstas#include "common.h"
12226031Sstas#include "crypto/ms_funcs.h"
13226031Sstas#include "crypto/random.h"
14226031Sstas#include "eap_i.h"
15226031Sstas
16226031Sstas
17226031Sstasstruct eap_mschapv2_hdr {
18226031Sstas	u8 op_code; /* MSCHAPV2_OP_* */
19226031Sstas	u8 mschapv2_id; /* must be changed for challenges, but not for
20226031Sstas			 * success/failure */
21226031Sstas	u8 ms_length[2]; /* Note: misaligned; length - 5 */
22226031Sstas	/* followed by data */
23226031Sstas} STRUCT_PACKED;
24226031Sstas
25226031Sstas#define MSCHAPV2_OP_CHALLENGE 1
26226031Sstas#define MSCHAPV2_OP_RESPONSE 2
27226031Sstas#define MSCHAPV2_OP_SUCCESS 3
28226031Sstas#define MSCHAPV2_OP_FAILURE 4
29226031Sstas#define MSCHAPV2_OP_CHANGE_PASSWORD 7
30226031Sstas
31226031Sstas#define MSCHAPV2_RESP_LEN 49
32226031Sstas
33226031Sstas#define ERROR_RESTRICTED_LOGON_HOURS 646
34226031Sstas#define ERROR_ACCT_DISABLED 647
35226031Sstas#define ERROR_PASSWD_EXPIRED 648
36226031Sstas#define ERROR_NO_DIALIN_PERMISSION 649
37226031Sstas#define ERROR_AUTHENTICATION_FAILURE 691
38226031Sstas#define ERROR_CHANGING_PASSWORD 709
39226031Sstas
40226031Sstas#define PASSWD_CHANGE_CHAL_LEN 16
41226031Sstas#define MSCHAPV2_KEY_LEN 16
42226031Sstas
43226031Sstas
44226031Sstas#define CHALLENGE_LEN 16
45226031Sstas
46226031Sstasstruct eap_mschapv2_data {
47226031Sstas	u8 auth_challenge[CHALLENGE_LEN];
48226031Sstas	int auth_challenge_from_tls;
49226031Sstas	u8 *peer_challenge;
50226031Sstas	u8 auth_response[20];
51226031Sstas	enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
52226031Sstas	u8 resp_mschapv2_id;
53226031Sstas	u8 master_key[16];
54226031Sstas	int master_key_valid;
55226031Sstas};
56226031Sstas
57226031Sstas
58226031Sstasstatic void * eap_mschapv2_init(struct eap_sm *sm)
59226031Sstas{
60226031Sstas	struct eap_mschapv2_data *data;
61226031Sstas
62226031Sstas	data = os_zalloc(sizeof(*data));
63226031Sstas	if (data == NULL)
64226031Sstas		return NULL;
65226031Sstas	data->state = CHALLENGE;
66226031Sstas
67226031Sstas	if (sm->auth_challenge) {
68226031Sstas		os_memcpy(data->auth_challenge, sm->auth_challenge,
69226031Sstas			  CHALLENGE_LEN);
70226031Sstas		data->auth_challenge_from_tls = 1;
71226031Sstas	}
72226031Sstas
73226031Sstas	if (sm->peer_challenge) {
74226031Sstas		data->peer_challenge = os_memdup(sm->peer_challenge,
75226031Sstas						 CHALLENGE_LEN);
76226031Sstas		if (data->peer_challenge == NULL) {
77226031Sstas			os_free(data);
78226031Sstas			return NULL;
79226031Sstas		}
80226031Sstas	}
81226031Sstas
82226031Sstas	return data;
83226031Sstas}
84226031Sstas
85226031Sstas
86226031Sstasstatic void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
87226031Sstas{
88226031Sstas	struct eap_mschapv2_data *data = priv;
89226031Sstas	if (data == NULL)
90226031Sstas		return;
91226031Sstas
92226031Sstas	os_free(data->peer_challenge);
93226031Sstas	bin_clear_free(data, sizeof(*data));
94226031Sstas}
95226031Sstas
96226031Sstas
97226031Sstasstatic struct wpabuf * eap_mschapv2_build_challenge(
98226031Sstas	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
99226031Sstas{
100226031Sstas	struct wpabuf *req;
101226031Sstas	struct eap_mschapv2_hdr *ms;
102226031Sstas	size_t ms_len;
103226031Sstas
104226031Sstas	if (!data->auth_challenge_from_tls &&
105226031Sstas	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
106226031Sstas		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
107226031Sstas			   "data");
108226031Sstas		data->state = FAILURE;
109226031Sstas		return NULL;
110226031Sstas	}
111226031Sstas
112226031Sstas	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
113226031Sstas	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
114226031Sstas			    EAP_CODE_REQUEST, id);
115226031Sstas	if (req == NULL) {
116226031Sstas		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
117226031Sstas			   " for request");
118226031Sstas		data->state = FAILURE;
119226031Sstas		return NULL;
120226031Sstas	}
121226031Sstas
122226031Sstas	ms = wpabuf_put(req, sizeof(*ms));
123226031Sstas	ms->op_code = MSCHAPV2_OP_CHALLENGE;
124226031Sstas	ms->mschapv2_id = id;
125226031Sstas	WPA_PUT_BE16(ms->ms_length, ms_len);
126226031Sstas
127226031Sstas	wpabuf_put_u8(req, CHALLENGE_LEN);
128226031Sstas	if (!data->auth_challenge_from_tls)
129226031Sstas		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
130226031Sstas	else
131226031Sstas		wpabuf_put(req, CHALLENGE_LEN);
132226031Sstas	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
133226031Sstas		    data->auth_challenge, CHALLENGE_LEN);
134226031Sstas	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
135226031Sstas
136226031Sstas	return req;
137226031Sstas}
138226031Sstas
139226031Sstas
140226031Sstasstatic struct wpabuf * eap_mschapv2_build_success_req(
141226031Sstas	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
142226031Sstas{
143226031Sstas	struct wpabuf *req;
144226031Sstas	struct eap_mschapv2_hdr *ms;
145226031Sstas	u8 *msg;
146226031Sstas	char *message = "OK";
147226031Sstas	size_t ms_len;
148226031Sstas
149226031Sstas	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
150226031Sstas		os_strlen(message);
151226031Sstas	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
152226031Sstas			    EAP_CODE_REQUEST, id);
153226031Sstas	if (req == NULL) {
154226031Sstas		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
155226031Sstas			   " for request");
156226031Sstas		data->state = FAILURE;
157226031Sstas		return NULL;
158226031Sstas	}
159226031Sstas
160226031Sstas	ms = wpabuf_put(req, sizeof(*ms));
161226031Sstas	ms->op_code = MSCHAPV2_OP_SUCCESS;
162226031Sstas	ms->mschapv2_id = data->resp_mschapv2_id;
163226031Sstas	WPA_PUT_BE16(ms->ms_length, ms_len);
164226031Sstas	msg = (u8 *) (ms + 1);
165226031Sstas
166226031Sstas	wpabuf_put_u8(req, 'S');
167226031Sstas	wpabuf_put_u8(req, '=');
168226031Sstas	wpa_snprintf_hex_uppercase(
169226031Sstas		wpabuf_put(req, sizeof(data->auth_response) * 2),
170226031Sstas		sizeof(data->auth_response) * 2 + 1,
171226031Sstas		data->auth_response, sizeof(data->auth_response));
172226031Sstas	wpabuf_put_u8(req, ' ');
173226031Sstas	wpabuf_put_u8(req, 'M');
174226031Sstas	wpabuf_put_u8(req, '=');
175226031Sstas	wpabuf_put_data(req, message, os_strlen(message));
176226031Sstas
177226031Sstas	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
178226031Sstas			  msg, ms_len - sizeof(*ms));
179226031Sstas
180226031Sstas	return req;
181226031Sstas}
182226031Sstas
183226031Sstas
184226031Sstasstatic struct wpabuf * eap_mschapv2_build_failure_req(
185226031Sstas	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
186226031Sstas{
187226031Sstas	struct wpabuf *req;
188226031Sstas	struct eap_mschapv2_hdr *ms;
189226031Sstas	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
190226031Sstas		"M=FAILED";
191226031Sstas	size_t ms_len;
192226031Sstas
193226031Sstas	ms_len = sizeof(*ms) + os_strlen(message);
194226031Sstas	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
195226031Sstas			    EAP_CODE_REQUEST, id);
196226031Sstas	if (req == NULL) {
197226031Sstas		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
198226031Sstas			   " for request");
199226031Sstas		data->state = FAILURE;
200226031Sstas		return NULL;
201226031Sstas	}
202226031Sstas
203226031Sstas	ms = wpabuf_put(req, sizeof(*ms));
204226031Sstas	ms->op_code = MSCHAPV2_OP_FAILURE;
205226031Sstas	ms->mschapv2_id = data->resp_mschapv2_id;
206226031Sstas	WPA_PUT_BE16(ms->ms_length, ms_len);
207226031Sstas
208226031Sstas	wpabuf_put_data(req, message, os_strlen(message));
209226031Sstas
210226031Sstas	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
211226031Sstas			  (u8 *) message, os_strlen(message));
212226031Sstas
213226031Sstas	return req;
214226031Sstas}
215226031Sstas
216226031Sstas
217226031Sstasstatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
218226031Sstas					     u8 id)
219226031Sstas{
220226031Sstas	struct eap_mschapv2_data *data = priv;
221226031Sstas
222226031Sstas	switch (data->state) {
223226031Sstas	case CHALLENGE:
224226031Sstas		return eap_mschapv2_build_challenge(sm, data, id);
225226031Sstas	case SUCCESS_REQ:
226226031Sstas		return eap_mschapv2_build_success_req(sm, data, id);
227226031Sstas	case FAILURE_REQ:
228226031Sstas		return eap_mschapv2_build_failure_req(sm, data, id);
229226031Sstas	default:
230226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
231226031Sstas			   "buildReq", data->state);
232226031Sstas		break;
233226031Sstas	}
234226031Sstas	return NULL;
235226031Sstas}
236226031Sstas
237226031Sstas
238226031Sstasstatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
239226031Sstas				  struct wpabuf *respData)
240226031Sstas{
241226031Sstas	struct eap_mschapv2_data *data = priv;
242226031Sstas	struct eap_mschapv2_hdr *resp;
243226031Sstas	const u8 *pos;
244226031Sstas	size_t len;
245226031Sstas
246226031Sstas	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
247226031Sstas			       &len);
248226031Sstas	if (pos == NULL || len < 1) {
249226031Sstas		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
250226031Sstas		return TRUE;
251226031Sstas	}
252226031Sstas
253226031Sstas	resp = (struct eap_mschapv2_hdr *) pos;
254226031Sstas	if (data->state == CHALLENGE &&
255226031Sstas	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
256226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
257226031Sstas			   "ignore op %d", resp->op_code);
258226031Sstas		return TRUE;
259226031Sstas	}
260226031Sstas
261226031Sstas	if (data->state == SUCCESS_REQ &&
262226031Sstas	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
263226031Sstas	    resp->op_code != MSCHAPV2_OP_FAILURE) {
264226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
265226031Sstas			   "Failure - ignore op %d", resp->op_code);
266226031Sstas		return TRUE;
267226031Sstas	}
268226031Sstas
269226031Sstas	if (data->state == FAILURE_REQ &&
270226031Sstas	    resp->op_code != MSCHAPV2_OP_FAILURE) {
271226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
272226031Sstas			   "- ignore op %d", resp->op_code);
273226031Sstas		return TRUE;
274226031Sstas	}
275226031Sstas
276226031Sstas	return FALSE;
277226031Sstas}
278226031Sstas
279226031Sstas
280226031Sstasstatic void eap_mschapv2_process_response(struct eap_sm *sm,
281226031Sstas					  struct eap_mschapv2_data *data,
282226031Sstas					  struct wpabuf *respData)
283226031Sstas{
284226031Sstas	struct eap_mschapv2_hdr *resp;
285226031Sstas	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
286226031Sstas	u8 flags;
287226031Sstas	size_t len, name_len, i;
288226031Sstas	u8 expected[24];
289226031Sstas	const u8 *username, *user;
290226031Sstas	size_t username_len, user_len;
291226031Sstas	int res;
292226031Sstas	char *buf;
293226031Sstas
294226031Sstas	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
295226031Sstas			       &len);
296226031Sstas	if (pos == NULL || len < 1)
297226031Sstas		return; /* Should not happen - frame already validated */
298226031Sstas
299226031Sstas	end = pos + len;
300226031Sstas	resp = (struct eap_mschapv2_hdr *) pos;
301226031Sstas	pos = (u8 *) (resp + 1);
302226031Sstas
303226031Sstas	if (len < sizeof(*resp) + 1 + 49 ||
304226031Sstas	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
305226031Sstas	    pos[0] != 49) {
306226031Sstas		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
307226031Sstas				respData);
308226031Sstas		data->state = FAILURE;
309226031Sstas		return;
310226031Sstas	}
311226031Sstas	data->resp_mschapv2_id = resp->mschapv2_id;
312226031Sstas	pos++;
313226031Sstas	peer_challenge = pos;
314226031Sstas	pos += 16 + 8;
315226031Sstas	nt_response = pos;
316226031Sstas	pos += 24;
317226031Sstas	flags = *pos++;
318226031Sstas	name = pos;
319226031Sstas	name_len = end - name;
320226031Sstas
321226031Sstas	if (data->peer_challenge) {
322226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
323226031Sstas			   "Peer-Challenge");
324226031Sstas		peer_challenge = data->peer_challenge;
325226031Sstas	}
326226031Sstas	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
327226031Sstas		    peer_challenge, 16);
328226031Sstas	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
329226031Sstas	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
330226031Sstas	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
331226031Sstas
332226031Sstas	buf = os_malloc(name_len * 4 + 1);
333226031Sstas	if (buf) {
334226031Sstas		printf_encode(buf, name_len * 4 + 1, name, name_len);
335226031Sstas		eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
336226031Sstas		os_free(buf);
337226031Sstas	}
338226031Sstas
339226031Sstas	/* MSCHAPv2 does not include optional domain name in the
340226031Sstas	 * challenge-response calculation, so remove domain prefix
341226031Sstas	 * (if present). */
342226031Sstas	username = sm->identity;
343226031Sstas	username_len = sm->identity_len;
344226031Sstas	for (i = 0; i < username_len; i++) {
345226031Sstas		if (username[i] == '\\') {
346226031Sstas			username_len -= i + 1;
347226031Sstas			username += i + 1;
348226031Sstas			break;
349226031Sstas		}
350226031Sstas	}
351226031Sstas
352226031Sstas	user = name;
353226031Sstas	user_len = name_len;
354226031Sstas	for (i = 0; i < user_len; i++) {
355226031Sstas		if (user[i] == '\\') {
356226031Sstas			user_len -= i + 1;
357226031Sstas			user += i + 1;
358226031Sstas			break;
359226031Sstas		}
360226031Sstas	}
361226031Sstas
362226031Sstas#ifdef CONFIG_TESTING_OPTIONS
363226031Sstas	{
364226031Sstas		u8 challenge[8];
365226031Sstas
366226031Sstas		if (challenge_hash(peer_challenge, data->auth_challenge,
367226031Sstas				   username, username_len, challenge) == 0) {
368226031Sstas			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
369226031Sstas						      username, username_len,
370226031Sstas						      challenge, nt_response);
371226031Sstas		}
372226031Sstas	}
373226031Sstas#endif /* CONFIG_TESTING_OPTIONS */
374226031Sstas
375226031Sstas	if (username_len != user_len ||
376226031Sstas	    os_memcmp(username, user, username_len) != 0) {
377226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
378226031Sstas		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
379226031Sstas				  "name", username, username_len);
380226031Sstas		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
381226031Sstas				  "name", user, user_len);
382226031Sstas		data->state = FAILURE;
383226031Sstas		return;
384226031Sstas	}
385226031Sstas
386226031Sstas	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
387226031Sstas			  username, username_len);
388226031Sstas
389226031Sstas	if (sm->user->password_hash) {
390226031Sstas		res = generate_nt_response_pwhash(data->auth_challenge,
391226031Sstas						  peer_challenge,
392226031Sstas						  username, username_len,
393226031Sstas						  sm->user->password,
394226031Sstas						  expected);
395226031Sstas	} else {
396226031Sstas		res = generate_nt_response(data->auth_challenge,
397226031Sstas					   peer_challenge,
398226031Sstas					   username, username_len,
399226031Sstas					   sm->user->password,
400226031Sstas					   sm->user->password_len,
401226031Sstas					   expected);
402226031Sstas	}
403226031Sstas	if (res) {
404226031Sstas		data->state = FAILURE;
405226031Sstas		return;
406226031Sstas	}
407226031Sstas
408226031Sstas	if (os_memcmp_const(nt_response, expected, 24) == 0) {
409226031Sstas		const u8 *pw_hash;
410226031Sstas		u8 pw_hash_buf[16], pw_hash_hash[16];
411226031Sstas
412226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
413226031Sstas		data->state = SUCCESS_REQ;
414226031Sstas
415226031Sstas		/* Authenticator response is not really needed yet, but
416226031Sstas		 * calculate it here so that peer_challenge and username need
417226031Sstas		 * not be saved. */
418226031Sstas		if (sm->user->password_hash) {
419226031Sstas			pw_hash = sm->user->password;
420226031Sstas		} else {
421226031Sstas			if (nt_password_hash(sm->user->password,
422226031Sstas					     sm->user->password_len,
423226031Sstas					     pw_hash_buf) < 0) {
424226031Sstas				data->state = FAILURE;
425226031Sstas				return;
426226031Sstas			}
427226031Sstas			pw_hash = pw_hash_buf;
428226031Sstas		}
429226031Sstas		if (generate_authenticator_response_pwhash(
430226031Sstas			    pw_hash, peer_challenge, data->auth_challenge,
431226031Sstas			    username, username_len, nt_response,
432226031Sstas			    data->auth_response) < 0 ||
433226031Sstas		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
434226031Sstas		    get_master_key(pw_hash_hash, nt_response,
435226031Sstas				   data->master_key)) {
436226031Sstas			data->state = FAILURE;
437226031Sstas			return;
438226031Sstas		}
439226031Sstas		data->master_key_valid = 1;
440226031Sstas		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
441226031Sstas				data->master_key, MSCHAPV2_KEY_LEN);
442226031Sstas	} else {
443226031Sstas		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
444226031Sstas			    expected, 24);
445226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
446226031Sstas		data->state = FAILURE_REQ;
447226031Sstas	}
448226031Sstas}
449226031Sstas
450226031Sstas
451226031Sstasstatic void eap_mschapv2_process_success_resp(struct eap_sm *sm,
452226031Sstas					      struct eap_mschapv2_data *data,
453226031Sstas					      struct wpabuf *respData)
454226031Sstas{
455226031Sstas	struct eap_mschapv2_hdr *resp;
456226031Sstas	const u8 *pos;
457226031Sstas	size_t len;
458226031Sstas
459226031Sstas	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
460226031Sstas			       &len);
461226031Sstas	if (pos == NULL || len < 1)
462226031Sstas		return; /* Should not happen - frame already validated */
463226031Sstas
464226031Sstas	resp = (struct eap_mschapv2_hdr *) pos;
465226031Sstas
466226031Sstas	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
467226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
468226031Sstas			   " - authentication completed successfully");
469226031Sstas		data->state = SUCCESS;
470226031Sstas	} else {
471226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
472226031Sstas			   "Response - peer rejected authentication");
473226031Sstas		data->state = FAILURE;
474226031Sstas	}
475226031Sstas}
476226031Sstas
477226031Sstas
478226031Sstasstatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
479226031Sstas					      struct eap_mschapv2_data *data,
480226031Sstas					      struct wpabuf *respData)
481226031Sstas{
482226031Sstas	struct eap_mschapv2_hdr *resp;
483226031Sstas	const u8 *pos;
484226031Sstas	size_t len;
485226031Sstas
486226031Sstas	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
487226031Sstas			       &len);
488226031Sstas	if (pos == NULL || len < 1)
489226031Sstas		return; /* Should not happen - frame already validated */
490226031Sstas
491226031Sstas	resp = (struct eap_mschapv2_hdr *) pos;
492226031Sstas
493226031Sstas	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
494226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
495226031Sstas			   " - authentication failed");
496226031Sstas	} else {
497226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
498226031Sstas			   "Response - authentication failed");
499226031Sstas	}
500226031Sstas
501226031Sstas	data->state = FAILURE;
502226031Sstas}
503226031Sstas
504226031Sstas
505226031Sstasstatic void eap_mschapv2_process(struct eap_sm *sm, void *priv,
506226031Sstas				 struct wpabuf *respData)
507226031Sstas{
508226031Sstas	struct eap_mschapv2_data *data = priv;
509226031Sstas
510226031Sstas	if (sm->user == NULL || sm->user->password == NULL) {
511226031Sstas		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
512226031Sstas		data->state = FAILURE;
513226031Sstas		return;
514226031Sstas	}
515226031Sstas
516226031Sstas	switch (data->state) {
517226031Sstas	case CHALLENGE:
518226031Sstas		eap_mschapv2_process_response(sm, data, respData);
519226031Sstas		break;
520226031Sstas	case SUCCESS_REQ:
521226031Sstas		eap_mschapv2_process_success_resp(sm, data, respData);
522226031Sstas		break;
523226031Sstas	case FAILURE_REQ:
524226031Sstas		eap_mschapv2_process_failure_resp(sm, data, respData);
525226031Sstas		break;
526226031Sstas	default:
527226031Sstas		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
528226031Sstas			   "process", data->state);
529226031Sstas		break;
530226031Sstas	}
531226031Sstas}
532226031Sstas
533226031Sstas
534226031Sstasstatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
535226031Sstas{
536226031Sstas	struct eap_mschapv2_data *data = priv;
537226031Sstas	return data->state == SUCCESS || data->state == FAILURE;
538226031Sstas}
539226031Sstas
540226031Sstas
541226031Sstasstatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
542226031Sstas{
543226031Sstas	struct eap_mschapv2_data *data = priv;
544226031Sstas	u8 *key;
545226031Sstas
546226031Sstas	if (data->state != SUCCESS || !data->master_key_valid)
547226031Sstas		return NULL;
548226031Sstas
549226031Sstas	*len = 2 * MSCHAPV2_KEY_LEN;
550226031Sstas	key = os_malloc(*len);
551226031Sstas	if (key == NULL)
552226031Sstas		return NULL;
553226031Sstas	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554226031Sstas	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
555226031Sstas				    1) < 0 ||
556226031Sstas	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557226031Sstas				    MSCHAPV2_KEY_LEN, 1, 1) < 0) {
558226031Sstas		os_free(key);
559226031Sstas		return NULL;
560226031Sstas	}
561226031Sstas	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
562226031Sstas
563226031Sstas	return key;
564226031Sstas}
565226031Sstas
566226031Sstas
567226031Sstasstatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
568226031Sstas{
569226031Sstas	struct eap_mschapv2_data *data = priv;
570226031Sstas	return data->state == SUCCESS;
571226031Sstas}
572226031Sstas
573226031Sstas
574226031Sstasint eap_server_mschapv2_register(void)
575226031Sstas{
576226031Sstas	struct eap_method *eap;
577226031Sstas
578226031Sstas	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
579226031Sstas				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
580226031Sstas				      "MSCHAPV2");
581226031Sstas	if (eap == NULL)
582226031Sstas		return -1;
583226031Sstas
584226031Sstas	eap->init = eap_mschapv2_init;
585226031Sstas	eap->reset = eap_mschapv2_reset;
586226031Sstas	eap->buildReq = eap_mschapv2_buildReq;
587226031Sstas	eap->check = eap_mschapv2_check;
588226031Sstas	eap->process = eap_mschapv2_process;
589226031Sstas	eap->isDone = eap_mschapv2_isDone;
590226031Sstas	eap->getKey = eap_mschapv2_getKey;
591226031Sstas	eap->isSuccess = eap_mschapv2_isSuccess;
592226031Sstas
593226031Sstas	return eap_server_method_register(eap);
594226031Sstas}
595226031Sstas