eap_server_mschapv2.c revision 1f69aa52ea2e0a73ac502565df8c666ee49cab6a
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 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 23struct 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 52struct 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 64static 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 93static 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 104static 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 148static 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 192static 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 225static 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 246static 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 288static 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 if (nt_password_hash(sm->user->password, 409 sm->user->password_len, 410 pw_hash_buf) < 0) { 411 data->state = FAILURE; 412 return; 413 } 414 pw_hash = pw_hash_buf; 415 } 416 generate_authenticator_response_pwhash( 417 pw_hash, peer_challenge, data->auth_challenge, 418 username, username_len, nt_response, 419 data->auth_response); 420 421 hash_nt_password_hash(pw_hash, pw_hash_hash); 422 get_master_key(pw_hash_hash, nt_response, data->master_key); 423 data->master_key_valid = 1; 424 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 425 data->master_key, MSCHAPV2_KEY_LEN); 426 } else { 427 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 428 expected, 24); 429 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 430 data->state = FAILURE_REQ; 431 } 432} 433 434 435static void eap_mschapv2_process_success_resp(struct eap_sm *sm, 436 struct eap_mschapv2_data *data, 437 struct wpabuf *respData) 438{ 439 struct eap_mschapv2_hdr *resp; 440 const u8 *pos; 441 size_t len; 442 443 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 444 &len); 445 if (pos == NULL || len < 1) 446 return; /* Should not happen - frame already validated */ 447 448 resp = (struct eap_mschapv2_hdr *) pos; 449 450 if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 451 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 452 " - authentication completed successfully"); 453 data->state = SUCCESS; 454 } else { 455 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 456 "Response - peer rejected authentication"); 457 data->state = FAILURE; 458 } 459} 460 461 462static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 463 struct eap_mschapv2_data *data, 464 struct wpabuf *respData) 465{ 466 struct eap_mschapv2_hdr *resp; 467 const u8 *pos; 468 size_t len; 469 470 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 471 &len); 472 if (pos == NULL || len < 1) 473 return; /* Should not happen - frame already validated */ 474 475 resp = (struct eap_mschapv2_hdr *) pos; 476 477 if (resp->op_code == MSCHAPV2_OP_FAILURE) { 478 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 479 " - authentication failed"); 480 } else { 481 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 482 "Response - authentication failed"); 483 } 484 485 data->state = FAILURE; 486} 487 488 489static void eap_mschapv2_process(struct eap_sm *sm, void *priv, 490 struct wpabuf *respData) 491{ 492 struct eap_mschapv2_data *data = priv; 493 494 if (sm->user == NULL || sm->user->password == NULL) { 495 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 496 data->state = FAILURE; 497 return; 498 } 499 500 switch (data->state) { 501 case CHALLENGE: 502 eap_mschapv2_process_response(sm, data, respData); 503 break; 504 case SUCCESS_REQ: 505 eap_mschapv2_process_success_resp(sm, data, respData); 506 break; 507 case FAILURE_REQ: 508 eap_mschapv2_process_failure_resp(sm, data, respData); 509 break; 510 default: 511 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 512 "process", data->state); 513 break; 514 } 515} 516 517 518static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 519{ 520 struct eap_mschapv2_data *data = priv; 521 return data->state == SUCCESS || data->state == FAILURE; 522} 523 524 525static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 526{ 527 struct eap_mschapv2_data *data = priv; 528 u8 *key; 529 530 if (data->state != SUCCESS || !data->master_key_valid) 531 return NULL; 532 533 *len = 2 * MSCHAPV2_KEY_LEN; 534 key = os_malloc(*len); 535 if (key == NULL) 536 return NULL; 537 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 538 get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 539 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 540 MSCHAPV2_KEY_LEN, 1, 1); 541 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 542 543 return key; 544} 545 546 547static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 548{ 549 struct eap_mschapv2_data *data = priv; 550 return data->state == SUCCESS; 551} 552 553 554int eap_server_mschapv2_register(void) 555{ 556 struct eap_method *eap; 557 int ret; 558 559 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 560 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 561 "MSCHAPV2"); 562 if (eap == NULL) 563 return -1; 564 565 eap->init = eap_mschapv2_init; 566 eap->reset = eap_mschapv2_reset; 567 eap->buildReq = eap_mschapv2_buildReq; 568 eap->check = eap_mschapv2_check; 569 eap->process = eap_mschapv2_process; 570 eap->isDone = eap_mschapv2_isDone; 571 eap->getKey = eap_mschapv2_getKey; 572 eap->isSuccess = eap_mschapv2_isSuccess; 573 574 ret = eap_server_method_register(eap); 575 if (ret) 576 eap_server_method_free(eap); 577 return ret; 578} 579