From 458f356be0f4e3b34b8ec8ad50c903f95707bdc0 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 25 Apr 2026 16:23:21 +0530 Subject: [PATCH] fix(wps): harden enrollee WSC fragment handling --- .../esp_supplicant/src/esp_wps.c | 188 +++++++++--------- .../esp_supplicant/src/esp_wps_i.h | 14 +- 2 files changed, 102 insertions(+), 100 deletions(-) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c index 400c159e478..6bbedf108d9 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c @@ -555,75 +555,80 @@ _err: return ret; } -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 = gWpsSm; - u8 identifier; - - if (!sm) { - 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 (buf == NULL || frag_data == NULL) { - wpa_printf(MSG_ERROR, "fun:%s. line:%d, frag buf or frag data is null", __FUNCTION__, __LINE__); - return ESP_FAIL; + 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; } - - *buf = wpabuf_alloc(tot_len); - if (*buf == NULL) { - return ESP_ERR_NO_MEM; - } - - wpabuf_put_data(*buf, frag_data, frag_len); - return wps_send_frag_ack(identifier); + 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)); } - 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; - } - - wpabuf_put_data(*buf, frag_data, frag_len); - - if (flag & WPS_MSG_FLAG_MORE) { - return wps_send_frag_ack(identifier); - } - - return ESP_OK; + return 0; } int wps_process_wps_mX_req(u8 *ubuf, int len, enum wps_process_res *res) { struct wps_sm *sm = gWpsSm; - 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; + struct wpabuf tmpbuf; if (!sm || !res) { return ESP_FAIL; } + frag = &sm->wsc_frag; + if (len < (int)(sizeof(struct eap_expand) + 1)) { wpa_printf(MSG_ERROR, "WPS: Truncated EAP-Expanded header"); return ESP_FAIL; } expd = (struct eap_expand *) ubuf; - wpa_printf(MSG_DEBUG, "wps process mX req: len %d, tlen %d", len, tlen); + pos = ubuf + sizeof(struct eap_expand); + end = ubuf + len; if (sm->state == WAIT_START) { if (expd->opcode != WSC_Start) { @@ -639,74 +644,65 @@ 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 < 2) { + 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 += 2; - 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, "EAP-WSC: Invalid Message Length"); - return ESP_FAIL; - } - if ((flag & WPS_MSG_FLAG_MORE) || wps_buf != NULL) {//frag msg - - wpa_printf(MSG_DEBUG, "rx frag msg id:%d, flag:%d, frag_len: %d, tot_len: %d, be_tot_len:%d", sm->current_identifier, flag, frag_len, tlen, be_tot_len); - 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 > 50000) { + 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, "something is wrong, frag buf is not freed"); - 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 (frag->in_buf && + wps_eap_wsc_process_cont(frag, pos, end - pos, expd->opcode) < 0) { + wpabuf_free(frag->in_buf); + frag->in_buf = NULL; return ESP_FAIL; } + if (flags & WSC_FLAGS_MF) { + if (wps_eap_wsc_process_fragment(frag, flags, expd->opcode, + message_length, pos, + end - pos) < 0) { + wpabuf_free(frag->in_buf); + frag->in_buf = NULL; + return ESP_FAIL; + } + if (wps_send_frag_ack(sm->current_identifier) != ESP_OK) { + wpabuf_free(frag->in_buf); + frag->in_buf = NULL; + return ESP_FAIL; + } + *res = WPS_FRAGMENT; + return ESP_OK; + } + + if (frag->in_buf == NULL) { + wpabuf_set(&tmpbuf, pos, end - pos); + frag->in_buf = &tmpbuf; + } + 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_FAILURE) { 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; } @@ -1635,6 +1631,10 @@ 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); 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 f2414a20915..97445a08606 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-2024 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; #ifdef USE_WPS_TASK enum wps_sig_type { @@ -59,6 +55,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; @@ -86,6 +87,7 @@ struct wps_sm { struct discard_ap_list_t dis_ap_list[WPS_MAX_DIS_AP_NUM]; u8 discard_ap_cnt; bool intermediate_disconnect; + struct wps_eap_wsc_frag_data wsc_frag; }; #define API_MUTEX_TAKE() do {\