diff --git a/autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py b/autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py new file mode 100644 index 00000000..20e0b62c --- /dev/null +++ b/autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py @@ -0,0 +1,70 @@ +#! /usr/bin/python3 + +import unittest +import sys + +import dbus + +sys.path.append('../util') +import iwd +from iwd import IWD +from hwsim import Hwsim +from hostapd import HostapdCLI + +BSS_INTERFACE = 'net.connman.iwd.BasicServiceSet' + + +class Test(unittest.TestCase): + def _get_rsn(self, device, ssid): + ordered_network = device.get_ordered_network(ssid, full_scan=True) + network = ordered_network.network_object + self.assertEqual(len(network.extended_service_set), 1) + + path = network.extended_service_set[0] + proxy = self.wd._bus.get_object(iwd.IWD_SERVICE, path) + properties = dbus.Interface(proxy, iwd.DBUS_PROPERTIES) + + return properties.GetAll(BSS_INTERFACE)['RSN'] + + def test_rsn(self): + device = self.wd.list_devices(1)[0] + + rsn = self._get_rsn(device, 'ssidWPA2') + self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'wpa-psk'}) + self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'}) + self.assertEqual(str(rsn['Group']), 'ccmp') + + rsn = self._get_rsn(device, 'ssidSAE') + self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'sae'}) + self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'}) + self.assertEqual(str(rsn['Group']), 'ccmp') + + rsn = self._get_rsn(device, 'ssidTransition') + self.assertEqual(set(map(str, rsn['KeyMgmt'])), + {'wpa-psk', 'sae'}) + self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'}) + self.assertEqual(str(rsn['Group']), 'ccmp') + + def setUp(self): + self.wd = IWD(True) + + def tearDown(self): + self.wd.stop() + self.wd = None + + @classmethod + def setUpClass(cls): + Hwsim() + cls.hostapd = [ + HostapdCLI(config='ssidWPA2.conf'), + HostapdCLI(config='ssidSAE.conf'), + HostapdCLI(config='ssidTransition.conf'), + ] + + @classmethod + def tearDownClass(cls): + cls.hostapd = None + + +if __name__ == '__main__': + unittest.main(exit=True) diff --git a/autotests/testBasicServiceSetRSN/hw.conf b/autotests/testBasicServiceSetRSN/hw.conf new file mode 100644 index 00000000..a43062d9 --- /dev/null +++ b/autotests/testBasicServiceSetRSN/hw.conf @@ -0,0 +1,9 @@ +[SETUP] +num_radios=4 +hwsim_medium=yes +start_iwd=no + +[HOSTAPD] +rad0=ssidWPA2.conf +rad1=ssidSAE.conf +rad2=ssidTransition.conf diff --git a/autotests/testBasicServiceSetRSN/ssidSAE.conf b/autotests/testBasicServiceSetRSN/ssidSAE.conf new file mode 100644 index 00000000..2ec9ae61 --- /dev/null +++ b/autotests/testBasicServiceSetRSN/ssidSAE.conf @@ -0,0 +1,11 @@ +hw_mode=g +channel=2 +ssid=ssidSAE + +wpa=2 +wpa_key_mgmt=SAE +wpa_pairwise=CCMP +sae_password=secret123 +sae_groups=19 +ieee80211w=2 +sae_pwe=0 diff --git a/autotests/testBasicServiceSetRSN/ssidTransition.conf b/autotests/testBasicServiceSetRSN/ssidTransition.conf new file mode 100644 index 00000000..00170772 --- /dev/null +++ b/autotests/testBasicServiceSetRSN/ssidTransition.conf @@ -0,0 +1,12 @@ +hw_mode=g +channel=3 +ssid=ssidTransition + +wpa=2 +wpa_key_mgmt=WPA-PSK SAE +wpa_pairwise=CCMP +wpa_passphrase=secret123 +sae_password=secret123 +sae_groups=19 +ieee80211w=1 +sae_pwe=0 diff --git a/autotests/testBasicServiceSetRSN/ssidWPA2.conf b/autotests/testBasicServiceSetRSN/ssidWPA2.conf new file mode 100644 index 00000000..d706142b --- /dev/null +++ b/autotests/testBasicServiceSetRSN/ssidWPA2.conf @@ -0,0 +1,8 @@ +hw_mode=g +channel=1 +ssid=ssidWPA2 + +wpa=2 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=CCMP +wpa_passphrase=secret123 diff --git a/doc/basic-service-set.txt b/doc/basic-service-set.txt index f827ca02..b4f2486f 100644 --- a/doc/basic-service-set.txt +++ b/doc/basic-service-set.txt @@ -8,3 +8,26 @@ Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx Properties string Address [readonly] MAC address of BSS + + dict RSN [readonly, optional] + + Security capabilities advertised in the RSN information + element. The dictionary contains the following entries: + + array(string) KeyMgmt + + Key management suites. Values use the same names as + the wpa_supplicant D-Bus BSS.RSN property, such as + "wpa-psk", "wpa-ft-psk", "sae", "ft-sae", + "wpa-eap", and "owe". + + array(string) Pairwise + + Pairwise ciphers. Possible values are "tkip", + "ccmp", "gcmp", "ccmp-256", and "gcmp-256". + + string Group + + Group cipher. Possible values are "wep40", "wep104", + "tkip", "ccmp", "gcmp", "ccmp-256", and + "gcmp-256". diff --git a/src/network.c b/src/network.c index a5a2375a..aa9e1281 100644 --- a/src/network.c +++ b/src/network.c @@ -2198,10 +2198,120 @@ static bool network_bss_property_get_address(struct l_dbus *dbus, return true; } +struct network_bss_suite_name { + uint32_t suite; + const char *name; +}; + +static const struct network_bss_suite_name network_bss_akm_names[] = { + { IE_RSN_AKM_SUITE_PSK, "wpa-psk" }, + { IE_RSN_AKM_SUITE_FT_USING_PSK, "wpa-ft-psk" }, + { IE_RSN_AKM_SUITE_PSK_SHA256, "wpa-psk-sha256" }, + { IE_RSN_AKM_SUITE_8021X, "wpa-eap" }, + { IE_RSN_AKM_SUITE_FT_OVER_8021X, "wpa-ft-eap" }, + { IE_RSN_AKM_SUITE_8021X_SHA256, "wpa-eap-sha256" }, + { IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256, "wpa-eap-suite-b" }, + { IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384, "wpa-eap-suite-b-192" }, + { IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384, "wpa-ft-eap-sha384" }, + { IE_RSN_AKM_SUITE_FILS_SHA256, "wpa-fils-sha256" }, + { IE_RSN_AKM_SUITE_FILS_SHA384, "wpa-fils-sha384" }, + { IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256, "wpa-ft-fils-sha256" }, + { IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384, "wpa-ft-fils-sha384" }, + { IE_RSN_AKM_SUITE_SAE_SHA256, "sae" }, + { IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256, "ft-sae" }, + { IE_RSN_AKM_SUITE_OWE, "owe" }, +}; + +static const struct network_bss_suite_name network_bss_pairwise_names[] = { + { IE_RSN_CIPHER_SUITE_TKIP, "tkip" }, + { IE_RSN_CIPHER_SUITE_CCMP, "ccmp" }, + { IE_RSN_CIPHER_SUITE_GCMP, "gcmp" }, + { IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" }, + { IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" }, +}; + +static const struct network_bss_suite_name network_bss_group_names[] = { + { IE_RSN_CIPHER_SUITE_WEP40, "wep40" }, + { IE_RSN_CIPHER_SUITE_WEP104, "wep104" }, + { IE_RSN_CIPHER_SUITE_TKIP, "tkip" }, + { IE_RSN_CIPHER_SUITE_CCMP, "ccmp" }, + { IE_RSN_CIPHER_SUITE_GCMP, "gcmp" }, + { IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" }, + { IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" }, +}; + +static void network_bss_append_suite_names( + struct l_dbus_message_builder *builder, + const char *name, uint32_t suites, + const struct network_bss_suite_name *names, + size_t names_len) +{ + size_t i; + + l_dbus_message_builder_enter_dict(builder, "sv"); + l_dbus_message_builder_append_basic(builder, 's', name); + l_dbus_message_builder_enter_variant(builder, "as"); + l_dbus_message_builder_enter_array(builder, "s"); + + for (i = 0; i < names_len; i++) + if (suites & names[i].suite) + l_dbus_message_builder_append_basic(builder, 's', + names[i].name); + + l_dbus_message_builder_leave_array(builder); + l_dbus_message_builder_leave_variant(builder); + l_dbus_message_builder_leave_dict(builder); +} + +static const char *network_bss_group_cipher_name( + enum ie_rsn_cipher_suite cipher) +{ + size_t i; + + for (i = 0; i < L_ARRAY_SIZE(network_bss_group_names); i++) + if (network_bss_group_names[i].suite == cipher) + return network_bss_group_names[i].name; + + return ""; +} + +static bool network_bss_property_get_rsn(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + struct scan_bss *bss = user_data; + struct ie_rsn_info info; + const char *group; + + if (!bss->rsne || ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2, + &info) < 0) + return false; + + l_dbus_message_builder_enter_array(builder, "{sv}"); + + network_bss_append_suite_names(builder, "KeyMgmt", info.akm_suites, + network_bss_akm_names, + L_ARRAY_SIZE(network_bss_akm_names)); + network_bss_append_suite_names(builder, "Pairwise", + info.pairwise_ciphers, + network_bss_pairwise_names, + L_ARRAY_SIZE(network_bss_pairwise_names)); + + group = network_bss_group_cipher_name(info.group_cipher); + dbus_append_dict_basic(builder, "Group", 's', group); + + l_dbus_message_builder_leave_array(builder); + + return true; +} + static void setup_bss_interface(struct l_dbus_interface *interface) { l_dbus_interface_property(interface, "Address", 0, "s", network_bss_property_get_address, NULL); + l_dbus_interface_property(interface, "RSN", 0, "a{sv}", + network_bss_property_get_rsn, NULL); } static int network_init(void)