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