WPS 2.0: Validate WPS attributes in management frames and WSC messages
If CONFIG_WPS_STRICT is set, validate WPS IE(s) in management frames and reject the frames if any of the mandatory attributes is missing or if an included attribute uses an invalid value. In addition, verify that all mandatory attributes are included and have valid values in the WSC messages.
This commit is contained in:
parent
00ae50bc87
commit
54f489be45
13 changed files with 2158 additions and 0 deletions
|
@ -327,6 +327,11 @@ OBJS += ../src/wps/wps_nfc_pn531.o
|
|||
LIBS += ${PN531_PATH}/lib/wpsnfc.dll
|
||||
LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WPS_STRICT
|
||||
CFLAGS += -DCONFIG_WPS_STRICT
|
||||
OBJS += ../src/wps/wps_validate.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NEED_WPS_OOB
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "wps/wps.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
|
@ -139,6 +140,19 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
|
|||
return -1;
|
||||
}
|
||||
} else if (hapd->conf->wps_state) {
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
struct wpabuf *wps;
|
||||
wps = ieee802_11_vendor_ie_concat(ie, ielen,
|
||||
WPS_IE_VENDOR_TYPE);
|
||||
if (wps && wps_validate_assoc_req(wps) < 0) {
|
||||
hapd->drv.sta_disassoc(hapd, sta->addr,
|
||||
WLAN_REASON_INVALID_IE);
|
||||
ap_free_sta(hapd, sta);
|
||||
wpabuf_free(wps);
|
||||
return -1;
|
||||
}
|
||||
wpabuf_free(wps);
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
|
||||
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
|
||||
sta->flags |= WLAN_STA_WPS;
|
||||
|
|
|
@ -664,6 +664,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
|
|||
WPS_IE_VENDOR_TYPE);
|
||||
wpa_ie = NULL;
|
||||
wpa_ie_len = 0;
|
||||
if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
|
||||
"(Re)Association Request - reject");
|
||||
return WLAN_STATUS_INVALID_IE;
|
||||
}
|
||||
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
|
||||
"(Re)Association Request - possible WPS use");
|
||||
|
|
|
@ -820,6 +820,10 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
|
|||
wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
|
||||
if (wps_ie == NULL)
|
||||
return 0;
|
||||
if (wps_validate_probe_req(wps_ie) < 0) {
|
||||
wpabuf_free(wps_ie);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wpabuf_len(wps_ie) > 0) {
|
||||
wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
|
||||
|
|
135
src/wps/wps.h
135
src/wps/wps.h
|
@ -751,4 +751,139 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
|
|||
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
|
||||
u16 wps_config_methods_str2bin(const char *str);
|
||||
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
int wps_validate_beacon(const struct wpabuf *wps_ie);
|
||||
int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe);
|
||||
int wps_validate_probe_req(const struct wpabuf *wps_ie);
|
||||
int wps_validate_assoc_req(const struct wpabuf *wps_ie);
|
||||
int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
|
||||
int wps_validate_m1(const struct wpabuf *tlvs);
|
||||
int wps_validate_m2(const struct wpabuf *tlvs);
|
||||
int wps_validate_m2d(const struct wpabuf *tlvs);
|
||||
int wps_validate_m3(const struct wpabuf *tlvs);
|
||||
int wps_validate_m4(const struct wpabuf *tlvs);
|
||||
int wps_validate_m4_encr(const struct wpabuf *tlvs);
|
||||
int wps_validate_m5(const struct wpabuf *tlvs);
|
||||
int wps_validate_m5_encr(const struct wpabuf *tlvs);
|
||||
int wps_validate_m6(const struct wpabuf *tlvs);
|
||||
int wps_validate_m6_encr(const struct wpabuf *tlvs);
|
||||
int wps_validate_m7(const struct wpabuf *tlvs);
|
||||
int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap);
|
||||
int wps_validate_m8(const struct wpabuf *tlvs);
|
||||
int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap);
|
||||
int wps_validate_wsc_ack(const struct wpabuf *tlvs);
|
||||
int wps_validate_wsc_nack(const struct wpabuf *tlvs);
|
||||
int wps_validate_wsc_done(const struct wpabuf *tlvs);
|
||||
#else /* CONFIG_WPS_STRICT */
|
||||
static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
|
||||
int probe)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_probe_req(const struct wpabuf *wps_ie)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m1(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m2(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m2d(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m3(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m4(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m4_encr(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m5(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m5_encr(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m6(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m6_encr(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m7(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m8(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
|
||||
#endif /* WPS_H */
|
||||
|
|
|
@ -975,6 +975,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps,
|
|||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
if (wps_validate_m4_encr(decrypted) < 0) {
|
||||
wpabuf_free(decrypted);
|
||||
wps->state = SEND_WSC_NACK;
|
||||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
|
||||
"attribute");
|
||||
if (wps_parse_msg(decrypted, &eattr) < 0 ||
|
||||
|
@ -1022,6 +1028,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps,
|
|||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
if (wps_validate_m6_encr(decrypted) < 0) {
|
||||
wpabuf_free(decrypted);
|
||||
wps->state = SEND_WSC_NACK;
|
||||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
|
||||
"attribute");
|
||||
if (wps_parse_msg(decrypted, &eattr) < 0 ||
|
||||
|
@ -1069,6 +1081,12 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps,
|
|||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
if (wps_validate_m8_encr(decrypted, wps->wps->ap) < 0) {
|
||||
wpabuf_free(decrypted);
|
||||
wps->state = SEND_WSC_NACK;
|
||||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
|
||||
"attribute");
|
||||
if (wps_parse_msg(decrypted, &eattr) < 0 ||
|
||||
|
@ -1112,22 +1130,32 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
|
|||
|
||||
switch (*attr.msg_type) {
|
||||
case WPS_M2:
|
||||
if (wps_validate_m2(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m2(wps, msg, &attr);
|
||||
break;
|
||||
case WPS_M2D:
|
||||
if (wps_validate_m2d(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m2d(wps, &attr);
|
||||
break;
|
||||
case WPS_M4:
|
||||
if (wps_validate_m4(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m4(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M4);
|
||||
break;
|
||||
case WPS_M6:
|
||||
if (wps_validate_m6(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m6(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M6);
|
||||
break;
|
||||
case WPS_M8:
|
||||
if (wps_validate_m8(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m8(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M8);
|
||||
|
@ -1300,8 +1328,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
|
|||
case WSC_UPnP:
|
||||
return wps_process_wsc_msg(wps, msg);
|
||||
case WSC_ACK:
|
||||
if (wps_validate_wsc_ack(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
return wps_process_wsc_ack(wps, msg);
|
||||
case WSC_NACK:
|
||||
if (wps_validate_wsc_nack(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
return wps_process_wsc_nack(wps, msg);
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
|
||||
|
|
|
@ -2288,6 +2288,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
|
|||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
if (wps_validate_m5_encr(decrypted) < 0) {
|
||||
wpabuf_free(decrypted);
|
||||
wps->state = SEND_WSC_NACK;
|
||||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
|
||||
"attribute");
|
||||
if (wps_parse_msg(decrypted, &eattr) < 0 ||
|
||||
|
@ -2411,6 +2417,12 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps,
|
|||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er) < 0) {
|
||||
wpabuf_free(decrypted);
|
||||
wps->state = SEND_WSC_NACK;
|
||||
return WPS_CONTINUE;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
|
||||
"attribute");
|
||||
if (wps_parse_msg(decrypted, &eattr) < 0 ||
|
||||
|
@ -2455,6 +2467,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
|
|||
|
||||
switch (*attr.msg_type) {
|
||||
case WPS_M1:
|
||||
if (wps_validate_m1(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
#ifdef CONFIG_WPS_UPNP
|
||||
if (wps->wps->wps_upnp && attr.mac_addr) {
|
||||
/* Remove old pending messages when starting new run */
|
||||
|
@ -2469,16 +2483,22 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
|
|||
ret = wps_process_m1(wps, &attr);
|
||||
break;
|
||||
case WPS_M3:
|
||||
if (wps_validate_m3(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m3(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M3);
|
||||
break;
|
||||
case WPS_M5:
|
||||
if (wps_validate_m5(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m5(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M5);
|
||||
break;
|
||||
case WPS_M7:
|
||||
if (wps_validate_m7(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_m7(wps, msg, &attr);
|
||||
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
|
||||
wps_fail_event(wps->wps, WPS_M7);
|
||||
|
@ -2803,10 +2823,16 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
|
|||
case WSC_MSG:
|
||||
return wps_process_wsc_msg(wps, msg);
|
||||
case WSC_ACK:
|
||||
if (wps_validate_wsc_ack(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
return wps_process_wsc_ack(wps, msg);
|
||||
case WSC_NACK:
|
||||
if (wps_validate_wsc_nack(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
return wps_process_wsc_nack(wps, msg);
|
||||
case WSC_Done:
|
||||
if (wps_validate_wsc_done(msg) < 0)
|
||||
return WPS_FAILURE;
|
||||
ret = wps_process_wsc_done(wps, msg);
|
||||
if (ret == WPS_FAILURE) {
|
||||
wps->state = SEND_WSC_NACK;
|
||||
|
|
1852
src/wps/wps_validate.c
Normal file
1852
src/wps/wps_validate.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -496,6 +496,11 @@ OBJS += ../src/wps/wps_nfc_pn531.o
|
|||
LIBS += ${PN531_PATH}/lib/wpsnfc.dll
|
||||
LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WPS_STRICT
|
||||
CFLAGS += -DCONFIG_WPS_STRICT
|
||||
OBJS += ../src/wps/wps_validate.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NEED_WPS_OOB
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "ap/hostapd.h"
|
||||
#include "notify.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "blacklist.h"
|
||||
#include "wpas_glue.h"
|
||||
#include "wps_supplicant.h"
|
||||
|
@ -921,6 +922,27 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
|
|||
p = data->assoc_info.resp_ies;
|
||||
l = data->assoc_info.resp_ies_len;
|
||||
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
if (wpa_s->current_ssid &&
|
||||
wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
|
||||
struct wpabuf *wps;
|
||||
wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
|
||||
if (wps == NULL) {
|
||||
wpa_printf(MSG_INFO, "WPS-STRICT: AP did not include "
|
||||
"WPS IE in (Re)Association Response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wps_validate_assoc_resp(wps) < 0) {
|
||||
wpabuf_free(wps);
|
||||
wpa_supplicant_deauthenticate(
|
||||
wpa_s, WLAN_REASON_INVALID_IE);
|
||||
return -1;
|
||||
}
|
||||
wpabuf_free(wps);
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
|
||||
/* Go through the IEs and make a copy of the MDIE, if present. */
|
||||
while (p && l >= 2) {
|
||||
len = p[1] + 2;
|
||||
|
|
|
@ -580,6 +580,40 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
|
|||
}
|
||||
|
||||
|
||||
struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
|
||||
const struct wpa_scan_res *res, u32 vendor_type)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
const u8 *end, *pos;
|
||||
|
||||
if (res->beacon_ie_len == 0)
|
||||
return NULL;
|
||||
buf = wpabuf_alloc(res->beacon_ie_len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = (const u8 *) (res + 1);
|
||||
pos += res->ie_len;
|
||||
end = pos + res->beacon_ie_len;
|
||||
|
||||
while (pos + 1 < end) {
|
||||
if (pos + 2 + pos[1] > end)
|
||||
break;
|
||||
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
|
||||
vendor_type == WPA_GET_BE32(&pos[2]))
|
||||
wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
|
||||
pos += 2 + pos[1];
|
||||
}
|
||||
|
||||
if (wpabuf_len(buf) == 0) {
|
||||
wpabuf_free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/* Compare function for sorting scan results. Return >0 if @b is considered
|
||||
* better. */
|
||||
static int wpa_scan_result_compar(const void *a, const void *b)
|
||||
|
|
|
@ -32,6 +32,8 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
|
|||
u32 vendor_type);
|
||||
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
|
||||
u32 vendor_type);
|
||||
struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
|
||||
const struct wpa_scan_res *res, u32 vendor_type);
|
||||
void wpa_scan_results_free(struct wpa_scan_results *res);
|
||||
|
||||
#endif /* SCAN_H */
|
||||
|
|
|
@ -1029,6 +1029,28 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
|
|||
ret = 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
if (wps_ie) {
|
||||
if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
|
||||
0) < 0)
|
||||
ret = 0;
|
||||
if (bss->beacon_ie_len) {
|
||||
struct wpabuf *bcn_wps;
|
||||
bcn_wps = wpa_scan_get_vendor_ie_multi_beacon(
|
||||
bss, WPS_IE_VENDOR_TYPE);
|
||||
if (bcn_wps == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
|
||||
"missing from AP Beacon");
|
||||
ret = 0;
|
||||
} else {
|
||||
if (wps_validate_beacon(wps_ie) < 0)
|
||||
ret = 0;
|
||||
wpabuf_free(bcn_wps);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
|
||||
wpabuf_free(wps_ie);
|
||||
|
||||
return ret;
|
||||
|
|
Loading…
Reference in a new issue