From 24b7f2822ef30037210e439b695079feeef9cad0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 28 Aug 2015 20:33:52 +0300 Subject: [PATCH] tests: More WPS/HTTP test coverage Signed-off-by: Jouni Malinen --- tests/hwsim/test_ap_wps.py | 1038 ++++++++++++++++++++++++++++++++++++- tests/hwsim/test_nfc_wps.py | 17 + tests/hwsim/test_p2p_discovery.py | 4 + tests/hwsim/test_p2p_grpform.py | 2 + tests/hwsim/vm/parallel-vm.py | 1 + 5 files changed, 1037 insertions(+), 25 deletions(-) diff --git a/tests/hwsim/test_ap_wps.py b/tests/hwsim/test_ap_wps.py index 9bfc3ad..a817ddb 100644 --- a/tests/hwsim/test_ap_wps.py +++ b/tests/hwsim/test_ap_wps.py @@ -25,6 +25,12 @@ import hostapd from wpasupplicant import WpaSupplicant from utils import HwsimSkip, alloc_fail, fail_test, skip_with_fips +def wps_start_ap(apdev, ssid="test-wps-conf"): + params = { "ssid": ssid, "eap_server": "1", "wps_state": "2", + "wpa_passphrase": "12345678", "wpa": "2", + "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" } + return hostapd.add_ap(apdev['ifname'], params) + def test_ap_wps_init(dev, apdev): """Initial AP configuration with first WPS Enrollee""" ssid = "test-wps" @@ -556,6 +562,15 @@ def test_ap_wps_random_ap_pin(dev, apdev): raise Exception("AP PIN unexpectedly still enabled") check_wps_reg_failure(dev[1], apdev[0], appin) + with fail_test(hapd, 1, "os_get_random;wps_generate_pin"): + if "FAIL" in hapd.request("WPS_AP_PIN random 1"): + raise Exception("Failed to generate PIN during OOM") + hapd.request("WPS_AP_PIN disable") + + with alloc_fail(hapd, 1, "upnp_wps_set_ap_pin"): + hapd.request("WPS_AP_PIN set 12345670") + hapd.request("WPS_AP_PIN disable") + def test_ap_wps_reg_config(dev, apdev): """WPS registrar configuring an AP using AP PIN""" ssid = "test-wps-init-ap-pin" @@ -683,6 +698,13 @@ def test_ap_wps_setup_locked(dev, apdev): time.sleep(0.1) if not ap_setup_locked: raise Exception("AP setup was not locked") + dev[0].request("WPS_CANCEL") + dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True, + only_new=True) + bss = dev[0].get_bss(apdev[0]['bssid']) + if 'wps_ap_setup_locked' not in bss or bss['wps_ap_setup_locked'] != '1': + logger.info("BSS: " + str(bss)) + raise Exception("AP Setup Locked not indicated in scan results") hapd = hostapd.Hostapd(apdev[0]['ifname']) status = hapd.request("WPS_GET_STATUS") @@ -871,6 +893,7 @@ def _test_ap_wps_er_add_enrollee(dev, apdev): "model_name": "WAP", "model_number": "123", "serial_number": "12345", "device_type": "6-0050F204-1", "os_version": "01020300", + 'friendly_name': "WPS AP - <>&'\" - TEST", "config_methods": "label push_button", "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}) logger.info("WPS configuration step") @@ -896,6 +919,8 @@ def _test_ap_wps_er_add_enrollee(dev, apdev): raise Exception("AP discovery timed out") if ap_uuid not in ev: raise Exception("Expected AP UUID not found") + if "|WPS AP - <>&'" - TEST|Company|" not in ev: + raise Exception("Expected friendly name not found") logger.info("Learn AP configuration through UPnP") dev[0].dump_monitor() @@ -1819,21 +1844,21 @@ def test_ap_wps_pbc_timeout(dev, apdev, params): def add_ssdp_ap(ifname, ap_uuid): ssid = "wps-ssdp" ap_pin = "12345670" - hostapd.add_ap(ifname, - { "ssid": ssid, "eap_server": "1", "wps_state": "2", - "wpa_passphrase": "12345678", "wpa": "2", - "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP", - "device_name": "Wireless AP", "manufacturer": "Company", - "model_name": "WAP", "model_number": "123", - "serial_number": "12345", "device_type": "6-0050F204-1", - "os_version": "01020300", - "config_methods": "label push_button", - "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo", - "friendly_name": "WPS Access Point", - "manufacturer_url": "http://www.example.com/", - "model_description": "Wireless Access Point", - "model_url": "http://www.example.com/model/", - "upc": "123456789012" }) + params = { "ssid": ssid, "eap_server": "1", "wps_state": "2", + "wpa_passphrase": "12345678", "wpa": "2", + "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP", + "device_name": "Wireless AP", "manufacturer": "Company", + "model_name": "WAP", "model_number": "123", + "serial_number": "12345", "device_type": "6-0050F204-1", + "os_version": "01020300", + "config_methods": "label push_button", + "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo", + "friendly_name": "WPS Access Point", + "manufacturer_url": "http://www.example.com/", + "model_description": "Wireless Access Point", + "model_url": "http://www.example.com/model/", + "upc": "123456789012" } + return hostapd.add_ap(ifname, params) def ssdp_send(msg, no_recv=False): socket.setdefaulttimeout(1) @@ -2241,7 +2266,7 @@ def test_ap_wps_upnp(dev, apdev): def test_ap_wps_upnp_subscribe(dev, apdev): """WPS AP and UPnP event subscription""" ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e" - add_ssdp_ap(apdev[0]['ifname'], ap_uuid) + hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid) location = ssdp_get_location(ap_uuid) urls = upnp_get_urls(location) @@ -2413,6 +2438,179 @@ def test_ap_wps_upnp_subscribe(dev, apdev): sid = resp.getheader("sid") logger.debug("Subscription SID " + sid) + # Force subscription to be deleted due to errors + dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412) + dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412) + with alloc_fail(hapd, 1, "event_build_message"): + for i in range(10): + dev[1].dump_monitor() + dev[2].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[2].request("WPS_CANCEL") + if i % 4 == 1: + time.sleep(1) + else: + time.sleep(0.1) + time.sleep(0.2) + + headers = { "sid": sid } + conn.request("UNSUBSCRIBE", eventurl.path, "", headers) + resp = conn.getresponse() + if resp.status != 200 and resp.status != 412: + raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status) + + headers = { "callback": '', + "NT": "upnp:event", + "timeout": "Second-1234" } + with alloc_fail(hapd, 1, "http_client_addr;event_send_start"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 200: + raise Exception("Unexpected HTTP response for SUBSCRIBE: %d" % resp.status) + sid = resp.getheader("sid") + logger.debug("Subscription SID " + sid) + + headers = { "sid": sid } + conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 200: + raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status) + + headers = { "callback": '', + "NT": "upnp:event", + "timeout": "Second-1234" } + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 200: + raise Exception("Unexpected HTTP response: %d" % resp.status) + sid = resp.getheader("sid") + logger.debug("Subscription SID " + sid) + + with alloc_fail(hapd, 1, "=event_add"): + for i in range(2): + dev[1].dump_monitor() + dev[2].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[2].request("WPS_CANCEL") + if i == 0: + time.sleep(1) + else: + time.sleep(0.1) + + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 200: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 1, "wpabuf_dup;event_add"): + dev[1].dump_monitor() + dev[2].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[2].request("WPS_CANCEL") + time.sleep(0.1) + + with fail_test(hapd, 1, "os_get_random;uuid_make;subscription_start"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 1, "=subscription_start"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + headers = { "callback": '', + "NT": "upnp:event", + "timeout": "Second-1234" } + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + headers = { "callback": ' <', + "NT": "upnp:event", + "timeout": "Second-1234" } + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + headers = { "callback": '', + "NT": "upnp:event", + "timeout": "Second-1234" } + with alloc_fail(hapd, 1, "wpabuf_alloc;subscription_first_event"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 1, "event_add;subscription_first_event"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 1, "subscr_addr_add_url"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 2, "subscr_addr_add_url"): + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 500: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + for i in range(6): + headers = { "callback": '' % (12345 + i), + "NT": "upnp:event", + "timeout": "Second-1234" } + conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers) + resp = conn.getresponse() + if resp.status != 200: + raise Exception("Unexpected HTTP response: %d" % resp.status) + + with alloc_fail(hapd, 1, "=upnp_wps_device_send_wlan_event"): + dev[1].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + time.sleep(0.1) + + with alloc_fail(hapd, 1, "wpabuf_alloc;upnp_wps_device_send_event"): + dev[1].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + time.sleep(0.1) + + with alloc_fail(hapd, 1, "base64_encode;upnp_wps_device_send_wlan_event"): + dev[1].dump_monitor() + dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670") + dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5) + dev[1].request("WPS_CANCEL") + time.sleep(0.1) + + hapd.disable() + with alloc_fail(hapd, 1, "get_netif_info"): + if "FAIL" not in hapd.request("ENABLE"): + raise Exception("ENABLE succeeded during OOM") + def test_ap_wps_upnp_http_proto(dev, apdev): """WPS AP and UPnP/HTTP protocol testing""" ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e" @@ -3183,6 +3381,8 @@ class WPSAPHTTPServer(SocketServer.StreamRequestHandler): self.handle_wps_control() elif "SUBSCRIBE /wps_event" in data: self.handle_wps_event() + else: + self.handle_others(data) def handle_upnp_info(self): self.wfile.write(gen_upnp_info()) @@ -3193,25 +3393,36 @@ class WPSAPHTTPServer(SocketServer.StreamRequestHandler): def handle_wps_event(self): self.wfile.write(gen_wps_event()) + def handle_others(self, data): + logger.info("Ignore HTTP request: " + data) + class MyTCPServer(SocketServer.TCPServer): def __init__(self, addr, handler): self.allow_reuse_address = True SocketServer.TCPServer.__init__(self, addr, handler) -def wps_er_start(dev, http_server, max_age=1): +def wps_er_start(dev, http_server, max_age=1, wait_m_search=False, + location_url=None): socket.setdefaulttimeout(1) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("239.255.255.250", 1900)) dev.request("WPS_ER_START ifname=lo") - (msg,addr) = sock.recvfrom(1000) - logger.debug("Received SSDP message from %s: %s" % (str(addr), msg)) - if "M-SEARCH" not in msg: - raise Exception("Not an M-SEARCH") + for i in range(100): + (msg,addr) = sock.recvfrom(1000) + logger.debug("Received SSDP message from %s: %s" % (str(addr), msg)) + if "M-SEARCH" in msg: + break + if not wait_m_search: + raise Exception("Not an M-SEARCH") + if i == 99: + raise Exception("No M-SEARCH seen") # Add an AP with a valid URL and server listing to it server = MyTCPServer(("127.0.0.1", 12345), http_server) - sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=%d\r\n\r\n" % max_age, addr) + if not location_url: + location_url = 'http://127.0.0.1:12345/foo.xml' + sock.sendto("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:%s\r\ncache-control:max-age=%d\r\n\r\n" % (location_url, max_age), addr) server.timeout = 1 return server,sock @@ -3235,10 +3446,10 @@ def wps_er_stop(dev, sock, server, on_alloc_fail=False): raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout") dev.request("WPS_ER_STOP") -def run_wps_er_proto_test(dev, handler, no_event_url=False): +def run_wps_er_proto_test(dev, handler, no_event_url=False, location_url=None): try: uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e' - server,sock = wps_er_start(dev, handler) + server,sock = wps_er_start(dev, handler, location_url=location_url) global wps_event_url wps_event_url = None server.handle_request() @@ -3253,7 +3464,7 @@ def run_wps_er_proto_test(dev, handler, no_event_url=False): raise Exception("Did not get event URL") logger.info("Event URL: " + wps_event_url) finally: - dev.request("WPS_ER_STOP") + dev.request("WPS_ER_STOP") def send_wlanevent(url, uuid, data): conn = httplib.HTTPConnection(url.netloc) @@ -3534,6 +3745,121 @@ RGV2aWNlIEEQSQAGADcqAAEg data += '\x10\x09\x00\x02\x00\x00' send_wlanevent(url, uuid, data) + logger.info("Check max concurrent requests") + addr = (url.hostname, url.port) + socks = {} + for i in range(20): + socks[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + socks[i].connect(addr) + for i in range(20): + socks[i].send("GET / HTTP/1.1\r\n\r\n") + count = 0 + for i in range(20): + try: + res = socks[i].recv(100) + if "HTTP/1" in res: + count += 1 + except: + pass + socks[i].close() + logger.info("%d concurrent HTTP GET operations returned response" % count) + if count < 10: + raise Exception("Too few concurrent HTTP connections accepted") + + logger.info("OOM in HTTP server") + for func in [ "http_request_init", "httpread_create", + "eloop_register_timeout;httpread_create", + "eloop_register_sock;httpread_create", + "httpread_hdr_analyze" ]: + with alloc_fail(dev[0], 1, func): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + sock.connect(addr) + sock.send("GET / HTTP/1.1\r\n\r\n") + try: + sock.recv(100) + except: + pass + sock.close() + + logger.info("Invalid HTTP header") + for req in [ " GET / HTTP/1.1\r\n\r\n", + "HTTP/1.1 200 OK\r\n\r\n", + "HTTP/\r\n\r\n", + "GET %%a%aa% HTTP/1.1\r\n\r\n", + "GET / HTTP/1.1\r\n FOO\r\n\r\n", + "NOTIFY / HTTP/1.1\r\n" + 4097*'a' + '\r\n\r\n', + "NOTIFY / HTTP/1.1\r\n\r\n" + 8193*'a', + "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n foo\r\n", + "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n1\r\nfoo\r\n", + "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\n", + "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\naa\ra\r\n\ra" ]: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + sock.settimeout(0.1) + sock.connect(addr) + sock.send(req) + try: + sock.recv(100) + except: + pass + sock.close() + + with alloc_fail(dev[0], 2, "httpread_read_handler"): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + sock.connect(addr) + sock.send("NOTIFY / HTTP/1.1\r\n\r\n" + 4500*'a') + try: + sock.recv(100) + except: + pass + sock.close() + + conn = httplib.HTTPConnection(url.netloc) + payload = '