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