tests: WPS ER init failure
[mech_eap.git] / tests / hwsim / test_ap_wps.py
index a817ddb..9684a36 100644 (file)
@@ -5,6 +5,7 @@
 # See README for more details.
 
 import base64
+import binascii
 import os
 import time
 import stat
@@ -174,6 +175,21 @@ def test_ap_wps_init_through_wps_config(dev, apdev):
     dev[0].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
                    pairwise="CCMP", group="CCMP")
 
+def test_ap_wps_init_through_wps_config_2(dev, apdev):
+    """AP configuration using wps_config and wps_cred_processing=2"""
+    ssid = "test-wps-init-config"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1",
+                     "wps_cred_processing": "2" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" in hapd.request("WPS_CONFIG " + ssid.encode("hex") + " WPA2PSK CCMP " + "12345678".encode("hex")):
+        raise Exception("WPS_CONFIG command failed")
+    ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+    if ev is None:
+        raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
+    if "100e" not in ev:
+        raise Exception("WPS-NEW-AP-SETTINGS did not include Credential")
+
 def test_ap_wps_invalid_wps_config_passphrase(dev, apdev):
     """AP configuration using wps_config command with invalid passphrase"""
     ssid = "test-wps-init-config"
@@ -771,6 +787,44 @@ def test_ap_wps_setup_locked_timeout(dev, apdev):
     if ev is None:
         raise Exception("AP PIN did not get unlocked on 60 second timeout")
 
+def test_ap_wps_setup_locked_2(dev, apdev):
+    """WPS AP configured for special ap_setup_locked=2 mode"""
+    ssid = "test-wps-ap-pin"
+    appin = "12345670"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+               "wpa_passphrase": "12345678", "wpa": "2",
+               "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+               "ap_pin": appin, "ap_setup_locked": "2" }
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    new_ssid = "wps-new-ssid-test"
+    new_passphrase = "1234567890"
+
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[0].wps_reg(apdev[0]['bssid'], appin)
+    dev[0].request("REMOVE_NETWORK all")
+    dev[0].wait_disconnected()
+
+    hapd.dump_monitor()
+    dev[0].dump_monitor()
+    dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK",
+                   "CCMP", new_passphrase, no_wait=True)
+
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("hostapd did not report WPS failure")
+    if "msg=12 config_error=15" not in ev:
+        raise Exception("Unexpected failure reason (AP): " + ev)
+
+    ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
+    if ev is None:
+        raise Exception("Timeout on receiving WPS operation failure event")
+    if "CTRL-EVENT-CONNECTED" in ev:
+        raise Exception("Unexpected connection")
+    if "config_error=15" not in ev:
+        raise Exception("Unexpected failure reason (STA): " + ev)
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+
 def test_ap_wps_pbc_overlap_2ap(dev, apdev):
     """WPS PBC session overlap with two active APs"""
     hostapd.add_ap(apdev[0]['ifname'],
@@ -1092,10 +1146,71 @@ def _test_ap_wps_er_add_enrollee_uuid(dev, apdev):
     if ev is None:
         raise Exception("WPS ER did not report success")
 
+    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-REMOVE"], timeout=15)
+    if ev is None:
+        raise Exception("No Enrollee STA entry timeout seen")
+
     logger.info("Stop ER")
     dev[0].dump_monitor()
     dev[0].request("WPS_ER_STOP")
 
+def test_ap_wps_er_multi_add_enrollee(dev, apdev):
+    """Multiple WPS ERs adding a new enrollee using PIN"""
+    try:
+        _test_ap_wps_er_multi_add_enrollee(dev, apdev)
+    finally:
+        dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_multi_add_enrollee(dev, apdev):
+    ssid = "wps-er-add-enrollee"
+    ap_pin = "12345670"
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hostapd.add_ap(apdev[0]['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",
+                     'friendly_name': "WPS AP",
+                     "config_methods": "label push_button",
+                     "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+    for i in range(2):
+        dev[i].scan_for_bss(apdev[0]['bssid'], freq=2412)
+        dev[i].wps_reg(apdev[0]['bssid'], ap_pin)
+        dev[i].request("WPS_ER_START ifname=lo")
+    for i in range(2):
+        ev = dev[i].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+        if ev is None:
+            raise Exception("AP discovery timed out")
+        dev[i].dump_monitor()
+        dev[i].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+        ev = dev[i].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+        if ev is None:
+            raise Exception("AP learn timed out")
+        ev = dev[i].wait_event(["WPS-FAIL"], timeout=15)
+        if ev is None:
+            raise Exception("WPS-FAIL after AP learn timed out")
+
+    time.sleep(0.1)
+
+    pin = dev[2].wps_read_pin()
+    addr = dev[2].own_addr()
+    dev[0].dump_monitor()
+    dev[0].request("WPS_ER_PIN any " + pin + " " + addr)
+    dev[1].dump_monitor()
+    dev[1].request("WPS_ER_PIN any " + pin + " " + addr)
+
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].dump_monitor()
+    dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+    ev = dev[2].wait_event(["WPS-SUCCESS"], timeout=30)
+    if ev is None:
+        raise Exception("Enrollee did not report success")
+    dev[2].wait_connected(timeout=15)
+
 def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
     """WPS ER connected to AP and adding a new enrollee using PBC"""
     try:
@@ -1827,17 +1942,111 @@ def test_ap_wps_auto_setup_with_config_file(dev, apdev):
             pass
 
 def test_ap_wps_pbc_timeout(dev, apdev, params):
-    """wpa_supplicant PBC walk time [long]"""
+    """wpa_supplicant PBC walk time and WPS ER SelReg timeout [long]"""
     if not params['long']:
         raise HwsimSkip("Skip test case with long duration due to --long not specified")
-    ssid = "test-wps"
-    hostapd.add_ap(apdev[0]['ifname'],
-                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
-    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+    ctrlurl = urlparse.urlparse(urls['control_url'])
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "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)
+
+    msg = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewMessage>EEoAARAQQQABARASAAIAABBTAAIxSBBJAA4ANyoAASABBv///////xBIABA2LbR7pTpRkYj7
+VFi5hrLk
+</NewMessage>
+</u:SetSelectedRegistrar>
+</s:Body>
+</s:Envelope>'''
+    headers = { "Content-type": 'text/xml; charset="utf-8"' }
+    headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+    conn.request("POST", ctrlurl.path, msg, headers)
+    resp = conn.getresponse()
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+    server.handle_request()
+
     logger.info("Start WPS_PBC and wait for PBC walk time expiration")
     if "OK" not in dev[0].request("WPS_PBC"):
         raise Exception("WPS_PBC failed")
-    ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=150)
+
+    start = os.times()[4]
+
+    server.handle_request()
+    dev[1].request("BSS_FLUSH 0")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+                        only_new=True)
+    bss = dev[1].get_bss(apdev[0]['bssid'])
+    logger.debug("BSS: " + str(bss))
+    if '[WPS-AUTH]' not in bss['flags']:
+        raise Exception("WPS not indicated authorized")
+
+    server.handle_request()
+
+    wps_timeout_seen = False
+
+    while True:
+        hapd.dump_monitor()
+        dev[1].dump_monitor()
+        if not wps_timeout_seen:
+            ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=0)
+            if ev is not None:
+                logger.info("PBC timeout seen")
+                wps_timeout_seen = True
+        else:
+            dev[0].dump_monitor()
+        now = os.times()[4]
+        if now - start > 130:
+            raise Exception("Selected registration information not removed")
+        dev[1].request("BSS_FLUSH 0")
+        dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+                            only_new=True)
+        bss = dev[1].get_bss(apdev[0]['bssid'])
+        logger.debug("BSS: " + str(bss))
+        if '[WPS-AUTH]' not in bss['flags']:
+            break
+        server.handle_request()
+
+    server.server_close()
+
+    if wps_timeout_seen:
+        return
+
+    now = os.times()[4]
+    if now < start + 150:
+        dur = start + 150 - now
+    else:
+        dur = 1
+    logger.info("Continue waiting for PBC timeout (%d sec)" % dur)
+    ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=dur)
     if ev is None:
         raise Exception("WPS-TIMEOUT not reported")
 
@@ -1871,7 +2080,7 @@ def ssdp_send(msg, no_recv=False):
         return None
     return sock.recv(1000)
 
-def ssdp_send_msearch(st):
+def ssdp_send_msearch(st, no_recv=False):
     msg = '\r\n'.join([
             'M-SEARCH * HTTP/1.1',
             'HOST: 239.255.255.250:1900',
@@ -1879,7 +2088,7 @@ def ssdp_send_msearch(st):
             'MAN: "ssdp:discover"',
             'ST: ' + st,
             '', ''])
-    return ssdp_send(msg)
+    return ssdp_send(msg, no_recv=no_recv)
 
 def test_ap_wps_ssdp_msearch(dev, apdev):
     """WPS AP and SSDP M-SEARCH messages"""
@@ -2611,6 +2820,73 @@ def test_ap_wps_upnp_subscribe(dev, apdev):
         if "FAIL" not in hapd.request("ENABLE"):
             raise Exception("ENABLE succeeded during OOM")
 
+def test_ap_wps_upnp_subscribe_events(dev, apdev):
+    """WPS AP and UPnP event subscription and many events"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "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)
+
+    # Fetch the first event message
+    server.handle_request()
+
+    # Force subscription event queue to reach the maximum length by generating
+    # new proxied events without the ER fetching any of the pending events.
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+    for i in range(16):
+        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)
+
+    hapd.request("WPS_PIN any 12345670")
+    dev[1].dump_monitor()
+    dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=10)
+    if ev is None:
+        raise Exception("WPS success not reported")
+
+    # Close the WPS ER HTTP server without fetching all the pending events.
+    # This tests hostapd code path that clears subscription and the remaining
+    # event queue when the interface is deinitialized.
+    server.handle_request()
+    server.server_close()
+
+    dev[1].wait_connected()
+
 def test_ap_wps_upnp_http_proto(dev, apdev):
     """WPS AP and UPnP/HTTP protocol testing"""
     ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
@@ -3143,6 +3419,12 @@ def _test_ap_wps_er_init_oom(dev, apdev):
         if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
             raise Exception("WPS_ER_START succeeded during os_get_random failure")
 
+def test_ap_wps_er_init_fail(dev, apdev):
+    """WPS ER init failure"""
+    if "FAIL" not in dev[0].request("WPS_ER_START ifname=does-not-exist"):
+        dev[0].request("WPS_ER_STOP")
+        raise Exception("WPS_ER_START with non-existing ifname succeeded")
+
 def test_ap_wps_wpa_cli_action(dev, apdev, test_params):
     """WPS events and wpa_cli action script"""
     logdir = os.path.abspath(test_params['logdir'])
@@ -4512,7 +4794,7 @@ def wps_ext_eap_wsc(dst, src, src_addr, msg):
     if "OK" not in res:
         raise Exception("EAPOL_RX failed")
 
-def wps_start_ext(apdev, dev):
+def wps_start_ext(apdev, dev, pbc=False):
     addr = dev.own_addr()
     bssid = apdev['bssid']
     ssid = "test-wps-conf"
@@ -4521,13 +4803,19 @@ def wps_start_ext(apdev, dev):
                "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
     hapd = hostapd.add_ap(apdev['ifname'], params)
 
-    pin = dev.wps_read_pin()
-    hapd.request("WPS_PIN any " + pin)
+    if pbc:
+        hapd.request("WPS_PBC")
+    else:
+        pin = dev.wps_read_pin()
+        hapd.request("WPS_PIN any " + pin)
     dev.scan_for_bss(bssid, freq="2412")
     hapd.request("SET ext_eapol_frame_io 1")
     dev.request("SET ext_eapol_frame_io 1")
 
-    dev.request("WPS_PIN " + bssid + " " + pin)
+    if pbc:
+        dev.request("WPS_PBC " + bssid)
+    else:
+        dev.request("WPS_PIN " + bssid + " " + pin)
     return addr,bssid,hapd
 
 def wps_auth_corrupt(dst, src, addr):
@@ -4678,6 +4966,423 @@ def test_ap_wps_authenticator_missing_m2(dev, apdev):
         raise Exception("EAPOL_RX failed")
     wps_fail_finish(hapd, dev[0], "msg=5")
 
+def test_ap_wps_m2_dev_passwd_id_p2p(dev, apdev):
+    """WPS and M2 with different Device Password ID (P2P)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    msg = msg[0:730] + "0005" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pin_to_pbc(dev, apdev):
+    """WPS and M2 with different Device Password ID (PIN to PBC)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value (PIN --> PBC). This will be rejected.
+    msg = msg[0:730] + "0004" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pbc_to_pin(dev, apdev):
+    """WPS and M2 with different Device Password ID (PBC to PIN)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Replace Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    msg = msg[0:730] + "0000" + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_dev_passwd_id(dev, apdev):
+    """WPS and M2 without Device Password ID"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0])
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[722:730] != '10120002':
+        raise Exception("Could not find Device Password ID attribute")
+    # Remove Device Password ID value. This will fail Authenticator check, but
+    # allows the code path in wps_process_dev_pw_id() to be checked from debug
+    # log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 6)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:722] + msg[734:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_missing_registrar_nonce(dev, apdev):
+    """WPS and M2 without Registrar Nonce"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[96:104] != '10390010':
+        raise Exception("Could not find Registrar Nonce attribute")
+    # Remove Registrar Nonce. This will fail Authenticator check, but
+    # allows the code path in wps_process_registrar_nonce() to be checked from
+    # the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:96] + msg[136:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_enrollee_nonce(dev, apdev):
+    """WPS and M2 without Enrollee Nonce"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[56:64] != '101a0010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove Enrollee Nonce. This will fail Authenticator check, but
+    # allows the code path in wps_process_enrollee_nonce() to be checked from
+    # the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:56] + msg[96:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_uuid_r(dev, apdev):
+    """WPS and M2 without UUID-R"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[136:144] != '10480010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove UUID-R. This will fail Authenticator check, but allows the code
+    # path in wps_process_uuid_r() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 20)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:136] + msg[176:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_invalid(dev, apdev):
+    """WPS and M2 parsing failure"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[136:144] != '10480010':
+        raise Exception("Could not find enrollee Nonce attribute")
+    # Remove UUID-R. This will fail Authenticator check, but allows the code
+    # path in wps_process_uuid_r() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 1)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:-2]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_msg_type(dev, apdev):
+    """WPS and M2 without Message Type"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Remove Message Type. This will fail Authenticator check, but allows the
+    # code path in wps_process_wsc_msg() to be checked from the debug log.
+    mlen = "%04x" % (int(msg[4:8], 16) - 5)
+    msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:46] + msg[56:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_msg_type(dev, apdev):
+    """WPS and M2 but unknown Message Type"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + "00" + msg[56:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+    if ev is None:
+        raise Exception("Disconnect event not seen")
+    dev[0].request("WPS_CANCEL")
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode(dev, apdev):
+    """WPS and M2 but unknown opcode"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in EAP-WSC processing.
+    msg = msg[0:32] + "00" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode2(dev, apdev):
+    """WPS and M2 but unknown opcode (WSC_Start)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in EAP-WSC processing.
+    msg = msg[0:32] + "01" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode3(dev, apdev):
+    """WPS and M2 but unknown opcode (WSC_Done)"""
+    addr,bssid,hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+    wps_ext_eap_identity_req(dev[0], hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev[0], addr)
+    wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+    logger.debug("M2")
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev[0].request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    # Replace opcode. This will be discarded in WPS Enrollee processing.
+    msg = msg[0:32] + "05" + msg[34:]
+    res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    dev[0].request("WPS_CANCEL")
+    dev[0].wait_disconnected()
+    dev[0].flush_scan_cache()
+
+def wps_m2_but_other(dev, apdev, title, msgtype):
+    addr,bssid,hapd = wps_start_ext(apdev, dev)
+    wps_ext_eap_identity_req(dev, hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev, addr)
+    wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev, addr, "M1")
+    logger.debug(title)
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev.request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + msgtype + msg[56:]
+    res = dev.request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = dev.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("WPS-FAIL event not seen")
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+
+def wps_m4_but_other(dev, apdev, title, msgtype):
+    addr,bssid,hapd = wps_start_ext(apdev, dev)
+    wps_ext_eap_identity_req(dev, hapd, bssid)
+    wps_ext_eap_identity_resp(hapd, dev, addr)
+    wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+    wps_ext_eap_wsc(hapd, dev, addr, "M1")
+    wps_ext_eap_wsc(dev, hapd, bssid, "M2")
+    wps_ext_eap_wsc(hapd, dev, addr, "M3")
+    logger.debug(title)
+    ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+    if ev is None:
+        raise Exception("Timeout on EAPOL-TX")
+    hapd.request("SET ext_eapol_frame_io 0")
+    dev.request("SET ext_eapol_frame_io 0")
+    msg = ev.split(' ')[2]
+    if msg[46:54] != '10220001':
+        raise Exception("Could not find Message Type attribute")
+    # Replace Message Type value. This will be rejected.
+    msg = msg[0:54] + msgtype + msg[56:]
+    res = dev.request("EAPOL_RX " + bssid + " " + msg)
+    if "OK" not in res:
+        raise Exception("EAPOL_RX failed")
+    ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+    if ev is None:
+        raise Exception("WPS-FAIL event not seen")
+    dev.request("WPS_CANCEL")
+    dev.wait_disconnected()
+
+def test_ap_wps_m2_msg_type_m4(dev, apdev):
+    """WPS and M2 but Message Type M4"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M4", "08")
+
+def test_ap_wps_m2_msg_type_m6(dev, apdev):
+    """WPS and M2 but Message Type M6"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M6", "0a")
+
+def test_ap_wps_m2_msg_type_m8(dev, apdev):
+    """WPS and M2 but Message Type M8"""
+    wps_m2_but_other(dev[0], apdev[0], "M2/M8", "0c")
+
+def test_ap_wps_m4_msg_type_m2(dev, apdev):
+    """WPS and M4 but Message Type M2"""
+    wps_m4_but_other(dev[0], apdev[0], "M4/M2", "05")
+
+def test_ap_wps_m4_msg_type_m2d(dev, apdev):
+    """WPS and M4 but Message Type M2D"""
+    wps_m4_but_other(dev[0], apdev[0], "M4/M2D", "06")
+
 def test_ap_wps_config_methods(dev, apdev):
     """WPS configuration method parsing"""
     ssid = "test-wps-conf"
@@ -4691,3 +5396,97 @@ def test_ap_wps_config_methods(dev, apdev):
                "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
                "config_methods": "display push_button" }
     hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+def test_ap_wps_set_selected_registrar_proto(dev, apdev):
+    """WPS UPnP SetSelectedRegistrar protocol testing"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+    ctrlurl = urlparse.urlparse(urls['control_url'])
+    url = urlparse.urlparse(location)
+    conn = httplib.HTTPConnection(url.netloc)
+
+    class WPSERHTTPServer(SocketServer.StreamRequestHandler):
+        def handle(self):
+            data = self.rfile.readline().strip()
+            logger.debug(data)
+            self.wfile.write(gen_wps_event())
+
+    server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+    server.timeout = 1
+
+    headers = { "callback": '<http://127.0.0.1:12345/event>',
+                "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)
+    server.handle_request()
+
+    tests = [ (500, "10"),
+              (200, "104a000110" + "1041000101" + "101200020000" +
+               "105300023148" +
+               "1049002c00372a0001200124111111111111222222222222333333333333444444444444555555555555666666666666" +
+               "10480010362db47ba53a519188fb5458b986b2e4"),
+              (200, "104a000110" + "1041000100" + "101200020000" +
+               "105300020000"),
+              (200, "104a000110" + "1041000100"),
+              (200, "104a000110") ]
+    for status,test in tests:
+        tlvs = binascii.unhexlify(test)
+        newmsg = base64.b64encode(tlvs)
+        msg = '<?xml version="1.0"?>\n'
+        msg += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
+        msg += '<s:Body>'
+        msg += '<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">'
+        msg += '<NewMessage>'
+        msg += newmsg
+        msg += "</NewMessage></u:SetSelectedRegistrar></s:Body></s:Envelope>"
+        headers = { "Content-type": 'text/xml; charset="utf-8"' }
+        headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+        conn.request("POST", ctrlurl.path, msg, headers)
+        resp = conn.getresponse()
+        if resp.status != status:
+            raise Exception("Unexpected HTTP response: %d (expected %d)" % (resp.status, status))
+
+def test_ap_wps_adv_oom(dev, apdev):
+    """WPS AP and advertisement OOM"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    hapd = add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    with alloc_fail(hapd, 1, "=msearchreply_state_machine_start"):
+        ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+                          no_recv=True)
+        time.sleep(0.2)
+
+    with alloc_fail(hapd, 1, "eloop_register_timeout;msearchreply_state_machine_start"):
+        ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+                          no_recv=True)
+        time.sleep(0.2)
+
+    with alloc_fail(hapd, 1,
+                    "next_advertisement;advertisement_state_machine_stop"):
+        hapd.disable()
+
+    with alloc_fail(hapd, 1, "ssdp_listener_start"):
+        if "FAIL" not in hapd.request("ENABLE"):
+            raise Exception("ENABLE succeeded during OOM")
+
+def test_wps_config_methods(dev):
+    """WPS config method update"""
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5")
+    if "OK" not in wpas.request("SET config_methods display label"):
+        raise Exception("Failed to set config_methods")
+    if wpas.request("GET config_methods").strip() != "display label":
+        raise Exception("config_methods were not updated")
+    if "OK" not in wpas.request("SET config_methods "):
+        raise Exception("Failed to clear config_methods")
+    if wpas.request("GET config_methods").strip() != "":
+        raise Exception("config_methods were not cleared")