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.
This commit is contained in:
Andrew Zaborowski 2018-08-16 16:03:51 +02:00 committed by Thomas Haller
parent 977d298c5f
commit 2c8161868e

View file

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