diff --git a/autotests/misc/certs/radius-clients.text b/autotests/misc/certs/radius-clients.text new file mode 100644 index 00000000..3bb0e980 --- /dev/null +++ b/autotests/misc/certs/radius-clients.text @@ -0,0 +1 @@ +0.0.0.0/0 secret diff --git a/autotests/testPreauth-roam/TestPreauth.8021x b/autotests/testPreauth-roam/TestPreauth.8021x new file mode 100644 index 00000000..fe8a2e46 --- /dev/null +++ b/autotests/testPreauth-roam/TestPreauth.8021x @@ -0,0 +1,6 @@ +[Security] +EAP-Method=TLS +EAP-TLS-CACert=/tmp/certs/cert-ca.pem +EAP-TLS-ClientCert=/tmp/certs/cert-client.pem +EAP-TLS-ClientKey=/tmp/certs/cert-client-key-pkcs8.pem +EAP-Identity=abc@example.com diff --git a/autotests/testPreauth-roam/eaptls-preauth-1.conf b/autotests/testPreauth-roam/eaptls-preauth-1.conf new file mode 100644 index 00000000..0c7f96a7 --- /dev/null +++ b/autotests/testPreauth-roam/eaptls-preauth-1.conf @@ -0,0 +1,36 @@ +hw_mode=g +channel=1 +ssid=TestPreauth +utf8_ssid=1 +ctrl_interface=/var/run/hostapd + +wpa=2 +wpa_key_mgmt=WPA-EAP +wpa_pairwise=CCMP +ieee8021x=1 +wpa_ptk_rekey=30 +wpa_group_rekey=80 +ieee80211w=1 + +# Run the RADIUS server in the BSS 0 hostapd only, listen for BSS 1 connections +eap_server=1 +eap_user_file=/tmp/certs/eap-user-tls.text +ca_cert=/tmp/certs/cert-ca.pem +server_cert=/tmp/certs/cert-server.pem +private_key=/tmp/certs/cert-server-key.pem +server_id=testeap +radius_server_clients=/tmp/certs/radius-clients.text +radius_server_auth_port=1812 +nas_identifier=testeap1 + +rsn_preauth=1 +rsn_preauth_interfaces=wln0 wln1 +disable_pmksa_caching=0 + +# Allow PMK cache to be shared opportunistically among configured interfaces +# and BSSes (i.e., all configurations within a single hostapd process). +okc=1 + +ap_table_expiration_time=36000 +ap_table_max_size=10 +rrm_neighbor_report=1 diff --git a/autotests/testPreauth-roam/eaptls-preauth-2.conf b/autotests/testPreauth-roam/eaptls-preauth-2.conf new file mode 100644 index 00000000..bddcca69 --- /dev/null +++ b/autotests/testPreauth-roam/eaptls-preauth-2.conf @@ -0,0 +1,32 @@ +hw_mode=g +channel=2 +ssid=TestPreauth +utf8_ssid=1 +ctrl_interface=/var/run/hostapd + +wpa=2 +wpa_key_mgmt=WPA-EAP +wpa_pairwise=CCMP +ieee8021x=1 +wpa_ptk_rekey=30 +wpa_group_rekey=80 +ieee80211w=1 + +# For EAP connect to the RADIUS server in the BSS 0 +own_ip_addr=127.0.0.1 +nas_identifier=testeap2 +auth_server_addr=127.0.0.1 +auth_server_port=1812 +auth_server_shared_secret=secret + +rsn_preauth=1 +rsn_preauth_interfaces=wln0 wln1 +disable_pmksa_caching=0 + +# Allow PMK cache to be shared opportunistically among configured interfaces +# and BSSes (i.e., all configurations within a single hostapd process). +okc=1 + +ap_table_expiration_time=36000 +ap_table_max_size=10 +rrm_neighbor_report=1 diff --git a/autotests/testPreauth-roam/hw.conf b/autotests/testPreauth-roam/hw.conf new file mode 100644 index 00000000..eba07167 --- /dev/null +++ b/autotests/testPreauth-roam/hw.conf @@ -0,0 +1,8 @@ +[SETUP] +num_radios=3 +max_test_exec_interval_sec=30 +tmpfs_extra_stuff=../misc/certs + +[HOSTAPD] +rad0=eaptls-preauth-1.conf +rad1=eaptls-preauth-2.conf diff --git a/autotests/testPreauth-roam/test.py b/autotests/testPreauth-roam/test.py new file mode 100644 index 00000000..b95d605f --- /dev/null +++ b/autotests/testPreauth-roam/test.py @@ -0,0 +1,135 @@ +#! /usr/bin/python3 + +import unittest +import sys, os + +sys.path.append('../util') +import iwd +from iwd import IWD +from iwd import PSKAgent +from iwd import NetworkType +from hwsim import Hwsim +from hostapd import HostapdCLI +from wiphy import wiphy_map + +class Test(unittest.TestCase): + def test_preauth_success(self): + hwsim = Hwsim() + + bss_hostapd = [None, None] + bss_radio = [None, None] + for wname in wiphy_map: + wiphy = wiphy_map[wname] + intf = list(wiphy.values())[0] + if intf.config and '1' in intf.config: + bss_idx = 0 + elif intf.config and '2' in intf.config: + bss_idx = 1 + else: + continue + + for path in hwsim.radios: + radio = hwsim.radios[path] + if radio.name == wname: + break + + bss_hostapd[bss_idx] = HostapdCLI(intf) + bss_radio[bss_idx] = radio + + rule0 = hwsim.rules.create() + rule0.source = bss_radio[0].addresses[0] + rule0.bidirectional = True + + rule1 = hwsim.rules.create() + rule1.source = bss_radio[1].addresses[0] + rule1.bidirectional = True + + # Fill in the neighbor AP tables in both BSSes. By default each + # instance knows only about current BSS, even inside one hostapd + # process. + # Roaming still works without the neighbor AP table but neighbor + # reports have to be disabled in the .conf files + bss0_nr = ''.join(bss_radio[0].addresses[0].split(':')) + \ + '8f0000005101060603000000' + bss1_nr = ''.join(bss_radio[1].addresses[0].split(':')) + \ + '8f0000005102060603000000' + + bss_hostapd[0].set_neighbor(bss_radio[1].addresses[0], 'TestPreauth', + bss1_nr) + bss_hostapd[1].set_neighbor(bss_radio[0].addresses[0], 'TestPreauth', + bss0_nr) + + wd = IWD() + + device = wd.list_devices()[0]; + + # Check that iwd selects BSS 0 first + rule0.signal = -2500 + rule1.signal = -3500 + + condition = 'not obj.scanning' + wd.wait_for_object_condition(device, condition) + + device.scan() + + condition = 'obj.scanning' + wd.wait_for_object_condition(device, condition) + + condition = 'not obj.scanning' + wd.wait_for_object_condition(device, condition) + + ordered_networks = device.get_ordered_networks() + + self.assertEqual(len(ordered_networks), 1) + ordered_network = ordered_networks[0] + self.assertEqual(ordered_network.name, "TestPreauth") + self.assertEqual(ordered_network.type, NetworkType.eap) + self.assertEqual(ordered_network.signal_strength, -2500) + + condition = 'not obj.connected' + wd.wait_for_object_condition(ordered_network.network_object, condition) + + self.assertFalse(bss_hostapd[0].list_sta()) + self.assertFalse(bss_hostapd[1].list_sta()) + + ordered_network.network_object.connect() + + condition = 'obj.connected' + wd.wait_for_object_condition(ordered_network.network_object, condition) + + self.assertTrue(bss_hostapd[0].list_sta()) + self.assertFalse(bss_hostapd[1].list_sta()) + + # Check that iwd starts transition to BSS 1 in less than 15 seconds + rule0.signal = -8000 + + condition = 'obj.state == DeviceState.roaming' + wd.wait_for_object_condition(device, condition, 15) + + # TODO: verify that the PMK from preauthentication was used + + # Check that iwd is on BSS 1 once out of roaming state and doesn't + # go through 'disconnected', 'autoconnect', 'connecting' in between + condition = 'obj.state != DeviceState.roaming' + wd.wait_for_object_condition(device, condition, 5) + + self.assertEqual(device.state, iwd.DeviceState.connected) + self.assertTrue(bss_hostapd[1].list_sta()) + + device.disconnect() + + condition = 'not obj.connected' + wd.wait_for_object_condition(ordered_network.network_object, condition) + + @classmethod + def setUpClass(cls): + IWD.copy_to_storage('TestPreauth.8021x') + + os.system('ifconfig lo up') + + @classmethod + def tearDownClass(cls): + IWD.clear_storage() + +if __name__ == '__main__': + unittest.main(exit=True)