eap_server_mschapv2.c revision 252726
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); 94214501Srpaulo os_free(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 char *name = "hostapd"; /* TODO: make this configurable */ 104214501Srpaulo size_t ms_len; 105214501Srpaulo 106214501Srpaulo if (!data->auth_challenge_from_tls && 107252726Srpaulo random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { 108214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " 109214501Srpaulo "data"); 110214501Srpaulo data->state = FAILURE; 111214501Srpaulo return NULL; 112214501Srpaulo } 113214501Srpaulo 114214501Srpaulo ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); 115214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 116214501Srpaulo EAP_CODE_REQUEST, id); 117214501Srpaulo if (req == NULL) { 118214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 119214501Srpaulo " for request"); 120214501Srpaulo data->state = FAILURE; 121214501Srpaulo return NULL; 122214501Srpaulo } 123214501Srpaulo 124214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 125214501Srpaulo ms->op_code = MSCHAPV2_OP_CHALLENGE; 126214501Srpaulo ms->mschapv2_id = id; 127214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 128214501Srpaulo 129214501Srpaulo wpabuf_put_u8(req, CHALLENGE_LEN); 130214501Srpaulo if (!data->auth_challenge_from_tls) 131214501Srpaulo wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); 132214501Srpaulo else 133214501Srpaulo wpabuf_put(req, CHALLENGE_LEN); 134214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", 135214501Srpaulo data->auth_challenge, CHALLENGE_LEN); 136214501Srpaulo wpabuf_put_data(req, name, os_strlen(name)); 137214501Srpaulo 138214501Srpaulo return req; 139214501Srpaulo} 140214501Srpaulo 141214501Srpaulo 142214501Srpaulostatic struct wpabuf * eap_mschapv2_build_success_req( 143214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 144214501Srpaulo{ 145214501Srpaulo struct wpabuf *req; 146214501Srpaulo struct eap_mschapv2_hdr *ms; 147214501Srpaulo u8 *msg; 148214501Srpaulo char *message = "OK"; 149214501Srpaulo size_t ms_len; 150214501Srpaulo 151214501Srpaulo ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + 152214501Srpaulo os_strlen(message); 153214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 154214501Srpaulo EAP_CODE_REQUEST, id); 155214501Srpaulo if (req == NULL) { 156214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 157214501Srpaulo " for request"); 158214501Srpaulo data->state = FAILURE; 159214501Srpaulo return NULL; 160214501Srpaulo } 161214501Srpaulo 162214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 163214501Srpaulo ms->op_code = MSCHAPV2_OP_SUCCESS; 164214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 165214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 166214501Srpaulo msg = (u8 *) (ms + 1); 167214501Srpaulo 168214501Srpaulo wpabuf_put_u8(req, 'S'); 169214501Srpaulo wpabuf_put_u8(req, '='); 170214501Srpaulo wpa_snprintf_hex_uppercase( 171214501Srpaulo wpabuf_put(req, sizeof(data->auth_response) * 2), 172214501Srpaulo sizeof(data->auth_response) * 2 + 1, 173214501Srpaulo data->auth_response, sizeof(data->auth_response)); 174214501Srpaulo wpabuf_put_u8(req, ' '); 175214501Srpaulo wpabuf_put_u8(req, 'M'); 176214501Srpaulo wpabuf_put_u8(req, '='); 177214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 178214501Srpaulo 179214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", 180214501Srpaulo msg, ms_len - sizeof(*ms)); 181214501Srpaulo 182214501Srpaulo return req; 183214501Srpaulo} 184214501Srpaulo 185214501Srpaulo 186214501Srpaulostatic struct wpabuf * eap_mschapv2_build_failure_req( 187214501Srpaulo struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) 188214501Srpaulo{ 189214501Srpaulo struct wpabuf *req; 190214501Srpaulo struct eap_mschapv2_hdr *ms; 191214501Srpaulo char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " 192214501Srpaulo "M=FAILED"; 193214501Srpaulo size_t ms_len; 194214501Srpaulo 195214501Srpaulo ms_len = sizeof(*ms) + os_strlen(message); 196214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 197214501Srpaulo EAP_CODE_REQUEST, id); 198214501Srpaulo if (req == NULL) { 199214501Srpaulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" 200214501Srpaulo " for request"); 201214501Srpaulo data->state = FAILURE; 202214501Srpaulo return NULL; 203214501Srpaulo } 204214501Srpaulo 205214501Srpaulo ms = wpabuf_put(req, sizeof(*ms)); 206214501Srpaulo ms->op_code = MSCHAPV2_OP_FAILURE; 207214501Srpaulo ms->mschapv2_id = data->resp_mschapv2_id; 208214501Srpaulo WPA_PUT_BE16(ms->ms_length, ms_len); 209214501Srpaulo 210214501Srpaulo wpabuf_put_data(req, message, os_strlen(message)); 211214501Srpaulo 212214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", 213214501Srpaulo (u8 *) message, os_strlen(message)); 214214501Srpaulo 215214501Srpaulo return req; 216214501Srpaulo} 217214501Srpaulo 218214501Srpaulo 219214501Srpaulostatic struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, 220214501Srpaulo u8 id) 221214501Srpaulo{ 222214501Srpaulo struct eap_mschapv2_data *data = priv; 223214501Srpaulo 224214501Srpaulo switch (data->state) { 225214501Srpaulo case CHALLENGE: 226214501Srpaulo return eap_mschapv2_build_challenge(sm, data, id); 227214501Srpaulo case SUCCESS_REQ: 228214501Srpaulo return eap_mschapv2_build_success_req(sm, data, id); 229214501Srpaulo case FAILURE_REQ: 230214501Srpaulo return eap_mschapv2_build_failure_req(sm, data, id); 231214501Srpaulo default: 232214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 233214501Srpaulo "buildReq", data->state); 234214501Srpaulo break; 235214501Srpaulo } 236214501Srpaulo return NULL; 237214501Srpaulo} 238214501Srpaulo 239214501Srpaulo 240214501Srpaulostatic Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, 241214501Srpaulo struct wpabuf *respData) 242214501Srpaulo{ 243214501Srpaulo struct eap_mschapv2_data *data = priv; 244214501Srpaulo struct eap_mschapv2_hdr *resp; 245214501Srpaulo const u8 *pos; 246214501Srpaulo size_t len; 247214501Srpaulo 248214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 249214501Srpaulo &len); 250214501Srpaulo if (pos == NULL || len < 1) { 251214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); 252214501Srpaulo return TRUE; 253214501Srpaulo } 254214501Srpaulo 255214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 256214501Srpaulo if (data->state == CHALLENGE && 257214501Srpaulo resp->op_code != MSCHAPV2_OP_RESPONSE) { 258214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " 259214501Srpaulo "ignore op %d", resp->op_code); 260214501Srpaulo return TRUE; 261214501Srpaulo } 262214501Srpaulo 263214501Srpaulo if (data->state == SUCCESS_REQ && 264214501Srpaulo resp->op_code != MSCHAPV2_OP_SUCCESS && 265214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 266214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " 267214501Srpaulo "Failure - ignore op %d", resp->op_code); 268214501Srpaulo return TRUE; 269214501Srpaulo } 270214501Srpaulo 271214501Srpaulo if (data->state == FAILURE_REQ && 272214501Srpaulo resp->op_code != MSCHAPV2_OP_FAILURE) { 273214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " 274214501Srpaulo "- ignore op %d", resp->op_code); 275214501Srpaulo return TRUE; 276214501Srpaulo } 277214501Srpaulo 278214501Srpaulo return FALSE; 279214501Srpaulo} 280214501Srpaulo 281214501Srpaulo 282214501Srpaulostatic void eap_mschapv2_process_response(struct eap_sm *sm, 283214501Srpaulo struct eap_mschapv2_data *data, 284214501Srpaulo struct wpabuf *respData) 285214501Srpaulo{ 286214501Srpaulo struct eap_mschapv2_hdr *resp; 287214501Srpaulo const u8 *pos, *end, *peer_challenge, *nt_response, *name; 288214501Srpaulo u8 flags; 289214501Srpaulo size_t len, name_len, i; 290214501Srpaulo u8 expected[24]; 291214501Srpaulo const u8 *username, *user; 292214501Srpaulo size_t username_len, user_len; 293214501Srpaulo int res; 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 333214501Srpaulo /* MSCHAPv2 does not include optional domain name in the 334214501Srpaulo * challenge-response calculation, so remove domain prefix 335214501Srpaulo * (if present). */ 336214501Srpaulo username = sm->identity; 337214501Srpaulo username_len = sm->identity_len; 338214501Srpaulo for (i = 0; i < username_len; i++) { 339214501Srpaulo if (username[i] == '\\') { 340214501Srpaulo username_len -= i + 1; 341214501Srpaulo username += i + 1; 342214501Srpaulo break; 343214501Srpaulo } 344214501Srpaulo } 345214501Srpaulo 346214501Srpaulo user = name; 347214501Srpaulo user_len = name_len; 348214501Srpaulo for (i = 0; i < user_len; i++) { 349214501Srpaulo if (user[i] == '\\') { 350214501Srpaulo user_len -= i + 1; 351214501Srpaulo user += i + 1; 352214501Srpaulo break; 353214501Srpaulo } 354214501Srpaulo } 355214501Srpaulo 356214501Srpaulo if (username_len != user_len || 357214501Srpaulo os_memcmp(username, user, username_len) != 0) { 358214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); 359214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " 360214501Srpaulo "name", username, username_len); 361214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " 362214501Srpaulo "name", user, user_len); 363214501Srpaulo data->state = FAILURE; 364214501Srpaulo return; 365214501Srpaulo } 366214501Srpaulo 367214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", 368214501Srpaulo username, username_len); 369214501Srpaulo 370214501Srpaulo if (sm->user->password_hash) { 371214501Srpaulo res = generate_nt_response_pwhash(data->auth_challenge, 372214501Srpaulo peer_challenge, 373214501Srpaulo username, username_len, 374214501Srpaulo sm->user->password, 375214501Srpaulo expected); 376214501Srpaulo } else { 377214501Srpaulo res = generate_nt_response(data->auth_challenge, 378214501Srpaulo peer_challenge, 379214501Srpaulo username, username_len, 380214501Srpaulo sm->user->password, 381214501Srpaulo sm->user->password_len, 382214501Srpaulo expected); 383214501Srpaulo } 384214501Srpaulo if (res) { 385214501Srpaulo data->state = FAILURE; 386214501Srpaulo return; 387214501Srpaulo } 388214501Srpaulo 389214501Srpaulo if (os_memcmp(nt_response, expected, 24) == 0) { 390214501Srpaulo const u8 *pw_hash; 391214501Srpaulo u8 pw_hash_buf[16], pw_hash_hash[16]; 392214501Srpaulo 393214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); 394214501Srpaulo data->state = SUCCESS_REQ; 395214501Srpaulo 396214501Srpaulo /* Authenticator response is not really needed yet, but 397214501Srpaulo * calculate it here so that peer_challenge and username need 398214501Srpaulo * not be saved. */ 399214501Srpaulo if (sm->user->password_hash) { 400214501Srpaulo pw_hash = sm->user->password; 401214501Srpaulo } else { 402252726Srpaulo if (nt_password_hash(sm->user->password, 403252726Srpaulo sm->user->password_len, 404252726Srpaulo pw_hash_buf) < 0) { 405252726Srpaulo data->state = FAILURE; 406252726Srpaulo return; 407252726Srpaulo } 408214501Srpaulo pw_hash = pw_hash_buf; 409214501Srpaulo } 410214501Srpaulo generate_authenticator_response_pwhash( 411214501Srpaulo pw_hash, peer_challenge, data->auth_challenge, 412214501Srpaulo username, username_len, nt_response, 413214501Srpaulo data->auth_response); 414214501Srpaulo 415214501Srpaulo hash_nt_password_hash(pw_hash, pw_hash_hash); 416214501Srpaulo get_master_key(pw_hash_hash, nt_response, data->master_key); 417214501Srpaulo data->master_key_valid = 1; 418214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", 419214501Srpaulo data->master_key, MSCHAPV2_KEY_LEN); 420214501Srpaulo } else { 421214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", 422214501Srpaulo expected, 24); 423214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); 424214501Srpaulo data->state = FAILURE_REQ; 425214501Srpaulo } 426214501Srpaulo} 427214501Srpaulo 428214501Srpaulo 429214501Srpaulostatic void eap_mschapv2_process_success_resp(struct eap_sm *sm, 430214501Srpaulo struct eap_mschapv2_data *data, 431214501Srpaulo struct wpabuf *respData) 432214501Srpaulo{ 433214501Srpaulo struct eap_mschapv2_hdr *resp; 434214501Srpaulo const u8 *pos; 435214501Srpaulo size_t len; 436214501Srpaulo 437214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 438214501Srpaulo &len); 439214501Srpaulo if (pos == NULL || len < 1) 440214501Srpaulo return; /* Should not happen - frame already validated */ 441214501Srpaulo 442214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 443214501Srpaulo 444214501Srpaulo if (resp->op_code == MSCHAPV2_OP_SUCCESS) { 445214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" 446214501Srpaulo " - authentication completed successfully"); 447214501Srpaulo data->state = SUCCESS; 448214501Srpaulo } else { 449214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " 450214501Srpaulo "Response - peer rejected authentication"); 451214501Srpaulo data->state = FAILURE; 452214501Srpaulo } 453214501Srpaulo} 454214501Srpaulo 455214501Srpaulo 456214501Srpaulostatic void eap_mschapv2_process_failure_resp(struct eap_sm *sm, 457214501Srpaulo struct eap_mschapv2_data *data, 458214501Srpaulo struct wpabuf *respData) 459214501Srpaulo{ 460214501Srpaulo struct eap_mschapv2_hdr *resp; 461214501Srpaulo const u8 *pos; 462214501Srpaulo size_t len; 463214501Srpaulo 464214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, 465214501Srpaulo &len); 466214501Srpaulo if (pos == NULL || len < 1) 467214501Srpaulo return; /* Should not happen - frame already validated */ 468214501Srpaulo 469214501Srpaulo resp = (struct eap_mschapv2_hdr *) pos; 470214501Srpaulo 471214501Srpaulo if (resp->op_code == MSCHAPV2_OP_FAILURE) { 472214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" 473214501Srpaulo " - authentication failed"); 474214501Srpaulo } else { 475214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " 476214501Srpaulo "Response - authentication failed"); 477214501Srpaulo } 478214501Srpaulo 479214501Srpaulo data->state = FAILURE; 480214501Srpaulo} 481214501Srpaulo 482214501Srpaulo 483214501Srpaulostatic void eap_mschapv2_process(struct eap_sm *sm, void *priv, 484214501Srpaulo struct wpabuf *respData) 485214501Srpaulo{ 486214501Srpaulo struct eap_mschapv2_data *data = priv; 487214501Srpaulo 488214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 489214501Srpaulo wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 490214501Srpaulo data->state = FAILURE; 491214501Srpaulo return; 492214501Srpaulo } 493214501Srpaulo 494214501Srpaulo switch (data->state) { 495214501Srpaulo case CHALLENGE: 496214501Srpaulo eap_mschapv2_process_response(sm, data, respData); 497214501Srpaulo break; 498214501Srpaulo case SUCCESS_REQ: 499214501Srpaulo eap_mschapv2_process_success_resp(sm, data, respData); 500214501Srpaulo break; 501214501Srpaulo case FAILURE_REQ: 502214501Srpaulo eap_mschapv2_process_failure_resp(sm, data, respData); 503214501Srpaulo break; 504214501Srpaulo default: 505214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " 506214501Srpaulo "process", data->state); 507214501Srpaulo break; 508214501Srpaulo } 509214501Srpaulo} 510214501Srpaulo 511214501Srpaulo 512214501Srpaulostatic Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) 513214501Srpaulo{ 514214501Srpaulo struct eap_mschapv2_data *data = priv; 515214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 516214501Srpaulo} 517214501Srpaulo 518214501Srpaulo 519214501Srpaulostatic u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 520214501Srpaulo{ 521214501Srpaulo struct eap_mschapv2_data *data = priv; 522214501Srpaulo u8 *key; 523214501Srpaulo 524214501Srpaulo if (data->state != SUCCESS || !data->master_key_valid) 525214501Srpaulo return NULL; 526214501Srpaulo 527214501Srpaulo *len = 2 * MSCHAPV2_KEY_LEN; 528214501Srpaulo key = os_malloc(*len); 529214501Srpaulo if (key == NULL) 530214501Srpaulo return NULL; 531214501Srpaulo /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ 532214501Srpaulo get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); 533214501Srpaulo get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 534214501Srpaulo MSCHAPV2_KEY_LEN, 1, 1); 535214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); 536214501Srpaulo 537214501Srpaulo return key; 538214501Srpaulo} 539214501Srpaulo 540214501Srpaulo 541214501Srpaulostatic Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) 542214501Srpaulo{ 543214501Srpaulo struct eap_mschapv2_data *data = priv; 544214501Srpaulo return data->state == SUCCESS; 545214501Srpaulo} 546214501Srpaulo 547214501Srpaulo 548214501Srpauloint eap_server_mschapv2_register(void) 549214501Srpaulo{ 550214501Srpaulo struct eap_method *eap; 551214501Srpaulo int ret; 552214501Srpaulo 553214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 554214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 555214501Srpaulo "MSCHAPV2"); 556214501Srpaulo if (eap == NULL) 557214501Srpaulo return -1; 558214501Srpaulo 559214501Srpaulo eap->init = eap_mschapv2_init; 560214501Srpaulo eap->reset = eap_mschapv2_reset; 561214501Srpaulo eap->buildReq = eap_mschapv2_buildReq; 562214501Srpaulo eap->check = eap_mschapv2_check; 563214501Srpaulo eap->process = eap_mschapv2_process; 564214501Srpaulo eap->isDone = eap_mschapv2_isDone; 565214501Srpaulo eap->getKey = eap_mschapv2_getKey; 566214501Srpaulo eap->isSuccess = eap_mschapv2_isSuccess; 567214501Srpaulo 568214501Srpaulo ret = eap_server_method_register(eap); 569214501Srpaulo if (ret) 570214501Srpaulo eap_server_method_free(eap); 571214501Srpaulo return ret; 572214501Srpaulo} 573