autotests: Add a Preauthentication test

Unfortunately this doesn't currently ensure that the preauthentication
has succeeded and that later the PMKSA from the preauthentication was
used in the transition, only that the preauthentication process doesn't
break the transition.  For now this can be confirmed by looking at the
testrunner -v output to see that the line "EAP completed with eapSuccess"
appears before the following line, and not after:

src/device.c:device_enter_state() Old State: connected, new state: roaming
This commit is contained in:
Andrew Zaborowski 2017-04-29 03:53:29 +02:00 committed by Denis Kenzior
parent a620a02d35
commit f0fb1d2c89
6 changed files with 218 additions and 0 deletions

View File

@ -0,0 +1 @@
0.0.0.0/0 secret

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)