1/* 2 * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) 3 * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 * 8 * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). 9 * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP 10 * Extensions Protocol, Version 2, for mutual authentication and key 11 * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in 12 * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in 13 * RFC 3079. 14 */ 15 16#include "includes.h" 17 18#include "common.h" 19#include "crypto/ms_funcs.h" 20#include "crypto/random.h" 21#include "common/wpa_ctrl.h" 22#include "mschapv2.h" 23#include "eap_i.h" 24#include "eap_config.h" 25 26 27#ifdef _MSC_VER 28#pragma pack(push, 1) 29#endif /* _MSC_VER */ 30 31struct eap_mschapv2_hdr { 32 u8 op_code; /* MSCHAPV2_OP_* */ 33 u8 mschapv2_id; /* usually same as EAP identifier; must be changed 34 * for challenges, but not for success/failure */ 35 u8 ms_length[2]; /* Note: misaligned; length - 5 */ 36 /* followed by data */ 37} STRUCT_PACKED; 38 39/* Response Data field */ 40struct ms_response { 41 u8 peer_challenge[MSCHAPV2_CHAL_LEN]; 42 u8 reserved[8]; 43 u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; 44 u8 flags; 45} STRUCT_PACKED; 46 47/* Change-Password Data field */ 48struct ms_change_password { 49 u8 encr_password[516]; 50 u8 encr_hash[16]; 51 u8 peer_challenge[MSCHAPV2_CHAL_LEN]; 52 u8 reserved[8]; 53 u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; 54 u8 flags[2]; 55} STRUCT_PACKED; 56 57#ifdef _MSC_VER 58#pragma pack(pop) 59#endif /* _MSC_VER */ 60 61#define MSCHAPV2_OP_CHALLENGE 1 62#define MSCHAPV2_OP_RESPONSE 2 63#define MSCHAPV2_OP_SUCCESS 3 64#define MSCHAPV2_OP_FAILURE 4 65#define MSCHAPV2_OP_CHANGE_PASSWORD 7 66 67#define ERROR_RESTRICTED_LOGON_HOURS 646 68#define ERROR_ACCT_DISABLED 647 69#define ERROR_PASSWD_EXPIRED 648 70#define ERROR_NO_DIALIN_PERMISSION 649 71#define ERROR_AUTHENTICATION_FAILURE 691 72#define ERROR_CHANGING_PASSWORD 709 73 74#define PASSWD_CHANGE_CHAL_LEN 16 75#define MSCHAPV2_KEY_LEN 16 76 77 78struct eap_mschapv2_data { 79 u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; 80 int auth_response_valid; 81 82 int prev_error; 83 u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; 84 int passwd_change_challenge_valid; 85 int passwd_change_version; 86 87 /* Optional challenge values generated in EAP-FAST Phase 1 negotiation 88 */ 89 u8 *peer_challenge; 90 u8 *auth_challenge; 91 92 int phase2; 93 u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; 94 int master_key_valid; 95 int success; 96 97 struct wpabuf *prev_challenge; 98}; 99 100 101static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); 102 103 104static void * eap_mschapv2_init(struct eap_sm *sm) 105{ 106 struct eap_mschapv2_data *data; 107 data = os_zalloc(sizeof(*data)); 108 if (data == NULL) 109 return NULL; 110 111 if (sm->peer_challenge) { 112 data->peer_challenge = os_memdup(sm->peer_challenge, 113 MSCHAPV2_CHAL_LEN); 114 if (data->peer_challenge == NULL) { 115 eap_mschapv2_deinit(sm, data); 116 return NULL; 117 } 118 } 119 120 if (sm->auth_challenge) { 121 data->auth_challenge = os_memdup(sm->auth_challenge, 122 MSCHAPV2_CHAL_LEN); 123 if (data->auth_challenge == NULL) { 124 eap_mschapv2_deinit(sm, data); 125 return NULL; 126 } 127 } 128 129 data->phase2 = sm->init_phase2; 130 131 return data; 132} 133 134 135static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) 136{ 137 struct eap_mschapv2_data *data = priv; 138 os_free(data->peer_challenge); 139 os_free(data->auth_challenge); 140 wpabuf_free(data->prev_challenge); 141 bin_clear_free(data, sizeof(*data)); 142} 143 144 145static struct wpabuf * eap_mschapv2_challenge_reply( 146 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, 147 u8 mschapv2_id, const u8 *auth_challenge) 148{ 149 struct wpabuf *resp; 150 struct eap_mschapv2_hdr *ms; 151 u8 *peer_challenge; 152 int ms_len; 153 struct ms_response *r; 154 size_t identity_len, password_len; 155 const u8 *identity, *password; 156 int pwhash; 157 158 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); 159 160 identity = eap_get_config_identity(sm, &identity_len); 161 password = eap_get_config_password2(sm, &password_len, &pwhash); 162 if (identity == NULL || password == NULL) 163 return NULL; 164 165 ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; 166 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 167 EAP_CODE_RESPONSE, id); 168 if (resp == NULL) 169 return NULL; 170 171 ms = wpabuf_put(resp, sizeof(*ms)); 172 ms->op_code = MSCHAPV2_OP_RESPONSE; 173 ms->mschapv2_id = mschapv2_id; 174 if (data->prev_error) { 175 /* 176 * TODO: this does not seem to be enough when processing two 177 * or more failure messages. IAS did not increment mschapv2_id 178 * in its own packets, but it seemed to expect the peer to 179 * increment this for all packets(?). 180 */ 181 ms->mschapv2_id++; 182 } 183 WPA_PUT_BE16(ms->ms_length, ms_len); 184 185 wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ 186 187 /* Response */ 188 r = wpabuf_put(resp, sizeof(*r)); 189 peer_challenge = r->peer_challenge; 190 if (data->peer_challenge) { 191 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " 192 "in Phase 1"); 193 peer_challenge = data->peer_challenge; 194 os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); 195 } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { 196 wpabuf_free(resp); 197 return NULL; 198 } 199 os_memset(r->reserved, 0, 8); 200 if (data->auth_challenge) { 201 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " 202 "in Phase 1"); 203 auth_challenge = data->auth_challenge; 204 } 205 if (mschapv2_derive_response(identity, identity_len, password, 206 password_len, pwhash, auth_challenge, 207 peer_challenge, r->nt_response, 208 data->auth_response, data->master_key)) { 209 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " 210 "response"); 211 wpabuf_free(resp); 212 return NULL; 213 } 214 data->auth_response_valid = 1; 215 data->master_key_valid = 1; 216 217 r->flags = 0; /* reserved, must be zero */ 218 219 wpabuf_put_data(resp, identity, identity_len); 220 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 221 "(response)", id, ms->mschapv2_id); 222 return resp; 223} 224 225 226/** 227 * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message 228 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 229 * @data: Pointer to private EAP method data from eap_mschapv2_init() 230 * @ret: Return values from EAP request validation and processing 231 * @req: Pointer to EAP-MSCHAPv2 header from the request 232 * @req_len: Length of the EAP-MSCHAPv2 data 233 * @id: EAP identifier used in the request 234 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 235 * no reply available 236 */ 237static struct wpabuf * eap_mschapv2_challenge( 238 struct eap_sm *sm, struct eap_mschapv2_data *data, 239 struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, 240 size_t req_len, u8 id) 241{ 242 size_t len, challenge_len; 243 const u8 *pos, *challenge; 244 245 if (eap_get_config_identity(sm, &len) == NULL || 246 eap_get_config_password(sm, &len) == NULL) 247 return NULL; 248 249 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); 250 if (req_len < sizeof(*req) + 1) { 251 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " 252 "(len %lu)", (unsigned long) req_len); 253 ret->ignore = TRUE; 254 return NULL; 255 } 256 pos = (const u8 *) (req + 1); 257 challenge_len = *pos++; 258 len = req_len - sizeof(*req) - 1; 259 if (challenge_len != MSCHAPV2_CHAL_LEN) { 260 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " 261 "%lu", (unsigned long) challenge_len); 262 ret->ignore = TRUE; 263 return NULL; 264 } 265 266 if (len < challenge_len) { 267 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" 268 " packet: len=%lu challenge_len=%lu", 269 (unsigned long) len, (unsigned long) challenge_len); 270 ret->ignore = TRUE; 271 return NULL; 272 } 273 274 if (data->passwd_change_challenge_valid) { 275 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " 276 "failure message"); 277 challenge = data->passwd_change_challenge; 278 } else 279 challenge = pos; 280 pos += challenge_len; 281 len -= challenge_len; 282 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", 283 pos, len); 284 285 ret->ignore = FALSE; 286 ret->methodState = METHOD_MAY_CONT; 287 ret->decision = DECISION_FAIL; 288 ret->allowNotifications = TRUE; 289 290 return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, 291 challenge); 292} 293 294 295static void eap_mschapv2_password_changed(struct eap_sm *sm, 296 struct eap_mschapv2_data *data) 297{ 298 struct eap_peer_config *config = eap_get_config(sm); 299 if (config && config->new_password) { 300 wpa_msg(sm->msg_ctx, MSG_INFO, 301 WPA_EVENT_PASSWORD_CHANGED 302 "EAP-MSCHAPV2: Password changed successfully"); 303 data->prev_error = 0; 304 bin_clear_free(config->password, config->password_len); 305 if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { 306 /* TODO: update external storage */ 307 } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { 308 config->password = os_malloc(16); 309 config->password_len = 16; 310 if (config->password && 311 nt_password_hash(config->new_password, 312 config->new_password_len, 313 config->password)) { 314 bin_clear_free(config->password, 315 config->password_len); 316 config->password = NULL; 317 config->password_len = 0; 318 } 319 bin_clear_free(config->new_password, 320 config->new_password_len); 321 } else { 322 config->password = config->new_password; 323 config->password_len = config->new_password_len; 324 } 325 config->new_password = NULL; 326 config->new_password_len = 0; 327 } 328} 329 330 331/** 332 * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message 333 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 334 * @data: Pointer to private EAP method data from eap_mschapv2_init() 335 * @ret: Return values from EAP request validation and processing 336 * @req: Pointer to EAP-MSCHAPv2 header from the request 337 * @req_len: Length of the EAP-MSCHAPv2 data 338 * @id: EAP identifier used in th erequest 339 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 340 * no reply available 341 */ 342static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, 343 struct eap_mschapv2_data *data, 344 struct eap_method_ret *ret, 345 const struct eap_mschapv2_hdr *req, 346 size_t req_len, u8 id) 347{ 348 struct wpabuf *resp; 349 const u8 *pos; 350 size_t len; 351 352 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); 353 len = req_len - sizeof(*req); 354 pos = (const u8 *) (req + 1); 355 if (!data->auth_response_valid || 356 mschapv2_verify_auth_response(data->auth_response, pos, len)) { 357 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " 358 "response in success request"); 359 ret->methodState = METHOD_DONE; 360 ret->decision = DECISION_FAIL; 361 return NULL; 362 } 363 pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 364 len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 365 while (len > 0 && *pos == ' ') { 366 pos++; 367 len--; 368 } 369 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", 370 pos, len); 371 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); 372 373 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success 374 * message. */ 375 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 376 EAP_CODE_RESPONSE, id); 377 if (resp == NULL) { 378 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " 379 "buffer for success response"); 380 ret->ignore = TRUE; 381 return NULL; 382 } 383 384 wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ 385 386 ret->methodState = METHOD_DONE; 387 ret->decision = DECISION_UNCOND_SUCC; 388 ret->allowNotifications = FALSE; 389 data->success = 1; 390 391 if (data->prev_error == ERROR_PASSWD_EXPIRED) 392 eap_mschapv2_password_changed(sm, data); 393 394 return resp; 395} 396 397 398static int eap_mschapv2_failure_txt(struct eap_sm *sm, 399 struct eap_mschapv2_data *data, char *txt) 400{ 401 char *pos, *msg = ""; 402 int retry = 1; 403 struct eap_peer_config *config = eap_get_config(sm); 404 405 /* For example: 406 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure 407 */ 408 409 pos = txt; 410 411 if (pos && os_strncmp(pos, "E=", 2) == 0) { 412 pos += 2; 413 data->prev_error = atoi(pos); 414 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", 415 data->prev_error); 416 pos = os_strchr(pos, ' '); 417 if (pos) 418 pos++; 419 } 420 421 if (pos && os_strncmp(pos, "R=", 2) == 0) { 422 pos += 2; 423 retry = atoi(pos); 424 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", 425 retry == 1 ? "" : "not "); 426 pos = os_strchr(pos, ' '); 427 if (pos) 428 pos++; 429 } 430 431 if (pos && os_strncmp(pos, "C=", 2) == 0) { 432 int hex_len; 433 pos += 2; 434 hex_len = os_strchr(pos, ' ') - (char *) pos; 435 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { 436 if (hexstr2bin(pos, data->passwd_change_challenge, 437 PASSWD_CHANGE_CHAL_LEN)) { 438 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " 439 "failure challenge"); 440 } else { 441 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " 442 "challenge", 443 data->passwd_change_challenge, 444 PASSWD_CHANGE_CHAL_LEN); 445 data->passwd_change_challenge_valid = 1; 446 } 447 } else { 448 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " 449 "challenge len %d", hex_len); 450 } 451 pos = os_strchr(pos, ' '); 452 if (pos) 453 pos++; 454 } else { 455 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " 456 "was not present in failure message"); 457 } 458 459 if (pos && os_strncmp(pos, "V=", 2) == 0) { 460 pos += 2; 461 data->passwd_change_version = atoi(pos); 462 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " 463 "protocol version %d", data->passwd_change_version); 464 pos = os_strchr(pos, ' '); 465 if (pos) 466 pos++; 467 } 468 469 if (pos && os_strncmp(pos, "M=", 2) == 0) { 470 pos += 2; 471 msg = pos; 472 } 473 if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry && 474 config && config->phase2 && 475 os_strstr(config->phase2, "mschapv2_retry=0")) { 476 wpa_printf(MSG_DEBUG, 477 "EAP-MSCHAPV2: mark password retry disabled based on local configuration"); 478 retry = 0; 479 } 480 wpa_msg(sm->msg_ctx, MSG_WARNING, 481 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " 482 "%d)", 483 msg, retry == 1 ? "" : "not ", data->prev_error); 484 if (data->prev_error == ERROR_PASSWD_EXPIRED && 485 data->passwd_change_version == 3 && config) { 486 if (config->new_password == NULL) { 487 wpa_msg(sm->msg_ctx, MSG_INFO, 488 "EAP-MSCHAPV2: Password expired - password " 489 "change required"); 490 eap_sm_request_new_password(sm); 491 } 492 } else if (retry == 1 && config) { 493 /* TODO: could prevent the current password from being used 494 * again at least for some period of time */ 495 if (!config->mschapv2_retry) 496 eap_sm_request_identity(sm); 497 eap_sm_request_password(sm); 498 config->mschapv2_retry = 1; 499 } else if (config) { 500 /* TODO: prevent retries using same username/password */ 501 config->mschapv2_retry = 0; 502 } 503 504 return retry == 1; 505} 506 507 508static struct wpabuf * eap_mschapv2_change_password( 509 struct eap_sm *sm, struct eap_mschapv2_data *data, 510 struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) 511{ 512#ifdef CONFIG_NO_RC4 513 wpa_printf(MSG_ERROR, 514 "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); 515 return NULL; 516#else /* CONFIG_NO_RC4 */ 517 struct wpabuf *resp; 518 int ms_len; 519 const u8 *username, *password, *new_password; 520 size_t username_len, password_len, new_password_len; 521 struct eap_mschapv2_hdr *ms; 522 struct ms_change_password *cp; 523 u8 password_hash[16], password_hash_hash[16]; 524 int pwhash; 525 526 username = eap_get_config_identity(sm, &username_len); 527 password = eap_get_config_password2(sm, &password_len, &pwhash); 528 new_password = eap_get_config_new_password(sm, &new_password_len); 529 if (username == NULL || password == NULL || new_password == NULL) 530 return NULL; 531 532 username = mschapv2_remove_domain(username, &username_len); 533 534 ret->ignore = FALSE; 535 ret->methodState = METHOD_MAY_CONT; 536 ret->decision = DECISION_COND_SUCC; 537 ret->allowNotifications = TRUE; 538 539 ms_len = sizeof(*ms) + sizeof(*cp); 540 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 541 EAP_CODE_RESPONSE, id); 542 if (resp == NULL) 543 return NULL; 544 545 ms = wpabuf_put(resp, sizeof(*ms)); 546 ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; 547 ms->mschapv2_id = req->mschapv2_id + 1; 548 WPA_PUT_BE16(ms->ms_length, ms_len); 549 cp = wpabuf_put(resp, sizeof(*cp)); 550 551 /* Encrypted-Password */ 552 if (pwhash) { 553 if (encrypt_pw_block_with_password_hash( 554 new_password, new_password_len, 555 password, cp->encr_password)) 556 goto fail; 557 } else { 558 if (new_password_encrypted_with_old_nt_password_hash( 559 new_password, new_password_len, 560 password, password_len, cp->encr_password)) 561 goto fail; 562 } 563 564 /* Encrypted-Hash */ 565 if (pwhash) { 566 u8 new_password_hash[16]; 567 if (nt_password_hash(new_password, new_password_len, 568 new_password_hash) || 569 nt_password_hash_encrypted_with_block(password, 570 new_password_hash, 571 cp->encr_hash)) 572 goto fail; 573 } else { 574 if (old_nt_password_hash_encrypted_with_new_nt_password_hash( 575 new_password, new_password_len, 576 password, password_len, cp->encr_hash)) 577 goto fail; 578 } 579 580 /* Peer-Challenge */ 581 if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) 582 goto fail; 583 584 /* Reserved, must be zero */ 585 os_memset(cp->reserved, 0, 8); 586 587 /* NT-Response */ 588 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", 589 data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); 590 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", 591 cp->peer_challenge, MSCHAPV2_CHAL_LEN); 592 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", 593 username, username_len); 594 wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", 595 new_password, new_password_len); 596 generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, 597 username, username_len, 598 new_password, new_password_len, 599 cp->nt_response); 600 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", 601 cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); 602 603 /* Authenticator response is not really needed yet, but calculate it 604 * here so that challenges need not be saved. */ 605 generate_authenticator_response(new_password, new_password_len, 606 cp->peer_challenge, 607 data->passwd_change_challenge, 608 username, username_len, 609 cp->nt_response, data->auth_response); 610 data->auth_response_valid = 1; 611 612 /* Likewise, generate master_key here since we have the needed data 613 * available. */ 614 if (nt_password_hash(new_password, new_password_len, password_hash) || 615 hash_nt_password_hash(password_hash, password_hash_hash) || 616 get_master_key(password_hash_hash, cp->nt_response, 617 data->master_key)) { 618 data->auth_response_valid = 0; 619 goto fail; 620 } 621 data->master_key_valid = 1; 622 623 /* Flags */ 624 os_memset(cp->flags, 0, 2); 625 626 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 627 "(change pw)", id, ms->mschapv2_id); 628 629 return resp; 630 631fail: 632 wpabuf_free(resp); 633 return NULL; 634#endif /* CONFIG_NO_RC4 */ 635} 636 637 638/** 639 * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message 640 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 641 * @data: Pointer to private EAP method data from eap_mschapv2_init() 642 * @ret: Return values from EAP request validation and processing 643 * @req: Pointer to EAP-MSCHAPv2 header from the request 644 * @req_len: Length of the EAP-MSCHAPv2 data 645 * @id: EAP identifier used in th erequest 646 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 647 * no reply available 648 */ 649static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, 650 struct eap_mschapv2_data *data, 651 struct eap_method_ret *ret, 652 const struct eap_mschapv2_hdr *req, 653 size_t req_len, u8 id) 654{ 655 struct wpabuf *resp; 656 const u8 *msdata = (const u8 *) (req + 1); 657 char *buf; 658 size_t len = req_len - sizeof(*req); 659 int retry = 0; 660 661 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); 662 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", 663 msdata, len); 664 /* 665 * eap_mschapv2_failure_txt() expects a nul terminated string, so we 666 * must allocate a large enough temporary buffer to create that since 667 * the received message does not include nul termination. 668 */ 669 buf = dup_binstr(msdata, len); 670 if (buf) { 671 retry = eap_mschapv2_failure_txt(sm, data, buf); 672 os_free(buf); 673 } 674 675 ret->ignore = FALSE; 676 ret->methodState = METHOD_DONE; 677 ret->decision = DECISION_FAIL; 678 ret->allowNotifications = FALSE; 679 680 if (data->prev_error == ERROR_PASSWD_EXPIRED && 681 data->passwd_change_version == 3) { 682 struct eap_peer_config *config = eap_get_config(sm); 683 if (config && config->new_password) 684 return eap_mschapv2_change_password(sm, data, ret, req, 685 id); 686 if (config && config->pending_req_new_password) 687 return NULL; 688 } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 689 /* TODO: could try to retry authentication, e.g, after having 690 * changed the username/password. In this case, EAP MS-CHAP-v2 691 * Failure Response would not be sent here. */ 692 return NULL; 693 } 694 695 /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure 696 * message. */ 697 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 698 EAP_CODE_RESPONSE, id); 699 if (resp == NULL) 700 return NULL; 701 702 wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ 703 704 return resp; 705} 706 707 708static int eap_mschapv2_check_config(struct eap_sm *sm) 709{ 710 size_t len; 711 712 if (eap_get_config_identity(sm, &len) == NULL) { 713 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); 714 eap_sm_request_identity(sm); 715 return -1; 716 } 717 718 if (eap_get_config_password(sm, &len) == NULL) { 719 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 720 eap_sm_request_password(sm); 721 return -1; 722 } 723 724 return 0; 725} 726 727 728static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, 729 const struct eap_mschapv2_hdr *ms) 730{ 731 size_t ms_len = WPA_GET_BE16(ms->ms_length); 732 733 if (ms_len == len) 734 return 0; 735 736 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " 737 "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); 738 if (sm->workaround) { 739 /* Some authentication servers use invalid ms_len, 740 * ignore it for interoperability. */ 741 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" 742 " invalid ms_len %lu (len %lu)", 743 (unsigned long) ms_len, 744 (unsigned long) len); 745 return 0; 746 } 747 748 return -1; 749} 750 751 752static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, 753 const struct wpabuf *reqData) 754{ 755 /* 756 * Store a copy of the challenge message, so that it can be processed 757 * again in case retry is allowed after a possible failure. 758 */ 759 wpabuf_free(data->prev_challenge); 760 data->prev_challenge = wpabuf_dup(reqData); 761} 762 763 764/** 765 * eap_mschapv2_process - Process an EAP-MSCHAPv2 request 766 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 767 * @priv: Pointer to private EAP method data from eap_mschapv2_init() 768 * @ret: Return values from EAP request validation and processing 769 * @reqData: EAP request to be processed (eapReqData) 770 * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 771 * no reply available 772 */ 773static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, 774 struct eap_method_ret *ret, 775 const struct wpabuf *reqData) 776{ 777 struct eap_mschapv2_data *data = priv; 778 struct eap_peer_config *config = eap_get_config(sm); 779 const struct eap_mschapv2_hdr *ms; 780 int using_prev_challenge = 0; 781 const u8 *pos; 782 size_t len; 783 u8 id; 784 785 if (eap_mschapv2_check_config(sm)) { 786 ret->ignore = TRUE; 787 return NULL; 788 } 789 790 if (config->mschapv2_retry && data->prev_challenge && 791 data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 792 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " 793 "with the previous challenge"); 794 795 reqData = data->prev_challenge; 796 using_prev_challenge = 1; 797 config->mschapv2_retry = 0; 798 } 799 800 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, 801 &len); 802 if (pos == NULL || len < sizeof(*ms) + 1) { 803 ret->ignore = TRUE; 804 return NULL; 805 } 806 807 ms = (const struct eap_mschapv2_hdr *) pos; 808 if (eap_mschapv2_check_mslen(sm, len, ms)) { 809 ret->ignore = TRUE; 810 return NULL; 811 } 812 813 id = eap_get_id(reqData); 814 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", 815 id, ms->mschapv2_id); 816 817 switch (ms->op_code) { 818 case MSCHAPV2_OP_CHALLENGE: 819 if (!using_prev_challenge) 820 eap_mschapv2_copy_challenge(data, reqData); 821 return eap_mschapv2_challenge(sm, data, ret, ms, len, id); 822 case MSCHAPV2_OP_SUCCESS: 823 return eap_mschapv2_success(sm, data, ret, ms, len, id); 824 case MSCHAPV2_OP_FAILURE: 825 return eap_mschapv2_failure(sm, data, ret, ms, len, id); 826 default: 827 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", 828 ms->op_code); 829 ret->ignore = TRUE; 830 return NULL; 831 } 832} 833 834 835static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) 836{ 837 struct eap_mschapv2_data *data = priv; 838 return data->success && data->master_key_valid; 839} 840 841 842static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 843{ 844 struct eap_mschapv2_data *data = priv; 845 u8 *key; 846 int key_len; 847 848 if (!data->master_key_valid || !data->success) 849 return NULL; 850 851 key_len = 2 * MSCHAPV2_KEY_LEN; 852 853 key = os_malloc(key_len); 854 if (key == NULL) 855 return NULL; 856 857 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., 858 * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ 859 get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); 860 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 861 MSCHAPV2_KEY_LEN, 0, 0); 862 863 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", 864 key, key_len); 865 866 *len = key_len; 867 return key; 868} 869 870 871/** 872 * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method 873 * Returns: 0 on success, -1 on failure 874 * 875 * This function is used to register EAP-MSCHAPv2 peer method into the EAP 876 * method list. 877 */ 878int eap_peer_mschapv2_register(void) 879{ 880 struct eap_method *eap; 881 882 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 883 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 884 "MSCHAPV2"); 885 if (eap == NULL) 886 return -1; 887 888 eap->init = eap_mschapv2_init; 889 eap->deinit = eap_mschapv2_deinit; 890 eap->process = eap_mschapv2_process; 891 eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; 892 eap->getKey = eap_mschapv2_getKey; 893 894 return eap_peer_method_register(eap); 895} 896