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