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