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