From 351f09a2218263ccee52c0f73ab7863a7364cf42 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 29 Nov 2008 22:06:34 +0200 Subject: [PATCH] WPS: Added support for fragmented WPS IE in Beacon and Probe Response Fragment WPS IE if needed to fit into the IE length limits in hostapd and Reassemble WPS IE data from multiple IEs in wpa_supplicant. In addition, moved WPS code from events.c into wps_supplicant.c to clean up module interfaces. --- src/drivers/driver.h | 2 + src/drivers/scan_helpers.c | 33 +++++++- src/wps/wps.c | 18 ++-- src/wps/wps.h | 6 +- src/wps/wps_registrar.c | 53 +++++++++--- wpa_supplicant/events.c | 145 ++------------------------------ wpa_supplicant/wps_supplicant.c | 142 +++++++++++++++++++++++++++++++ wpa_supplicant/wps_supplicant.h | 26 +++++- 8 files changed, 259 insertions(+), 166 deletions(-) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 378042eee..53575984f 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1244,6 +1244,8 @@ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); #define WPS_IE_VENDOR_TYPE 0x0050f204 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); int wpa_scan_get_max_rate(const struct wpa_scan_res *res); void wpa_scan_results_free(struct wpa_scan_results *res); void wpa_scan_sort_results(struct wpa_scan_results *res); diff --git a/src/drivers/scan_helpers.c b/src/drivers/scan_helpers.c index bd774c1bd..72e360f24 100644 --- a/src/drivers/scan_helpers.c +++ b/src/drivers/scan_helpers.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Helper functions for scan result processing - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -59,6 +59,37 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, } +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(res->ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (res + 1); + end = pos + res->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; +} + + int wpa_scan_get_max_rate(const struct wpa_scan_res *res) { int rate = 0; diff --git a/src/wps/wps.c b/src/wps/wps.c index 44cac3f49..add84416c 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -124,13 +124,11 @@ struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code) } -int wps_is_selected_pbc_registrar(const u8 *buf, size_t len) +int wps_is_selected_pbc_registrar(const struct wpabuf *msg) { struct wps_parse_attr attr; - struct wpabuf msg; - wpabuf_set(&msg, buf, len); - if (wps_parse_msg(&msg, &attr) < 0 || + if (wps_parse_msg(msg, &attr) < 0 || !attr.selected_registrar || *attr.selected_registrar == 0 || !attr.sel_reg_config_methods || !(WPA_GET_BE16(attr.sel_reg_config_methods) & @@ -143,13 +141,11 @@ int wps_is_selected_pbc_registrar(const u8 *buf, size_t len) } -int wps_is_selected_pin_registrar(const u8 *buf, size_t len) +int wps_is_selected_pin_registrar(const struct wpabuf *msg) { struct wps_parse_attr attr; - struct wpabuf msg; - wpabuf_set(&msg, buf, len); - if (wps_parse_msg(&msg, &attr) < 0 || + if (wps_parse_msg(msg, &attr) < 0 || !attr.selected_registrar || *attr.selected_registrar == 0 || !attr.sel_reg_config_methods || !(WPA_GET_BE16(attr.sel_reg_config_methods) & @@ -162,13 +158,11 @@ int wps_is_selected_pin_registrar(const u8 *buf, size_t len) } -const u8 * wps_get_uuid_e(const u8 *buf, size_t len) +const u8 * wps_get_uuid_e(const struct wpabuf *msg) { struct wps_parse_attr attr; - struct wpabuf msg; - wpabuf_set(&msg, buf, len); - if (wps_parse_msg(&msg, &attr) < 0) + if (wps_parse_msg(msg, &attr) < 0) return NULL; return attr.uuid_e; } diff --git a/src/wps/wps.h b/src/wps/wps.h index c0ea6946d..c1e8eb78f 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -76,9 +76,9 @@ enum wps_process_res wps_process_msg(struct wps_data *wps, u8 op_code, struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code); -int wps_is_selected_pbc_registrar(const u8 *buf, size_t len); -int wps_is_selected_pin_registrar(const u8 *buf, size_t len); -const u8 * wps_get_uuid_e(const u8 *buf, size_t len); +int wps_is_selected_pbc_registrar(const struct wpabuf *msg); +int wps_is_selected_pin_registrar(const struct wpabuf *msg); +const u8 * wps_get_uuid_e(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(u8 req_type); struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, const u8 *uuid, u8 req_type); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 7404eaf47..c3699a9b4 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -542,32 +542,55 @@ static int wps_cb_set_ie(struct wps_registrar *reg, } +/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ +static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + ie = wpabuf_alloc(wpabuf_len(data) + 100); + if (ie == NULL) { + wpabuf_free(data); + return NULL; + } + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + wpabuf_free(data); + + return ie; +} + + static int wps_set_ie(struct wps_registrar *reg) { struct wpabuf *beacon; struct wpabuf *probe; int ret; - u8 *blen, *plen; wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); beacon = wpabuf_alloc(300); if (beacon == NULL) return -1; - probe = wpabuf_alloc(300); + probe = wpabuf_alloc(400); if (probe == NULL) { wpabuf_free(beacon); return -1; } - wpabuf_put_u8(beacon, WLAN_EID_VENDOR_SPECIFIC); - blen = wpabuf_put(beacon, 1); - wpabuf_put_be32(beacon, WPS_DEV_OUI_WFA); - - wpabuf_put_u8(probe, WLAN_EID_VENDOR_SPECIFIC); - plen = wpabuf_put(probe, 1); - wpabuf_put_be32(probe, WPS_DEV_OUI_WFA); - if (wps_build_version(beacon) || wps_build_wps_state(reg->wps, beacon) || wps_build_ap_setup_locked(reg->wps, beacon) || @@ -590,8 +613,14 @@ static int wps_set_ie(struct wps_registrar *reg) return -1; } - *blen = wpabuf_len(beacon) - 2; - *plen = wpabuf_len(probe) - 2; + beacon = wps_ie_encapsulate(beacon); + probe = wps_ie_encapsulate(probe); + + if (!beacon || !probe) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } ret = wps_cb_set_ie(reg, beacon, probe); wpabuf_free(beacon); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 34b06e3d7..a1bd2ba17 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -31,7 +31,7 @@ #include "ieee802_11_defs.h" #include "blacklist.h" #include "wpas_glue.h" -#include "wps/wps.h" +#include "wps_supplicant.h" static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) @@ -276,53 +276,11 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, struct wpa_ie_data ie; int proto_match = 0; const u8 *rsn_ie, *wpa_ie; + int ret; -#ifdef CONFIG_WPS - if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { - const u8 *wps_ie; - wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (eap_is_wps_pbc_enrollee(&ssid->eap)) { - if (!wps_ie) { - wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); - return 0; - } - - if (!wps_is_selected_pbc_registrar(wps_ie + 6, - wps_ie[1] - 4)) { - wpa_printf(MSG_DEBUG, " skip - WPS AP " - "without active PBC Registrar"); - return 0; - } - - /* TODO: overlap detection */ - wpa_printf(MSG_DEBUG, " selected based on WPS IE " - "(Active PBC)"); - return 1; - } - - if (eap_is_wps_pin_enrollee(&ssid->eap)) { - if (!wps_ie) { - wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); - return 0; - } - - if (!wps_is_selected_pin_registrar(wps_ie + 6, - wps_ie[1] - 4)) { - wpa_printf(MSG_DEBUG, " skip - WPS AP " - "without active PIN Registrar"); - return 0; - } - wpa_printf(MSG_DEBUG, " selected based on WPS IE " - "(Active PIN)"); - return 1; - } - - if (wps_ie) { - wpa_printf(MSG_DEBUG, " selected based on WPS IE"); - return 1; - } - } -#endif /* CONFIG_WPS */ + ret = wpas_wps_ssid_bss_match(ssid, bss); + if (ret >= 0) + return ret; rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { @@ -412,34 +370,6 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, } -#ifdef CONFIG_WPS -static int wps_ssid_wildcard_ok(struct wpa_ssid *ssid, - struct wpa_scan_res *bss) -{ - const u8 *wps_ie; - - if (eap_is_wps_pbc_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (wps_ie && - wps_is_selected_pbc_registrar(wps_ie + 6, wps_ie[1] - 4)) { - /* allow wildcard SSID for WPS PBC */ - return 1; - } - } - - if (eap_is_wps_pin_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (wps_ie && - wps_is_selected_pin_registrar(wps_ie + 6, wps_ie[1] - 4)) { - /* allow wildcard SSID for WPS PIN */ - return 1; - } - } - - return 0; -} -#endif /* CONFIG_WPS */ - static struct wpa_scan_res * wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, @@ -494,7 +424,7 @@ wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WPS if (ssid->ssid_len == 0 && - wps_ssid_wildcard_ok(ssid, bss)) + wpas_wps_ssid_wildcard_ok(ssid, bss)) check_ssid = 0; #endif /* CONFIG_WPS */ @@ -584,7 +514,7 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, * with our mode. */ check_ssid = 1; if (ssid->ssid_len == 0 && - wps_ssid_wildcard_ok(ssid, bss)) + wpas_wps_ssid_wildcard_ok(ssid, bss)) check_ssid = 0; } #endif /* CONFIG_WPS */ @@ -670,65 +600,6 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, } -#ifdef CONFIG_WPS - -static int wpa_scan_pbc_overlap(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *selected, - struct wpa_ssid *ssid) -{ - const u8 *sel_uuid, *uuid; - size_t i; - const u8 *wps_ie; - - if (!eap_is_wps_pbc_enrollee(&ssid->eap)) - return 0; - - /* Make sure that only one AP is in active PBC mode */ - wps_ie = wpa_scan_get_vendor_ie(selected, WPS_IE_VENDOR_TYPE); - if (wps_ie) - sel_uuid = wps_get_uuid_e(wps_ie + 6, wps_ie[1] - 4); - else - sel_uuid = NULL; - if (!sel_uuid) { - wpa_printf(MSG_DEBUG, "WPS: UUID-E not " - "available for PBC overlap " - "detection"); - return 1; - } - - for (i = 0; i < wpa_s->scan_res->num; i++) { - struct wpa_scan_res *bss = wpa_s->scan_res->res[i]; - if (bss == selected) - continue; - wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (!wps_ie) - continue; - if (!wps_is_selected_pbc_registrar(wps_ie + 6, - wps_ie[1] - 4)) - continue; - uuid = wps_get_uuid_e(wps_ie + 6, wps_ie[1] - 4); - if (uuid == NULL) { - wpa_printf(MSG_DEBUG, "WPS: UUID-E not " - "available for PBC overlap " - "detection (other BSS)"); - return 1; - } - if (os_memcmp(sel_uuid, uuid, 16) != 0) - return 1; /* PBC overlap */ - - /* TODO: verify that this is reasonable dual-band situation */ - } - - return 0; -} - -#else /* CONFIG_WPS */ - -#define wpa_scan_pbc_overlap(w, s, i) 0 - -#endif /* CONFIG_WPS */ - - static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) { int prio, timeout; @@ -778,7 +649,7 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) } if (selected) { - if (wpa_scan_pbc_overlap(wpa_s, selected, ssid)) { + if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); timeout = 10; diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 49869f9a4..2b1e6b8e7 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -391,3 +391,145 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) os_free(wpa_s->wps); wpa_s->wps = NULL; } + + +int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid, struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + return -1; + + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + if (!wps_is_selected_pbc_registrar(wps_ie)) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PBC Registrar"); + wpabuf_free(wps_ie); + return 0; + } + + /* TODO: overlap detection */ + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Active PBC)"); + wpabuf_free(wps_ie); + return 1; + } + + if (eap_is_wps_pin_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + if (!wps_is_selected_pin_registrar(wps_ie)) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PIN Registrar"); + wpabuf_free(wps_ie); + return 0; + } + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Active PIN)"); + wpabuf_free(wps_ie); + return 1; + } + + if (wps_ie) { + wpa_printf(MSG_DEBUG, " selected based on WPS IE"); + wpabuf_free(wps_ie); + return 1; + } + + return -1; +} + + +int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie = NULL; + int ret = 0; + + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { + /* allow wildcard SSID for WPS PBC */ + ret = 1; + } + } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && wps_is_selected_pin_registrar(wps_ie)) { + /* allow wildcard SSID for WPS PIN */ + ret = 1; + } + } + + wpabuf_free(wps_ie); + + return ret; +} + + +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid) +{ + const u8 *sel_uuid, *uuid; + size_t i; + struct wpabuf *wps_ie; + int ret = 0; + + if (!eap_is_wps_pbc_enrollee(&ssid->eap)) + return 0; + + /* Make sure that only one AP is in active PBC mode */ + wps_ie = wpa_scan_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE); + if (wps_ie) + sel_uuid = wps_get_uuid_e(wps_ie); + else + sel_uuid = NULL; + if (!sel_uuid) { + wpa_printf(MSG_DEBUG, "WPS: UUID-E not available for PBC " + "overlap detection"); + wpabuf_free(wps_ie); + return 1; + } + + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *bss = wpa_s->scan_res->res[i]; + struct wpabuf *ie; + if (bss == selected) + continue; + ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!ie) + continue; + if (!wps_is_selected_pbc_registrar(ie)) { + wpabuf_free(ie); + continue; + } + uuid = wps_get_uuid_e(ie); + if (uuid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: UUID-E not available for " + "PBC overlap detection (other BSS)"); + ret = 1; + wpabuf_free(ie); + break; + } + if (os_memcmp(sel_uuid, uuid, 16) != 0) { + ret = 1; /* PBC overlap */ + wpabuf_free(ie); + break; + } + + /* TODO: verify that this is reasonable dual-band situation */ + } + + wpabuf_free(wps_ie); + + return ret; +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 1e393e36c..bdde6abb7 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -26,6 +26,11 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin); +int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid); #else /* CONFIG_WPS */ @@ -43,7 +48,26 @@ static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) return 0; } -u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) +static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) +{ + return 0; +} + +static inline int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return -1; +} + +static inline int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return 0; +} + +static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid) { return 0; }