eap_server_mschapv2.c revision 337817
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) { 74214501Srpaulo data->peer_challenge = os_malloc(CHALLENGE_LEN); 75214501Srpaulo if (data->peer_challenge == NULL) { 76214501Srpaulo os_free(data); 77214501Srpaulo return NULL; 78214501Srpaulo } 79214501Srpaulo os_memcpy(data->peer_challenge, sm->peer_challenge, 80214501Srpaulo CHALLENGE_LEN); 81214501Srpaulo } 82214501Srpaulo 83214501Srpaulo return data; 84214501Srpaulo} 85214501Srpaulo 86214501Srpaulo 87214501Srpaulostatic void eap_mschapv2_reset(struct eap_sm *sm, void *priv) 88214501Srpaulo{ 89214501Srpaulo struct eap_mschapv2_data *data = priv; 90214501Srpaulo if (data == NULL) 91214501Srpaulo return; 92214501Srpaulo 93214501Srpaulo os_free(data->peer_challenge); 94281806Srpaulo bin_clear_free(data, sizeof(*data)); 95214501Srpaulo} 96214501Srpaulo 97214501Srpaulo 98214501Srpaulostatic struct wpabuf * eap_mschapv2_build_challenge( 99214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 100214501Srpaulo{ 101214501Srpaulo struct wpabuf *req; 102214501Srpaulo struct eap_mschapv2_hdr *ms; 103214501Srpaulo size_t ms_len; 104214501Srpaulo 105214501Srpaulo if (!data->auth_challenge_from_tls && 106252726Srpaulo random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { 107214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 108214501Srpaulo "data"); 109214501Srpaulo data->state = FAILURE; 110214501Srpaulo return NULL; 111214501Srpaulo } 112214501Srpaulo 113281806Srpaulo ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; 114214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 115214501Srpaulo EAP_CODE_REQUEST, id); 116214501Srpaulo if (req == NULL) { 117214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 118214501Srpaulo " for request"); 119214501Srpaulo data->state = FAILURE; 120214501Srpaulo return NULL; 121214501Srpaulo } 122214501Srpaulo 123214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 124214501Srpaulo ms->op_code = MSCHAPV2_OP_CHALLENGE; 125214501Srpaulo ms->mschapv2_id = id; 126214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 127214501Srpaulo 128214501Srpaulo wpabuf_put_u8(req, CHALLENGE_LEN); 129214501Srpaulo if (!data->auth_challenge_from_tls) 130214501Srpaulo wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 131214501Srpaulo else 132214501Srpaulo wpabuf_put(req, CHALLENGE_LEN); 133214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 134214501Srpaulo data->auth_challenge, CHALLENGE_LEN); 135281806Srpaulo wpabuf_put_data(req, sm->server_id, sm->server_id_len); 136214501Srpaulo 137214501Srpaulo return req; 138214501Srpaulo} 139214501Srpaulo 140214501Srpaulo 141214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req( 142214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 143214501Srpaulo{ 144214501Srpaulo struct wpabuf *req; 145214501Srpaulo struct eap_mschapv2_hdr *ms; 146214501Srpaulo u8 *msg; 147214501Srpaulo char *message = "OK"; 148214501Srpaulo size_t ms_len; 149214501Srpaulo 150214501Srpaulo ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 151214501Srpaulo os_strlen(message); 152214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 153214501Srpaulo EAP_CODE_REQUEST, id); 154214501Srpaulo if (req == NULL) { 155214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 156214501Srpaulo " for request"); 157214501Srpaulo data->state = FAILURE; 158214501Srpaulo return NULL; 159214501Srpaulo } 160214501Srpaulo 161214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 162214501Srpaulo ms->op_code = MSCHAPV2_OP_SUCCESS; 163214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 164214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 165214501Srpaulo msg = (u8 *) (ms + 1); 166214501Srpaulo 167214501Srpaulo wpabuf_put_u8(req, 'S'); 168214501Srpaulo wpabuf_put_u8(req, '='); 169214501Srpaulo wpa_snprintf_hex_uppercase( 170214501Srpaulo wpabuf_put(req, sizeof(data->auth_response) * 2), 171214501Srpaulo sizeof(data->auth_response) * 2 + 1, 172214501Srpaulo data->auth_response, sizeof(data->auth_response)); 173214501Srpaulo wpabuf_put_u8(req, ' '); 174214501Srpaulo wpabuf_put_u8(req, 'M'); 175214501Srpaulo wpabuf_put_u8(req, '='); 176214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 177214501Srpaulo 178214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 179214501Srpaulo msg, ms_len - sizeof(*ms)); 180214501Srpaulo 181214501Srpaulo return req; 182214501Srpaulo} 183214501Srpaulo 184214501Srpaulo 185214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req( 186214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 187214501Srpaulo{ 188214501Srpaulo struct wpabuf *req; 189214501Srpaulo struct eap_mschapv2_hdr *ms; 190214501Srpaulo char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 191214501Srpaulo "M=FAILED"; 192214501Srpaulo size_t ms_len; 193214501Srpaulo 194214501Srpaulo ms_len = sizeof(*ms) + os_strlen(message); 195214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 196214501Srpaulo EAP_CODE_REQUEST, id); 197214501Srpaulo if (req == NULL) { 198214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 199214501Srpaulo " for request"); 200214501Srpaulo data->state = FAILURE; 201214501Srpaulo return NULL; 202214501Srpaulo } 203214501Srpaulo 204214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 205214501Srpaulo ms->op_code = MSCHAPV2_OP_FAILURE; 206214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 207214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 208214501Srpaulo 209214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 210214501Srpaulo 211214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 212214501Srpaulo (u8 *) message, os_strlen(message)); 213214501Srpaulo 214214501Srpaulo return req; 215214501Srpaulo} 216214501Srpaulo 217214501Srpaulo 218214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 219214501Srpaulo u8 id) 220214501Srpaulo{ 221214501Srpaulo struct eap_mschapv2_data *data = priv; 222214501Srpaulo 223214501Srpaulo switch (data->state) { 224214501Srpaulo case CHALLENGE: 225214501Srpaulo return eap_mschapv2_build_challenge(sm, data, id); 226214501Srpaulo case SUCCESS_REQ: 227214501Srpaulo return eap_mschapv2_build_success_req(sm, data, id); 228214501Srpaulo case FAILURE_REQ: 229214501Srpaulo return eap_mschapv2_build_failure_req(sm, data, id); 230214501Srpaulo default: 231214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 232214501Srpaulo "buildReq", data->state); 233214501Srpaulo break; 234214501Srpaulo } 235214501Srpaulo return NULL; 236214501Srpaulo} 237214501Srpaulo 238214501Srpaulo 239214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, 240214501Srpaulo struct wpabuf *respData) 241214501Srpaulo{ 242214501Srpaulo struct eap_mschapv2_data *data = priv; 243214501Srpaulo struct eap_mschapv2_hdr *resp; 244214501Srpaulo const u8 *pos; 245214501Srpaulo size_t len; 246214501Srpaulo 247214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 248214501Srpaulo &len); 249214501Srpaulo if (pos == NULL || len < 1) { 250214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 251214501Srpaulo return TRUE; 252214501Srpaulo } 253214501Srpaulo 254214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 255214501Srpaulo if (data->state == CHALLENGE && 256214501Srpaulo resp->op_code != MSCHAPV2_OP_RESPONSE) { 257214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 258214501Srpaulo "ignore op %d", resp->op_code); 259214501Srpaulo return TRUE; 260214501Srpaulo } 261214501Srpaulo 262214501Srpaulo if (data->state == SUCCESS_REQ && 263214501Srpaulo resp->op_code != MSCHAPV2_OP_SUCCESS && 264214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 265214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 266214501Srpaulo "Failure - ignore op %d", resp->op_code); 267214501Srpaulo return TRUE; 268214501Srpaulo } 269214501Srpaulo 270214501Srpaulo if (data->state == FAILURE_REQ && 271214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 272214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 273214501Srpaulo "- ignore op %d", resp->op_code); 274214501Srpaulo return TRUE; 275214501Srpaulo } 276214501Srpaulo 277214501Srpaulo return FALSE; 278214501Srpaulo} 279214501Srpaulo 280214501Srpaulo 281214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm, 282214501Srpaulo struct eap_mschapv2_data *data, 283214501Srpaulo struct wpabuf *respData) 284214501Srpaulo{ 285214501Srpaulo struct eap_mschapv2_hdr *resp; 286214501Srpaulo const u8 *pos, *end, *peer_challenge, *nt_response, *name; 287214501Srpaulo u8 flags; 288214501Srpaulo size_t len, name_len, i; 289214501Srpaulo u8 expected[24]; 290214501Srpaulo const u8 *username, *user; 291214501Srpaulo size_t username_len, user_len; 292214501Srpaulo int res; 293281806Srpaulo char *buf; 294214501Srpaulo 295214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 296214501Srpaulo &len); 297214501Srpaulo if (pos == NULL || len < 1) 298214501Srpaulo return; /* Should not happen - frame already validated */ 299214501Srpaulo 300214501Srpaulo end = pos + len; 301214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 302214501Srpaulo pos = (u8 *) (resp + 1); 303214501Srpaulo 304214501Srpaulo if (len < sizeof(*resp) + 1 + 49 || 305214501Srpaulo resp->op_code != MSCHAPV2_OP_RESPONSE || 306214501Srpaulo pos[0] != 49) { 307214501Srpaulo wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", 308214501Srpaulo respData); 309214501Srpaulo data->state = FAILURE; 310214501Srpaulo return; 311214501Srpaulo } 312214501Srpaulo data->resp_mschapv2_id = resp->mschapv2_id; 313214501Srpaulo pos++; 314214501Srpaulo peer_challenge = pos; 315214501Srpaulo pos += 16 + 8; 316214501Srpaulo nt_response = pos; 317214501Srpaulo pos += 24; 318214501Srpaulo flags = *pos++; 319214501Srpaulo name = pos; 320214501Srpaulo name_len = end - name; 321214501Srpaulo 322214501Srpaulo if (data->peer_challenge) { 323214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " 324214501Srpaulo "Peer-Challenge"); 325214501Srpaulo peer_challenge = data->peer_challenge; 326214501Srpaulo } 327214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", 328214501Srpaulo peer_challenge, 16); 329214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); 330214501Srpaulo wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); 331214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); 332214501Srpaulo 333281806Srpaulo buf = os_malloc(name_len * 4 + 1); 334281806Srpaulo if (buf) { 335281806Srpaulo printf_encode(buf, name_len * 4 + 1, name, name_len); 336281806Srpaulo eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); 337281806Srpaulo os_free(buf); 338281806Srpaulo } 339281806Srpaulo 340214501Srpaulo /* MSCHAPv2 does not include optional domain name in the 341214501Srpaulo * challenge-response calculation, so remove domain prefix 342214501Srpaulo * (if present). */ 343214501Srpaulo username = sm->identity; 344214501Srpaulo username_len = sm->identity_len; 345214501Srpaulo for (i = 0; i < username_len; i++) { 346214501Srpaulo if (username[i] == '\\') { 347214501Srpaulo username_len -= i + 1; 348214501Srpaulo username += i + 1; 349214501Srpaulo break; 350214501Srpaulo } 351214501Srpaulo } 352214501Srpaulo 353214501Srpaulo user = name; 354214501Srpaulo user_len = name_len; 355214501Srpaulo for (i = 0; i < user_len; i++) { 356214501Srpaulo if (user[i] == '\\') { 357214501Srpaulo user_len -= i + 1; 358214501Srpaulo user += i + 1; 359214501Srpaulo break; 360214501Srpaulo } 361214501Srpaulo } 362214501Srpaulo 363289549Srpaulo#ifdef CONFIG_TESTING_OPTIONS 364289549Srpaulo { 365289549Srpaulo u8 challenge[8]; 366289549Srpaulo 367289549Srpaulo if (challenge_hash(peer_challenge, data->auth_challenge, 368289549Srpaulo username, username_len, challenge) == 0) { 369289549Srpaulo eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", 370289549Srpaulo username, username_len, 371289549Srpaulo challenge, nt_response); 372289549Srpaulo } 373289549Srpaulo } 374289549Srpaulo#endif /* CONFIG_TESTING_OPTIONS */ 375289549Srpaulo 376214501Srpaulo if (username_len != user_len || 377214501Srpaulo os_memcmp(username, user, username_len) != 0) { 378214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 379214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 380214501Srpaulo "name", username, username_len); 381214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 382214501Srpaulo "name", user, user_len); 383214501Srpaulo data->state = FAILURE; 384214501Srpaulo return; 385214501Srpaulo } 386214501Srpaulo 387214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 388214501Srpaulo username, username_len); 389214501Srpaulo 390214501Srpaulo if (sm->user->password_hash) { 391214501Srpaulo res = generate_nt_response_pwhash(data->auth_challenge, 392214501Srpaulo peer_challenge, 393214501Srpaulo username, username_len, 394214501Srpaulo sm->user->password, 395214501Srpaulo expected); 396214501Srpaulo } else { 397214501Srpaulo res = generate_nt_response(data->auth_challenge, 398214501Srpaulo peer_challenge, 399214501Srpaulo username, username_len, 400214501Srpaulo sm->user->password, 401214501Srpaulo sm->user->password_len, 402214501Srpaulo expected); 403214501Srpaulo } 404214501Srpaulo if (res) { 405214501Srpaulo data->state = FAILURE; 406214501Srpaulo return; 407214501Srpaulo } 408214501Srpaulo 409281806Srpaulo if (os_memcmp_const(nt_response, expected, 24) == 0) { 410214501Srpaulo const u8 *pw_hash; 411214501Srpaulo u8 pw_hash_buf[16], pw_hash_hash[16]; 412214501Srpaulo 413214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 414214501Srpaulo data->state = SUCCESS_REQ; 415214501Srpaulo 416214501Srpaulo /* Authenticator response is not really needed yet, but 417214501Srpaulo * calculate it here so that peer_challenge and username need 418214501Srpaulo * not be saved. */ 419214501Srpaulo if (sm->user->password_hash) { 420214501Srpaulo pw_hash = sm->user->password; 421214501Srpaulo } else { 422252726Srpaulo if (nt_password_hash(sm->user->password, 423252726Srpaulo sm->user->password_len, 424252726Srpaulo pw_hash_buf) < 0) { 425252726Srpaulo data->state = FAILURE; 426252726Srpaulo return; 427252726Srpaulo } 428214501Srpaulo pw_hash = pw_hash_buf; 429214501Srpaulo } 430281806Srpaulo if (generate_authenticator_response_pwhash( 431281806Srpaulo pw_hash, peer_challenge, data->auth_challenge, 432281806Srpaulo username, username_len, nt_response, 433281806Srpaulo data->auth_response) < 0 || 434281806Srpaulo hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || 435281806Srpaulo get_master_key(pw_hash_hash, nt_response, 436281806Srpaulo data->master_key)) { 437281806Srpaulo data->state = FAILURE; 438281806Srpaulo return; 439281806Srpaulo } 440214501Srpaulo data->master_key_valid = 1; 441214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 442214501Srpaulo data->master_key, MSCHAPV2_KEY_LEN); 443214501Srpaulo } else { 444214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 445214501Srpaulo expected, 24); 446214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 447214501Srpaulo data->state = FAILURE_REQ; 448214501Srpaulo } 449214501Srpaulo} 450214501Srpaulo 451214501Srpaulo 452214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm, 453214501Srpaulo struct eap_mschapv2_data *data, 454214501Srpaulo struct wpabuf *respData) 455214501Srpaulo{ 456214501Srpaulo struct eap_mschapv2_hdr *resp; 457214501Srpaulo const u8 *pos; 458214501Srpaulo size_t len; 459214501Srpaulo 460214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 461214501Srpaulo &len); 462214501Srpaulo if (pos == NULL || len < 1) 463214501Srpaulo return; /* Should not happen - frame already validated */ 464214501Srpaulo 465214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 466214501Srpaulo 467214501Srpaulo if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 468214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 469214501Srpaulo " - authentication completed successfully"); 470214501Srpaulo data->state = SUCCESS; 471214501Srpaulo } else { 472214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 473214501Srpaulo "Response - peer rejected authentication"); 474214501Srpaulo data->state = FAILURE; 475214501Srpaulo } 476214501Srpaulo} 477214501Srpaulo 478214501Srpaulo 479214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 480214501Srpaulo struct eap_mschapv2_data *data, 481214501Srpaulo struct wpabuf *respData) 482214501Srpaulo{ 483214501Srpaulo struct eap_mschapv2_hdr *resp; 484214501Srpaulo const u8 *pos; 485214501Srpaulo size_t len; 486214501Srpaulo 487214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 488214501Srpaulo &len); 489214501Srpaulo if (pos == NULL || len < 1) 490214501Srpaulo return; /* Should not happen - frame already validated */ 491214501Srpaulo 492214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 493214501Srpaulo 494214501Srpaulo if (resp->op_code == MSCHAPV2_OP_FAILURE) { 495214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 496214501Srpaulo " - authentication failed"); 497214501Srpaulo } else { 498214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 499214501Srpaulo "Response - authentication failed"); 500214501Srpaulo } 501214501Srpaulo 502214501Srpaulo data->state = FAILURE; 503214501Srpaulo} 504214501Srpaulo 505214501Srpaulo 506214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv, 507214501Srpaulo struct wpabuf *respData) 508214501Srpaulo{ 509214501Srpaulo struct eap_mschapv2_data *data = priv; 510214501Srpaulo 511214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 512214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 513214501Srpaulo data->state = FAILURE; 514214501Srpaulo return; 515214501Srpaulo } 516214501Srpaulo 517214501Srpaulo switch (data->state) { 518214501Srpaulo case CHALLENGE: 519214501Srpaulo eap_mschapv2_process_response(sm, data, respData); 520214501Srpaulo break; 521214501Srpaulo case SUCCESS_REQ: 522214501Srpaulo eap_mschapv2_process_success_resp(sm, data, respData); 523214501Srpaulo break; 524214501Srpaulo case FAILURE_REQ: 525214501Srpaulo eap_mschapv2_process_failure_resp(sm, data, respData); 526214501Srpaulo break; 527214501Srpaulo default: 528214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 529214501Srpaulo "process", data->state); 530214501Srpaulo break; 531214501Srpaulo } 532214501Srpaulo} 533214501Srpaulo 534214501Srpaulo 535214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 536214501Srpaulo{ 537214501Srpaulo struct eap_mschapv2_data *data = priv; 538214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 539214501Srpaulo} 540214501Srpaulo 541214501Srpaulo 542214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 543214501Srpaulo{ 544214501Srpaulo struct eap_mschapv2_data *data = priv; 545214501Srpaulo u8 *key; 546214501Srpaulo 547214501Srpaulo if (data->state != SUCCESS || !data->master_key_valid) 548214501Srpaulo return NULL; 549214501Srpaulo 550214501Srpaulo *len = 2 * MSCHAPV2_KEY_LEN; 551214501Srpaulo key = os_malloc(*len); 552214501Srpaulo if (key == NULL) 553214501Srpaulo return NULL; 554214501Srpaulo /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 555214501Srpaulo get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 556214501Srpaulo get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 557214501Srpaulo MSCHAPV2_KEY_LEN, 1, 1); 558214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 559214501Srpaulo 560214501Srpaulo return key; 561214501Srpaulo} 562214501Srpaulo 563214501Srpaulo 564214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 565214501Srpaulo{ 566214501Srpaulo struct eap_mschapv2_data *data = priv; 567214501Srpaulo return data->state == SUCCESS; 568214501Srpaulo} 569214501Srpaulo 570214501Srpaulo 571214501Srpauloint eap_server_mschapv2_register(void) 572214501Srpaulo{ 573214501Srpaulo struct eap_method *eap; 574214501Srpaulo 575214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 576214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 577214501Srpaulo "MSCHAPV2"); 578214501Srpaulo if (eap == NULL) 579214501Srpaulo return -1; 580214501Srpaulo 581214501Srpaulo eap->init = eap_mschapv2_init; 582214501Srpaulo eap->reset = eap_mschapv2_reset; 583214501Srpaulo eap->buildReq = eap_mschapv2_buildReq; 584214501Srpaulo eap->check = eap_mschapv2_check; 585214501Srpaulo eap->process = eap_mschapv2_process; 586214501Srpaulo eap->isDone = eap_mschapv2_isDone; 587214501Srpaulo eap->getKey = eap_mschapv2_getKey; 588214501Srpaulo eap->isSuccess = eap_mschapv2_isSuccess; 589214501Srpaulo 590337817Scy return eap_server_method_register(eap); 591214501Srpaulo} 592