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