139beb93cSSam Leffler /* 239beb93cSSam Leffler * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) 339beb93cSSam Leffler * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler * 839beb93cSSam Leffler * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). 939beb93cSSam Leffler * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP 1039beb93cSSam Leffler * Extensions Protocol, Version 2, for mutual authentication and key 1139beb93cSSam Leffler * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in 1239beb93cSSam Leffler * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in 1339beb93cSSam Leffler * RFC 3079. 1439beb93cSSam Leffler */ 1539beb93cSSam Leffler 1639beb93cSSam Leffler #include "includes.h" 1739beb93cSSam Leffler 1839beb93cSSam Leffler #include "common.h" 19e28a4053SRui Paulo #include "crypto/ms_funcs.h" 20f05cddf9SRui Paulo #include "crypto/random.h" 21e28a4053SRui Paulo #include "common/wpa_ctrl.h" 22e28a4053SRui Paulo #include "mschapv2.h" 2339beb93cSSam Leffler #include "eap_i.h" 2439beb93cSSam Leffler #include "eap_config.h" 2539beb93cSSam Leffler 2639beb93cSSam Leffler 2739beb93cSSam Leffler #ifdef _MSC_VER 2839beb93cSSam Leffler #pragma pack(push, 1) 2939beb93cSSam Leffler #endif /* _MSC_VER */ 3039beb93cSSam Leffler 3139beb93cSSam Leffler struct eap_mschapv2_hdr { 3239beb93cSSam Leffler u8 op_code; /* MSCHAPV2_OP_* */ 3339beb93cSSam Leffler u8 mschapv2_id; /* usually same as EAP identifier; must be changed 3439beb93cSSam Leffler * for challenges, but not for success/failure */ 3539beb93cSSam Leffler u8 ms_length[2]; /* Note: misaligned; length - 5 */ 3639beb93cSSam Leffler /* followed by data */ 3739beb93cSSam Leffler } STRUCT_PACKED; 3839beb93cSSam Leffler 3939beb93cSSam Leffler /* Response Data field */ 4039beb93cSSam Leffler struct ms_response { 4139beb93cSSam Leffler u8 peer_challenge[MSCHAPV2_CHAL_LEN]; 4239beb93cSSam Leffler u8 reserved[8]; 4339beb93cSSam Leffler u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; 4439beb93cSSam Leffler u8 flags; 4539beb93cSSam Leffler } STRUCT_PACKED; 4639beb93cSSam Leffler 4739beb93cSSam Leffler /* Change-Password Data field */ 4839beb93cSSam Leffler struct ms_change_password { 4939beb93cSSam Leffler u8 encr_password[516]; 5039beb93cSSam Leffler u8 encr_hash[16]; 5139beb93cSSam Leffler u8 peer_challenge[MSCHAPV2_CHAL_LEN]; 5239beb93cSSam Leffler u8 reserved[8]; 5339beb93cSSam Leffler u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; 5439beb93cSSam Leffler u8 flags[2]; 5539beb93cSSam Leffler } STRUCT_PACKED; 5639beb93cSSam Leffler 5739beb93cSSam Leffler #ifdef _MSC_VER 5839beb93cSSam Leffler #pragma pack(pop) 5939beb93cSSam Leffler #endif /* _MSC_VER */ 6039beb93cSSam Leffler 6139beb93cSSam Leffler #define MSCHAPV2_OP_CHALLENGE 1 6239beb93cSSam Leffler #define MSCHAPV2_OP_RESPONSE 2 6339beb93cSSam Leffler #define MSCHAPV2_OP_SUCCESS 3 6439beb93cSSam Leffler #define MSCHAPV2_OP_FAILURE 4 6539beb93cSSam Leffler #define MSCHAPV2_OP_CHANGE_PASSWORD 7 6639beb93cSSam Leffler 6739beb93cSSam Leffler #define ERROR_RESTRICTED_LOGON_HOURS 646 6839beb93cSSam Leffler #define ERROR_ACCT_DISABLED 647 6939beb93cSSam Leffler #define ERROR_PASSWD_EXPIRED 648 7039beb93cSSam Leffler #define ERROR_NO_DIALIN_PERMISSION 649 7139beb93cSSam Leffler #define ERROR_AUTHENTICATION_FAILURE 691 7239beb93cSSam Leffler #define ERROR_CHANGING_PASSWORD 709 7339beb93cSSam Leffler 7439beb93cSSam Leffler #define PASSWD_CHANGE_CHAL_LEN 16 7539beb93cSSam Leffler #define MSCHAPV2_KEY_LEN 16 7639beb93cSSam Leffler 7739beb93cSSam Leffler 7839beb93cSSam Leffler struct eap_mschapv2_data { 7939beb93cSSam Leffler u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; 8039beb93cSSam Leffler int auth_response_valid; 8139beb93cSSam Leffler 8239beb93cSSam Leffler int prev_error; 8339beb93cSSam Leffler u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; 8439beb93cSSam Leffler int passwd_change_challenge_valid; 8539beb93cSSam Leffler int passwd_change_version; 8639beb93cSSam Leffler 8739beb93cSSam Leffler /* Optional challenge values generated in EAP-FAST Phase 1 negotiation 8839beb93cSSam Leffler */ 8939beb93cSSam Leffler u8 *peer_challenge; 9039beb93cSSam Leffler u8 *auth_challenge; 9139beb93cSSam Leffler 9239beb93cSSam Leffler int phase2; 9339beb93cSSam Leffler u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; 9439beb93cSSam Leffler int master_key_valid; 9539beb93cSSam Leffler int success; 9639beb93cSSam Leffler 9739beb93cSSam Leffler struct wpabuf *prev_challenge; 9839beb93cSSam Leffler }; 9939beb93cSSam Leffler 10039beb93cSSam Leffler 10139beb93cSSam Leffler static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); 10239beb93cSSam Leffler 10339beb93cSSam Leffler 10439beb93cSSam Leffler static void * eap_mschapv2_init(struct eap_sm *sm) 10539beb93cSSam Leffler { 10639beb93cSSam Leffler struct eap_mschapv2_data *data; 10739beb93cSSam Leffler data = os_zalloc(sizeof(*data)); 10839beb93cSSam Leffler if (data == NULL) 10939beb93cSSam Leffler return NULL; 11039beb93cSSam Leffler 111*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s", 112*a90b9d01SCy Schubert sm->eap_fast_mschapv2 ? "FAST-" : "", 113*a90b9d01SCy Schubert sm->peer_challenge && sm->auth_challenge ? 114*a90b9d01SCy Schubert " with preset challenges" : "", 115*a90b9d01SCy Schubert sm->init_phase2 ? " for Phase 2" : ""); 116*a90b9d01SCy Schubert 11739beb93cSSam Leffler if (sm->peer_challenge) { 11885732ac8SCy Schubert data->peer_challenge = os_memdup(sm->peer_challenge, 11985732ac8SCy Schubert MSCHAPV2_CHAL_LEN); 12039beb93cSSam Leffler if (data->peer_challenge == NULL) { 12139beb93cSSam Leffler eap_mschapv2_deinit(sm, data); 12239beb93cSSam Leffler return NULL; 12339beb93cSSam Leffler } 12439beb93cSSam Leffler } 12539beb93cSSam Leffler 12639beb93cSSam Leffler if (sm->auth_challenge) { 12785732ac8SCy Schubert data->auth_challenge = os_memdup(sm->auth_challenge, 12885732ac8SCy Schubert MSCHAPV2_CHAL_LEN); 12939beb93cSSam Leffler if (data->auth_challenge == NULL) { 13039beb93cSSam Leffler eap_mschapv2_deinit(sm, data); 13139beb93cSSam Leffler return NULL; 13239beb93cSSam Leffler } 13339beb93cSSam Leffler } 13439beb93cSSam Leffler 13539beb93cSSam Leffler data->phase2 = sm->init_phase2; 13639beb93cSSam Leffler 13739beb93cSSam Leffler return data; 13839beb93cSSam Leffler } 13939beb93cSSam Leffler 14039beb93cSSam Leffler 14139beb93cSSam Leffler static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) 14239beb93cSSam Leffler { 14339beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 14439beb93cSSam Leffler os_free(data->peer_challenge); 14539beb93cSSam Leffler os_free(data->auth_challenge); 14639beb93cSSam Leffler wpabuf_free(data->prev_challenge); 1475b9c547cSRui Paulo bin_clear_free(data, sizeof(*data)); 14839beb93cSSam Leffler } 14939beb93cSSam Leffler 15039beb93cSSam Leffler 15139beb93cSSam Leffler static struct wpabuf * eap_mschapv2_challenge_reply( 15239beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, 15339beb93cSSam Leffler u8 mschapv2_id, const u8 *auth_challenge) 15439beb93cSSam Leffler { 15539beb93cSSam Leffler struct wpabuf *resp; 15639beb93cSSam Leffler struct eap_mschapv2_hdr *ms; 15739beb93cSSam Leffler u8 *peer_challenge; 15839beb93cSSam Leffler int ms_len; 15939beb93cSSam Leffler struct ms_response *r; 16039beb93cSSam Leffler size_t identity_len, password_len; 16139beb93cSSam Leffler const u8 *identity, *password; 16239beb93cSSam Leffler int pwhash; 16339beb93cSSam Leffler 16439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); 16539beb93cSSam Leffler 16639beb93cSSam Leffler identity = eap_get_config_identity(sm, &identity_len); 16739beb93cSSam Leffler password = eap_get_config_password2(sm, &password_len, &pwhash); 16839beb93cSSam Leffler if (identity == NULL || password == NULL) 16939beb93cSSam Leffler return NULL; 17039beb93cSSam Leffler 17139beb93cSSam Leffler ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; 17239beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 17339beb93cSSam Leffler EAP_CODE_RESPONSE, id); 17439beb93cSSam Leffler if (resp == NULL) 17539beb93cSSam Leffler return NULL; 17639beb93cSSam Leffler 17739beb93cSSam Leffler ms = wpabuf_put(resp, sizeof(*ms)); 17839beb93cSSam Leffler ms->op_code = MSCHAPV2_OP_RESPONSE; 17939beb93cSSam Leffler ms->mschapv2_id = mschapv2_id; 18039beb93cSSam Leffler if (data->prev_error) { 18139beb93cSSam Leffler /* 18239beb93cSSam Leffler * TODO: this does not seem to be enough when processing two 18339beb93cSSam Leffler * or more failure messages. IAS did not increment mschapv2_id 18439beb93cSSam Leffler * in its own packets, but it seemed to expect the peer to 18539beb93cSSam Leffler * increment this for all packets(?). 18639beb93cSSam Leffler */ 18739beb93cSSam Leffler ms->mschapv2_id++; 18839beb93cSSam Leffler } 18939beb93cSSam Leffler WPA_PUT_BE16(ms->ms_length, ms_len); 19039beb93cSSam Leffler 19139beb93cSSam Leffler wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ 19239beb93cSSam Leffler 19339beb93cSSam Leffler /* Response */ 19439beb93cSSam Leffler r = wpabuf_put(resp, sizeof(*r)); 19539beb93cSSam Leffler peer_challenge = r->peer_challenge; 19639beb93cSSam Leffler if (data->peer_challenge) { 19739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " 19839beb93cSSam Leffler "in Phase 1"); 19939beb93cSSam Leffler peer_challenge = data->peer_challenge; 20039beb93cSSam Leffler os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); 201f05cddf9SRui Paulo } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { 20239beb93cSSam Leffler wpabuf_free(resp); 20339beb93cSSam Leffler return NULL; 20439beb93cSSam Leffler } 20539beb93cSSam Leffler os_memset(r->reserved, 0, 8); 20639beb93cSSam Leffler if (data->auth_challenge) { 20739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " 20839beb93cSSam Leffler "in Phase 1"); 20939beb93cSSam Leffler auth_challenge = data->auth_challenge; 21039beb93cSSam Leffler } 211e28a4053SRui Paulo if (mschapv2_derive_response(identity, identity_len, password, 21239beb93cSSam Leffler password_len, pwhash, auth_challenge, 21339beb93cSSam Leffler peer_challenge, r->nt_response, 214e28a4053SRui Paulo data->auth_response, data->master_key)) { 215e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " 216e28a4053SRui Paulo "response"); 217e28a4053SRui Paulo wpabuf_free(resp); 218e28a4053SRui Paulo return NULL; 219e28a4053SRui Paulo } 22039beb93cSSam Leffler data->auth_response_valid = 1; 22139beb93cSSam Leffler data->master_key_valid = 1; 22239beb93cSSam Leffler 22339beb93cSSam Leffler r->flags = 0; /* reserved, must be zero */ 22439beb93cSSam Leffler 22539beb93cSSam Leffler wpabuf_put_data(resp, identity, identity_len); 22639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 22739beb93cSSam Leffler "(response)", id, ms->mschapv2_id); 22839beb93cSSam Leffler return resp; 22939beb93cSSam Leffler } 23039beb93cSSam Leffler 23139beb93cSSam Leffler 23239beb93cSSam Leffler /** 23339beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message 23439beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 23539beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 23639beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 23739beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 23839beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 23939beb93cSSam Leffler * @id: EAP identifier used in the request 24039beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 24139beb93cSSam Leffler * no reply available 24239beb93cSSam Leffler */ 24339beb93cSSam Leffler static struct wpabuf * eap_mschapv2_challenge( 24439beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, 24539beb93cSSam Leffler struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, 24639beb93cSSam Leffler size_t req_len, u8 id) 24739beb93cSSam Leffler { 24839beb93cSSam Leffler size_t len, challenge_len; 24939beb93cSSam Leffler const u8 *pos, *challenge; 25039beb93cSSam Leffler 25139beb93cSSam Leffler if (eap_get_config_identity(sm, &len) == NULL || 25239beb93cSSam Leffler eap_get_config_password(sm, &len) == NULL) 25339beb93cSSam Leffler return NULL; 25439beb93cSSam Leffler 25539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); 25639beb93cSSam Leffler if (req_len < sizeof(*req) + 1) { 25739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " 25839beb93cSSam Leffler "(len %lu)", (unsigned long) req_len); 259c1d255d3SCy Schubert ret->ignore = true; 26039beb93cSSam Leffler return NULL; 26139beb93cSSam Leffler } 26239beb93cSSam Leffler pos = (const u8 *) (req + 1); 26339beb93cSSam Leffler challenge_len = *pos++; 26439beb93cSSam Leffler len = req_len - sizeof(*req) - 1; 26539beb93cSSam Leffler if (challenge_len != MSCHAPV2_CHAL_LEN) { 26639beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " 26739beb93cSSam Leffler "%lu", (unsigned long) challenge_len); 268c1d255d3SCy Schubert ret->ignore = true; 26939beb93cSSam Leffler return NULL; 27039beb93cSSam Leffler } 27139beb93cSSam Leffler 27239beb93cSSam Leffler if (len < challenge_len) { 27339beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" 27439beb93cSSam Leffler " packet: len=%lu challenge_len=%lu", 27539beb93cSSam Leffler (unsigned long) len, (unsigned long) challenge_len); 276c1d255d3SCy Schubert ret->ignore = true; 27739beb93cSSam Leffler return NULL; 27839beb93cSSam Leffler } 27939beb93cSSam Leffler 28039beb93cSSam Leffler if (data->passwd_change_challenge_valid) { 28139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " 28239beb93cSSam Leffler "failure message"); 28339beb93cSSam Leffler challenge = data->passwd_change_challenge; 28439beb93cSSam Leffler } else 28539beb93cSSam Leffler challenge = pos; 28639beb93cSSam Leffler pos += challenge_len; 28739beb93cSSam Leffler len -= challenge_len; 28839beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", 28939beb93cSSam Leffler pos, len); 29039beb93cSSam Leffler 291c1d255d3SCy Schubert ret->ignore = false; 29239beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT; 29339beb93cSSam Leffler ret->decision = DECISION_FAIL; 294c1d255d3SCy Schubert ret->allowNotifications = true; 29539beb93cSSam Leffler 29639beb93cSSam Leffler return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, 29739beb93cSSam Leffler challenge); 29839beb93cSSam Leffler } 29939beb93cSSam Leffler 30039beb93cSSam Leffler 30139beb93cSSam Leffler static void eap_mschapv2_password_changed(struct eap_sm *sm, 30239beb93cSSam Leffler struct eap_mschapv2_data *data) 30339beb93cSSam Leffler { 30439beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 30539beb93cSSam Leffler if (config && config->new_password) { 30639beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_INFO, 30739beb93cSSam Leffler WPA_EVENT_PASSWORD_CHANGED 30839beb93cSSam Leffler "EAP-MSCHAPV2: Password changed successfully"); 30939beb93cSSam Leffler data->prev_error = 0; 3105b9c547cSRui Paulo bin_clear_free(config->password, config->password_len); 311f05cddf9SRui Paulo if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { 312f05cddf9SRui Paulo /* TODO: update external storage */ 313f05cddf9SRui Paulo } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { 31439beb93cSSam Leffler config->password = os_malloc(16); 31539beb93cSSam Leffler config->password_len = 16; 3165b9c547cSRui Paulo if (config->password && 31739beb93cSSam Leffler nt_password_hash(config->new_password, 31839beb93cSSam Leffler config->new_password_len, 3195b9c547cSRui Paulo config->password)) { 3205b9c547cSRui Paulo bin_clear_free(config->password, 3215b9c547cSRui Paulo config->password_len); 3225b9c547cSRui Paulo config->password = NULL; 3235b9c547cSRui Paulo config->password_len = 0; 32439beb93cSSam Leffler } 3255b9c547cSRui Paulo bin_clear_free(config->new_password, 3265b9c547cSRui Paulo config->new_password_len); 32739beb93cSSam Leffler } else { 32839beb93cSSam Leffler config->password = config->new_password; 32939beb93cSSam Leffler config->password_len = config->new_password_len; 33039beb93cSSam Leffler } 33139beb93cSSam Leffler config->new_password = NULL; 33239beb93cSSam Leffler config->new_password_len = 0; 33339beb93cSSam Leffler } 33439beb93cSSam Leffler } 33539beb93cSSam Leffler 33639beb93cSSam Leffler 33739beb93cSSam Leffler /** 33839beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message 33939beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 34039beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 34139beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 34239beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 34339beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 34439beb93cSSam Leffler * @id: EAP identifier used in th erequest 34539beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 34639beb93cSSam Leffler * no reply available 34739beb93cSSam Leffler */ 34839beb93cSSam Leffler static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, 34939beb93cSSam Leffler struct eap_mschapv2_data *data, 35039beb93cSSam Leffler struct eap_method_ret *ret, 35139beb93cSSam Leffler const struct eap_mschapv2_hdr *req, 35239beb93cSSam Leffler size_t req_len, u8 id) 35339beb93cSSam Leffler { 35439beb93cSSam Leffler struct wpabuf *resp; 35539beb93cSSam Leffler const u8 *pos; 35639beb93cSSam Leffler size_t len; 35739beb93cSSam Leffler 35839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); 35939beb93cSSam Leffler len = req_len - sizeof(*req); 36039beb93cSSam Leffler pos = (const u8 *) (req + 1); 36139beb93cSSam Leffler if (!data->auth_response_valid || 36239beb93cSSam Leffler mschapv2_verify_auth_response(data->auth_response, pos, len)) { 36339beb93cSSam Leffler wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " 36439beb93cSSam Leffler "response in success request"); 36539beb93cSSam Leffler ret->methodState = METHOD_DONE; 36639beb93cSSam Leffler ret->decision = DECISION_FAIL; 36739beb93cSSam Leffler return NULL; 36839beb93cSSam Leffler } 36939beb93cSSam Leffler pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 37039beb93cSSam Leffler len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 37139beb93cSSam Leffler while (len > 0 && *pos == ' ') { 37239beb93cSSam Leffler pos++; 37339beb93cSSam Leffler len--; 37439beb93cSSam Leffler } 37539beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", 37639beb93cSSam Leffler pos, len); 37739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); 37839beb93cSSam Leffler 37939beb93cSSam Leffler /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success 38039beb93cSSam Leffler * message. */ 38139beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 38239beb93cSSam Leffler EAP_CODE_RESPONSE, id); 38339beb93cSSam Leffler if (resp == NULL) { 38439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " 38539beb93cSSam Leffler "buffer for success response"); 386c1d255d3SCy Schubert ret->ignore = true; 38739beb93cSSam Leffler return NULL; 38839beb93cSSam Leffler } 38939beb93cSSam Leffler 39039beb93cSSam Leffler wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ 39139beb93cSSam Leffler 39239beb93cSSam Leffler ret->methodState = METHOD_DONE; 39339beb93cSSam Leffler ret->decision = DECISION_UNCOND_SUCC; 394c1d255d3SCy Schubert ret->allowNotifications = false; 39539beb93cSSam Leffler data->success = 1; 39639beb93cSSam Leffler 39739beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED) 39839beb93cSSam Leffler eap_mschapv2_password_changed(sm, data); 39939beb93cSSam Leffler 40039beb93cSSam Leffler return resp; 40139beb93cSSam Leffler } 40239beb93cSSam Leffler 40339beb93cSSam Leffler 40439beb93cSSam Leffler static int eap_mschapv2_failure_txt(struct eap_sm *sm, 40539beb93cSSam Leffler struct eap_mschapv2_data *data, char *txt) 40639beb93cSSam Leffler { 40739beb93cSSam Leffler char *pos, *msg = ""; 40839beb93cSSam Leffler int retry = 1; 40939beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 41039beb93cSSam Leffler 41139beb93cSSam Leffler /* For example: 41239beb93cSSam Leffler * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure 41339beb93cSSam Leffler */ 41439beb93cSSam Leffler 41539beb93cSSam Leffler pos = txt; 41639beb93cSSam Leffler 41739beb93cSSam Leffler if (pos && os_strncmp(pos, "E=", 2) == 0) { 41839beb93cSSam Leffler pos += 2; 41939beb93cSSam Leffler data->prev_error = atoi(pos); 42039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", 42139beb93cSSam Leffler data->prev_error); 42239beb93cSSam Leffler pos = os_strchr(pos, ' '); 42339beb93cSSam Leffler if (pos) 42439beb93cSSam Leffler pos++; 42539beb93cSSam Leffler } 42639beb93cSSam Leffler 42739beb93cSSam Leffler if (pos && os_strncmp(pos, "R=", 2) == 0) { 42839beb93cSSam Leffler pos += 2; 42939beb93cSSam Leffler retry = atoi(pos); 43039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", 43139beb93cSSam Leffler retry == 1 ? "" : "not "); 43239beb93cSSam Leffler pos = os_strchr(pos, ' '); 43339beb93cSSam Leffler if (pos) 43439beb93cSSam Leffler pos++; 43539beb93cSSam Leffler } 43639beb93cSSam Leffler 43739beb93cSSam Leffler if (pos && os_strncmp(pos, "C=", 2) == 0) { 43839beb93cSSam Leffler int hex_len; 43939beb93cSSam Leffler pos += 2; 44039beb93cSSam Leffler hex_len = os_strchr(pos, ' ') - (char *) pos; 44139beb93cSSam Leffler if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { 44239beb93cSSam Leffler if (hexstr2bin(pos, data->passwd_change_challenge, 44339beb93cSSam Leffler PASSWD_CHANGE_CHAL_LEN)) { 44439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " 44539beb93cSSam Leffler "failure challenge"); 44639beb93cSSam Leffler } else { 44739beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " 44839beb93cSSam Leffler "challenge", 44939beb93cSSam Leffler data->passwd_change_challenge, 45039beb93cSSam Leffler PASSWD_CHANGE_CHAL_LEN); 45139beb93cSSam Leffler data->passwd_change_challenge_valid = 1; 45239beb93cSSam Leffler } 45339beb93cSSam Leffler } else { 45439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " 45539beb93cSSam Leffler "challenge len %d", hex_len); 45639beb93cSSam Leffler } 45739beb93cSSam Leffler pos = os_strchr(pos, ' '); 45839beb93cSSam Leffler if (pos) 45939beb93cSSam Leffler pos++; 46039beb93cSSam Leffler } else { 46139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " 46239beb93cSSam Leffler "was not present in failure message"); 46339beb93cSSam Leffler } 46439beb93cSSam Leffler 46539beb93cSSam Leffler if (pos && os_strncmp(pos, "V=", 2) == 0) { 46639beb93cSSam Leffler pos += 2; 46739beb93cSSam Leffler data->passwd_change_version = atoi(pos); 46839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " 46939beb93cSSam Leffler "protocol version %d", data->passwd_change_version); 47039beb93cSSam Leffler pos = os_strchr(pos, ' '); 47139beb93cSSam Leffler if (pos) 47239beb93cSSam Leffler pos++; 47339beb93cSSam Leffler } 47439beb93cSSam Leffler 47539beb93cSSam Leffler if (pos && os_strncmp(pos, "M=", 2) == 0) { 47639beb93cSSam Leffler pos += 2; 47739beb93cSSam Leffler msg = pos; 47839beb93cSSam Leffler } 4795b9c547cSRui Paulo if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry && 4805b9c547cSRui Paulo config && config->phase2 && 4815b9c547cSRui Paulo os_strstr(config->phase2, "mschapv2_retry=0")) { 4825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 4835b9c547cSRui Paulo "EAP-MSCHAPV2: mark password retry disabled based on local configuration"); 4845b9c547cSRui Paulo retry = 0; 4855b9c547cSRui Paulo } 48639beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_WARNING, 48739beb93cSSam Leffler "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " 48839beb93cSSam Leffler "%d)", 48939beb93cSSam Leffler msg, retry == 1 ? "" : "not ", data->prev_error); 49039beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED && 49139beb93cSSam Leffler data->passwd_change_version == 3 && config) { 49239beb93cSSam Leffler if (config->new_password == NULL) { 49339beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_INFO, 49439beb93cSSam Leffler "EAP-MSCHAPV2: Password expired - password " 49539beb93cSSam Leffler "change required"); 49639beb93cSSam Leffler eap_sm_request_new_password(sm); 49739beb93cSSam Leffler } 49839beb93cSSam Leffler } else if (retry == 1 && config) { 49939beb93cSSam Leffler /* TODO: could prevent the current password from being used 50039beb93cSSam Leffler * again at least for some period of time */ 50139beb93cSSam Leffler if (!config->mschapv2_retry) 50239beb93cSSam Leffler eap_sm_request_identity(sm); 50339beb93cSSam Leffler eap_sm_request_password(sm); 50439beb93cSSam Leffler config->mschapv2_retry = 1; 50539beb93cSSam Leffler } else if (config) { 50639beb93cSSam Leffler /* TODO: prevent retries using same username/password */ 50739beb93cSSam Leffler config->mschapv2_retry = 0; 50839beb93cSSam Leffler } 50939beb93cSSam Leffler 51039beb93cSSam Leffler return retry == 1; 51139beb93cSSam Leffler } 51239beb93cSSam Leffler 51339beb93cSSam Leffler 51439beb93cSSam Leffler static struct wpabuf * eap_mschapv2_change_password( 51539beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, 51639beb93cSSam Leffler struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) 51739beb93cSSam Leffler { 518325151a3SRui Paulo #ifdef CONFIG_NO_RC4 519325151a3SRui Paulo wpa_printf(MSG_ERROR, 520325151a3SRui Paulo "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); 521325151a3SRui Paulo return NULL; 522325151a3SRui Paulo #else /* CONFIG_NO_RC4 */ 52339beb93cSSam Leffler struct wpabuf *resp; 52439beb93cSSam Leffler int ms_len; 52539beb93cSSam Leffler const u8 *username, *password, *new_password; 52639beb93cSSam Leffler size_t username_len, password_len, new_password_len; 52739beb93cSSam Leffler struct eap_mschapv2_hdr *ms; 52839beb93cSSam Leffler struct ms_change_password *cp; 52939beb93cSSam Leffler u8 password_hash[16], password_hash_hash[16]; 53039beb93cSSam Leffler int pwhash; 53139beb93cSSam Leffler 53239beb93cSSam Leffler username = eap_get_config_identity(sm, &username_len); 53339beb93cSSam Leffler password = eap_get_config_password2(sm, &password_len, &pwhash); 53439beb93cSSam Leffler new_password = eap_get_config_new_password(sm, &new_password_len); 53539beb93cSSam Leffler if (username == NULL || password == NULL || new_password == NULL) 53639beb93cSSam Leffler return NULL; 53739beb93cSSam Leffler 53839beb93cSSam Leffler username = mschapv2_remove_domain(username, &username_len); 53939beb93cSSam Leffler 540c1d255d3SCy Schubert ret->ignore = false; 54139beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT; 54239beb93cSSam Leffler ret->decision = DECISION_COND_SUCC; 543c1d255d3SCy Schubert ret->allowNotifications = true; 54439beb93cSSam Leffler 54539beb93cSSam Leffler ms_len = sizeof(*ms) + sizeof(*cp); 54639beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 54739beb93cSSam Leffler EAP_CODE_RESPONSE, id); 54839beb93cSSam Leffler if (resp == NULL) 54939beb93cSSam Leffler return NULL; 55039beb93cSSam Leffler 55139beb93cSSam Leffler ms = wpabuf_put(resp, sizeof(*ms)); 55239beb93cSSam Leffler ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; 55339beb93cSSam Leffler ms->mschapv2_id = req->mschapv2_id + 1; 55439beb93cSSam Leffler WPA_PUT_BE16(ms->ms_length, ms_len); 55539beb93cSSam Leffler cp = wpabuf_put(resp, sizeof(*cp)); 55639beb93cSSam Leffler 55739beb93cSSam Leffler /* Encrypted-Password */ 55839beb93cSSam Leffler if (pwhash) { 55939beb93cSSam Leffler if (encrypt_pw_block_with_password_hash( 56039beb93cSSam Leffler new_password, new_password_len, 56139beb93cSSam Leffler password, cp->encr_password)) 56239beb93cSSam Leffler goto fail; 56339beb93cSSam Leffler } else { 56439beb93cSSam Leffler if (new_password_encrypted_with_old_nt_password_hash( 56539beb93cSSam Leffler new_password, new_password_len, 56639beb93cSSam Leffler password, password_len, cp->encr_password)) 56739beb93cSSam Leffler goto fail; 56839beb93cSSam Leffler } 56939beb93cSSam Leffler 57039beb93cSSam Leffler /* Encrypted-Hash */ 57139beb93cSSam Leffler if (pwhash) { 57239beb93cSSam Leffler u8 new_password_hash[16]; 5735b9c547cSRui Paulo if (nt_password_hash(new_password, new_password_len, 57485732ac8SCy Schubert new_password_hash) || 57539beb93cSSam Leffler nt_password_hash_encrypted_with_block(password, 57639beb93cSSam Leffler new_password_hash, 57785732ac8SCy Schubert cp->encr_hash)) 57885732ac8SCy Schubert goto fail; 57939beb93cSSam Leffler } else { 5805b9c547cSRui Paulo if (old_nt_password_hash_encrypted_with_new_nt_password_hash( 58139beb93cSSam Leffler new_password, new_password_len, 5825b9c547cSRui Paulo password, password_len, cp->encr_hash)) 5835b9c547cSRui Paulo goto fail; 58439beb93cSSam Leffler } 58539beb93cSSam Leffler 58639beb93cSSam Leffler /* Peer-Challenge */ 587f05cddf9SRui Paulo if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) 58839beb93cSSam Leffler goto fail; 58939beb93cSSam Leffler 59039beb93cSSam Leffler /* Reserved, must be zero */ 59139beb93cSSam Leffler os_memset(cp->reserved, 0, 8); 59239beb93cSSam Leffler 59339beb93cSSam Leffler /* NT-Response */ 59439beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", 59539beb93cSSam Leffler data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); 59639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", 59739beb93cSSam Leffler cp->peer_challenge, MSCHAPV2_CHAL_LEN); 59839beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", 59939beb93cSSam Leffler username, username_len); 60039beb93cSSam Leffler wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", 60139beb93cSSam Leffler new_password, new_password_len); 60239beb93cSSam Leffler generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, 60339beb93cSSam Leffler username, username_len, 60439beb93cSSam Leffler new_password, new_password_len, 60539beb93cSSam Leffler cp->nt_response); 60639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", 60739beb93cSSam Leffler cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); 60839beb93cSSam Leffler 60939beb93cSSam Leffler /* Authenticator response is not really needed yet, but calculate it 61039beb93cSSam Leffler * here so that challenges need not be saved. */ 61139beb93cSSam Leffler generate_authenticator_response(new_password, new_password_len, 61239beb93cSSam Leffler cp->peer_challenge, 61339beb93cSSam Leffler data->passwd_change_challenge, 61439beb93cSSam Leffler username, username_len, 61539beb93cSSam Leffler cp->nt_response, data->auth_response); 61639beb93cSSam Leffler data->auth_response_valid = 1; 61739beb93cSSam Leffler 61839beb93cSSam Leffler /* Likewise, generate master_key here since we have the needed data 61939beb93cSSam Leffler * available. */ 6205b9c547cSRui Paulo if (nt_password_hash(new_password, new_password_len, password_hash) || 6215b9c547cSRui Paulo hash_nt_password_hash(password_hash, password_hash_hash) || 6225b9c547cSRui Paulo get_master_key(password_hash_hash, cp->nt_response, 6235b9c547cSRui Paulo data->master_key)) { 6245b9c547cSRui Paulo data->auth_response_valid = 0; 6255b9c547cSRui Paulo goto fail; 6265b9c547cSRui Paulo } 62739beb93cSSam Leffler data->master_key_valid = 1; 62839beb93cSSam Leffler 62939beb93cSSam Leffler /* Flags */ 63039beb93cSSam Leffler os_memset(cp->flags, 0, 2); 63139beb93cSSam Leffler 63239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 63339beb93cSSam Leffler "(change pw)", id, ms->mschapv2_id); 63439beb93cSSam Leffler 63539beb93cSSam Leffler return resp; 63639beb93cSSam Leffler 63739beb93cSSam Leffler fail: 63839beb93cSSam Leffler wpabuf_free(resp); 63939beb93cSSam Leffler return NULL; 640325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */ 64139beb93cSSam Leffler } 64239beb93cSSam Leffler 64339beb93cSSam Leffler 64439beb93cSSam Leffler /** 64539beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message 64639beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 64739beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 64839beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 64939beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 65039beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 65139beb93cSSam Leffler * @id: EAP identifier used in th erequest 65239beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 65339beb93cSSam Leffler * no reply available 65439beb93cSSam Leffler */ 65539beb93cSSam Leffler static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, 65639beb93cSSam Leffler struct eap_mschapv2_data *data, 65739beb93cSSam Leffler struct eap_method_ret *ret, 65839beb93cSSam Leffler const struct eap_mschapv2_hdr *req, 65939beb93cSSam Leffler size_t req_len, u8 id) 66039beb93cSSam Leffler { 66139beb93cSSam Leffler struct wpabuf *resp; 66239beb93cSSam Leffler const u8 *msdata = (const u8 *) (req + 1); 66339beb93cSSam Leffler char *buf; 66439beb93cSSam Leffler size_t len = req_len - sizeof(*req); 66539beb93cSSam Leffler int retry = 0; 66639beb93cSSam Leffler 66739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); 66839beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", 66939beb93cSSam Leffler msdata, len); 67039beb93cSSam Leffler /* 67139beb93cSSam Leffler * eap_mschapv2_failure_txt() expects a nul terminated string, so we 67239beb93cSSam Leffler * must allocate a large enough temporary buffer to create that since 67339beb93cSSam Leffler * the received message does not include nul termination. 67439beb93cSSam Leffler */ 6755b9c547cSRui Paulo buf = dup_binstr(msdata, len); 67639beb93cSSam Leffler if (buf) { 67739beb93cSSam Leffler retry = eap_mschapv2_failure_txt(sm, data, buf); 67839beb93cSSam Leffler os_free(buf); 67939beb93cSSam Leffler } 68039beb93cSSam Leffler 681c1d255d3SCy Schubert ret->ignore = false; 68239beb93cSSam Leffler ret->methodState = METHOD_DONE; 68339beb93cSSam Leffler ret->decision = DECISION_FAIL; 684c1d255d3SCy Schubert ret->allowNotifications = false; 68539beb93cSSam Leffler 68639beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED && 68739beb93cSSam Leffler data->passwd_change_version == 3) { 68839beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 68939beb93cSSam Leffler if (config && config->new_password) 69039beb93cSSam Leffler return eap_mschapv2_change_password(sm, data, ret, req, 69139beb93cSSam Leffler id); 69239beb93cSSam Leffler if (config && config->pending_req_new_password) 69339beb93cSSam Leffler return NULL; 69439beb93cSSam Leffler } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 69539beb93cSSam Leffler /* TODO: could try to retry authentication, e.g, after having 69639beb93cSSam Leffler * changed the username/password. In this case, EAP MS-CHAP-v2 69739beb93cSSam Leffler * Failure Response would not be sent here. */ 69839beb93cSSam Leffler return NULL; 69939beb93cSSam Leffler } 70039beb93cSSam Leffler 70139beb93cSSam Leffler /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure 70239beb93cSSam Leffler * message. */ 70339beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 70439beb93cSSam Leffler EAP_CODE_RESPONSE, id); 70539beb93cSSam Leffler if (resp == NULL) 70639beb93cSSam Leffler return NULL; 70739beb93cSSam Leffler 70839beb93cSSam Leffler wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ 70939beb93cSSam Leffler 71039beb93cSSam Leffler return resp; 71139beb93cSSam Leffler } 71239beb93cSSam Leffler 71339beb93cSSam Leffler 71439beb93cSSam Leffler static int eap_mschapv2_check_config(struct eap_sm *sm) 71539beb93cSSam Leffler { 71639beb93cSSam Leffler size_t len; 71739beb93cSSam Leffler 71839beb93cSSam Leffler if (eap_get_config_identity(sm, &len) == NULL) { 71939beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); 72039beb93cSSam Leffler eap_sm_request_identity(sm); 72139beb93cSSam Leffler return -1; 72239beb93cSSam Leffler } 72339beb93cSSam Leffler 72439beb93cSSam Leffler if (eap_get_config_password(sm, &len) == NULL) { 72539beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 72639beb93cSSam Leffler eap_sm_request_password(sm); 72739beb93cSSam Leffler return -1; 72839beb93cSSam Leffler } 72939beb93cSSam Leffler 73039beb93cSSam Leffler return 0; 73139beb93cSSam Leffler } 73239beb93cSSam Leffler 73339beb93cSSam Leffler 73439beb93cSSam Leffler static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, 73539beb93cSSam Leffler const struct eap_mschapv2_hdr *ms) 73639beb93cSSam Leffler { 73739beb93cSSam Leffler size_t ms_len = WPA_GET_BE16(ms->ms_length); 73839beb93cSSam Leffler 73939beb93cSSam Leffler if (ms_len == len) 74039beb93cSSam Leffler return 0; 74139beb93cSSam Leffler 74239beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " 74339beb93cSSam Leffler "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); 74439beb93cSSam Leffler if (sm->workaround) { 74539beb93cSSam Leffler /* Some authentication servers use invalid ms_len, 74639beb93cSSam Leffler * ignore it for interoperability. */ 74739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" 74839beb93cSSam Leffler " invalid ms_len %lu (len %lu)", 74939beb93cSSam Leffler (unsigned long) ms_len, 75039beb93cSSam Leffler (unsigned long) len); 75139beb93cSSam Leffler return 0; 75239beb93cSSam Leffler } 75339beb93cSSam Leffler 75439beb93cSSam Leffler return -1; 75539beb93cSSam Leffler } 75639beb93cSSam Leffler 75739beb93cSSam Leffler 75839beb93cSSam Leffler static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, 75939beb93cSSam Leffler const struct wpabuf *reqData) 76039beb93cSSam Leffler { 76139beb93cSSam Leffler /* 76239beb93cSSam Leffler * Store a copy of the challenge message, so that it can be processed 76339beb93cSSam Leffler * again in case retry is allowed after a possible failure. 76439beb93cSSam Leffler */ 76539beb93cSSam Leffler wpabuf_free(data->prev_challenge); 76639beb93cSSam Leffler data->prev_challenge = wpabuf_dup(reqData); 76739beb93cSSam Leffler } 76839beb93cSSam Leffler 76939beb93cSSam Leffler 77039beb93cSSam Leffler /** 77139beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 request 77239beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 77339beb93cSSam Leffler * @priv: Pointer to private EAP method data from eap_mschapv2_init() 77439beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 77539beb93cSSam Leffler * @reqData: EAP request to be processed (eapReqData) 77639beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 77739beb93cSSam Leffler * no reply available 77839beb93cSSam Leffler */ 77939beb93cSSam Leffler static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, 78039beb93cSSam Leffler struct eap_method_ret *ret, 78139beb93cSSam Leffler const struct wpabuf *reqData) 78239beb93cSSam Leffler { 78339beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 78439beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 78539beb93cSSam Leffler const struct eap_mschapv2_hdr *ms; 78639beb93cSSam Leffler int using_prev_challenge = 0; 78739beb93cSSam Leffler const u8 *pos; 78839beb93cSSam Leffler size_t len; 78939beb93cSSam Leffler u8 id; 79039beb93cSSam Leffler 79139beb93cSSam Leffler if (eap_mschapv2_check_config(sm)) { 792c1d255d3SCy Schubert ret->ignore = true; 79339beb93cSSam Leffler return NULL; 79439beb93cSSam Leffler } 79539beb93cSSam Leffler 79639beb93cSSam Leffler if (config->mschapv2_retry && data->prev_challenge && 79739beb93cSSam Leffler data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 79839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " 79939beb93cSSam Leffler "with the previous challenge"); 80039beb93cSSam Leffler 80139beb93cSSam Leffler reqData = data->prev_challenge; 80239beb93cSSam Leffler using_prev_challenge = 1; 80339beb93cSSam Leffler config->mschapv2_retry = 0; 80439beb93cSSam Leffler } 80539beb93cSSam Leffler 80639beb93cSSam Leffler pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, 80739beb93cSSam Leffler &len); 80839beb93cSSam Leffler if (pos == NULL || len < sizeof(*ms) + 1) { 809c1d255d3SCy Schubert ret->ignore = true; 81039beb93cSSam Leffler return NULL; 81139beb93cSSam Leffler } 81239beb93cSSam Leffler 81339beb93cSSam Leffler ms = (const struct eap_mschapv2_hdr *) pos; 81439beb93cSSam Leffler if (eap_mschapv2_check_mslen(sm, len, ms)) { 815c1d255d3SCy Schubert ret->ignore = true; 81639beb93cSSam Leffler return NULL; 81739beb93cSSam Leffler } 81839beb93cSSam Leffler 81939beb93cSSam Leffler id = eap_get_id(reqData); 82039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", 82139beb93cSSam Leffler id, ms->mschapv2_id); 82239beb93cSSam Leffler 82339beb93cSSam Leffler switch (ms->op_code) { 82439beb93cSSam Leffler case MSCHAPV2_OP_CHALLENGE: 82539beb93cSSam Leffler if (!using_prev_challenge) 82639beb93cSSam Leffler eap_mschapv2_copy_challenge(data, reqData); 82739beb93cSSam Leffler return eap_mschapv2_challenge(sm, data, ret, ms, len, id); 82839beb93cSSam Leffler case MSCHAPV2_OP_SUCCESS: 82939beb93cSSam Leffler return eap_mschapv2_success(sm, data, ret, ms, len, id); 83039beb93cSSam Leffler case MSCHAPV2_OP_FAILURE: 83139beb93cSSam Leffler return eap_mschapv2_failure(sm, data, ret, ms, len, id); 83239beb93cSSam Leffler default: 83339beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", 83439beb93cSSam Leffler ms->op_code); 835c1d255d3SCy Schubert ret->ignore = true; 83639beb93cSSam Leffler return NULL; 83739beb93cSSam Leffler } 83839beb93cSSam Leffler } 83939beb93cSSam Leffler 84039beb93cSSam Leffler 841c1d255d3SCy Schubert static bool eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) 84239beb93cSSam Leffler { 84339beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 84439beb93cSSam Leffler return data->success && data->master_key_valid; 84539beb93cSSam Leffler } 84639beb93cSSam Leffler 84739beb93cSSam Leffler 84839beb93cSSam Leffler static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 84939beb93cSSam Leffler { 85039beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 85139beb93cSSam Leffler u8 *key; 85239beb93cSSam Leffler int key_len; 853*a90b9d01SCy Schubert bool first_is_send; 85439beb93cSSam Leffler 85539beb93cSSam Leffler if (!data->master_key_valid || !data->success) 85639beb93cSSam Leffler return NULL; 85739beb93cSSam Leffler 85839beb93cSSam Leffler key_len = 2 * MSCHAPV2_KEY_LEN; 85939beb93cSSam Leffler 86039beb93cSSam Leffler key = os_malloc(key_len); 86139beb93cSSam Leffler if (key == NULL) 86239beb93cSSam Leffler return NULL; 86339beb93cSSam Leffler 864*a90b9d01SCy Schubert /* 865*a90b9d01SCy Schubert * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation 866*a90b9d01SCy Schubert * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding) 867*a90b9d01SCy Schubert * On a Peer: 868*a90b9d01SCy Schubert * MS-MPPE-Recv-Key = MasterSendKey 869*a90b9d01SCy Schubert * MS-MPPE-Send-Key = MasterReceiveKey 870*a90b9d01SCy Schubert * 871*a90b9d01SCy Schubert * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2) 872*a90b9d01SCy Schubert * MSK = MasterSendKey + MasterReceiveKey 873*a90b9d01SCy Schubert * (i.e., reverse order and no padding) 874*a90b9d01SCy Schubert * 875*a90b9d01SCy Schubert * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2 876*a90b9d01SCy Schubert * starts with Receive key. 877*a90b9d01SCy Schubert */ 878*a90b9d01SCy Schubert first_is_send = !sm->eap_fast_mschapv2; 879*a90b9d01SCy Schubert if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 880*a90b9d01SCy Schubert first_is_send, 0) < 0 || 88139beb93cSSam Leffler get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 882*a90b9d01SCy Schubert MSCHAPV2_KEY_LEN, !first_is_send, 0) < 0) { 8834bc52338SCy Schubert os_free(key); 8844bc52338SCy Schubert return NULL; 8854bc52338SCy Schubert } 88639beb93cSSam Leffler 88739beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", 88839beb93cSSam Leffler key, key_len); 88939beb93cSSam Leffler 89039beb93cSSam Leffler *len = key_len; 89139beb93cSSam Leffler return key; 89239beb93cSSam Leffler } 89339beb93cSSam Leffler 89439beb93cSSam Leffler 89539beb93cSSam Leffler /** 89639beb93cSSam Leffler * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method 89739beb93cSSam Leffler * Returns: 0 on success, -1 on failure 89839beb93cSSam Leffler * 89939beb93cSSam Leffler * This function is used to register EAP-MSCHAPv2 peer method into the EAP 90039beb93cSSam Leffler * method list. 90139beb93cSSam Leffler */ 90239beb93cSSam Leffler int eap_peer_mschapv2_register(void) 90339beb93cSSam Leffler { 90439beb93cSSam Leffler struct eap_method *eap; 90539beb93cSSam Leffler 90639beb93cSSam Leffler eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 90739beb93cSSam Leffler EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 90839beb93cSSam Leffler "MSCHAPV2"); 90939beb93cSSam Leffler if (eap == NULL) 91039beb93cSSam Leffler return -1; 91139beb93cSSam Leffler 91239beb93cSSam Leffler eap->init = eap_mschapv2_init; 91339beb93cSSam Leffler eap->deinit = eap_mschapv2_deinit; 91439beb93cSSam Leffler eap->process = eap_mschapv2_process; 91539beb93cSSam Leffler eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; 91639beb93cSSam Leffler eap->getKey = eap_mschapv2_getKey; 91739beb93cSSam Leffler 918780fb4a2SCy Schubert return eap_peer_method_register(eap); 91939beb93cSSam Leffler } 920