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 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