eap_server_mschapv2.c revision c28170251eb54dbf64a9074a07fee377587425b2
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_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	if (username_len != user_len ||
364	    os_memcmp(username, user, username_len) != 0) {
365		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
366		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
367				  "name", username, username_len);
368		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
369				  "name", user, user_len);
370		data->state = FAILURE;
371		return;
372	}
373
374	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
375			  username, username_len);
376
377	if (sm->user->password_hash) {
378		res = generate_nt_response_pwhash(data->auth_challenge,
379						  peer_challenge,
380						  username, username_len,
381						  sm->user->password,
382						  expected);
383	} else {
384		res = generate_nt_response(data->auth_challenge,
385					   peer_challenge,
386					   username, username_len,
387					   sm->user->password,
388					   sm->user->password_len,
389					   expected);
390	}
391	if (res) {
392		data->state = FAILURE;
393		return;
394	}
395
396	if (os_memcmp_const(nt_response, expected, 24) == 0) {
397		const u8 *pw_hash;
398		u8 pw_hash_buf[16], pw_hash_hash[16];
399
400		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
401		data->state = SUCCESS_REQ;
402
403		/* Authenticator response is not really needed yet, but
404		 * calculate it here so that peer_challenge and username need
405		 * not be saved. */
406		if (sm->user->password_hash) {
407			pw_hash = sm->user->password;
408		} else {
409			if (nt_password_hash(sm->user->password,
410					     sm->user->password_len,
411					     pw_hash_buf) < 0) {
412				data->state = FAILURE;
413				return;
414			}
415			pw_hash = pw_hash_buf;
416		}
417		generate_authenticator_response_pwhash(
418			pw_hash, peer_challenge, data->auth_challenge,
419			username, username_len, nt_response,
420			data->auth_response);
421
422		hash_nt_password_hash(pw_hash, pw_hash_hash);
423		get_master_key(pw_hash_hash, nt_response, data->master_key);
424		data->master_key_valid = 1;
425		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
426				data->master_key, MSCHAPV2_KEY_LEN);
427	} else {
428		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
429			    expected, 24);
430		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
431		data->state = FAILURE_REQ;
432	}
433}
434
435
436static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
437					      struct eap_mschapv2_data *data,
438					      struct wpabuf *respData)
439{
440	struct eap_mschapv2_hdr *resp;
441	const u8 *pos;
442	size_t len;
443
444	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
445			       &len);
446	if (pos == NULL || len < 1)
447		return; /* Should not happen - frame already validated */
448
449	resp = (struct eap_mschapv2_hdr *) pos;
450
451	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
452		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
453			   " - authentication completed successfully");
454		data->state = SUCCESS;
455	} else {
456		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
457			   "Response - peer rejected authentication");
458		data->state = FAILURE;
459	}
460}
461
462
463static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
464					      struct eap_mschapv2_data *data,
465					      struct wpabuf *respData)
466{
467	struct eap_mschapv2_hdr *resp;
468	const u8 *pos;
469	size_t len;
470
471	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
472			       &len);
473	if (pos == NULL || len < 1)
474		return; /* Should not happen - frame already validated */
475
476	resp = (struct eap_mschapv2_hdr *) pos;
477
478	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
479		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
480			   " - authentication failed");
481	} else {
482		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
483			   "Response - authentication failed");
484	}
485
486	data->state = FAILURE;
487}
488
489
490static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
491				 struct wpabuf *respData)
492{
493	struct eap_mschapv2_data *data = priv;
494
495	if (sm->user == NULL || sm->user->password == NULL) {
496		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
497		data->state = FAILURE;
498		return;
499	}
500
501	switch (data->state) {
502	case CHALLENGE:
503		eap_mschapv2_process_response(sm, data, respData);
504		break;
505	case SUCCESS_REQ:
506		eap_mschapv2_process_success_resp(sm, data, respData);
507		break;
508	case FAILURE_REQ:
509		eap_mschapv2_process_failure_resp(sm, data, respData);
510		break;
511	default:
512		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
513			   "process", data->state);
514		break;
515	}
516}
517
518
519static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
520{
521	struct eap_mschapv2_data *data = priv;
522	return data->state == SUCCESS || data->state == FAILURE;
523}
524
525
526static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
527{
528	struct eap_mschapv2_data *data = priv;
529	u8 *key;
530
531	if (data->state != SUCCESS || !data->master_key_valid)
532		return NULL;
533
534	*len = 2 * MSCHAPV2_KEY_LEN;
535	key = os_malloc(*len);
536	if (key == NULL)
537		return NULL;
538	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
539	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
540	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
541				MSCHAPV2_KEY_LEN, 1, 1);
542	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
543
544	return key;
545}
546
547
548static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
549{
550	struct eap_mschapv2_data *data = priv;
551	return data->state == SUCCESS;
552}
553
554
555int eap_server_mschapv2_register(void)
556{
557	struct eap_method *eap;
558	int ret;
559
560	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
561				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
562				      "MSCHAPV2");
563	if (eap == NULL)
564		return -1;
565
566	eap->init = eap_mschapv2_init;
567	eap->reset = eap_mschapv2_reset;
568	eap->buildReq = eap_mschapv2_buildReq;
569	eap->check = eap_mschapv2_check;
570	eap->process = eap_mschapv2_process;
571	eap->isDone = eap_mschapv2_isDone;
572	eap->getKey = eap_mschapv2_getKey;
573	eap->isSuccess = eap_mschapv2_isSuccess;
574
575	ret = eap_server_method_register(eap);
576	if (ret)
577		eap_server_method_free(eap);
578	return ret;
579}
580