1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server 3214501Srpaulo * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12214501Srpaulo#include "crypto/ms_funcs.h" 13252726Srpaulo#include "crypto/random.h" 14214501Srpaulo#include "eap_i.h" 15214501Srpaulo 16214501Srpaulo 17214501Srpaulostruct eap_mschapv2_hdr { 18214501Srpaulo u8 op_code; /* MSCHAPV2_OP_* */ 19214501Srpaulo u8 mschapv2_id; /* must be changed for challenges, but not for 20214501Srpaulo * success/failure */ 21214501Srpaulo u8 ms_length[2]; /* Note: misaligned; length - 5 */ 22214501Srpaulo /* followed by data */ 23214501Srpaulo} STRUCT_PACKED; 24214501Srpaulo 25214501Srpaulo#define MSCHAPV2_OP_CHALLENGE 1 26214501Srpaulo#define MSCHAPV2_OP_RESPONSE 2 27214501Srpaulo#define MSCHAPV2_OP_SUCCESS 3 28214501Srpaulo#define MSCHAPV2_OP_FAILURE 4 29214501Srpaulo#define MSCHAPV2_OP_CHANGE_PASSWORD 7 30214501Srpaulo 31214501Srpaulo#define MSCHAPV2_RESP_LEN 49 32214501Srpaulo 33214501Srpaulo#define ERROR_RESTRICTED_LOGON_HOURS 646 34214501Srpaulo#define ERROR_ACCT_DISABLED 647 35214501Srpaulo#define ERROR_PASSWD_EXPIRED 648 36214501Srpaulo#define ERROR_NO_DIALIN_PERMISSION 649 37214501Srpaulo#define ERROR_AUTHENTICATION_FAILURE 691 38214501Srpaulo#define ERROR_CHANGING_PASSWORD 709 39214501Srpaulo 40214501Srpaulo#define PASSWD_CHANGE_CHAL_LEN 16 41214501Srpaulo#define MSCHAPV2_KEY_LEN 16 42214501Srpaulo 43214501Srpaulo 44214501Srpaulo#define CHALLENGE_LEN 16 45214501Srpaulo 46214501Srpaulostruct eap_mschapv2_data { 47214501Srpaulo u8 auth_challenge[CHALLENGE_LEN]; 48214501Srpaulo int auth_challenge_from_tls; 49214501Srpaulo u8 *peer_challenge; 50214501Srpaulo u8 auth_response[20]; 51214501Srpaulo enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; 52214501Srpaulo u8 resp_mschapv2_id; 53214501Srpaulo u8 master_key[16]; 54214501Srpaulo int master_key_valid; 55214501Srpaulo}; 56214501Srpaulo 57214501Srpaulo 58214501Srpaulostatic void * eap_mschapv2_init(struct eap_sm *sm) 59214501Srpaulo{ 60214501Srpaulo struct eap_mschapv2_data *data; 61214501Srpaulo 62214501Srpaulo data = os_zalloc(sizeof(*data)); 63214501Srpaulo if (data == NULL) 64214501Srpaulo return NULL; 65214501Srpaulo data->state = CHALLENGE; 66214501Srpaulo 67214501Srpaulo if (sm->auth_challenge) { 68214501Srpaulo os_memcpy(data->auth_challenge, sm->auth_challenge, 69214501Srpaulo CHALLENGE_LEN); 70214501Srpaulo data->auth_challenge_from_tls = 1; 71214501Srpaulo } 72214501Srpaulo 73214501Srpaulo if (sm->peer_challenge) { 74346981Scy data->peer_challenge = os_memdup(sm->peer_challenge, 75346981Scy CHALLENGE_LEN); 76214501Srpaulo if (data->peer_challenge == NULL) { 77214501Srpaulo os_free(data); 78214501Srpaulo return NULL; 79214501Srpaulo } 80214501Srpaulo } 81214501Srpaulo 82214501Srpaulo return data; 83214501Srpaulo} 84214501Srpaulo 85214501Srpaulo 86214501Srpaulostatic void eap_mschapv2_reset(struct eap_sm *sm, void *priv) 87214501Srpaulo{ 88214501Srpaulo struct eap_mschapv2_data *data = priv; 89214501Srpaulo if (data == NULL) 90214501Srpaulo return; 91214501Srpaulo 92214501Srpaulo os_free(data->peer_challenge); 93281806Srpaulo bin_clear_free(data, sizeof(*data)); 94214501Srpaulo} 95214501Srpaulo 96214501Srpaulo 97214501Srpaulostatic struct wpabuf * eap_mschapv2_build_challenge( 98214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 99214501Srpaulo{ 100214501Srpaulo struct wpabuf *req; 101214501Srpaulo struct eap_mschapv2_hdr *ms; 102214501Srpaulo size_t ms_len; 103214501Srpaulo 104214501Srpaulo if (!data->auth_challenge_from_tls && 105252726Srpaulo random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { 106214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 107214501Srpaulo "data"); 108214501Srpaulo data->state = FAILURE; 109214501Srpaulo return NULL; 110214501Srpaulo } 111214501Srpaulo 112281806Srpaulo ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; 113214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 114214501Srpaulo EAP_CODE_REQUEST, id); 115214501Srpaulo if (req == NULL) { 116214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 117214501Srpaulo " for request"); 118214501Srpaulo data->state = FAILURE; 119214501Srpaulo return NULL; 120214501Srpaulo } 121214501Srpaulo 122214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 123214501Srpaulo ms->op_code = MSCHAPV2_OP_CHALLENGE; 124214501Srpaulo ms->mschapv2_id = id; 125214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 126214501Srpaulo 127214501Srpaulo wpabuf_put_u8(req, CHALLENGE_LEN); 128214501Srpaulo if (!data->auth_challenge_from_tls) 129214501Srpaulo wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 130214501Srpaulo else 131214501Srpaulo wpabuf_put(req, CHALLENGE_LEN); 132214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 133214501Srpaulo data->auth_challenge, CHALLENGE_LEN); 134281806Srpaulo wpabuf_put_data(req, sm->server_id, sm->server_id_len); 135214501Srpaulo 136214501Srpaulo return req; 137214501Srpaulo} 138214501Srpaulo 139214501Srpaulo 140214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req( 141214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 142214501Srpaulo{ 143214501Srpaulo struct wpabuf *req; 144214501Srpaulo struct eap_mschapv2_hdr *ms; 145214501Srpaulo u8 *msg; 146214501Srpaulo char *message = "OK"; 147214501Srpaulo size_t ms_len; 148214501Srpaulo 149214501Srpaulo ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 150214501Srpaulo os_strlen(message); 151214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 152214501Srpaulo EAP_CODE_REQUEST, id); 153214501Srpaulo if (req == NULL) { 154214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 155214501Srpaulo " for request"); 156214501Srpaulo data->state = FAILURE; 157214501Srpaulo return NULL; 158214501Srpaulo } 159214501Srpaulo 160214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 161214501Srpaulo ms->op_code = MSCHAPV2_OP_SUCCESS; 162214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 163214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 164214501Srpaulo msg = (u8 *) (ms + 1); 165214501Srpaulo 166214501Srpaulo wpabuf_put_u8(req, 'S'); 167214501Srpaulo wpabuf_put_u8(req, '='); 168214501Srpaulo wpa_snprintf_hex_uppercase( 169214501Srpaulo wpabuf_put(req, sizeof(data->auth_response) * 2), 170214501Srpaulo sizeof(data->auth_response) * 2 + 1, 171214501Srpaulo data->auth_response, sizeof(data->auth_response)); 172214501Srpaulo wpabuf_put_u8(req, ' '); 173214501Srpaulo wpabuf_put_u8(req, 'M'); 174214501Srpaulo wpabuf_put_u8(req, '='); 175214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 176214501Srpaulo 177214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 178214501Srpaulo msg, ms_len - sizeof(*ms)); 179214501Srpaulo 180214501Srpaulo return req; 181214501Srpaulo} 182214501Srpaulo 183214501Srpaulo 184214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req( 185214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 186214501Srpaulo{ 187214501Srpaulo struct wpabuf *req; 188214501Srpaulo struct eap_mschapv2_hdr *ms; 189214501Srpaulo char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 190214501Srpaulo "M=FAILED"; 191214501Srpaulo size_t ms_len; 192214501Srpaulo 193214501Srpaulo ms_len = sizeof(*ms) + os_strlen(message); 194214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 195214501Srpaulo EAP_CODE_REQUEST, id); 196214501Srpaulo if (req == NULL) { 197214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 198214501Srpaulo " for request"); 199214501Srpaulo data->state = FAILURE; 200214501Srpaulo return NULL; 201214501Srpaulo } 202214501Srpaulo 203214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 204214501Srpaulo ms->op_code = MSCHAPV2_OP_FAILURE; 205214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 206214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 207214501Srpaulo 208214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 209214501Srpaulo 210214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 211214501Srpaulo (u8 *) message, os_strlen(message)); 212214501Srpaulo 213214501Srpaulo return req; 214214501Srpaulo} 215214501Srpaulo 216214501Srpaulo 217214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 218214501Srpaulo u8 id) 219214501Srpaulo{ 220214501Srpaulo struct eap_mschapv2_data *data = priv; 221214501Srpaulo 222214501Srpaulo switch (data->state) { 223214501Srpaulo case CHALLENGE: 224214501Srpaulo return eap_mschapv2_build_challenge(sm, data, id); 225214501Srpaulo case SUCCESS_REQ: 226214501Srpaulo return eap_mschapv2_build_success_req(sm, data, id); 227214501Srpaulo case FAILURE_REQ: 228214501Srpaulo return eap_mschapv2_build_failure_req(sm, data, id); 229214501Srpaulo default: 230214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 231214501Srpaulo "buildReq", data->state); 232214501Srpaulo break; 233214501Srpaulo } 234214501Srpaulo return NULL; 235214501Srpaulo} 236214501Srpaulo 237214501Srpaulo 238214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, 239214501Srpaulo struct wpabuf *respData) 240214501Srpaulo{ 241214501Srpaulo struct eap_mschapv2_data *data = priv; 242214501Srpaulo struct eap_mschapv2_hdr *resp; 243214501Srpaulo const u8 *pos; 244214501Srpaulo size_t len; 245214501Srpaulo 246214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 247214501Srpaulo &len); 248214501Srpaulo if (pos == NULL || len < 1) { 249214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 250214501Srpaulo return TRUE; 251214501Srpaulo } 252214501Srpaulo 253214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 254214501Srpaulo if (data->state == CHALLENGE && 255214501Srpaulo resp->op_code != MSCHAPV2_OP_RESPONSE) { 256214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 257214501Srpaulo "ignore op %d", resp->op_code); 258214501Srpaulo return TRUE; 259214501Srpaulo } 260214501Srpaulo 261214501Srpaulo if (data->state == SUCCESS_REQ && 262214501Srpaulo resp->op_code != MSCHAPV2_OP_SUCCESS && 263214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 264214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 265214501Srpaulo "Failure - ignore op %d", resp->op_code); 266214501Srpaulo return TRUE; 267214501Srpaulo } 268214501Srpaulo 269214501Srpaulo if (data->state == FAILURE_REQ && 270214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 271214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 272214501Srpaulo "- ignore op %d", resp->op_code); 273214501Srpaulo return TRUE; 274214501Srpaulo } 275214501Srpaulo 276214501Srpaulo return FALSE; 277214501Srpaulo} 278214501Srpaulo 279214501Srpaulo 280214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm, 281214501Srpaulo struct eap_mschapv2_data *data, 282214501Srpaulo struct wpabuf *respData) 283214501Srpaulo{ 284214501Srpaulo struct eap_mschapv2_hdr *resp; 285214501Srpaulo const u8 *pos, *end, *peer_challenge, *nt_response, *name; 286214501Srpaulo u8 flags; 287214501Srpaulo size_t len, name_len, i; 288214501Srpaulo u8 expected[24]; 289214501Srpaulo const u8 *username, *user; 290214501Srpaulo size_t username_len, user_len; 291214501Srpaulo int res; 292281806Srpaulo char *buf; 293214501Srpaulo 294214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 295214501Srpaulo &len); 296214501Srpaulo if (pos == NULL || len < 1) 297214501Srpaulo return; /* Should not happen - frame already validated */ 298214501Srpaulo 299214501Srpaulo end = pos + len; 300214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 301214501Srpaulo pos = (u8 *) (resp + 1); 302214501Srpaulo 303214501Srpaulo if (len < sizeof(*resp) + 1 + 49 || 304214501Srpaulo resp->op_code != MSCHAPV2_OP_RESPONSE || 305214501Srpaulo pos[0] != 49) { 306214501Srpaulo wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 307214501Srpaulo respData); 308214501Srpaulo data->state = FAILURE; 309214501Srpaulo return; 310214501Srpaulo } 311214501Srpaulo data->resp_mschapv2_id = resp->mschapv2_id; 312214501Srpaulo pos++; 313214501Srpaulo peer_challenge = pos; 314214501Srpaulo pos += 16 + 8; 315214501Srpaulo nt_response = pos; 316214501Srpaulo pos += 24; 317214501Srpaulo flags = *pos++; 318214501Srpaulo name = pos; 319214501Srpaulo name_len = end - name; 320214501Srpaulo 321214501Srpaulo if (data->peer_challenge) { 322214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 323214501Srpaulo "Peer-Challenge"); 324214501Srpaulo peer_challenge = data->peer_challenge; 325214501Srpaulo } 326214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 327214501Srpaulo peer_challenge, 16); 328214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 329214501Srpaulo wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 330214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 331214501Srpaulo 332281806Srpaulo buf = os_malloc(name_len * 4 + 1); 333281806Srpaulo if (buf) { 334281806Srpaulo printf_encode(buf, name_len * 4 + 1, name, name_len); 335281806Srpaulo eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); 336281806Srpaulo os_free(buf); 337281806Srpaulo } 338281806Srpaulo 339214501Srpaulo /* MSCHAPv2 does not include optional domain name in the 340214501Srpaulo * challenge-response calculation, so remove domain prefix 341214501Srpaulo * (if present). */ 342214501Srpaulo username = sm->identity; 343214501Srpaulo username_len = sm->identity_len; 344214501Srpaulo for (i = 0; i < username_len; i++) { 345214501Srpaulo if (username[i] == '\\') { 346214501Srpaulo username_len -= i + 1; 347214501Srpaulo username += i + 1; 348214501Srpaulo break; 349214501Srpaulo } 350214501Srpaulo } 351214501Srpaulo 352214501Srpaulo user = name; 353214501Srpaulo user_len = name_len; 354214501Srpaulo for (i = 0; i < user_len; i++) { 355214501Srpaulo if (user[i] == '\\') { 356214501Srpaulo user_len -= i + 1; 357214501Srpaulo user += i + 1; 358214501Srpaulo break; 359214501Srpaulo } 360214501Srpaulo } 361214501Srpaulo 362289549Srpaulo#ifdef CONFIG_TESTING_OPTIONS 363289549Srpaulo { 364289549Srpaulo u8 challenge[8]; 365289549Srpaulo 366289549Srpaulo if (challenge_hash(peer_challenge, data->auth_challenge, 367289549Srpaulo username, username_len, challenge) == 0) { 368289549Srpaulo eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", 369289549Srpaulo username, username_len, 370289549Srpaulo challenge, nt_response); 371289549Srpaulo } 372289549Srpaulo } 373289549Srpaulo#endif /* CONFIG_TESTING_OPTIONS */ 374289549Srpaulo 375214501Srpaulo if (username_len != user_len || 376214501Srpaulo os_memcmp(username, user, username_len) != 0) { 377214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 378214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 379214501Srpaulo "name", username, username_len); 380214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 381214501Srpaulo "name", user, user_len); 382214501Srpaulo data->state = FAILURE; 383214501Srpaulo return; 384214501Srpaulo } 385214501Srpaulo 386214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 387214501Srpaulo username, username_len); 388214501Srpaulo 389214501Srpaulo if (sm->user->password_hash) { 390214501Srpaulo res = generate_nt_response_pwhash(data->auth_challenge, 391214501Srpaulo peer_challenge, 392214501Srpaulo username, username_len, 393214501Srpaulo sm->user->password, 394214501Srpaulo expected); 395214501Srpaulo } else { 396214501Srpaulo res = generate_nt_response(data->auth_challenge, 397214501Srpaulo peer_challenge, 398214501Srpaulo username, username_len, 399214501Srpaulo sm->user->password, 400214501Srpaulo sm->user->password_len, 401214501Srpaulo expected); 402214501Srpaulo } 403214501Srpaulo if (res) { 404214501Srpaulo data->state = FAILURE; 405214501Srpaulo return; 406214501Srpaulo } 407214501Srpaulo 408281806Srpaulo if (os_memcmp_const(nt_response, expected, 24) == 0) { 409214501Srpaulo const u8 *pw_hash; 410214501Srpaulo u8 pw_hash_buf[16], pw_hash_hash[16]; 411214501Srpaulo 412214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 413214501Srpaulo data->state = SUCCESS_REQ; 414214501Srpaulo 415214501Srpaulo /* Authenticator response is not really needed yet, but 416214501Srpaulo * calculate it here so that peer_challenge and username need 417214501Srpaulo * not be saved. */ 418214501Srpaulo if (sm->user->password_hash) { 419214501Srpaulo pw_hash = sm->user->password; 420214501Srpaulo } else { 421252726Srpaulo if (nt_password_hash(sm->user->password, 422252726Srpaulo sm->user->password_len, 423252726Srpaulo pw_hash_buf) < 0) { 424252726Srpaulo data->state = FAILURE; 425252726Srpaulo return; 426252726Srpaulo } 427214501Srpaulo pw_hash = pw_hash_buf; 428214501Srpaulo } 429281806Srpaulo if (generate_authenticator_response_pwhash( 430281806Srpaulo pw_hash, peer_challenge, data->auth_challenge, 431281806Srpaulo username, username_len, nt_response, 432281806Srpaulo data->auth_response) < 0 || 433281806Srpaulo hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || 434281806Srpaulo get_master_key(pw_hash_hash, nt_response, 435281806Srpaulo data->master_key)) { 436281806Srpaulo data->state = FAILURE; 437281806Srpaulo return; 438281806Srpaulo } 439214501Srpaulo data->master_key_valid = 1; 440214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 441214501Srpaulo data->master_key, MSCHAPV2_KEY_LEN); 442214501Srpaulo } else { 443214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 444214501Srpaulo expected, 24); 445214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 446214501Srpaulo data->state = FAILURE_REQ; 447214501Srpaulo } 448214501Srpaulo} 449214501Srpaulo 450214501Srpaulo 451214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm, 452214501Srpaulo struct eap_mschapv2_data *data, 453214501Srpaulo struct wpabuf *respData) 454214501Srpaulo{ 455214501Srpaulo struct eap_mschapv2_hdr *resp; 456214501Srpaulo const u8 *pos; 457214501Srpaulo size_t len; 458214501Srpaulo 459214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 460214501Srpaulo &len); 461214501Srpaulo if (pos == NULL || len < 1) 462214501Srpaulo return; /* Should not happen - frame already validated */ 463214501Srpaulo 464214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 465214501Srpaulo 466214501Srpaulo if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 467214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 468214501Srpaulo " - authentication completed successfully"); 469214501Srpaulo data->state = SUCCESS; 470214501Srpaulo } else { 471214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 472214501Srpaulo "Response - peer rejected authentication"); 473214501Srpaulo data->state = FAILURE; 474214501Srpaulo } 475214501Srpaulo} 476214501Srpaulo 477214501Srpaulo 478214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 479214501Srpaulo struct eap_mschapv2_data *data, 480214501Srpaulo struct wpabuf *respData) 481214501Srpaulo{ 482214501Srpaulo struct eap_mschapv2_hdr *resp; 483214501Srpaulo const u8 *pos; 484214501Srpaulo size_t len; 485214501Srpaulo 486214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 487214501Srpaulo &len); 488214501Srpaulo if (pos == NULL || len < 1) 489214501Srpaulo return; /* Should not happen - frame already validated */ 490214501Srpaulo 491214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 492214501Srpaulo 493214501Srpaulo if (resp->op_code == MSCHAPV2_OP_FAILURE) { 494214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 495214501Srpaulo " - authentication failed"); 496214501Srpaulo } else { 497214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 498214501Srpaulo "Response - authentication failed"); 499214501Srpaulo } 500214501Srpaulo 501214501Srpaulo data->state = FAILURE; 502214501Srpaulo} 503214501Srpaulo 504214501Srpaulo 505214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv, 506214501Srpaulo struct wpabuf *respData) 507214501Srpaulo{ 508214501Srpaulo struct eap_mschapv2_data *data = priv; 509214501Srpaulo 510214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 511214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 512214501Srpaulo data->state = FAILURE; 513214501Srpaulo return; 514214501Srpaulo } 515214501Srpaulo 516214501Srpaulo switch (data->state) { 517214501Srpaulo case CHALLENGE: 518214501Srpaulo eap_mschapv2_process_response(sm, data, respData); 519214501Srpaulo break; 520214501Srpaulo case SUCCESS_REQ: 521214501Srpaulo eap_mschapv2_process_success_resp(sm, data, respData); 522214501Srpaulo break; 523214501Srpaulo case FAILURE_REQ: 524214501Srpaulo eap_mschapv2_process_failure_resp(sm, data, respData); 525214501Srpaulo break; 526214501Srpaulo default: 527214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 528214501Srpaulo "process", data->state); 529214501Srpaulo break; 530214501Srpaulo } 531214501Srpaulo} 532214501Srpaulo 533214501Srpaulo 534214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 535214501Srpaulo{ 536214501Srpaulo struct eap_mschapv2_data *data = priv; 537214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 538214501Srpaulo} 539214501Srpaulo 540214501Srpaulo 541214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 542214501Srpaulo{ 543214501Srpaulo struct eap_mschapv2_data *data = priv; 544214501Srpaulo u8 *key; 545214501Srpaulo 546214501Srpaulo if (data->state != SUCCESS || !data->master_key_valid) 547214501Srpaulo return NULL; 548214501Srpaulo 549214501Srpaulo *len = 2 * MSCHAPV2_KEY_LEN; 550214501Srpaulo key = os_malloc(*len); 551214501Srpaulo if (key == NULL) 552214501Srpaulo return NULL; 553214501Srpaulo /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 554346981Scy if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 555346981Scy 1) < 0 || 556346981Scy get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 557346981Scy MSCHAPV2_KEY_LEN, 1, 1) < 0) { 558346981Scy os_free(key); 559346981Scy return NULL; 560346981Scy } 561214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 562214501Srpaulo 563214501Srpaulo return key; 564214501Srpaulo} 565214501Srpaulo 566214501Srpaulo 567214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 568214501Srpaulo{ 569214501Srpaulo struct eap_mschapv2_data *data = priv; 570214501Srpaulo return data->state == SUCCESS; 571214501Srpaulo} 572214501Srpaulo 573214501Srpaulo 574214501Srpauloint eap_server_mschapv2_register(void) 575214501Srpaulo{ 576214501Srpaulo struct eap_method *eap; 577214501Srpaulo 578214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 579214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 580214501Srpaulo "MSCHAPV2"); 581214501Srpaulo if (eap == NULL) 582214501Srpaulo return -1; 583214501Srpaulo 584214501Srpaulo eap->init = eap_mschapv2_init; 585214501Srpaulo eap->reset = eap_mschapv2_reset; 586214501Srpaulo eap->buildReq = eap_mschapv2_buildReq; 587214501Srpaulo eap->check = eap_mschapv2_check; 588214501Srpaulo eap->process = eap_mschapv2_process; 589214501Srpaulo eap->isDone = eap_mschapv2_isDone; 590214501Srpaulo eap->getKey = eap_mschapv2_getKey; 591214501Srpaulo eap->isSuccess = eap_mschapv2_isSuccess; 592214501Srpaulo 593337817Scy return eap_server_method_register(eap); 594214501Srpaulo} 595