summary refs log tree commit diff
path: root/src/supplicant-manager/nm-supplicant-interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplicant-manager/nm-supplicant-interface.c')
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c353
1 files changed, 317 insertions, 36 deletions
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index 857cde51..f8ad8f18 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -15,10 +15,11 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2006 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2012 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
+#include <config.h>
 #include <stdio.h>
 #include <string.h>
 #include <glib.h>
@@ -55,11 +56,14 @@ static void wpas_iface_scan_done (DBusGProxy *proxy,
 
 /* Signals */
 enum {
-	STATE,             /* change in the interface's state */
-	REMOVED,           /* interface was removed by the supplicant */
-	NEW_BSS,           /* interface saw a new access point from a scan */
-	SCAN_DONE,         /* wifi scan is complete */
-	CONNECTION_ERROR,  /* an error occurred during a connection request */
+	STATE,               /* change in the interface's state */
+	REMOVED,             /* interface was removed by the supplicant */
+	NEW_BSS,             /* interface saw a new access point from a scan */
+	BSS_UPDATED,         /* a BSS property changed */
+	BSS_REMOVED,         /* supplicant removed BSS from its scan list */
+	SCAN_DONE,           /* wifi scan is complete */
+	CONNECTION_ERROR,    /* an error occurred during a connection request */
+	CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
 	LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -79,6 +83,8 @@ typedef struct {
 	NMDBusManager *       dbus_mgr;
 	char *                dev;
 	gboolean              is_wireless;
+	gboolean              has_credreq;  /* Whether querying 802.1x credentials is supported */
+	gboolean              fast_supported;
 
 	char *                object_path;
 	guint32               state;
@@ -92,6 +98,7 @@ typedef struct {
 	DBusGProxy *          props_proxy;
 	char *                net_path;
 	guint32               blobs_left;
+	GHashTable *          bss_proxies;
 
 	guint32               last_scan;
 
@@ -100,6 +107,28 @@ typedef struct {
 	gboolean              disposed;
 } NMSupplicantInterfacePrivate;
 
+/* FIXME: remove this and just store the standard D-Bus properties
+ * proxy object in bss_proxies when we drop support for wpa_supplicant
+ * 0.7.x.
+ */
+typedef struct {
+	/* Proxy for standard D-Bus Properties interface */
+	DBusGProxy *props;
+	/* Proxy for old wpa_supplicant-specific PropertiesChanged signal */
+	DBusGProxy *old_props;
+} BssProxies;
+
+static void
+bss_proxies_free (gpointer data)
+{
+	BssProxies *proxies = data;
+
+	g_object_unref (proxies->props);
+	g_object_unref (proxies->old_props);
+	memset (proxies, 0, sizeof (*proxies));
+	g_free (proxies);
+}
+
 static gboolean
 cancel_all_cb (GObject *object, gpointer call_id, gpointer user_data)
 {
@@ -192,6 +221,14 @@ emit_error_helper (NMSupplicantInterface *self,
 }
 
 static void
+signal_new_bss (NMSupplicantInterface *self,
+                const char *object_path,
+                GHashTable *props)
+{
+	g_signal_emit (self, signals[NEW_BSS], 0, object_path, props);
+}
+
+static void
 bssid_properties_cb  (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
 {
 	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
@@ -201,7 +238,7 @@ bssid_properties_cb  (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_
 	if (dbus_g_proxy_end_call (proxy, call_id, &error,
 	                           DBUS_TYPE_G_MAP_OF_VARIANT, &props,
 	                           G_TYPE_INVALID)) {
-		g_signal_emit (info->interface, signals[NEW_BSS], 0, props);
+		signal_new_bss (info->interface, dbus_g_proxy_get_path (proxy), props);
 		g_hash_table_destroy (props);
 	} else {
 		if (!strstr (error->message, "The BSSID requested was invalid")) {
@@ -213,31 +250,93 @@ bssid_properties_cb  (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_
 }
 
 static void
-request_bss_properties (NMSupplicantInterface *self,
-                        GPtrArray *paths)
+bss_properties_changed (DBusGProxy *proxy,
+                        const char *interface,
+                        GHashTable *props,
+                        const char **unused,
+                        gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+
+	if (g_strcmp0 (interface, WPAS_DBUS_IFACE_BSS) == 0)
+		g_signal_emit (self, signals[BSS_UPDATED], 0, dbus_g_proxy_get_path (proxy), props);
+}
+
+static void
+old_bss_properties_changed (DBusGProxy *proxy,
+                            GHashTable *props,
+                            gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+
+	g_signal_emit (self, signals[BSS_UPDATED], 0, dbus_g_proxy_get_path (proxy), props);
+}
+
+static void
+handle_new_bss (NMSupplicantInterface *self,
+                const char *object_path,
+                GHashTable *props)
 {
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-	int i;
-
-	/* Fire off a "properties" call for each returned BSSID */
-	for (i = 0; i < paths->len; i++) {
-		NMSupplicantInfo *info;
-		DBusGProxy *proxy;
-		DBusGProxyCall *call;
-
-		proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
-			                               WPAS_DBUS_SERVICE,
-			                               g_ptr_array_index (paths, i),
-			                               DBUS_INTERFACE_PROPERTIES);
-		info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
-		call = dbus_g_proxy_begin_call (proxy, "GetAll",
-			                            bssid_properties_cb,
-			                            info,
-			                            nm_supplicant_info_destroy,
-			                            G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
-			                            G_TYPE_INVALID);
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+	BssProxies *proxies;
+
+	g_return_if_fail (object_path != NULL);
+
+	if (g_hash_table_lookup (priv->bss_proxies, object_path))
+		return;
+
+	proxies = g_malloc0 (sizeof (*proxies));
+	proxies->props = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                            WPAS_DBUS_SERVICE,
+	                                            object_path,
+	                                            DBUS_INTERFACE_PROPERTIES);
+	proxies->old_props = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                                WPAS_DBUS_SERVICE,
+	                                                object_path,
+	                                                WPAS_DBUS_IFACE_BSS);
+	g_hash_table_insert (priv->bss_proxies,
+	                     (gpointer) dbus_g_proxy_get_path (proxies->props),
+	                     proxies);
+
+	/* Standard D-Bus PropertiesChanged signal */
+	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED_BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (proxies->props, "PropertiesChanged",
+	                         G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (proxies->props, "PropertiesChanged",
+	                             G_CALLBACK (bss_properties_changed),
+	                             self, NULL);
+
+	/* Old wpa_supplicant-specific PropertiesChanged signal; since it's using
+	 * a different interface, we have to use a different DBusGProxy
+	 */
+	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+	                                   G_TYPE_NONE,
+	                                   DBUS_TYPE_G_MAP_OF_VARIANT,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (proxies->old_props, "PropertiesChanged",
+	                         DBUS_TYPE_G_MAP_OF_VARIANT,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (proxies->old_props, "PropertiesChanged",
+	                             G_CALLBACK (old_bss_properties_changed),
+	                             self, NULL);
+
+	if (props) {
+		signal_new_bss (self, object_path, props);
+	} else {
+		info = nm_supplicant_info_new (self, proxies->props, priv->other_pcalls);
+		call = dbus_g_proxy_begin_call (proxies->props, "GetAll",
+		                                bssid_properties_cb,
+		                                info,
+		                                nm_supplicant_info_destroy,
+		                                G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
+		                                G_TYPE_INVALID);
 		nm_supplicant_info_set_call (info, call);
-		g_object_unref (proxy);
 	}
 }
 
@@ -247,7 +346,20 @@ wpas_iface_bss_added (DBusGProxy *proxy,
                       GHashTable *props,
                       gpointer user_data)
 {
-	g_signal_emit (NM_SUPPLICANT_INTERFACE (user_data), signals[NEW_BSS], 0, props);
+	handle_new_bss (NM_SUPPLICANT_INTERFACE (user_data), object_path, props);
+}
+
+static void
+wpas_iface_bss_removed (DBusGProxy *proxy,
+                        const char *object_path,
+                        gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	g_signal_emit (self, signals[BSS_REMOVED], 0, object_path);
+
+	g_hash_table_remove (priv->bss_proxies, object_path);
 }
 
 static int
@@ -318,6 +430,10 @@ set_state (NMSupplicantInterface *self, guint32 new_state)
 			                                "BSSAdded",
 			                                G_CALLBACK (wpas_iface_bss_added),
 			                                self);
+			dbus_g_proxy_disconnect_signal (priv->iface_proxy,
+			                                "BSSRemoved",
+			                                G_CALLBACK (wpas_iface_bss_removed),
+			                                self);
 		}
 	}
 
@@ -403,8 +519,13 @@ wpas_iface_properties_changed (DBusGProxy *proxy,
 		set_state_from_string (self, g_value_get_string (value));
 
 	value = g_hash_table_lookup (props, "BSSs");
-	if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
-		request_bss_properties (self, g_value_get_boxed (value));
+	if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) {
+		GPtrArray *paths = g_value_get_boxed (value);
+		int i;
+
+		for (i = 0; paths && (i < paths->len); i++)
+			handle_new_bss (self, g_ptr_array_index (paths, i), NULL);
+	}
 }
 
 static void
@@ -443,6 +564,92 @@ wpas_iface_get_props (NMSupplicantInterface *self)
 	nm_supplicant_info_set_call (info, call);
 }
 
+gboolean
+nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self,
+                                           const char *field,
+                                           const char *value,
+                                           GError **error)
+{
+	NMSupplicantInterfacePrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+	g_return_val_if_fail (field != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->has_credreq == TRUE, FALSE);
+
+	/* Need a network block object path */
+	g_return_val_if_fail (priv->net_path, FALSE);
+	return dbus_g_proxy_call_with_timeout (priv->iface_proxy, "NetworkReply",
+	                                       5000,
+	                                       error,
+	                                       DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
+	                                       G_TYPE_STRING, field,
+	                                       G_TYPE_STRING, value,
+	                                       G_TYPE_INVALID);
+}
+
+static void
+wpas_iface_network_request (DBusGProxy *proxy,
+                            const char *object_path,
+                            const char *field,
+                            const char *message,
+                            gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->has_credreq == TRUE);
+	g_return_if_fail (priv->net_path != NULL);
+	g_return_if_fail (g_strcmp0 (object_path, priv->net_path) == 0);
+
+	g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message);
+}
+
+static void
+iface_check_netreply_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GError *error = NULL;
+
+	if (   dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)
+	    || dbus_g_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs")) {
+		/* We know NetworkReply is supported if the NetworkReply method returned
+		 * successfully (which is unexpected since we sent a bogus network
+		 * object path) or if we got an "InvalidArgs" (which indicates NetworkReply
+		 * is supported).  We know it's not supported if we get an
+		 * "UnknownMethod" error.
+		 */
+		priv->has_credreq = TRUE;
+
+		nm_log_dbg (LOGD_SUPPLICANT, "Supplicant %s network credentials requests",
+			        priv->has_credreq ? "supports" : "does not support");
+	}
+	g_clear_error (&error);
+}
+
+static void
+wpas_iface_check_network_reply (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+
+	info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "NetworkReply",
+	                                iface_check_netreply_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                DBUS_TYPE_G_OBJECT_PATH, "/foobaraasdfasdf",
+	                                G_TYPE_STRING, "foobar",
+	                                G_TYPE_STRING, "foobar",
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+}
+
 static void
 interface_add_done (NMSupplicantInterface *self, char *path)
 {
@@ -486,6 +693,30 @@ interface_add_done (NMSupplicantInterface *self, char *path)
 	                             self,
 	                             NULL);
 
+	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+	                                   G_TYPE_NONE,
+	                                   DBUS_TYPE_G_OBJECT_PATH,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->iface_proxy, "BSSRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSRemoved",
+	                             G_CALLBACK (wpas_iface_bss_removed),
+	                             self,
+	                             NULL);
+
+	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
+	                                   G_TYPE_NONE,
+	                                   DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->iface_proxy, "NetworkRequest",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->iface_proxy, "NetworkRequest",
+	                             G_CALLBACK (wpas_iface_network_request),
+	                             self,
+	                             NULL);
+
 	priv->props_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
 	                                               WPAS_DBUS_SERVICE,
 	                                               path,
@@ -493,6 +724,9 @@ interface_add_done (NMSupplicantInterface *self, char *path)
 	/* Get initial properties */
 	wpas_iface_get_props (self);
 
+	/* Check whether NetworkReply is supported */
+	wpas_iface_check_network_reply (self);
+
 	set_state (self, NM_SUPPLICANT_INTERFACE_STATE_READY);
 }
 
@@ -574,6 +808,12 @@ interface_add_cb (DBusGProxy *proxy,
 	}
 }
 
+#if HAVE_WEXT
+#define DEFAULT_WIFI_DRIVER "nl80211,wext"
+#else
+#define DEFAULT_WIFI_DRIVER "nl80211"
+#endif
+
 static void
 interface_add (NMSupplicantInterface *self, gboolean is_wireless)
 {
@@ -602,7 +842,7 @@ interface_add (NMSupplicantInterface *self, gboolean is_wireless)
 
 	driver = g_new0 (GValue, 1);
 	g_value_init (driver, G_TYPE_STRING);
-	g_value_set_string (driver, is_wireless ? "nl80211,wext" : "wired");
+	g_value_set_string (driver, is_wireless ? DEFAULT_WIFI_DRIVER : "wired");
 	g_hash_table_insert (hash, "Driver", driver);
 
 	ifname = g_new0 (GValue, 1);
@@ -851,7 +1091,15 @@ nm_supplicant_interface_set_config (NMSupplicantInterface * self,
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
 	nm_supplicant_interface_disconnect (self);
-	
+
+	/* Make sure the supplicant supports EAP-FAST before trying to send
+	 * it an EAP-FAST configuration.
+	 */
+	if (nm_supplicant_config_fast_required (cfg) && !priv->fast_supported) {
+		nm_log_warn (LOGD_SUPPLICANT, "EAP-FAST is not supported by the supplicant");
+		return FALSE;
+	}
+
 	if (priv->cfg)
 		g_object_unref (priv->cfg);
 	priv->cfg = cfg;
@@ -1017,6 +1265,7 @@ NMSupplicantInterface *
 nm_supplicant_interface_new (NMSupplicantManager *smgr,
                              const char *ifname,
                              gboolean is_wireless,
+                             gboolean fast_supported,
                              gboolean start_now)
 {
 	NMSupplicantInterface *self;
@@ -1039,6 +1288,7 @@ nm_supplicant_interface_new (NMSupplicantManager *smgr,
 
 		priv->dev = g_strdup (ifname);
 		priv->is_wireless = is_wireless;
+		priv->fast_supported = fast_supported;
 
 		if (start_now)
 			interface_add (self, priv->is_wireless);
@@ -1063,6 +1313,8 @@ nm_supplicant_interface_init (NMSupplicantInterface * self)
 	                                              WPAS_DBUS_SERVICE,
 	                                              WPAS_DBUS_PATH,
 	                                              WPAS_DBUS_INTERFACE);
+
+	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bss_proxies_free);
 }
 
 static void
@@ -1123,6 +1375,8 @@ dispose (GObject *object)
 	if (priv->wpas_proxy)
 		g_object_unref (priv->wpas_proxy);
 
+	g_hash_table_destroy (priv->bss_proxies);
+
 	if (priv->smgr) {
 		if (priv->smgr_avail_id)
 			g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
@@ -1187,8 +1441,26 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 		              G_SIGNAL_RUN_LAST,
 		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, new_bss),
 		              NULL, NULL,
-		              g_cclosure_marshal_VOID__POINTER,
-		              G_TYPE_NONE, 1, G_TYPE_POINTER);
+		              _nm_marshal_VOID__STRING_POINTER,
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+
+	signals[BSS_UPDATED] =
+		g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, bss_updated),
+		              NULL, NULL,
+		              _nm_marshal_VOID__STRING_POINTER,
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+
+	signals[BSS_REMOVED] =
+		g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_REMOVED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, bss_removed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__STRING,
+		              G_TYPE_NONE, 1, G_TYPE_STRING);
 
 	signals[SCAN_DONE] =
 		g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE,
@@ -1207,5 +1479,14 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 		              NULL, NULL,
 		              _nm_marshal_VOID__STRING_STRING,
 		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
+	signals[CREDENTIALS_REQUEST] =
+		g_signal_new (NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, credentials_request),
+		              NULL, NULL,
+		              _nm_marshal_VOID__STRING_STRING,
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 }