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 	char *name = "hostapd"; /* TODO: make this configurable */
    104 	size_t ms_len;
    105 
    106 	if (!data->auth_challenge_from_tls &&
    107 	    random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
    108 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
    109 			   "data");
    110 		data->state = FAILURE;
    111 		return NULL;
    112 	}
    113 
    114 	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
    115 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    116 			    EAP_CODE_REQUEST, id);
    117 	if (req == NULL) {
    118 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    119 			   " for request");
    120 		data->state = FAILURE;
    121 		return NULL;
    122 	}
    123 
    124 	ms = wpabuf_put(req, sizeof(*ms));
    125 	ms->op_code = MSCHAPV2_OP_CHALLENGE;
    126 	ms->mschapv2_id = id;
    127 	WPA_PUT_BE16(ms->ms_length, ms_len);
    128 
    129 	wpabuf_put_u8(req, CHALLENGE_LEN);
    130 	if (!data->auth_challenge_from_tls)
    131 		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
    132 	else
    133 		wpabuf_put(req, CHALLENGE_LEN);
    134 	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
    135 		    data->auth_challenge, CHALLENGE_LEN);
    136 	wpabuf_put_data(req, name, os_strlen(name));
    137 
    138 	return req;
    139 }
    140 
    141 
    142 static struct wpabuf * eap_mschapv2_build_success_req(
    143 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
    144 {
    145 	struct wpabuf *req;
    146 	struct eap_mschapv2_hdr *ms;
    147 	u8 *msg;
    148 	char *message = "OK";
    149 	size_t ms_len;
    150 
    151 	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
    152 		os_strlen(message);
    153 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    154 			    EAP_CODE_REQUEST, id);
    155 	if (req == NULL) {
    156 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    157 			   " for request");
    158 		data->state = FAILURE;
    159 		return NULL;
    160 	}
    161 
    162 	ms = wpabuf_put(req, sizeof(*ms));
    163 	ms->op_code = MSCHAPV2_OP_SUCCESS;
    164 	ms->mschapv2_id = data->resp_mschapv2_id;
    165 	WPA_PUT_BE16(ms->ms_length, ms_len);
    166 	msg = (u8 *) (ms + 1);
    167 
    168 	wpabuf_put_u8(req, 'S');
    169 	wpabuf_put_u8(req, '=');
    170 	wpa_snprintf_hex_uppercase(
    171 		wpabuf_put(req, sizeof(data->auth_response) * 2),
    172 		sizeof(data->auth_response) * 2 + 1,
    173 		data->auth_response, sizeof(data->auth_response));
    174 	wpabuf_put_u8(req, ' ');
    175 	wpabuf_put_u8(req, 'M');
    176 	wpabuf_put_u8(req, '=');
    177 	wpabuf_put_data(req, message, os_strlen(message));
    178 
    179 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
    180 			  msg, ms_len - sizeof(*ms));
    181 
    182 	return req;
    183 }
    184 
    185 
    186 static struct wpabuf * eap_mschapv2_build_failure_req(
    187 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
    188 {
    189 	struct wpabuf *req;
    190 	struct eap_mschapv2_hdr *ms;
    191 	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
    192 		"M=FAILED";
    193 	size_t ms_len;
    194 
    195 	ms_len = sizeof(*ms) + os_strlen(message);
    196 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    197 			    EAP_CODE_REQUEST, id);
    198 	if (req == NULL) {
    199 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    200 			   " for request");
    201 		data->state = FAILURE;
    202 		return NULL;
    203 	}
    204 
    205 	ms = wpabuf_put(req, sizeof(*ms));
    206 	ms->op_code = MSCHAPV2_OP_FAILURE;
    207 	ms->mschapv2_id = data->resp_mschapv2_id;
    208 	WPA_PUT_BE16(ms->ms_length, ms_len);
    209 
    210 	wpabuf_put_data(req, message, os_strlen(message));
    211 
    212 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
    213 			  (u8 *) message, os_strlen(message));
    214 
    215 	return req;
    216 }
    217 
    218 
    219 static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
    220 					     u8 id)
    221 {
    222 	struct eap_mschapv2_data *data = priv;
    223 
    224 	switch (data->state) {
    225 	case CHALLENGE:
    226 		return eap_mschapv2_build_challenge(sm, data, id);
    227 	case SUCCESS_REQ:
    228 		return eap_mschapv2_build_success_req(sm, data, id);
    229 	case FAILURE_REQ:
    230 		return eap_mschapv2_build_failure_req(sm, data, id);
    231 	default:
    232 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
    233 			   "buildReq", data->state);
    234 		break;
    235 	}
    236 	return NULL;
    237 }
    238 
    239 
    240 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
    241 				  struct wpabuf *respData)
    242 {
    243 	struct eap_mschapv2_data *data = priv;
    244 	struct eap_mschapv2_hdr *resp;
    245 	const u8 *pos;
    246 	size_t len;
    247 
    248 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    249 			       &len);
    250 	if (pos == NULL || len < 1) {
    251 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
    252 		return TRUE;
    253 	}
    254 
    255 	resp = (struct eap_mschapv2_hdr *) pos;
    256 	if (data->state == CHALLENGE &&
    257 	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
    258 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
    259 			   "ignore op %d", resp->op_code);
    260 		return TRUE;
    261 	}
    262 
    263 	if (data->state == SUCCESS_REQ &&
    264 	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
    265 	    resp->op_code != MSCHAPV2_OP_FAILURE) {
    266 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
    267 			   "Failure - ignore op %d", resp->op_code);
    268 		return TRUE;
    269 	}
    270 
    271 	if (data->state == FAILURE_REQ &&
    272 	    resp->op_code != MSCHAPV2_OP_FAILURE) {
    273 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
    274 			   "- ignore op %d", resp->op_code);
    275 		return TRUE;
    276 	}
    277 
    278 	return FALSE;
    279 }
    280 
    281 
    282 static void eap_mschapv2_process_response(struct eap_sm *sm,
    283 					  struct eap_mschapv2_data *data,
    284 					  struct wpabuf *respData)
    285 {
    286 	struct eap_mschapv2_hdr *resp;
    287 	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
    288 	u8 flags;
    289 	size_t len, name_len, i;
    290 	u8 expected[24];
    291 	const u8 *username, *user;
    292 	size_t username_len, user_len;
    293 	int res;
    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 	/* MSCHAPv2 does not include optional domain name in the
    334 	 * challenge-response calculation, so remove domain prefix
    335 	 * (if present). */
    336 	username = sm->identity;
    337 	username_len = sm->identity_len;
    338 	for (i = 0; i < username_len; i++) {
    339 		if (username[i] == '\\') {
    340 			username_len -= i + 1;
    341 			username += i + 1;
    342 			break;
    343 		}
    344 	}
    345 
    346 	user = name;
    347 	user_len = name_len;
    348 	for (i = 0; i < user_len; i++) {
    349 		if (user[i] == '\\') {
    350 			user_len -= i + 1;
    351 			user += i + 1;
    352 			break;
    353 		}
    354 	}
    355 
    356 	if (username_len != user_len ||
    357 	    os_memcmp(username, user, username_len) != 0) {
    358 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
    359 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
    360 				  "name", username, username_len);
    361 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
    362 				  "name", user, user_len);
    363 		data->state = FAILURE;
    364 		return;
    365 	}
    366 
    367 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
    368 			  username, username_len);
    369 
    370 	if (sm->user->password_hash) {
    371 		res = generate_nt_response_pwhash(data->auth_challenge,
    372 						  peer_challenge,
    373 						  username, username_len,
    374 						  sm->user->password,
    375 						  expected);
    376 	} else {
    377 		res = generate_nt_response(data->auth_challenge,
    378 					   peer_challenge,
    379 					   username, username_len,
    380 					   sm->user->password,
    381 					   sm->user->password_len,
    382 					   expected);
    383 	}
    384 	if (res) {
    385 		data->state = FAILURE;
    386 		return;
    387 	}
    388 
    389 	if (os_memcmp(nt_response, expected, 24) == 0) {
    390 		const u8 *pw_hash;
    391 		u8 pw_hash_buf[16], pw_hash_hash[16];
    392 
    393 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
    394 		data->state = SUCCESS_REQ;
    395 
    396 		/* Authenticator response is not really needed yet, but
    397 		 * calculate it here so that peer_challenge and username need
    398 		 * not be saved. */
    399 		if (sm->user->password_hash) {
    400 			pw_hash = sm->user->password;
    401 		} else {
    402 			if (nt_password_hash(sm->user->password,
    403 					     sm->user->password_len,
    404 					     pw_hash_buf) < 0) {
    405 				data->state = FAILURE;
    406 				return;
    407 			}
    408 			pw_hash = pw_hash_buf;
    409 		}
    410 		generate_authenticator_response_pwhash(
    411 			pw_hash, peer_challenge, data->auth_challenge,
    412 			username, username_len, nt_response,
    413 			data->auth_response);
    414 
    415 		hash_nt_password_hash(pw_hash, pw_hash_hash);
    416 		get_master_key(pw_hash_hash, nt_response, data->master_key);
    417 		data->master_key_valid = 1;
    418 		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
    419 				data->master_key, MSCHAPV2_KEY_LEN);
    420 	} else {
    421 		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
    422 			    expected, 24);
    423 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
    424 		data->state = FAILURE_REQ;
    425 	}
    426 }
    427 
    428 
    429 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
    430 					      struct eap_mschapv2_data *data,
    431 					      struct wpabuf *respData)
    432 {
    433 	struct eap_mschapv2_hdr *resp;
    434 	const u8 *pos;
    435 	size_t len;
    436 
    437 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    438 			       &len);
    439 	if (pos == NULL || len < 1)
    440 		return; /* Should not happen - frame already validated */
    441 
    442 	resp = (struct eap_mschapv2_hdr *) pos;
    443 
    444 	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
    445 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
    446 			   " - authentication completed successfully");
    447 		data->state = SUCCESS;
    448 	} else {
    449 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
    450 			   "Response - peer rejected authentication");
    451 		data->state = FAILURE;
    452 	}
    453 }
    454 
    455 
    456 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
    457 					      struct eap_mschapv2_data *data,
    458 					      struct wpabuf *respData)
    459 {
    460 	struct eap_mschapv2_hdr *resp;
    461 	const u8 *pos;
    462 	size_t len;
    463 
    464 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    465 			       &len);
    466 	if (pos == NULL || len < 1)
    467 		return; /* Should not happen - frame already validated */
    468 
    469 	resp = (struct eap_mschapv2_hdr *) pos;
    470 
    471 	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
    472 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
    473 			   " - authentication failed");
    474 	} else {
    475 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
    476 			   "Response - authentication failed");
    477 	}
    478 
    479 	data->state = FAILURE;
    480 }
    481 
    482 
    483 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
    484 				 struct wpabuf *respData)
    485 {
    486 	struct eap_mschapv2_data *data = priv;
    487 
    488 	if (sm->user == NULL || sm->user->password == NULL) {
    489 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
    490 		data->state = FAILURE;
    491 		return;
    492 	}
    493 
    494 	switch (data->state) {
    495 	case CHALLENGE:
    496 		eap_mschapv2_process_response(sm, data, respData);
    497 		break;
    498 	case SUCCESS_REQ:
    499 		eap_mschapv2_process_success_resp(sm, data, respData);
    500 		break;
    501 	case FAILURE_REQ:
    502 		eap_mschapv2_process_failure_resp(sm, data, respData);
    503 		break;
    504 	default:
    505 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
    506 			   "process", data->state);
    507 		break;
    508 	}
    509 }
    510 
    511 
    512 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
    513 {
    514 	struct eap_mschapv2_data *data = priv;
    515 	return data->state == SUCCESS || data->state == FAILURE;
    516 }
    517 
    518 
    519 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
    520 {
    521 	struct eap_mschapv2_data *data = priv;
    522 	u8 *key;
    523 
    524 	if (data->state != SUCCESS || !data->master_key_valid)
    525 		return NULL;
    526 
    527 	*len = 2 * MSCHAPV2_KEY_LEN;
    528 	key = os_malloc(*len);
    529 	if (key == NULL)
    530 		return NULL;
    531 	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
    532 	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
    533 	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
    534 				MSCHAPV2_KEY_LEN, 1, 1);
    535 	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
    536 
    537 	return key;
    538 }
    539 
    540 
    541 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
    542 {
    543 	struct eap_mschapv2_data *data = priv;
    544 	return data->state == SUCCESS;
    545 }
    546 
    547 
    548 int eap_server_mschapv2_register(void)
    549 {
    550 	struct eap_method *eap;
    551 	int ret;
    552 
    553 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
    554 				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
    555 				      "MSCHAPV2");
    556 	if (eap == NULL)
    557 		return -1;
    558 
    559 	eap->init = eap_mschapv2_init;
    560 	eap->reset = eap_mschapv2_reset;
    561 	eap->buildReq = eap_mschapv2_buildReq;
    562 	eap->check = eap_mschapv2_check;
    563 	eap->process = eap_mschapv2_process;
    564 	eap->isDone = eap_mschapv2_isDone;
    565 	eap->getKey = eap_mschapv2_getKey;
    566 	eap->isSuccess = eap_mschapv2_isSuccess;
    567 
    568 	ret = eap_server_method_register(eap);
    569 	if (ret)
    570 		eap_server_method_free(eap);
    571 	return ret;
    572 }
    573