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