summary refs log tree commit diff
path: root/src/supplicant-manager/nm-supplicant-interface.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/supplicant-manager/nm-supplicant-interface.c
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/supplicant-manager/nm-supplicant-interface.c')
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c414
1 files changed, 298 insertions, 116 deletions
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index 30be23ae..9251af1f 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -23,15 +23,13 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <glib.h>
 
+#include "nm-default.h"
 #include "NetworkManagerUtils.h"
 #include "nm-supplicant-interface.h"
-#include "nm-logging.h"
 #include "nm-supplicant-config.h"
-#include "nm-glib-compat.h"
-#include "gsystem-local-alloc.h"
 #include "nm-core-internal.h"
+#include "nm-dbus-compat.h"
 
 #define WPAS_DBUS_IFACE_INTERFACE   WPAS_DBUS_INTERFACE ".Interface"
 #define WPAS_DBUS_IFACE_BSS         WPAS_DBUS_INTERFACE ".BSS"
@@ -64,6 +62,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 enum {
 	PROP_0 = 0,
 	PROP_SCANNING,
+	PROP_CURRENT_BSS,
 	LAST_PROP
 };
 
@@ -72,7 +71,8 @@ typedef struct {
 	char *         dev;
 	gboolean       is_wireless;
 	gboolean       has_credreq;  /* Whether querying 802.1x credentials is supported */
-	ApSupport      ap_support;   /* Lightweight AP mode support */
+	NMSupplicantFeature ap_support;   /* Lightweight AP mode support */
+	NMSupplicantFeature mac_randomization_support;
 	gboolean       fast_supported;
 	guint32        max_scan_ssids;
 	guint32        ready_count;
@@ -91,6 +91,7 @@ typedef struct {
 	char *         net_path;
 	guint32        blobs_left;
 	GHashTable *   bss_proxies;
+	char *         current_bss;
 
 	gint32         last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */
 
@@ -333,6 +334,17 @@ nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
 	return FALSE;
 }
 
+const char *
+nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	return priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY ? priv->current_bss : NULL;
+}
+
 gint32
 nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self)
 {
@@ -413,21 +425,10 @@ nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self,
 	                                5000,
 	                                NULL,
 	                                error);
-	/* reply will be unrefed when function exits */
-	return !!reply;
-}
-
-static gboolean
-_dbus_error_has_name (GError *error, const char *dbus_error_name)
-{
-	gs_free char *error_name = NULL;
-	gboolean is_error = FALSE;
+	if (error && *error)
+		g_dbus_error_strip_remote_error (*error);
 
-	if (error && g_dbus_error_is_remote_error (error)) {
-		error_name = g_dbus_error_get_remote_error (error);
-		is_error = !g_strcmp0 (error_name, dbus_error_name);
-	}
-	return is_error;
+	return !!reply;
 }
 
 static void
@@ -452,7 +453,7 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (variant || _dbus_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs"))
+	if (variant || _nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs"))
 		priv->has_credreq = TRUE;
 
 	nm_log_dbg (LOGD_SUPPLICANT, "Supplicant %s network credentials requests",
@@ -461,7 +462,7 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 	iface_check_ready (self);
 }
 
-ApSupport
+NMSupplicantFeature
 nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self)
 {
 	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ap_support;
@@ -469,7 +470,7 @@ nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self)
 
 void
 nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
-                                        ApSupport ap_support)
+                                        NMSupplicantFeature ap_support)
 {
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
@@ -480,8 +481,31 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
 		priv->ap_support = ap_support;
 }
 
+NMSupplicantFeature
+nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self)
+{
+	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mac_randomization_support;
+}
+
+static void
+set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_variant GVariant *variant = NULL;
+	gs_free_error GError *error = NULL;
+
+	variant = _nm_dbus_proxy_call_finish (proxy, result,
+	                                      G_VARIANT_TYPE ("()"),
+	                                      &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+	if (error)
+		nm_log_warn (LOGD_SUPPLICANT, "Failed to enable scan MAC address randomization");
+
+	iface_check_ready (NM_SUPPLICANT_INTERFACE (user_data));
+}
+
 static void
-iface_check_ap_mode_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
 	NMSupplicantInterfacePrivate *priv;
@@ -489,77 +513,115 @@ iface_check_ap_mode_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d
 	gs_free_error GError *error = NULL;
 	const char *data;
 
-	/* The ProbeRequest method only exists if AP mode has been enabled */
-	variant = g_dbus_proxy_call_finish (proxy, result, &error);
+	variant = _nm_dbus_proxy_call_finish (proxy, result,
+	                                      G_VARIANT_TYPE ("(s)"),
+	                                      &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(s)"))) {
+	if (variant) {
 		g_variant_get (variant, "(&s)", &data);
+
+		/* The ProbeRequest method only exists if AP mode has been enabled */
 		if (strstr (data, "ProbeRequest"))
-			priv->ap_support = AP_SUPPORT_YES;
+			priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
+
+		if (strstr (data, "PreassocMacAddr")) {
+			priv->mac_randomization_support = NM_SUPPLICANT_FEATURE_YES;
+
+			/* Turn on MAC randomization during scans by default */
+			priv->ready_count++;
+			g_dbus_proxy_call (priv->iface_proxy,
+			                   DBUS_INTERFACE_PROPERTIES ".Set",
+			                   g_variant_new ("(ssv)",
+			                                  WPAS_DBUS_IFACE_INTERFACE,
+			                                  "PreassocMacAddr",
+			                                  g_variant_new_string ("1")),
+			                   G_DBUS_CALL_FLAGS_NONE,
+			                   -1,
+			                   priv->init_cancellable,
+			                   (GAsyncReadyCallback) set_preassoc_scan_mac_cb,
+			                   self);
+		}
 	}
 
 	iface_check_ready (self);
 }
 
-#define MATCH_SIGNAL(s, n, v, t) (!strcmp (s, n) && g_variant_is_of_type (v, t))
-
 static void
-signal_cb (GDBusProxy  *proxy,
-           const gchar *sender,
-           const gchar *signal,
-           GVariant    *args,
-           gpointer     user_data)
+wpas_iface_scan_done (GDBusProxy *proxy,
+                      gboolean success,
+                      gpointer user_data)
 {
 	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-	const char *path, *field, *message;
-	gboolean success;
+	GVariant *props;
+	GHashTableIter iter;
+	char *bss_path;
+	GDBusProxy *bss_proxy;
 
-	if (MATCH_SIGNAL (signal, "ScanDone", args, G_VARIANT_TYPE ("(b)"))) {
-		GVariant *props;
-		GHashTableIter iter;
-		char *bss_path;
-		GDBusProxy *bss_proxy;
+	/* Cache last scan completed time */
+	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
+
+	g_signal_emit (self, signals[SCAN_DONE], 0, success);
+
+	/* Emit NEW_BSS so that wifi device has the APs (in case it removed them) */
+	g_hash_table_iter_init (&iter, priv->bss_proxies);
+	while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
+		if (g_object_get_data (G_OBJECT (bss_proxy), BSS_PROXY_INITED)) {
+			props = _get_bss_proxy_properties (self, bss_proxy);
+			if (props) {
+				g_signal_emit (self, signals[NEW_BSS], 0,
+				               bss_path,
+				               g_variant_ref_sink (props));
+				g_variant_unref (props);
+			}
+		}
+	}
+}
+
+static void
+wpas_iface_bss_added (GDBusProxy *proxy,
+                      const char *path,
+                      GVariant *props,
+                      gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-		/* Cache last scan completed time */
+	if (priv->scanning)
 		priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
 
-		g_variant_get (args, "(b)", &success);
-		g_signal_emit (self, signals[SCAN_DONE], 0, success);
-
-		/* Emit NEW_BSS so that wifi device has the APs (in case it removed them) */
-		g_hash_table_iter_init (&iter, priv->bss_proxies);
-		while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
-			if (g_object_get_data (G_OBJECT (bss_proxy), BSS_PROXY_INITED)) {
-				props = _get_bss_proxy_properties (self, bss_proxy);
-				if (props) {
-					g_signal_emit (self, signals[NEW_BSS], 0,
-					               bss_path,
-					               g_variant_ref_sink (props));
-					g_variant_unref (props);
-				}
-			}
-		}
-	} else if (MATCH_SIGNAL (signal, "BSSAdded", args, G_VARIANT_TYPE ("(oa{sv})"))) {
-		if (priv->scanning)
-			priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
+	handle_new_bss (self, path);
+}
 
-		g_variant_get (args, "(&oa{sv})", &path, NULL);
-		handle_new_bss (self, path);
-	} else if (MATCH_SIGNAL (signal, "BSSRemoved", args, G_VARIANT_TYPE ("(o)"))) {
-		g_variant_get (args, "(&o)", &path);
-		g_signal_emit (self, signals[BSS_REMOVED], 0, path);
-		g_hash_table_remove (priv->bss_proxies, path);
-	} else if (MATCH_SIGNAL (signal, "NetworkRequest", args, G_VARIANT_TYPE ("(oss)"))) {
-		g_variant_get (args, "(&o&s&s)", &path, &field, &message);
-		if (priv->has_credreq && priv->net_path && !g_strcmp0 (path, priv->net_path))
-			g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message);
-	}
+static void
+wpas_iface_bss_removed (GDBusProxy *proxy,
+                        const char *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, path);
+	g_hash_table_remove (priv->bss_proxies, path);
+}
+
+static void
+wpas_iface_network_request (GDBusProxy *proxy,
+                            const char *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);
+
+	if (priv->has_credreq && priv->net_path && !g_strcmp0 (path, priv->net_path))
+		g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message);
 }
 
 static void
@@ -575,6 +637,8 @@ props_changed_cb (GDBusProxy *proxy,
 	gint32 i32;
 	GVariant *v;
 
+	g_object_freeze_notify (G_OBJECT (self));
+
 	if (g_variant_lookup (changed_properties, "Scanning", "b", &b))
 		set_scanning (self, b);
 
@@ -595,6 +659,16 @@ props_changed_cb (GDBusProxy *proxy,
 		g_free (array);
 	}
 
+	if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) {
+		if (strcmp (s, "/") == 0)
+			s = NULL;
+		if (g_strcmp0 (s, priv->current_bss) != 0) {
+			g_free (priv->current_bss);
+			priv->current_bss = g_strdup (s);
+			g_object_notify (G_OBJECT (self), NM_SUPPLICANT_INTERFACE_CURRENT_BSS);
+		}
+	}
+
 	v = g_variant_lookup_value (changed_properties, "Capabilities", G_VARIANT_TYPE_VARDICT);
 	if (v) {
 		parse_capabilities (self, v);
@@ -614,6 +688,8 @@ props_changed_cb (GDBusProxy *proxy,
 				         priv->disconnect_reason);
 		}
 	}
+
+	g_object_thaw_notify (G_OBJECT (self));
 }
 
 static void
@@ -634,7 +710,38 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	g_signal_connect (priv->iface_proxy, "g-signal", G_CALLBACK (signal_cb), self);
+	_nm_dbus_signal_connect (priv->iface_proxy, "ScanDone", G_VARIANT_TYPE ("(b)"),
+	                         G_CALLBACK (wpas_iface_scan_done), self);
+	_nm_dbus_signal_connect (priv->iface_proxy, "BSSAdded", G_VARIANT_TYPE ("(oa{sv})"),
+	                         G_CALLBACK (wpas_iface_bss_added), self);
+	_nm_dbus_signal_connect (priv->iface_proxy, "BSSRemoved", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (wpas_iface_bss_removed), self);
+	_nm_dbus_signal_connect (priv->iface_proxy, "NetworkRequest", G_VARIANT_TYPE ("(oss)"),
+	                         G_CALLBACK (wpas_iface_network_request), self);
+
+	/* Scan result aging parameters */
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   "org.freedesktop.DBus.Properties.Set",
+	                   g_variant_new ("(ssv)",
+	                                  WPAS_DBUS_IFACE_INTERFACE,
+	                                  "BSSExpireAge",
+	                                  g_variant_new_uint32 (250)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->init_cancellable,
+	                   NULL,
+	                   NULL);
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   "org.freedesktop.DBus.Properties.Set",
+	                   g_variant_new ("(ssv)",
+	                                  WPAS_DBUS_IFACE_INTERFACE,
+	                                  "BSSExpireCount",
+	                                  g_variant_new_uint32 (2)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->init_cancellable,
+	                   NULL,
+	                   NULL);
 
 	/* Check whether NetworkReply and AP mode are supported */
 	priv->ready_count = 1;
@@ -650,7 +757,8 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 	                   (GAsyncReadyCallback) iface_check_netreply_cb,
 	                   self);
 
-	if (priv->ap_support == AP_SUPPORT_UNKNOWN) {
+	if (priv->ap_support == NM_SUPPLICANT_FEATURE_UNKNOWN ||
+	    priv->mac_randomization_support == NM_SUPPLICANT_FEATURE_UNKNOWN) {
 		/* If the global supplicant capabilities property is not present, we can
 		 * fall back to checking whether the ProbeRequest method is supported.  If
 		 * neither of these works we have no way of determining if AP mode is
@@ -658,12 +766,12 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 		 */
 		priv->ready_count++;
 		g_dbus_proxy_call (priv->iface_proxy,
-		                   "org.freedesktop.DBus.Introspectable.Introspect",
+		                   DBUS_INTERFACE_INTROSPECTABLE ".Introspect",
 		                   NULL,
 		                   G_DBUS_CALL_FLAGS_NONE,
 		                   -1,
 		                   priv->init_cancellable,
-		                   (GAsyncReadyCallback) iface_check_ap_mode_cb,
+		                   (GAsyncReadyCallback) iface_introspect_cb,
 		                   self);
 	}
 }
@@ -698,20 +806,22 @@ interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	NMSupplicantInterfacePrivate *priv;
 	gs_unref_variant GVariant *variant = NULL;
 	gs_free_error GError *error = NULL;
-	char *path;
+	const char *path;
 
-	variant = g_dbus_proxy_call_finish (proxy, result, &error);
+	variant = _nm_dbus_proxy_call_finish (proxy, result,
+	                                      G_VARIANT_TYPE ("(o)"),
+	                                      &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(o)"))) {
-		g_variant_get (variant, "(o)", &path);
+	if (variant) {
+		g_variant_get (variant, "(&o)", &path);
 		interface_add_done (self, path);
-		g_free (path);
 	} else {
+		g_dbus_error_strip_remote_error (error);
 		nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s", priv->dev, error->message);
 		set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
 	}
@@ -724,20 +834,21 @@ interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	NMSupplicantInterfacePrivate *priv;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *variant = NULL;
-	char *path;
+	const char *path;
 
-	variant = g_dbus_proxy_call_finish (proxy, result, &error);
+	variant = _nm_dbus_proxy_call_finish (proxy, result,
+	                                      G_VARIANT_TYPE ("(o)"),
+	                                      &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(o)"))) {
-		g_variant_get (variant, "(o)", &path);
+	if (variant) {
+		g_variant_get (variant, "(&o)", &path);
 		interface_add_done (self, path);
-		g_free (path);
-	} else if (_dbus_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) {
+	} else if (_nm_dbus_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) {
 		/* Interface already added, just get its object path */
 		g_dbus_proxy_call (priv->wpas_proxy,
 		                   "GetInterface",
@@ -759,10 +870,12 @@ interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 		 * activation.  Wait for it to start by moving back to the INIT
 		 * state.
 		 */
+		g_dbus_error_strip_remote_error (error);
 		nm_log_dbg (LOGD_SUPPLICANT, "(%s): failed to activate supplicant: %s",
 		            priv->dev, error->message);
 		set_state (self, NM_SUPPLICANT_INTERFACE_STATE_INIT);
 	} else {
+		g_dbus_error_strip_remote_error (error);
 		nm_log_err (LOGD_SUPPLICANT, "(%s): error adding interface: %s", priv->dev, error->message);
 		set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
 	}
@@ -877,8 +990,12 @@ log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	gs_free_error GError *error = NULL;
 
 	reply = g_dbus_proxy_call_finish (proxy, result, &error);
-	if (!reply && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		nm_log_warn (LOGD_SUPPLICANT, "Failed to %s: %s.", error->message, (char *) user_data);
+	if (   !reply
+	    && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
+	    && !strstr (error->message, "fi.w1.wpa_supplicant1.NotConnected")) {
+		g_dbus_error_strip_remote_error (error);
+		nm_log_warn (LOGD_SUPPLICANT, "Failed to %s: %s.", (char *) user_data, error->message);
+	}
 }
 
 void
@@ -936,6 +1053,7 @@ select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 
 	reply = g_dbus_proxy_call_finish (proxy, result, &err);
 	if (!reply && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_dbus_error_strip_remote_error (err);
 		nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message);
 		emit_error_helper (NM_SUPPLICANT_INTERFACE (user_data), err);
 	}
@@ -978,6 +1096,7 @@ add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	if (reply)
 		call_select_network (self);
 	else {
+		g_dbus_error_strip_remote_error (err);
 		nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message);
 		emit_error_helper (self, err);
 	}
@@ -995,30 +1114,26 @@ add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	const char *blob_name;
 	GByteArray *blob_data;
 
-	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+	reply = _nm_dbus_proxy_call_finish (proxy, result,
+	                                    G_VARIANT_TYPE ("(o)"),
+	                                    &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (reply && !g_variant_is_of_type (reply, G_VARIANT_TYPE ("(o)"))) {
-		error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		                     "Unexpected AddNetwork reply type %s",
-		                     g_variant_get_type_string (reply));
-	}
-
 	g_free (priv->net_path);
 	priv->net_path = NULL;
 
 	if (error) {
+		g_dbus_error_strip_remote_error (error);
 		nm_log_warn (LOGD_SUPPLICANT, "Adding network to supplicant failed: %s.", error->message);
 		emit_error_helper (self, error);
 		return;
 	}
 
 	g_variant_get (reply, "(o)", &priv->net_path);
-	g_assert (priv->net_path);
 
 	/* Send blobs first; otherwise jump to selecting the network */
 	blobs = nm_supplicant_config_get_blobs (priv->cfg);
@@ -1043,6 +1158,51 @@ add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 }
 
 static void
+add_network (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   "AddNetwork",
+	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->assoc_cancellable,
+	                   (GAsyncReadyCallback) add_network_cb,
+	                   self);
+}
+
+static void
+set_mac_randomization_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	NMSupplicantInterface *self;
+	NMSupplicantInterfacePrivate *priv;
+	gs_unref_variant GVariant *reply = NULL;
+	gs_free_error GError *error = NULL;
+
+	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_SUPPLICANT_INTERFACE (user_data);
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	if (!reply) {
+		g_dbus_error_strip_remote_error (error);
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't send MAC randomization mode to "
+		             "the supplicant interface: %s.",
+		             error->message);
+		emit_error_helper (self, error);
+		return;
+	}
+
+	nm_log_info (LOGD_SUPPLICANT, "Config: set MAC randomization to %s",
+	             nm_supplicant_config_get_mac_randomization (priv->cfg));
+
+	add_network (self);
+}
+
+static void
 set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
@@ -1058,6 +1218,7 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
 	if (!reply) {
+		g_dbus_error_strip_remote_error (error);
 		nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.",
 		             error->message);
 		emit_error_helper (self, error);
@@ -1067,19 +1228,30 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d",
 	             nm_supplicant_config_get_ap_scan (priv->cfg));
 
-	g_dbus_proxy_call (priv->iface_proxy,
-	                   "AddNetwork",
-	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   priv->assoc_cancellable,
-	                   (GAsyncReadyCallback) add_network_cb,
-	                   self);
+	if (priv->mac_randomization_support == NM_SUPPLICANT_FEATURE_YES) {
+		const char *mac_randomization = nm_supplicant_config_get_mac_randomization (priv->cfg);
+
+		/* Enable/disable association MAC address randomization */
+		g_dbus_proxy_call (priv->iface_proxy,
+		                   DBUS_INTERFACE_PROPERTIES ".Set",
+		                   g_variant_new ("(ssv)",
+		                                  WPAS_DBUS_IFACE_INTERFACE,
+		                                  "MacAddr",
+		                                  g_variant_new_string (mac_randomization)),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->assoc_cancellable,
+		                   (GAsyncReadyCallback) set_mac_randomization_cb,
+		                   self);
+	} else {
+		add_network (self);
+	}
 }
 
 gboolean
 nm_supplicant_interface_set_config (NMSupplicantInterface *self,
-                                    NMSupplicantConfig *cfg)
+                                    NMSupplicantConfig *cfg,
+                                    GError **error)
 {
 	NMSupplicantInterfacePrivate *priv;
 
@@ -1093,7 +1265,8 @@ nm_supplicant_interface_set_config (NMSupplicantInterface *self,
 	 * 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");
+		g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
+		             "EAP-FAST is not supported by the supplicant");
 		return FALSE;
 	}
 
@@ -1101,7 +1274,7 @@ nm_supplicant_interface_set_config (NMSupplicantInterface *self,
 	if (cfg) {
 		priv->cfg = g_object_ref (cfg);
 		g_dbus_proxy_call (priv->iface_proxy,
-		                   "org.freedesktop.DBus.Properties.Set",
+		                   DBUS_INTERFACE_PROPERTIES ".Set",
 		                   g_variant_new ("(ssv)",
 		                                  WPAS_DBUS_IFACE_INTERFACE,
 		                                  "ApScan",
@@ -1258,7 +1431,7 @@ NMSupplicantInterface *
 nm_supplicant_interface_new (const char *ifname,
                              gboolean is_wireless,
                              gboolean fast_supported,
-                             ApSupport ap_support,
+                             NMSupplicantFeature ap_support,
                              gboolean start_now)
 {
 	NMSupplicantInterface *self;
@@ -1295,11 +1468,7 @@ set_property (GObject *object,
               const GValue *value,
               GParamSpec *pspec)
 {
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static void
@@ -1308,9 +1477,14 @@ get_property (GObject *object,
               GValue *value,
               GParamSpec *pspec)
 {
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+
 	switch (prop_id) {
 	case PROP_SCANNING:
-		g_value_set_boolean (value, NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object)->scanning);
+		g_value_set_boolean (value, priv->scanning);
+		break;
+	case PROP_CURRENT_BSS:
+		g_value_set_string (value, priv->current_bss);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1341,6 +1515,7 @@ dispose (GObject *object)
 	g_clear_pointer (&priv->net_path, g_free);
 	g_clear_pointer (&priv->dev, g_free);
 	g_clear_pointer (&priv->object_path, g_free);
+	g_clear_pointer (&priv->current_bss, g_free);
 
 	g_clear_object (&priv->cfg);
 
@@ -1367,6 +1542,13 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 		                       G_PARAM_READABLE |
 		                       G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property
+		(object_class, PROP_CURRENT_BSS,
+		 g_param_spec_string (NM_SUPPLICANT_INTERFACE_CURRENT_BSS, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
 	/* Signals */
 	signals[STATE] =
 		g_signal_new (NM_SUPPLICANT_INTERFACE_STATE,
@@ -1398,7 +1580,7 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 		              G_SIGNAL_RUN_LAST,
 		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, bss_updated),
 		              NULL, NULL, NULL,
-		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
 
 	signals[BSS_REMOVED] =
 		g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_REMOVED,