diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 529029de8..7e004c903 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -2804,6 +2804,7 @@ static EAPMethodsTable eap_methods_table[] = { { "sim", need_secrets_sim, NULL }, { "gtc", need_secrets_password, verify_identity }, { "otp", NULL, NULL }, // FIXME: implement + { "external", NULL, NULL }, { NULL, NULL, NULL } }; @@ -2812,7 +2813,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) { NMSetting8021x *self = NM_SETTING_802_1X (setting); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); - const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL }; + const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", "external", NULL }; GSList *iter; if (error) diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 3be6159f2..5781bf9a0 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -448,33 +448,12 @@ deactivate_async (NMDevice *device, G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx); } -static NMIwdNetworkSecurity -get_connection_iwd_security (NMConnection *connection) -{ - NMSettingWirelessSecurity *s_wireless_sec; - const char *key_mgmt = NULL; - - s_wireless_sec = nm_connection_get_setting_wireless_security (connection); - if (!s_wireless_sec) - return NM_IWD_NETWORK_SECURITY_NONE; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); - nm_assert (key_mgmt); - - if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x")) - return NM_IWD_NETWORK_SECURITY_WEP; - - if (!strcmp (key_mgmt, "wpa-psk")) - return NM_IWD_NETWORK_SECURITY_PSK; - - nm_assert (!strcmp (key_mgmt, "wpa-eap")); - return NM_IWD_NETWORK_SECURITY_8021X; -} - static gboolean is_connection_known_network (NMConnection *connection) { NMSettingWireless *s_wireless; + NMIwdNetworkSecurity security; + gboolean security_ok; GBytes *ssid; gs_free char *ssid_utf8 = NULL; @@ -487,9 +466,13 @@ is_connection_known_network (NMConnection *connection) return FALSE; ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + security = nm_wifi_connection_get_iwd_security (connection, &security_ok); + if (!security_ok) + return FALSE; + return nm_iwd_manager_is_known_network (nm_iwd_manager_get (), - ssid_utf8, - get_connection_iwd_security (connection)); + ssid_utf8, security); } static gboolean @@ -543,7 +526,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError /* 8021x networks can only be used if they've been provisioned on the IWD side and * thus are Known Networks. */ - if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { if (!is_connection_known_network (connection)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "802.1x profile is not a known network"); @@ -587,7 +570,7 @@ check_connection_available (NMDevice *device, /* 8021x networks can only be used if they've been provisioned on the IWD side and * thus are Known Networks. */ - if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { if (!is_connection_known_network (connection)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "network is not known to iwd"); @@ -731,7 +714,7 @@ complete_connection (NMDevice *device, /* 8021x networks can only be used if they've been provisioned on the IWD side and * thus are Known Networks. */ - if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { if (!is_connection_known_network (connection)) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -772,6 +755,32 @@ complete_connection (NMDevice *device, return TRUE; } +static gboolean +get_variant_boolean (GVariant *v, const char *property) +{ + if (!v || !g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Property %s not cached or not boolean type", property); + + return FALSE; + } + + return g_variant_get_boolean (v); +} + +static const char * +get_variant_state (GVariant *v) +{ + if (!v || !g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "State property not cached or not a string"); + + return "unknown"; + } + + return g_variant_get_string (v, NULL); +} + static gboolean is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) { @@ -783,7 +792,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) return FALSE; value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Powered"); - return g_variant_get_boolean (value); + return get_variant_boolean (value, "Powered"); } static gboolean @@ -835,7 +844,7 @@ can_auto_connect (NMDevice *device, /* 8021x networks can only be used if they've been provisioned on the IWD side and * thus are Known Networks. */ - if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { if (!is_connection_known_network (connection)) return FALSE; } @@ -1305,9 +1314,6 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) ssid_utf8); nm_device_activate_schedule_stage3_ip_config_start (device); - nm_iwd_manager_network_connected (nm_iwd_manager_get (), ssid_utf8, - get_connection_iwd_security (connection)); - return; failed: @@ -1756,7 +1762,8 @@ state_changed (NMDeviceIwd *self, const char *new_state) NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); return; - } + } else if (nm_streq (new_state, "unknown")) + return; _LOGE (LOGD_WIFI, "State %s unknown", new_state); } @@ -1802,13 +1809,13 @@ properties_changed (GDBusProxy *proxy, GVariant *changed_properties, g_variant_get (changed_properties, "a{sv}", &iter); while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { if (!strcmp (key, "State")) - state_changed (self, g_variant_get_string (value, NULL)); + state_changed (self, get_variant_state (value)); if (!strcmp (key, "Scanning")) - scanning_changed (self, g_variant_get_boolean (value)); + scanning_changed (self, get_variant_boolean (value, "Scanning")); if (!strcmp (key, "Powered")) - powered_changed (self, g_variant_get_boolean (value)); + powered_changed (self, get_variant_boolean (value, "Powered")); g_variant_unref (value); } @@ -1849,12 +1856,12 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) priv->dbus_proxy = G_DBUS_PROXY (interface); value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning"); - priv->scanning = g_variant_get_boolean (value); + priv->scanning = get_variant_boolean (value, "Scanning"); g_variant_unref (value); priv->scan_requested = FALSE; value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State"); - state_changed (self, g_variant_get_string (value, NULL)); + state_changed (self, get_variant_state (value)); g_variant_unref (value); g_signal_connect (priv->dbus_proxy, "g-properties-changed", diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index 37210c99e..2f7bd1609 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -29,13 +29,21 @@ #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" /*****************************************************************************/ typedef struct { - char *name; + const char *name; NMIwdNetworkSecurity security; + char buf[0]; +} KnownNetworkId; + +typedef struct { + GDBusProxy *known_network; + NMSettingsConnection *mirror_connection; } KnownNetworkData; typedef struct { @@ -45,7 +53,7 @@ typedef struct { GDBusObjectManager *object_manager; guint agent_id; char *agent_path; - GSList *known_networks; + GHashTable *known_networks; } NMIwdManagerPrivate; struct _NMIwdManager { @@ -83,6 +91,36 @@ G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT) /*****************************************************************************/ +static void mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn); + +/*****************************************************************************/ + +static const char * +get_variant_string_or_null (GVariant *v) +{ + if (!v) + return NULL; + + if ( !g_variant_is_of_type (v, G_VARIANT_TYPE_STRING) + && !g_variant_is_of_type (v, G_VARIANT_TYPE_OBJECT_PATH)) + return NULL; + + return g_variant_get_string (v, NULL); +} + +static const char * +get_property_string_or_null (GDBusProxy *proxy, const char *property) +{ + gs_unref_variant GVariant *value = NULL; + + if (!proxy || !property) + return NULL; + + value = g_dbus_proxy_get_cached_property (proxy, property); + + return get_variant_string_or_null (value); +} + static void agent_dbus_method_cb (GDBusConnection *connection, const char *sender, const char *object_path, @@ -95,7 +133,6 @@ agent_dbus_method_cb (GDBusConnection *connection, NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); const char *network_path, *device_path, *ifname; gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL; - gs_unref_variant GVariant *value = NULL; int ifindex; NMDevice *device; gs_free char *name_owner = NULL; @@ -113,9 +150,8 @@ agent_dbus_method_cb (GDBusConnection *connection, network = g_dbus_object_manager_get_interface (priv->object_manager, network_path, NM_IWD_NETWORK_INTERFACE); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (network), "Device"); - device_path = g_variant_get_string (value, NULL); + device_path = get_property_string_or_null (G_DBUS_PROXY (network), "Device"); if (!device_path) { _LOGD ("agent-request: device not cached for network %s in IWD Agent request", network_path); @@ -125,10 +161,8 @@ agent_dbus_method_cb (GDBusConnection *connection, device_obj = g_dbus_object_manager_get_interface (priv->object_manager, device_path, NM_IWD_DEVICE_INTERFACE); - g_variant_unref (value); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (device_obj), "Name"); - ifname = g_variant_get_string (value, NULL); + ifname = get_property_string_or_null (G_DBUS_PROXY (device_obj), "Name"); if (!ifname) { _LOGD ("agent-request: name not cached for device %s in IWD Agent request", device_path); @@ -251,38 +285,68 @@ register_agent (NMIwdManager *self) /*****************************************************************************/ +static KnownNetworkId * +known_network_id_new (const char *name, NMIwdNetworkSecurity security) +{ + KnownNetworkId *id; + gsize strsize = strlen (name) + 1; + + id = g_malloc (sizeof (KnownNetworkId) + strsize); + id->name = id->buf; + id->security = security; + memcpy (id->buf, name, strsize); + + return id; +} + +static guint +known_network_id_hash (KnownNetworkId *id) +{ + NMHashState h; + + nm_hash_init (&h, 1947951703u); + nm_hash_update_val (&h, id->security); + nm_hash_update_str (&h, id->name); + return nm_hash_complete (&h); +} + +static gboolean +known_network_id_equal (KnownNetworkId *a, KnownNetworkId *b) +{ + return a->security == b->security + && nm_streq (a->name, b->name); +} + static void -set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface, +known_network_data_free (KnownNetworkData *network) +{ + if (!network) + return; + + g_object_unref (network->known_network); + mirror_8021x_connection_take_and_delete (network->mirror_connection); + g_slice_free (KnownNetworkData, network); +} + +/*****************************************************************************/ + +static void +set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, GDBusObject *object) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); - GDBusProxy *proxy; - GVariant *value; const char *ifname; int ifindex; NMDevice *device; - if (!priv->running) - return; - - g_return_if_fail (G_IS_DBUS_PROXY (interface)); - - proxy = G_DBUS_PROXY (interface); - - if (strcmp (g_dbus_proxy_get_interface_name (proxy), - NM_IWD_DEVICE_INTERFACE)) - return; - - value = g_dbus_proxy_get_cached_property (proxy, "Name"); - if (!value) { + ifname = get_property_string_or_null (proxy, "Name"); + if (!ifname) { _LOGE ("Name not cached for Device at %s", g_dbus_proxy_get_object_path (proxy)); return; } - ifname = g_variant_get_string (value, NULL); ifindex = if_nametoindex (ifname); - g_variant_unref (value); if (!ifindex) { _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", @@ -299,13 +363,183 @@ set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface, 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 NMSettingsConnection * +mirror_8021x_connection (NMIwdManager *self, + const char *name) +{ + NMSettings *settings = NM_SETTINGS_GET; + NMSettingsConnection *const*iter; + gs_unref_object NMConnection *connection = NULL; + NMSettingsConnection *settings_connection; + char uuid[37]; + NMSetting *setting; + GError *error = NULL; + gs_unref_bytes GBytes *new_ssid = NULL; + + for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) { + NMSettingsConnection *sett_conn = *iter; + NMConnection *conn = nm_settings_connection_get_connection (sett_conn); + NMIwdNetworkSecurity security; + gs_free char *ssid_name = NULL; + NMSettingWireless *s_wifi; + + security = nm_wifi_connection_get_iwd_security (conn, NULL); + if (security != NM_IWD_NETWORK_SECURITY_8021X) + continue; + + s_wifi = nm_connection_get_setting_wireless (conn); + if (!s_wifi) + continue; + + ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi)); + + /* 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 (nm_streq (ssid_name, name)) + return NULL; + } + + 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, name, + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid), + NM_SETTING_CONNECTION_READ_ONLY, TRUE, + NULL)); + nm_connection_add_setting (connection, setting); + + new_ssid = g_bytes_new (name, strlen (name)); + setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS, + NM_SETTING_WIRELESS_SSID, new_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); + + if (!nm_connection_normalize (connection, NULL, NULL, NULL)) + return NULL; + + settings_connection = nm_settings_add_connection (settings, connection, + FALSE, &error); + if (!settings_connection) { + _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", + name, error->message); + g_error_free (error); + return NULL; + } + + nm_settings_connection_set_flags (settings_connection, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, + TRUE); + return settings_connection; +} + +static void +mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn) +{ + NMSettingsConnectionIntFlags flags; + + if (!sett_conn) + return; + + flags = nm_settings_connection_get_flags (sett_conn); + + /* If connection has not been saved since we created it + * in interface_added it too can be removed now. */ + if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) + nm_settings_connection_delete (sett_conn, NULL); + + g_object_unref (sett_conn); +} + static void interface_added (GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) { NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusProxy *proxy; + const char *iface_name; - set_device_dbus_object (self, interface, object); + if (!priv->running) + return; + + g_return_if_fail (G_IS_DBUS_PROXY (interface)); + + proxy = G_DBUS_PROXY (interface); + iface_name = g_dbus_proxy_get_interface_name (proxy); + + if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) { + set_device_dbus_object (self, proxy, object); + return; + } + + if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) { + KnownNetworkId *id; + KnownNetworkData *data; + NMIwdNetworkSecurity security; + const char *type_str, *name; + NMSettingsConnection *sett_conn = NULL; + + type_str = get_property_string_or_null (proxy, "Type"); + name = get_property_string_or_null (proxy, "Name"); + if (!type_str || !name) + return; + + if (nm_streq (type_str, "open")) + security = NM_IWD_NETWORK_SECURITY_NONE; + else if (nm_streq (type_str, "psk")) + security = NM_IWD_NETWORK_SECURITY_PSK; + else if (nm_streq (type_str, "8021x")) + security = NM_IWD_NETWORK_SECURITY_8021X; + else + return; + + id = known_network_id_new (name, security); + + data = g_hash_table_lookup (priv->known_networks, id); + if (data) + g_free (id); + else { + data = g_slice_new0 (KnownNetworkData); + data->known_network = g_object_ref (proxy); + g_hash_table_insert (priv->known_networks, id, data); + } + + if (security == NM_IWD_NETWORK_SECURITY_8021X) { + sett_conn = mirror_8021x_connection (self, name); + + if ( sett_conn + && sett_conn != data->mirror_connection) { + NMSettingsConnection *sett_conn_old = data->mirror_connection; + + data->mirror_connection = nm_g_object_ref (sett_conn); + mirror_8021x_connection_take_and_delete (sett_conn_old); + } + } else + mirror_8021x_connection_take_and_delete (g_steal_pointer (&data->mirror_connection)); + + return; + } } static void @@ -313,15 +547,41 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) { NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusProxy *proxy; + const char *iface_name; - /* - * TODO: we may need to save the GDBusInterface or GDBusObject - * pointer in the hash table because we may be no longer able to - * access the Name property or map the name to ifindex with - * if_nametoindex at this point. - */ + g_return_if_fail (G_IS_DBUS_PROXY (interface)); - set_device_dbus_object (self, interface, NULL); + proxy = G_DBUS_PROXY (interface); + iface_name = g_dbus_proxy_get_interface_name (proxy); + + if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) { + set_device_dbus_object (self, proxy, NULL); + return; + } + + if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) { + KnownNetworkId id; + const char *type_str; + + type_str = get_property_string_or_null (proxy, "Type"); + id.name = get_property_string_or_null (proxy, "Name"); + if (!type_str || !id.name) + return; + + if (nm_streq (type_str, "open")) + id.security = NM_IWD_NETWORK_SECURITY_NONE; + else if (nm_streq (type_str, "psk")) + id.security = NM_IWD_NETWORK_SECURITY_PSK; + else if (nm_streq (type_str, "8021x")) + id.security = NM_IWD_NETWORK_SECURITY_8021X; + else + return; + + g_hash_table_remove (priv->known_networks, &id); + return; + } } static gboolean @@ -341,108 +601,16 @@ object_added (NMIwdManager *self, GDBusObject *object) GList *interfaces, *iter; interfaces = g_dbus_object_get_interfaces (object); + for (iter = interfaces; iter; iter = iter->next) { GDBusInterface *interface = G_DBUS_INTERFACE (iter->data); - set_device_dbus_object (self, interface, object); + interface_added (NULL, object, interface, self); } g_list_free_full (interfaces, g_object_unref); } -static void -known_network_free (KnownNetworkData *network) -{ - g_free (network->name); - g_free (network); -} - -static void -list_known_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) -{ - NMIwdManager *self = user_data; - NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *variant = NULL; - GVariantIter *networks, *props; - - variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("(aa{sv})"), - &error); - if (!variant) { - _LOGE ("ListKnownNetworks() failed: %s", error->message); - return; - } - - g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free); - priv->known_networks = NULL; - - g_variant_get (variant, "(aa{sv})", &networks); - - while (g_variant_iter_next (networks, "a{sv}", &props)) { - const char *key; - const char *name = NULL; - const char *type = NULL; - GVariant *val; - KnownNetworkData *network_data; - - while (g_variant_iter_next (props, "{&sv}", &key, &val)) { - if (!strcmp (key, "Name")) - name = g_variant_get_string (val, NULL); - - if (!strcmp (key, "Type")) - type = g_variant_get_string (val, NULL); - - g_variant_unref (val); - } - - if (!name || !type) - goto next; - - network_data = g_new (KnownNetworkData, 1); - network_data->name = g_strdup (name); - if (!strcmp (type, "open")) - network_data->security = NM_IWD_NETWORK_SECURITY_NONE; - else if (!strcmp (type, "psk")) - network_data->security = NM_IWD_NETWORK_SECURITY_PSK; - else if (!strcmp (type, "8021x")) - network_data->security = NM_IWD_NETWORK_SECURITY_8021X; - - priv->known_networks = g_slist_append (priv->known_networks, - network_data); - -next: - g_variant_iter_free (props); - } - - g_variant_iter_free (networks); - - /* For completness we may want to call nm_device_emit_recheck_auto_activate - * and nm_device_recheck_available_connections for all affected devices - * now but the ListKnownNetworks call should have been really fast, - * faster than any scan on any newly created devices could have happened. - */ -} - -static void -update_known_networks (NMIwdManager *self) -{ - NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); - GDBusInterface *known_networks_if; - - known_networks_if = g_dbus_object_manager_get_interface (priv->object_manager, - "/", - NM_IWD_KNOWN_NETWORKS_INTERFACE); - - g_dbus_proxy_call (G_DBUS_PROXY (known_networks_if), - "ListKnownNetworks", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, - priv->cancellable, list_known_networks_cb, self); - - g_object_unref (known_networks_if); -} - static void prepare_object_manager (NMIwdManager *self); static void @@ -492,28 +660,14 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data) objects = g_dbus_object_manager_get_objects (priv->object_manager); for (iter = objects; iter; iter = iter->next) { GDBusObject *object = G_DBUS_OBJECT (iter->data); - GDBusInterface *interface; - GDBusProxy *proxy; - GVariant *value; + gs_unref_object GDBusInterface *interface = NULL; const char *obj_ifname; interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE); - if (!interface) - continue; + obj_ifname = get_property_string_or_null ((GDBusProxy *) interface, "Name"); - proxy = G_DBUS_PROXY (interface); - value = g_dbus_proxy_get_cached_property (proxy, "Name"); - if (!value) { - g_object_unref (interface); - continue; - } - - obj_ifname = g_variant_get_string (value, NULL); - g_variant_unref (value); - g_object_unref (interface); - - if (strcmp (nm_device_get_iface (device), obj_ifname)) + if (!obj_ifname || strcmp (nm_device_get_iface (device), obj_ifname)) continue; nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); @@ -535,7 +689,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error); if (object_manager == NULL) { _LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)", - NM_G_ERROR_MSG (error)); + error->message); g_clear_error (&error); return; } @@ -549,11 +703,13 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); - priv->agent_id = iwd_agent_export (connection, self, - &priv->agent_path, &error); + priv->agent_id = iwd_agent_export (connection, + self, + &priv->agent_path, + &error); if (!priv->agent_id) { _LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks will not work: %s", - NM_G_ERROR_MSG (error)); + error->message); g_clear_error (&error); } @@ -567,6 +723,8 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) g_signal_connect (priv->object_manager, "interface-removed", G_CALLBACK (interface_removed), self); + g_hash_table_remove_all (priv->known_networks); + objects = g_dbus_object_manager_get_objects (object_manager); for (iter = objects; iter; iter = iter->next) object_added (self, G_DBUS_OBJECT (iter->data)); @@ -575,8 +733,6 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) if (priv->agent_id) register_agent (self); - - update_known_networks (self); } } @@ -598,32 +754,9 @@ nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name, NMIwdNetworkSecurity security) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); - const GSList *iter; + KnownNetworkId kn_id = { name, security }; - for (iter = priv->known_networks; iter; iter = g_slist_next (iter)) { - const KnownNetworkData *network = iter->data; - - if (!strcmp (network->name, name) && network->security == security) - return true; - } - - return false; -} - -void -nm_iwd_manager_network_connected (NMIwdManager *self, const char *name, - NMIwdNetworkSecurity security) -{ - NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); - KnownNetworkData *network_data; - - if (nm_iwd_manager_is_known_network (self, name, security)) - return; - - network_data = g_new (KnownNetworkData, 1); - network_data->name = g_strdup (name); - network_data->security = security; - priv->known_networks = g_slist_append (priv->known_networks, network_data); + return g_hash_table_contains (priv->known_networks, &kn_id); } /*****************************************************************************/ @@ -642,6 +775,11 @@ nm_iwd_manager_init (NMIwdManager *self) priv->cancellable = g_cancellable_new (); + priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash, + (GEqualFunc) known_network_id_equal, + g_free, + (GDestroyNotify) known_network_data_free); + prepare_object_manager (self); } @@ -673,8 +811,7 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); - g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free); - priv->known_networks = NULL; + nm_clear_pointer (&priv->known_networks, g_hash_table_destroy); if (priv->manager) { g_signal_handlers_disconnect_by_data (priv->manager, self); diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h index 6e9bff879..59b70f6e3 100644 --- a/src/devices/wifi/nm-iwd-manager.h +++ b/src/devices/wifi/nm-iwd-manager.h @@ -22,6 +22,7 @@ #define __NETWORKMANAGER_IWD_MANAGER_H__ #include "devices/nm-device.h" +#include "nm-wifi-utils.h" #define NM_IWD_BUS_TYPE G_BUS_TYPE_SYSTEM #define NM_IWD_SERVICE "net.connman.iwd" @@ -33,16 +34,9 @@ #define NM_IWD_AGENT_INTERFACE "net.connman.iwd.Agent" #define NM_IWD_WSC_INTERFACE \ "net.connman.iwd.WiFiSimpleConfiguration" -#define NM_IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks" +#define NM_IWD_KNOWN_NETWORK_INTERFACE "net.connman.iwd.KnownNetwork" #define NM_IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent" -typedef enum { - NM_IWD_NETWORK_SECURITY_NONE, - NM_IWD_NETWORK_SECURITY_WEP, - NM_IWD_NETWORK_SECURITY_PSK, - NM_IWD_NETWORK_SECURITY_8021X, -} NMIwdNetworkSecurity; - #define NM_TYPE_IWD_MANAGER (nm_iwd_manager_get_type ()) #define NM_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IWD_MANAGER, NMIwdManager)) #define NM_IWD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IWD_MANAGER, NMIwdManagerClass)) @@ -59,7 +53,5 @@ NMIwdManager *nm_iwd_manager_get (void); gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name, NMIwdNetworkSecurity security); -void nm_iwd_manager_network_connected (NMIwdManager *self, const char *name, - NMIwdNetworkSecurity security); #endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */ diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index c9806a53c..0f7836beb 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -815,3 +815,36 @@ nm_wifi_utils_is_manf_default_ssid (GBytes *ssid) } return FALSE; } + +NMIwdNetworkSecurity +nm_wifi_connection_get_iwd_security (NMConnection *connection, + gboolean *mapped) +{ + NMSettingWirelessSecurity *s_wireless_sec; + const char *key_mgmt = NULL; + + if (!nm_connection_get_setting_wireless (connection)) + goto error; + + NM_SET_OUT (mapped, TRUE); + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (!s_wireless_sec) + return NM_IWD_NETWORK_SECURITY_NONE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + nm_assert (key_mgmt); + + if (NM_IN_STRSET (key_mgmt, "none", "ieee8021x")) + return NM_IWD_NETWORK_SECURITY_WEP; + + if (nm_streq (key_mgmt, "wpa-psk")) + return NM_IWD_NETWORK_SECURITY_PSK; + + if (nm_streq (key_mgmt, "wpa-eap")) + return NM_IWD_NETWORK_SECURITY_8021X; + +error: + NM_SET_OUT (mapped, FALSE); + return NM_IWD_NETWORK_SECURITY_NONE; +} diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h index 88dc37911..03238c246 100644 --- a/src/devices/wifi/nm-wifi-utils.h +++ b/src/devices/wifi/nm-wifi-utils.h @@ -27,6 +27,13 @@ #include "nm-setting-wireless-security.h" #include "nm-setting-8021x.h" +typedef enum { + NM_IWD_NETWORK_SECURITY_NONE, + NM_IWD_NETWORK_SECURITY_WEP, + NM_IWD_NETWORK_SECURITY_PSK, + NM_IWD_NETWORK_SECURITY_8021X, +} NMIwdNetworkSecurity; + gboolean nm_wifi_utils_complete_connection (GBytes *ssid, const char *bssid, NM80211Mode mode, @@ -41,4 +48,7 @@ guint32 nm_wifi_utils_level_to_quality (int val); gboolean nm_wifi_utils_is_manf_default_ssid (GBytes *ssid); +NMIwdNetworkSecurity nm_wifi_connection_get_iwd_security (NMConnection *connection, + gboolean *mapped); + #endif /* __NM_WIFI_UTILS_H__ */ diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index a0628d74a..41d510e87 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -1001,6 +1001,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, guint32 frag, hdrs; gs_free char *frag_str = NULL; NMSetting8021xAuthFlags phase1_auth_flags; + nm_auto_free_gstring GString *eap_str = NULL; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); g_return_val_if_fail (setting != NULL, FALSE); @@ -1037,20 +1038,38 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, priv->ap_scan = 0; } - if (!ADD_STRING_LIST_VAL (self, setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, NULL, error)) - return FALSE; - - /* Check EAP method for special handling: PEAP + GTC, FAST */ + /* Build the "eap" option string while we check for EAP methods needing + * special handling: PEAP + GTC, FAST, external */ + eap_str = g_string_new (NULL); num_eap = nm_setting_802_1x_get_num_eap_methods (setting); for (i = 0; i < num_eap; i++) { const char *method = nm_setting_802_1x_get_eap_method (setting, i); - if (method && (strcasecmp (method, "fast") == 0)) { + if (nm_streq (method, "fast")) { fast = TRUE; priv->fast_required = TRUE; } + + if (nm_streq (method, "external")) { + if (num_eap == 1) { + g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, + "Connection settings managed externally to NM, connection" + " cannot be used with wpa_supplicant"); + return FALSE; + } + continue; + } + + if (eap_str->len) + g_string_append_c (eap_str, ' '); + g_string_append (eap_str, method); } + g_string_ascii_up (eap_str); + if ( eap_str->len + && !nm_supplicant_config_add_option (self, "eap", eap_str->str, -1, NULL, error)) + return FALSE; + /* Adjust the fragment size according to MTU, but do not set it higher than 1280-14 * for better compatibility */ hdrs = 14; /* EAPOL + EAP-TLS */