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 "eap_i.h"
     19 #include "ms_funcs.h"
     20 
     21 
     22 struct eap_mschapv2_hdr {
     23 	u8 op_code; /* MSCHAPV2_OP_* */
     24 	u8 mschapv2_id; /* must be changed for challenges, but not for
     25 			 * success/failure */
     26 	u8 ms_length[2]; /* Note: misaligned; length - 5 */
     27 	/* followed by data */
     28 } STRUCT_PACKED;
     29 
     30 #define MSCHAPV2_OP_CHALLENGE 1
     31 #define MSCHAPV2_OP_RESPONSE 2
     32 #define MSCHAPV2_OP_SUCCESS 3
     33 #define MSCHAPV2_OP_FAILURE 4
     34 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
     35 
     36 #define MSCHAPV2_RESP_LEN 49
     37 
     38 #define ERROR_RESTRICTED_LOGON_HOURS 646
     39 #define ERROR_ACCT_DISABLED 647
     40 #define ERROR_PASSWD_EXPIRED 648
     41 #define ERROR_NO_DIALIN_PERMISSION 649
     42 #define ERROR_AUTHENTICATION_FAILURE 691
     43 #define ERROR_CHANGING_PASSWORD 709
     44 
     45 #define PASSWD_CHANGE_CHAL_LEN 16
     46 #define MSCHAPV2_KEY_LEN 16
     47 
     48 
     49 #define CHALLENGE_LEN 16
     50 
     51 struct eap_mschapv2_data {
     52 	u8 auth_challenge[CHALLENGE_LEN];
     53 	int auth_challenge_from_tls;
     54 	u8 *peer_challenge;
     55 	u8 auth_response[20];
     56 	enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
     57 	u8 resp_mschapv2_id;
     58 	u8 master_key[16];
     59 	int master_key_valid;
     60 };
     61 
     62 
     63 static void * eap_mschapv2_init(struct eap_sm *sm)
     64 {
     65 	struct eap_mschapv2_data *data;
     66 
     67 	data = os_zalloc(sizeof(*data));
     68 	if (data == NULL)
     69 		return NULL;
     70 	data->state = CHALLENGE;
     71 
     72 	if (sm->auth_challenge) {
     73 		os_memcpy(data->auth_challenge, sm->auth_challenge,
     74 			  CHALLENGE_LEN);
     75 		data->auth_challenge_from_tls = 1;
     76 	}
     77 
     78 	if (sm->peer_challenge) {
     79 		data->peer_challenge = os_malloc(CHALLENGE_LEN);
     80 		if (data->peer_challenge == NULL) {
     81 			os_free(data);
     82 			return NULL;
     83 		}
     84 		os_memcpy(data->peer_challenge, sm->peer_challenge,
     85 			  CHALLENGE_LEN);
     86 	}
     87 
     88 	return data;
     89 }
     90 
     91 
     92 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
     93 {
     94 	struct eap_mschapv2_data *data = priv;
     95 	if (data == NULL)
     96 		return;
     97 
     98 	os_free(data->peer_challenge);
     99 	os_free(data);
    100 }
    101 
    102 
    103 static struct wpabuf * eap_mschapv2_build_challenge(
    104 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
    105 {
    106 	struct wpabuf *req;
    107 	struct eap_mschapv2_hdr *ms;
    108 	char *name = "hostapd"; /* TODO: make this configurable */
    109 	size_t ms_len;
    110 
    111 	if (!data->auth_challenge_from_tls &&
    112 	    os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
    113 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
    114 			   "data");
    115 		data->state = FAILURE;
    116 		return NULL;
    117 	}
    118 
    119 	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
    120 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    121 			    EAP_CODE_REQUEST, id);
    122 	if (req == NULL) {
    123 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    124 			   " for request");
    125 		data->state = FAILURE;
    126 		return NULL;
    127 	}
    128 
    129 	ms = wpabuf_put(req, sizeof(*ms));
    130 	ms->op_code = MSCHAPV2_OP_CHALLENGE;
    131 	ms->mschapv2_id = id;
    132 	WPA_PUT_BE16(ms->ms_length, ms_len);
    133 
    134 	wpabuf_put_u8(req, CHALLENGE_LEN);
    135 	if (!data->auth_challenge_from_tls)
    136 		wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
    137 	else
    138 		wpabuf_put(req, CHALLENGE_LEN);
    139 	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
    140 		    data->auth_challenge, CHALLENGE_LEN);
    141 	wpabuf_put_data(req, name, os_strlen(name));
    142 
    143 	return req;
    144 }
    145 
    146 
    147 static struct wpabuf * eap_mschapv2_build_success_req(
    148 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
    149 {
    150 	struct wpabuf *req;
    151 	struct eap_mschapv2_hdr *ms;
    152 	u8 *msg;
    153 	char *message = "OK";
    154 	size_t ms_len;
    155 
    156 	ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
    157 		os_strlen(message);
    158 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    159 			    EAP_CODE_REQUEST, id);
    160 	if (req == NULL) {
    161 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    162 			   " for request");
    163 		data->state = FAILURE;
    164 		return NULL;
    165 	}
    166 
    167 	ms = wpabuf_put(req, sizeof(*ms));
    168 	ms->op_code = MSCHAPV2_OP_SUCCESS;
    169 	ms->mschapv2_id = data->resp_mschapv2_id;
    170 	WPA_PUT_BE16(ms->ms_length, ms_len);
    171 	msg = (u8 *) (ms + 1);
    172 
    173 	wpabuf_put_u8(req, 'S');
    174 	wpabuf_put_u8(req, '=');
    175 	wpa_snprintf_hex_uppercase(
    176 		wpabuf_put(req, sizeof(data->auth_response) * 2),
    177 		sizeof(data->auth_response) * 2 + 1,
    178 		data->auth_response, sizeof(data->auth_response));
    179 	wpabuf_put_u8(req, ' ');
    180 	wpabuf_put_u8(req, 'M');
    181 	wpabuf_put_u8(req, '=');
    182 	wpabuf_put_data(req, message, os_strlen(message));
    183 
    184 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
    185 			  msg, ms_len - sizeof(*ms));
    186 
    187 	return req;
    188 }
    189 
    190 
    191 static struct wpabuf * eap_mschapv2_build_failure_req(
    192 	struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
    193 {
    194 	struct wpabuf *req;
    195 	struct eap_mschapv2_hdr *ms;
    196 	char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
    197 		"M=FAILED";
    198 	size_t ms_len;
    199 
    200 	ms_len = sizeof(*ms) + os_strlen(message);
    201 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
    202 			    EAP_CODE_REQUEST, id);
    203 	if (req == NULL) {
    204 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
    205 			   " for request");
    206 		data->state = FAILURE;
    207 		return NULL;
    208 	}
    209 
    210 	ms = wpabuf_put(req, sizeof(*ms));
    211 	ms->op_code = MSCHAPV2_OP_FAILURE;
    212 	ms->mschapv2_id = data->resp_mschapv2_id;
    213 	WPA_PUT_BE16(ms->ms_length, ms_len);
    214 
    215 	wpabuf_put_data(req, message, os_strlen(message));
    216 
    217 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
    218 			  (u8 *) message, os_strlen(message));
    219 
    220 	return req;
    221 }
    222 
    223 
    224 static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
    225 					     u8 id)
    226 {
    227 	struct eap_mschapv2_data *data = priv;
    228 
    229 	switch (data->state) {
    230 	case CHALLENGE:
    231 		return eap_mschapv2_build_challenge(sm, data, id);
    232 	case SUCCESS_REQ:
    233 		return eap_mschapv2_build_success_req(sm, data, id);
    234 	case FAILURE_REQ:
    235 		return eap_mschapv2_build_failure_req(sm, data, id);
    236 	default:
    237 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
    238 			   "buildReq", data->state);
    239 		break;
    240 	}
    241 	return NULL;
    242 }
    243 
    244 
    245 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
    246 				  struct wpabuf *respData)
    247 {
    248 	struct eap_mschapv2_data *data = priv;
    249 	struct eap_mschapv2_hdr *resp;
    250 	const u8 *pos;
    251 	size_t len;
    252 
    253 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    254 			       &len);
    255 	if (pos == NULL || len < 1) {
    256 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
    257 		return TRUE;
    258 	}
    259 
    260 	resp = (struct eap_mschapv2_hdr *) pos;
    261 	if (data->state == CHALLENGE &&
    262 	    resp->op_code != MSCHAPV2_OP_RESPONSE) {
    263 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
    264 			   "ignore op %d", resp->op_code);
    265 		return TRUE;
    266 	}
    267 
    268 	if (data->state == SUCCESS_REQ &&
    269 	    resp->op_code != MSCHAPV2_OP_SUCCESS &&
    270 	    resp->op_code != MSCHAPV2_OP_FAILURE) {
    271 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
    272 			   "Failure - ignore op %d", resp->op_code);
    273 		return TRUE;
    274 	}
    275 
    276 	if (data->state == FAILURE_REQ &&
    277 	    resp->op_code != MSCHAPV2_OP_FAILURE) {
    278 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
    279 			   "- ignore op %d", resp->op_code);
    280 		return TRUE;
    281 	}
    282 
    283 	return FALSE;
    284 }
    285 
    286 
    287 static void eap_mschapv2_process_response(struct eap_sm *sm,
    288 					  struct eap_mschapv2_data *data,
    289 					  struct wpabuf *respData)
    290 {
    291 	struct eap_mschapv2_hdr *resp;
    292 	const u8 *pos, *end, *peer_challenge, *nt_response, *name;
    293 	u8 flags;
    294 	size_t len, name_len, i;
    295 	u8 expected[24];
    296 	const u8 *username, *user;
    297 	size_t username_len, user_len;
    298 
    299 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    300 			       &len);
    301 	if (pos == NULL || len < 1)
    302 		return; /* Should not happen - frame already validated */
    303 
    304 	end = pos + len;
    305 	resp = (struct eap_mschapv2_hdr *) pos;
    306 	pos = (u8 *) (resp + 1);
    307 
    308 	if (len < sizeof(*resp) + 1 + 49 ||
    309 	    resp->op_code != MSCHAPV2_OP_RESPONSE ||
    310 	    pos[0] != 49) {
    311 		wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
    312 				respData);
    313 		data->state = FAILURE;
    314 		return;
    315 	}
    316 	data->resp_mschapv2_id = resp->mschapv2_id;
    317 	pos++;
    318 	peer_challenge = pos;
    319 	pos += 16 + 8;
    320 	nt_response = pos;
    321 	pos += 24;
    322 	flags = *pos++;
    323 	name = pos;
    324 	name_len = end - name;
    325 
    326 	if (data->peer_challenge) {
    327 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
    328 			   "Peer-Challenge");
    329 		peer_challenge = data->peer_challenge;
    330 	}
    331 	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
    332 		    peer_challenge, 16);
    333 	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
    334 	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
    335 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
    336 
    337 	/* MSCHAPv2 does not include optional domain name in the
    338 	 * challenge-response calculation, so remove domain prefix
    339 	 * (if present). */
    340 	username = sm->identity;
    341 	username_len = sm->identity_len;
    342 	for (i = 0; i < username_len; i++) {
    343 		if (username[i] == '\\') {
    344 			username_len -= i + 1;
    345 			username += i + 1;
    346 			break;
    347 		}
    348 	}
    349 
    350 	user = name;
    351 	user_len = name_len;
    352 	for (i = 0; i < user_len; i++) {
    353 		if (user[i] == '\\') {
    354 			user_len -= i + 1;
    355 			user += i + 1;
    356 			break;
    357 		}
    358 	}
    359 
    360 	if (username_len != user_len ||
    361 	    os_memcmp(username, user, username_len) != 0) {
    362 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
    363 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
    364 				  "name", username, username_len);
    365 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
    366 				  "name", user, user_len);
    367 		data->state = FAILURE;
    368 		return;
    369 	}
    370 
    371 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
    372 			  username, username_len);
    373 
    374 	if (sm->user->password_hash) {
    375 		generate_nt_response_pwhash(data->auth_challenge,
    376 					    peer_challenge,
    377 					    username, username_len,
    378 					    sm->user->password,
    379 					    expected);
    380 	} else {
    381 		generate_nt_response(data->auth_challenge, peer_challenge,
    382 				     username, username_len,
    383 				     sm->user->password,
    384 				     sm->user->password_len,
    385 				     expected);
    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 			nt_password_hash(sm->user->password,
    402 					 sm->user->password_len,
    403 					 pw_hash_buf);
    404 			pw_hash = pw_hash_buf;
    405 		}
    406 		generate_authenticator_response_pwhash(
    407 			pw_hash, peer_challenge, data->auth_challenge,
    408 			username, username_len, nt_response,
    409 			data->auth_response);
    410 
    411 		hash_nt_password_hash(pw_hash, pw_hash_hash);
    412 		get_master_key(pw_hash_hash, nt_response, data->master_key);
    413 		data->master_key_valid = 1;
    414 		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
    415 				data->master_key, MSCHAPV2_KEY_LEN);
    416 	} else {
    417 		wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
    418 			    expected, 24);
    419 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
    420 		data->state = FAILURE_REQ;
    421 	}
    422 }
    423 
    424 
    425 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
    426 					      struct eap_mschapv2_data *data,
    427 					      struct wpabuf *respData)
    428 {
    429 	struct eap_mschapv2_hdr *resp;
    430 	const u8 *pos;
    431 	size_t len;
    432 
    433 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    434 			       &len);
    435 	if (pos == NULL || len < 1)
    436 		return; /* Should not happen - frame already validated */
    437 
    438 	resp = (struct eap_mschapv2_hdr *) pos;
    439 
    440 	if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
    441 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
    442 			   " - authentication completed successfully");
    443 		data->state = SUCCESS;
    444 	} else {
    445 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
    446 			   "Response - peer rejected authentication");
    447 		data->state = FAILURE;
    448 	}
    449 }
    450 
    451 
    452 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
    453 					      struct eap_mschapv2_data *data,
    454 					      struct wpabuf *respData)
    455 {
    456 	struct eap_mschapv2_hdr *resp;
    457 	const u8 *pos;
    458 	size_t len;
    459 
    460 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
    461 			       &len);
    462 	if (pos == NULL || len < 1)
    463 		return; /* Should not happen - frame already validated */
    464 
    465 	resp = (struct eap_mschapv2_hdr *) pos;
    466 
    467 	if (resp->op_code == MSCHAPV2_OP_FAILURE) {
    468 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
    469 			   " - authentication failed");
    470 	} else {
    471 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
    472 			   "Response - authentication failed");
    473 	}
    474 
    475 	data->state = FAILURE;
    476 }
    477 
    478 
    479 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
    480 				 struct wpabuf *respData)
    481 {
    482 	struct eap_mschapv2_data *data = priv;
    483 
    484 	if (sm->user == NULL || sm->user->password == NULL) {
    485 		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
    486 		data->state = FAILURE;
    487 		return;
    488 	}
    489 
    490 	switch (data->state) {
    491 	case CHALLENGE:
    492 		eap_mschapv2_process_response(sm, data, respData);
    493 		break;
    494 	case SUCCESS_REQ:
    495 		eap_mschapv2_process_success_resp(sm, data, respData);
    496 		break;
    497 	case FAILURE_REQ:
    498 		eap_mschapv2_process_failure_resp(sm, data, respData);
    499 		break;
    500 	default:
    501 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
    502 			   "process", data->state);
    503 		break;
    504 	}
    505 }
    506 
    507 
    508 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
    509 {
    510 	struct eap_mschapv2_data *data = priv;
    511 	return data->state == SUCCESS || data->state == FAILURE;
    512 }
    513 
    514 
    515 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
    516 {
    517 	struct eap_mschapv2_data *data = priv;
    518 	u8 *key;
    519 
    520 	if (data->state != SUCCESS || !data->master_key_valid)
    521 		return NULL;
    522 
    523 	*len = 2 * MSCHAPV2_KEY_LEN;
    524 	key = os_malloc(*len);
    525 	if (key == NULL)
    526 		return NULL;
    527 	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
    528 	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
    529 	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
    530 				MSCHAPV2_KEY_LEN, 1, 1);
    531 	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
    532 
    533 	return key;
    534 }
    535 
    536 
    537 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
    538 {
    539 	struct eap_mschapv2_data *data = priv;
    540 	return data->state == SUCCESS;
    541 }
    542 
    543 
    544 int eap_server_mschapv2_register(void)
    545 {
    546 	struct eap_method *eap;
    547 	int ret;
    548 
    549 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
    550 				      EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
    551 				      "MSCHAPV2");
    552 	if (eap == NULL)
    553 		return -1;
    554 
    555 	eap->init = eap_mschapv2_init;
    556 	eap->reset = eap_mschapv2_reset;
    557 	eap->buildReq = eap_mschapv2_buildReq;
    558 	eap->check = eap_mschapv2_check;
    559 	eap->process = eap_mschapv2_process;
    560 	eap->isDone = eap_mschapv2_isDone;
    561 	eap->getKey = eap_mschapv2_getKey;
    562 	eap->isSuccess = eap_mschapv2_isSuccess;
    563 
    564 	ret = eap_server_method_register(eap);
    565 	if (ret)
    566 		eap_server_method_free(eap);
    567 	return ret;
    568 }
    569