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