xref: /netbsd-src/external/bsd/wpa/dist/src/eap_server/eap_server_mschapv2.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
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 
23 struct 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 
52 struct 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 
64 static 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 
93 static 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 
104 static 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 
148 static 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 
192 static 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 
225 static 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 
246 static 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 
288 static 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 			nt_password_hash(sm->user->password,
409 					 sm->user->password_len,
410 					 pw_hash_buf);
411 			pw_hash = pw_hash_buf;
412 		}
413 		generate_authenticator_response_pwhash(
414 			pw_hash, peer_challenge, data->auth_challenge,
415 			username, username_len, nt_response,
416 			data->auth_response);
417 
418 		hash_nt_password_hash(pw_hash, pw_hash_hash);
419 		get_master_key(pw_hash_hash, nt_response, data->master_key);
420 		data->master_key_valid = 1;
421 		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
422 				data->master_key, MSCHAPV2_KEY_LEN);
423 	} else {
424 		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
425 			    expected, 24);
426 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
427 		data->state = FAILURE_REQ;
428 	}
429 }
430 
431 
432 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
433 					      struct eap_mschapv2_data *data,
434 					      struct wpabuf *respData)
435 {
436 	struct eap_mschapv2_hdr *resp;
437 	const u8 *pos;
438 	size_t len;
439 
440 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
441 			       &len);
442 	if (pos == NULL || len < 1)
443 		return; /* Should not happen - frame already validated */
444 
445 	resp = (struct eap_mschapv2_hdr *) pos;
446 
447 	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
448 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
449 			   " - authentication completed successfully");
450 		data->state = SUCCESS;
451 	} else {
452 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
453 			   "Response - peer rejected authentication");
454 		data->state = FAILURE;
455 	}
456 }
457 
458 
459 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
460 					      struct eap_mschapv2_data *data,
461 					      struct wpabuf *respData)
462 {
463 	struct eap_mschapv2_hdr *resp;
464 	const u8 *pos;
465 	size_t len;
466 
467 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
468 			       &len);
469 	if (pos == NULL || len < 1)
470 		return; /* Should not happen - frame already validated */
471 
472 	resp = (struct eap_mschapv2_hdr *) pos;
473 
474 	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
475 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
476 			   " - authentication failed");
477 	} else {
478 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
479 			   "Response - authentication failed");
480 	}
481 
482 	data->state = FAILURE;
483 }
484 
485 
486 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
487 				 struct wpabuf *respData)
488 {
489 	struct eap_mschapv2_data *data = priv;
490 
491 	if (sm->user == NULL || sm->user->password == NULL) {
492 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
493 		data->state = FAILURE;
494 		return;
495 	}
496 
497 	switch (data->state) {
498 	case CHALLENGE:
499 		eap_mschapv2_process_response(sm, data, respData);
500 		break;
501 	case SUCCESS_REQ:
502 		eap_mschapv2_process_success_resp(sm, data, respData);
503 		break;
504 	case FAILURE_REQ:
505 		eap_mschapv2_process_failure_resp(sm, data, respData);
506 		break;
507 	default:
508 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
509 			   "process", data->state);
510 		break;
511 	}
512 }
513 
514 
515 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
516 {
517 	struct eap_mschapv2_data *data = priv;
518 	return data->state == SUCCESS || data->state == FAILURE;
519 }
520 
521 
522 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
523 {
524 	struct eap_mschapv2_data *data = priv;
525 	u8 *key;
526 
527 	if (data->state != SUCCESS || !data->master_key_valid)
528 		return NULL;
529 
530 	*len = 2 * MSCHAPV2_KEY_LEN;
531 	key = os_malloc(*len);
532 	if (key == NULL)
533 		return NULL;
534 	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
535 	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
536 	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
537 				MSCHAPV2_KEY_LEN, 1, 1);
538 	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
539 
540 	return key;
541 }
542 
543 
544 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
545 {
546 	struct eap_mschapv2_data *data = priv;
547 	return data->state == SUCCESS;
548 }
549 
550 
551 int eap_server_mschapv2_register(void)
552 {
553 	struct eap_method *eap;
554 	int ret;
555 
556 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
557 				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
558 				      "MSCHAPV2");
559 	if (eap == NULL)
560 		return -1;
561 
562 	eap->init = eap_mschapv2_init;
563 	eap->reset = eap_mschapv2_reset;
564 	eap->buildReq = eap_mschapv2_buildReq;
565 	eap->check = eap_mschapv2_check;
566 	eap->process = eap_mschapv2_process;
567 	eap->isDone = eap_mschapv2_isDone;
568 	eap->getKey = eap_mschapv2_getKey;
569 	eap->isSuccess = eap_mschapv2_isSuccess;
570 
571 	ret = eap_server_method_register(eap);
572 	if (ret)
573 		eap_server_method_free(eap);
574 	return ret;
575 }
576