esp-idf/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c
Shyamal Khachane dbe7c4a78d fix(esp_wifi): Backport some wifi fixes
1. Fix memory leak caused by assoc IE and retry timer
2. Discard commit frame received at confirmed state in SAE
3. Ignore immediate assoc req received from the station while we are
processing the older one. Fix regression caused by 4cb4faa9
2023-07-21 15:06:09 +05:30

424 lines
12 KiB
C

/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/wpa_i.h"
#include "common/eapol_common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "rsn_supp/wpa_ie.h"
#include "ap/wpa_auth.h"
#include "ap/wpa_auth_i.h"
#include "ap/ap_config.h"
#include "ap/hostapd.h"
#include "esp_wpas_glue.h"
#include "esp_hostap.h"
#include "esp_system.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/aes_wrap.h"
#include "esp_wifi_driver.h"
#include "esp_private/wifi.h"
#include "esp_wpa3_i.h"
#include "esp_wpa2.h"
#include "esp_common_i.h"
#include "esp_owe_i.h"
#include "esp_wps.h"
#include "eap_server/eap.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap/ieee802_1x.h"
#include "ap/sta_info.h"
#include "wps/wps_defs.h"
const wifi_osi_funcs_t *wifi_funcs;
struct wpa_funcs *wpa_cb;
void wpa_install_key(enum wpa_alg alg, u8 *addr, int key_idx, int set_tx,
u8 *seq, size_t seq_len, u8 *key, size_t key_len, enum key_flag key_flag)
{
esp_wifi_set_sta_key_internal(alg, addr, key_idx, set_tx, seq, seq_len, key, key_len, key_flag);
}
int wpa_get_key(uint8_t *ifx, int *alg, u8 *addr, int *key_idx,
u8 *key, size_t key_len, enum key_flag key_flag)
{
return esp_wifi_get_sta_key_internal(ifx, alg, addr, key_idx, key, key_len, key_flag);
}
/**
* eapol_sm_notify_eap_success - Notification of external EAP success trigger
* @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
* @success: %TRUE = set success, %FALSE = clear success
*
* Notify the EAPOL state machine that external event has forced EAP state to
* success (success = %TRUE). This can be cleared by setting success = %FALSE.
*
* This function is called to update EAP state when WPA-PSK key handshake has
* been completed successfully since WPA-PSK does not use EAP state machine.
*/
/* fix buf for tx for now */
#define WPA_TX_MSG_BUFF_MAXLEN 200
void wpa_sendto_wrapper(void *buffer, u16 len)
{
esp_wifi_internal_tx(0, buffer, len);
}
void wpa_deauthenticate(u8 reason_code)
{
esp_wifi_deauthenticate_internal(reason_code);
}
int wpa_config_profile(uint8_t *bssid)
{
int ret = 0;
if (esp_wifi_sta_prof_is_wpa_internal()) {
wpa_set_profile(WPA_PROTO_WPA, esp_wifi_sta_get_prof_authmode_internal());
} else if (esp_wifi_sta_prof_is_rsn_internal()) {
wpa_set_profile(WPA_PROTO_RSN, esp_wifi_sta_get_prof_authmode_internal());
} else if (esp_wifi_sta_prof_is_wapi_internal()) {
wpa_set_profile(WPA_PROTO_WAPI, esp_wifi_sta_get_prof_authmode_internal());
} else {
ret = -1;
}
return ret;
}
int wpa_config_bss(uint8_t *bssid)
{
int ret = 0;
struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal();
u8 mac[6];
esp_wifi_get_macaddr_internal(0, mac);
ret = wpa_set_bss((char *)mac, (char *)bssid, esp_wifi_sta_get_pairwise_cipher_internal(), esp_wifi_sta_get_group_cipher_internal(),
(char *)esp_wifi_sta_get_prof_password_internal(), ssid->ssid, ssid->len);
return ret;
}
void wpa_config_assoc_ie(u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len)
{
if (proto == BIT(0)) {
esp_wifi_set_appie_internal(WIFI_APPIE_WPA, assoc_buf, assoc_wpa_ie_len, 1);
} else {
esp_wifi_set_appie_internal(WIFI_APPIE_RSN, assoc_buf, assoc_wpa_ie_len, 1);
}
}
void wpa_neg_complete(void)
{
esp_wifi_auth_done_internal();
}
bool wpa_attach(void)
{
bool ret = true;
ret = wpa_sm_init();
if(ret) {
ret = (esp_wifi_register_tx_cb_internal(eapol_txcb, WIFI_TXCB_EAPOL_ID) == ESP_OK);
}
esp_set_scan_ie();
return ret;
}
uint8_t *wpa_ap_get_wpa_ie(uint8_t *ie_len)
{
struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal();
if (!hapd || !hapd->wpa_auth || !hapd->wpa_auth->wpa_ie) {
return NULL;
}
*ie_len = hapd->wpa_auth->wpa_ie_len;
return hapd->wpa_auth->wpa_ie;
}
bool wpa_ap_rx_eapol(void *hapd_data, void *sm_data, u8 *data, size_t data_len)
{
struct hostapd_data *hapd = (struct hostapd_data *)hapd_data;
struct sta_info *sta = (struct sta_info *)sm_data;
if (!hapd || !sta) {
wpa_printf(MSG_DEBUG, "hapd=%p sta=%p", hapd, sta);
return false;
}
#ifdef CONFIG_WPS_REGISTRAR
int wps_type = esp_wifi_get_wps_type_internal();
if ((wps_type == WPS_TYPE_PBC) ||
(wps_type == WPS_TYPE_PIN)) {
ieee802_1x_receive(hapd, sta->addr, data, data_len);
return true;
}
#endif
wpa_receive(hapd->wpa_auth, sta->wpa_sm, data, data_len);
return true;
}
void wpa_ap_get_peer_spp_msg(void *sm_data, bool *spp_cap, bool *spp_req)
{
struct wpa_state_machine *sm = (struct wpa_state_machine *)sm_data;
if (!sm) {
return;
}
*spp_cap = sm->spp_sup.capable;
*spp_req = sm->spp_sup.require;
}
bool wpa_deattach(void)
{
struct wpa_sm *sm = &gWpaSm;
esp_wpa3_free_sae_data();
if (sm->wpa_sm_wpa2_ent_disable) {
sm->wpa_sm_wpa2_ent_disable();
}
if (sm->wpa_sm_wps_disable) {
sm->wpa_sm_wps_disable();
}
wpa_sm_deinit();
return true;
}
int wpa_sta_connect(uint8_t *bssid)
{
/* use this API to set AP specific IEs during connection */
int ret = 0;
ret = wpa_config_profile(bssid);
if (ret == 0) {
ret = wpa_config_bss(bssid);
if (ret) {
wpa_printf(MSG_DEBUG, "Rejecting bss, validation failed");
return ret;
}
} else if (esp_wifi_sta_get_prof_authmode_internal() == NONE_AUTH) {
esp_set_assoc_ie((uint8_t *)bssid, NULL, 0, false);
}
return 0;
}
void wpa_config_done(void)
{
esp_set_scan_ie();
}
int wpa_parse_wpa_ie_wrapper(const u8 *wpa_ie, size_t wpa_ie_len, wifi_wpa_ie_t *data)
{
struct wpa_ie_data ie;
int ret = 0;
ret = wpa_parse_wpa_ie(wpa_ie, wpa_ie_len, &ie);
data->proto = ie.proto;
data->pairwise_cipher = cipher_type_map_supp_to_public(ie.pairwise_cipher);
data->group_cipher = cipher_type_map_supp_to_public(ie.group_cipher);
data->key_mgmt = ie.key_mgmt;
data->capabilities = ie.capabilities;
data->pmkid = ie.pmkid;
data->mgmt_group_cipher = cipher_type_map_supp_to_public(ie.mgmt_group_cipher);
data->rsnxe_capa = ie.rsnxe_capa;
return ret;
}
static void wpa_sta_connected_cb(uint8_t *bssid)
{
supplicant_sta_conn_handler(bssid);
}
static void wpa_sta_disconnected_cb(uint8_t reason_code)
{
switch (reason_code) {
case WIFI_REASON_AUTH_EXPIRE:
case WIFI_REASON_NOT_AUTHED:
case WIFI_REASON_NOT_ASSOCED:
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
case WIFI_REASON_INVALID_PMKID:
case WIFI_REASON_AUTH_FAIL:
case WIFI_REASON_ASSOC_FAIL:
case WIFI_REASON_CONNECTION_FAIL:
case WIFI_REASON_HANDSHAKE_TIMEOUT:
esp_wpa3_free_sae_data();
wpa_sta_clear_curr_pmksa();
wpa_sm_notify_disassoc(&gWpaSm);
break;
default:
break;
}
#ifdef CONFIG_OWE_STA
owe_deinit();
#endif /* CONFIG_OWE_STA */
supplicant_sta_disconn_handler();
}
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
#ifdef CONFIG_WPS_REGISTRAR
static int check_n_add_wps_sta(struct hostapd_data *hapd, struct sta_info *sta_info, u8 *ies, u8 ies_len, bool *pmf_enable, int subtype)
{
struct wpabuf *wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
int wps_type = esp_wifi_get_wps_type_internal();
/* Condition for this, WPS is running and WPS IEs are part of assoc req */
if (!wps_ie || (wps_type == WPS_TYPE_DISABLE)) {
return 0;
}
sta_info->wps_ie = wps_ie;
sta_info->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta_info);
if (sta_info->eapol_sm) {
wpa_printf(MSG_DEBUG, "considering station " MACSTR " for WPS", MAC2STR(sta_info->addr));
if (esp_send_assoc_resp(hapd, sta_info, sta_info->addr, WLAN_STATUS_SUCCESS, true, subtype) != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR, "failed to send assoc response " MACSTR, MAC2STR(sta_info->addr));
return -1;
}
}
return 0;
}
#endif
static bool hostap_sta_join(void **sta, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len,u8 *rsnxe, u8 rsnxe_len, bool *pmf_enable, int subtype)
{
struct sta_info *sta_info = NULL;
struct hostapd_data *hapd = hostapd_get_hapd_data();
if (!hapd) {
goto fail;
}
if (*sta && !esp_wifi_ap_is_sta_sae_reauth_node(bssid)) {
ap_free_sta(hapd, *sta);
}
sta_info = ap_sta_add(hapd, bssid);
if (!sta_info) {
wpa_printf(MSG_ERROR, "failed to add station " MACSTR, MAC2STR(bssid));
return false;
}
#ifdef CONFIG_SAE
if (sta_info->lock && os_semphr_take(sta_info->lock, 0) != TRUE) {
wpa_printf(MSG_INFO, "Ignore assoc request as softap is busy with sae calculation for station "MACSTR, MAC2STR(bssid));
return false;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_WPS_REGISTRAR
if (check_n_add_wps_sta(hapd, sta_info, wpa_ie, wpa_ie_len, pmf_enable, subtype) == 0) {
if (sta_info->eapol_sm) {
*sta = sta_info;
#ifdef CONFIG_SAE
if (sta_info->lock) {
os_semphr_give(sta_info->lock);
}
#endif /* CONFIG_SAE */
return true;
}
} else {
goto fail;
}
#endif
if (wpa_ap_join(sta_info, bssid, wpa_ie, wpa_ie_len, rsnxe, rsnxe_len, pmf_enable, subtype)) {
*sta = sta_info;
#ifdef CONFIG_SAE
if (sta_info->lock) {
os_semphr_give(sta_info->lock);
}
#endif /* CONFIG_SAE */
return true;
}
fail:
if (sta_info) {
ap_free_sta(hapd, sta_info);
}
return false;
}
#endif
int esp_supplicant_init(void)
{
int ret = ESP_OK;
wifi_funcs = WIFI_OSI_FUNCS_INITIALIZER();
if (!wifi_funcs) {
return ESP_ERR_NOT_FOUND;
}
wpa_cb = (struct wpa_funcs *)os_zalloc(sizeof(struct wpa_funcs));
if (!wpa_cb) {
return ESP_ERR_NO_MEM;
}
wpa_cb->wpa_sta_init = wpa_attach;
wpa_cb->wpa_sta_deinit = wpa_deattach;
wpa_cb->wpa_sta_rx_eapol = wpa_sm_rx_eapol;
wpa_cb->wpa_sta_connect = wpa_sta_connect;
wpa_cb->wpa_sta_connected_cb = wpa_sta_connected_cb;
wpa_cb->wpa_sta_disconnected_cb = wpa_sta_disconnected_cb;
wpa_cb->wpa_sta_in_4way_handshake = wpa_sta_in_4way_handshake;
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
wpa_cb->wpa_ap_join = hostap_sta_join;
wpa_cb->wpa_ap_remove = wpa_ap_remove;
wpa_cb->wpa_ap_get_wpa_ie = wpa_ap_get_wpa_ie;
wpa_cb->wpa_ap_rx_eapol = wpa_ap_rx_eapol;
wpa_cb->wpa_ap_get_peer_spp_msg = wpa_ap_get_peer_spp_msg;
wpa_cb->wpa_ap_init = hostap_init;
wpa_cb->wpa_ap_deinit = hostap_deinit;
#endif
wpa_cb->wpa_config_parse_string = wpa_config_parse_string;
wpa_cb->wpa_parse_wpa_ie = wpa_parse_wpa_ie_wrapper;
wpa_cb->wpa_config_bss = NULL;//wpa_config_bss;
wpa_cb->wpa_michael_mic_failure = wpa_michael_mic_failure;
wpa_cb->wpa_config_done = wpa_config_done;
wpa_cb->wpa_sta_set_ap_rsnxe = wpa_sm_set_ap_rsnxe;
esp_wifi_register_wpa3_ap_cb(wpa_cb);
esp_wifi_register_wpa3_cb(wpa_cb);
#ifdef CONFIG_OWE_STA
esp_wifi_register_owe_cb(wpa_cb);
#endif /* CONFIG_OWE_STA */
eloop_init();
ret = esp_supplicant_common_init(wpa_cb);
if (ret != 0) {
return ret;
}
esp_wifi_register_wpa_cb_internal(wpa_cb);
#if CONFIG_ESP_WIFI_WAPI_PSK
ret = esp_wifi_internal_wapi_init();
#endif
return ret;
}
int esp_supplicant_deinit(void)
{
esp_supplicant_common_deinit();
esp_supplicant_unset_all_appie();
eloop_destroy();
wpa_cb = NULL;
return esp_wifi_unregister_wpa_cb_internal();
}