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 "eap_i.h" 19#include "ms_funcs.h" 20 21 22struct 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 51struct 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 63static 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 92static 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 103static 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 147static 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 191static 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 224static 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 245static 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 287static 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 299 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 300 &len); 301 if (pos == NULL || len < 1) 302 return; /* Should not happen - frame already validated */ 303 304 end = pos + len; 305 resp = (struct eap_mschapv2_hdr *) pos; 306 pos = (u8 *) (resp + 1); 307 308 if (len < sizeof(*resp) + 1 + 49 || 309 resp->op_code != MSCHAPV2_OP_RESPONSE || 310 pos[0] != 49) { 311 wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 312 respData); 313 data->state = FAILURE; 314 return; 315 } 316 data->resp_mschapv2_id = resp->mschapv2_id; 317 pos++; 318 peer_challenge = pos; 319 pos += 16 + 8; 320 nt_response = pos; 321 pos += 24; 322 flags = *pos++; 323 name = pos; 324 name_len = end - name; 325 326 if (data->peer_challenge) { 327 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 328 "Peer-Challenge"); 329 peer_challenge = data->peer_challenge; 330 } 331 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 332 peer_challenge, 16); 333 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 334 wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 335 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 336 337 /* MSCHAPv2 does not include optional domain name in the 338 * challenge-response calculation, so remove domain prefix 339 * (if present). */ 340 username = sm->identity; 341 username_len = sm->identity_len; 342 for (i = 0; i < username_len; i++) { 343 if (username[i] == '\\') { 344 username_len -= i + 1; 345 username += i + 1; 346 break; 347 } 348 } 349 350 user = name; 351 user_len = name_len; 352 for (i = 0; i < user_len; i++) { 353 if (user[i] == '\\') { 354 user_len -= i + 1; 355 user += i + 1; 356 break; 357 } 358 } 359 360 if (username_len != user_len || 361 os_memcmp(username, user, username_len) != 0) { 362 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 363 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 364 "name", username, username_len); 365 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 366 "name", user, user_len); 367 data->state = FAILURE; 368 return; 369 } 370 371 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 372 username, username_len); 373 374 if (sm->user->password_hash) { 375 generate_nt_response_pwhash(data->auth_challenge, 376 peer_challenge, 377 username, username_len, 378 sm->user->password, 379 expected); 380 } else { 381 generate_nt_response(data->auth_challenge, peer_challenge, 382 username, username_len, 383 sm->user->password, 384 sm->user->password_len, 385 expected); 386 } 387 388 if (os_memcmp(nt_response, expected, 24) == 0) { 389 const u8 *pw_hash; 390 u8 pw_hash_buf[16], pw_hash_hash[16]; 391 392 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 393 data->state = SUCCESS_REQ; 394 395 /* Authenticator response is not really needed yet, but 396 * calculate it here so that peer_challenge and username need 397 * not be saved. */ 398 if (sm->user->password_hash) { 399 pw_hash = sm->user->password; 400 } else { 401 nt_password_hash(sm->user->password, 402 sm->user->password_len, 403 pw_hash_buf); 404 pw_hash = pw_hash_buf; 405 } 406 generate_authenticator_response_pwhash( 407 pw_hash, peer_challenge, data->auth_challenge, 408 username, username_len, nt_response, 409 data->auth_response); 410 411 hash_nt_password_hash(pw_hash, pw_hash_hash); 412 get_master_key(pw_hash_hash, nt_response, data->master_key); 413 data->master_key_valid = 1; 414 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 415 data->master_key, MSCHAPV2_KEY_LEN); 416 } else { 417 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 418 expected, 24); 419 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 420 data->state = FAILURE_REQ; 421 } 422} 423 424 425static void eap_mschapv2_process_success_resp(struct eap_sm *sm, 426 struct eap_mschapv2_data *data, 427 struct wpabuf *respData) 428{ 429 struct eap_mschapv2_hdr *resp; 430 const u8 *pos; 431 size_t len; 432 433 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 434 &len); 435 if (pos == NULL || len < 1) 436 return; /* Should not happen - frame already validated */ 437 438 resp = (struct eap_mschapv2_hdr *) pos; 439 440 if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 441 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 442 " - authentication completed successfully"); 443 data->state = SUCCESS; 444 } else { 445 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 446 "Response - peer rejected authentication"); 447 data->state = FAILURE; 448 } 449} 450 451 452static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 453 struct eap_mschapv2_data *data, 454 struct wpabuf *respData) 455{ 456 struct eap_mschapv2_hdr *resp; 457 const u8 *pos; 458 size_t len; 459 460 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 461 &len); 462 if (pos == NULL || len < 1) 463 return; /* Should not happen - frame already validated */ 464 465 resp = (struct eap_mschapv2_hdr *) pos; 466 467 if (resp->op_code == MSCHAPV2_OP_FAILURE) { 468 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 469 " - authentication failed"); 470 } else { 471 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 472 "Response - authentication failed"); 473 } 474 475 data->state = FAILURE; 476} 477 478 479static void eap_mschapv2_process(struct eap_sm *sm, void *priv, 480 struct wpabuf *respData) 481{ 482 struct eap_mschapv2_data *data = priv; 483 484 if (sm->user == NULL || sm->user->password == NULL) { 485 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 486 data->state = FAILURE; 487 return; 488 } 489 490 switch (data->state) { 491 case CHALLENGE: 492 eap_mschapv2_process_response(sm, data, respData); 493 break; 494 case SUCCESS_REQ: 495 eap_mschapv2_process_success_resp(sm, data, respData); 496 break; 497 case FAILURE_REQ: 498 eap_mschapv2_process_failure_resp(sm, data, respData); 499 break; 500 default: 501 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 502 "process", data->state); 503 break; 504 } 505} 506 507 508static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 509{ 510 struct eap_mschapv2_data *data = priv; 511 return data->state == SUCCESS || data->state == FAILURE; 512} 513 514 515static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 516{ 517 struct eap_mschapv2_data *data = priv; 518 u8 *key; 519 520 if (data->state != SUCCESS || !data->master_key_valid) 521 return NULL; 522 523 *len = 2 * MSCHAPV2_KEY_LEN; 524 key = os_malloc(*len); 525 if (key == NULL) 526 return NULL; 527 /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 528 get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 529 get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 530 MSCHAPV2_KEY_LEN, 1, 1); 531 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 532 533 return key; 534} 535 536 537static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 538{ 539 struct eap_mschapv2_data *data = priv; 540 return data->state == SUCCESS; 541} 542 543 544int eap_server_mschapv2_register(void) 545{ 546 struct eap_method *eap; 547 int ret; 548 549 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 550 EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 551 "MSCHAPV2"); 552 if (eap == NULL) 553 return -1; 554 555 eap->init = eap_mschapv2_init; 556 eap->reset = eap_mschapv2_reset; 557 eap->buildReq = eap_mschapv2_buildReq; 558 eap->check = eap_mschapv2_check; 559 eap->process = eap_mschapv2_process; 560 eap->isDone = eap_mschapv2_isDone; 561 eap->getKey = eap_mschapv2_getKey; 562 eap->isSuccess = eap_mschapv2_isSuccess; 563 564 ret = eap_server_method_register(eap); 565 if (ret) 566 eap_server_method_free(eap); 567 return ret; 568} 569