diff --git a/components/wpa_supplicant/port/esp_supplicant/wpa_main.c b/components/wpa_supplicant/port/esp_supplicant/wpa_main.c new file mode 100644 index 00000000..2e56d7ec --- /dev/null +++ b/components/wpa_supplicant/port/esp_supplicant/wpa_main.c @@ -0,0 +1,152 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/includes.h" +#include "utils/common.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_i.h" +#include "common/eapol_common.h" +#include "common/ieee802_11_defs.h" +#include "rsn_supp/wpa_ie.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/aes_wrap.h" +#include "esp_supplicant/esp_wpas_glue.h" + +#include "esp_wifi_driver.h" +#define LOCAL static + +extern struct ieee80211_cipher tkip; +extern struct ieee80211_cipher ccmp; +extern struct ieee80211_cipher wep; + +extern bool dhcpc_flag; + + +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, int key_entry_valid) +{ + esp_wifi_set_sta_key_internal(alg, addr, key_idx, set_tx, seq, seq_len, key, key_len, key_entry_valid); +} + +int wpa_get_key(uint8_t *ifx, int *alg, u8 *addr, int *key_idx, + u8 *key, size_t key_len, int key_entry_valid) +{ + return esp_wifi_get_sta_key_internal(ifx, alg, addr, key_idx, key, key_len, key_entry_valid); +} + +/** + * 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. + */ + +#include "esp_aio.h" +#define EP_OFFSET 36 +int ieee80211_output_pbuf(esp_aio_t *aio); +/* fix buf for tx for now */ +#define WPA_TX_MSG_BUFF_MAXLEN 200 + +LOCAL int ICACHE_FLASH_ATTR wpa_send_cb(esp_aio_t* aio) +{ + char* pb = (char*)aio->arg; + + os_free(pb); + + return 0; +} + +void wpa_sendto_wrapper(void* dataptr, u16 datalen) +{ +#ifndef IOT_SIP_MODE + esp_aio_t aio; + + aio.arg = dataptr - EP_OFFSET; + aio.cb = wpa_send_cb; + aio.fd = 0; + aio.pbuf = (char *)dataptr; + aio.len = datalen; + aio.ret = 0; + + if (ieee80211_output_pbuf(&aio) != 0) { + os_free(dataptr - EP_OFFSET); + } + +#else + esf_buf* eb = NULL; + uint8_t* frm; + + eb = ieee80211_getmgtframe(&frm, sizeof(struct ieee80211_frame), WPA_TX_MSG_BUFF_MAXLEN); + os_memcpy(frm, msg, msg_len); +// eb->hdr_len = sizeof(struct ieee80211_frame); + eb->data_len = msg_len; + EBUF_START(eb) = frm; + ieee80211_output_pbuf(ic->ic_if0_conn, eb); +#endif +} + +void wpa_deauthenticate(u8 reason_code) +{ + esp_wifi_deauthenticate_internal(reason_code); +} + +void wpa_config_profile(void) +{ + 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_wpa2_internal() || esp_wifi_sta_prof_is_wpa3_internal()) { + wpa_set_profile(WPA_PROTO_RSN, esp_wifi_sta_get_prof_authmode_internal()); + } else { + WPA_ASSERT(0); + } +} + +void wpa_config_bss(uint8_t *bssid) +{ + u8 mac[6]; + struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal(); + esp_wifi_get_mac(WIFI_IF_STA, mac); + + 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); +} + +void wpa_config_assoc_ie(u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len) +{ + esp_wifi_set_appie_internal(proto, assoc_buf, assoc_wpa_ie_len, 0); +} + +void wpa_neg_complete(void) +{ + esp_wifi_auth_done_internal(); +} + +void ICACHE_FLASH_ATTR wpa_sta_init() +{ + wpa_register(NULL, wpa_sendto_wrapper, + wpa_config_assoc_ie, wpa_install_key, wpa_get_key, wpa_deauthenticate, wpa_neg_complete); +} + +void wpa_sta_connect(uint8_t *bssid) +{ + wpa_config_profile(); + wpa_config_bss(bssid); +} + diff --git a/components/wpa_supplicant/port/esp_supplicant/wpas_glue.c b/components/wpa_supplicant/port/esp_supplicant/wpas_glue.c new file mode 100644 index 00000000..b0f6756b --- /dev/null +++ b/components/wpa_supplicant/port/esp_supplicant/wpas_glue.c @@ -0,0 +1,120 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef EMBEDDED_SUPP + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/eapol_common.h" +#include "rsn_supp/wpa.h" +#define EP_OFFSET 36 + +#define wpa_malloc_dram(s) heap_caps_malloc(s, MALLOC_CAP_8BIT) +#define wpa_calloc_dram(n, s) heap_caps_calloc(n, s, MALLOC_CAP_8BIT) + +static u8* wpa_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr* hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = (struct ieee802_1x_hdr*)((int)sm->wpadata + sizeof(struct l2_ethhdr)); //keep head byte remain for filling later + + if (hdr == NULL) { + return NULL; + } + + hdr->version = sm->eapol_version; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) { + memcpy(hdr + 1, data, data_len); + } else { + memset(hdr + 1, 0, data_len); + } + + if (data_pos) { + *data_pos = hdr + 1; + } + + return (u8 *) hdr; +} + + +u8* ICACHE_FLASH_ATTR wpa_sm_alloc_eapol(struct wpa_sm* sm, u8 type, + const void* data, u16 data_len, + size_t* msg_len, void** data_pos) +{ + sm->wpadata = wpa_malloc_dram(256 + EP_OFFSET); + sm->wpadata += EP_OFFSET; + return wpa_alloc_eapol(sm, type, data, data_len, msg_len, data_pos); + + + return NULL; +} + +void wpa_sm_deauthenticate(struct wpa_sm *sm, u8 reason_code) +{ + + /*only need send deauth frame when associated*/ + if (WPA_SM_STATE(sm) >= WPA_ASSOCIATED) { + sm->wpa_deauthenticate(reason_code); + } +} + +/** + * mlme_setprotection - MLME-SETPROTECTION.request primitive + * @priv: Private driver interface data + * @addr: Address of the station for which to set protection (may be + * %NULL for group keys) + * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* + * @key_type: MLME_SETPROTECTION_KEY_TYPE_* + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used to set the driver to + * require protection for Tx and/or Rx frames. This uses the layer + * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 + * (MLME-SETPROTECTION.request). Many drivers do not use explicit + * set protection operation; instead, they set protection implicitly + * based on configured keys. + */ +int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, + int protect_type, int key_type) +{ + return 0; +} + +/* + *use above two functions to get wpa_ie and rsn_ie, then don't need wpa_sm_get_beacon_ie function +*/ +int wpa_sm_get_beacon_ie(struct wpa_sm *sm) +{ + return 0; +} + +/** + * wpa_supplicant_disassociate - Disassociate the current connection + * @wpa_s: Pointer to wpa_supplicant data + * @reason_code: IEEE 802.11 reason code for the disassociate frame + * + * This function is used to request %wpa_supplicant to disassociate with the + * current AP. + */ +void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) +{ + /*check if need clear internal state and data value*/ +} +#endif diff --git a/components/wpa_supplicant/src/ap/ap_config.c b/components/wpa_supplicant/src/ap/ap_config.c new file mode 100644 index 00000000..15847b49 --- /dev/null +++ b/components/wpa_supplicant/src/ap/ap_config.c @@ -0,0 +1,106 @@ +/* + * hostapd / Configuration helper functions + * Copyright (c) 2003-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "crypto/sha1.h" +//#include "radius/radius_client.h" +#include "common/ieee802_11_defs.h" +#include "common/eapol_common.h" +//#include "eap_common/eap_wsc_common.h" +//#include "eap_server/eap.h" +#include "ap/wpa_auth.h" +//#include "sta_info.h" +#include "ap/ap_config.h" +#include "utils/wpa_debug.h" +#include "esp_supplicant/esp_wifi_driver.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +static int ICACHE_FLASH_ATTR hostapd_derive_psk(struct hostapd_ssid* ssid) +{ + ssid->wpa_psk = (struct hostapd_wpa_psk*)os_zalloc(sizeof(struct hostapd_wpa_psk)); + + if (ssid->wpa_psk == NULL) { + wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "SSID", + (u8*) ssid->ssid, ssid->ssid_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)", + (u8*) ssid->wpa_passphrase, + os_strlen(ssid->wpa_passphrase)); + /* It's too SLOW */ +// pbkdf2_sha1(ssid->wpa_passphrase, +// ssid->ssid, ssid->ssid_len, +// 4096, ssid->wpa_psk->psk, PMK_LEN); + os_memcpy(ssid->wpa_psk->psk, esp_wifi_ap_get_prof_pmk_internal(), PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)", + ssid->wpa_psk->psk, PMK_LEN); + return 0; +} + + +int ICACHE_FLASH_ATTR hostapd_setup_wpa_psk(struct hostapd_bss_config* conf) +{ + struct hostapd_ssid* ssid = &conf->ssid; + + if (ssid->wpa_passphrase != NULL) { + if (ssid->wpa_psk != NULL) { + wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK " + "instead of passphrase"); + } else { + wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on " + "passphrase"); + + if (hostapd_derive_psk(ssid) < 0) { + return -1; + } + } + + ssid->wpa_psk->group = 1; + } + +#if 0 + + if (ssid->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, + &conf->ssid)) { + return -1; + } + } + +#endif + + return 0; +} + +const u8* ICACHE_FLASH_ATTR hostapd_get_psk(const struct hostapd_bss_config* conf, + const u8* addr, const u8* prev_psk) +{ + struct hostapd_wpa_psk* psk; + int next_ok = prev_psk == NULL; + + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0)) { + return psk->psk; + } + + if (psk->psk == prev_psk) { + next_ok = 1; + } + } + + return NULL; +} diff --git a/components/wpa_supplicant/src/ap/wpa_auth.c b/components/wpa_supplicant/src/ap/wpa_auth.c new file mode 100644 index 00000000..e67101f7 --- /dev/null +++ b/components/wpa_supplicant/src/ap/wpa_auth.c @@ -0,0 +1,3498 @@ +/* + * IEEE 802.11 RSN / WPA Authenticator + * Copyright (c) 2004-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +//#include "utils/eloop.h" +#include "utils/state_machine.h" +#include "common/ieee802_11_defs.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +//#include "eapol_auth/eapol_auth_sm.h" +//#include "ap_config.h" +//#include "ieee802_11.h" +#include "ap/wpa_auth.h" +//#include "pmksa_cache_auth.h" +#include "ap/wpa_auth_i.h" +#include "ap/wpa_auth_ie.h" +#include "utils/wpa_debug.h" +#include "ap/hostapd.h" +#include "rsn_supp/wpa.h" + +//#include "net80211/ieee80211_var.h" +//#include "net80211/ieee80211_node.h" +//#include "net80211/wl_cxm.h" +#include "esp_supplicant/esp_wifi_driver.h" +#define wpa_malloc_dram(s) heap_caps_malloc(s, MALLOC_CAP_8BIT) +#define wpa_calloc_dram(n, s) heap_caps_calloc(n, s, MALLOC_CAP_8BIT) + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +static void wpa_send_eapol_timeout(void* eloop_ctx, void* timeout_ctx); +static int wpa_sm_step(struct wpa_state_machine* sm); +static int wpa_verify_key_mic(struct wpa_ptk* PTK, u8* data, size_t data_len); +//static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); +static void wpa_group_sm_step(struct wpa_authenticator* wpa_auth, + struct wpa_group* group); +static void wpa_request_new_ptk(struct wpa_state_machine* sm); +static int wpa_gtk_update(struct wpa_authenticator* wpa_auth, + struct wpa_group* group); +static int wpa_group_config_group_keys(struct wpa_authenticator* wpa_auth, + struct wpa_group* group); + +static const u32 dot11RSNAConfigGroupUpdateCount ICACHE_RODATA_ATTR = 4; +static const u32 dot11RSNAConfigPairwiseUpdateCount ICACHE_RODATA_ATTR = 4; +static const u32 eapol_key_timeout_first ICACHE_RODATA_ATTR = 100; /* ms */ +static const u32 eapol_key_timeout_subseq ICACHE_RODATA_ATTR = 1000; /* ms */ +static const u32 eapol_key_timeout_first_group ICACHE_RODATA_ATTR = 500; /* ms */ + +#define WPA_SM_MAX_INDEX 16 +static void* s_sm_table[WPA_SM_MAX_INDEX]; +static u32 s_sm_valid_bitmap = 0; + +os_timer_t resend_eapol; + +/* TODO: make these configurable */ +//static const int dot11RSNAConfigPMKLifetime = 43200; +//static const int dot11RSNAConfigPMKReauthThreshold = 70; +//static const int dot11RSNAConfigSATimeout = 60; + +static struct ICACHE_FLASH_ATTR wpa_state_machine* wpa_auth_get_sm(u32 index) +{ + if ((index < WPA_SM_MAX_INDEX) && (BIT(index) & s_sm_valid_bitmap)) { + return s_sm_table[index]; + } + + return NULL; +} + +static void ICACHE_FLASH_ATTR wpa_auth_add_sm(struct wpa_state_machine* sm) +{ + if (sm) { + u8 i; + + for (i = 0; i < WPA_SM_MAX_INDEX; i++) { + if (BIT(i) & s_sm_valid_bitmap) { + if (s_sm_table[i] == sm) { + wpa_printf(MSG_INFO, "add sm already exist i = %d", i); + } + + continue; + } + + s_sm_table[i] = sm; + s_sm_valid_bitmap |= BIT(i); + sm->index = i; + wpa_printf(MSG_DEBUG, "add sm, index = %d, bitmap = %x", i, s_sm_valid_bitmap); + return; + } + } +} + +static void ICACHE_FLASH_ATTR wpa_auth_del_sm(struct wpa_state_machine* sm) +{ + if (sm && (sm->index < WPA_SM_MAX_INDEX)) { + if (sm != s_sm_table[sm->index]) { + wpa_printf(MSG_INFO, "del sm error %d", sm->index); + } + + s_sm_table[sm->index] = NULL; + s_sm_valid_bitmap &= ~BIT(sm->index); + wpa_printf(MSG_DEBUG, "del sm, index = %d, bitmap = %x", sm->index, s_sm_valid_bitmap); + } +} + +static inline int ICACHE_FLASH_ATTR wpa_auth_mic_failure_report( + struct wpa_authenticator* wpa_auth, const u8* addr) +{ + return 0; +} + + +static inline void ICACHE_FLASH_ATTR wpa_auth_set_eapol(struct wpa_authenticator* wpa_auth, + const u8* addr, wpa_eapol_variable var, + int value) +{ +} + + +static inline int ICACHE_FLASH_ATTR wpa_auth_get_eapol(struct wpa_authenticator* wpa_auth, + const u8* addr, wpa_eapol_variable var) +{ + return -1; +} + +static inline const u8* ICACHE_FLASH_ATTR wpa_auth_get_psk(struct wpa_authenticator* wpa_auth, + const u8* addr, const u8* prev_psk) +{ + + struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal(); + + if (!hapd){ + return NULL; + } + + return (u8*)hostapd_get_psk(hapd->conf, addr, prev_psk); +} + +static inline int ICACHE_FLASH_ATTR wpa_auth_get_msk(struct wpa_authenticator* wpa_auth, + const u8* addr, u8* msk, size_t* len) +{ + return -1; +} + +static inline int ICACHE_FLASH_ATTR wpa_auth_set_key(struct wpa_authenticator* wpa_auth, + int vlan_id, + enum wpa_alg alg, const u8* addr, int idx, + u8* key, size_t key_len) +{ + return esp_wifi_set_ap_key_internal(alg, addr, idx, key, key_len); +} + + +static inline int ICACHE_FLASH_ATTR wpa_auth_get_seqnum(struct wpa_authenticator* wpa_auth, + const u8* addr, int idx, u8* seq) +{ + return -1; +} + +#include "esp_aio.h" +int ieee80211_output_pbuf(esp_aio_t *aio); +#define EP_OFFSET 36 +/* fix buf for tx for now */ +#define WPA_TX_MSG_BUFF_MAXLEN 200 + +static int ICACHE_FLASH_ATTR wpa_auth_send_cb(esp_aio_t* aio) +{ + char* pb = (char*)aio->arg; + + os_free(pb); + + return 0; +} + +static inline int ICACHE_FLASH_ATTR +wpa_auth_send_eapol(struct wpa_authenticator* wpa_auth, const u8* addr, + const u8* data, size_t data_len, int encrypt) +{ + // wujg : need to add send data function + +#ifndef IOT_SIP_MODE + esp_aio_t aio; + uint8_t* pb = wpa_malloc_dram(data_len + EP_OFFSET + sizeof(struct l2_ethhdr)); + struct l2_ethhdr* eth; + u8 mac[6]; + pb = pb + EP_OFFSET; + esp_wifi_get_mac(WIFI_IF_AP, mac); + + if (pb == NULL) { + return -1; + } + + eth = (struct l2_ethhdr*)pb; + os_memcpy(eth->h_dest, addr, ETH_ALEN); + os_memcpy(eth->h_source, mac, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + + os_memcpy((uint8_t*)pb + sizeof(struct l2_ethhdr), data, data_len); + + aio.arg = pb - EP_OFFSET; + aio.cb = wpa_auth_send_cb; + aio.fd = 1; + aio.pbuf = (char *)pb; + aio.len = sizeof(struct l2_ethhdr) + data_len; + aio.ret = 0; + + if (ieee80211_output_pbuf(&aio) != 0) { + os_free(pb - EP_OFFSET); + wpa_printf(MSG_INFO, "wpa_auth_send_eapol failed"); + } + +#else + esf_buf* eb = NULL; + struct l2_ethhdr* eth; + uint8_t* frm; + + eb = ieee80211_getmgtframe(&frm, sizeof(struct ieee80211_frame), WPA_TX_MSG_BUFF_MAXLEN); + eth = etod(eb, struct l2_ethhdr); + os_memcpy(eth->h_dest, addr, ETH_ALEN); + os_memcpy(eth->h_source, g_ic.ic_if1_conn->ni_myaddr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + + os_memcpy(frm + sizeof(struct l2_ethhdr), data, data_len); + + eb->hdr_len = sizeof(struct ieee80211_frame); + eb->data_len = sizeof(struct l2_ethhdr) + data_len; + + ieee80211_output_pbuf(g_ic.ic_if1_conn, eb); +#endif + + return -1; +} + +int ICACHE_FLASH_ATTR wpa_auth_for_each_sta(struct wpa_authenticator* wpa_auth, + int (*cb)(struct wpa_state_machine* sm, void* ctx), + void* cb_ctx) +{ + return 0; +} + +// wujg : not support now +#if 0 +int wpa_auth_for_each_auth(struct wpa_authenticator* wpa_auth, + int (*cb)(struct wpa_authenticator* a, void* ctx), + void* cb_ctx) +{ + if (wpa_auth->cb.for_each_auth == NULL) { + return 0; + } + + return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); +} + +void wpa_auth_logger(struct wpa_authenticator* wpa_auth, const u8* addr, + logger_level level, const char* txt) +{ + return; +} + +void wpa_auth_vlogger(struct wpa_authenticator* wpa_auth, const u8* addr, + logger_level level, const char* fmt, ...) +{ +} +#endif + +static void ICACHE_FLASH_ATTR wpa_sta_disconnect(struct wpa_authenticator* wpa_auth, + const u8* addr) +{ + // wujg : need to add disconnect function + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); + return; +} + +static int ICACHE_FLASH_ATTR wpa_use_aes_cmac(struct wpa_state_machine* sm) +{ + int ret = 0; +#ifdef CONFIG_IEEE80211R + + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + ret = 1; + } + +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + + if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) { + ret = 1; + } + +#endif /* CONFIG_IEEE80211W */ + return ret; +} + +// wujg : not support now +#if 0 +static void wpa_rekey_gmk(void* eloop_ctx, void* timeout_ctx) +{ + struct wpa_authenticator* wpa_auth = eloop_ctx; + + if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + wpa_hexdump_key(MSG_DEBUG, "GMK", + wpa_auth->group->GMK, WPA_GMK_LEN); + } + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } +} +#endif + +static void ICACHE_FLASH_ATTR wpa_rekey_gtk(void* eloop_ctx, void* timeout_ctx) +{ + struct wpa_authenticator* wpa_auth = eloop_ctx; + struct wpa_group* group; + + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; + + do { + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); + } +} + + +static void ICACHE_FLASH_ATTR wpa_rekey_ptk(void* eloop_ctx, void* timeout_ctx) +{ + struct wpa_authenticator* wpa_auth = eloop_ctx; + struct wpa_state_machine* sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); + wpa_request_new_ptk(sm); + wpa_sm_step(sm); +} + +#if 0 +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine* sm, void* ctx) +{ + if (sm->pmksa == ctx) { + sm->pmksa = NULL; + } + + return 0; +} + + +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry* entry, + void* ctx) +{ + struct wpa_authenticator* wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +} +#endif + +static int ICACHE_FLASH_ATTR wpa_group_init_gmk_and_counter(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) { + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + + if (random_get_bytes(rkey, sizeof(rkey)) < 0) { + return -1; + } + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) { + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; +} + +static struct wpa_group* ICACHE_FLASH_ATTR wpa_group_init(struct wpa_authenticator* wpa_auth, + int vlan_id, int delay_init) +{ + struct wpa_group* group; + + group = (struct wpa_group*)os_zalloc(sizeof(struct wpa_group)); + + if (group == NULL) { + return NULL; + } + + group->GTKAuthenticator = TRUE; +// group->vlan_id = vlan_id; + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); + + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "for secure operations - update keys later when " + "the first station connects"); + } + + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. + */ + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + os_free(group); + return NULL; + } + + group->GInit = TRUE; + + if (delay_init) { + wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " + "until Beacon frames have been configured"); + /* Initialization is completed in wpa_init_keys(). */ + } else { + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + } + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * @cb: Callback functions for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator* ICACHE_FLASH_ATTR wpa_init(const u8* addr, + struct wpa_auth_config* conf, + struct wpa_auth_callbacks* cb) +{ + struct wpa_authenticator* wpa_auth; + + wpa_auth = (struct wpa_authenticator*)os_zalloc(sizeof(struct wpa_authenticator)); + + if (wpa_auth == NULL) { + return NULL; + } + + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); +// os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->group = wpa_group_init(wpa_auth, 0, 0); + + if (wpa_auth->group == NULL) { + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +// wujg : not support now +#if 0 + wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, + wpa_auth); + + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +#endif + +#ifdef CONFIG_IEEE80211R + wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); + + if (wpa_auth->ft_pmk_cache == NULL) { + wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->wpa_ie); + pmksa_cache_auth_deinit(wpa_auth->pmksa); + os_free(wpa_auth); + return NULL; + } + +#endif /* CONFIG_IEEE80211R */ + +// wujg : not support now +#if 0 + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + +#endif + + return wpa_auth; +} + +// wujg : not needed now +#if 0 +int wpa_init_keys(struct wpa_authenticator* wpa_auth) +{ + struct wpa_group* group = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " + "keys"); + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + return 0; +} + + +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator* wpa_auth) +{ + struct wpa_group* group, *prev; + + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + +#ifdef CONFIG_PEERKEY + + while (wpa_auth->stsl_negotiations) { + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); + } + +#endif /* CONFIG_PEERKEY */ + + pmksa_cache_auth_deinit(wpa_auth->pmksa); + +#ifdef CONFIG_IEEE80211R + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_auth->wpa_ie); + + group = wpa_auth->group; + + while (group) { + prev = group; + group = group->next; + os_free(prev); + } + + os_free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator* wpa_auth, + struct wpa_auth_config* conf) +{ + struct wpa_group* group; + + if (wpa_auth == NULL) { + return 0; + } + + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + return -1; + } + + /* + * Reinitialize GTK to make sure it is suitable for the new + * configuration. + */ + group = wpa_auth->group; + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return 0; +} +#endif + +struct wpa_state_machine* ICACHE_FLASH_ATTR +wpa_auth_sta_init(struct wpa_authenticator* wpa_auth, const u8* addr) +{ + struct wpa_state_machine* sm; + + sm = (struct wpa_state_machine*)os_zalloc(sizeof(struct wpa_state_machine)); + + if (sm == NULL) { + return NULL; + } + + os_memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + wpa_auth_add_sm(sm); + + return sm; +} + +int ICACHE_FLASH_ATTR wpa_auth_sta_associated(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) { + return -1; + } + +#ifdef CONFIG_IEEE80211R + + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " + "start 4-way handshake"); + return 0; + } + +#endif /* CONFIG_IEEE80211R */ + + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; + return wpa_sm_step(sm); + } + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; + + sm->Init = TRUE; + + if (wpa_sm_step(sm) == 1) { + return 1; /* should not really happen */ + } + + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + return wpa_sm_step(sm); +} + + +void ICACHE_FLASH_ATTR wpa_auth_sta_no_wpa(struct wpa_state_machine* sm) +{ + /* WPA/RSN was not used - clear WPA state. This is needed if the STA + * reassociates back to the same AP while the previous entry for the + * STA has not yet been removed. */ + if (sm == NULL) { + return; + } + + sm->wpa_key_mgmt = 0; +} + + +static void ICACHE_FLASH_ATTR wpa_free_sta_sm(struct wpa_state_machine* sm) +{ + wpa_auth_del_sm(sm); + + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } + +#ifdef CONFIG_IEEE80211R + os_free(sm->assoc_resp_ftie); +#endif /* CONFIG_IEEE80211R */ + os_free(sm->last_rx_eapol_key); + os_free(sm->wpa_ie); + os_free(sm); +} + + +void ICACHE_FLASH_ATTR wpa_auth_sta_deinit(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return; + } + +#if 0 + + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +#endif + + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else { + wpa_free_sta_sm(sm); + } +} + + +static void ICACHE_FLASH_ATTR wpa_request_new_ptk(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return; + } + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + +static int ICACHE_FLASH_ATTR wpa_replay_counter_valid(struct wpa_key_replay_counter* ctr, + const u8* replay_counter) +{ + int i; + + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!ctr[i].valid) { + break; + } + + if (os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) { + return 1; + } + } + + return 0; +} + +static void ICACHE_FLASH_ATTR wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter* ctr, + const u8* replay_counter) +{ + int i; + + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (ctr[i].valid && + (replay_counter == NULL || + os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0)) { + ctr[i].valid = FALSE; + } + } +} + +#ifdef CONFIG_IEEE80211R +static int ft_check_msg_2_of_4(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, + struct wpa_eapol_ie_parse* kde) +{ + struct wpa_ie_data ie; + struct rsn_mdie* mdie; + + if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || + ie.num_pmkid != 1 || ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 2/4"); + return -1; + } + + os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", + sm->sup_pmk_r1_name, PMKID_LEN); + + if (!kde->mdie || !kde->ftie) { + wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake " + "message 2/4", kde->mdie ? "FTIE" : "MDIE"); + return -1; + } + + mdie = (struct rsn_mdie*)(kde->mdie + 2); + + if (kde->mdie[1] < sizeof(struct rsn_mdie) || + os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); + return -1; + } + + if (sm->assoc_resp_ftie && + (kde->ftie[1] != sm->assoc_resp_ftie[1] || + os_memcmp(kde->ftie, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]) != 0)) { + wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4", + kde->ftie, kde->ftie_len); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp", + sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + +static int ICACHE_FLASH_ATTR wpa_receive_error_report(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, int group) +{ + /* Supplicant reported a Michael MIC error */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure (group=%d))", + group); + + if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "group cipher is not TKIP"); + } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "pairwise cipher is not TKIP"); + } else { + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) { + return 1; /* STA entry was removed */ + } + +// sm->dot11RSNAStatsTKIPRemoteMICFailures++; +// wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + } + + /* + * Error report is not a request for a new key handshake, but since + * Authenticator may do it, let's change the keys now anyway. + */ + wpa_request_new_ptk(sm); + return 0; +} + +void ICACHE_FLASH_ATTR wpa_receive(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, + u8* data, size_t data_len) +{ + struct ieee802_1x_hdr* hdr; + struct wpa_eapol_key* key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR + } msg; + char* msgtxt; + struct wpa_eapol_ie_parse kde; + int ft; + const u8* eapol_key_ie; + size_t eapol_key_ie_len; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) { + return; + } + + if (data_len < sizeof(*hdr) + sizeof(*key)) { + return; + } + + + + hdr = (struct ieee802_1x_hdr*) data; + key = (struct wpa_eapol_key*)(hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + key_data_length = WPA_GET_BE16(key->key_data_length); + wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR + " key_info=0x%x type=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, key_data_length); + + if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { + wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %lu)", + key_data_length, + (unsigned long)(data_len - sizeof(*hdr) - + sizeof(*key))); + return; + } + + if (sm->wpa == WPA_VERSION_WPA2) { + if (key->type == EAPOL_KEY_TYPE_WPA) { + /* + * Some deployed station implementations seem to send + * msg 4/4 with incorrect type value in WPA2 mode. + */ + wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " + "with unexpected WPA type in RSN mode"); + } else if (key->type != EAPOL_KEY_TYPE_RSN) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in RSN mode", + key->type); + return; + } + } else { + if (key->type != EAPOL_KEY_TYPE_WPA) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in WPA mode", + key->type); + return; + } + } + + wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys + * are set */ + + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { + if (wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "advertised support for " + "AES-128-CMAC, but did not " + "use it"); + return; + } + + if (!wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "did not use HMAC-SHA1-AES " + "with CCMP/GCMP"); + return; + } + } + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->req_replay_counter_used && + os_memcmp(key->replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { + int i; + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + /* + * Some supplicant implementations (e.g., Windows XP + * WZC) update SNonce for each EAPOL-Key 2/4. This + * breaks the workaround on accepting any of the + * pending requests, so allow the SNonce to be updated + * even if we have already sent out EAPOL-Key 3/4. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Process SNonce update from STA " + "based on retransmitted EAPOL-Key " + "1/4"); + sm->update_snonce = 1; + wpa_replay_counter_mark_invalid(sm->prev_key_replay, + key->replay_counter); + goto continue_processing; + } + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - " + "SNonce did not change", msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key %s with " + "unexpected replay counter", msgtxt); + } + + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) { + break; + } + + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + return; + } + +continue_processing: + + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && + (!sm->update_snonce || + sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + + random_add_randomness(key->key_nonce, WPA_NONCE_LEN); + + if (sm->group->reject_4way_hs_for_entropy) { + /* + * The system did not have enough entropy to generate + * strong random numbers. Reject the first 4-way + * handshake(s) and collect some entropy based on the + * information from it. Once enough entropy is + * available, the next atempt will trigger GMK/Key + * Counter update and the station will be allowed to + * continue. + */ + wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " + "collect more entropy for random number " + "generation"); + random_mark_pool_ready(); + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } + + if (wpa_parse_kde_ies((u8*)(key + 1), key_data_length, + &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 with " + "invalid Key Data contents"); + return; + } + + if (kde.rsn_ie) { + eapol_key_ie = kde.rsn_ie; + eapol_key_ie_len = kde.rsn_ie_len; + } else { + eapol_key_ie = kde.wpa_ie; + eapol_key_ie_len = kde.wpa_ie_len; + } + + ft = sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_ft(sm->wpa_key_mgmt); + + if (sm->wpa_ie == NULL || + wpa_compare_rsn_ie(ft, + sm->wpa_ie, sm->wpa_ie_len, + eapol_key_ie, eapol_key_ie_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + eapol_key_ie, eapol_key_ie_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } + +#ifdef CONFIG_IEEE80211R + + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } + +#endif /* CONFIG_IEEE80211R */ + break; + + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + + break; + + case GROUP_2: + if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING + || !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + + break; +#ifdef CONFIG_PEERKEY + + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + + break; +#else /* CONFIG_PEERKEY */ + + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ + + case REQUEST: + break; + } + + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + + sm->MICVerified = FALSE; + + if (sm->PTK_valid && !sm->update_snonce) { + if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } + + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + os_timer_disarm(&resend_eapol); + sm->pending_1_of_4_timeout = 0; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sm->req_replay_counter_used = 1; + os_memcpy(sm->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) { + return; /* STA entry was removed */ + } + } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8*)(key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); + } + } else { + /* Do not allow the same key replay counter to be reused. */ + wpa_replay_counter_mark_invalid(sm->key_replay, + key->replay_counter); + + if (msg == PAIRWISE_2) { + /* + * Maintain a copy of the pending EAPOL-Key frames in + * case the EAPOL-Key frame was retransmitted. This is + * needed to allow EAPOL-Key msg 2/4 reply to another + * pending msg 1/4 to update the SNonce to work around + * unexpected supplicant behavior. + */ + os_memcpy(sm->prev_key_replay, sm->key_replay, + sizeof(sm->key_replay)); + } else { + os_memset(sm->prev_key_replay, 0, + sizeof(sm->prev_key_replay)); + } + + /* + * Make sure old valid counters are not accepted anymore and + * do not get copied again. + */ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); + } + +#ifdef CONFIG_PEERKEY + + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } + +#endif /* CONFIG_PEERKEY */ + + os_free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = (u8*)os_malloc(data_len); + + if (sm->last_rx_eapol_key == NULL) { + return; + } + + os_memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); + sm->EAPOLKeyReceived = TRUE; + sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); + sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static int ICACHE_FLASH_ATTR wpa_gmk_to_gtk(const u8* gmk, const char* label, const u8* addr, + const u8* gnonce, u8* gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8* pos; + int ret = 0; + + /* GTK = PRF-X(GMK, "Group key expansion", + * AA || GNonce || Time || random data) + * The example described in the IEEE 802.11 standard uses only AA and + * GNonce as inputs here. Add some more entropy since this derivation + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ + os_memcpy(data, addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + pos = data + ETH_ALEN + WPA_NONCE_LEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + + if (random_get_bytes(pos, 16) < 0) { + ret = -1; + } + +#ifdef CONFIG_IEEE80211W + sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); +#else /* CONFIG_IEEE80211W */ + + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) { + ret = -1; + } + +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void ICACHE_FLASH_ATTR wpa_send_eapol_timeout(void* eloop_ctx, void* timeout_ctx) +{ + struct wpa_authenticator* wpa_auth = eloop_ctx; + struct wpa_state_machine* sm = timeout_ctx; + + sm->pending_1_of_4_timeout = 0; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); +} + + +void ICACHE_FLASH_ATTR __wpa_send_eapol(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, int key_info, + const u8* key_rsc, const u8* nonce, + const u8* kde, size_t kde_len, + int keyidx, int encr, int force_version) +{ + struct ieee802_1x_hdr* hdr; + struct wpa_eapol_key* key; + size_t len; + int alg; + int key_data_len, pad_len = 0; + u8* buf, *pos; + int version, pairwise; + int i; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (force_version) { + version = force_version; + } else if (wpa_use_aes_cmac(sm)) { + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + } else if (sm->pairwise != WPA_CIPHER_TKIP) { + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + } else { + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + } + + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " + "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " + "encr=%d)", + version, + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + + if (pad_len) { + pad_len = 8 - pad_len; + } + + key_data_len += pad_len + 8; + } + + len += key_data_len; + + hdr = (struct ieee802_1x_hdr*)os_zalloc(len); + + if (hdr == NULL) { + return; + } + + hdr->version = wpa_auth->conf.eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key*)(hdr + 1); + + key->type = sm->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info |= version; + + if (encr && sm->wpa == WPA_VERSION_WPA2) { + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + } + + if (sm->wpa != WPA_VERSION_WPA2) { + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + } + + WPA_PUT_BE16(key->key_info, key_info); + + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); + + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + WPA_PUT_BE16(key->key_length, 0); + } + + /* FIX: STSL: what to use as key_replay_counter? */ + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay[0].valid = TRUE; + + if (nonce) { + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + } + + if (key_rsc) { + os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + } + + if (kde && !encr) { + os_memcpy(key + 1, kde, kde_len); + WPA_PUT_BE16(key->key_data_length, kde_len); + } else if (encr && kde) { + buf = (u8*)os_zalloc(key_data_len); + + if (buf == NULL) { + os_free(hdr); + return; + } + + pos = buf; + os_memcpy(pos, kde, kde_len); + pos += kde_len; + + if (pad_len) { + *pos++ = 0xdd; + } + + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + (u8*)(key + 1))) { + os_free(hdr); + os_free(buf); + return; + } + + WPA_PUT_BE16(key->key_data_length, key_data_len); + } else { + u8 ek[32]; + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->PTK.kek, 16); + os_memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8*)(key + 1), key_data_len); + WPA_PUT_BE16(key->key_data_length, key_data_len); + } + + os_free(buf); + } + + if (key_info & WPA_KEY_INFO_MIC) { + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); + os_free(hdr); + return; + } + + wpa_eapol_key_mic(sm->PTK.kck, version, (u8*) hdr, len, + key->key_mic); + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8*) hdr, len, + sm->pairwise_set); + os_free(hdr); +} + +void ICACHE_FLASH_ATTR resend_eapol_handle(void* timeout_ctx) +{ + u32 index = (u32)timeout_ctx; + struct wpa_state_machine* sm = wpa_auth_get_sm(index); + + wpa_printf(MSG_DEBUG, "resend eapol1"); + + if (sm) { + sm->pending_1_of_4_timeout = 0; + sm->TimeoutEvt = TRUE; + sm->in_step_loop = 0; + wpa_sm_step(sm); + } else { + wpa_printf(MSG_INFO, "Station left, stop send EAPOL frame"); + } +} + +static void ICACHE_FLASH_ATTR wpa_send_eapol(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, int key_info, + const u8* key_rsc, const u8* nonce, + const u8* kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + int ctr; + + if (sm == NULL) { + return; + } + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); + + ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; + + if (ctr == 1 && wpa_auth->conf.tx_status) + timeout_ms = pairwise ? eapol_key_timeout_first : + eapol_key_timeout_first_group; + else { + timeout_ms = eapol_key_timeout_subseq; + } + + if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) { + sm->pending_1_of_4_timeout = 1; + } + + wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " + "counter %d)", timeout_ms, ctr); + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + os_timer_disarm(&resend_eapol); + os_timer_setfn(&resend_eapol, (os_timer_func_t*)resend_eapol_handle, (void*)(sm->index)); + os_timer_arm(&resend_eapol, 1000, 0); +} + + +static int ICACHE_FLASH_ATTR wpa_verify_key_mic(struct wpa_ptk* PTK, u8* data, size_t data_len) +{ + struct ieee802_1x_hdr* hdr; + struct wpa_eapol_key* key; + u16 key_info; + int ret = 0; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) { + return -1; + } + + hdr = (struct ieee802_1x_hdr*) data; + key = (struct wpa_eapol_key*)(hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + os_memcpy(mic, key->key_mic, 16); + os_memset(key->key_mic, 0, 16); + + if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key->key_mic) || + os_memcmp(mic, key->key_mic, 16) != 0) { + ret = -1; + } + + os_memcpy(key->key_mic, mic, 16); + return ret; +} + + +void ICACHE_FLASH_ATTR wpa_remove_ptk(struct wpa_state_machine* sm) +{ + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +} + + +int ICACHE_FLASH_ATTR wpa_auth_sm_event(struct wpa_state_machine* sm, wpa_event event) +{ + int remove_ptk = 1; + + if (sm == NULL) { + return -1; + } + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + if (!sm->started) { + /* + * When using WPS, we may end up here if the STA + * manages to re-associate without the previous STA + * entry getting removed. Consequently, we need to make + * sure that the WPA state machines gets initialized + * properly at this point. + */ + wpa_printf(MSG_DEBUG, "WPA state machine had not been " + "started - initialize now"); + sm->started = 1; + sm->Init = TRUE; + + if (wpa_sm_step(sm) == 1) { + return 1; /* should not really happen */ + } + + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + break; + } + + if (sm->GUpdateStationKeys) { + /* + * Reauthentication cancels the pending group key + * update for this STA. + */ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->PtkGroupInit = TRUE; + } + + sm->ReAuthenticationRequest = TRUE; + break; + + case WPA_ASSOC_FT: +#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " + "after association"); + wpa_ft_install_ptk(sm); + + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; + return 0; +#else /* CONFIG_IEEE80211R */ + break; +#endif /* CONFIG_IEEE80211R */ + } + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + + if (sm->mgmt_frame_prot && event == WPA_AUTH) { + remove_ptk = 0; + } + +#endif /* CONFIG_IEEE80211W */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) { + wpa_remove_ptk(sm); + } + } + + return wpa_sm_step(sm); +} + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); + + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + } + + sm->GUpdateStationKeys = FALSE; + + if (sm->wpa == WPA_VERSION_WPA) { + sm->PInitAKeys = FALSE; + } + + if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and + * Local AA > Remote AA)) */) { + sm->Pair = TRUE; + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); + sm->TimeoutCtr = 0; + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->wpa_auth, sm->addr); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); + sm->AuthenticationRequest = FALSE; +} + + +static void ICACHE_FLASH_ATTR wpa_group_ensure_init(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + if (group->first_sta_seen) { + return; + } + + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "to proceed - reject first 4-way handshake"); + group->reject_4way_hs_for_entropy = TRUE; + } else { + group->first_sta_seen = TRUE; + group->reject_4way_hs_for_entropy = FALSE; + } + + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + +SM_STATE(WPA_PTK, AUTHENTICATION2) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + + wpa_group_ensure_init(sm->wpa_auth, sm->group); + + /* + * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat + * ambiguous. The Authenticator state machine uses a counter that is + * incremented by one for each 4-way handshake. However, the security + * analysis of 4-way handshake points out that unpredictable nonces + * help in preventing precomputation attacks. Instead of the state + * machine definition, use an unpredictable nonce value here to provide + * stronger protection against potential precomputation attacks. + */ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " + "ANonce."); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, + WPA_NONCE_LEN); + sm->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i does not clear TimeoutCtr here, but this is more + * logical place than INITIALIZE since AUTHENTICATION2 can be + * re-entered on ReAuthenticationRequest without going through + * INITIALIZE. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 msk[2 * PMK_LEN]; + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ +#if 0 + + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + } else +#endif + if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + os_memcpy(sm->PMK, msk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + + if (len >= 2 * PMK_LEN) { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } + +#endif /* CONFIG_IEEE80211R */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this + * will break reauthentication since EAPOL state machines may not be + * get into AUTHENTICATING state that clears keyRun before WPA state + * machine enters AUTHENTICATION2 state and goes immediately to INITPMK + * state and takes PMK from the previously used AAA Key. This will + * eventually fail in 4-Way Handshake because Supplicant uses PMK + * derived from the new AAA Key. Setting keyRun = FALSE here seems to + * be good workaround for this issue. */ + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8* psk; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + + if (psk) { + os_memcpy(sm->PMK, psk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +#endif /* CONFIG_IEEE80211R */ + } + + sm->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + +#if 0 + + if (sm->pmksa) + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + else +#endif + { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + } + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); +} + + +static int ICACHE_FLASH_ATTR wpa_derive_ptk(struct wpa_state_machine* sm, const u8* pmk, + struct wpa_ptk* ptk) +{ + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; +#ifdef CONFIG_IEEE80211R + + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); + } + +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + (u8*) ptk, ptk_len, + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + + return 0; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8* pmk = NULL; + + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = FALSE; + + /* WPA with IEEE 802.1X: use the derived PMK from EAP + * WPA-PSK: iterate through possible PSKs and select the one matching + * the packet */ + for (;;) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + + if (pmk == NULL) { + break; + } + } else { + pmk = sm->PMK; + } + + wpa_derive_ptk(sm, pmk, &PTK); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + break; + } + } + + if (!ok) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); + return; + } + +#ifdef CONFIG_IEEE80211R + + if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + /* + * Verify that PMKR1Name from EAPOL-Key message 2/4 matches + * with the value we derived. + */ + if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKR1Name mismatch in FT 4-way " + "handshake"); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " + "Supplicant", + sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + return; + } + } + +#endif /* CONFIG_IEEE80211R */ + + sm->pending_1_of_4_timeout = 0; + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); + } + + sm->MICVerified = TRUE; + + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine* sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8* ieee80211w_kde_add(struct wpa_state_machine* sm, u8* pos) +{ + struct wpa_igtk_kde igtk; + struct wpa_group* gsm = sm->group; + + if (!sm->mgmt_frame_prot) { + return pos; + } + + igtk.keyid[0] = gsm->GN_igtk; + igtk.keyid[1] = 0; + + if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) { + os_memset(igtk.pn, 0, sizeof(igtk.pn)); + } + + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) { + return pos; + } + } + + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8*) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ICACHE_FLASH_ATTR ieee80211w_kde_len(struct wpa_state_machine* sm) +{ + return 0; +} + + +static u8* ICACHE_FLASH_ATTR ieee80211w_kde_add(struct wpa_state_machine* sm, u8* pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; + size_t gtk_len, kde_len; + struct wpa_group* gsm = sm->group; + u8* wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) { + return; + } + + gtk = dummy_gtk; + } + + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + + if (gtk) { + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; + } + +#ifdef CONFIG_IEEE80211R + + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ + kde_len += 300; /* FTIE + 2 * TIE */ + } + +#endif /* CONFIG_IEEE80211R */ + kde = (u8*)wpa_malloc_dram(kde_len); + + if (kde == NULL) { + return; + } + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R + + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return; + } + + pos += res; + } + +#endif /* CONFIG_IEEE80211R */ + + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + + pos = ieee80211w_kde_add(sm, pos); + +#ifdef CONFIG_IEEE80211R + + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config* conf; + + conf = &sm->wpa_auth->conf; + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, kde + kde_len - pos, + NULL, 0); + + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return; + } + + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + pos += 4; + } + +#endif /* CONFIG_IEEE80211R */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + + if (sm->Pair) { + enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); + int klen = wpa_cipher_key_len(sm->pairwise); + + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->wpa_auth->conf.wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf. + wpa_ptk_rekey, 0, wpa_rekey_ptk, + sm->wpa_auth, sm); + } + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + + if (sm->keycount == 2) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); + } + } else { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + + if (sm->wpa == WPA_VERSION_WPA) { + sm->PInitAKeys = TRUE; + } else { + sm->has_GTK = TRUE; + } + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + + +{ + esp_wifi_wpa_ptk_init_done_internal(sm->addr); +} +#ifdef CONFIG_IEEE80211R + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +#endif /* CONFIG_IEEE80211R */ +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator* wpa_auth = sm->wpa_auth; + + if (sm->Init) { + SM_ENTER(WPA_PTK, INITIALIZE); + } else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA_PTK: sm->Disconnect"); + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->DeauthenticationRequest) { + SM_ENTER(WPA_PTK, DISCONNECTED); + } else if (sm->AuthenticationRequest) { + SM_ENTER(WPA_PTK, AUTHENTICATION); + } else if (sm->ReAuthenticationRequest) { + SM_ENTER(WPA_PTK, AUTHENTICATION2); + } else if (sm->PTKRequest) { + SM_ENTER(WPA_PTK, PTKSTART); + } else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + + case WPA_PTK_AUTHENTICATION: + SM_ENTER(WPA_PTK, AUTHENTICATION2); + break; + + case WPA_PTK_AUTHENTICATION2: + if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) { + SM_ENTER(WPA_PTK, INITPMK); + } else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + /* FIX: && 802.1X::keyRun */) { + SM_ENTER(WPA_PTK, INITPSK); + } + + break; + + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) { + SM_ENTER(WPA_PTK, PTKSTART); + } else { +// wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); + SM_ENTER(WPA_PTK, DISCONNECT); + } + + break; + + case WPA_PTK_INITPSK: + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) { + SM_ENTER(WPA_PTK, PTKSTART); + } else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); +// wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + + break; + + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) { + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + } else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { +// wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %d reached", + dot11RSNAConfigPairwiseUpdateCount); + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) { + SM_ENTER(WPA_PTK, PTKSTART); + } + + break; + + case WPA_PTK_PTKCALCNEGOTIATING: + if (sm->MICVerified) { + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); + } else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) { + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + } else if (sm->TimeoutEvt) { + SM_ENTER(WPA_PTK, PTKSTART); + } + + break; + + case WPA_PTK_PTKCALCNEGOTIATING2: + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + + case WPA_PTK_PTKINITNEGOTIATING: + if (sm->update_snonce) { + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + } else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) { + SM_ENTER(WPA_PTK, PTKINITDONE); + } else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { +// wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %d " + "reached", + dot11RSNAConfigPairwiseUpdateCount); + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) { + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + } + + break; + + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group* gsm = sm->group; + u8* kde, *pos, hdr[2]; + size_t kde_len; + u8* gtk, dummy_gtk[32]; + + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; + + if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + if (sm->wpa == WPA_VERSION_WPA) { + sm->PInitAKeys = FALSE; + } + + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) { + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + gtk = gsm->GTK[gsm->GN - 1]; + + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) { + return; + } + + gtk = dummy_gtk; + } + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = (u8*)wpa_malloc_dram(kde_len); + + if (kde == NULL) { + return; + } + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gtk; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + + if (sm->wpa == WPA_VERSION_WPA2) { + os_free(kde); + } +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + } + + sm->GUpdateStationKeys = FALSE; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + } + + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init || sm->PtkGroupInit) { + SM_ENTER(WPA_PTK_GROUP, IDLE); + sm->PtkGroupInit = FALSE; + } else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) { + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + } + + break; + + case WPA_PTK_GROUP_REKEYNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + !sm->EAPOLKeyPairwise && sm->MICVerified) { + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + } else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) { + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + } else if (sm->TimeoutEvt) { + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + } + + break; + + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static int ICACHE_FLASH_ATTR wpa_gtk_update(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + int ret = 0; + + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + + if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", + wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len) < 0) { + ret = -1; + } + + wpa_hexdump_key(MSG_DEBUG, "GTK", + group->GTK[group->GN - 1], group->GTK_len); + +#ifdef CONFIG_IEEE80211W + + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + + if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", + wpa_auth->addr, group->GNonce, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) { + ret = -1; + } + + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); + } + +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void ICACHE_FLASH_ATTR wpa_group_gtk_init(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + os_memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; +#ifdef CONFIG_IEEE80211W + group->GN_igtk = 4; + group->GM_igtk = 5; +#endif /* CONFIG_IEEE80211W */ + /* GTK[GN] = CalcGTK() */ + wpa_gtk_update(wpa_auth, group); +} + + +static int ICACHE_FLASH_ATTR wpa_group_update_sta(struct wpa_state_machine* sm, void* ctx) +{ + if (ctx != NULL && ctx != sm->group) { + return 0; + } + + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Not in PTKINITDONE; skip Group Key update"); + sm->GUpdateStationKeys = FALSE; + return 0; + } + + if (sm->GUpdateStationKeys) { + /* + * This should not really happen, so add a debug log entry. + * Since we clear the GKeyDoneStations before the loop, the + * station needs to be counted here anyway. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "GUpdateStationKeys was already set when " + "marking station for GTK rekeying"); + } + + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ + if (sm->is_wnmsleep) { + return 0; + } + + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + + wpa_sm_step(sm); + return 0; +} + + +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine* sm) +{ + if (sm->is_wnmsleep) { + return; + } + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine* sm, int flag) +{ + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine* sm, u8* pos) +{ + struct wpa_group* gsm = sm->group; + u8* start = pos; + + /* + * GTK subelement: + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] + */ + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(pos, gsm->GN & 0x03); + pos += 2; + *pos++ = gsm->GTK_len; + + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) { + return 0; + } + + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; + + wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN); + wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + + return pos - start; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine* sm, u8* pos) +{ + struct wpa_group* gsm = sm->group; + u8* start = pos; + + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_LEN; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) { + return 0; + } + + pos += 6; + + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; + + wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_igtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + return pos - start; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_WNM */ + + +static void ICACHE_FLASH_ATTR wpa_group_setkeys(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + /* "GKeyDoneStations = GNoStations" is done in more robust way by + * counting the STAs that are marked with GUpdateStationKeys instead of + * including all STAs that could be in not-yet-completed state. */ + wpa_gtk_update(wpa_auth, group); + + if (group->GKeyDoneStations) { + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " + "GKeyDoneStations=%d when starting new GTK rekey", + group->GKeyDoneStations); + group->GKeyDoneStations = 0; + } + + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", + group->GKeyDoneStations); +} + + +static int ICACHE_FLASH_ATTR wpa_group_config_group_keys(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + int ret = 0; + + if (wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_cipher_to_alg(wpa_auth->conf.wpa_group), + (u8*)broadcast_ether_addr, group->GN, + group->GTK[group->GN - 1], group->GTK_len) < 0) { + ret = -1; + } + +#ifdef CONFIG_IEEE80211W + + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) { + ret = -1; + } + +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static int ICACHE_FLASH_ATTR wpa_group_setkeysdone(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + + if (wpa_group_config_group_keys(wpa_auth, group) < 0) { + return -1; + } + + return 0; +} + + +static void ICACHE_FLASH_ATTR wpa_group_sm_step(struct wpa_authenticator* wpa_auth, + struct wpa_group* group) +{ + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } + } +} + + +static int ICACHE_FLASH_ATTR wpa_sm_step(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return 0; + } + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); + return 0; + } + + sm->in_step_loop = 1; + + do { + if (sm->pending_deinit) { + break; + } + + sm->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + + if (sm->pending_deinit) { + break; + } + + SM_STEP_RUN(WPA_PTK_GROUP); + + if (sm->pending_deinit) { + break; + } + + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + return 1; + } + + return 0; +} + +#if 0 +static void wpa_sm_call_step(void* eloop_ctx, void* timeout_ctx) +{ + struct wpa_state_machine* sm = eloop_ctx; + wpa_sm_step(sm); +} + +void wpa_auth_sm_notify(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return; + } + + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} + +void wpa_gtk_rekey(struct wpa_authenticator* wpa_auth) +{ + int tmp, i; + struct wpa_group* group; + + if (wpa_auth == NULL) { + return; + } + + group = wpa_auth->group; + + for (i = 0; i < 2; i++) { + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); + } +} + +static const char* wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ + ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +int wpa_get_mib(struct wpa_authenticator* wpa_auth, char* buf, size_t buflen) +{ + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; +#ifdef CONFIG_RSN_PREAUTH + const int preauth = 1; +#else /* CONFIG_RSN_PREAUTH */ + const int preauth = 0; +#endif /* CONFIG_RSN_PREAUTH */ + + if (wpa_auth == NULL) { + return len; + } + + ret = os_snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=%s\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(preauth), + wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + return len; +} + + +int wpa_get_mib_sta(struct wpa_state_machine* sm, char* buf, size_t buflen) +{ + int len = 0, ret; + u32 pairwise = 0; + + if (sm == NULL) { + return 0; + } + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + sm->pairwise); + + if (pairwise == 0) { + return 0; + } + + ret = os_snprintf( + buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + return len; +} + +void wpa_auth_countermeasures_start(struct wpa_authenticator* wpa_auth) +{ + if (wpa_auth) { + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; + } +} + +int wpa_auth_pairwise_set(struct wpa_state_machine* sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_get_pairwise(struct wpa_state_machine* sm) +{ + return sm->pairwise; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return -1; + } + + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return 0; + } + + return sm->wpa; +} + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine* sm, + struct rsn_pmksa_cache_entry* entry) +{ + if (sm == NULL || sm->pmksa != entry) { + return -1; + } + + sm->pmksa = NULL; + return 0; +} + +struct rsn_pmksa_cache_entry* +wpa_auth_sta_get_pmksa(struct wpa_state_machine* sm) +{ + return sm ? sm->pmksa : NULL; +} + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine* sm) +{ + if (sm) { + sm->dot11RSNAStatsTKIPLocalMICFailures++; + } +} + +const u8* wpa_auth_get_wpa_ie(struct wpa_authenticator* wpa_auth, size_t* len) +{ + if (wpa_auth == NULL) { + return NULL; + } + + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + +int wpa_auth_pmksa_add(struct wpa_state_machine* sm, const u8* pmk, + int session_timeout, struct eapol_state_machine* eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) { + return -1; + } + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) { + return 0; + } + + return -1; +} + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator* wpa_auth, + const u8* pmk, size_t len, const u8* sta_addr, + int session_timeout, + struct eapol_state_machine* eapol) +{ + if (wpa_auth == NULL) { + return -1; + } + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol, + WPA_KEY_MGMT_IEEE8021X)) { + return 0; + } + + return -1; +} + +static struct wpa_group* +wpa_auth_add_group(struct wpa_authenticator* wpa_auth, int vlan_id) +{ + struct wpa_group* group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) { + return NULL; + } + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); + + if (group == NULL) { + return NULL; + } + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine* sm, int vlan_id) +{ + struct wpa_group* group; + + if (sm == NULL || sm->wpa_auth == NULL) { + return 0; + } + + group = sm->wpa_auth->group; + + while (group) { + if (group->vlan_id == vlan_id) { + break; + } + + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + + if (group == NULL) { + return -1; + } + } + + if (sm->group == group) { + return 0; + } + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + sm->group = group; + return 0; +} + + +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator* wpa_auth, + struct wpa_state_machine* sm, int ack) +{ + if (wpa_auth == NULL || sm == NULL) { + return; + } + + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR + " ack=%d", MAC2STR(sm->addr), ack); + + if (sm->pending_1_of_4_timeout && ack) { + /* + * Some deployed supplicant implementations update their SNonce + * for each EAPOL-Key 2/4 message even within the same 4-way + * handshake and then fail to use the first SNonce when + * deriving the PTK. This results in unsuccessful 4-way + * handshake whenever the relatively short initial timeout is + * reached and EAPOL-Key 1/4 is retransmitted. Try to work + * around this by increasing the timeout now that we know that + * the station has received the frame. + */ + int timeout_ms = eapol_key_timeout_subseq; + wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " + "timeout by %u ms because of acknowledged frame", + timeout_ms); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_register_timeout(timeout_ms / 1000, + (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } +} + +int wpa_auth_uses_sae(struct wpa_state_machine* sm) +{ + if (sm == NULL) { + return 0; + } + + return wpa_key_mgmt_sae(sm->wpa_key_mgmt); +} +#endif + diff --git a/components/wpa_supplicant/src/ap/wpa_auth_ie.c b/components/wpa_supplicant/src/ap/wpa_auth_ie.c new file mode 100644 index 00000000..34f12217 --- /dev/null +++ b/components/wpa_supplicant/src/ap/wpa_auth_ie.c @@ -0,0 +1,705 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "ap/wpa_auth.h" +#include "ap/wpa_auth_ie.h" +#include "ap/wpa_auth_i.h" +#include "common/wpa_common.h" +#include "utils/wpa_debug.h" + +#ifdef CONFIG_RSN_TESTING +int rsn_testing = 0; +#endif /* CONFIG_RSN_TESTING */ + + +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + struct wpa_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + u32 suite; + + hdr = (struct wpa_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group); + if (suite == 0) { + wpa_printf( MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += WPA_SELECTOR_LEN; + + count = pos; + pos += 2; + + num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise); + if (num_suites == 0) { + wpa_printf( MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); + return -1; + } + pos += num_suites * WPA_SELECTOR_LEN; + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf( MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + int num_suites, res; + u8 *pos, *count; + u16 capab; + u32 suite; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + if (suite == 0) { + wpa_printf( MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + + res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); + num_suites += res; + pos += res * RSN_SELECTOR_LEN; + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + + if (num_suites == 0) { + wpa_printf( MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->rsn_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + + if (num_suites == 0) { + wpa_printf( MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* RSN Capabilities */ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) + capab |= BIT(8) | BIT(14) | BIT(15); +#endif /* CONFIG_RSN_TESTING */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + if (pos + 2 + PMKID_LEN > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (pos + 2 + 4 > buf + len) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + /* + * Fill in any defined fields and add extra data to the end of + * the element. + */ + int pmkid_count_set = pmkid != NULL; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + pmkid_count_set = 1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } + + memset(pos, 0x12, 17); + pos += 17; + } +#endif /* CONFIG_RSN_TESTING */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) +{ + u8 *pos, buf[128]; + int res; + + pos = buf; + + if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos, NULL); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = os_malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) + return -1; + memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; + + return 0; +} + +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + memcpy(pos, data2, data2_len); + pos += data2_len; + } + return pos; +} + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len/*, + const u8 *mdie, size_t mdie_len*/) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; + u32 selector; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = WPA_PROTO_RSN; + else + version = WPA_PROTO_WPA; + + if (!(wpa_auth->conf.wpa & version)) { + wpa_printf( MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, + version, MAC2STR(sm->addr)); + return WPA_INVALID_PROTO; + } + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (data.key_mgmt & WPA_KEY_MGMT_SAE) + selector = RSN_AUTH_KEY_MGMT_SAE; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) + selector = RSN_AUTH_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_CCMP; + + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.group_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_CCMP; + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; + + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_TKIP; + + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.group_cipher); + if (!selector) + selector = WPA_CIPHER_SUITE_TKIP; + } + if (res) { + wpa_printf( MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf( MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf( MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); + return WPA_INVALID_AKMP; + } + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (key_mgmt & WPA_KEY_MGMT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE; + else if (key_mgmt & WPA_KEY_MGMT_FT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + if (version == WPA_PROTO_RSN) + ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; + else + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; + if (!ciphers) { + wpa_printf( MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " + "from " MACSTR, + version == WPA_PROTO_RSN ? "RSN" : "WPA", + data.pairwise_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_PAIRWISE; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf( MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf( MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf( MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf( MSG_DEBUG, "RSN: Trying to use FT, but " + "MDIE not included"); + return WPA_INVALID_MDIE; + } + if (memcmp(mdie, wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } + } +#endif /* CONFIG_IEEE80211R */ + + if (ciphers & WPA_CIPHER_CCMP) + sm->pairwise = WPA_CIPHER_CCMP; + else if (ciphers & WPA_CIPHER_GCMP) + sm->pairwise = WPA_CIPHER_GCMP; + else + sm->pairwise = WPA_CIPHER_TKIP; + + /* TODO: clear WPA/WPA2 state if STA changes from one to another */ + if (wpa_ie[0] == WLAN_EID_RSN) + sm->wpa = WPA_VERSION_WPA2; + else + sm->wpa = WPA_VERSION_WPA; + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + + return WPA_IE_OK; +} + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf( MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +int wpa_auth_uses_mfp(struct wpa_state_machine *sm) +{ + return sm ? sm->mgmt_frame_prot : 0; +} diff --git a/components/wpa_supplicant/src/common/wpa_common.c b/components/wpa_supplicant/src/common/wpa_common.c new file mode 100644 index 00000000..9443b2e8 --- /dev/null +++ b/components/wpa_supplicant/src/common/wpa_common.c @@ -0,0 +1,675 @@ +/* + * WPA/RSN - Shared functions for supplicant and authenticator + * Copyright (c) 2002-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +#ifdef EMBEDDED_SUPP + +#include "rom/ets_sys.h" +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/md5.h" +#include "crypto/aes.h" + +#ifndef CONFIG_NO_WPA2 +static int rsn_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ + return 0; +} + +static int rsn_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; +#ifdef CONFIG_IEEE80211R + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) + return WPA_KEY_MGMT_FT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) + return WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPA3_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; +#endif /* CONFIG_WPA3_SAE */ +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) + return WPA_KEY_MGMT_IEEE8021X_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) + return WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + return 0; +} + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} +#endif /* CONFIG_NO_WPA2 */ +/** + * wpa_parse_wpa_ie_rsn - Parse RSN IE + * @rsn_ie: Buffer containing RSN IE + * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) + * @data: Pointer to structure that will be filled in with parsed data + * Returns: 0 on success, <0 on failure + */ +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ +#ifndef CONFIG_NO_WPA2 + const struct rsn_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_RSN; + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + data->mgmt_group_cipher = 0; + + if (rsn_ie_len == 0) { + /* No RSN IE - fail silently */ + return -1; + } + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) rsn_ie_len); + #endif + return -1; + } + + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + #endif + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + #endif + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + #endif + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + #endif + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + #endif + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + #endif + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (left < (int) data->num_pmkid * PMKID_LEN) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: PMKID underflow " + "(num_pmkid=%lu left=%d)", + __func__, (unsigned long) data->num_pmkid, + left); + #endif + data->num_pmkid = 0; + return -9; + } else { + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + } + + if (left > 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + #endif + } + + return 0; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_WPA; + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + +/** + * wpa_eapol_key_mic - Calculate EAPOL-Key MIC + * @key: EAPOL-Key Key Confirmation Key (KCK) + * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) + * @buf: Pointer to the beginning of the EAPOL header (version field) + * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) + * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: 0 on success, -1 on failure + * + * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has + * to be cleared (all zeroes) when calling this function. + * + * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the + * description of the Key MIC calculation. It includes packet data from the + * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change + * happened during final editing of the standard and the correct behavior is + * defined in the last draft (IEEE 802.11i/D10). + */ +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic) +{ + u8 hash[SHA1_MAC_LEN]; + + switch (ver) { + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + return hmac_md5(key, 16, buf, len, mic); + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + if (hmac_sha1(key, 16, buf, len, hash)) + return -1; + memcpy(mic, hash, MD5_MAC_LEN); + break; +#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_WPA3_SAE + case WPA_KEY_INFO_TYPE_AKM_DEFINED: +#endif + case WPA_KEY_INFO_TYPE_AES_128_CMAC: + return omac1_aes_128(key, buf, len, mic); +#endif + default: + return -1; + } + + return 0; +} + +int wpa_compare_rsn_ie(int ft_initial_assoc, + const u8 *ie1, size_t ie1len, + const u8 *ie2, size_t ie2len) +{ + if (ie1 == NULL || ie2 == NULL) + return -1; + + if (ie1len == ie2len && memcmp(ie1, ie2, ie1len) == 0) + return 0; /* identical IEs */ + +#ifdef CONFIG_IEEE80211R + if (ft_initial_assoc) { + struct wpa_ie_data ie1d, ie2d; + /* + * The PMKID-List in RSN IE is different between Beacon/Probe + * Response/(Re)Association Request frames and EAPOL-Key + * messages in FT initial mobility domain association. Allow + * for this, but verify that other parts of the RSN IEs are + * identical. + */ + if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 || + wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0) + return -1; + if (ie1d.proto == ie2d.proto && + ie1d.pairwise_cipher == ie2d.pairwise_cipher && + ie1d.group_cipher == ie2d.group_cipher && + ie1d.key_mgmt == ie2d.key_mgmt && + ie1d.capabilities == ie2d.capabilities && + ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher) + return 0; + } +#endif /* CONFIG_IEEE80211R */ + + return -1; +} + +#ifdef DEBUG_PRINT +/** + * wpa_cipher_txt - Convert cipher suite to a text string + * @cipher: Cipher suite (WPA_CIPHER_* enum) + * Returns: Pointer to a text string of the cipher suite name + */ +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: + return "CCMP+TKIP"; + default: + return "UNKNOWN"; + } +} +#endif + +/** + * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces + * @pmk: Pairwise master key + * @pmk_len: Length of PMK + * @label: Label to use in derivation + * @addr1: AA or SA + * @addr2: SA or AA + * @nonce1: ANonce or SNonce + * @nonce2: SNonce or ANonce + * @ptk: Buffer for pairwise transient key + * @ptk_len: Length of PTK + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * + * STK = PRF-X(SMK, "Peer key expansion", + * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || + * Min(INonce, PNonce) || Max(INonce, PNonce)) + */ +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + + if (memcmp(addr1, addr2, ETH_ALEN) < 0) { + memcpy(data, addr1, ETH_ALEN); + memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + memcpy(data, addr2, ETH_ALEN); + memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { + memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, + WPA_NONCE_LEN); + } else { + memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, + WPA_NONCE_LEN); + } + + if (use_sha256) { + sha256_prf(pmk, pmk_len, label, data, sizeof(data), + ptk, ptk_len); + } + else + { + sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk, ptk_len); + } + wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR"\n", + MAC2STR(addr1), MAC2STR(addr2)); + + wpa_hexdump(MSG_MSGDUMP, "WPA: PMK", pmk, pmk_len); + wpa_hexdump(MSG_MSGDUMP, "WPA: PTK", ptk, ptk_len); +} + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256) +{ + char* title = "PMK Name"; + const u8* addr[3]; + static const size_t len[3] ICACHE_RODATA_ATTR = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) { + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + } + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + memcpy(pmkid, hash, PMKID_LEN); +} + +int wpa_cipher_key_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + return 16; + case WPA_CIPHER_TKIP: + return 32; + case WPA_CIPHER_WEP104: + return 13; + case WPA_CIPHER_WEP40: + return 5; + } + + return 0; +} + +int wpa_cipher_to_alg(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return WPA_ALG_WEP; + } + return WPA_ALG_NONE; +} + +u32 wpa_cipher_to_suite(int proto, int cipher) +{ + if (cipher & WPA_CIPHER_CCMP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + if (cipher & WPA_CIPHER_GCMP) + return RSN_CIPHER_SUITE_GCMP; + if (cipher & WPA_CIPHER_TKIP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + if (cipher & WPA_CIPHER_WEP104) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + if (cipher & WPA_CIPHER_WEP40) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + if (cipher & WPA_CIPHER_NONE) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + return 0; +} + +int rsn_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + +int wpa_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + +#endif // EMBEDDED_SUPP + + diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c new file mode 100644 index 00000000..30382982 --- /dev/null +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -0,0 +1,2291 @@ + +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * Copyright (c) 2003-2010, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +#include "utils/includes.h" + +#include "utils/common.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" +#include "rsn_supp/wpa_i.h" +#include "common/eapol_common.h" +#include "common/ieee802_11_defs.h" +#include "rsn_supp/wpa_ie.h" +#include "esp_supplicant/esp_wpas_glue.h" +#include "esp_supplicant/esp_wifi_driver.h" + +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/aes_wrap.h" + +#ifdef EMBEDDED_SUPP + +#define LOCAL static + +/** + * 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. + */ + + struct wpa_sm gWpaSm; +/* fix buf for tx for now */ +#define WPA_TX_MSG_BUFF_MAXLEN 200 + +#define ASSOC_IE_LEN 24 + 2 + PMKID_LEN + RSN_SELECTOR_LEN +u8 assoc_ie_buf[ASSOC_IE_LEN+2]; + +void set_assoc_ie(u8 * assoc_buf); + +int wpa_sm_set_key(struct install_key *sm, enum wpa_alg alg, + u8 *addr, int key_idx, int set_tx, + u8 *seq, size_t seq_len, + u8 *key, size_t key_len, + int key_entry_valid); + +int wpa_sm_get_key(uint8_t *ifx, int *alg, u8 *addr, int *key_idx, u8 *key, size_t key_len, int key_entry_valid); + +void wpa_set_passphrase(char * passphrase, u8 *ssid, size_t ssid_len); + +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); +static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) +{ + return sm->wpa_state;; +} + +static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) +{ + +} + +void eapol_sm_notify_eap_success(Boolean success) +{ + +} + +uint32_t cipher_type_map_public_to_supp(wifi_cipher_type_t cipher) +{ + switch (cipher) { + case WIFI_CIPHER_TYPE_NONE: + return WPA_CIPHER_NONE; + + case WIFI_CIPHER_TYPE_WEP40: + return WPA_CIPHER_WEP40; + + case WIFI_CIPHER_TYPE_WEP104: + return WPA_CIPHER_WEP104; + + case WIFI_CIPHER_TYPE_TKIP: + return WPA_CIPHER_TKIP; + + case WIFI_CIPHER_TYPE_CCMP: + return WPA_CIPHER_CCMP; + + case WIFI_CIPHER_TYPE_TKIP_CCMP: + return WPA_CIPHER_CCMP|WPA_CIPHER_TKIP; + + case WIFI_CIPHER_TYPE_AES_CMAC128: + return WPA_CIPHER_AES_128_CMAC; + + default: + return WPA_CIPHER_NONE; + } +} + +/** + * get_bssid - Get the current BSSID + * @priv: private driver interface data + * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) + * + * Returns: 0 on success, -1 on failure + * + * Query kernel driver for the current BSSID and copy it to bssid. + * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ +static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) +{ + memcpy(bssid, sm->bssid, ETH_ALEN); + return 0; +} + + /* + * wpa_ether_send - Send Ethernet frame + * @wpa_s: Pointer to wpa_supplicant data + * @dest: Destination MAC address + * @proto: Ethertype in host byte order + * @buf: Frame payload starting from IEEE 802.1X header + * @len: Frame payload length + * Returns: >=0 on success, <0 on failure + */ +static inline int wpa_sm_ether_send( struct wpa_sm *sm, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + char* msg; + size_t msg_len; + struct l2_ethhdr eth; + + os_memset(ð, 0, sizeof(eth)); + os_memcpy(eth.h_dest, dest, ETH_ALEN); + os_memcpy(eth.h_source, sm->own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + msg_len = sizeof(eth) + data_len; + + //replaced by xxx to remove malloc + msg = (char*)(sm->wpadata); + sm->wpadatalen = msg_len; +// msg = (char *)os_zalloc(msg_len); +// sm->msg_len = msg_len; + + if (msg == NULL) { + return -1; + } + + os_memcpy(msg, ð, sizeof(eth)); + os_memcpy(msg + sizeof(eth), data, data_len); + + sm->sendto(sm->wpadata, sm->wpadatalen); + return 0; +} + +/** + * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @kck: Key Confirmation Key (KCK, part of PTK) + * @ver: Version field from Key Info + * @dest: Destination address for the frame + * @proto: Ethertype (usually ETH_P_EAPOL) + * @msg: EAPOL-Key message + * @msg_len: Length of message + * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + */ +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) +{ + if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { + /* + * Association event was not yet received; try to fetch + * BSSID from the driver. + */ + if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); + #endif + } else { + dest = sm->bssid; + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR + ") as the destination for EAPOL-Key", + MAC2STR(dest)); + #endif + } + } + if (key_mic && + wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to generate EAPOL-Key " + "version %d MIC", ver); + #endif + goto out; + } + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); + wpa_sm_ether_send(sm, dest, proto, msg, msg_len); +out: + return; +} + +/** + * wpa_sm_key_request - Send EAPOL-Key Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @error: Indicate whether this is an Michael MIC error report + * @pairwise: 1 = error report for pairwise packet, 0 = for group packet + * + * Send an EAPOL-Key Request to the current authenticator. This function is + * used to request rekeying and it is usually called when a local Michael MIC + * failure is detected. + */ +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) +{ + size_t rlen; + struct wpa_eapol_key *reply; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf; + + if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else if (sm->key_mgmt == WPA_KEY_MGMT_SAE) + ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_sm_get_bssid(sm, bssid) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "Failed to read BSSID for EAPOL-Key " + "request"); + #endif + return; + } + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info = WPA_KEY_INFO_REQUEST | ver; + if (sm->ptk_set) + key_info |= WPA_KEY_INFO_MIC; + if (error) + key_info |= WPA_KEY_INFO_ERROR|WPA_KEY_INFO_SECURE; + if (pairwise) + key_info |= WPA_KEY_INFO_KEY_TYPE; + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + memcpy(reply->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key Request (error=%d " + "pairwise=%d ptk_set=%d len=%lu)", + error, pairwise, sm->ptk_set, (unsigned long) rlen); + #endif + wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, + rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? + reply->key_mic : NULL); +} +/* +int wpa_supplicant_get_pmk(struct wpa_sm *sm) +{ + if(sm->pmk_len >0) { + return 0; + } else { + return 1; + } +} +*/ + + +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason) +{ + struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_printf( MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); + + if (sm->cur_pmksa == entry) { + wpa_printf( MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + * */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } + + if (reason == PMKSA_EXPIRE && + (sm->pmk_len == entry->pmk_len && + os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { + wpa_printf( MSG_DEBUG, + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + + if (deauth) { + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); + } +} + + + + + +static int wpa_supplicant_get_pmk(struct wpa_sm *sm, + const unsigned char *src_addr, + const u8 *pmkid) +{ + int abort_cached = 0; + + if (pmkid && !sm->cur_pmksa) { + /* When using drivers that generate RSN IE, wpa_supplicant may + * not have enough time to get the association information + * event before receiving this 1/4 message, so try to find a + * matching PMKSA cache entry here. */ + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); + if (sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); + } else { + wpa_printf( MSG_DEBUG, + "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && sm->cur_pmksa && + os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + + wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); + wpa_sm_set_pmk_from_pmksa(sm); + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", + sm->pmk, sm->pmk_len); + //eapol_sm_notify_cached(sm->eapol); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) { + int res = 0, pmk_len; + pmk_len = PMK_LEN; + /* For ESP_SUPPLICANT this is already set using wpa_set_pmk*/ + //res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + + if(!sm->pmk_len) { + res = -1; + } + + if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " + "machines", sm->pmk, pmk_len); + sm->pmk_len = pmk_len; + //wpa_supplicant_key_mgmt_set_pmk(sm); + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, + NULL, NULL, 0, src_addr, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } + if (!sm->cur_pmksa && pmkid && + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { + wpa_printf( MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); + abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_printf( MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; + } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } else { + wpa_printf( MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines - key handshake " + "aborted"); + if (sm->cur_pmksa) { + wpa_printf( MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); + sm->cur_pmksa = NULL; + abort_cached = 1; + } else if (!abort_cached) { + return -1; + } + } + } + + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) + { + /* Send EAPOL-Start to trigger full EAP authentication. */ + u8 *buf; + size_t buflen; + + wpa_printf( MSG_DEBUG, + "RSN: no PMKSA entry found - trigger " + "full EAP authentication"); + buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, + NULL, 0, &buflen, NULL); + if (buf) { + wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, + buf, buflen); + os_free(buf); + return -2; + } + + return -1; + } + + return 0; +} + + +/** + * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @nonce: Nonce value for the EAPOL-Key frame + * @wpa_ie: WPA/RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + int ver, const u8 *nonce, + const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + if (wpa_ie == NULL) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_ERROR, "WPA: No wpa_ie set - cannot " + "generate msg 2/4"); + #endif + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "WPA: WPA IE for msg 2/4\n", wpa_ie, wpa_ie_len); + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*reply) + wpa_ie_len, + &rlen, (void *) &reply); + if (rbuf == NULL) { + return -1; + } + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + WPA_PUT_BE16(reply->key_info, + ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); + + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); + memcpy(reply + 1, wpa_ie, wpa_ie_len); + + memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 2/4\n"); + + wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + +int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk) +{ + size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + + wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", + sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, + (u8 *) ptk, ptk_len, + wpa_key_mgmt_sha256(sm->key_mgmt)); + return 0; +} + +void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + struct wpa_ptk *ptk; + int res; + + wpa_sm_set_state(WPA_FIRST_HALF_4WAY_HANDSHAKE); + + wpa_printf(MSG_DEBUG, "WPA 1/4-Way Handshake\n"); + + memset(&ie, 0, sizeof(ie)); + +#ifndef CONFIG_NO_WPA2 + if (sm->proto == WPA_PROTO_RSN) { + /* RSN: msg 1/4 should contain PMKID for the selected PMK */ + const u8 *_buf = (const u8 *) (key + 1); + size_t len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_MSGDUMP, "RSN: msg 1/4 key data", _buf, len); + wpa_supplicant_parse_ies(_buf, len, &ie); + if (ie.pmkid) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " + "Authenticator", ie.pmkid, PMKID_LEN); + } + } +#endif /* CONFIG_NO_WPA2 */ + res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); + + if (res == -2) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - " + "requesting full EAP authentication"); + #endif + return; + } + if (res) + goto failed; + + if (esp_wifi_sta_prof_is_wpa2_internal() && + esp_wifi_sta_get_prof_authmode_internal() == WPA2_AUTH_ENT) { + pmksa_cache_set_current(sm, NULL, sm->bssid, 0, 0); + } + + if (sm->renew_snonce) { + if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to get random data for SNonce"); + #endif + goto failed; + } + + sm->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + sm->snonce, WPA_NONCE_LEN); + } + + /* Calculate PTK which will be stored as a temporary PTK until it has + * been verified when processing message 3/4. */ + ptk = &sm->tptk; + wpa_derive_ptk(sm, src_addr, key, ptk); + /* Supplicant: swap tx/rx Mic keys */ + sm->tptk_set = 1; + sm->ptk_set = 0; + sm->key_install = true; + + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, + ptk)) + goto failed; + + memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying"); + #endif + wpa_sm_key_request(sm, 0, 1); +} + + +int wpa_supplicant_install_ptk(struct wpa_sm *sm) +{ + int keylen; + enum wpa_alg alg; + + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.\n"); + #endif + + switch (sm->pairwise_cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + keylen = 16; + break; + case WPA_CIPHER_TKIP: + alg = WPA_ALG_TKIP; + keylen = 32; + break; + case WPA_CIPHER_NONE: + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + #endif + return 0; + default: + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + #endif + return -1; + } + + //now only use keyentry 0 for pairwise key + sm->key_entry_valid = 5; + + if (wpa_sm_set_key(&(sm->install_ptk), alg, sm->bssid, 0, 1, (sm->install_ptk).seq, WPA_KEY_RSC_LEN, + (u8 *) sm->ptk.tk1, keylen,sm->key_entry_valid) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to set PTK to the " + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); + #endif + return -1; + } + + if (sm->wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, + sm, NULL); + } + + return 0; +} + +int wpa_supplicant_check_group_cipher(int group_cipher, + int keylen, int maxkeylen, + int *key_rsc_len, + enum wpa_alg *alg) +{ + int ret = 0; + + switch (group_cipher) { + case WPA_CIPHER_CCMP: + if (keylen != 16 || maxkeylen < 16) { + ret = -1; + break; + } + *key_rsc_len = 6; + *alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + if (keylen != 32 || maxkeylen < 32) { + ret = -1; + break; + } + *key_rsc_len = 6; + *alg = WPA_ALG_TKIP; + break; + case WPA_CIPHER_WEP104: + if (keylen != 13 || maxkeylen < 13) { + ret = -1; + break; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP104; + break; + case WPA_CIPHER_WEP40: + if (keylen != 5 || maxkeylen < 5) { + ret = -1; + break; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP40; + break; + default: + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported Group Cipher %d", + group_cipher); + #endif + return -1; + } + + if (ret < 0 ) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported %s Group Cipher key " + "length %d (%d).", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + #endif + } + + return ret; +} + +void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, + const u8 *addr, int secure) +{ +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Key negotiation completed with " + MACSTR " [PTK=%s GTK=%s]\n", MAC2STR(addr), + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher)); +#endif + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(WPA_COMPLETED); + + sm->wpa_neg_complete(); + + if (secure) { + wpa_sm_mlme_setprotection( + sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + eapol_sm_notify_eap_success(TRUE); + /* + * Start preauthentication after a short wait to avoid a + * possible race condition between the data receive and key + * configuration after the 4-Way Handshake. This increases the + * likelyhood of the first preauth EAPOL-Start frame getting to + * the target AP. + */ + } + +} + + +int wpa_supplicant_install_gtk(struct wpa_sm *sm, + struct wpa_gtk_data *gd) +{ + u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + u8 *key_rsc=(sm->install_gtk).seq; + + wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); + + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " + "(keyidx=%d tx=%d len=%d).\n", gd->keyidx, gd->tx, + gd->gtk_len); + #endif + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + memcpy(gtk_buf, gd->gtk, 16); + memcpy(gtk_buf + 16, gd->gtk + 16, 8); + memcpy(gtk_buf + 24, gd->gtk + 24, 8); + _gtk = gtk_buf; + } + //now only use keycache entry1 for group key + sm->key_entry_valid = gd->keyidx; + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_sm_set_key(&(sm->install_gtk), gd->alg, + sm->bssid, //(u8 *) "\xff\xff\xff\xff\xff\xff", + gd->keyidx, 1, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len,sm->key_entry_valid) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to set " + "GTK to the driver (Group only)."); + #endif + return -1; + } + } else if (wpa_sm_set_key(&(sm->install_gtk), gd->alg, + sm->bssid, //(u8 *) "\xff\xff\xff\xff\xff\xff", + gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len, sm->key_entry_valid) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Failed to set GTK to " + "the driver (alg=%d keylen=%d keyidx=%d)", + gd->alg, gd->gtk_len, gd->keyidx); + #endif + return -1; + } + + return 0; +} + +bool wpa_supplicant_gtk_in_use(struct wpa_sm *sm, struct wpa_gtk_data *gd) +{ + u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + u8 gtk_get[32] = {0}; + u8 ifx; + int alg; + u8 bssid[6]; + int keyidx; + + wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); + + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Judge GTK: (keyidx=%d len=%d).", gd->keyidx, gd->gtk_len); + #endif + + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + memcpy(gtk_buf, gd->gtk, 16); + memcpy(gtk_buf + 16, gd->gtk + 16, 8); + memcpy(gtk_buf + 24, gd->gtk + 24, 8); + _gtk = gtk_buf; + } + + //check if gtk is in use. + if (wpa_sm_get_key(&ifx, &alg, bssid, &keyidx, gtk_get, gd->gtk_len, gd->keyidx) == 0) { + if (ifx == 0 && alg == gd->alg && memcmp(bssid, sm->bssid, ETH_ALEN) == 0 && + memcmp(_gtk, gtk_get, gd->gtk_len) == 0) { + wpa_printf(MSG_DEBUG, "GTK %d is already in use in entry %d, it may be an attack, ignor it.", gd->keyidx, gd->keyidx + 2); + return true; + } + } + + if (wpa_sm_get_key(&ifx, &alg, bssid, &keyidx, gtk_get, gd->gtk_len, (gd->keyidx+1)%2) == 0) { + if (ifx == 0 && alg == gd->alg && memcmp(bssid, sm->bssid, ETH_ALEN) == 0 && + memcmp(_gtk, gtk_get, gd->gtk_len) == 0) { + wpa_printf(MSG_DEBUG, "GTK %d is already in use in entry %d, it may be an attack, ignor it.", gd->keyidx, (gd->keyidx+1)%2 + 2); + return true; + } + } + + return false; +} + +int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, + int tx) +{ + if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit for GTK if a pairwise key is used. One AP + * seemed to set this bit (incorrectly, since Tx is only when + * doing Group Key only APs) and without this workaround, the + * data connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. */ + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); + #endif + return 0; + } + return tx; +} + +int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, + const u8 *gtk, size_t gtk_len, + int key_info) +{ +#ifndef CONFIG_NO_WPA2 + struct wpa_gtk_data *gd=&(sm->gd); + + /* + * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x + * GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] + * Reserved [bits 0-7] + * GTK + */ + + memset(gd, 0, sizeof(struct wpa_gtk_data)); + wpa_hexdump(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + if (gtk_len < 2 || gtk_len - 2 > sizeof(gd->gtk)) + return -1; + + gd->keyidx = gtk[0] & 0x3; + gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(gtk[0] & BIT(2))); + gtk += 2; + gtk_len -= 2; + + memcpy(gd->gtk, gtk, gtk_len); + gd->gtk_len = gtk_len; + + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gtk_len, gtk_len, + &(gd->key_rsc_len), &(gd->alg))) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK"); + #endif + return -1; + } + return 0; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + +#ifdef DEBUG_PRINT +void wpa_report_ie_mismatch(struct wpa_sm *sm, + const char *reason, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +#else +void wpa_report_ie_mismatch(struct wpa_sm *sm, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +#endif +{ + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: %s (src=" MACSTR ")", + reason, MAC2STR(src_addr)); + #endif + if (sm->ap_wpa_ie) { + wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", + sm->ap_wpa_ie, sm->ap_wpa_ie_len); + } + if (wpa_ie) { + if (!sm->ap_wpa_ie) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No WPA IE in " + "Beacon/ProbeResp"); + #endif + } + wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", + wpa_ie, wpa_ie_len); + } + + if (sm->ap_rsn_ie) { + wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + } + if (rsn_ie) { + if (!sm->ap_rsn_ie) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No RSN IE in " + "Beacon/ProbeResp"); + #endif + } + wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", + rsn_ie, rsn_ie_len); + } + + wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); +} + +int ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + return 0; + } + + if (ie->igtk) { + const wifi_wpa_igtk_t *igtk; + uint16_t keyidx; + + if (ie->igtk_len != sizeof(*igtk)) { + return -1; + } + igtk = (const wifi_wpa_igtk_t*)ie->igtk; + keyidx = WPA_GET_LE16(igtk->keyid); + if (keyidx > 4095) { + return -1; + } + return esp_wifi_set_igtk_internal(ESP_IF_WIFI_STA, igtk); + } + return 0; +#else + return 0; +#endif +} + + int wpa_supplicant_validate_ie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " + "Trying to get from scan results\n"); + #endif + if (wpa_sm_get_beacon_ie(sm) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Could not find AP from " + "the scan results"); + #endif + } else { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Found the current AP from " + "updated scan results\n"); + #endif + } + } + + if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && + (sm->ap_wpa_ie || sm->ap_rsn_ie)) { +#ifdef DEBUG_PRINT + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp (no IE?)", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#else + wpa_report_ie_mismatch(sm, + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#endif + return -1; + } + + if ((ie->wpa_ie && sm->ap_wpa_ie && + (ie->wpa_ie_len != sm->ap_wpa_ie_len || + memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || + (ie->rsn_ie && sm->ap_rsn_ie && + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + ie->rsn_ie, ie->rsn_ie_len))) { +#ifdef DEBUG_PRINT + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#else + wpa_report_ie_mismatch(sm, + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#endif + return -1; + } + + if (sm->proto == WPA_PROTO_WPA && + ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { +#ifdef DEBUG_PRINT + wpa_report_ie_mismatch(sm, "Possible downgrade attack " + "detected - RSN was enabled and RSN IE " + "was in msg 3/4, but not in " + "Beacon/ProbeResp", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#else + wpa_report_ie_mismatch(sm, + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); +#endif + return -1; + } + + return 0; +} + +/** + * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @key_info: Key Info + * @kde: KDEs to include the EAPOL-Key frame + * @kde_len: Length of KDEs + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ + int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info, + const u8 *kde, size_t kde_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + if (kde) + wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply) + kde_len, + &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + // indicate this is 4_4 phsk pbuf + sm->flags |= 0x40; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, kde_len); + if (kde) + memcpy(reply + 1, kde, kde_len); + + wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 4/4\n"); + wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + void wpa_sm_set_seq(struct wpa_sm *sm, struct wpa_eapol_key *key, u8 isptk) +{ + u8 *key_rsc, *seq; + u8 null_rsc[WPA_KEY_RSC_LEN]; + + os_bzero(null_rsc, WPA_KEY_RSC_LEN); + + if (sm->proto == WPA_PROTO_RSN) { + key_rsc = null_rsc; + } else { + key_rsc = key->key_rsc; + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, WPA_KEY_RSC_LEN); + } + + seq=(isptk) ? (sm->install_ptk).seq : (sm->install_gtk).seq; + memcpy(seq, key_rsc, WPA_KEY_RSC_LEN); +} + + void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + struct wpa_eapol_key *key, + u16 ver) +{ + u16 key_info, keylen, len; + const u8 *pos; + struct wpa_eapol_ie_parse ie; + + wpa_sm_set_state(WPA_LAST_HALF_4WAY_HANDSHAKE); + wpa_printf(MSG_DEBUG, "WPA 3/4-Way Handshake\n"); + + key_info = WPA_GET_BE16(key->key_info); + + pos = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); + wpa_supplicant_parse_ies(pos, len, &ie); + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: GTK IE in unencrypted key data"); + #endif + goto failed; + } + + if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) + goto failed; + + if (memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: ANonce from message 1 of 4-Way " + "Handshake differs from 3 of 4-Way Handshake - drop" + " packet (src=" MACSTR ")", MAC2STR(sm->bssid)); + #endif + goto failed; + } + + keylen = WPA_GET_BE16(key->key_length); + switch (sm->pairwise_cipher) { + case WPA_CIPHER_CCMP: + if (keylen != 16) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Invalid CCMP key length " + "%d (src=" MACSTR ")", + keylen, MAC2STR(sm->bssid)); + #endif + goto failed; + } + break; + case WPA_CIPHER_TKIP: + if (keylen != 32) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Invalid TKIP key length " + "%d (src=" MACSTR ")", + keylen, MAC2STR(sm->bssid)); + #endif + goto failed; + } + break; + } + + + /* SNonce was successfully used in msg 3/4, so mark it to be renewed + * for the next 4-Way Handshake. If msg 3 is received again, the old + * SNonce will still be used to avoid changing PTK. */ + sm->renew_snonce = 1; + + /*ready for txcallback , set seq and set txcallback param*/ + wpa_sm_set_seq(sm, key, 1); + sm->key_info=key_info; + (sm->gd).gtk_len=0; //used as flag if gtk is installed in callback + if (ie.gtk) { + wpa_sm_set_seq(sm, key, 0); + if (wpa_supplicant_pairwise_gtk(sm, + ie.gtk, ie.gtk_len, key_info) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Failed to configure GTK"); + #endif + goto failed; + } + } + + if (ieee80211w_set_keys(sm, &ie) < 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Failed to configure IGTK"); + #endif + goto failed; + } + + /*after txover, callback will continue run remain task*/ + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + NULL, 0, &sm->ptk)) { + goto failed; + } + + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + + int wpa_supplicant_send_4_of_4_txcallback(struct wpa_sm *sm) +{ + u16 key_info=sm->key_info; + + if (sm->key_install && key_info & WPA_KEY_INFO_INSTALL) { + if (wpa_supplicant_install_ptk(sm)) + goto failed; + } + else if (sm->key_install == false) { + wpa_printf(MSG_DEBUG, "PTK has been installed, it may be an attack, ignor it."); + } + + wpa_sm_set_state(WPA_GROUP_HANDSHAKE); + + if((sm->gd).gtk_len) { + if (sm->key_install) { + if (wpa_supplicant_install_gtk(sm, &(sm->gd))) + goto failed; + } + else { + wpa_printf(MSG_DEBUG, "GTK has been installed, it may be an attack, ignor it."); + } + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + } + + + if (key_info & WPA_KEY_INFO_SECURE) { + wpa_sm_mlme_setprotection( + sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + } + + sm->key_install = false; + + return 0; + +failed: + return WLAN_REASON_UNSPECIFIED; +} + + + int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, + const u8 *keydata, + size_t keydatalen, + u16 key_info, + struct wpa_gtk_data *gd) +{ + int maxkeylen; + struct wpa_eapol_ie_parse ie; + + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + wpa_supplicant_parse_ies(keydata, keydatalen, &ie); + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: GTK IE in unencrypted key data"); + #endif + return -1; + } + if (ie.gtk == NULL) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No GTK IE in Group Key msg 1/2"); + #endif + return -1; + } + maxkeylen = gd->gtk_len = ie.gtk_len - 2; + + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); + gd->keyidx = ie.gtk[0] & 0x3; + gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(ie.gtk[0] & BIT(2))); + if (ie.gtk_len - 2 > sizeof(gd->gtk)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Too long GTK in GTK IE " + "(len=%lu)", (unsigned long) ie.gtk_len - 2); + #endif + return -1; + } + memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); + + if (ieee80211w_set_keys(sm, &ie) < 0) + { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "RSN: Failed to configure IGTK"); + #endif + } + return 0; +} + + int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + size_t keydatalen, int key_info, + size_t extra_len, u16 ver, + struct wpa_gtk_data *gd) +{ + size_t maxkeylen; + u8 ek[32]; + + gd->gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = keydatalen; + if (keydatalen > extra_len) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Truncated EAPOL-Key packet:" + " key_data_length=%lu > extra_len=%lu", + (unsigned long) keydatalen, + (unsigned long) extra_len); + #endif + return -1; + } + if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (maxkeylen < 8) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); + #endif + return -1; + } + maxkeylen -= 8; + } + + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT; + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, sm->ptk.kek, 16); + if (keydatalen > sizeof(gd->gtk)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: RC4 key data " + "too long (%lu)", + (unsigned long) keydatalen); + #endif + return -1; + } + memcpy(gd->gtk, key + 1, keydatalen); + if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: RC4 failed"); + #endif + return -1; + } + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (keydatalen % 8) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP " + "len %lu", (unsigned long) keydatalen); + #endif + return -1; + } + if (maxkeylen > sizeof(gd->gtk)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: AES-WRAP key data " + "too long (keydatalen=%lu maxkeylen=%lu)", + (unsigned long) keydatalen, + (unsigned long) maxkeylen); + #endif + return -1; + } + if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, + (const u8 *) (key + 1), gd->gtk)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: AES unwrap " + "failed - could not decrypt GTK"); + #endif + return -1; + } + } else { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported key_info type %d", + ver); + #endif + return -1; + } + gd->tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(key_info & WPA_KEY_INFO_TXRX)); + return 0; +} + + int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + int ver, u16 key_info) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + // indicate this is 2_2 phsk pbuf + sm->flags |= 0x80; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; + key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_printf(MSG_DEBUG, "WPA Send 2/2 Group key\n"); + + wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_key *key, + int extra_len, u16 ver) +{ + u16 key_info, keydatalen; + int ret; + struct wpa_gtk_data *gd=&(sm->gd); + + memset(gd, 0, sizeof(struct wpa_gtk_data)); + + wpa_printf(MSG_DEBUG, "WPA 1/2 Group Key Handshake\n"); + + key_info = WPA_GET_BE16(key->key_info); + keydatalen = WPA_GET_BE16(key->key_data_length); + + if (sm->proto == WPA_PROTO_RSN) { + ret = wpa_supplicant_process_1_of_2_rsn(sm, + (const u8 *) (key + 1), + keydatalen, key_info, + gd); + } else { + ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, + key_info, extra_len, + ver, gd); + } + + wpa_sm_set_state(WPA_GROUP_HANDSHAKE); + + if (ret) + goto failed; + + /*before callback, set seq for add param difficult in callback*/ + wpa_sm_set_seq(sm, key, 0); + sm->key_info=key_info; + + if (wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) + goto failed; + + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + int wpa_supplicant_send_2_of_2_txcallback(struct wpa_sm *sm) +{ + u16 key_info=sm->key_info; + u16 rekey= (WPA_SM_STATE(sm) == WPA_COMPLETED); + + if((sm->gd).gtk_len) { + if (wpa_supplicant_gtk_in_use(sm, &(sm->gd)) == false) { + if (wpa_supplicant_install_gtk(sm, &(sm->gd))) + goto failed; + } + } else { + goto failed; + } + + if (rekey) { +#ifdef MSG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Group rekeying " + "completed with " MACSTR " [GTK=%s]", + MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); +#endif + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(WPA_COMPLETED); + } else + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info &WPA_KEY_INFO_SECURE); + return 0; + +failed: + return WLAN_REASON_UNSPECIFIED; +} + + int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_eapol_key *key, + u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + memcpy(mic, key->key_mic, 16); + if (sm->tptk_set) { + memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, + key->key_mic); + if (memcmp(mic, key->key_mic, 16) != 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); + #endif + } else { + ok = 1; + sm->tptk_set = 0; + sm->ptk_set = 1; + memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + } + } + + if (!ok && sm->ptk_set) { + memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, + key->key_mic); + if (memcmp(mic, key->key_mic, 16) != 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Invalid EAPOL-Key MIC " + "- dropping packet"); + #endif + return -1; + } + ok = 1; + } + + if (!ok) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Could not verify EAPOL-Key MIC " + "- dropping packet"); + #endif + return -1; + } + + memcpy(sm->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + /*update request_counter for mic failure report*/ + memcpy(sm->request_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + return 0; +} + + +/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ + int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, + struct wpa_eapol_key *key, u16 ver) +{ + u16 keydatalen = WPA_GET_BE16(key->key_data_length); + + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + (u8 *) (key + 1), keydatalen); + if (!sm->ptk_set) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: PTK not available, " + "cannot decrypt EAPOL-Key key data."); + #endif + return -1; + } + + /* Decrypt key data here so that this operation does not need + * to be implemented separately for each message type. */ + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + u8 ek[32]; + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, sm->ptk.kek, 16); + if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: RC4 failed"); + #endif + return -1; + } + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || + sm->key_mgmt == WPA_KEY_MGMT_SAE) { + u8 *buf; + if (keydatalen % 8) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported " + "AES-WRAP len %d", keydatalen); + #endif + return -1; + } + keydatalen -= 8; /* AES-WRAP adds 8 bytes */ + + /*replaced by xxx to remove malloc*/ + buf = ((u8 *) (key+1))+ 8; + /* + buf = os_wifi_malloc(keydatalen); + if (buf == NULL) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No memory for " + "AES-UNWRAP buffer"); + #endif + return -1; + } + */ + if (aes_unwrap(sm->ptk.kek, keydatalen / 8, + (u8 *) (key + 1), buf)) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); + #endif + return -1; + } + memcpy(key + 1, buf, keydatalen); + WPA_PUT_BE16(key->key_data_length, keydatalen); + } else { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported key_info type %d", + ver); + #endif + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", + (u8 *) (key + 1), keydatalen); + return 0; +} + + + void wpa_eapol_key_dump(int level, const struct wpa_eapol_key *key) +{ +#ifdef DEBUG_PRINT + if (level < MSG_MSGDUMP) + return; + + u16 key_info = WPA_GET_BE16(key->key_info); + + wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d\n", key->type); + wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s" + "%s%s%s%s%s%s%s)\n", + key_info, (u32)(key_info & WPA_KEY_INFO_TYPE_MASK), + (u32)((key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT), + (u32)((key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13), + key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", + key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", + key_info & WPA_KEY_INFO_ACK ? " Ack" : "", + key_info & WPA_KEY_INFO_MIC ? " MIC" : "", + key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", + key_info & WPA_KEY_INFO_ERROR ? " Error" : "", + key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", + key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); + wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u\n", + WPA_GET_BE16(key->key_length), + WPA_GET_BE16(key->key_data_length)); +#endif +} + +/** + * wpa_sm_rx_eapol - Process received WPA EAPOL frames + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @src_addr: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure + * + * This function is called for each received EAPOL frame. Other than EAPOL-Key + * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is + * only processing WPA and WPA2 EAPOL-Key frames. + * + * The received EAPOL-Key packets are validated and valid packets are replied + * to. In addition, key material (PTK, GTK) is configured at the end of a + * successful key handshake. + * buf begin from version, so remove mac header ,snap header and ether_type + */ +int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len) +{ + struct wpa_sm *sm = &gWpaSm; + u32 plen, data_len, extra_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, ver; + u8 *tmp; + int ret = -1; + + if (len < sizeof(*hdr) + sizeof(*key)) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " + "EAPOL-Key (len %lu, expecting at least %lu)", + (unsigned long) len, + (unsigned long) sizeof(*hdr) + sizeof(*key)); +#endif + return 0; + } + + tmp = buf; + + hdr = (struct ieee802_1x_hdr *) tmp; + key = (struct wpa_eapol_key *) (hdr + 1); + plen = be_to_host16(hdr->length); + data_len = plen + sizeof(*hdr); + +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%d\n", + hdr->version, hdr->type, plen); +#endif + + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " + "not a Key frame", hdr->type); +#endif + ret = 0; + goto out; + } + if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); +#endif + ret = 0; + goto out; + } + + if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) + { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " + "discarded", key->type); +#endif + ret = 0; + goto out; + } + + wpa_eapol_key_dump(MSG_MSGDUMP, key); + + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); + + if (data_len < len) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " + "802.1X data\n", (unsigned long) len - data_len); +#endif + } + key_info = WPA_GET_BE16(key->key_info); + ver = key_info & WPA_KEY_INFO_TYPE_MASK; + + if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && +#ifdef CONFIG_IEEE80211W + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && +#ifdef CONFIG_WPA3_SAE + sm->key_mgmt != WPA_KEY_MGMT_SAE && +#endif +#endif + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Unsupported EAPOL-Key descriptor " + "version %d.", ver); +#endif + goto out; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && + sm->key_mgmt != WPA_KEY_MGMT_SAE) { + goto out; + } + } else +#endif + + if (sm->pairwise_cipher == WPA_CIPHER_CCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { +#ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: CCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2.", ver); +#endif + if (sm->group_cipher != WPA_CIPHER_CCMP && + !(key_info & WPA_KEY_INFO_KEY_TYPE)) { + /* Earlier versions of IEEE 802.11i did not explicitly + * require version 2 descriptor for all EAPOL-Key + * packets, so allow group keys to use version 1 if + * CCMP is not used for them. */ + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Backwards compatibility: " + "allow invalid version for non-CCMP group " + "keys"); + #endif + } else + goto out; + } + + + if ( sm->rx_replay_counter_set && + memcmp(key->replay_counter, sm->rx_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Replay Counter did not" + " increase - dropping packet"); + #endif + goto out; + } + + if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: No Ack bit in key_info"); + #endif + goto out; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key with Request bit - dropped"); + #endif + goto out; + } + + if ((key_info & WPA_KEY_INFO_MIC) && + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + goto out; + + extra_len = data_len - sizeof(*hdr) - sizeof(*key); + + if (WPA_GET_BE16(key->key_data_length) > extra_len) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%d > %lu)", + WPA_GET_BE16(key->key_data_length), + (unsigned long) extra_len); + #endif + goto out; + } + extra_len = WPA_GET_BE16(key->key_data_length); + + if (sm->proto == WPA_PROTO_RSN && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supplicant_decrypt_key_data(sm, key, ver)) + goto out; + extra_len = WPA_GET_BE16(key->key_data_length); + } + + if (key_info & WPA_KEY_INFO_KEY_TYPE) { + if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: Ignored EAPOL-Key " + "(Pairwise) with non-zero key index"); + #endif + goto out; + } + + if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(sm, key, ver); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(sm, src_addr, key, + ver); + } + } else { + if (key_info & WPA_KEY_INFO_MIC) { + /* 1/2 Group Key Handshake */ + wpa_supplicant_process_1_of_2(sm, src_addr, key, + extra_len, ver); + } else { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key (Group) " + "without Mic bit - dropped"); + #endif + } + } + + ret = 1; + +out: + + return ret; +} + +/** + * wpa_supplicant_set_state - Set current connection state + * @wpa_s: Pointer to wpa_supplicant data + * @state: The new connection state + * + * This function is called whenever the connection state changes, e.g., + * association is completed for WPA/WPA2 4-Way Handshake is started. + */ +void wpa_sm_set_state(enum wpa_states state) +{ + struct wpa_sm *sm = &gWpaSm; + if(WPA_MIC_FAILURE==WPA_SM_STATE(sm)) + os_timer_disarm(&(sm->cm_timer)); + sm->wpa_state= state; +} + +/** + * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK + * will be cleared. + */ +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->cur_pmksa) { + sm->pmk_len = sm->cur_pmksa->pmk_len; + os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); + } else { + sm->pmk_len = PMK_LEN; + os_memset(sm->pmk, 0, PMK_LEN); + } +} + +void ICACHE_FLASH_ATTR wpa_register(char* payload, WPA_SEND_FUNC snd_func, + WPA_SET_ASSOC_IE set_assoc_ie_func, WPA_INSTALL_KEY ppinstallkey, WPA_GET_KEY ppgetkey, WPA_DEAUTH_FUNC wpa_deauth, + WPA_NEG_COMPLETE wpa_neg_complete) +{ + struct wpa_sm *sm = &gWpaSm; + + sm->eapol_version = 0x1; /* DEFAULT_EAPOL_VERSION */ + sm->sendto = snd_func; + sm->config_assoc_ie = set_assoc_ie_func; + sm->install_ppkey = ppinstallkey; + sm->get_ppkey = ppgetkey; + sm->wpa_deauthenticate = wpa_deauth; + sm->wpa_neg_complete = wpa_neg_complete; + sm->key_entry_valid = 0; + sm->key_install = false; + sm->flags = 0; + wpa_sm_set_state(WPA_INACTIVE); + + sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); + if (sm->pmksa == NULL) { + wpa_printf(MSG_ERROR, + "RSN: PMKSA cache initialization failed"); + return false; + } + return true; +} + +void wpa_set_profile(u32 wpa_proto, u8 auth_mode) +{ + struct wpa_sm *sm = &gWpaSm; + + sm->proto = wpa_proto; + if (auth_mode == WPA2_AUTH_ENT) { + sm->key_mgmt = WPA_KEY_MGMT_IEEE8021X; /* for wpa2 enterprise */ + } else if (auth_mode == WPA2_AUTH_PSK_SHA256) { + sm->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; + } else if (auth_mode == WPA3_AUTH_PSK) { + sm->key_mgmt = WPA_KEY_MGMT_SAE; /* for WPA3 PSK */ + } else { + sm->key_mgmt = WPA_KEY_MGMT_PSK; /* fixed to PSK for now */ + } +} + +void wpa_set_pmk(uint8_t *pmk, const u8 *pmkid, bool cache_pmksa) +{ + struct wpa_sm *sm = &gWpaSm; + + memcpy(sm->pmk, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; + + if (cache_pmksa) { + pmksa_cache_add(sm->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } +} + +int wpa_set_bss(char *macddr, char * bssid, u8 pairwise_cipher, u8 group_cipher, char *passphrase, u8 *ssid, size_t ssid_len) +{ + int res = 0; + struct wpa_sm *sm = &gWpaSm; + + sm->pairwise_cipher = BIT(pairwise_cipher); + sm->group_cipher = BIT(group_cipher); + sm->rx_replay_counter_set = 0; //init state not intall replay counter value + memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + sm->wpa_ptk_rekey = 0; + sm->renew_snonce = 1; + memcpy(sm->own_addr, macddr, ETH_ALEN); + memcpy(sm->bssid, bssid, ETH_ALEN); + sm->ap_notify_completed_rsne = esp_wifi_sta_is_ap_notify_completed_rsne_internal(); + + if (sm->key_mgmt == WPA_KEY_MGMT_SAE || + (esp_wifi_sta_prof_is_wpa2_internal() && + esp_wifi_sta_get_prof_authmode_internal() == WPA2_AUTH_ENT)) { + pmksa_cache_set_current(sm, NULL, (const u8*) bssid, 0, 0); + wpa_sm_set_pmk_from_pmksa(sm); + } + +#ifdef CONFIG_IEEE80211W + if (esp_wifi_sta_pmf_enabled()) { + wifi_config_t wifi_cfg; + + esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg); + sm->pmf_cfg = wifi_cfg.sta.pmf_cfg; + sm->mgmt_group_cipher = cipher_type_map_public_to_supp(esp_wifi_sta_get_mgmt_group_cipher()); + } +#endif + set_assoc_ie(assoc_ie_buf); /* use static buffer */ + res = wpa_gen_wpa_ie(sm, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); + if (res < 0) + return -1; + sm->assoc_wpa_ie_len = res; + wpa_set_passphrase(passphrase, ssid, ssid_len); + return 0; +} + +/* + * Call after set ssid since we calc pmk inside this routine directly + */ + void +wpa_set_passphrase(char * passphrase, u8 *ssid, size_t ssid_len) +{ + struct wifi_ssid *sta_ssid = esp_wifi_sta_get_prof_ssid_internal(); + struct wpa_sm *sm = &gWpaSm; + u8 phlen = 0; + + if (passphrase == NULL) return; + + phlen = os_strlen(passphrase); + + /* + * Here only handle passphrase string. Need extra step to handle 32B, 64Hex raw + * PMK. + */ + WPA_ASSERT(phlen >= 6 && phlen < 64); + + /* This is really SLOW, so just re cacl while reset param */ + if (esp_wifi_sta_get_reset_param_internal() != 0) { + // check it's psk + if (os_strlen((char *)esp_wifi_sta_get_prof_password_internal()) == 64) { + hexstr2bin((char *)esp_wifi_sta_get_prof_password_internal(), esp_wifi_sta_get_prof_pmk_internal(), PMK_LEN); + } else { + pbkdf2_sha1((char *)esp_wifi_sta_get_prof_password_internal(), (char *)sta_ssid->ssid, (size_t)sta_ssid->len, + 4096, esp_wifi_sta_get_prof_pmk_internal(), PMK_LEN); + } + esp_wifi_sta_update_ap_info_internal(); + esp_wifi_sta_set_reset_param_internal(0); + } + + if (sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + /* TODO nothing */ + } else { + memcpy(sm->pmk, esp_wifi_sta_get_prof_pmk_internal(), PMK_LEN); + } + sm->pmk_len = PMK_LEN; +} + + void +set_assoc_ie(u8 * assoc_buf) +{ + struct wpa_sm *sm = &gWpaSm; + + sm->assoc_wpa_ie = assoc_buf + 2; + //wpa_ie insert OUI 4 byte before ver, but RSN have 2 bytes of RSN capability, + // so wpa_ie have two more bytes than rsn_ie + if ( sm->proto == WPA_PROTO_WPA) + sm->assoc_wpa_ie_len = ASSOC_IE_LEN; + else + sm->assoc_wpa_ie_len = ASSOC_IE_LEN - 2; + + sm->config_assoc_ie(sm->proto, assoc_buf, sm->assoc_wpa_ie_len); +} + + int +wpa_sm_set_key(struct install_key *key_sm, enum wpa_alg alg, + u8 *addr, int key_idx, int set_tx, + u8 *seq, size_t seq_len, + u8 *key, size_t key_len, + int key_entry_valid) +{ + struct wpa_sm *sm = &gWpaSm; + + /*gtk or ptk both need check countermeasures*/ + if (alg == WPA_ALG_TKIP && key_len == 32) { + /* Clear the MIC error counter when setting a new PTK. */ + key_sm->mic_errors_seen = 0; + } + + key_sm->keys_cleared = 0; + key_sm->alg = alg; + memcpy(key_sm->addr, addr, ETH_ALEN); + key_sm->key_idx = key_idx; + key_sm->set_tx = set_tx; + memcpy(key_sm->key, key, key_len); + + sm->install_ppkey(alg, addr, key_idx, set_tx, seq, seq_len, key, key_len, key_entry_valid); + return 0; +} + + int +wpa_sm_get_key(uint8_t *ifx, int *alg, u8 *addr, int *key_idx, u8 *key, size_t key_len, int key_entry_valid) +{ + struct wpa_sm *sm = &gWpaSm; + return sm->get_ppkey(ifx, alg, addr, key_idx, key, key_len, key_entry_valid); +} + +void wpa_supplicant_clr_countermeasures(u16 *pisunicast) +{ + struct wpa_sm *sm = &gWpaSm; + (sm->install_ptk).mic_errors_seen=0; + (sm->install_gtk).mic_errors_seen=0; + wpa_printf(MSG_DEBUG, "WPA: TKIP countermeasures clean\n"); +} + +/*recovery from countermeasures state, countermeasures state is period that stop connection with ap + also used in wpa_init after connecting with ap +*/ +void wpa_supplicant_stop_countermeasures(u16 *pisunicast) +{ + struct wpa_sm *sm = &gWpaSm; + + if (sm->countermeasures) { + sm->countermeasures = 0; + wpa_supplicant_clr_countermeasures(NULL); + + wpa_printf(MSG_DEBUG, "WPA: TKIP countermeasures stopped\n"); + /*renew scan preocess, this isn't done now*/ + } + wpa_sm_set_state(WPA_DISCONNECTED); +} + +void ICACHE_FLASH_ATTR +wpa_michael_mic_failure(u16 isunicast) +{ + struct wpa_sm* sm = &gWpaSm; + s32* pmic_errors_seen = (isunicast) ? &((sm->install_ptk).mic_errors_seen) : &((sm->install_gtk).mic_errors_seen); + + wpa_printf(MSG_DEBUG, "\nTKIP MIC failure occur"); + + if (sm->wpa_state == WPA_GROUP_HANDSHAKE) { + return; + } + + /*both unicast and multicast mic_errors_seen need statistics*/ + if ((sm->install_ptk).mic_errors_seen + (sm->install_gtk).mic_errors_seen) { + /* Send the new MIC error report immediately since we are going + * to start countermeasures and AP better do the same. + */ + wpa_sm_set_state(WPA_TKIP_COUNTERMEASURES); + wpa_sm_key_request(sm, 1, 0); + + /* initialize countermeasures */ + sm->countermeasures = 1; + wpa_printf(MSG_DEBUG, "TKIP countermeasures started\n"); + + /* + * Need to wait for completion of request frame. We do not get + * any callback for the message completion, so just wait a + * short while and hope for the best. */ + os_delay_us(10000); + + /*deauthenticate AP*/ + + /*stop monitor next mic_failure timer,disconnect for 60sec, then stop contermeasures*/ + os_timer_disarm(&(sm->cm_timer)); + os_timer_setfn(&(sm->cm_timer), (os_timer_func_t*)wpa_supplicant_stop_countermeasures, NULL); + os_timer_arm(&(sm->cm_timer), 60 * 1000, false); + + /* TODO: mark the AP rejected for 60 second. STA is + * allowed to associate with another AP.. */ + } else { + *pmic_errors_seen=(*pmic_errors_seen)+1; + wpa_sm_set_state(WPA_MIC_FAILURE); + wpa_sm_key_request(sm, 1, 0); + /*start 60sec counter to monitor whether next mic_failure occur in this period, or clear mic_errors_seen*/ + os_timer_disarm(&(sm->cm_timer)); + os_timer_setfn(&(sm->cm_timer), (os_timer_func_t*)wpa_supplicant_clr_countermeasures, NULL); + os_timer_arm(&(sm->cm_timer), 60 * 1000, false); + } +} + +/* + eapol tx callback function to make sure new key + install after 4-way handoff +*/ +void eapol_txcb(void *eb) +{ + struct wpa_sm *sm = &gWpaSm; + u8 isdeauth = 0; //no_zero value is the reason for deauth + + if (false == esp_wifi_sta_is_running_internal()){ + return; + } + + switch(WPA_SM_STATE(sm)) { + case WPA_FIRST_HALF_4WAY_HANDSHAKE: + break; + case WPA_LAST_HALF_4WAY_HANDSHAKE: + if ((sm->flags & 0x40) != 0) { + isdeauth = wpa_supplicant_send_4_of_4_txcallback(sm); + sm->flags &= (~0x40); + } + break; + case WPA_GROUP_HANDSHAKE: + if ((sm->flags & 0x80) != 0) { + isdeauth = wpa_supplicant_send_2_of_2_txcallback(sm); + sm->flags &= (~0x80); + } + break; + case WPA_TKIP_COUNTERMEASURES: isdeauth=WLAN_REASON_MICHAEL_MIC_FAILURE; + break; + default: break; + } + + if(isdeauth) { + wpa_sm_deauthenticate(sm, isdeauth); + } +} + +bool wpa_sta_is_cur_pmksa_set(void) { + struct wpa_sm *sm = &gWpaSm; + return (pmksa_cache_get_current(sm) != NULL); +} + +#endif // EMBEDDED_SUPP + diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c new file mode 100644 index 00000000..147c4f34 --- /dev/null +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c @@ -0,0 +1,407 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE processing + * Copyright (c) 2003-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +#if 1//def EMBEDDED_SUPP + +#include "utils/includes.h" + +#include "utils/common.h" +#include "rsn_supp/wpa.h" +#include "common/ieee802_11_defs.h" +#include "rsn_supp/wpa_ie.h" +#include "rsn_supp/pmksa_cache.h" + +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + else + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); +} + + +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos; + struct wpa_ie_hdr *hdr; + + if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (group_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (group_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (group_cipher == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); + } else if (group_cipher == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (pairwise_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (pairwise_cipher == WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + } else { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + key_mgmt); + return -1; + } + pos += WPA_SELECTOR_LEN; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - wpa_ie) - 2; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} + + +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm) +{ +#ifndef CONFIG_NO_WPA2 + u8 *pos; + struct rsn_ie_hdr *hdr; + u16 capab; + u8 min_len = 0; + + if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { + wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", + (unsigned long) rsn_ie_len); + return -1; + } + + /* For WPA2-PSK, if the RSNE in AP beacon/probe response doesn't specify the + * pairwise cipher or AKM suite, the RSNE IE in association request + * should only contain group cihpher suite, otherwise the WPA2 improvements + * certification will fail. + */ + if ( (sm->ap_notify_completed_rsne == true) || (key_mgmt == WPA_KEY_MGMT_IEEE8021X) ) { + min_len = sizeof(*hdr) + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2; + } else { + min_len = sizeof(*hdr) + RSN_SELECTOR_LEN; + } + + if (rsn_ie_len < min_len) { + wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", (unsigned long) rsn_ie_len); + } + + hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (group_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (group_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (group_cipher == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); + } else if (group_cipher == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + if ( (sm->ap_notify_completed_rsne == false) && (key_mgmt != WPA_KEY_MGMT_IEEE8021X) ) { + hdr->len = (pos - rsn_ie) - 2; + return (pos - rsn_ie); + } + + *pos++ = 1; + *pos++ = 0; + if (pairwise_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (pairwise_cipher == WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + } else { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); +#ifdef CONFIG_IEEE80211R + } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPA3_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); +#endif /* CONFIG_WPA3_SAE */ + } else { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + key_mgmt); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->pmf_cfg.capable) { + capab |= WPA_CAPABILITY_MFPC; + if (sm->pmf_cfg.required || key_mgmt == WPA_KEY_MGMT_SAE) { + capab |= WPA_CAPABILITY_MFPR; + } + } +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (sm->cur_pmksa) { + /* PMKID Count (2 octets, little endian) */ + *pos++ = 1; + *pos++ = 0; + /* PMKID */ + os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + if (!sm->cur_pmksa) { + /* 0 PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - rsn_ie) - 2; + + WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); + + return pos - rsn_ie; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + + +/** + * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE + * @wpa_ie_len: Maximum length of the generated WPA/RSN IE + * Returns: Length of the generated WPA/RSN IE or -1 on failure + */ +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) +{ + if (sm->proto == WPA_PROTO_RSN) + return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt, sm->mgmt_group_cipher, + sm); + else + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", + ie->wpa_ie, ie->wpa_ie_len); + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: GTK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif + return 0; +} + + +/** + * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + #ifdef DEBUG_PRINT + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + #endif + wpa_hexdump(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", + ie->rsn_ie, ie->rsn_ie_len); + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +#endif // EMBEDDED_SUPP + diff --git a/components/wpa_supplicant/src/utils/common.c b/components/wpa_supplicant/src/utils/common.c new file mode 100644 index 00000000..c0ae047e --- /dev/null +++ b/components/wpa_supplicant/src/utils/common.c @@ -0,0 +1,237 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** + * inc_byte_array - Increment arbitrary length byte array by one + * @counter: Pointer to byte array + * @len: Length of the counter in bytes + * + * This function increments the last byte of the counter by one and continues + * rolling over to more significant bytes if the byte was incremented from + * 0xff to 0x00. + */ +void ICACHE_FLASH_ATTR inc_byte_array(u8* counter, size_t len) +{ + int pos = len - 1; + + while (pos >= 0) { + counter[pos]++; + + if (counter[pos] != 0) { + break; + } + + pos--; + } +} + +static int ICACHE_FLASH_ATTR hex2num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + + return -1; +} + + +int ICACHE_FLASH_ATTR hex2byte(const char* hex) +{ + int a, b; + a = hex2num(*hex++); + + if (a < 0) { + return -1; + } + + b = hex2num(*hex++); + + if (b < 0) { + return -1; + } + + return (a << 4) | b; +} + + +/** + * hexstr2bin - Convert ASCII hex string into binary data + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ +int ICACHE_FLASH_ATTR hexstr2bin(const char* hex, u8* buf, size_t len) +{ + size_t i; + int a; + const char* ipos = hex; + u8* opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + + if (a < 0) { + return -1; + } + + *opos++ = a; + ipos += 2; + } + + return 0; +} + +void ICACHE_FLASH_ATTR wpa_get_ntp_timestamp(u8* buf) +{ + struct os_time now; + u32 sec, usec; + be32 tmp; + + /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ + os_get_time(&now); + sec = now.sec + 2208988800U; /* Epoch to 1900 */ + /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ + usec = now.usec; + usec = 4295 * usec - (usec >> 5) - (usec >> 9); + tmp = host_to_be32(sec); + os_memcpy(buf, (u8*) &tmp, 4); + tmp = host_to_be32(usec); + os_memcpy(buf + 4, (u8*) &tmp, 4); +} + +char* ICACHE_FLASH_ATTR wpa_config_parse_string(const char* value, size_t* len) +{ + if (*value == '"' && (os_strlen(value) == 7 || os_strlen(value) == 15)) { + const char* pos; + char* str; + value++; + pos = (char*)os_strrchr(value, '"'); + + if (pos == NULL) { + return NULL; + } + + *len = pos - value; + str = (char*)os_malloc(*len + 1); + + if (str == NULL) { + return NULL; + } + + os_memcpy(str, value, *len); + str[*len] = '\0'; + return str; + } else { + u8* str; + size_t tlen, hlen = os_strlen(value); + + if (hlen == 5 || hlen == 13) { + *len = hlen; + str = (u8*)os_malloc(*len + 1); + os_memcpy(str, value, *len); + str[*len] = '\0'; + } else if (hlen == 10 || hlen == 26) { + tlen = hlen / 2; + str = (u8*)os_malloc(tlen + 1); + + if (str == NULL) { + return NULL; + } + + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + + str[tlen] = '\0'; + *len = tlen; + } else { + return NULL; + } + + return (char*) str; + } +} + +char* ICACHE_FLASH_ATTR +dup_binstr(const void* src, size_t len) +{ + char* res; + + if (src == NULL) { + return NULL; + } + + res = os_malloc(len + 1); + + if (res == NULL) { + return NULL; + } + + os_memcpy(res, src, len); + res[len] = '\0'; + + return res; +} + +void wpa_bin_clear_free(void *bin, size_t len) +{ + if (bin) { + os_memset(bin, 0, len); + os_free(bin); + } +} + +int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + +void bin_clear_free(void *bin, size_t len) +{ + if (bin) { + os_memset(bin, 0, len); + os_free(bin); + } +} + +void str_clear_free(char *str) +{ + if (str) { + size_t len = os_strlen(str); + os_memset(str, 0, len); + os_free(str); + } +} \ No newline at end of file diff --git a/components/wpa_supplicant/src/utils/wpa_debug.c b/components/wpa_supplicant/src/utils/wpa_debug.c new file mode 100644 index 00000000..a65b2b39 --- /dev/null +++ b/components/wpa_supplicant/src/utils/wpa_debug.c @@ -0,0 +1,80 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +#ifdef EMBEDDED_SUPP +#include "rom/ets_sys.h" +#include "utils/includes.h" +#include "utils/common.h" + +#if 0//def DEBUG_PRINT +int ICACHE_FLASH_ATTR wpa_snprintf_hex(char* buf, size_t buf_size, const u8* data, size_t len) +{ + return 0; +} + +void ICACHE_FLASH_ATTR wpa_debug_print_timestamp(void) +{ +#ifdef DEBUG_PRINT + struct os_time tv; + os_get_time(&tv); + wpa_printf(MSG_DEBUG, "%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#endif +} + +void ICACHE_FLASH_ATTR wpa_hexdump(int level, const char* title, const u8* buf, size_t len) +{ +#ifdef DEBUG_PRINT + size_t i; + + if (level < MSG_MSGDUMP) { + return; + } + + wpa_printf(MSG_DEBUG, "%s - hexdump(len=%lu):", title, (unsigned long) len); + + if (buf == NULL) { + wpa_printf(MSG_DEBUG, " [NULL]"); + } else { + for (i = 0; i < len; i++) { + wpa_printf(MSG_DEBUG, " %02x", buf[i]); + + if ((i + 1) % 16 == 0) { + wpa_printf(MSG_DEBUG, "\n"); + } + } + } + + wpa_printf(MSG_DEBUG, ""); +#endif +} + +void ICACHE_FLASH_ATTR wpa_hexdump_key(int level, const char* title, const u8* buf, size_t len) +{ + wpa_hexdump(level, title, buf, len); +} +#endif + +int ICACHE_FLASH_ATTR eloop_cancel_timeout(eloop_timeout_handler handler, + void* eloop_data, void* user_data) +{ + return 0; +} + +int ICACHE_FLASH_ATTR eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void* eloop_data, void* user_data) +{ + return 0; +} +#endif // EMBEDDED_SUPP + diff --git a/components/wpa_supplicant/src/utils/wpabuf.c b/components/wpa_supplicant/src/utils/wpabuf.c new file mode 100644 index 00000000..4282ef72 --- /dev/null +++ b/components/wpa_supplicant/src/utils/wpabuf.c @@ -0,0 +1,369 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/wpabuf.h" +#include "stdio.h" +#include "stdarg.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#define wpa_trace_show ets_printf + +#ifdef WPA_TRACE +#define WPABUF_MAGIC 0x51a974e3 + +struct wpabuf_trace { + unsigned int magic; +}; + +static struct wpabuf_trace* wpabuf_get_trace(const struct wpabuf* buf) +{ + return (struct wpabuf_trace*) + ((const u8*) buf - sizeof(struct wpabuf_trace)); +} +#endif /* WPA_TRACE */ + + +static void ICACHE_FLASH_ATTR wpabuf_overflow(const struct wpabuf* buf, size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace* trace = wpabuf_get_trace(buf); + + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + } + +#endif /* WPA_TRACE */ + wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", + buf, (unsigned long) buf->size, (unsigned long) buf->used, + (unsigned long) len); + wpa_trace_show("wpabuf overflow"); + //abort(); +} + + +int ICACHE_FLASH_ATTR wpabuf_resize(struct wpabuf** _buf, size_t add_len) +{ + struct wpabuf* buf = *_buf; +#ifdef WPA_TRACE + struct wpabuf_trace* trace; +#endif /* WPA_TRACE */ + + if (buf == NULL) { + *_buf = wpabuf_alloc(add_len); + return *_buf == NULL ? -1 : 0; + } + +#ifdef WPA_TRACE + trace = wpabuf_get_trace(buf); + + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_resize invalid magic"); + abort(); + } + +#endif /* WPA_TRACE */ + + if (buf->used + add_len > buf->size) { + unsigned char* nbuf; + + if (buf->ext_data) { + nbuf = (unsigned char*)os_realloc(buf->ext_data, buf->used + add_len); + + if (nbuf == NULL) { + return -1; + } + + os_memset(nbuf + buf->used, 0, add_len); + buf->ext_data = nbuf; + } else { +#ifdef WPA_TRACE + nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + + buf->used + add_len); + + if (nbuf == NULL) { + return -1; + } + + trace = (struct wpabuf_trace*) nbuf; + buf = (struct wpabuf*)(trace + 1); + os_memset(nbuf + sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + buf->used, 0, + add_len); +#else /* WPA_TRACE */ + nbuf = (unsigned char*)os_realloc(buf, sizeof(struct wpabuf) + + buf->used + add_len); + + if (nbuf == NULL) { + return -1; + } + + buf = (struct wpabuf*) nbuf; + os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, + add_len); +#endif /* WPA_TRACE */ + *_buf = buf; + } + + buf->size = buf->used + add_len; + } + + return 0; +} + + +/** + * wpabuf_alloc - Allocate a wpabuf of the given size + * @len: Length for the allocated buffer + * Returns: Buffer to the allocated wpabuf or %NULL on failure + */ +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_alloc(size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace* trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + len); + struct wpabuf* buf; + + if (trace == NULL) { + return NULL; + } + + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf*)(trace + 1); +#else /* WPA_TRACE */ + struct wpabuf* buf = (struct wpabuf*)os_zalloc(sizeof(struct wpabuf) + len); + + if (buf == NULL) { + return NULL; + } + +#endif /* WPA_TRACE */ + + buf->size = len; + return buf; +} + + +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_alloc_ext_data(u8* data, size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace* trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf)); + struct wpabuf* buf; + + if (trace == NULL) { + return NULL; + } + + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf*)(trace + 1); +#else /* WPA_TRACE */ + struct wpabuf* buf = (struct wpabuf*)os_zalloc(sizeof(struct wpabuf)); + + if (buf == NULL) { + return NULL; + } + +#endif /* WPA_TRACE */ + + buf->size = len; + buf->used = len; + buf->ext_data = data; + + return buf; +} + + +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_alloc_copy(const void* data, size_t len) +{ + struct wpabuf* buf = wpabuf_alloc(len); + + if (buf) { + wpabuf_put_data(buf, data, len); + } + + return buf; +} + + +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_dup(const struct wpabuf* src) +{ + struct wpabuf* buf = wpabuf_alloc(wpabuf_len(src)); + + if (buf) { + wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); + } + + return buf; +} + + +/** + * wpabuf_free - Free a wpabuf + * @buf: wpabuf buffer + */ +void ICACHE_FLASH_ATTR wpabuf_free(struct wpabuf* buf) +{ +#ifdef WPA_TRACE + struct wpabuf_trace* trace; + + if (buf == NULL) { + return; + } + + trace = wpabuf_get_trace(buf); + + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_free magic mismatch"); + abort(); + } + + os_free(buf->ext_data); + os_free(trace); +#else /* WPA_TRACE */ + + if (buf == NULL) { + return; + } + + os_free(buf->ext_data); + os_free(buf); +#endif /* WPA_TRACE */ +} + + +void* ICACHE_FLASH_ATTR wpabuf_put(struct wpabuf* buf, size_t len) +{ + void* tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + buf->used += len; + + if (buf->used > buf->size) { + wpabuf_overflow(buf, len); + } + + return tmp; +} + + +/** + * wpabuf_concat - Concatenate two buffers into a newly allocated one + * @a: First buffer + * @b: Second buffer + * Returns: wpabuf with concatenated a + b data or %NULL on failure + * + * Both buffers a and b will be freed regardless of the return value. Input + * buffers can be %NULL which is interpreted as an empty buffer. + */ +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_concat(struct wpabuf* a, struct wpabuf* b) +{ + struct wpabuf* n = NULL; + size_t len = 0; + + if (b == NULL) { + return a; + } + + if (a) { + len += wpabuf_len(a); + } + + if (b) { + len += wpabuf_len(b); + } + + n = wpabuf_alloc(len); + + if (n) { + if (a) { + wpabuf_put_buf(n, a); + } + + if (b) { + wpabuf_put_buf(n, b); + } + } + + wpabuf_free(a); + wpabuf_free(b); + + return n; +} + + +/** + * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length + * @buf: Buffer to be padded + * @len: Length for the padded buffer + * Returns: wpabuf padded to len octets or %NULL on failure + * + * If buf is longer than len octets or of same size, it will be returned as-is. + * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed + * by the source data. The source buffer will be freed on error, i.e., caller + * will only be responsible on freeing the returned buffer. If buf is %NULL, + * %NULL will be returned. + */ +struct wpabuf* ICACHE_FLASH_ATTR wpabuf_zeropad(struct wpabuf* buf, size_t len) +{ + struct wpabuf* ret; + size_t blen; + + if (buf == NULL) { + return NULL; + } + + blen = wpabuf_len(buf); + + if (blen >= len) { + return buf; + } + + ret = wpabuf_alloc(len); + + if (ret) { + os_memset(wpabuf_put(ret, len - blen), 0, len - blen); + wpabuf_put_buf(ret, buf); + } + + wpabuf_free(buf); + + return ret; +} + +void ICACHE_FLASH_ATTR wpabuf_printf(struct wpabuf* buf, char* fmt, ...) +{ + va_list ap; + void* tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + int res; + + va_start(ap, fmt); + res = vsnprintf(tmp, buf->size - buf->used, fmt, ap); + va_end(ap); + + if (res < 0 || (size_t) res >= buf->size - buf->used) { + wpabuf_overflow(buf, res); + } + + buf->used += res; +}