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