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:
Jouni Malinen 2010-05-26 17:16:14 +03:00 committed by Jouni Malinen
parent 00ae50bc87
commit 54f489be45
13 changed files with 2158 additions and 0 deletions

View file

@ -327,6 +327,11 @@ OBJS += ../src/wps/wps_nfc_pn531.o
LIBS += ${PN531_PATH}/lib/wpsnfc.dll LIBS += ${PN531_PATH}/lib/wpsnfc.dll
LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
endif endif
ifdef CONFIG_WPS_STRICT
CFLAGS += -DCONFIG_WPS_STRICT
OBJS += ../src/wps/wps_validate.o
endif
endif endif
ifdef NEED_WPS_OOB ifdef NEED_WPS_OOB

View file

@ -20,6 +20,7 @@
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h" #include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h" #include "common/wpa_ctrl.h"
#include "wps/wps.h"
#include "hostapd.h" #include "hostapd.h"
#include "ieee802_11.h" #include "ieee802_11.h"
#include "sta_info.h" #include "sta_info.h"
@ -139,6 +140,19 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
return -1; return -1;
} }
} else if (hapd->conf->wps_state) { } 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 && if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
sta->flags |= WLAN_STA_WPS; sta->flags |= WLAN_STA_WPS;

View file

@ -664,6 +664,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
WPS_IE_VENDOR_TYPE); WPS_IE_VENDOR_TYPE);
wpa_ie = NULL; wpa_ie = NULL;
wpa_ie_len = 0; 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) { } else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
"(Re)Association Request - possible WPS use"); "(Re)Association Request - possible WPS use");

View file

@ -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); wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
if (wps_ie == NULL) if (wps_ie == NULL)
return 0; return 0;
if (wps_validate_probe_req(wps_ie) < 0) {
wpabuf_free(wps_ie);
return 0;
}
if (wpabuf_len(wps_ie) > 0) { if (wpabuf_len(wps_ie) > 0) {
wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);

View file

@ -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); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
u16 wps_config_methods_str2bin(const char *str); 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 */ #endif /* WPS_H */

View file

@ -975,6 +975,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps,
return WPS_CONTINUE; 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 " wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute"); "attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 || 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; 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 " wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute"); "attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 || 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; 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 " wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute"); "attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 || 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) { switch (*attr.msg_type) {
case WPS_M2: case WPS_M2:
if (wps_validate_m2(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m2(wps, msg, &attr); ret = wps_process_m2(wps, msg, &attr);
break; break;
case WPS_M2D: case WPS_M2D:
if (wps_validate_m2d(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m2d(wps, &attr); ret = wps_process_m2d(wps, &attr);
break; break;
case WPS_M4: case WPS_M4:
if (wps_validate_m4(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m4(wps, msg, &attr); ret = wps_process_m4(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M4); wps_fail_event(wps->wps, WPS_M4);
break; break;
case WPS_M6: case WPS_M6:
if (wps_validate_m6(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m6(wps, msg, &attr); ret = wps_process_m6(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M6); wps_fail_event(wps->wps, WPS_M6);
break; break;
case WPS_M8: case WPS_M8:
if (wps_validate_m8(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m8(wps, msg, &attr); ret = wps_process_m8(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M8); 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: case WSC_UPnP:
return wps_process_wsc_msg(wps, msg); return wps_process_wsc_msg(wps, msg);
case WSC_ACK: case WSC_ACK:
if (wps_validate_wsc_ack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg); return wps_process_wsc_ack(wps, msg);
case WSC_NACK: case WSC_NACK:
if (wps_validate_wsc_nack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg); return wps_process_wsc_nack(wps, msg);
default: default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);

View file

@ -2288,6 +2288,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
return WPS_CONTINUE; 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 " wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute"); "attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 || 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; 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 " wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute"); "attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 || 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) { switch (*attr.msg_type) {
case WPS_M1: case WPS_M1:
if (wps_validate_m1(msg) < 0)
return WPS_FAILURE;
#ifdef CONFIG_WPS_UPNP #ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && attr.mac_addr) { if (wps->wps->wps_upnp && attr.mac_addr) {
/* Remove old pending messages when starting new run */ /* 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); ret = wps_process_m1(wps, &attr);
break; break;
case WPS_M3: case WPS_M3:
if (wps_validate_m3(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m3(wps, msg, &attr); ret = wps_process_m3(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M3); wps_fail_event(wps->wps, WPS_M3);
break; break;
case WPS_M5: case WPS_M5:
if (wps_validate_m5(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m5(wps, msg, &attr); ret = wps_process_m5(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M5); wps_fail_event(wps->wps, WPS_M5);
break; break;
case WPS_M7: case WPS_M7:
if (wps_validate_m7(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m7(wps, msg, &attr); ret = wps_process_m7(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M7); 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: case WSC_MSG:
return wps_process_wsc_msg(wps, msg); return wps_process_wsc_msg(wps, msg);
case WSC_ACK: case WSC_ACK:
if (wps_validate_wsc_ack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg); return wps_process_wsc_ack(wps, msg);
case WSC_NACK: case WSC_NACK:
if (wps_validate_wsc_nack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg); return wps_process_wsc_nack(wps, msg);
case WSC_Done: case WSC_Done:
if (wps_validate_wsc_done(msg) < 0)
return WPS_FAILURE;
ret = wps_process_wsc_done(wps, msg); ret = wps_process_wsc_done(wps, msg);
if (ret == WPS_FAILURE) { if (ret == WPS_FAILURE) {
wps->state = SEND_WSC_NACK; wps->state = SEND_WSC_NACK;

1852
src/wps/wps_validate.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -496,6 +496,11 @@ OBJS += ../src/wps/wps_nfc_pn531.o
LIBS += ${PN531_PATH}/lib/wpsnfc.dll LIBS += ${PN531_PATH}/lib/wpsnfc.dll
LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
endif endif
ifdef CONFIG_WPS_STRICT
CFLAGS += -DCONFIG_WPS_STRICT
OBJS += ../src/wps/wps_validate.o
endif
endif endif
ifdef NEED_WPS_OOB ifdef NEED_WPS_OOB

View file

@ -30,6 +30,7 @@
#include "ap/hostapd.h" #include "ap/hostapd.h"
#include "notify.h" #include "notify.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "blacklist.h" #include "blacklist.h"
#include "wpas_glue.h" #include "wpas_glue.h"
#include "wps_supplicant.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; p = data->assoc_info.resp_ies;
l = data->assoc_info.resp_ies_len; 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. */ /* Go through the IEs and make a copy of the MDIE, if present. */
while (p && l >= 2) { while (p && l >= 2) {
len = p[1] + 2; len = p[1] + 2;

View file

@ -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 /* Compare function for sorting scan results. Return >0 if @b is considered
* better. */ * better. */
static int wpa_scan_result_compar(const void *a, const void *b) static int wpa_scan_result_compar(const void *a, const void *b)

View file

@ -32,6 +32,8 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
u32 vendor_type); u32 vendor_type);
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type); 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); void wpa_scan_results_free(struct wpa_scan_results *res);
#endif /* SCAN_H */ #endif /* SCAN_H */

View file

@ -1029,6 +1029,28 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
ret = 1; 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); wpabuf_free(wps_ie);
return ret; return ret;