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 11139beb93cSSam Leffler if (sm->peer_challenge) { 11285732ac8SCy Schubert data->peer_challenge = os_memdup(sm->peer_challenge, 11385732ac8SCy Schubert MSCHAPV2_CHAL_LEN); 11439beb93cSSam Leffler if (data->peer_challenge == NULL) { 11539beb93cSSam Leffler eap_mschapv2_deinit(sm, data); 11639beb93cSSam Leffler return NULL; 11739beb93cSSam Leffler } 11839beb93cSSam Leffler } 11939beb93cSSam Leffler 12039beb93cSSam Leffler if (sm->auth_challenge) { 12185732ac8SCy Schubert data->auth_challenge = os_memdup(sm->auth_challenge, 12285732ac8SCy Schubert MSCHAPV2_CHAL_LEN); 12339beb93cSSam Leffler if (data->auth_challenge == NULL) { 12439beb93cSSam Leffler eap_mschapv2_deinit(sm, data); 12539beb93cSSam Leffler return NULL; 12639beb93cSSam Leffler } 12739beb93cSSam Leffler } 12839beb93cSSam Leffler 12939beb93cSSam Leffler data->phase2 = sm->init_phase2; 13039beb93cSSam Leffler 13139beb93cSSam Leffler return data; 13239beb93cSSam Leffler } 13339beb93cSSam Leffler 13439beb93cSSam Leffler 13539beb93cSSam Leffler static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) 13639beb93cSSam Leffler { 13739beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 13839beb93cSSam Leffler os_free(data->peer_challenge); 13939beb93cSSam Leffler os_free(data->auth_challenge); 14039beb93cSSam Leffler wpabuf_free(data->prev_challenge); 1415b9c547cSRui Paulo bin_clear_free(data, sizeof(*data)); 14239beb93cSSam Leffler } 14339beb93cSSam Leffler 14439beb93cSSam Leffler 14539beb93cSSam Leffler static struct wpabuf * eap_mschapv2_challenge_reply( 14639beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, 14739beb93cSSam Leffler u8 mschapv2_id, const u8 *auth_challenge) 14839beb93cSSam Leffler { 14939beb93cSSam Leffler struct wpabuf *resp; 15039beb93cSSam Leffler struct eap_mschapv2_hdr *ms; 15139beb93cSSam Leffler u8 *peer_challenge; 15239beb93cSSam Leffler int ms_len; 15339beb93cSSam Leffler struct ms_response *r; 15439beb93cSSam Leffler size_t identity_len, password_len; 15539beb93cSSam Leffler const u8 *identity, *password; 15639beb93cSSam Leffler int pwhash; 15739beb93cSSam Leffler 15839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); 15939beb93cSSam Leffler 16039beb93cSSam Leffler identity = eap_get_config_identity(sm, &identity_len); 16139beb93cSSam Leffler password = eap_get_config_password2(sm, &password_len, &pwhash); 16239beb93cSSam Leffler if (identity == NULL || password == NULL) 16339beb93cSSam Leffler return NULL; 16439beb93cSSam Leffler 16539beb93cSSam Leffler ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; 16639beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 16739beb93cSSam Leffler EAP_CODE_RESPONSE, id); 16839beb93cSSam Leffler if (resp == NULL) 16939beb93cSSam Leffler return NULL; 17039beb93cSSam Leffler 17139beb93cSSam Leffler ms = wpabuf_put(resp, sizeof(*ms)); 17239beb93cSSam Leffler ms->op_code = MSCHAPV2_OP_RESPONSE; 17339beb93cSSam Leffler ms->mschapv2_id = mschapv2_id; 17439beb93cSSam Leffler if (data->prev_error) { 17539beb93cSSam Leffler /* 17639beb93cSSam Leffler * TODO: this does not seem to be enough when processing two 17739beb93cSSam Leffler * or more failure messages. IAS did not increment mschapv2_id 17839beb93cSSam Leffler * in its own packets, but it seemed to expect the peer to 17939beb93cSSam Leffler * increment this for all packets(?). 18039beb93cSSam Leffler */ 18139beb93cSSam Leffler ms->mschapv2_id++; 18239beb93cSSam Leffler } 18339beb93cSSam Leffler WPA_PUT_BE16(ms->ms_length, ms_len); 18439beb93cSSam Leffler 18539beb93cSSam Leffler wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ 18639beb93cSSam Leffler 18739beb93cSSam Leffler /* Response */ 18839beb93cSSam Leffler r = wpabuf_put(resp, sizeof(*r)); 18939beb93cSSam Leffler peer_challenge = r->peer_challenge; 19039beb93cSSam Leffler if (data->peer_challenge) { 19139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " 19239beb93cSSam Leffler "in Phase 1"); 19339beb93cSSam Leffler peer_challenge = data->peer_challenge; 19439beb93cSSam Leffler os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); 195f05cddf9SRui Paulo } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { 19639beb93cSSam Leffler wpabuf_free(resp); 19739beb93cSSam Leffler return NULL; 19839beb93cSSam Leffler } 19939beb93cSSam Leffler os_memset(r->reserved, 0, 8); 20039beb93cSSam Leffler if (data->auth_challenge) { 20139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " 20239beb93cSSam Leffler "in Phase 1"); 20339beb93cSSam Leffler auth_challenge = data->auth_challenge; 20439beb93cSSam Leffler } 205e28a4053SRui Paulo if (mschapv2_derive_response(identity, identity_len, password, 20639beb93cSSam Leffler password_len, pwhash, auth_challenge, 20739beb93cSSam Leffler peer_challenge, r->nt_response, 208e28a4053SRui Paulo data->auth_response, data->master_key)) { 209e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " 210e28a4053SRui Paulo "response"); 211e28a4053SRui Paulo wpabuf_free(resp); 212e28a4053SRui Paulo return NULL; 213e28a4053SRui Paulo } 21439beb93cSSam Leffler data->auth_response_valid = 1; 21539beb93cSSam Leffler data->master_key_valid = 1; 21639beb93cSSam Leffler 21739beb93cSSam Leffler r->flags = 0; /* reserved, must be zero */ 21839beb93cSSam Leffler 21939beb93cSSam Leffler wpabuf_put_data(resp, identity, identity_len); 22039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 22139beb93cSSam Leffler "(response)", id, ms->mschapv2_id); 22239beb93cSSam Leffler return resp; 22339beb93cSSam Leffler } 22439beb93cSSam Leffler 22539beb93cSSam Leffler 22639beb93cSSam Leffler /** 22739beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message 22839beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 22939beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 23039beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 23139beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 23239beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 23339beb93cSSam Leffler * @id: EAP identifier used in the request 23439beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 23539beb93cSSam Leffler * no reply available 23639beb93cSSam Leffler */ 23739beb93cSSam Leffler static struct wpabuf * eap_mschapv2_challenge( 23839beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, 23939beb93cSSam Leffler struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, 24039beb93cSSam Leffler size_t req_len, u8 id) 24139beb93cSSam Leffler { 24239beb93cSSam Leffler size_t len, challenge_len; 24339beb93cSSam Leffler const u8 *pos, *challenge; 24439beb93cSSam Leffler 24539beb93cSSam Leffler if (eap_get_config_identity(sm, &len) == NULL || 24639beb93cSSam Leffler eap_get_config_password(sm, &len) == NULL) 24739beb93cSSam Leffler return NULL; 24839beb93cSSam Leffler 24939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); 25039beb93cSSam Leffler if (req_len < sizeof(*req) + 1) { 25139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " 25239beb93cSSam Leffler "(len %lu)", (unsigned long) req_len); 25339beb93cSSam Leffler ret->ignore = TRUE; 25439beb93cSSam Leffler return NULL; 25539beb93cSSam Leffler } 25639beb93cSSam Leffler pos = (const u8 *) (req + 1); 25739beb93cSSam Leffler challenge_len = *pos++; 25839beb93cSSam Leffler len = req_len - sizeof(*req) - 1; 25939beb93cSSam Leffler if (challenge_len != MSCHAPV2_CHAL_LEN) { 26039beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " 26139beb93cSSam Leffler "%lu", (unsigned long) challenge_len); 26239beb93cSSam Leffler ret->ignore = TRUE; 26339beb93cSSam Leffler return NULL; 26439beb93cSSam Leffler } 26539beb93cSSam Leffler 26639beb93cSSam Leffler if (len < challenge_len) { 26739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" 26839beb93cSSam Leffler " packet: len=%lu challenge_len=%lu", 26939beb93cSSam Leffler (unsigned long) len, (unsigned long) challenge_len); 27039beb93cSSam Leffler ret->ignore = TRUE; 27139beb93cSSam Leffler return NULL; 27239beb93cSSam Leffler } 27339beb93cSSam Leffler 27439beb93cSSam Leffler if (data->passwd_change_challenge_valid) { 27539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " 27639beb93cSSam Leffler "failure message"); 27739beb93cSSam Leffler challenge = data->passwd_change_challenge; 27839beb93cSSam Leffler } else 27939beb93cSSam Leffler challenge = pos; 28039beb93cSSam Leffler pos += challenge_len; 28139beb93cSSam Leffler len -= challenge_len; 28239beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", 28339beb93cSSam Leffler pos, len); 28439beb93cSSam Leffler 28539beb93cSSam Leffler ret->ignore = FALSE; 28639beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT; 28739beb93cSSam Leffler ret->decision = DECISION_FAIL; 28839beb93cSSam Leffler ret->allowNotifications = TRUE; 28939beb93cSSam Leffler 29039beb93cSSam Leffler return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, 29139beb93cSSam Leffler challenge); 29239beb93cSSam Leffler } 29339beb93cSSam Leffler 29439beb93cSSam Leffler 29539beb93cSSam Leffler static void eap_mschapv2_password_changed(struct eap_sm *sm, 29639beb93cSSam Leffler struct eap_mschapv2_data *data) 29739beb93cSSam Leffler { 29839beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 29939beb93cSSam Leffler if (config && config->new_password) { 30039beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_INFO, 30139beb93cSSam Leffler WPA_EVENT_PASSWORD_CHANGED 30239beb93cSSam Leffler "EAP-MSCHAPV2: Password changed successfully"); 30339beb93cSSam Leffler data->prev_error = 0; 3045b9c547cSRui Paulo bin_clear_free(config->password, config->password_len); 305f05cddf9SRui Paulo if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { 306f05cddf9SRui Paulo /* TODO: update external storage */ 307f05cddf9SRui Paulo } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { 30839beb93cSSam Leffler config->password = os_malloc(16); 30939beb93cSSam Leffler config->password_len = 16; 3105b9c547cSRui Paulo if (config->password && 31139beb93cSSam Leffler nt_password_hash(config->new_password, 31239beb93cSSam Leffler config->new_password_len, 3135b9c547cSRui Paulo config->password)) { 3145b9c547cSRui Paulo bin_clear_free(config->password, 3155b9c547cSRui Paulo config->password_len); 3165b9c547cSRui Paulo config->password = NULL; 3175b9c547cSRui Paulo config->password_len = 0; 31839beb93cSSam Leffler } 3195b9c547cSRui Paulo bin_clear_free(config->new_password, 3205b9c547cSRui Paulo config->new_password_len); 32139beb93cSSam Leffler } else { 32239beb93cSSam Leffler config->password = config->new_password; 32339beb93cSSam Leffler config->password_len = config->new_password_len; 32439beb93cSSam Leffler } 32539beb93cSSam Leffler config->new_password = NULL; 32639beb93cSSam Leffler config->new_password_len = 0; 32739beb93cSSam Leffler } 32839beb93cSSam Leffler } 32939beb93cSSam Leffler 33039beb93cSSam Leffler 33139beb93cSSam Leffler /** 33239beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message 33339beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 33439beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 33539beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 33639beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 33739beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 33839beb93cSSam Leffler * @id: EAP identifier used in th erequest 33939beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 34039beb93cSSam Leffler * no reply available 34139beb93cSSam Leffler */ 34239beb93cSSam Leffler static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, 34339beb93cSSam Leffler struct eap_mschapv2_data *data, 34439beb93cSSam Leffler struct eap_method_ret *ret, 34539beb93cSSam Leffler const struct eap_mschapv2_hdr *req, 34639beb93cSSam Leffler size_t req_len, u8 id) 34739beb93cSSam Leffler { 34839beb93cSSam Leffler struct wpabuf *resp; 34939beb93cSSam Leffler const u8 *pos; 35039beb93cSSam Leffler size_t len; 35139beb93cSSam Leffler 35239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); 35339beb93cSSam Leffler len = req_len - sizeof(*req); 35439beb93cSSam Leffler pos = (const u8 *) (req + 1); 35539beb93cSSam Leffler if (!data->auth_response_valid || 35639beb93cSSam Leffler mschapv2_verify_auth_response(data->auth_response, pos, len)) { 35739beb93cSSam Leffler wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " 35839beb93cSSam Leffler "response in success request"); 35939beb93cSSam Leffler ret->methodState = METHOD_DONE; 36039beb93cSSam Leffler ret->decision = DECISION_FAIL; 36139beb93cSSam Leffler return NULL; 36239beb93cSSam Leffler } 36339beb93cSSam Leffler pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 36439beb93cSSam Leffler len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; 36539beb93cSSam Leffler while (len > 0 && *pos == ' ') { 36639beb93cSSam Leffler pos++; 36739beb93cSSam Leffler len--; 36839beb93cSSam Leffler } 36939beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", 37039beb93cSSam Leffler pos, len); 37139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); 37239beb93cSSam Leffler 37339beb93cSSam Leffler /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success 37439beb93cSSam Leffler * message. */ 37539beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 37639beb93cSSam Leffler EAP_CODE_RESPONSE, id); 37739beb93cSSam Leffler if (resp == NULL) { 37839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " 37939beb93cSSam Leffler "buffer for success response"); 38039beb93cSSam Leffler ret->ignore = TRUE; 38139beb93cSSam Leffler return NULL; 38239beb93cSSam Leffler } 38339beb93cSSam Leffler 38439beb93cSSam Leffler wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ 38539beb93cSSam Leffler 38639beb93cSSam Leffler ret->methodState = METHOD_DONE; 38739beb93cSSam Leffler ret->decision = DECISION_UNCOND_SUCC; 38839beb93cSSam Leffler ret->allowNotifications = FALSE; 38939beb93cSSam Leffler data->success = 1; 39039beb93cSSam Leffler 39139beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED) 39239beb93cSSam Leffler eap_mschapv2_password_changed(sm, data); 39339beb93cSSam Leffler 39439beb93cSSam Leffler return resp; 39539beb93cSSam Leffler } 39639beb93cSSam Leffler 39739beb93cSSam Leffler 39839beb93cSSam Leffler static int eap_mschapv2_failure_txt(struct eap_sm *sm, 39939beb93cSSam Leffler struct eap_mschapv2_data *data, char *txt) 40039beb93cSSam Leffler { 40139beb93cSSam Leffler char *pos, *msg = ""; 40239beb93cSSam Leffler int retry = 1; 40339beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 40439beb93cSSam Leffler 40539beb93cSSam Leffler /* For example: 40639beb93cSSam Leffler * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure 40739beb93cSSam Leffler */ 40839beb93cSSam Leffler 40939beb93cSSam Leffler pos = txt; 41039beb93cSSam Leffler 41139beb93cSSam Leffler if (pos && os_strncmp(pos, "E=", 2) == 0) { 41239beb93cSSam Leffler pos += 2; 41339beb93cSSam Leffler data->prev_error = atoi(pos); 41439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", 41539beb93cSSam Leffler data->prev_error); 41639beb93cSSam Leffler pos = os_strchr(pos, ' '); 41739beb93cSSam Leffler if (pos) 41839beb93cSSam Leffler pos++; 41939beb93cSSam Leffler } 42039beb93cSSam Leffler 42139beb93cSSam Leffler if (pos && os_strncmp(pos, "R=", 2) == 0) { 42239beb93cSSam Leffler pos += 2; 42339beb93cSSam Leffler retry = atoi(pos); 42439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", 42539beb93cSSam Leffler retry == 1 ? "" : "not "); 42639beb93cSSam Leffler pos = os_strchr(pos, ' '); 42739beb93cSSam Leffler if (pos) 42839beb93cSSam Leffler pos++; 42939beb93cSSam Leffler } 43039beb93cSSam Leffler 43139beb93cSSam Leffler if (pos && os_strncmp(pos, "C=", 2) == 0) { 43239beb93cSSam Leffler int hex_len; 43339beb93cSSam Leffler pos += 2; 43439beb93cSSam Leffler hex_len = os_strchr(pos, ' ') - (char *) pos; 43539beb93cSSam Leffler if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { 43639beb93cSSam Leffler if (hexstr2bin(pos, data->passwd_change_challenge, 43739beb93cSSam Leffler PASSWD_CHANGE_CHAL_LEN)) { 43839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " 43939beb93cSSam Leffler "failure challenge"); 44039beb93cSSam Leffler } else { 44139beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " 44239beb93cSSam Leffler "challenge", 44339beb93cSSam Leffler data->passwd_change_challenge, 44439beb93cSSam Leffler PASSWD_CHANGE_CHAL_LEN); 44539beb93cSSam Leffler data->passwd_change_challenge_valid = 1; 44639beb93cSSam Leffler } 44739beb93cSSam Leffler } else { 44839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " 44939beb93cSSam Leffler "challenge len %d", hex_len); 45039beb93cSSam Leffler } 45139beb93cSSam Leffler pos = os_strchr(pos, ' '); 45239beb93cSSam Leffler if (pos) 45339beb93cSSam Leffler pos++; 45439beb93cSSam Leffler } else { 45539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " 45639beb93cSSam Leffler "was not present in failure message"); 45739beb93cSSam Leffler } 45839beb93cSSam Leffler 45939beb93cSSam Leffler if (pos && os_strncmp(pos, "V=", 2) == 0) { 46039beb93cSSam Leffler pos += 2; 46139beb93cSSam Leffler data->passwd_change_version = atoi(pos); 46239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " 46339beb93cSSam Leffler "protocol version %d", data->passwd_change_version); 46439beb93cSSam Leffler pos = os_strchr(pos, ' '); 46539beb93cSSam Leffler if (pos) 46639beb93cSSam Leffler pos++; 46739beb93cSSam Leffler } 46839beb93cSSam Leffler 46939beb93cSSam Leffler if (pos && os_strncmp(pos, "M=", 2) == 0) { 47039beb93cSSam Leffler pos += 2; 47139beb93cSSam Leffler msg = pos; 47239beb93cSSam Leffler } 4735b9c547cSRui Paulo if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry && 4745b9c547cSRui Paulo config && config->phase2 && 4755b9c547cSRui Paulo os_strstr(config->phase2, "mschapv2_retry=0")) { 4765b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 4775b9c547cSRui Paulo "EAP-MSCHAPV2: mark password retry disabled based on local configuration"); 4785b9c547cSRui Paulo retry = 0; 4795b9c547cSRui Paulo } 48039beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_WARNING, 48139beb93cSSam Leffler "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " 48239beb93cSSam Leffler "%d)", 48339beb93cSSam Leffler msg, retry == 1 ? "" : "not ", data->prev_error); 48439beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED && 48539beb93cSSam Leffler data->passwd_change_version == 3 && config) { 48639beb93cSSam Leffler if (config->new_password == NULL) { 48739beb93cSSam Leffler wpa_msg(sm->msg_ctx, MSG_INFO, 48839beb93cSSam Leffler "EAP-MSCHAPV2: Password expired - password " 48939beb93cSSam Leffler "change required"); 49039beb93cSSam Leffler eap_sm_request_new_password(sm); 49139beb93cSSam Leffler } 49239beb93cSSam Leffler } else if (retry == 1 && config) { 49339beb93cSSam Leffler /* TODO: could prevent the current password from being used 49439beb93cSSam Leffler * again at least for some period of time */ 49539beb93cSSam Leffler if (!config->mschapv2_retry) 49639beb93cSSam Leffler eap_sm_request_identity(sm); 49739beb93cSSam Leffler eap_sm_request_password(sm); 49839beb93cSSam Leffler config->mschapv2_retry = 1; 49939beb93cSSam Leffler } else if (config) { 50039beb93cSSam Leffler /* TODO: prevent retries using same username/password */ 50139beb93cSSam Leffler config->mschapv2_retry = 0; 50239beb93cSSam Leffler } 50339beb93cSSam Leffler 50439beb93cSSam Leffler return retry == 1; 50539beb93cSSam Leffler } 50639beb93cSSam Leffler 50739beb93cSSam Leffler 50839beb93cSSam Leffler static struct wpabuf * eap_mschapv2_change_password( 50939beb93cSSam Leffler struct eap_sm *sm, struct eap_mschapv2_data *data, 51039beb93cSSam Leffler struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) 51139beb93cSSam Leffler { 512325151a3SRui Paulo #ifdef CONFIG_NO_RC4 513325151a3SRui Paulo wpa_printf(MSG_ERROR, 514325151a3SRui Paulo "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); 515325151a3SRui Paulo return NULL; 516325151a3SRui Paulo #else /* CONFIG_NO_RC4 */ 51739beb93cSSam Leffler struct wpabuf *resp; 51839beb93cSSam Leffler int ms_len; 51939beb93cSSam Leffler const u8 *username, *password, *new_password; 52039beb93cSSam Leffler size_t username_len, password_len, new_password_len; 52139beb93cSSam Leffler struct eap_mschapv2_hdr *ms; 52239beb93cSSam Leffler struct ms_change_password *cp; 52339beb93cSSam Leffler u8 password_hash[16], password_hash_hash[16]; 52439beb93cSSam Leffler int pwhash; 52539beb93cSSam Leffler 52639beb93cSSam Leffler username = eap_get_config_identity(sm, &username_len); 52739beb93cSSam Leffler password = eap_get_config_password2(sm, &password_len, &pwhash); 52839beb93cSSam Leffler new_password = eap_get_config_new_password(sm, &new_password_len); 52939beb93cSSam Leffler if (username == NULL || password == NULL || new_password == NULL) 53039beb93cSSam Leffler return NULL; 53139beb93cSSam Leffler 53239beb93cSSam Leffler username = mschapv2_remove_domain(username, &username_len); 53339beb93cSSam Leffler 53439beb93cSSam Leffler ret->ignore = FALSE; 53539beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT; 53639beb93cSSam Leffler ret->decision = DECISION_COND_SUCC; 53739beb93cSSam Leffler ret->allowNotifications = TRUE; 53839beb93cSSam Leffler 53939beb93cSSam Leffler ms_len = sizeof(*ms) + sizeof(*cp); 54039beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, 54139beb93cSSam Leffler EAP_CODE_RESPONSE, id); 54239beb93cSSam Leffler if (resp == NULL) 54339beb93cSSam Leffler return NULL; 54439beb93cSSam Leffler 54539beb93cSSam Leffler ms = wpabuf_put(resp, sizeof(*ms)); 54639beb93cSSam Leffler ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; 54739beb93cSSam Leffler ms->mschapv2_id = req->mschapv2_id + 1; 54839beb93cSSam Leffler WPA_PUT_BE16(ms->ms_length, ms_len); 54939beb93cSSam Leffler cp = wpabuf_put(resp, sizeof(*cp)); 55039beb93cSSam Leffler 55139beb93cSSam Leffler /* Encrypted-Password */ 55239beb93cSSam Leffler if (pwhash) { 55339beb93cSSam Leffler if (encrypt_pw_block_with_password_hash( 55439beb93cSSam Leffler new_password, new_password_len, 55539beb93cSSam Leffler password, cp->encr_password)) 55639beb93cSSam Leffler goto fail; 55739beb93cSSam Leffler } else { 55839beb93cSSam Leffler if (new_password_encrypted_with_old_nt_password_hash( 55939beb93cSSam Leffler new_password, new_password_len, 56039beb93cSSam Leffler password, password_len, cp->encr_password)) 56139beb93cSSam Leffler goto fail; 56239beb93cSSam Leffler } 56339beb93cSSam Leffler 56439beb93cSSam Leffler /* Encrypted-Hash */ 56539beb93cSSam Leffler if (pwhash) { 56639beb93cSSam Leffler u8 new_password_hash[16]; 5675b9c547cSRui Paulo if (nt_password_hash(new_password, new_password_len, 56885732ac8SCy Schubert new_password_hash) || 56939beb93cSSam Leffler nt_password_hash_encrypted_with_block(password, 57039beb93cSSam Leffler new_password_hash, 57185732ac8SCy Schubert cp->encr_hash)) 57285732ac8SCy Schubert goto fail; 57339beb93cSSam Leffler } else { 5745b9c547cSRui Paulo if (old_nt_password_hash_encrypted_with_new_nt_password_hash( 57539beb93cSSam Leffler new_password, new_password_len, 5765b9c547cSRui Paulo password, password_len, cp->encr_hash)) 5775b9c547cSRui Paulo goto fail; 57839beb93cSSam Leffler } 57939beb93cSSam Leffler 58039beb93cSSam Leffler /* Peer-Challenge */ 581f05cddf9SRui Paulo if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) 58239beb93cSSam Leffler goto fail; 58339beb93cSSam Leffler 58439beb93cSSam Leffler /* Reserved, must be zero */ 58539beb93cSSam Leffler os_memset(cp->reserved, 0, 8); 58639beb93cSSam Leffler 58739beb93cSSam Leffler /* NT-Response */ 58839beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", 58939beb93cSSam Leffler data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); 59039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", 59139beb93cSSam Leffler cp->peer_challenge, MSCHAPV2_CHAL_LEN); 59239beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", 59339beb93cSSam Leffler username, username_len); 59439beb93cSSam Leffler wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", 59539beb93cSSam Leffler new_password, new_password_len); 59639beb93cSSam Leffler generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, 59739beb93cSSam Leffler username, username_len, 59839beb93cSSam Leffler new_password, new_password_len, 59939beb93cSSam Leffler cp->nt_response); 60039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", 60139beb93cSSam Leffler cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); 60239beb93cSSam Leffler 60339beb93cSSam Leffler /* Authenticator response is not really needed yet, but calculate it 60439beb93cSSam Leffler * here so that challenges need not be saved. */ 60539beb93cSSam Leffler generate_authenticator_response(new_password, new_password_len, 60639beb93cSSam Leffler cp->peer_challenge, 60739beb93cSSam Leffler data->passwd_change_challenge, 60839beb93cSSam Leffler username, username_len, 60939beb93cSSam Leffler cp->nt_response, data->auth_response); 61039beb93cSSam Leffler data->auth_response_valid = 1; 61139beb93cSSam Leffler 61239beb93cSSam Leffler /* Likewise, generate master_key here since we have the needed data 61339beb93cSSam Leffler * available. */ 6145b9c547cSRui Paulo if (nt_password_hash(new_password, new_password_len, password_hash) || 6155b9c547cSRui Paulo hash_nt_password_hash(password_hash, password_hash_hash) || 6165b9c547cSRui Paulo get_master_key(password_hash_hash, cp->nt_response, 6175b9c547cSRui Paulo data->master_key)) { 6185b9c547cSRui Paulo data->auth_response_valid = 0; 6195b9c547cSRui Paulo goto fail; 6205b9c547cSRui Paulo } 62139beb93cSSam Leffler data->master_key_valid = 1; 62239beb93cSSam Leffler 62339beb93cSSam Leffler /* Flags */ 62439beb93cSSam Leffler os_memset(cp->flags, 0, 2); 62539beb93cSSam Leffler 62639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " 62739beb93cSSam Leffler "(change pw)", id, ms->mschapv2_id); 62839beb93cSSam Leffler 62939beb93cSSam Leffler return resp; 63039beb93cSSam Leffler 63139beb93cSSam Leffler fail: 63239beb93cSSam Leffler wpabuf_free(resp); 63339beb93cSSam Leffler return NULL; 634325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */ 63539beb93cSSam Leffler } 63639beb93cSSam Leffler 63739beb93cSSam Leffler 63839beb93cSSam Leffler /** 63939beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message 64039beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 64139beb93cSSam Leffler * @data: Pointer to private EAP method data from eap_mschapv2_init() 64239beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 64339beb93cSSam Leffler * @req: Pointer to EAP-MSCHAPv2 header from the request 64439beb93cSSam Leffler * @req_len: Length of the EAP-MSCHAPv2 data 64539beb93cSSam Leffler * @id: EAP identifier used in th erequest 64639beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 64739beb93cSSam Leffler * no reply available 64839beb93cSSam Leffler */ 64939beb93cSSam Leffler static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, 65039beb93cSSam Leffler struct eap_mschapv2_data *data, 65139beb93cSSam Leffler struct eap_method_ret *ret, 65239beb93cSSam Leffler const struct eap_mschapv2_hdr *req, 65339beb93cSSam Leffler size_t req_len, u8 id) 65439beb93cSSam Leffler { 65539beb93cSSam Leffler struct wpabuf *resp; 65639beb93cSSam Leffler const u8 *msdata = (const u8 *) (req + 1); 65739beb93cSSam Leffler char *buf; 65839beb93cSSam Leffler size_t len = req_len - sizeof(*req); 65939beb93cSSam Leffler int retry = 0; 66039beb93cSSam Leffler 66139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); 66239beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", 66339beb93cSSam Leffler msdata, len); 66439beb93cSSam Leffler /* 66539beb93cSSam Leffler * eap_mschapv2_failure_txt() expects a nul terminated string, so we 66639beb93cSSam Leffler * must allocate a large enough temporary buffer to create that since 66739beb93cSSam Leffler * the received message does not include nul termination. 66839beb93cSSam Leffler */ 6695b9c547cSRui Paulo buf = dup_binstr(msdata, len); 67039beb93cSSam Leffler if (buf) { 67139beb93cSSam Leffler retry = eap_mschapv2_failure_txt(sm, data, buf); 67239beb93cSSam Leffler os_free(buf); 67339beb93cSSam Leffler } 67439beb93cSSam Leffler 67539beb93cSSam Leffler ret->ignore = FALSE; 67639beb93cSSam Leffler ret->methodState = METHOD_DONE; 67739beb93cSSam Leffler ret->decision = DECISION_FAIL; 67839beb93cSSam Leffler ret->allowNotifications = FALSE; 67939beb93cSSam Leffler 68039beb93cSSam Leffler if (data->prev_error == ERROR_PASSWD_EXPIRED && 68139beb93cSSam Leffler data->passwd_change_version == 3) { 68239beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 68339beb93cSSam Leffler if (config && config->new_password) 68439beb93cSSam Leffler return eap_mschapv2_change_password(sm, data, ret, req, 68539beb93cSSam Leffler id); 68639beb93cSSam Leffler if (config && config->pending_req_new_password) 68739beb93cSSam Leffler return NULL; 68839beb93cSSam Leffler } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 68939beb93cSSam Leffler /* TODO: could try to retry authentication, e.g, after having 69039beb93cSSam Leffler * changed the username/password. In this case, EAP MS-CHAP-v2 69139beb93cSSam Leffler * Failure Response would not be sent here. */ 69239beb93cSSam Leffler return NULL; 69339beb93cSSam Leffler } 69439beb93cSSam Leffler 69539beb93cSSam Leffler /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure 69639beb93cSSam Leffler * message. */ 69739beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, 69839beb93cSSam Leffler EAP_CODE_RESPONSE, id); 69939beb93cSSam Leffler if (resp == NULL) 70039beb93cSSam Leffler return NULL; 70139beb93cSSam Leffler 70239beb93cSSam Leffler wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ 70339beb93cSSam Leffler 70439beb93cSSam Leffler return resp; 70539beb93cSSam Leffler } 70639beb93cSSam Leffler 70739beb93cSSam Leffler 70839beb93cSSam Leffler static int eap_mschapv2_check_config(struct eap_sm *sm) 70939beb93cSSam Leffler { 71039beb93cSSam Leffler size_t len; 71139beb93cSSam Leffler 71239beb93cSSam Leffler if (eap_get_config_identity(sm, &len) == NULL) { 71339beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); 71439beb93cSSam Leffler eap_sm_request_identity(sm); 71539beb93cSSam Leffler return -1; 71639beb93cSSam Leffler } 71739beb93cSSam Leffler 71839beb93cSSam Leffler if (eap_get_config_password(sm, &len) == NULL) { 71939beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); 72039beb93cSSam Leffler eap_sm_request_password(sm); 72139beb93cSSam Leffler return -1; 72239beb93cSSam Leffler } 72339beb93cSSam Leffler 72439beb93cSSam Leffler return 0; 72539beb93cSSam Leffler } 72639beb93cSSam Leffler 72739beb93cSSam Leffler 72839beb93cSSam Leffler static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, 72939beb93cSSam Leffler const struct eap_mschapv2_hdr *ms) 73039beb93cSSam Leffler { 73139beb93cSSam Leffler size_t ms_len = WPA_GET_BE16(ms->ms_length); 73239beb93cSSam Leffler 73339beb93cSSam Leffler if (ms_len == len) 73439beb93cSSam Leffler return 0; 73539beb93cSSam Leffler 73639beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " 73739beb93cSSam Leffler "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); 73839beb93cSSam Leffler if (sm->workaround) { 73939beb93cSSam Leffler /* Some authentication servers use invalid ms_len, 74039beb93cSSam Leffler * ignore it for interoperability. */ 74139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" 74239beb93cSSam Leffler " invalid ms_len %lu (len %lu)", 74339beb93cSSam Leffler (unsigned long) ms_len, 74439beb93cSSam Leffler (unsigned long) len); 74539beb93cSSam Leffler return 0; 74639beb93cSSam Leffler } 74739beb93cSSam Leffler 74839beb93cSSam Leffler return -1; 74939beb93cSSam Leffler } 75039beb93cSSam Leffler 75139beb93cSSam Leffler 75239beb93cSSam Leffler static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, 75339beb93cSSam Leffler const struct wpabuf *reqData) 75439beb93cSSam Leffler { 75539beb93cSSam Leffler /* 75639beb93cSSam Leffler * Store a copy of the challenge message, so that it can be processed 75739beb93cSSam Leffler * again in case retry is allowed after a possible failure. 75839beb93cSSam Leffler */ 75939beb93cSSam Leffler wpabuf_free(data->prev_challenge); 76039beb93cSSam Leffler data->prev_challenge = wpabuf_dup(reqData); 76139beb93cSSam Leffler } 76239beb93cSSam Leffler 76339beb93cSSam Leffler 76439beb93cSSam Leffler /** 76539beb93cSSam Leffler * eap_mschapv2_process - Process an EAP-MSCHAPv2 request 76639beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 76739beb93cSSam Leffler * @priv: Pointer to private EAP method data from eap_mschapv2_init() 76839beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 76939beb93cSSam Leffler * @reqData: EAP request to be processed (eapReqData) 77039beb93cSSam Leffler * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if 77139beb93cSSam Leffler * no reply available 77239beb93cSSam Leffler */ 77339beb93cSSam Leffler static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, 77439beb93cSSam Leffler struct eap_method_ret *ret, 77539beb93cSSam Leffler const struct wpabuf *reqData) 77639beb93cSSam Leffler { 77739beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 77839beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 77939beb93cSSam Leffler const struct eap_mschapv2_hdr *ms; 78039beb93cSSam Leffler int using_prev_challenge = 0; 78139beb93cSSam Leffler const u8 *pos; 78239beb93cSSam Leffler size_t len; 78339beb93cSSam Leffler u8 id; 78439beb93cSSam Leffler 78539beb93cSSam Leffler if (eap_mschapv2_check_config(sm)) { 78639beb93cSSam Leffler ret->ignore = TRUE; 78739beb93cSSam Leffler return NULL; 78839beb93cSSam Leffler } 78939beb93cSSam Leffler 79039beb93cSSam Leffler if (config->mschapv2_retry && data->prev_challenge && 79139beb93cSSam Leffler data->prev_error == ERROR_AUTHENTICATION_FAILURE) { 79239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " 79339beb93cSSam Leffler "with the previous challenge"); 79439beb93cSSam Leffler 79539beb93cSSam Leffler reqData = data->prev_challenge; 79639beb93cSSam Leffler using_prev_challenge = 1; 79739beb93cSSam Leffler config->mschapv2_retry = 0; 79839beb93cSSam Leffler } 79939beb93cSSam Leffler 80039beb93cSSam Leffler pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, 80139beb93cSSam Leffler &len); 80239beb93cSSam Leffler if (pos == NULL || len < sizeof(*ms) + 1) { 80339beb93cSSam Leffler ret->ignore = TRUE; 80439beb93cSSam Leffler return NULL; 80539beb93cSSam Leffler } 80639beb93cSSam Leffler 80739beb93cSSam Leffler ms = (const struct eap_mschapv2_hdr *) pos; 80839beb93cSSam Leffler if (eap_mschapv2_check_mslen(sm, len, ms)) { 80939beb93cSSam Leffler ret->ignore = TRUE; 81039beb93cSSam Leffler return NULL; 81139beb93cSSam Leffler } 81239beb93cSSam Leffler 81339beb93cSSam Leffler id = eap_get_id(reqData); 81439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", 81539beb93cSSam Leffler id, ms->mschapv2_id); 81639beb93cSSam Leffler 81739beb93cSSam Leffler switch (ms->op_code) { 81839beb93cSSam Leffler case MSCHAPV2_OP_CHALLENGE: 81939beb93cSSam Leffler if (!using_prev_challenge) 82039beb93cSSam Leffler eap_mschapv2_copy_challenge(data, reqData); 82139beb93cSSam Leffler return eap_mschapv2_challenge(sm, data, ret, ms, len, id); 82239beb93cSSam Leffler case MSCHAPV2_OP_SUCCESS: 82339beb93cSSam Leffler return eap_mschapv2_success(sm, data, ret, ms, len, id); 82439beb93cSSam Leffler case MSCHAPV2_OP_FAILURE: 82539beb93cSSam Leffler return eap_mschapv2_failure(sm, data, ret, ms, len, id); 82639beb93cSSam Leffler default: 82739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", 82839beb93cSSam Leffler ms->op_code); 82939beb93cSSam Leffler ret->ignore = TRUE; 83039beb93cSSam Leffler return NULL; 83139beb93cSSam Leffler } 83239beb93cSSam Leffler } 83339beb93cSSam Leffler 83439beb93cSSam Leffler 83539beb93cSSam Leffler static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) 83639beb93cSSam Leffler { 83739beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 83839beb93cSSam Leffler return data->success && data->master_key_valid; 83939beb93cSSam Leffler } 84039beb93cSSam Leffler 84139beb93cSSam Leffler 84239beb93cSSam Leffler static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) 84339beb93cSSam Leffler { 84439beb93cSSam Leffler struct eap_mschapv2_data *data = priv; 84539beb93cSSam Leffler u8 *key; 84639beb93cSSam Leffler int key_len; 84739beb93cSSam Leffler 84839beb93cSSam Leffler if (!data->master_key_valid || !data->success) 84939beb93cSSam Leffler return NULL; 85039beb93cSSam Leffler 85139beb93cSSam Leffler key_len = 2 * MSCHAPV2_KEY_LEN; 85239beb93cSSam Leffler 85339beb93cSSam Leffler key = os_malloc(key_len); 85439beb93cSSam Leffler if (key == NULL) 85539beb93cSSam Leffler return NULL; 85639beb93cSSam Leffler 85739beb93cSSam Leffler /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., 85839beb93cSSam Leffler * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ 859*4bc52338SCy Schubert if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 860*4bc52338SCy Schubert 0) < 0 || 86139beb93cSSam Leffler get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, 862*4bc52338SCy Schubert MSCHAPV2_KEY_LEN, 0, 0) < 0) { 863*4bc52338SCy Schubert os_free(key); 864*4bc52338SCy Schubert return NULL; 865*4bc52338SCy Schubert } 86639beb93cSSam Leffler 86739beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", 86839beb93cSSam Leffler key, key_len); 86939beb93cSSam Leffler 87039beb93cSSam Leffler *len = key_len; 87139beb93cSSam Leffler return key; 87239beb93cSSam Leffler } 87339beb93cSSam Leffler 87439beb93cSSam Leffler 87539beb93cSSam Leffler /** 87639beb93cSSam Leffler * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method 87739beb93cSSam Leffler * Returns: 0 on success, -1 on failure 87839beb93cSSam Leffler * 87939beb93cSSam Leffler * This function is used to register EAP-MSCHAPv2 peer method into the EAP 88039beb93cSSam Leffler * method list. 88139beb93cSSam Leffler */ 88239beb93cSSam Leffler int eap_peer_mschapv2_register(void) 88339beb93cSSam Leffler { 88439beb93cSSam Leffler struct eap_method *eap; 88539beb93cSSam Leffler 88639beb93cSSam Leffler eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 88739beb93cSSam Leffler EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 88839beb93cSSam Leffler "MSCHAPV2"); 88939beb93cSSam Leffler if (eap == NULL) 89039beb93cSSam Leffler return -1; 89139beb93cSSam Leffler 89239beb93cSSam Leffler eap->init = eap_mschapv2_init; 89339beb93cSSam Leffler eap->deinit = eap_mschapv2_deinit; 89439beb93cSSam Leffler eap->process = eap_mschapv2_process; 89539beb93cSSam Leffler eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; 89639beb93cSSam Leffler eap->getKey = eap_mschapv2_getKey; 89739beb93cSSam Leffler 898780fb4a2SCy Schubert return eap_peer_method_register(eap); 89939beb93cSSam Leffler } 900