diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c b/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c index 59a3618eeb..2c99f691ef 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c @@ -1129,7 +1129,7 @@ esp_err_t esp_supp_dpp_init(void) return ESP_FAIL; } - if (is_wps_enabled()) { + if (wps_get_owner() != WPS_OWNER_NONE) { wpa_printf(MSG_ERROR, "DPP: failed to init since WPS is enabled"); return ESP_FAIL; } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c b/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c index 044ca46223..7f0e74f99c 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,7 +31,6 @@ extern struct wps_sm *gWpsSm; extern void *s_wps_api_lock; extern void *s_wps_api_sem; -extern bool s_wps_enabled; static int wps_reg_eloop_post_block(uint32_t sig, void *arg); @@ -74,7 +73,9 @@ static int wifi_ap_wps_init(const esp_wps_config_t *config) cfg.wps = sm->wps_ctx; os_memcpy((void *)cfg.pin, config->pin, 8); - wps_init_cfg_pin(&cfg); + if (wps_init_cfg_pin(&cfg) < 0) { + goto _err; + } os_memcpy(cfg.wps->uuid, sm->uuid, WPS_UUID_LEN); if ((sm->wps = wps_init(&cfg)) == NULL) { /* alloc wps_data */ goto _err; @@ -143,8 +144,8 @@ int wifi_ap_wps_deinit(void) static int wifi_ap_wps_enable_internal(const esp_wps_config_t *config) { - struct wps_sm *sm = gWpsSm; wifi_mode_t mode = WIFI_MODE_NULL; + enum wps_owner owner; if (esp_wifi_get_user_init_flag_internal() == 0) { wpa_printf(MSG_ERROR, "wps enable: wifi not started cannot enable wpsreg"); @@ -166,8 +167,9 @@ static int wifi_ap_wps_enable_internal(const esp_wps_config_t *config) return ESP_ERR_WIFI_MODE; } - if (s_wps_enabled) { - if (sm && os_memcmp(sm->identity, WSC_ID_ENROLLEE, sm->identity_len) == 0) { + owner = wps_get_owner(); + if (owner != WPS_OWNER_NONE) { + if (owner == WPS_OWNER_ENROLLEE) { wpa_printf(MSG_ERROR, "wps enable: wps enrollee already enabled cannot enable wpsreg"); return ESP_ERR_WIFI_MODE; } else { @@ -199,7 +201,7 @@ static int wifi_ap_wps_enable_internal(const esp_wps_config_t *config) } wpa_printf(MSG_INFO, "wifi_wps_enable"); - s_wps_enabled = true; + wps_set_owner(WPS_OWNER_REGISTRAR); return ESP_OK; _err: @@ -222,16 +224,15 @@ int esp_wifi_ap_wps_enable(const esp_wps_config_t *config) int wifi_ap_wps_disable_internal(void) { - struct wps_sm *sm = gWpsSm; + enum wps_owner owner = wps_get_owner(); - if (sm && os_memcmp(sm->identity, WSC_ID_ENROLLEE, sm->identity_len) == 0) { - return ESP_ERR_WIFI_MODE; - } - - if (!s_wps_enabled) { + if (owner == WPS_OWNER_NONE) { wpa_printf(MSG_DEBUG, "wps disable: already disabled"); return ESP_OK; } + if (owner == WPS_OWNER_ENROLLEE) { + return ESP_ERR_WIFI_MODE; + } wpa_printf(MSG_INFO, "wifi_wps_disable"); if (wps_set_type(WPS_TYPE_DISABLE) != ESP_OK) { @@ -246,7 +247,7 @@ int wifi_ap_wps_disable_internal(void) goto _err; } - s_wps_enabled = false; + wps_set_owner(WPS_OWNER_NONE); return ESP_OK; _err: @@ -266,6 +267,8 @@ int esp_wifi_ap_wps_disable(void) static int wifi_ap_wps_start_internal(const unsigned char *pin) { wifi_mode_t mode = WIFI_MODE_NULL; + int prev_wps_status; + enum wps_owner owner; esp_wifi_get_mode(&mode); if (mode != WIFI_MODE_AP && mode != WIFI_MODE_APSTA) { @@ -273,11 +276,15 @@ static int wifi_ap_wps_start_internal(const unsigned char *pin) return ESP_ERR_WIFI_MODE; } - if (!s_wps_enabled) { + owner = wps_get_owner(); + if (owner == WPS_OWNER_NONE) { wpa_printf(MSG_ERROR, "wps start: wps not enabled"); - API_MUTEX_GIVE(); return ESP_ERR_WIFI_WPS_SM; } + if (owner != WPS_OWNER_REGISTRAR) { + wpa_printf(MSG_ERROR, "wps start: wps enrollee already enabled"); + return ESP_ERR_WIFI_MODE; + } if (wps_get_type() == WPS_TYPE_DISABLE || (wps_get_status() != WPS_STATUS_DISABLE && @@ -299,19 +306,24 @@ static int wifi_ap_wps_start_internal(const unsigned char *pin) /* TODO ideally SoftAP mode should also do a single scan in PBC mode * however softAP scanning is not available at the moment */ + prev_wps_status = wps_get_status(); if (wps_set_status(WPS_STATUS_PENDING) != ESP_OK) { return ESP_FAIL; } if (wps_get_type() == WPS_TYPE_PBC) { if (hostapd_wps_button_pushed(hostapd_get_hapd_data(), NULL) != ESP_OK) { - return ESP_FAIL; + goto _restore_status; } } else if (wps_get_type() == WPS_TYPE_PIN) { if (hostapd_wps_add_pin(hostapd_get_hapd_data(), pin) != ESP_OK) { - return ESP_FAIL; + goto _restore_status; } } return ESP_OK; + +_restore_status: + wps_set_status(prev_wps_status); + return ESP_FAIL; } int esp_wifi_ap_wps_start(const unsigned char *pin) @@ -366,7 +378,11 @@ static int wps_reg_eloop_post_block(uint32_t sig, void *arg) } } - eloop_register_timeout(0, 0, wps_reg_eloop_handler, (void *)&sig, (void *)¶m); + if (eloop_register_timeout(0, 0, wps_reg_eloop_handler, + (void *)&sig, (void *)¶m) != 0) { + wpa_printf(MSG_ERROR, "%s(): failed to post WPS work", __func__); + return ESP_FAIL; + } if (TRUE == os_semphr_take(s_wps_api_sem, OS_BLOCK)) { ret = param.ret; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c index 7905a2240b..85bd8e3365 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c @@ -18,6 +18,7 @@ #include #include +#include #include "utils/includes.h" #include "common.h" @@ -57,6 +58,12 @@ static const int WPS_POST_M8_WORKAROUND_SECS = 2; static const int WPS_SUCCESS_EVENT_TIMEOUT_SECS = 1; static const int WPS_SCAN_RETRY_TIMEOUT_SECS = 2; +#define WPS_IE_VENDOR_DATA_MIN_LEN 4 +#define WPS_IE_VENDOR_DATA_OFFSET 6 +#define EAP_REQUEST_TYPE_LEN 1 +#define EAP_WSC_MESSAGE_LEN_FIELD_LEN 2 +#define WPS_MESSAGE_LENGTH_MAX 50000 + // Callback structure for handling WPS events within the Wi-Fi task static struct wps_sm_funcs *s_wps_sm_cb = NULL; @@ -65,8 +72,8 @@ const char *wps_model_number = CONFIG_IDF_TARGET; /* API lock to ensure thread safety for public WPS functions */ void *s_wps_api_lock = NULL; /* Used in WPS/WPS-REG public API only, never be freed */ void *s_wps_api_sem = NULL; /* Sync semaphore used between WPS/WPS-REG public API caller task and WPS task, never be freed */ -/* Synchronous flag to indicate if the WPS feature is enabled by the user. Protected by s_wps_api_lock. */ -bool s_wps_enabled = false; +/* Atomic WPS owner shared between API callers and Wi-Fi task callbacks. */ +static atomic_int s_wps_owner = ATOMIC_VAR_INIT(WPS_OWNER_NONE); static void wifi_wps_scan_done(void *arg, ETS_STATUS status); static void wifi_wps_scan(void *data, void *user_ctx); @@ -270,9 +277,22 @@ static bool wps_parse_scan_result(struct wps_scan_ie *scan) if (scan->wps) { bool ap_found = false; - struct wpabuf *buf = wpabuf_alloc_copy(scan->wps + 6, scan->wps[1] - 4); + struct wpabuf *buf; const u8 *scan_uuid; + if (scan->wps[1] < WPS_IE_VENDOR_DATA_MIN_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE length %u", + scan->wps[1]); + return false; + } + + buf = wpabuf_alloc_copy(scan->wps + WPS_IE_VENDOR_DATA_OFFSET, + scan->wps[1] - WPS_IE_VENDOR_DATA_MIN_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "WPS: Failed to copy WPS IE"); + return false; + } + if ((wps_get_type() == WPS_TYPE_PBC && wps_is_selected_pbc_registrar(buf)) || (wps_get_type() == WPS_TYPE_PIN && wps_is_addr_authorized(buf, sm->ownaddr, 1))) { /* Found one AP with selected registrar true */ @@ -280,7 +300,7 @@ static bool wps_parse_scan_result(struct wps_scan_ie *scan) } if (ap_found) { - if (scan->ssid[1] > SSID_MAX_LEN) { + if (!scan->ssid || scan->ssid[1] > SSID_MAX_LEN) { wpabuf_free(buf); return false; } @@ -376,53 +396,100 @@ static int wps_send_frag_ack(u8 id) return ret; } -static int wps_enrollee_process_msg_frag(struct wpabuf **buf, int tot_len, u8 *frag_data, int frag_len, u8 flag) +static int wps_eap_wsc_process_cont(struct wps_eap_wsc_frag_data *frag, + const u8 *buf, size_t len, u8 op_code) { - struct wps_sm *sm = wps_sm_get(); - u8 identifier; - - if (!sm || !buf || !frag_data) { - wpa_printf(MSG_ERROR, "WPS: %s: Invalid arguments", __func__); - return ESP_FAIL; + if (op_code != frag->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in fragment " + "(expected %d)", op_code, frag->in_op_code); + return -1; } - identifier = sm->current_identifier; + if (len > wpabuf_tailroom(frag->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + return -1; + } - if (*buf == NULL) { - if (frag_len < 0 || tot_len < frag_len) { - wpa_printf(MSG_ERROR, "WPS: Invalid first fragment length"); - return ESP_FAIL; + wpabuf_put_data(frag->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(frag->in_buf)); + + return 0; +} + +static int wps_eap_wsc_process_fragment(struct wps_eap_wsc_frag_data *frag, + u8 flags, u8 op_code, + u16 message_length, + const u8 *buf, size_t len) +{ + if (frag->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (frag->in_buf == NULL) { + frag->in_buf = wpabuf_alloc(message_length); + if (frag->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for message"); + return -1; + } + frag->in_op_code = op_code; + wpabuf_put_data(frag->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(frag->in_buf)); + } + + return 0; +} + +static int wps_eap_wsc_prepare_rx_buf(struct wps_sm *sm, + struct wps_eap_wsc_frag_data *frag, + u8 flags, u8 op_code, + u16 message_length, + const u8 *buf, size_t len, + struct wpabuf *tmpbuf, + bool *fragment_pending) +{ + *fragment_pending = false; + + if (frag->in_buf) { + if (wps_eap_wsc_process_cont(frag, buf, len, op_code) < 0) { + goto fail; } - *buf = wpabuf_alloc(tot_len); - if (*buf == NULL) { - return ESP_ERR_NO_MEM; + if (flags & WSC_FLAGS_MF) { + if (wps_send_frag_ack(sm->current_identifier) != ESP_OK) { + goto fail; + } + *fragment_pending = true; } - - wpabuf_put_data(*buf, frag_data, frag_len); - return wps_send_frag_ack(identifier); + return ESP_OK; } - if (flag & WPS_MSG_FLAG_LEN) { - wpa_printf(MSG_ERROR, "WPS: %s: Invalid fragment flag: 0x%02x", __func__, flag); - wpabuf_free(*buf); - *buf = NULL; - return ESP_FAIL; - } - - if (frag_len < 0 || wpabuf_len(*buf) + frag_len > tot_len) { - wpa_printf(MSG_ERROR, "WPS: Invalid subsequent fragment length"); - wpabuf_free(*buf); - *buf = NULL; - return ESP_FAIL; - } - wpabuf_put_data(*buf, frag_data, frag_len); - - if (flag & WPS_MSG_FLAG_MORE) { - return wps_send_frag_ack(identifier); + if (flags & WSC_FLAGS_MF) { + if (wps_eap_wsc_process_fragment(frag, flags, op_code, + message_length, buf, len) < 0) { + goto fail; + } + if (wps_send_frag_ack(sm->current_identifier) != ESP_OK) { + goto fail; + } + *fragment_pending = true; + return ESP_OK; } + wpabuf_set(tmpbuf, buf, len); + frag->in_buf = tmpbuf; return ESP_OK; + +fail: + wpabuf_free(frag->in_buf); + frag->in_buf = NULL; + return ESP_FAIL; } static void wifi_station_wps_post_m8_timeout(void *data, void *user_ctx) @@ -436,29 +503,38 @@ static void wifi_station_wps_post_m8_timeout(void *data, void *user_ctx) sm->post_m8_recv = false; wps_finish(); } -}; +} static int wps_process_wps_mX_req(u8 *ubuf, int len, enum wps_process_res *res) { struct wps_sm *sm = wps_sm_get(); - static struct wpabuf *wps_buf = NULL; + struct wps_eap_wsc_frag_data *frag; struct eap_expand *expd; - int tlen = 0; - u8 *tbuf; - u8 flag; - int frag_len; - u16 be_tot_len = 0; + const u8 *pos, *end; + u8 flags; + u16 message_length = 0; + bool fragment_pending; + struct wpabuf tmpbuf; if (!sm || !res) { return ESP_FAIL; } - if (len < (int)(sizeof(struct eap_expand) + 1)) { + frag = &sm->wsc_frag; + + if (len < (int)(sizeof(struct eap_expand) + EAP_REQUEST_TYPE_LEN)) { wpa_printf(MSG_ERROR, "WPS: Truncated EAP-Expanded header"); return ESP_FAIL; } expd = (struct eap_expand *) ubuf; - wpa_printf(MSG_DEBUG, "WPS: Processing WSC message (len=%d, total_len=%d)", len, tlen); + pos = ubuf + sizeof(struct eap_expand); + end = ubuf + len; + + if (WPA_GET_BE24(expd->vendor_id) != EAP_VENDOR_WFA || + WPA_GET_BE32((const u8 *)&expd->vendor_type) != EAP_VENDOR_TYPE_WSC) { + wpa_printf(MSG_WARNING, "WPS: Unexpected expanded EAP vendor/type"); + return ESP_ERR_INVALID_ARG; + } if (sm->state == WAIT_START) { if (expd->opcode != WSC_Start) { @@ -473,64 +549,38 @@ static int wps_process_wps_mX_req(u8 *ubuf, int len, enum wps_process_res *res) return ESP_ERR_INVALID_STATE; } - flag = *(u8 *)(ubuf + sizeof(struct eap_expand)); - if (flag & WPS_MSG_FLAG_LEN) { - if (len < (int)(sizeof(struct eap_expand) + 1 + 2)) { - wpa_printf(MSG_ERROR, "WPS: Missing total length field"); + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < EAP_WSC_MESSAGE_LEN_FIELD_LEN) { + wpa_printf(MSG_ERROR, "WPS: Message underflow"); return ESP_FAIL; } - tbuf = ubuf + sizeof(struct eap_expand) + 1 + 2; // includes 2-byte total length - frag_len = len - (sizeof(struct eap_expand) + 1 + 2); - be_tot_len = *(u16 *)(ubuf + sizeof(struct eap_expand) + 1); - tlen = ((be_tot_len & 0xff) << 8) | ((be_tot_len >> 8) & 0xff); - } else { - tbuf = ubuf + sizeof(struct eap_expand) + 1; - frag_len = len - (sizeof(struct eap_expand) + 1); - tlen = frag_len; - } + message_length = WPA_GET_BE16(pos); + pos += EAP_WSC_MESSAGE_LEN_FIELD_LEN; - if (frag_len < 0 || tlen < 0 || ((flag & WPS_MSG_FLAG_LEN) && tlen < frag_len)) { - wpa_printf(MSG_ERROR, "WPS: Invalid fragment sizes"); - if (wps_buf) { - wpabuf_free(wps_buf); - wps_buf = NULL; - } - return ESP_FAIL; - } - - if (tlen > 50000) { - wpa_printf(MSG_ERROR, "WPS: Invalid EAP-WSC message length"); - return ESP_FAIL; - } - if ((flag & WPS_MSG_FLAG_MORE) || wps_buf != NULL) {//frag msg - wpa_printf(MSG_DEBUG, "WPS: Received fragment (id=%d, flag=0x%x, frag_len=%d, total_len=%d)", sm->current_identifier, flag, frag_len, tlen); - if (ESP_OK != wps_enrollee_process_msg_frag(&wps_buf, tlen, tbuf, frag_len, flag)) { - if (wps_buf) { - wpabuf_free(wps_buf); - wps_buf = NULL; - } + if (message_length < (u16)(end - pos) || message_length > WPS_MESSAGE_LENGTH_MAX) { + wpa_printf(MSG_ERROR, "WPS: Invalid Message Length"); return ESP_FAIL; } - if (flag & WPS_MSG_FLAG_MORE) { - *res = WPS_FRAGMENT; - return ESP_OK; - } - } else { //not frag msg - if (wps_buf) {//if something wrong, frag msg buf is not freed, free first - wpa_printf(MSG_ERROR, "WPS: Fragment buffer not freed before receiving new message"); - wpabuf_free(wps_buf); - wps_buf = NULL; - } - wps_buf = wpabuf_alloc_copy(tbuf, tlen); } - if (!wps_buf) { + wpa_printf(MSG_DEBUG, "WPS: Received packet: Op-Code %d Flags 0x%x " + "Message Length %d", expd->opcode, flags, message_length); + + if (wps_eap_wsc_prepare_rx_buf(sm, frag, flags, expd->opcode, + message_length, pos, end - pos, + &tmpbuf, &fragment_pending) != ESP_OK) { return ESP_FAIL; } + if (fragment_pending) { + *res = WPS_FRAGMENT; + return ESP_OK; + } + eloop_cancel_timeout(wifi_station_wps_msg_timeout, NULL, NULL); - *res = wps_enrollee_process_msg(sm->wps, expd->opcode, wps_buf); + *res = wps_enrollee_process_msg(sm->wps, expd->opcode, frag->in_buf); if (*res == WPS_CONTINUE && sm->wps->state == WPS_MSG_DONE) { wpa_printf(MSG_DEBUG, "WPS: M8 processed, starting workaround timer"); @@ -543,10 +593,10 @@ static int wps_process_wps_mX_req(u8 *ubuf, int len, enum wps_process_res *res) sm->state = WPA_FAIL; } - if (wps_buf) { - wpabuf_free(wps_buf); - wps_buf = NULL; + if (frag->in_buf != &tmpbuf) { + wpabuf_free(frag->in_buf); } + frag->in_buf = NULL; return ESP_OK; } @@ -632,10 +682,6 @@ static int wps_send_event_and_disable(int32_t event_id, void* event_data, size_t wps_set_type(WPS_TYPE_DISABLE); wifi_wps_disable_internal(NULL, NULL); - API_MUTEX_TAKE(); - s_wps_enabled = false; - API_MUTEX_GIVE(); - return 0; } @@ -736,6 +782,8 @@ static int wps_finish(void) if (connect) { esp_wifi_connect(); + } else { + esp_wifi_disconnect(); } /* wait for 1 sec before sending WPS success event to give connection time */ @@ -749,10 +797,6 @@ static int wps_finish(void) ret = wps_handle_failure(WPS_FAIL_REASON_NORMAL); } - API_MUTEX_TAKE(); - s_wps_enabled = false; - API_MUTEX_GIVE(); - return ret; } @@ -901,6 +945,11 @@ static int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len) ret = 0; break; case EAP_CODE_REQUEST: + if (plen < sizeof(*ehdr) + EAP_REQUEST_TYPE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Truncated EAP-Request frame"); + ret = 0; + break; + } eap_type = ((u8 *)ehdr)[sizeof(*ehdr)]; switch (eap_type) { case EAP_TYPE_IDENTITY: @@ -915,8 +964,8 @@ static int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len) wpa_printf(MSG_DEBUG, "WPS: Received EAP-WSC message (len=%u)", plen); sm->current_identifier = ehdr->identifier; - tmp = (u8 *)(ehdr + 1) + 1; - ret = wps_process_wps_mX_req(tmp, plen - sizeof(*ehdr) - 1, &res); + tmp = (u8 *)(ehdr + 1) + EAP_REQUEST_TYPE_LEN; + ret = wps_process_wps_mX_req(tmp, plen - sizeof(*ehdr) - EAP_REQUEST_TYPE_LEN, &res); if (res == WPS_FRAGMENT) { wpa_printf(MSG_DEBUG, "WPS fragment received, waiting for more."); ret = ESP_OK; @@ -929,6 +978,8 @@ static int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len) wpa_printf(MSG_DEBUG, "WPS: State updated to %d", sm->wps->state); wps_start_msg_timer(); } + } else if (ret == ESP_ERR_INVALID_ARG) { + ret = ESP_OK; } else if (ret == ESP_ERR_INVALID_STATE) { ret = ESP_OK; } else { @@ -1163,7 +1214,7 @@ static int save_credentials_cb(void *ctx, const struct wps_credential *cred) sm->ap_cred_cnt++; wpa_hexdump_ascii(MSG_DEBUG, "WPS: Received credential - SSID ", cred->ssid, cred->ssid_len); - wpa_hexdump_ascii(MSG_DEBUG, "WPS: Received credential - Key ", cred->key, cred->key_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Received credential - Key ", cred->key, cred->key_len); return ESP_OK; } @@ -1185,7 +1236,7 @@ int wps_init_cfg_pin(struct wps_config *cfg) if (wps_generate_pin(&spin) < 0) { return -1; } - wpa_printf(MSG_INFO, "WPS: Invalid PIN provided, generated new PIN: %08d", spin); + wpa_printf(MSG_DEBUG, "WPS: Invalid PIN provided, generated a new PIN"); os_snprintf((char *)cfg->pin, 9, "%08d", spin); } @@ -1199,7 +1250,7 @@ struct wps_sm_funcs* wps_get_wps_sm_cb(void) static int wifi_station_wps_init(const esp_wps_config_t *config) { - struct wps_funcs *wps_cb; + struct wps_funcs *wps_cb = NULL; struct wps_sm *sm = NULL; struct wps_config cfg = {0}; @@ -1263,7 +1314,7 @@ static int wifi_station_wps_init(const esp_wps_config_t *config) /* Cancel any pending timers from previous WPS attempts */ wps_delete_timer(); - wps_cb = os_malloc(sizeof(struct wps_funcs)); + wps_cb = os_zalloc(sizeof(struct wps_funcs)); if (wps_cb == NULL) { goto _err; } @@ -1271,12 +1322,17 @@ static int wifi_station_wps_init(const esp_wps_config_t *config) wps_cb->wifi_station_wps_start = NULL; wps_cb->wps_sm_rx_eapol = wps_sm_rx_eapol; wps_cb->wps_start_pending = wps_start_pending; - esp_wifi_set_wps_cb_internal(wps_cb); s_wps_sm_cb = os_zalloc(sizeof(struct wps_sm_funcs)); if (!s_wps_sm_cb) { goto _err; } + + if (esp_wifi_set_wps_cb_internal(wps_cb) != ESP_OK) { + goto _err; + } + + wps_cb = NULL; os_memset(&s_wps_success_evt, 0, sizeof(s_wps_success_evt)); return ESP_OK; @@ -1285,6 +1341,14 @@ _err: esp_wifi_unset_appie_internal(WIFI_APPIE_WPS_PR); esp_wifi_unset_appie_internal(WIFI_APPIE_WPS_AR); + if (wps_cb) { + os_free(wps_cb); + } + if (s_wps_sm_cb) { + os_free(s_wps_sm_cb); + s_wps_sm_cb = NULL; + } + if (sm->dev) { wps_dev_deinit(sm->dev); sm->dev = NULL; @@ -1352,6 +1416,10 @@ static int wifi_station_wps_deinit(void) wps_deinit(sm->wps); sm->wps = NULL; } + if (sm->wsc_frag.in_buf) { + wpabuf_free(sm->wsc_frag.in_buf); + sm->wsc_frag.in_buf = NULL; + } if (s_wps_sm_cb) { s_wps_sm_cb->wps_sm_notify_deauth = NULL; os_free(s_wps_sm_cb); @@ -1554,7 +1622,7 @@ static int wps_check_wifi_mode(void) int esp_wifi_wps_enable(const esp_wps_config_t *config) { int ret = ESP_OK; - struct wps_sm *sm = gWpsSm; + enum wps_owner owner; if (esp_wifi_get_user_init_flag_internal() == 0) { wpa_printf(MSG_ERROR, "WPS: Wi-Fi not started, cannot enable"); @@ -1571,8 +1639,9 @@ int esp_wifi_wps_enable(const esp_wps_config_t *config) } API_MUTEX_TAKE(); - if (s_wps_enabled) { - if (sm && os_memcmp(sm->identity, WSC_ID_REGISTRAR, sm->identity_len) == 0) { + owner = wps_get_owner(); + if (owner != WPS_OWNER_NONE) { + if (owner == WPS_OWNER_REGISTRAR) { wpa_printf(MSG_ERROR, "WPS: Cannot enable Enrollee, Registrar is already enabled"); ret = ESP_ERR_WIFI_MODE; } else { @@ -1583,20 +1652,18 @@ int esp_wifi_wps_enable(const esp_wps_config_t *config) } ret = eloop_register_timeout_blocking(wifi_wps_enable_internal, NULL, (void *)config); - s_wps_enabled = true; API_MUTEX_GIVE(); return ret; } -bool is_wps_enabled(void) +enum wps_owner wps_get_owner(void) { - bool enabled; + return (enum wps_owner) atomic_load(&s_wps_owner); +} - API_MUTEX_TAKE(); - enabled = s_wps_enabled; - API_MUTEX_GIVE(); - - return enabled; +void wps_set_owner(enum wps_owner owner) +{ + atomic_store(&s_wps_owner, owner); } static int wifi_wps_disable(void) @@ -1625,8 +1692,13 @@ static int wifi_wps_enable_internal(void *ctx, void *data) wpa_printf(MSG_INFO, "WPS: Enabling"); - wps_set_type(config->wps_type); - wps_set_status(WPS_STATUS_DISABLE); + if (wps_set_type(config->wps_type) != ESP_OK) { + return ESP_FAIL; + } + if (wps_set_status(WPS_STATUS_DISABLE) != ESP_OK) { + wps_set_type(WPS_TYPE_DISABLE); + return ESP_FAIL; + } ret = wifi_station_wps_init(config); @@ -1636,12 +1708,17 @@ static int wifi_wps_enable_internal(void *ctx, void *data) return ESP_FAIL; } wpa_sm->wpa_sm_wps_disable = wifi_wps_disable; + wps_set_owner(WPS_OWNER_ENROLLEE); return ESP_OK; } static int wifi_wps_disable_internal(void *ctx, void *data) { /* Only disconnect in case of WPS pending */ + gWpaSm.wpa_sm_wps_disable = NULL; + wps_set_owner(WPS_OWNER_NONE); + esp_wifi_set_wps_start_flag_internal(false); + if (wps_get_status() == WPS_STATUS_PENDING) { esp_wifi_disconnect(); } @@ -1666,39 +1743,42 @@ static int wifi_wps_disable_internal(void *ctx, void *data) int esp_wifi_wps_disable(void) { int ret = 0; - struct wps_sm *wps_sm = gWpsSm; - struct wpa_sm *wpa_sm = &gWpaSm; - - if (wps_sm && os_memcmp(wps_sm->identity, WSC_ID_REGISTRAR, wps_sm->identity_len) == 0) { - return ESP_ERR_WIFI_MODE; - } + int prev_wps_type; + enum wps_owner prev_owner; API_MUTEX_TAKE(); - if (!s_wps_enabled) { + prev_owner = wps_get_owner(); + if (prev_owner == WPS_OWNER_NONE) { wpa_printf(MSG_DEBUG, "WPS: Already disabled"); API_MUTEX_GIVE(); return ESP_OK; } + if (prev_owner == WPS_OWNER_REGISTRAR) { + API_MUTEX_GIVE(); + return ESP_ERR_WIFI_MODE; + } wpa_printf(MSG_INFO, "WPS: Disabling"); + prev_wps_type = wps_get_type(); wps_set_type(WPS_TYPE_DISABLE); /* Notify WiFi task */ + wps_set_owner(WPS_OWNER_NONE); ret = eloop_register_timeout_blocking(wifi_wps_disable_internal, NULL, NULL); if (ESP_OK != ret) { wpa_printf(MSG_ERROR, "WPS: Failed to disable (ret=%d)", ret); + wps_set_type(prev_wps_type); + wps_set_owner(prev_owner); } - esp_wifi_set_wps_start_flag_internal(false); - s_wps_enabled = false; API_MUTEX_GIVE(); - wpa_sm->wpa_sm_wps_disable = NULL; - return ESP_OK; + return ret; } int esp_wifi_wps_start(void) { int ret; + enum wps_owner owner; if (ESP_OK != wps_check_wifi_mode()) { return ESP_ERR_WIFI_MODE; @@ -1706,11 +1786,17 @@ int esp_wifi_wps_start(void) API_MUTEX_TAKE(); - if (!s_wps_enabled) { + owner = wps_get_owner(); + if (owner == WPS_OWNER_NONE) { wpa_printf(MSG_ERROR, "WPS: Cannot start, not enabled"); API_MUTEX_GIVE(); return ESP_ERR_WIFI_WPS_SM; } + if (owner != WPS_OWNER_ENROLLEE) { + wpa_printf(MSG_ERROR, "WPS: Cannot start Enrollee, Registrar is enabled"); + API_MUTEX_GIVE(); + return ESP_ERR_WIFI_MODE; + } if (esp_wifi_get_user_init_flag_internal() == 0) { API_MUTEX_GIVE(); diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h index 2ff0fd73e1..cf304e729b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,11 +9,7 @@ #include "wps/wps.h" #include "wps/wps_attr_parse.h" -/* WPS message flag */ -enum wps_msg_flag { - WPS_MSG_FLAG_MORE = 0x01, - WPS_MSG_FLAG_LEN = 0x02 -}; +struct wpabuf; enum wps_reg_sig_type { SIG_WPS_REG_ENABLE = 1, //1 @@ -22,6 +18,12 @@ enum wps_reg_sig_type { SIG_WPS_REG_MAX, //4 }; +enum wps_owner { + WPS_OWNER_NONE = 0, + WPS_OWNER_ENROLLEE, + WPS_OWNER_REGISTRAR, +}; + typedef struct { void *arg; int ret; /* return value */ @@ -41,6 +43,11 @@ struct discard_ap_list_t { u8 bssid[6]; }; +struct wps_eap_wsc_frag_data { + struct wpabuf *in_buf; + enum wsc_op_code in_op_code; +}; + struct wps_sm { u8 state; struct wps_config *wps_cfg; @@ -61,6 +68,7 @@ struct wps_sm { u8 discover_ssid_cnt; bool wps_pbc_overlap; bool post_m8_recv; + struct wps_eap_wsc_frag_data wsc_frag; }; #define API_MUTEX_TAKE() do {\ @@ -110,7 +118,8 @@ static inline int wps_set_status(uint32_t status) return esp_wifi_set_wps_status_internal(status); } -bool is_wps_enabled(void); +enum wps_owner wps_get_owner(void); +void wps_set_owner(enum wps_owner owner); int wps_init_cfg_pin(struct wps_config *cfg); void wifi_station_wps_eapol_start_handle(void *data, void *user_ctx); int wifi_ap_wps_disable_internal(void);