Home | History | Annotate | Download | only in eap_server
      1 /*
      2  * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
      3  * Copyright (c) 2004-2007, Jouni Malinen <j (at) 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_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 
     87 static 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 
     98 static 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 
    141 static 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 
    185 static 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 
    218 static 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 
    239 static 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 
    281 static 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 
    436 static 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 
    463 static 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 
    490 static 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 
    519 static 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 
    526 static 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 
    548 static 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 
    555 int 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