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