xref: /freebsd/contrib/wpa/src/eap_server/eap_server_mschapv2.c (revision 3078531de10dcae44b253a35125c949ff4235284)
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 
17 struct 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 
46 struct 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 
58 static 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 
86 static 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 
97 static 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->cfg->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->cfg->server_id, sm->cfg->server_id_len);
135 
136 	return req;
137 }
138 
139 
140 static 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 
184 static 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 
217 static 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 
238 static bool 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 
280 static 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 
451 static 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 
478 static 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 
505 static 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 
534 static bool 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 
541 static 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 	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
555 				    1) < 0 ||
556 	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
557 				    MSCHAPV2_KEY_LEN, 1, 1) < 0) {
558 		os_free(key);
559 		return NULL;
560 	}
561 	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
562 
563 	return key;
564 }
565 
566 
567 static bool eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
568 {
569 	struct eap_mschapv2_data *data = priv;
570 	return data->state == SUCCESS;
571 }
572 
573 
574 int eap_server_mschapv2_register(void)
575 {
576 	struct eap_method *eap;
577 
578 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
579 				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
580 				      "MSCHAPV2");
581 	if (eap == NULL)
582 		return -1;
583 
584 	eap->init = eap_mschapv2_init;
585 	eap->reset = eap_mschapv2_reset;
586 	eap->buildReq = eap_mschapv2_buildReq;
587 	eap->check = eap_mschapv2_check;
588 	eap->process = eap_mschapv2_process;
589 	eap->isDone = eap_mschapv2_isDone;
590 	eap->getKey = eap_mschapv2_getKey;
591 	eap->isSuccess = eap_mschapv2_isSuccess;
592 
593 	return eap_server_method_register(eap);
594 }
595