eap_server_mschapv2.c revision 252726
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);
94214501Srpaulo	os_free(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	char *name = "hostapd"; /* TODO: make this configurable */
104214501Srpaulo	size_t ms_len;
105214501Srpaulo
106214501Srpaulo	if (!data->auth_challenge_from_tls &&
107252726Srpaulo	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
108214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
109214501Srpaulo			   "data");
110214501Srpaulo		data->state = FAILURE;
111214501Srpaulo		return NULL;
112214501Srpaulo	}
113214501Srpaulo
114214501Srpaulo	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
115214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
116214501Srpaulo			    EAP_CODE_REQUEST, id);
117214501Srpaulo	if (req == NULL) {
118214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
119214501Srpaulo			   " for request");
120214501Srpaulo		data->state = FAILURE;
121214501Srpaulo		return NULL;
122214501Srpaulo	}
123214501Srpaulo
124214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
125214501Srpaulo	ms->op_code = MSCHAPV2_OP_CHALLENGE;
126214501Srpaulo	ms->mschapv2_id = id;
127214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
128214501Srpaulo
129214501Srpaulo	wpabuf_put_u8(req, CHALLENGE_LEN);
130214501Srpaulo	if (!data->auth_challenge_from_tls)
131214501Srpaulo		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
132214501Srpaulo	else
133214501Srpaulo		wpabuf_put(req, CHALLENGE_LEN);
134214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
135214501Srpaulo		    data->auth_challenge, CHALLENGE_LEN);
136214501Srpaulo	wpabuf_put_data(req, name, os_strlen(name));
137214501Srpaulo
138214501Srpaulo	return req;
139214501Srpaulo}
140214501Srpaulo
141214501Srpaulo
142214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req(
143214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
144214501Srpaulo{
145214501Srpaulo	struct wpabuf *req;
146214501Srpaulo	struct eap_mschapv2_hdr *ms;
147214501Srpaulo	u8 *msg;
148214501Srpaulo	char *message = "OK";
149214501Srpaulo	size_t ms_len;
150214501Srpaulo
151214501Srpaulo	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
152214501Srpaulo		os_strlen(message);
153214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
154214501Srpaulo			    EAP_CODE_REQUEST, id);
155214501Srpaulo	if (req == NULL) {
156214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
157214501Srpaulo			   " for request");
158214501Srpaulo		data->state = FAILURE;
159214501Srpaulo		return NULL;
160214501Srpaulo	}
161214501Srpaulo
162214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
163214501Srpaulo	ms->op_code = MSCHAPV2_OP_SUCCESS;
164214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
165214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
166214501Srpaulo	msg = (u8 *) (ms + 1);
167214501Srpaulo
168214501Srpaulo	wpabuf_put_u8(req, 'S');
169214501Srpaulo	wpabuf_put_u8(req, '=');
170214501Srpaulo	wpa_snprintf_hex_uppercase(
171214501Srpaulo		wpabuf_put(req, sizeof(data->auth_response) * 2),
172214501Srpaulo		sizeof(data->auth_response) * 2 + 1,
173214501Srpaulo		data->auth_response, sizeof(data->auth_response));
174214501Srpaulo	wpabuf_put_u8(req, ' ');
175214501Srpaulo	wpabuf_put_u8(req, 'M');
176214501Srpaulo	wpabuf_put_u8(req, '=');
177214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
178214501Srpaulo
179214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
180214501Srpaulo			  msg, ms_len - sizeof(*ms));
181214501Srpaulo
182214501Srpaulo	return req;
183214501Srpaulo}
184214501Srpaulo
185214501Srpaulo
186214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req(
187214501Srpaulo	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
188214501Srpaulo{
189214501Srpaulo	struct wpabuf *req;
190214501Srpaulo	struct eap_mschapv2_hdr *ms;
191214501Srpaulo	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
192214501Srpaulo		"M=FAILED";
193214501Srpaulo	size_t ms_len;
194214501Srpaulo
195214501Srpaulo	ms_len = sizeof(*ms) + os_strlen(message);
196214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
197214501Srpaulo			    EAP_CODE_REQUEST, id);
198214501Srpaulo	if (req == NULL) {
199214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
200214501Srpaulo			   " for request");
201214501Srpaulo		data->state = FAILURE;
202214501Srpaulo		return NULL;
203214501Srpaulo	}
204214501Srpaulo
205214501Srpaulo	ms = wpabuf_put(req, sizeof(*ms));
206214501Srpaulo	ms->op_code = MSCHAPV2_OP_FAILURE;
207214501Srpaulo	ms->mschapv2_id = data->resp_mschapv2_id;
208214501Srpaulo	WPA_PUT_BE16(ms->ms_length, ms_len);
209214501Srpaulo
210214501Srpaulo	wpabuf_put_data(req, message, os_strlen(message));
211214501Srpaulo
212214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
213214501Srpaulo			  (u8 *) message, os_strlen(message));
214214501Srpaulo
215214501Srpaulo	return req;
216214501Srpaulo}
217214501Srpaulo
218214501Srpaulo
219214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
220214501Srpaulo					     u8 id)
221214501Srpaulo{
222214501Srpaulo	struct eap_mschapv2_data *data = priv;
223214501Srpaulo
224214501Srpaulo	switch (data->state) {
225214501Srpaulo	case CHALLENGE:
226214501Srpaulo		return eap_mschapv2_build_challenge(sm, data, id);
227214501Srpaulo	case SUCCESS_REQ:
228214501Srpaulo		return eap_mschapv2_build_success_req(sm, data, id);
229214501Srpaulo	case FAILURE_REQ:
230214501Srpaulo		return eap_mschapv2_build_failure_req(sm, data, id);
231214501Srpaulo	default:
232214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
233214501Srpaulo			   "buildReq", data->state);
234214501Srpaulo		break;
235214501Srpaulo	}
236214501Srpaulo	return NULL;
237214501Srpaulo}
238214501Srpaulo
239214501Srpaulo
240214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
241214501Srpaulo				  struct wpabuf *respData)
242214501Srpaulo{
243214501Srpaulo	struct eap_mschapv2_data *data = priv;
244214501Srpaulo	struct eap_mschapv2_hdr *resp;
245214501Srpaulo	const u8 *pos;
246214501Srpaulo	size_t len;
247214501Srpaulo
248214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
249214501Srpaulo			       &len);
250214501Srpaulo	if (pos == NULL || len < 1) {
251214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
252214501Srpaulo		return TRUE;
253214501Srpaulo	}
254214501Srpaulo
255214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
256214501Srpaulo	if (data->state == CHALLENGE &&
257214501Srpaulo	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
258214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
259214501Srpaulo			   "ignore op %d", resp->op_code);
260214501Srpaulo		return TRUE;
261214501Srpaulo	}
262214501Srpaulo
263214501Srpaulo	if (data->state == SUCCESS_REQ &&
264214501Srpaulo	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
265214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
266214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
267214501Srpaulo			   "Failure - ignore op %d", resp->op_code);
268214501Srpaulo		return TRUE;
269214501Srpaulo	}
270214501Srpaulo
271214501Srpaulo	if (data->state == FAILURE_REQ &&
272214501Srpaulo	    resp->op_code != MSCHAPV2_OP_FAILURE) {
273214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
274214501Srpaulo			   "- ignore op %d", resp->op_code);
275214501Srpaulo		return TRUE;
276214501Srpaulo	}
277214501Srpaulo
278214501Srpaulo	return FALSE;
279214501Srpaulo}
280214501Srpaulo
281214501Srpaulo
282214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm,
283214501Srpaulo					  struct eap_mschapv2_data *data,
284214501Srpaulo					  struct wpabuf *respData)
285214501Srpaulo{
286214501Srpaulo	struct eap_mschapv2_hdr *resp;
287214501Srpaulo	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
288214501Srpaulo	u8 flags;
289214501Srpaulo	size_t len, name_len, i;
290214501Srpaulo	u8 expected[24];
291214501Srpaulo	const u8 *username, *user;
292214501Srpaulo	size_t username_len, user_len;
293214501Srpaulo	int res;
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
333214501Srpaulo	/* MSCHAPv2 does not include optional domain name in the
334214501Srpaulo	 * challenge-response calculation, so remove domain prefix
335214501Srpaulo	 * (if present). */
336214501Srpaulo	username = sm->identity;
337214501Srpaulo	username_len = sm->identity_len;
338214501Srpaulo	for (i = 0; i < username_len; i++) {
339214501Srpaulo		if (username[i] == '\\') {
340214501Srpaulo			username_len -= i + 1;
341214501Srpaulo			username += i + 1;
342214501Srpaulo			break;
343214501Srpaulo		}
344214501Srpaulo	}
345214501Srpaulo
346214501Srpaulo	user = name;
347214501Srpaulo	user_len = name_len;
348214501Srpaulo	for (i = 0; i < user_len; i++) {
349214501Srpaulo		if (user[i] == '\\') {
350214501Srpaulo			user_len -= i + 1;
351214501Srpaulo			user += i + 1;
352214501Srpaulo			break;
353214501Srpaulo		}
354214501Srpaulo	}
355214501Srpaulo
356214501Srpaulo	if (username_len != user_len ||
357214501Srpaulo	    os_memcmp(username, user, username_len) != 0) {
358214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
359214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
360214501Srpaulo				  "name", username, username_len);
361214501Srpaulo		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
362214501Srpaulo				  "name", user, user_len);
363214501Srpaulo		data->state = FAILURE;
364214501Srpaulo		return;
365214501Srpaulo	}
366214501Srpaulo
367214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
368214501Srpaulo			  username, username_len);
369214501Srpaulo
370214501Srpaulo	if (sm->user->password_hash) {
371214501Srpaulo		res = generate_nt_response_pwhash(data->auth_challenge,
372214501Srpaulo						  peer_challenge,
373214501Srpaulo						  username, username_len,
374214501Srpaulo						  sm->user->password,
375214501Srpaulo						  expected);
376214501Srpaulo	} else {
377214501Srpaulo		res = generate_nt_response(data->auth_challenge,
378214501Srpaulo					   peer_challenge,
379214501Srpaulo					   username, username_len,
380214501Srpaulo					   sm->user->password,
381214501Srpaulo					   sm->user->password_len,
382214501Srpaulo					   expected);
383214501Srpaulo	}
384214501Srpaulo	if (res) {
385214501Srpaulo		data->state = FAILURE;
386214501Srpaulo		return;
387214501Srpaulo	}
388214501Srpaulo
389214501Srpaulo	if (os_memcmp(nt_response, expected, 24) == 0) {
390214501Srpaulo		const u8 *pw_hash;
391214501Srpaulo		u8 pw_hash_buf[16], pw_hash_hash[16];
392214501Srpaulo
393214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
394214501Srpaulo		data->state = SUCCESS_REQ;
395214501Srpaulo
396214501Srpaulo		/* Authenticator response is not really needed yet, but
397214501Srpaulo		 * calculate it here so that peer_challenge and username need
398214501Srpaulo		 * not be saved. */
399214501Srpaulo		if (sm->user->password_hash) {
400214501Srpaulo			pw_hash = sm->user->password;
401214501Srpaulo		} else {
402252726Srpaulo			if (nt_password_hash(sm->user->password,
403252726Srpaulo					     sm->user->password_len,
404252726Srpaulo					     pw_hash_buf) < 0) {
405252726Srpaulo				data->state = FAILURE;
406252726Srpaulo				return;
407252726Srpaulo			}
408214501Srpaulo			pw_hash = pw_hash_buf;
409214501Srpaulo		}
410214501Srpaulo		generate_authenticator_response_pwhash(
411214501Srpaulo			pw_hash, peer_challenge, data->auth_challenge,
412214501Srpaulo			username, username_len, nt_response,
413214501Srpaulo			data->auth_response);
414214501Srpaulo
415214501Srpaulo		hash_nt_password_hash(pw_hash, pw_hash_hash);
416214501Srpaulo		get_master_key(pw_hash_hash, nt_response, data->master_key);
417214501Srpaulo		data->master_key_valid = 1;
418214501Srpaulo		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
419214501Srpaulo				data->master_key, MSCHAPV2_KEY_LEN);
420214501Srpaulo	} else {
421214501Srpaulo		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
422214501Srpaulo			    expected, 24);
423214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
424214501Srpaulo		data->state = FAILURE_REQ;
425214501Srpaulo	}
426214501Srpaulo}
427214501Srpaulo
428214501Srpaulo
429214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm,
430214501Srpaulo					      struct eap_mschapv2_data *data,
431214501Srpaulo					      struct wpabuf *respData)
432214501Srpaulo{
433214501Srpaulo	struct eap_mschapv2_hdr *resp;
434214501Srpaulo	const u8 *pos;
435214501Srpaulo	size_t len;
436214501Srpaulo
437214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
438214501Srpaulo			       &len);
439214501Srpaulo	if (pos == NULL || len < 1)
440214501Srpaulo		return; /* Should not happen - frame already validated */
441214501Srpaulo
442214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
443214501Srpaulo
444214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
445214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
446214501Srpaulo			   " - authentication completed successfully");
447214501Srpaulo		data->state = SUCCESS;
448214501Srpaulo	} else {
449214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
450214501Srpaulo			   "Response - peer rejected authentication");
451214501Srpaulo		data->state = FAILURE;
452214501Srpaulo	}
453214501Srpaulo}
454214501Srpaulo
455214501Srpaulo
456214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
457214501Srpaulo					      struct eap_mschapv2_data *data,
458214501Srpaulo					      struct wpabuf *respData)
459214501Srpaulo{
460214501Srpaulo	struct eap_mschapv2_hdr *resp;
461214501Srpaulo	const u8 *pos;
462214501Srpaulo	size_t len;
463214501Srpaulo
464214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
465214501Srpaulo			       &len);
466214501Srpaulo	if (pos == NULL || len < 1)
467214501Srpaulo		return; /* Should not happen - frame already validated */
468214501Srpaulo
469214501Srpaulo	resp = (struct eap_mschapv2_hdr *) pos;
470214501Srpaulo
471214501Srpaulo	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
472214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
473214501Srpaulo			   " - authentication failed");
474214501Srpaulo	} else {
475214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
476214501Srpaulo			   "Response - authentication failed");
477214501Srpaulo	}
478214501Srpaulo
479214501Srpaulo	data->state = FAILURE;
480214501Srpaulo}
481214501Srpaulo
482214501Srpaulo
483214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv,
484214501Srpaulo				 struct wpabuf *respData)
485214501Srpaulo{
486214501Srpaulo	struct eap_mschapv2_data *data = priv;
487214501Srpaulo
488214501Srpaulo	if (sm->user == NULL || sm->user->password == NULL) {
489214501Srpaulo		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
490214501Srpaulo		data->state = FAILURE;
491214501Srpaulo		return;
492214501Srpaulo	}
493214501Srpaulo
494214501Srpaulo	switch (data->state) {
495214501Srpaulo	case CHALLENGE:
496214501Srpaulo		eap_mschapv2_process_response(sm, data, respData);
497214501Srpaulo		break;
498214501Srpaulo	case SUCCESS_REQ:
499214501Srpaulo		eap_mschapv2_process_success_resp(sm, data, respData);
500214501Srpaulo		break;
501214501Srpaulo	case FAILURE_REQ:
502214501Srpaulo		eap_mschapv2_process_failure_resp(sm, data, respData);
503214501Srpaulo		break;
504214501Srpaulo	default:
505214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
506214501Srpaulo			   "process", data->state);
507214501Srpaulo		break;
508214501Srpaulo	}
509214501Srpaulo}
510214501Srpaulo
511214501Srpaulo
512214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
513214501Srpaulo{
514214501Srpaulo	struct eap_mschapv2_data *data = priv;
515214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
516214501Srpaulo}
517214501Srpaulo
518214501Srpaulo
519214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
520214501Srpaulo{
521214501Srpaulo	struct eap_mschapv2_data *data = priv;
522214501Srpaulo	u8 *key;
523214501Srpaulo
524214501Srpaulo	if (data->state != SUCCESS || !data->master_key_valid)
525214501Srpaulo		return NULL;
526214501Srpaulo
527214501Srpaulo	*len = 2 * MSCHAPV2_KEY_LEN;
528214501Srpaulo	key = os_malloc(*len);
529214501Srpaulo	if (key == NULL)
530214501Srpaulo		return NULL;
531214501Srpaulo	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
532214501Srpaulo	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
533214501Srpaulo	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
534214501Srpaulo				MSCHAPV2_KEY_LEN, 1, 1);
535214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
536214501Srpaulo
537214501Srpaulo	return key;
538214501Srpaulo}
539214501Srpaulo
540214501Srpaulo
541214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
542214501Srpaulo{
543214501Srpaulo	struct eap_mschapv2_data *data = priv;
544214501Srpaulo	return data->state == SUCCESS;
545214501Srpaulo}
546214501Srpaulo
547214501Srpaulo
548214501Srpauloint eap_server_mschapv2_register(void)
549214501Srpaulo{
550214501Srpaulo	struct eap_method *eap;
551214501Srpaulo	int ret;
552214501Srpaulo
553214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
554214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
555214501Srpaulo				      "MSCHAPV2");
556214501Srpaulo	if (eap == NULL)
557214501Srpaulo		return -1;
558214501Srpaulo
559214501Srpaulo	eap->init = eap_mschapv2_init;
560214501Srpaulo	eap->reset = eap_mschapv2_reset;
561214501Srpaulo	eap->buildReq = eap_mschapv2_buildReq;
562214501Srpaulo	eap->check = eap_mschapv2_check;
563214501Srpaulo	eap->process = eap_mschapv2_process;
564214501Srpaulo	eap->isDone = eap_mschapv2_isDone;
565214501Srpaulo	eap->getKey = eap_mschapv2_getKey;
566214501Srpaulo	eap->isSuccess = eap_mschapv2_isSuccess;
567214501Srpaulo
568214501Srpaulo	ret = eap_server_method_register(eap);
569214501Srpaulo	if (ret)
570214501Srpaulo		eap_server_method_free(eap);
571214501Srpaulo	return ret;
572214501Srpaulo}
573