mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(wps): harden enrollee WSC fragment handling
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -376,53 +376,54 @@ 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;
|
||||
}
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wifi_station_wps_post_m8_timeout(void *data, void *user_ctx)
|
||||
@@ -436,29 +437,31 @@ 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;
|
||||
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: Processing WSC message (len=%d, total_len=%d)", len, tlen);
|
||||
pos = ubuf + sizeof(struct eap_expand);
|
||||
end = ubuf + len;
|
||||
|
||||
if (sm->state == WAIT_START) {
|
||||
if (expd->opcode != WSC_Start) {
|
||||
@@ -473,64 +476,56 @@ 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 < 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, "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 > 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, "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 (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_CONTINUE && sm->wps->state == WPS_MSG_DONE) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: M8 processed, starting workaround timer");
|
||||
@@ -543,10 +538,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;
|
||||
}
|
||||
|
||||
@@ -1343,6 +1338,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);
|
||||
|
||||
@@ -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
|
||||
@@ -41,6 +37,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 +62,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 {\
|
||||
|
||||
Reference in New Issue
Block a user