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.
master
Jouni Malinen 14 years ago committed by Jouni Malinen
parent 00ae50bc87
commit 54f489be45

@ -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);

@ -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;

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…
Cancel
Save