From 2c8161868ecce5f01dc09b1cc19e75c3f811ba15 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Thu, 16 Aug 2018 16:03:51 +0200 Subject: [PATCH] wifi/iwd: Create connections for IWD-side known networks IWD's mechanism for connecting to EAP networks requires a network config file to be present in IWD's storage. NM and its clients however won't allow a connection to be attempted until a valid NMConnection is created on the NM side for the network. To avoid duplicating the settings from the IWD-side profiles in NM, automatically create NMSettingConnections for EAP networks preconfigured on the IWD side, unless a matching connection already exists. These connections will use the "external" EAP method to mean their EAP settings can't be modified through NM, also they won't be valid for devices configured to use the wpa_supplicant backend unfortunately. Those nm-generated connections can be modified by NM users (makes sense for settings not related to the wifi authentication) in which case they get saved as normal profiles and will not be recreated as nm-generated connections on the next run. I want to additionally handle deleting connections from NM clients so that they're also forgotten by IWD, in a later patch. --- src/devices/wifi/nm-iwd-manager.c | 127 +++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index d9d539114e..7beb44e96a 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -29,7 +29,9 @@ #include "nm-core-internal.h" #include "nm-manager.h" #include "nm-device-iwd.h" +#include "nm-wifi-utils.h" #include "nm-utils/nm-random-utils.h" +#include "settings/nm-settings.h" /*****************************************************************************/ @@ -41,6 +43,7 @@ typedef struct { typedef struct { GDBusProxy *known_network; + NMSettingsConnection *mirror_connection; } KnownNetworkData; typedef struct { @@ -311,6 +314,8 @@ known_network_data_free (KnownNetworkData *network) return; g_object_unref (network->known_network); + if (network->mirror_connection) + g_object_unref (network->mirror_connection); g_slice_free (KnownNetworkData, network); } @@ -349,6 +354,100 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); } +/* Create an in-memory NMConnection for a WPA2-Enterprise network that + * has been preprovisioned with an IWD config file so that NM autoconnect + * mechanism and the clients know this networks needs no additional EAP + * configuration from the user. Only do this if no existing connection + * SSID and security type match that network yet. + */ +static void +mirror_8021x_connection (NMIwdManager *self, KnownNetworkId *id, + KnownNetworkData *data) +{ + NMSettings *settings = NM_SETTINGS_GET; + NMSettingsConnection *const *iter; + NMConnection *connection; + NMSettingsConnection *settings_connection; + char uuid[37]; + NMSetting *setting; + GBytes *ssid; + GError *error = NULL; + + for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) { + const NMSettingsConnection *conn = *iter; + NMIwdNetworkSecurity security; + gs_free char *name = NULL; + NMSettingWireless *s_wifi; + + security = nm_wifi_connection_get_iwd_security (NM_CONNECTION (conn), NULL); + if (security != NM_IWD_NETWORK_SECURITY_8021X) + continue; + + s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (conn)); + if (!s_wifi) + continue; + + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid) + continue; + + name = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + /* We already have an NMSettingsConnection matching this + * KnownNetwork, whether it's saved or an in-memory connection + * potentially created by ourselves. Nothing to do here. + */ + if (!strcmp (name, id->name)) + return; + } + + connection = nm_simple_connection_new (); + + setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_CONNECTION_ID, id->name, + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid), + NM_SETTING_CONNECTION_READ_ONLY, TRUE, + NULL)); + nm_connection_add_setting (connection, setting); + + ssid = g_bytes_new (id->name, strlen (id->name)); + setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL)); + nm_connection_add_setting (connection, setting); + + setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NULL)); + nm_connection_add_setting (connection, setting); + + setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X, NULL)); + nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), "external"); + nm_connection_add_setting (connection, setting); + + settings_connection = nm_settings_add_connection (settings, connection, + FALSE, &error); + g_object_unref (connection); + + if (!settings_connection) { + _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", + id->name, NM_G_ERROR_MSG (error)); + g_clear_error (&error); + return; + } + + nm_settings_connection_set_flags (settings_connection, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, + TRUE); + + data->mirror_connection = g_object_ref (settings_connection); +} + static void interface_added (GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) @@ -393,9 +492,13 @@ interface_added (GDBusObjectManager *object_manager, GDBusObject *object, id = known_network_id_new (name, security); - data = g_slice_new (KnownNetworkData); + data = g_slice_new0 (KnownNetworkData); data->known_network = (GDBusProxy *) g_object_ref (proxy); g_hash_table_insert (priv->known_networks, id, data); + + if (security == NM_IWD_NETWORK_SECURITY_8021X) + mirror_8021x_connection (self, id, data); + return; } } @@ -421,6 +524,7 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) { KnownNetworkId id; + KnownNetworkData *data; const char *type_str; type_str = get_property_string_or_null (proxy, "Type"); @@ -437,6 +541,27 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, else return; + data = g_hash_table_lookup (priv->known_networks, &id); + if (!data) + return; + + if (data->mirror_connection) { + NMSettingsConnectionIntFlags flags; + NMSettingsConnection *mirror_connection = data->mirror_connection; + + flags = nm_settings_connection_get_flags (mirror_connection); + + /* If connection has not been saved since we created it + * in interface_added it too can be removed now. */ + if (flags & NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED) { + data->mirror_connection = NULL; + + nm_settings_connection_delete (mirror_connection, NULL); + + g_object_unref (mirror_connection); + } + } + g_hash_table_remove (priv->known_networks, &id); return; }