1/* 2 * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server 3 * Copyright (c) 2004-2007, 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 9#include "includes.h" 10 11#include "common.h" 12#include "crypto/ms_funcs.h" 13#include "crypto/random.h" 14#include "eap_i.h" 15 16 17struct eap_mschapv2_hdr { 18 u8 op_code; /* MSCHAPV2_OP_* */ 19 u8 mschapv2_id; /* must be changed for challenges, but not for 20 * success/failure */ 21 u8 ms_length[2]; /* Note: misaligned; length - 5 */ 22 /* followed by data */ 23} STRUCT_PACKED; 24 25#define MSCHAPV2_OP_CHALLENGE 1 26#define MSCHAPV2_OP_RESPONSE 2 27#define MSCHAPV2_OP_SUCCESS 3 28#define MSCHAPV2_OP_FAILURE 4 29#define MSCHAPV2_OP_CHANGE_PASSWORD 7 30 31#define MSCHAPV2_RESP_LEN 49 32 33#define ERROR_RESTRICTED_LOGON_HOURS 646 34#define ERROR_ACCT_DISABLED 647 35#define ERROR_PASSWD_EXPIRED 648 36#define ERROR_NO_DIALIN_PERMISSION 649 37#define ERROR_AUTHENTICATION_FAILURE 691 38#define ERROR_CHANGING_PASSWORD 709 39 40#define PASSWD_CHANGE_CHAL_LEN 16 41#define MSCHAPV2_KEY_LEN 16 42 43 44#define CHALLENGE_LEN 16 45 46struct eap_mschapv2_data { 47 u8 auth_challenge[CHALLENGE_LEN]; 48 int auth_challenge_from_tls; 49 u8 *peer_challenge; 50 u8 auth_response[20]; 51 enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; 52 u8 resp_mschapv2_id; 53 u8 master_key[16]; 54 int master_key_valid; 55}; 56 57 58static void * eap_mschapv2_init(struct eap_sm *sm) 59{ 60 struct eap_mschapv2_data *data; 61 62 data = os_zalloc(sizeof(*data)); 63 if (data == NULL) 64 return NULL; 65 data->state = CHALLENGE; 66 67 if (sm->auth_challenge) { 68 os_memcpy(data->auth_challenge, sm->auth_challenge, 69 CHALLENGE_LEN); 70 data->auth_challenge_from_tls = 1; 71 } 72 73 if (sm->peer_challenge) { 74 data->peer_challenge = os_memdup(sm->peer_challenge, 75 CHALLENGE_LEN); 76 if (data->peer_challenge == NULL) { 77 os_free(data); 78 return NULL; 79 } 80 } 81 82 return data; 83} 84 85 86static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) 87{ 88 struct eap_mschapv2_data *data = priv; 89 if (data == NULL) 90 return; 91 92 os_free(data->peer_challenge); 93 bin_clear_free(data, sizeof(*data)); 94} 95 96 97static struct wpabuf * eap_mschapv2_build_challenge( 98 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 99{ 100 struct wpabuf *req; 101 struct eap_mschapv2_hdr *ms; 102 size_t ms_len; 103 104 if (!data->auth_challenge_from_tls && 105 random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { 106 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 107 "data"); 108 data->state = FAILURE; 109 return NULL; 110 } 111 112 ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; 113 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 114 EAP_CODE_REQUEST, id); 115 if (req == NULL) { 116 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 117 " for request"); 118 data->state = FAILURE; 119 return NULL; 120 } 121 122 ms = wpabuf_put(req, sizeof(*ms)); 123 ms->op_code = MSCHAPV2_OP_CHALLENGE; 124 ms->mschapv2_id = id; 125 WPA_PUT_BE16(ms->ms_length, ms_len); 126 127 wpabuf_put_u8(req, CHALLENGE_LEN); 128 if (!data->auth_challenge_from_tls) 129 wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 130 else 131 wpabuf_put(req, CHALLENGE_LEN); 132 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 133 data->auth_challenge, CHALLENGE_LEN); 134 wpabuf_put_data(req, sm->server_id, sm->server_id_len); 135 136 return req; 137} 138 139 140static struct wpabuf * eap_mschapv2_build_success_req( 141 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 142{ 143 struct wpabuf *req; 144 struct eap_mschapv2_hdr *ms; 145 u8 *msg; 146 char *message = "OK"; 147 size_t ms_len; 148 149 ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 150 os_strlen(message); 151 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 152 EAP_CODE_REQUEST, id); 153 if (req == NULL) { 154 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 155 " for request"); 156 data->state = FAILURE; 157 return NULL; 158 } 159 160 ms = wpabuf_put(req, sizeof(*ms)); 161 ms->op_code = MSCHAPV2_OP_SUCCESS; 162 ms->mschapv2_id = data->resp_mschapv2_id; 163 WPA_PUT_BE16(ms->ms_length, ms_len); 164 msg = (u8 *) (ms + 1); 165 166 wpabuf_put_u8(req, 'S'); 167 wpabuf_put_u8(req, '='); 168 wpa_snprintf_hex_uppercase( 169 wpabuf_put(req, sizeof(data->auth_response) * 2), 170 sizeof(data->auth_response) * 2 + 1, 171 data->auth_response, sizeof(data->auth_response)); 172 wpabuf_put_u8(req, ' '); 173 wpabuf_put_u8(req, 'M'); 174 wpabuf_put_u8(req, '='); 175 wpabuf_put_data(req, message, os_strlen(message)); 176 177 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 178 msg, ms_len - sizeof(*ms)); 179 180 return req; 181} 182 183 184static struct wpabuf * eap_mschapv2_build_failure_req( 185 struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 186{ 187 struct wpabuf *req; 188 struct eap_mschapv2_hdr *ms; 189 char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 190 "M=FAILED"; 191 size_t ms_len; 192 193 ms_len = sizeof(*ms) + os_strlen(message); 194 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 195 EAP_CODE_REQUEST, id); 196 if (req == NULL) { 197 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 198 " for request"); 199 data->state = FAILURE; 200 return NULL; 201 } 202 203 ms = wpabuf_put(req, sizeof(*ms)); 204 ms->op_code = MSCHAPV2_OP_FAILURE; 205 ms->mschapv2_id = data->resp_mschapv2_id; 206 WPA_PUT_BE16(ms->ms_length, ms_len); 207 208 wpabuf_put_data(req, message, os_strlen(message)); 209 210 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 211 (u8 *) message, os_strlen(message)); 212 213 return req; 214} 215 216 217static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 218 u8 id) 219{ 220 struct eap_mschapv2_data *data = priv; 221 222 switch (data->state) { 223 case CHALLENGE: 224 return eap_mschapv2_build_challenge(sm, data, id); 225 case SUCCESS_REQ: 226 return eap_mschapv2_build_success_req(sm, data, id); 227 case FAILURE_REQ: 228 return eap_mschapv2_build_failure_req(sm, data, id); 229 default: 230 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 231 "buildReq", data->state); 232 break; 233 } 234 return NULL; 235} 236 237 238static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, 239 struct wpabuf *respData) 240{ 241 struct eap_mschapv2_data *data = priv; 242 struct eap_mschapv2_hdr *resp; 243 const u8 *pos; 244 size_t len; 245 246 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 247 &len); 248 if (pos == NULL || len < 1) { 249 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 250 return TRUE; 251 } 252 253 resp = (struct eap_mschapv2_hdr *) pos; 254 if (data->state == CHALLENGE && 255 resp->op_code != MSCHAPV2_OP_RESPONSE) { 256 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 257 "ignore op %d", resp->op_code); 258 return TRUE; 259 } 260 261 if (data->state == SUCCESS_REQ && 262 resp->op_code != MSCHAPV2_OP_SUCCESS && 263 resp->op_code != MSCHAPV2_OP_FAILURE) { 264 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 265 "Failure - ignore op %d", resp->op_code); 266 return TRUE; 267 } 268 269 if (data->state == FAILURE_REQ && 270 resp->op_code != MSCHAPV2_OP_FAILURE) { 271 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 272 "- ignore op %d", resp->op_code); 273 return TRUE; 274 } 275 276 return FALSE; 277} 278 279 280static void eap_mschapv2_process_response(struct eap_sm *sm, 281 struct eap_mschapv2_data *data, 282 struct wpabuf *respData) 283{ 284 struct eap_mschapv2_hdr *resp; 285 const u8 *pos, *end, *peer_challenge, *nt_response, *name; 286 u8 flags; 287 size_t len, name_len, i; 288 u8 expected[24]; 289 const u8 *username, *user; 290 size_t username_len, user_len; 291 int res; 292 char *buf; 293 294 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 295 &len); 296 if (pos == NULL || len < 1) 297 return; /* Should not happen - frame already validated */ 298 299 end = pos + len; 300 resp = (struct eap_mschapv2_hdr *) pos; 301 pos = (u8 *) (resp + 1); 302 303 if (len < sizeof(*resp) + 1 + 49 || 304 resp->op_code != MSCHAPV2_OP_RESPONSE || 305 pos[0] != 49) { 306 wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 307 respData); 308 data->state = FAILURE; 309 return; 310 } 311 data->resp_mschapv2_id = resp->mschapv2_id; 312 pos++; 313 peer_challenge = pos; 314 pos += 16 + 8; 315 nt_response = pos; 316 pos += 24; 317 flags = *pos++; 318 name = pos; 319 name_len = end - name; 320 321 if (data->peer_challenge) { 322 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 323 "Peer-Challenge"); 324 peer_challenge = data->peer_challenge; 325 } 326 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 327 peer_challenge, 16); 328 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 329 wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 330 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 331 332 buf = os_malloc(name_len * 4 + 1); 333 if (buf) { 334 printf_encode(buf, name_len * 4 + 1, name, name_len); 335 eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); 336 os_free(buf); 337 } 338 339 /* MSCHAPv2 does not include optional domain name in the 340 * challenge-response calculation, so remove domain prefix 341 * (if present). */ 342 username = sm->identity; 343 username_len = sm->identity_len; 344 for (i = 0; i < username_len; i++) { 345 if (username[i] == '\\') { 346 username_len -= i + 1; 347 username += i + 1; 348 break; 349 } 350 } 351 352 user = name; 353 user_len = name_len; 354 for (i = 0; i < user_len; i++) { 355 if (user[i] == '\\') { 356 user_len -= i + 1; 357 user += i + 1; 358 break; 359 } 360 } 361 362#ifdef CONFIG_TESTING_OPTIONS 363 { 364 u8 challenge[8]; 365 366 if (challenge_hash(peer_challenge, data->auth_challenge, 367 username, username_len, challenge) == 0) { 368 eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", 369 username, username_len, 370 challenge, nt_response); 371 } 372 } 373#endif /* CONFIG_TESTING_OPTIONS */ 374 375 if (username_len != user_len || 376 os_memcmp(username, user, username_len) != 0) { 377 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 378 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 379 "name", username, username_len); 380 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 381 "name", user, user_len); 382 data->state = FAILURE; 383 return; 384 } 385 386 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 387 username, username_len); 388 389 if (sm->user->password_hash) { 390 res = generate_nt_response_pwhash(data->auth_challenge, 391 peer_challenge, 392 username, username_len, 393 sm->user->password, 394 expected); 395 } else { 396 res = generate_nt_response(data->auth_challenge, 397 peer_challenge, 398 username, username_len, 399 sm->user->password, 400 sm->user->password_len, 401 expected); 402 } 403 if (res) { 404 data->state = FAILURE; 405 return; 406 } 407 408 if (os_memcmp_const(nt_response, expected, 24) == 0) { 409 const u8 *pw_hash; 410 u8 pw_hash_buf[16], pw_hash_hash[16]; 411 412 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 413 data->state = SUCCESS_REQ; 414 415 /* Authenticator response is not really needed yet, but 416 * calculate it here so that peer_challenge and username need 417 * not be saved. */ 418 if (sm->user->password_hash) { 419 pw_hash = sm->user->password; 420 } else { 421 if (nt_password_hash(sm->user->password, 422 sm->user->password_len, 423 pw_hash_buf) < 0) { 424 data->state = FAILURE; 425 return; 426 } 427 pw_hash = pw_hash_buf; 428 } 429 if (generate_authenticator_response_pwhash( 430 pw_hash, peer_challenge, data->auth_challenge, 431 username, username_len, nt_response, 432 data->auth_response) < 0 || 433 hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || 434 get_master_key(pw_hash_hash, nt_response, 435 data->master_key)) { 436 data->state = FAILURE; 437 return; 438 } 439 data->master_key_valid = 1; 440 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 441 data->master_key, MSCHAPV2_KEY_LEN); 442 } else { 443 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 444 expected, 24); 445 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 446 data->state = FAILURE_REQ; 447 } 448} 449 450 451static void eap_mschapv2_process_success_resp(struct eap_sm *sm, 452 struct eap_mschapv2_data *data, 453 struct wpabuf *respData) 454{ 455 struct eap_mschapv2_hdr *resp; 456 const u8 *pos; 457 size_t len; 458 459 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 460 &len); 461 if (pos == NULL || len < 1) 462 return; /* Should not happen - frame already validated */ 463 464 resp = (struct eap_mschapv2_hdr *) pos; 465 466 if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 467 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 468 " - authentication completed successfully"); 469 data->state = SUCCESS; 470 } else { 471 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 472 "Response - peer rejected authentication"); 473 data->state = FAILURE; 474 } 475} 476 477 478static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 479 struct eap_mschapv2_data *data, 480 struct wpabuf *respData) 481{ 482 struct eap_mschapv2_hdr *resp; 483 const u8 *pos; 484 size_t len; 485 486 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 487 &len); 488 if (pos == NULL || len < 1) 489 return; /* Should not happen - frame already validated */ 490 491 resp = (struct eap_mschapv2_hdr *) pos; 492 493 if (resp->op_code == MSCHAPV2_OP_FAILURE) { 494 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 495 " - authentication failed"); 496 } else { 497 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 498 "Response - authentication failed"); 499 } 500 501 data->state = FAILURE; 502} 503 504 505static void eap_mschapv2_process(struct eap_sm *sm, void *priv, 506 struct wpabuf *respData) 507{ 508 struct eap_mschapv2_data *data = priv; 509 510 if (sm->user == NULL || sm->user->password == NULL) { 511 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 512 data->state = FAILURE; 513 return; 514 } 515 516 switch (data->state) { 517 case CHALLENGE: 518 eap_mschapv2_process_response(sm, data, respData); 519 break; 520 case SUCCESS_REQ: 521 eap_mschapv2_process_success_resp(sm, data, respData); 522 break; 523 case FAILURE_REQ: 524 eap_mschapv2_process_failure_resp(sm, data, respData); 525 break; 526 default: 527 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 528 "process", data->state); 529 break; 530 } 531} 532 533 534static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 535{ 536 struct eap_mschapv2_data *data = priv; 537 return data->state == SUCCESS || data->state == FAILURE; 538} 539 540 541static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 542{ 543 struct eap_mschapv2_data *data = priv; 544 u8 *key; 545 546 if (data->state != SUCCESS || !data->master_key_valid) 547 return NULL; 548 549 *len = 2 * MSCHAPV2_KEY_LEN; 550 key = os_malloc(*len); 551 if (key == NULL) 552 return NULL; 553 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 554 get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 555 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 556 MSCHAPV2_KEY_LEN, 1, 1); 557 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 558 559 return key; 560} 561 562 563static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 564{ 565 struct eap_mschapv2_data *data = priv; 566 return data->state == SUCCESS; 567} 568 569 570int eap_server_mschapv2_register(void) 571{ 572 struct eap_method *eap; 573 574 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 575 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 576 "MSCHAPV2"); 577 if (eap == NULL) 578 return -1; 579 580 eap->init = eap_mschapv2_init; 581 eap->reset = eap_mschapv2_reset; 582 eap->buildReq = eap_mschapv2_buildReq; 583 eap->check = eap_mschapv2_check; 584 eap->process = eap_mschapv2_process; 585 eap->isDone = eap_mschapv2_isDone; 586 eap->getKey = eap_mschapv2_getKey; 587 eap->isSuccess = eap_mschapv2_isSuccess; 588 589 return eap_server_method_register(eap); 590} 591