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