1/*
2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
3 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "crypto/ms_funcs.h"
13#include "crypto/random.h"
14#include "eap_i.h"
15
16
17struct eap_mschapv2_hdr {
18	u8 op_code; /* MSCHAPV2_OP_* */
19	u8 mschapv2_id; /* must be changed for challenges, but not for
20			 * success/failure */
21	u8 ms_length[2]; /* Note: misaligned; length - 5 */
22	/* followed by data */
23} STRUCT_PACKED;
24
25#define MSCHAPV2_OP_CHALLENGE 1
26#define MSCHAPV2_OP_RESPONSE 2
27#define MSCHAPV2_OP_SUCCESS 3
28#define MSCHAPV2_OP_FAILURE 4
29#define MSCHAPV2_OP_CHANGE_PASSWORD 7
30
31#define MSCHAPV2_RESP_LEN 49
32
33#define ERROR_RESTRICTED_LOGON_HOURS 646
34#define ERROR_ACCT_DISABLED 647
35#define ERROR_PASSWD_EXPIRED 648
36#define ERROR_NO_DIALIN_PERMISSION 649
37#define ERROR_AUTHENTICATION_FAILURE 691
38#define ERROR_CHANGING_PASSWORD 709
39
40#define PASSWD_CHANGE_CHAL_LEN 16
41#define MSCHAPV2_KEY_LEN 16
42
43
44#define CHALLENGE_LEN 16
45
46struct eap_mschapv2_data {
47	u8 auth_challenge[CHALLENGE_LEN];
48	int auth_challenge_from_tls;
49	u8 *peer_challenge;
50	u8 auth_response[20];
51	enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
52	u8 resp_mschapv2_id;
53	u8 master_key[16];
54	int master_key_valid;
55};
56
57
58static void * eap_mschapv2_init(struct eap_sm *sm)
59{
60	struct eap_mschapv2_data *data;
61
62	data = os_zalloc(sizeof(*data));
63	if (data == NULL)
64		return NULL;
65	data->state = CHALLENGE;
66
67	if (sm->auth_challenge) {
68		os_memcpy(data->auth_challenge, sm->auth_challenge,
69			  CHALLENGE_LEN);
70		data->auth_challenge_from_tls = 1;
71	}
72
73	if (sm->peer_challenge) {
74		data->peer_challenge = os_memdup(sm->peer_challenge,
75						 CHALLENGE_LEN);
76		if (data->peer_challenge == NULL) {
77			os_free(data);
78			return NULL;
79		}
80	}
81
82	return data;
83}
84
85
86static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
87{
88	struct eap_mschapv2_data *data = priv;
89	if (data == NULL)
90		return;
91
92	os_free(data->peer_challenge);
93	bin_clear_free(data, sizeof(*data));
94}
95
96
97static struct wpabuf * eap_mschapv2_build_challenge(
98	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
99{
100	struct wpabuf *req;
101	struct eap_mschapv2_hdr *ms;
102	size_t ms_len;
103
104	if (!data->auth_challenge_from_tls &&
105	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
106		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
107			   "data");
108		data->state = FAILURE;
109		return NULL;
110	}
111
112	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
113	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
114			    EAP_CODE_REQUEST, id);
115	if (req == NULL) {
116		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
117			   " for request");
118		data->state = FAILURE;
119		return NULL;
120	}
121
122	ms = wpabuf_put(req, sizeof(*ms));
123	ms->op_code = MSCHAPV2_OP_CHALLENGE;
124	ms->mschapv2_id = id;
125	WPA_PUT_BE16(ms->ms_length, ms_len);
126
127	wpabuf_put_u8(req, CHALLENGE_LEN);
128	if (!data->auth_challenge_from_tls)
129		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
130	else
131		wpabuf_put(req, CHALLENGE_LEN);
132	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
133		    data->auth_challenge, CHALLENGE_LEN);
134	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
135
136	return req;
137}
138
139
140static struct wpabuf * eap_mschapv2_build_success_req(
141	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
142{
143	struct wpabuf *req;
144	struct eap_mschapv2_hdr *ms;
145	u8 *msg;
146	char *message = "OK";
147	size_t ms_len;
148
149	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
150		os_strlen(message);
151	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
152			    EAP_CODE_REQUEST, id);
153	if (req == NULL) {
154		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
155			   " for request");
156		data->state = FAILURE;
157		return NULL;
158	}
159
160	ms = wpabuf_put(req, sizeof(*ms));
161	ms->op_code = MSCHAPV2_OP_SUCCESS;
162	ms->mschapv2_id = data->resp_mschapv2_id;
163	WPA_PUT_BE16(ms->ms_length, ms_len);
164	msg = (u8 *) (ms + 1);
165
166	wpabuf_put_u8(req, 'S');
167	wpabuf_put_u8(req, '=');
168	wpa_snprintf_hex_uppercase(
169		wpabuf_put(req, sizeof(data->auth_response) * 2),
170		sizeof(data->auth_response) * 2 + 1,
171		data->auth_response, sizeof(data->auth_response));
172	wpabuf_put_u8(req, ' ');
173	wpabuf_put_u8(req, 'M');
174	wpabuf_put_u8(req, '=');
175	wpabuf_put_data(req, message, os_strlen(message));
176
177	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
178			  msg, ms_len - sizeof(*ms));
179
180	return req;
181}
182
183
184static struct wpabuf * eap_mschapv2_build_failure_req(
185	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
186{
187	struct wpabuf *req;
188	struct eap_mschapv2_hdr *ms;
189	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
190		"M=FAILED";
191	size_t ms_len;
192
193	ms_len = sizeof(*ms) + os_strlen(message);
194	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
195			    EAP_CODE_REQUEST, id);
196	if (req == NULL) {
197		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
198			   " for request");
199		data->state = FAILURE;
200		return NULL;
201	}
202
203	ms = wpabuf_put(req, sizeof(*ms));
204	ms->op_code = MSCHAPV2_OP_FAILURE;
205	ms->mschapv2_id = data->resp_mschapv2_id;
206	WPA_PUT_BE16(ms->ms_length, ms_len);
207
208	wpabuf_put_data(req, message, os_strlen(message));
209
210	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
211			  (u8 *) message, os_strlen(message));
212
213	return req;
214}
215
216
217static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
218					     u8 id)
219{
220	struct eap_mschapv2_data *data = priv;
221
222	switch (data->state) {
223	case CHALLENGE:
224		return eap_mschapv2_build_challenge(sm, data, id);
225	case SUCCESS_REQ:
226		return eap_mschapv2_build_success_req(sm, data, id);
227	case FAILURE_REQ:
228		return eap_mschapv2_build_failure_req(sm, data, id);
229	default:
230		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
231			   "buildReq", data->state);
232		break;
233	}
234	return NULL;
235}
236
237
238static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
239				  struct wpabuf *respData)
240{
241	struct eap_mschapv2_data *data = priv;
242	struct eap_mschapv2_hdr *resp;
243	const u8 *pos;
244	size_t len;
245
246	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
247			       &len);
248	if (pos == NULL || len < 1) {
249		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
250		return TRUE;
251	}
252
253	resp = (struct eap_mschapv2_hdr *) pos;
254	if (data->state == CHALLENGE &&
255	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
256		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
257			   "ignore op %d", resp->op_code);
258		return TRUE;
259	}
260
261	if (data->state == SUCCESS_REQ &&
262	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
263	    resp->op_code != MSCHAPV2_OP_FAILURE) {
264		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
265			   "Failure - ignore op %d", resp->op_code);
266		return TRUE;
267	}
268
269	if (data->state == FAILURE_REQ &&
270	    resp->op_code != MSCHAPV2_OP_FAILURE) {
271		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
272			   "- ignore op %d", resp->op_code);
273		return TRUE;
274	}
275
276	return FALSE;
277}
278
279
280static void eap_mschapv2_process_response(struct eap_sm *sm,
281					  struct eap_mschapv2_data *data,
282					  struct wpabuf *respData)
283{
284	struct eap_mschapv2_hdr *resp;
285	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
286	u8 flags;
287	size_t len, name_len, i;
288	u8 expected[24];
289	const u8 *username, *user;
290	size_t username_len, user_len;
291	int res;
292	char *buf;
293
294	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
295			       &len);
296	if (pos == NULL || len < 1)
297		return; /* Should not happen - frame already validated */
298
299	end = pos + len;
300	resp = (struct eap_mschapv2_hdr *) pos;
301	pos = (u8 *) (resp + 1);
302
303	if (len < sizeof(*resp) + 1 + 49 ||
304	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
305	    pos[0] != 49) {
306		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
307				respData);
308		data->state = FAILURE;
309		return;
310	}
311	data->resp_mschapv2_id = resp->mschapv2_id;
312	pos++;
313	peer_challenge = pos;
314	pos += 16 + 8;
315	nt_response = pos;
316	pos += 24;
317	flags = *pos++;
318	name = pos;
319	name_len = end - name;
320
321	if (data->peer_challenge) {
322		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
323			   "Peer-Challenge");
324		peer_challenge = data->peer_challenge;
325	}
326	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
327		    peer_challenge, 16);
328	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
329	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
330	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
331
332	buf = os_malloc(name_len * 4 + 1);
333	if (buf) {
334		printf_encode(buf, name_len * 4 + 1, name, name_len);
335		eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
336		os_free(buf);
337	}
338
339	/* MSCHAPv2 does not include optional domain name in the
340	 * challenge-response calculation, so remove domain prefix
341	 * (if present). */
342	username = sm->identity;
343	username_len = sm->identity_len;
344	for (i = 0; i < username_len; i++) {
345		if (username[i] == '\\') {
346			username_len -= i + 1;
347			username += i + 1;
348			break;
349		}
350	}
351
352	user = name;
353	user_len = name_len;
354	for (i = 0; i < user_len; i++) {
355		if (user[i] == '\\') {
356			user_len -= i + 1;
357			user += i + 1;
358			break;
359		}
360	}
361
362#ifdef CONFIG_TESTING_OPTIONS
363	{
364		u8 challenge[8];
365
366		if (challenge_hash(peer_challenge, data->auth_challenge,
367				   username, username_len, challenge) == 0) {
368			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
369						      username, username_len,
370						      challenge, nt_response);
371		}
372	}
373#endif /* CONFIG_TESTING_OPTIONS */
374
375	if (username_len != user_len ||
376	    os_memcmp(username, user, username_len) != 0) {
377		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
378		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
379				  "name", username, username_len);
380		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
381				  "name", user, user_len);
382		data->state = FAILURE;
383		return;
384	}
385
386	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
387			  username, username_len);
388
389	if (sm->user->password_hash) {
390		res = generate_nt_response_pwhash(data->auth_challenge,
391						  peer_challenge,
392						  username, username_len,
393						  sm->user->password,
394						  expected);
395	} else {
396		res = generate_nt_response(data->auth_challenge,
397					   peer_challenge,
398					   username, username_len,
399					   sm->user->password,
400					   sm->user->password_len,
401					   expected);
402	}
403	if (res) {
404		data->state = FAILURE;
405		return;
406	}
407
408	if (os_memcmp_const(nt_response, expected, 24) == 0) {
409		const u8 *pw_hash;
410		u8 pw_hash_buf[16], pw_hash_hash[16];
411
412		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
413		data->state = SUCCESS_REQ;
414
415		/* Authenticator response is not really needed yet, but
416		 * calculate it here so that peer_challenge and username need
417		 * not be saved. */
418		if (sm->user->password_hash) {
419			pw_hash = sm->user->password;
420		} else {
421			if (nt_password_hash(sm->user->password,
422					     sm->user->password_len,
423					     pw_hash_buf) < 0) {
424				data->state = FAILURE;
425				return;
426			}
427			pw_hash = pw_hash_buf;
428		}
429		if (generate_authenticator_response_pwhash(
430			    pw_hash, peer_challenge, data->auth_challenge,
431			    username, username_len, nt_response,
432			    data->auth_response) < 0 ||
433		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
434		    get_master_key(pw_hash_hash, nt_response,
435				   data->master_key)) {
436			data->state = FAILURE;
437			return;
438		}
439		data->master_key_valid = 1;
440		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
441				data->master_key, MSCHAPV2_KEY_LEN);
442	} else {
443		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
444			    expected, 24);
445		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
446		data->state = FAILURE_REQ;
447	}
448}
449
450
451static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
452					      struct eap_mschapv2_data *data,
453					      struct wpabuf *respData)
454{
455	struct eap_mschapv2_hdr *resp;
456	const u8 *pos;
457	size_t len;
458
459	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
460			       &len);
461	if (pos == NULL || len < 1)
462		return; /* Should not happen - frame already validated */
463
464	resp = (struct eap_mschapv2_hdr *) pos;
465
466	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
467		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
468			   " - authentication completed successfully");
469		data->state = SUCCESS;
470	} else {
471		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
472			   "Response - peer rejected authentication");
473		data->state = FAILURE;
474	}
475}
476
477
478static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
479					      struct eap_mschapv2_data *data,
480					      struct wpabuf *respData)
481{
482	struct eap_mschapv2_hdr *resp;
483	const u8 *pos;
484	size_t len;
485
486	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
487			       &len);
488	if (pos == NULL || len < 1)
489		return; /* Should not happen - frame already validated */
490
491	resp = (struct eap_mschapv2_hdr *) pos;
492
493	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
494		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
495			   " - authentication failed");
496	} else {
497		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
498			   "Response - authentication failed");
499	}
500
501	data->state = FAILURE;
502}
503
504
505static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
506				 struct wpabuf *respData)
507{
508	struct eap_mschapv2_data *data = priv;
509
510	if (sm->user == NULL || sm->user->password == NULL) {
511		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
512		data->state = FAILURE;
513		return;
514	}
515
516	switch (data->state) {
517	case CHALLENGE:
518		eap_mschapv2_process_response(sm, data, respData);
519		break;
520	case SUCCESS_REQ:
521		eap_mschapv2_process_success_resp(sm, data, respData);
522		break;
523	case FAILURE_REQ:
524		eap_mschapv2_process_failure_resp(sm, data, respData);
525		break;
526	default:
527		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
528			   "process", data->state);
529		break;
530	}
531}
532
533
534static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
535{
536	struct eap_mschapv2_data *data = priv;
537	return data->state == SUCCESS || data->state == FAILURE;
538}
539
540
541static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
542{
543	struct eap_mschapv2_data *data = priv;
544	u8 *key;
545
546	if (data->state != SUCCESS || !data->master_key_valid)
547		return NULL;
548
549	*len = 2 * MSCHAPV2_KEY_LEN;
550	key = os_malloc(*len);
551	if (key == NULL)
552		return NULL;
553	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
554	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
555	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
556				MSCHAPV2_KEY_LEN, 1, 1);
557	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
558
559	return key;
560}
561
562
563static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
564{
565	struct eap_mschapv2_data *data = priv;
566	return data->state == SUCCESS;
567}
568
569
570int eap_server_mschapv2_register(void)
571{
572	struct eap_method *eap;
573
574	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
575				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
576				      "MSCHAPV2");
577	if (eap == NULL)
578		return -1;
579
580	eap->init = eap_mschapv2_init;
581	eap->reset = eap_mschapv2_reset;
582	eap->buildReq = eap_mschapv2_buildReq;
583	eap->check = eap_mschapv2_check;
584	eap->process = eap_mschapv2_process;
585	eap->isDone = eap_mschapv2_isDone;
586	eap->getKey = eap_mschapv2_getKey;
587	eap->isSuccess = eap_mschapv2_isSuccess;
588
589	return eap_server_method_register(eap);
590}
591