summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2011-08-23 19:16:47 +0200
committerMichael Biebl <biebl@debian.org>2011-08-23 19:16:47 +0200
commit263bf4c0c89bb88dc995acd9a6a2de9095fbd461 (patch)
tree7224326afe367409e7150e49fad9029f81fe24b8 /src
parentd465e5fac63f36bcf4069e36827f4b62c494556d (diff)
Imported Upstream version 0.9.0 upstream/0.9.0
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManager.conf13
-rw-r--r--src/NetworkManagerUtils.c30
-rw-r--r--src/NetworkManagerUtils.h1
-rw-r--r--src/dns-manager/nm-dns-manager.c19
-rw-r--r--src/modem-manager/nm-modem.c20
-rw-r--r--src/nm-device-ethernet.c42
-rw-r--r--src/nm-device-olpc-mesh.c75
-rw-r--r--src/nm-device-wifi.c317
-rw-r--r--src/nm-device.c46
-rw-r--r--src/nm-manager-auth.c111
-rw-r--r--src/nm-manager-auth.h6
-rw-r--r--src/nm-manager.c133
-rw-r--r--src/nm-policy.c160
-rw-r--r--src/nm-udev-manager.c3
-rw-r--r--src/settings/nm-agent-manager.c191
-rw-r--r--src/settings/nm-agent-manager.h7
-rw-r--r--src/settings/nm-secret-agent.c102
-rw-r--r--src/settings/nm-secret-agent.h9
-rw-r--r--src/settings/nm-settings-connection.c363
-rw-r--r--src/settings/nm-settings-connection.h12
-rw-r--r--src/settings/nm-settings.c28
-rw-r--r--src/settings/nm-settings.h6
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c45
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c24
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c167
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c129
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c41
-rw-r--r--src/settings/plugins/ifnet/plugin.c3
-rw-r--r--src/settings/plugins/keyfile/common.h3
-rw-r--r--src/settings/plugins/keyfile/plugin.c3
-rw-r--r--src/settings/plugins/keyfile/reader.c67
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.am5
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.in5
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID11
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob22
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing22
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c299
-rw-r--r--src/settings/plugins/keyfile/utils.c7
-rw-r--r--src/settings/plugins/keyfile/writer.c10
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c24
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c30
-rw-r--r--src/supplicant-manager/nm-supplicant-settings-verify.c4
-rw-r--r--src/supplicant-manager/tests/test-supplicant-config.c4
-rw-r--r--src/tests/test-dhcp-options.c4
-rw-r--r--src/vpn-manager/nm-vpn-connection.c170
46 files changed, 2161 insertions, 634 deletions
diff --git a/src/NetworkManager.conf b/src/NetworkManager.conf
index b256a444..15b11b7e 100644
--- a/src/NetworkManager.conf
+++ b/src/NetworkManager.conf
@@ -10,6 +10,19 @@
                        send_interface="org.freedesktop.NetworkManager.PPP"/>
 
                 <allow send_interface="org.freedesktop.NetworkManager.SecretAgent"/>
+
+                <!-- Allow NM to talk to known VPN plugins; due to a bug in
+                     the D-Bus daemon, when a plugin is installed and the user
+                     immediately tries to use it, the VPN plugin's rules aren't
+                     always loaded into dbus-daemon.  Those rules allow NM to
+                     talk to the plugin.  Oops.  Work around that by explicitly
+                     allowing NM to talk to VPN plugins here.
+                  -->
+                <allow send_destination="org.freedesktop.NetworkManager.openconnect"/>
+                <allow send_destination="org.freedesktop.NetworkManager.openswan"/>
+                <allow send_destination="org.freedesktop.NetworkManager.openvpn"/>
+                <allow send_destination="org.freedesktop.NetworkManager.pptp"/>
+                <allow send_destination="org.freedesktop.NetworkManager.vpnc"/>
         </policy>
         <policy at_console="true">
                 <allow send_destination="org.freedesktop.NetworkManager"/>
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 5a6a61b6..f96f2aad 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -39,6 +39,9 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-manager-auth.h"
 
 #include <netlink/addr.h>
 #include <netinet/in.h>
@@ -709,6 +712,33 @@ nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
 	return FALSE;
 }
 
+const char *
+nm_utils_get_shared_wifi_permission (NMConnection *connection)
+{
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSettingIP4Config *s_ip4;
+	const char *method = NULL;
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4)
+		method = nm_setting_ip4_config_get_method (s_ip4);
+
+	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0)
+		return NULL;  /* Not shared */
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	if (s_wifi) {
+		s_wsec = nm_connection_get_setting_wireless_security (connection);
+		if (nm_setting_wireless_get_security (s_wifi) || s_wsec)
+			return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
+		else
+			return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
+	}
+
+	return NULL;
+}
+
 /*********************************/
 
 static void
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index aaa267fb..1bf741ed 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -52,6 +52,7 @@ void nm_utils_call_dispatcher (const char *action,
 gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
 gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
 
+const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);
 
 GHashTable *value_hash_create          (void);
 void        value_hash_add             (GHashTable *hash,
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index fc582327..49cd74e5 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -165,7 +165,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 }
 
 static void
-merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
+merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
 {
 	guint32 num, i;
 
@@ -181,8 +181,15 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
 			if (inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, INET_ADDRSTRLEN) > 0)
 				add_string_item (rc->nameservers, buf);
 		} else {
-			if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0)
-				add_string_item (rc->nameservers, buf);
+			if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) {
+				if (IN6_IS_ADDR_LINKLOCAL (addr) && strchr (buf, '%') == NULL) {
+					char *tmp;
+					tmp = g_strdup_printf ("%s%%%s", buf, iface);
+					add_string_item (rc->nameservers, tmp);
+					g_free (tmp);
+				} else
+					add_string_item (rc->nameservers, buf);
+			}
 		}
 	}
 
@@ -594,9 +601,9 @@ update_dns (NMDnsManager *self,
 		merge_one_ip4_config (&rc, priv->ip4_device_config);
 
 	if (priv->ip6_vpn_config)
-		merge_one_ip6_config (&rc, priv->ip6_vpn_config);
+		merge_one_ip6_config (&rc, priv->ip6_vpn_config, iface);
 	if (priv->ip6_device_config)
-		merge_one_ip6_config (&rc, priv->ip6_device_config);
+		merge_one_ip6_config (&rc, priv->ip6_device_config, iface);
 
 	for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
 		if (   (iter->data == priv->ip4_vpn_config)
@@ -612,7 +619,7 @@ update_dns (NMDnsManager *self,
 		} else if (NM_IS_IP6_CONFIG (iter->data)) {
 			NMIP6Config *config = NM_IP6_CONFIG (iter->data);
 
-			merge_one_ip6_config (&rc, config);
+			merge_one_ip6_config (&rc, config, iface);
 		} else
 			g_assert_not_reached ();
 	}
diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c
index 33f10789..1823e5e6 100644
--- a/src/modem-manager/nm-modem.c
+++ b/src/modem-manager/nm-modem.c
@@ -86,6 +86,17 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static void
+update_mm_enabled (NMModem *self, gboolean new_enabled)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (priv->mm_enabled != new_enabled) {
+		priv->mm_enabled = new_enabled;
+		g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
+	}
+}
+
 gboolean
 nm_modem_get_mm_enabled (NMModem *self)
 {
@@ -822,8 +833,7 @@ get_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_d
 	}
 
 	if (G_VALUE_HOLDS_BOOLEAN (&value)) {
-		NM_MODEM_GET_PRIVATE (self)->mm_enabled = g_value_get_boolean (&value);
-		g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
+		update_mm_enabled (self, g_value_get_boolean (&value));
 	} else
 		nm_log_warn (LOGD_MB, "failed get modem enabled state: unexpected reply type");
 
@@ -880,6 +890,9 @@ nm_modem_set_mm_enabled (NMModem *self, gboolean enabled)
 		                         self, NULL,
 		                         G_TYPE_BOOLEAN, enabled,
 		                         G_TYPE_INVALID);
+		/* If we are disabling the modem, stop saying that it's enabled. */
+		if (!enabled)
+			update_mm_enabled (self, enabled);
 	}
 }
 
@@ -898,8 +911,7 @@ modem_properties_changed (DBusGProxy *proxy,
 
 	value = g_hash_table_lookup (props, "Enabled");
 	if (value && G_VALUE_HOLDS_BOOLEAN (value)) {
-		priv->mm_enabled = g_value_get_boolean (value);
-		g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
+		update_mm_enabled (self, g_value_get_boolean (value));
 	}
 
 	value = g_hash_table_lookup (props, "IpMethod");
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
index f470e090..35b73670 100644
--- a/src/nm-device-ethernet.c
+++ b/src/nm-device-ethernet.c
@@ -34,6 +34,7 @@
 #include <unistd.h>
 #include <linux/if.h>
 #include <errno.h>
+#include <netinet/ether.h>
 
 #include <gudev/gudev.h>
 
@@ -881,6 +882,8 @@ real_get_best_auto_connection (NMDevice *dev,
 		NMSettingWired *s_wired;
 		const char *connection_type;
 		gboolean is_pppoe = FALSE;
+		const GSList *mac_blacklist, *mac_blacklist_iter;
+		gboolean mac_blacklist_found = FALSE;
 
 		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
 		g_assert (s_con);
@@ -909,6 +912,25 @@ real_get_best_auto_connection (NMDevice *dev,
 			mac = nm_setting_wired_get_mac_address (s_wired);
 			if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
 				continue;
+
+			/* Check for MAC address blacklist */
+			mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
+			for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
+			     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
+				struct ether_addr addr;
+
+				if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+					g_warn_if_reached ();
+					continue;
+				}
+				if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) {
+					mac_blacklist_found = TRUE;
+					break;
+				}
+			}
+			/* Found device MAC address in the blacklist - do not use this connection */
+			if (mac_blacklist_found)
+				continue;
 		}
 
 		return connection;
@@ -1605,6 +1627,7 @@ real_check_connection_compatible (NMDevice *device,
 	gboolean is_pppoe = FALSE;
 	const GByteArray *mac;
 	gboolean try_mac = TRUE;
+	const GSList *mac_blacklist, *mac_blacklist_iter;
 
 	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
 	g_assert (s_con);
@@ -1645,6 +1668,25 @@ real_check_connection_compatible (NMDevice *device,
 			             "The connection's MAC address did not match this device.");
 			return FALSE;
 		}
+
+		/* Check for MAC address blacklist */
+		mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
+		for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
+		     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
+			struct ether_addr addr;
+
+			if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+				g_warn_if_reached ();
+				continue;
+			}
+			if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) {
+				g_set_error (error,
+				             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
+				             "The connection's MAC address (%s) is blacklisted in %s.",
+				             (char *) mac_blacklist_iter->data, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+				return FALSE;
+			}
+		}
 	}
 
 	// FIXME: check bitrate against device capabilities
diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c
index 8c8f9d0d..88322344 100644
--- a/src/nm-device-olpc-mesh.c
+++ b/src/nm-device-olpc-mesh.c
@@ -104,7 +104,6 @@ struct _NMDeviceOlpcMeshPrivate
 	gint8			  num_freqs;
 	guint32			  freqs[IW_MAX_FREQUENCIES];
 
-	guint8			  we_version;
 	gboolean          up;
 
 	NMDevice *        companion;
@@ -147,44 +146,7 @@ nm_olpc_mesh_error_get_type (void)
 static guint32
 real_get_generic_capabilities (NMDevice *dev)
 {
-	int fd;
-	guint32 caps = NM_DEVICE_CAP_NONE;
-	struct iw_range range;
-	struct iwreq wrq;
-	const char *iface = nm_device_get_iface (dev);
-
-	/* Check for Wireless Extensions support >= 16 for wireless devices */
-
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		nm_log_warn (LOGD_OLPC_MESH, "couldn't open control socket.");
-		goto out;
-	}
-
-	memset (&wrq, 0, sizeof (struct iwreq));
-	memset (&range, 0, sizeof (struct iw_range));
-	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
-	wrq.u.data.pointer = (caddr_t) &range;
-	wrq.u.data.length = sizeof (struct iw_range);
-
-	if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) {
-		nm_log_warn (LOGD_OLPC_MESH, "couldn't get driver range information.");
-		goto out;
-	}
-
-	if ((wrq.u.data.length < 300) || (range.we_version_compiled < 16)) {
-		nm_log_warn (LOGD_OLPC_MESH,
-		             "(%s): driver's Wireless Extensions version (%d) is too old.",
-		             iface, range.we_version_compiled);
-		goto out;
-	} else {
-		caps |= NM_DEVICE_CAP_NM_SUPPORTED;
-	}
-
-out:
-	if (fd >= 0)
-		close (fd);
-	return caps;
+	return NM_DEVICE_CAP_NM_SUPPORTED;
 }
 
 static void
@@ -193,7 +155,6 @@ nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self)
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
 	priv->dispose_has_run = FALSE;
-	priv->we_version = 0;
 	priv->companion = NULL;
 	priv->stage1_waiting = FALSE;
 
@@ -276,15 +237,24 @@ constructor (GType type,
 	wrq.u.data.pointer = (caddr_t) &range;
 	wrq.u.data.length = sizeof (struct iw_range);
 
-	if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0)
+	if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) {
+		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed",
+		             nm_device_get_iface (NM_DEVICE (self)));
+		goto error;
+	}
+
+	if ((wrq.u.data.length < 300) || (range.we_version_compiled < 21)) {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver WEXT version too old (got %d, expected >= 21)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             range.we_version_compiled);
 		goto error;
+	}
 
 	priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
 	for (i = 0; i < priv->num_freqs; i++)
 		priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
 
-	priv->we_version = range.we_version_compiled;
-
 	close (fd);
 
 	/* shorter timeout for mesh connectivity */
@@ -453,7 +423,7 @@ create_socket_with_request (NMDevice *self, struct iwreq *req)
 	g_return_val_if_fail (self != NULL, -1);
 
 	sk = socket (AF_INET, SOCK_DGRAM, 0);
-	if (!sk) {
+	if (sk == -1) {
 		nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno);
 		return -1;
 	}
@@ -531,7 +501,6 @@ nm_device_olpc_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
 static void
 nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid)
 {
-	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 	int sk;
 	struct iwreq wrq;
 	const char * iface;
@@ -541,7 +510,7 @@ nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid)
 	g_return_if_fail (self != NULL);
 
 	sk = socket (AF_INET, SOCK_DGRAM, 0);
-	if (!sk) {
+	if (sk == -1) {
 		nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno);
 		return;
 	}
@@ -554,24 +523,10 @@ nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid)
 		memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len));
 	}
 	wrq.u.essid.pointer = (caddr_t) buf;
-
-	if (priv->we_version < 21) {
-		/* For historic reasons, set SSID length to include one extra
-		 * character, C string nul termination, even though SSID is
-		 * really an octet string that should not be presented as a C
-		 * string. Some Linux drivers decrement the length by one and
-		 * can thus end up missing the last octet of the SSID if the
-		 * length is not incremented here. WE-21 changes this to
-		 * explicitly require the length _not_ to include nul
-		 * termination. */
-		if (len)
-			len++;
-	}
 	wrq.u.essid.length = len;
 	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
 
 	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
-
 	if (ioctl (sk, SIOCSIWESSID, &wrq) < 0) {
 		if (errno != ENODEV) {
 			nm_log_err (LOGD_OLPC_MESH, "(%s): error setting SSID to '%s': %s",
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c
index 7a6e7528..278d9a06 100644
--- a/src/nm-device-wifi.c
+++ b/src/nm-device-wifi.c
@@ -33,6 +33,7 @@
 #include <linux/sockios.h>
 #include <linux/ethtool.h>
 #include <sys/ioctl.h>
+#include <netinet/ether.h>
 
 #include "nm-glib-compat.h"
 #include "nm-device.h"
@@ -55,6 +56,7 @@
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
 #include "nm-system.h"
+#include "nm-settings-connection.h"
 
 static gboolean impl_device_get_access_points (NMDeviceWifi *device,
                                                GPtrArray **aps,
@@ -153,7 +155,6 @@ struct _NMDeviceWifiPrivate {
 	guint             link_timeout_id;
 
 	/* Static options from driver */
-	guint8            we_version;
 	guint32           capabilities;
 	gboolean          has_scan_capa_ssid;
 };
@@ -503,29 +504,14 @@ static guint32
 real_get_generic_capabilities (NMDevice *dev)
 {
 	int fd, err;
-	guint32 caps = NM_DEVICE_CAP_NONE, response_len = 0;
+	guint32 caps = NM_DEVICE_CAP_NONE;
 	struct iwreq wrq;
-	struct iw_range range;
 	const char *iface = nm_device_get_iface (dev);
-	gboolean success;
-
-	memset (&range, 0, sizeof (struct iw_range));
-	success = wireless_get_range (NM_DEVICE_WIFI (dev), &range, &response_len);
-	if (!success)
-		return NM_DEVICE_CAP_NONE;
-
-	/* Check for Wireless Extensions support >= 16 for wireless devices */
-	if ((response_len < 300) || (range.we_version_compiled < 16)) {
-		nm_log_err (LOGD_HW | LOGD_WIFI,
-		            "(%s): driver's Wireless Extensions version (%d) is too old.",
-					iface, range.we_version_compiled);
-		return NM_DEVICE_CAP_NONE;
-	}
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
 		nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface);
-		goto out;
+		return NM_DEVICE_CAP_NONE;
 	}
 
 	/* Cards that don't scan aren't supported */
@@ -538,7 +524,6 @@ real_get_generic_capabilities (NMDevice *dev)
 	else
 		caps |= NM_DEVICE_CAP_NM_SUPPORTED;
 
-out:
 	return caps;
 }
 
@@ -548,11 +533,8 @@ out:
                   NM_WIFI_DEVICE_CAP_RSN)
 
 static guint32
-get_wireless_capabilities (NMDeviceWifi *self,
-                           iwrange * range,
-                           guint32 data_len)
+get_wireless_capabilities (NMDeviceWifi *self, iwrange *range)
 {
-	guint32 minlen;
 	guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
 	const char * iface;
 
@@ -561,39 +543,35 @@ get_wireless_capabilities (NMDeviceWifi *self,
 
 	iface = nm_device_get_iface (NM_DEVICE (self));
 
-	minlen = ((char *) &range->enc_capa) - (char *) range + sizeof (range->enc_capa);
-
 	/* All drivers should support WEP by default */
 	caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
 
-	if ((data_len >= minlen) && range->we_version_compiled >= 18) {
-		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
-			caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP;
+	if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
+		caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP;
 
-		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
-			caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP;
+	if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
+		caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP;
 
-		if (range->enc_capa & IW_ENC_CAPA_WPA)
-			caps |= NM_WIFI_DEVICE_CAP_WPA;
+	if (range->enc_capa & IW_ENC_CAPA_WPA)
+		caps |= NM_WIFI_DEVICE_CAP_WPA;
 
-		if (range->enc_capa & IW_ENC_CAPA_WPA2)
-			caps |= NM_WIFI_DEVICE_CAP_RSN;
+	if (range->enc_capa & IW_ENC_CAPA_WPA2)
+		caps |= NM_WIFI_DEVICE_CAP_RSN;
 
-		/* Check for cipher support but not WPA support */
-		if (    (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
-		    && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) {
-			nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; "
-			             "WPA unavailable.", iface);
-			caps &= ~WPA_CAPS;
-		}
+	/* Check for cipher support but not WPA support */
+	if (    (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+	    && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) {
+		nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; "
+		             "WPA unavailable.", iface);
+		caps &= ~WPA_CAPS;
+	}
 
-		/* Check for WPA support but not cipher support */
-		if (    (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))
-		    && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) {
-			nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; "
-			             "WPA unavailable.", iface);
-			caps &= ~WPA_CAPS;
-		}
+	/* Check for WPA support but not cipher support */
+	if (    (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))
+	    && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) {
+		nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; "
+		             "WPA unavailable.", iface);
+		caps &= ~WPA_CAPS;
 	}
 
 	return caps;
@@ -665,8 +643,19 @@ constructor (GType type,
 
 	memset (&range, 0, sizeof (struct iw_range));
 	success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len);
-	if (!success)
+	if (!success) {
+		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed",
+		             nm_device_get_iface (NM_DEVICE (self)));
 		goto error;
+	}
+
+	if ((response_len < 300) || (range.we_version_compiled < 21)) {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver WEXT version too old (got %d, expected >= 21)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             range.we_version_compiled);
+		goto error;
+	}
 
 	priv->max_qual.qual = range.max_qual.qual;
 	priv->max_qual.level = range.max_qual.level;
@@ -677,8 +666,6 @@ constructor (GType type,
 	for (i = 0; i < priv->num_freqs; i++)
 		priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
 
-	priv->we_version = range.we_version_compiled;
-
 	/* Check for the ability to scan specific SSIDs.  Until the scan_capa
 	 * field gets added to wireless-tools, need to work around that by casting
 	 * to the custom structure.
@@ -698,7 +685,7 @@ constructor (GType type,
 	}
 
 	/* 802.11 wireless-specific capabilities */
-	priv->capabilities = get_wireless_capabilities (self, &range, response_len);
+	priv->capabilities = get_wireless_capabilities (self, &range);
 
 	/* Connect to the supplicant manager */
 	priv->supplicant.mgr = nm_supplicant_manager_get ();
@@ -978,6 +965,31 @@ get_active_ap (NMDeviceWifi *self,
 }
 
 static void
+update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
+{
+	NMActRequest *req;
+	NMConnection *connection;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+	
+	if (ap == NULL)
+		return;
+
+	/* Don't cache the BSSID for Ad-Hoc APs */
+	if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA)
+		return;
+
+	if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) {
+		req = nm_device_get_act_request (NM_DEVICE (self));
+		if (req) {
+			connection = nm_act_request_get_connection (req);
+			nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection),
+			                                       nm_ap_get_address (ap));
+		}
+	}
+}
+
+static void
 set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
 {
 	NMDeviceWifiPrivate *priv;
@@ -1003,6 +1015,9 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
 		 */
 		priv->ap_list = g_slist_remove (priv->ap_list, new_ap);
 		priv->ap_list = g_slist_prepend (priv->ap_list, new_ap);
+
+		/* Update seen BSSIDs cache */
+		update_seen_bssids_cache (self, priv->current_ap);
 	}
 
 	/* Unref old AP here to ensure object lives if new_ap == old_ap */
@@ -1303,6 +1318,7 @@ real_check_connection_compatible (NMDevice *device,
 	NMSettingConnection *s_con;
 	NMSettingWireless *s_wireless;
 	const GByteArray *mac;
+	const GSList *mac_blacklist, *mac_blacklist_iter;
 
 	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
 	g_assert (s_con);
@@ -1330,6 +1346,25 @@ real_check_connection_compatible (NMDevice *device,
 		return FALSE;
 	}
 
+	/* Check for MAC address blacklist */
+	mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+	for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
+	     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
+		struct ether_addr addr;
+
+		if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+			g_warn_if_reached ();
+			continue;
+		}
+		if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) {
+			g_set_error (error,
+			             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,
+			             "The connection's MAC address (%s) is blacklisted in %s.",
+			             (char *) mac_blacklist_iter->data, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+			return FALSE;
+		}
+	}
+
 	// FIXME: check channel/freq/band against bands the hardware supports
 	// FIXME: check encryption against device capabilities
 	// FIXME: check bitrate against device capabilities
@@ -1574,6 +1609,8 @@ real_get_best_auto_connection (NMDevice *dev,
 		NMSettingConnection *s_con;
 		NMSettingWireless *s_wireless;
 		const GByteArray *mac;
+		const GSList *mac_blacklist, *mac_blacklist_iter;
+		gboolean mac_blacklist_found = FALSE;
 		NMSettingIP4Config *s_ip4;
 		const char *method = NULL;
 
@@ -1591,7 +1628,26 @@ real_get_best_auto_connection (NMDevice *dev,
 
 		mac = nm_setting_wireless_get_mac_address (s_wireless);
 		if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
+			continue;
+
+		/* Check for MAC address blacklist */
+		mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+		for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
+		     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
+			struct ether_addr addr;
+
+			if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+				g_warn_if_reached ();
 				continue;
+			}
+			if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) {
+				mac_blacklist_found = TRUE;
+				break;
+			}
+		}
+		/* Found device MAC address in the blacklist - do not use this connection */
+		if (mac_blacklist_found)
+			continue;
 
 		/* Use the connection if it's a shared connection */
 		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
@@ -1820,7 +1876,7 @@ nm_device_wifi_get_ssid (NMDeviceWifi *self)
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	sk = socket (AF_INET, SOCK_DGRAM, 0);
-	if (!sk) {
+	if (sk == -1) {
 		nm_log_err (LOGD_HW, "couldn't create socket: %d.", errno);
 		return NULL;
 	}
@@ -1844,13 +1900,6 @@ nm_device_wifi_get_ssid (NMDeviceWifi *self)
 
 	len = wrq.u.essid.length;
 	if (!nm_utils_is_empty_ssid ((guint8 *) ssid, len)) {
-		/* Some drivers include nul termination in the SSID, so let's
-		 * remove it here before further processing. WE-21 changes this
-		 * to explicitly require the length _not_ to include nul
-		 * termination. */
-		if (len > 0 && ssid[len - 1] == '\0' && priv->we_version < 21)
-			len--;
-
 		priv->ssid = g_byte_array_sized_new (len);
 		g_byte_array_append (priv->ssid, (const guint8 *) ssid, len);
 	}
@@ -2446,88 +2495,86 @@ remove_link_timeout (NMDeviceWifi *self)
 static gboolean
 link_timeout_cb (gpointer user_data)
 {
-	NMDevice *              dev = NM_DEVICE (user_data);
-	NMDeviceWifi * self = NM_DEVICE_WIFI (dev);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMActRequest *          req = NULL;
-	NMAccessPoint *         ap = NULL;
-	NMConnection *          connection;
-	const char *            setting_name;
-	gboolean                auth_enforced, encrypted = FALSE;
-
-	g_assert (dev);
+	NMDevice *dev = NM_DEVICE (user_data);
 
-	priv->link_timeout_id = 0;
+	nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev));
 
-	req = nm_device_get_act_request (dev);
-	ap = nm_device_wifi_get_activation_ap (self);
-	if (req == NULL || ap == NULL) {
-		/* shouldn't ever happen */
-		nm_log_err (LOGD_WIFI, "couldn't get activation request or activation AP.");
-		if (nm_device_is_activating (dev)) {
-			cleanup_association_attempt (self, TRUE);
-			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE);
-		}
-		return FALSE;
-	}
+	NM_DEVICE_WIFI_GET_PRIVATE (dev)->link_timeout_id = 0;
 
 	/* Disconnect event while activated; the supplicant hasn't been able
 	 * to reassociate within the timeout period, so the connection must
 	 * fail.
 	 */
-	if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
+	if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED)
 		nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
-		return FALSE;
-	}
 
-	/* Disconnect event during initial authentication and credentials
-	 * ARE checked - we are likely to have wrong key.  Ask the user for
-	 * another one.
+	return FALSE;
+}
+
+static gboolean
+handle_authenticate_fail (NMDeviceWifi *self, guint32 new_state, guint32 old_state)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMSetting8021x *s_8021x;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	NMActRequest *req;
+	NMConnection *connection;
+	const char *setting_name = NULL;
+	gboolean handled = FALSE;
+
+	g_return_val_if_fail (new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE);
+
+	/* Only care about ASSOCIATED -> DISCONNECTED transitions since 802.1x stuff
+	 * happens between the ASSOCIATED and AUTHENTICATED states.
 	 */
-	if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG)
-		goto time_out;
+	if (old_state != NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED)
+		return FALSE;
 
-	connection = nm_act_request_get_connection (req);
-	if (!connection)
-		goto time_out;
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_return_val_if_fail (req != NULL, FALSE);
 
-	auth_enforced = ap_auth_enforced (connection, ap, &encrypted);
-	if (!encrypted || !auth_enforced)
-		goto time_out;
+	connection = nm_act_request_get_connection (req);
+	g_return_val_if_fail (connection != NULL, FALSE);
 
-	/* Drivers are still just too crappy, and emit too many disassociation
-	 * events during connection.  So for now, just let the driver and supplicant
-	 * keep trying to associate, and don't ask for new secrets when we get
-	 * disconnected during association.
+	/* If it's an 802.1x or LEAP connection with "always ask"/unsaved secrets
+	 * then we need to ask again because it might be an OTP token and the PIN
+	 * may have changed.
 	 */
-	if (0) {
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+
+	if (s_8021x) {
+		nm_setting_get_secret_flags (NM_SETTING (s_8021x),
+		                             NM_SETTING_802_1X_PASSWORD,
+		                             &secret_flags,
+		                             NULL);
+		setting_name = NM_SETTING_802_1X_SETTING_NAME;
+	} else if (s_wsec) {
+		nm_setting_get_secret_flags (NM_SETTING (s_wsec),
+		                             NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+		                             &secret_flags,
+		                             NULL);
+		setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+	}
+
+	if (setting_name && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
+		NMSettingsGetSecretsFlags flags =   NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION
+		                                  | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+
 		nm_connection_clear_secrets (connection);
-		setting_name = nm_connection_need_secrets (connection, NULL);
-		if (!setting_name)
-			goto time_out;
 
-		/* Association/authentication failed during association, probably have a 
-		 * bad encryption key and the authenticating entity (AP, RADIUS server, etc)
-		 * denied the association due to bad credentials.
-		 */
 		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
 		             "Activation (%s/wireless): disconnected during association,"
-		             " asking for new key.", nm_device_get_iface (dev));
-		cleanup_association_attempt (self, TRUE);
-		nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
-		nm_act_request_get_secrets (req,
-		                            setting_name,
-		                            NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW,
-		                            NULL,
-		                            wifi_secrets_cb,
-		                            self);
+		             " asking for new key.", nm_device_get_iface (device));
 
-		return FALSE;
+		cleanup_association_attempt (self, TRUE);
+		nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+		nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self);
+		handled = TRUE;
 	}
 
-time_out:
-	nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev));
-	return FALSE;
+	return handled;
 }
 
 static void
@@ -2596,14 +2643,20 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
 		if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
-			/* Start the link timeout so we allow some time for reauthentication,
-			 * use a longer timeout if we are scanning since some cards take a
-			 * while to scan.
+			/* Disconnect during authentication means the 802.1x password is wrong */
+			if (handle_authenticate_fail (self, new_state, old_state))
+				break;
+		}
+
+		if (devstate == NM_DEVICE_STATE_ACTIVATED) {
+			/* If it's a disconnect while activated then start the link timer
+			 * to let the supplicant reconnect for a bit and if that doesn't
+			 * work kill the connection and try something else.  Allow a bit
+			 * more time if the card is scanning since sometimes the link will
+			 * drop while scanning and come back when the scan is done.
 			 */
-			if (!priv->link_timeout_id) {
-				priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15,
-					                                           link_timeout_cb, self);
-			}
+			if (priv->link_timeout_id == 0)
+				priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, link_timeout_cb, self);
 		}
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
@@ -3436,11 +3489,13 @@ activation_success_handler (NMDevice *dev)
 done:
 	periodic_update (self);
 
+	/* Update seen BSSIDs cache with the connected AP */
+	update_seen_bssids_cache (self, priv->current_ap);
+
 	/* Reset scan interval to something reasonable */
 	priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
 }
 
-
 static void
 activation_failure_handler (NMDevice *dev)
 {
@@ -3565,6 +3620,10 @@ device_state_changed (NMDevice *device,
 		}
 		clear_aps = TRUE;
 		break;
+	case NM_DEVICE_STATE_NEED_AUTH:
+		if (priv->supplicant.iface)
+			nm_supplicant_interface_disconnect (priv->supplicant.iface);
+		break;
 	case NM_DEVICE_STATE_ACTIVATED:
 		activation_success_handler (device);
 		break;
diff --git a/src/nm-device.c b/src/nm-device.c
index 4b7ec5df..1301197a 100644
--- a/src/nm-device.c
+++ b/src/nm-device.c
@@ -2891,34 +2891,6 @@ check_connection_compatible (NMDeviceInterface *dev_iface,
 }
 
 static gboolean
-device_activation_precheck (NMDevice *self, NMConnection *connection, GError **error)
-{
-	NMConnection *current_connection;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
-
-	if (nm_device_get_state (self) != NM_DEVICE_STATE_ACTIVATED)
-		return TRUE;
-
-	if (!nm_device_is_activating (self))
-		return TRUE;
-
-	// FIXME: why not just check connection path & service?
-	current_connection = nm_act_request_get_connection (nm_device_get_act_request (self));
-	if (nm_connection_compare (connection, current_connection, NM_SETTING_COMPARE_FLAG_EXACT)) {
-		/* Already activating or activated with the same connection */
-		g_set_error (error,
-		             NM_DEVICE_INTERFACE_ERROR,
-		             NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING,
-		             "%s", "Connection is already activating");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean
 nm_device_activate (NMDeviceInterface *device,
                     NMActRequest *req,
                     GError **error)
@@ -2926,9 +2898,21 @@ nm_device_activate (NMDeviceInterface *device,
 	NMDevice *self = NM_DEVICE (device);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!device_activation_precheck (self, nm_act_request_get_connection (req), error)) {
-		g_assert (*error);
-		return FALSE;
+	/* Make sure this connection isn't activated already, or in the process of
+	 * being activated.
+	 */
+	if (   nm_device_is_activating (self)
+	    || (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
+		NMConnection *new = nm_act_request_get_connection (req);
+		NMConnection *current = nm_act_request_get_connection (priv->act_request);
+
+		if (new == current) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_INTERFACE_ERROR,
+			                     NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING,
+			                     "Connection is already activating");
+			return FALSE;
+		}
 	}
 
 	priv->act_request = g_object_ref (req);
diff --git a/src/nm-manager-auth.c b/src/nm-manager-auth.c
index 9171663b..beed65ab 100644
--- a/src/nm-manager-auth.c
+++ b/src/nm-manager-auth.c
@@ -173,6 +173,37 @@ nm_auth_chain_get_data (NMAuthChain *self, const char *tag)
 	return tmp ? tmp->data : NULL;
 }
 
+/**
+ * nm_auth_chain_steal_data:
+ * @self: A #NMAuthChain.
+ * @tag: A "tag" uniquely identifying the data to steal.
+ *
+ * Removes the datum assocated with @tag from the chain's data associations,
+ * without invoking the association's destroy handler.  The caller assumes
+ * ownership over the returned value.
+ *
+ * Returns: the datum originally associated with @tag
+ */
+gpointer
+nm_auth_chain_steal_data (NMAuthChain *self, const char *tag)
+{
+	ChainData *tmp;
+	gpointer value = NULL;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (tag != NULL, NULL);
+
+	tmp = g_hash_table_lookup (self->data, tag);
+	if (tmp) {
+		g_hash_table_steal (self->data, tag);
+		value = tmp->data;
+		/* Make sure the destroy handler isn't called when freeing */
+		tmp->destroy = NULL;
+		free_data (tmp);
+	}
+	return value;
+}
+
 void
 nm_auth_chain_set_data (NMAuthChain *self,
                         const char *tag,
@@ -309,7 +340,8 @@ auth_call_schedule_early_finish (AuthCall *call, GError *error)
 {
 	if (!call->chain->error)
 		call->chain->error = error;
-	call->idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
+	if (!call->idle_id)
+		call->idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
 }
 
 #if WITH_POLKIT
@@ -544,45 +576,82 @@ typedef struct {
 	gpointer changed_data;
 } PkChangedInfo;
 
+static GSList *funcs = NULL;
+
 #if WITH_POLKIT
 static void
-pk_authority_changed_cb (GObject *object, PkChangedInfo *info)
+pk_authority_changed_cb (GObject *object, gpointer unused)
 {
-	info->changed_callback (info->changed_data);
+	GSList *iter;
+
+	for (iter = funcs; iter; iter = g_slist_next (iter)) {
+		PkChangedInfo *info = iter->data;
+
+		info->changed_callback (info->changed_data);
+	}
 }
 #endif
 
 void
-nm_auth_set_changed_func (GDestroyNotify callback, gpointer callback_data)
+nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data)
 {
 #if WITH_POLKIT
-	static PkChangedInfo info = { NULL, NULL };
-	static guint32 changed_id = 0;
 	PolkitAuthority *authority;
+	static guint32 changed_id = 0;
+#endif
+	PkChangedInfo *info;
+	GSList *iter;
+	gboolean found = FALSE;
 
+#if WITH_POLKIT
 	authority = pk_authority_get ();
 	if (!authority)
 		return;
 
-	if (callback == NULL) {
-		/* Clearing the callback */
-		info.changed_callback = NULL;
-		info.changed_data = NULL;
-		g_signal_handler_disconnect (authority, changed_id);
-		changed_id = 0;
-	} else {
-		info.changed_callback = callback;
-		info.changed_data= callback_data;
-
-		if (changed_id == 0) {
-			changed_id = g_signal_connect (authority,
-			                               "changed",
-			                               G_CALLBACK (pk_authority_changed_cb),
-			                               &info);
+	/* Hook up the changed signal the first time a callback is registered */
+	if (changed_id == 0) {
+		changed_id = g_signal_connect (authority,
+		                               "changed",
+		                               G_CALLBACK (pk_authority_changed_cb),
+		                               &funcs);
+	}
+#endif
+
+	/* No duplicates */
+	for (iter = funcs; iter; iter = g_slist_next (iter)) {
+		info = iter->data;
+		if ((callback == info->changed_callback) && (callback_data == info->changed_data)) {
+			found = TRUE;
+			break;
 		}
 	}
 
+	g_warn_if_fail (found == FALSE);
+	if (found == FALSE) {
+		info = g_malloc0 (sizeof (*info));
+		info->changed_callback = callback;
+		info->changed_data = callback_data;
+		funcs = g_slist_append (funcs, info);
+	}
+
+#if WITH_POLKIT
 	g_object_unref (authority);
 #endif
 }
 
+void
+nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data)
+{
+	GSList *iter;
+
+	for (iter = funcs; iter; iter = g_slist_next (iter)) {
+		PkChangedInfo *info = iter->data;
+
+		if ((callback == info->changed_callback) && (callback_data == info->changed_data)) {
+			g_free (info);
+			funcs = g_slist_delete_link (funcs, iter);
+			break;
+		}
+	}
+}
+
diff --git a/src/nm-manager-auth.h b/src/nm-manager-auth.h
index ad14d306..15317c71 100644
--- a/src/nm-manager-auth.h
+++ b/src/nm-manager-auth.h
@@ -70,6 +70,8 @@ NMAuthChain *nm_auth_chain_new_dbus_sender (const char *dbus_sender,
 
 gpointer nm_auth_chain_get_data (NMAuthChain *chain, const char *tag);
 
+gpointer nm_auth_chain_steal_data (NMAuthChain *chain, const char *tag);
+
 void nm_auth_chain_set_data (NMAuthChain *chain,
                              const char *tag,
                              gpointer data,
@@ -102,7 +104,9 @@ gboolean nm_auth_uid_in_acl (NMConnection *connection,
                              gulong uid,
                              char **out_error_desc);
 
-void nm_auth_set_changed_func (GDestroyNotify callback, gpointer callback_data);
+void nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data);
+
+void nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data);
 
 #endif /* NM_MANAGER_AUTH_H */
 
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 961c9d8c..f5782254 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -63,6 +63,7 @@
 #include "nm-settings-connection.h"
 #include "nm-manager-auth.h"
 #include "NetworkManagerUtils.h"
+#include "nm-utils.h"
 
 #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
 #define NM_AUTOIP_DBUS_IFACE   "org.freedesktop.nm_avahi_autoipd"
@@ -172,6 +173,7 @@ struct PendingActivation {
 	DBusGMethodInvocation *context;
 	PendingActivationFunc callback;
 	NMAuthChain *chain;
+	const char *wifi_shared_permission;
 
 	char *connection_path;
 	NMConnection *connection;
@@ -739,17 +741,15 @@ pending_activation_new (NMManager *manager,
 }
 
 static void
-pending_auth_net_done (NMAuthChain *chain,
-                       GError *error,
-                       DBusGMethodInvocation *context,
-                       gpointer user_data)
+pending_auth_done (NMAuthChain *chain,
+                   GError *error,
+                   DBusGMethodInvocation *context,
+                   gpointer user_data)
 {
 	PendingActivation *pending = user_data;
 	NMAuthCallResult result;
 	GError *tmp_error = NULL;
 
-	pending->chain = NULL;
-
 	/* Caller has had a chance to obtain authorization, so we only need to
 	 * check for 'yes' here.
 	 */
@@ -758,10 +758,23 @@ pending_auth_net_done (NMAuthChain *chain,
 		tmp_error = g_error_new_literal (NM_MANAGER_ERROR,
 		                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                                 "Not authorized to control networking.");
+		goto out;
+	}
+
+	if (pending->wifi_shared_permission) {
+		result = nm_auth_chain_get_result (chain, pending->wifi_shared_permission);
+		if (result != NM_AUTH_CALL_RESULT_YES) {
+			tmp_error = g_error_new_literal (NM_MANAGER_ERROR,
+			                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
+			                                 "Not authorized to share connections via wifi.");
+			goto out;
+		}
 	}
 
+	/* Otherwise authorized and available to activate */
+
+out:
 	pending->callback (pending, tmp_error);
-	nm_auth_chain_unref (chain);
 	g_clear_error (&tmp_error);
 }
 
@@ -772,12 +785,15 @@ pending_activation_check_authorized (PendingActivation *pending,
 	char *error_desc = NULL;
 	gulong sender_uid = G_MAXULONG;
 	GError *error;
+	const char *wifi_permission = NULL;
+	NMConnection *connection;
+	NMSettings *settings;
 
 	g_return_if_fail (pending != NULL);
 	g_return_if_fail (dbus_mgr != NULL);
 
 	if (!nm_auth_get_caller_uid (pending->context, 
-		                         dbus_mgr,
+	                             dbus_mgr,
 	                             &sender_uid,
 	                             &error_desc)) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
@@ -795,17 +811,40 @@ pending_activation_check_authorized (PendingActivation *pending,
 		return;
 	}
 
+	/* By this point we have an auto-completed connection (for AddAndActivate)
+	 * or an existing connection (for Activate).
+	 */
+	connection = pending->connection;
+	if (!connection) {
+		settings = NM_MANAGER_GET_PRIVATE (pending->manager)->settings;
+		connection = (NMConnection *) nm_settings_get_connection_by_path (settings, pending->connection_path);
+	}
+
+	if (!connection) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		                             "Connection could not be found.");
+		pending->callback (pending, error);
+		g_error_free (error);
+		return;
+	}
+
 	/* First check if the user is allowed to use networking at all, giving
 	 * the user a chance to authenticate to gain the permission.
 	 */
 	pending->chain = nm_auth_chain_new (pending->context,
 	                                    NULL,
-	                                    pending_auth_net_done,
+	                                    pending_auth_done,
 	                                    pending);
 	g_assert (pending->chain);
-	nm_auth_chain_add_call (pending->chain,
-	                        NM_AUTH_PERMISSION_NETWORK_CONTROL,
-	                        TRUE);
+	nm_auth_chain_add_call (pending->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+
+	/* Shared wifi connections require special permissions too */
+	wifi_permission = nm_utils_get_shared_wifi_permission (connection);
+	if (wifi_permission) {
+		pending->wifi_shared_permission = wifi_permission;
+		nm_auth_chain_add_call (pending->chain, wifi_permission, TRUE);
+	}
 }
 
 static void
@@ -1040,52 +1079,27 @@ manager_hidden_ap_found (NMDeviceInterface *device,
 {
 	NMManager *manager = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	const struct ether_addr *ap_addr;
-	const GByteArray *ap_ssid;
+	const struct ether_addr *bssid;
 	GSList *iter;
 	GSList *connections;
 	gboolean done = FALSE;
 
-	ap_ssid = nm_ap_get_ssid (ap);
-	if (ap_ssid && ap_ssid->len)
-		return;
+	g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
 
-	ap_addr = nm_ap_get_address (ap);
-	g_assert (ap_addr);
+	bssid = nm_ap_get_address (ap);
+	g_assert (bssid);
 
 	/* Look for this AP's BSSID in the seen-bssids list of a connection,
 	 * and if a match is found, copy over the SSID */
 	connections = nm_settings_get_connections (priv->settings);
-
 	for (iter = connections; iter && !done; iter = g_slist_next (iter)) {
 		NMConnection *connection = NM_CONNECTION (iter->data);
-		NMSettingWireless *s_wireless;
-		const GByteArray *ssid;
-		guint32 num_bssids;
-		guint32 i;
-
-		s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
-		if (!s_wireless)
-			continue;
-
-		num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless);
-		if (num_bssids < 1)
-			continue;
-
-		ssid = nm_setting_wireless_get_ssid (s_wireless);
-		g_assert (ssid);
+		NMSettingWireless *s_wifi;
 
-		for (i = 0; i < num_bssids && !done; i++) {
-			const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i);
-			struct ether_addr seen_addr;
-
-			if (ether_aton_r (seen_bssid, &seen_addr)) {
-				if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr)) == 0) {
-					/* Copy the SSID from the connection to the AP */
-					nm_ap_set_ssid (ap, ssid);
-					done = TRUE;
-				}
-			}
+		s_wifi = nm_connection_get_setting_wireless (connection);
+		if (s_wifi) {
+			if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid))
+				nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
 		}
 	}
 	g_slist_free (connections);
@@ -1667,12 +1681,11 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
 	g_return_if_fail (bdaddr != NULL);
 	g_return_if_fail (object_path != NULL);
 
-	nm_log_info (LOGD_HW, "BT device %s removed", bdaddr);
-
 	for (iter = priv->devices; iter; iter = iter->next) {
 		NMDevice *device = NM_DEVICE (iter->data);
 
 		if (!strcmp (nm_device_get_udi (device), object_path)) {
+			nm_log_info (LOGD_HW, "BT device %s removed", bdaddr);
 			priv->devices = remove_one_device (self, priv->devices, device, FALSE);
 			break;
 		}
@@ -1866,6 +1879,8 @@ internal_activate_device (NMManager *manager,
 
 	/* Tear down any existing connection */
 	if (nm_device_get_act_request (device)) {
+		nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.",
+		             nm_device_get_iface (device));
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_DISCONNECTED,
 		                         NM_DEVICE_STATE_REASON_NONE);
@@ -2040,7 +2055,9 @@ pending_activate (NMManager *self, PendingActivation *pending)
 
 	if (!path) {
 		nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s",
-		             pending->connection_path, error->code, error->message);
+		             pending->connection_path,
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
 	} else
 		g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS);
 
@@ -3105,6 +3122,13 @@ nm_manager_get (NMSettings *settings,
 }
 
 static void
+authority_changed_cb (gpointer user_data)
+{
+	/* Let clients know they should re-check their authorization */
+	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
+}
+
+static void
 dispose (GObject *object)
 {
 	NMManager *manager = NM_MANAGER (object);
@@ -3121,7 +3145,7 @@ dispose (GObject *object)
 	g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL);
 	g_slist_free (priv->auth_chains);
 
-	nm_auth_set_changed_func (NULL, NULL);
+	nm_auth_changed_func_unregister (authority_changed_cb, manager);
 
 	while (g_slist_length (priv->devices)) {
 		priv->devices = remove_one_device (manager,
@@ -3398,13 +3422,6 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 }
 
 static void
-authority_changed_cb (gpointer user_data)
-{
-	/* Let clients know they should re-check their authorization */
-	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
-}
-
-static void
 nm_manager_init (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
@@ -3502,7 +3519,7 @@ nm_manager_init (NMManager *manager)
 		nm_log_warn (LOGD_SUSPEND, "could not initialize UPower D-Bus proxy");
 
 	/* Listen for authorization changes */
-	nm_auth_set_changed_func (authority_changed_cb, manager);
+	nm_auth_changed_func_register (authority_changed_cb, manager);
 
 	/* Monitor the firmware directory */
 	if (strlen (KERNEL_FIRMWARE_DIR)) {
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 194d1111..a42a0fa9 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -46,6 +46,7 @@
 #include "nm-dns-manager.h"
 #include "nm-vpn-manager.h"
 #include "nm-policy-hostname.h"
+#include "nm-manager-auth.h"
 
 struct NMPolicy {
 	NMManager *manager;
@@ -66,12 +67,17 @@ struct NMPolicy {
 
 	HostnameThread *lookup;
 
+	gint reset_retries_id;  /* idle handler for resetting the retries count */
+
 	char *orig_hostname; /* hostname at NM start time */
 	char *cur_hostname;  /* hostname we want to assign */
 };
 
 #define RETRIES_TAG "autoconnect-retries"
 #define RETRIES_DEFAULT	4
+#define RESET_RETRIES_TIMESTAMP_TAG "reset-retries-timestamp-tag"
+#define RESET_RETRIES_TIMER 300
+#define FAILURE_REASON_TAG "failure-reason"
 
 static NMDevice *
 get_best_ip4_device (NMManager *manager, NMActRequest **out_req)
@@ -316,36 +322,36 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 		/* Grab a hostname out of the device's DHCP4 config */
 		dhcp4_config = nm_device_get_dhcp4_config (best4);
 		if (dhcp4_config) {
-			p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name");
+			p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "new_host_name");
 			if (dhcp_hostname && strlen (dhcp_hostname)) {
 				/* Sanity check; strip leading spaces */
 				while (*p) {
 					if (!isblank (*p++)) {
-						_set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv4");
+						_set_hostname (policy, TRUE, p-1, "from DHCPv4");
 						return;
 					}
 				}
 				nm_log_warn (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
-					         dhcp_hostname);
+				             dhcp_hostname);
 			}
 		}
 	} else if (best6) {
 		NMDHCP6Config *dhcp6_config;
 
-		/* Grab a hostname out of the device's DHCP4 config */
+		/* Grab a hostname out of the device's DHCP6 config */
 		dhcp6_config = nm_device_get_dhcp6_config (best6);
 		if (dhcp6_config) {
-			p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name");
+			p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "new_host_name");
 			if (dhcp_hostname && strlen (dhcp_hostname)) {
 				/* Sanity check; strip leading spaces */
 				while (*p) {
 					if (!isblank (*p++)) {
-						_set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv6");
+						_set_hostname (policy, TRUE, p-1, "from DHCPv6");
 						return;
 					}
 				}
 				nm_log_warn (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
-					         dhcp_hostname);
+				             dhcp_hostname);
 			}
 		}
 	}
@@ -722,16 +728,29 @@ auto_activate_device (gpointer user_data)
 
 	/* Remove connections that shouldn't be auto-activated */
 	while (iter) {
-		NMConnection *candidate = NM_CONNECTION (iter->data);
+		NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data);
+		gboolean remove_it = FALSE;
+		const char *permission;
 
 		/* Grab next item before we possibly delete the current item */
 		iter = g_slist_next (iter);
 
 		/* Ignore connections that were tried too many times or are not visible
-		 * to any logged-in users.
+		 * to any logged-in users.  Also ignore shared wifi connections for
+		 * which no user has the shared wifi permission.
 		 */
-		if (   get_connection_auto_retries (candidate) == 0
-		    || nm_settings_connection_is_visible (NM_SETTINGS_CONNECTION (candidate)) == FALSE)
+		if (   get_connection_auto_retries (NM_CONNECTION (candidate)) == 0
+		    || nm_settings_connection_is_visible (candidate) == FALSE)
+			remove_it = TRUE;
+		else {
+			permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (candidate));
+			if (permission) {
+				if (nm_settings_connection_check_permission (candidate, permission) == FALSE)
+					remove_it = TRUE;
+			}
+		}
+
+		if (remove_it)
 			connections = g_slist_remove (connections, candidate);
 	}
 
@@ -739,6 +758,8 @@ auto_activate_device (gpointer user_data)
 	if (best_connection) {
 		GError *error = NULL;
 
+		nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.",
+		             nm_connection_get_id (best_connection));
 		if (!nm_manager_activate_connection (policy->manager,
 		                                     best_connection,
 		                                     specific_object,
@@ -817,13 +838,34 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 }
 
 static void
-reset_retries_all (NMSettings *settings)
+reset_retries_all (NMSettings *settings, NMDevice *device)
+{
+	GSList *connections, *iter;
+	GError *error = NULL;
+
+	connections = nm_settings_get_connections (settings);
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		if (!device || nm_device_interface_check_connection_compatible (NM_DEVICE_INTERFACE (device), iter->data, &error))
+			set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
+		g_clear_error (&error);
+	}
+	g_slist_free (connections);
+}
+
+static void
+reset_retries_for_failed_secrets (NMSettings *settings)
 {
 	GSList *connections, *iter;
 
 	connections = nm_settings_get_connections (settings);
-	for (iter = connections; iter; iter = g_slist_next (iter))
-		set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMDeviceStateReason reason = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iter->data), FAILURE_REASON_TAG));
+
+		if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) {
+			set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
+			g_object_set_data (G_OBJECT (iter->data), FAILURE_REASON_TAG, GUINT_TO_POINTER (0));
+		}
+	}
 	g_slist_free (connections);
 }
 
@@ -838,7 +880,7 @@ sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 
 	/* Reset retries on all connections so they'll checked on wakeup */
 	if (sleeping || !enabled)
-		reset_retries_all (policy->settings);
+		reset_retries_all (policy->settings, NULL);
 }
 
 static void
@@ -854,6 +896,9 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds
 	if (state < NM_DEVICE_STATE_DISCONNECTED)
 		return;
 
+	if (!nm_device_interface_get_enabled (NM_DEVICE_INTERFACE (device)))
+		return;
+
 	if (!nm_device_autoconnect_allowed (device))
 		return;
 
@@ -864,6 +909,37 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds
 	}
 }
 
+static gboolean
+reset_connections_retries (gpointer user_data)
+{
+	NMPolicy *policy = (NMPolicy *) user_data;
+	GSList *connections, *iter;
+	time_t con_stamp, min_stamp, now;
+
+	policy->reset_retries_id = 0;
+
+	min_stamp = now = time (NULL);
+	connections = nm_settings_get_connections (policy->settings);
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		con_stamp = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG));
+		if (con_stamp == 0)
+			continue;
+		if (con_stamp + RESET_RETRIES_TIMER <= now) {
+			set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
+			g_object_set_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER (0));
+			continue;
+		}
+		if (con_stamp < min_stamp)
+			min_stamp = con_stamp;
+	}
+	g_slist_free (connections);
+
+	/* Schedule the handler again if there are some stamps left */
+	if (min_stamp != now)
+		policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER - (now - min_stamp), reset_connections_retries, policy);
+	return FALSE;
+}
+
 static NMConnection *
 get_device_connection (NMDevice *device)
 {
@@ -886,6 +962,9 @@ device_state_changed (NMDevice *device,
 	NMPolicy *policy = (NMPolicy *) user_data;
 	NMConnection *connection = get_device_connection (device);
 
+	if (connection)
+		g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (0));
+
 	switch (new_state) {
 	case NM_DEVICE_STATE_FAILED:
 		/* Mark the connection invalid if it failed during activation so that
@@ -900,6 +979,11 @@ device_state_changed (NMDevice *device,
 				 * automatically retrying because it's just going to fail anyway.
 				 */
 				set_connection_auto_retries (connection, 0);
+
+				/* Mark the connection as failed due to missing secrets so that we can reset
+				 * RETRIES_TAG and automatically re-try when an secret agent registers.
+				 */
+				g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (NM_DEVICE_STATE_REASON_NO_SECRETS));
 			} else if (tries > 0) {
 				/* Otherwise if it's a random failure, just decrease the number
 				 * of automatic retries so that the connection gets tried again
@@ -908,8 +992,13 @@ device_state_changed (NMDevice *device,
 				set_connection_auto_retries (connection, tries - 1);
 			}
 
-			if (get_connection_auto_retries (connection) == 0)
+			if (get_connection_auto_retries (connection) == 0) {
 				nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", nm_connection_get_id (connection));
+				/* Schedule a handler to reset retries count */
+				g_object_set_data (G_OBJECT (connection), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER ((gsize) time (NULL)));
+				if (!policy->reset_retries_id)
+					policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER, reset_connections_retries, policy);
+			}
 			nm_connection_clear_secrets (connection);
 		}
 		schedule_activate_check (policy, device, 3);
@@ -932,6 +1021,11 @@ device_state_changed (NMDevice *device,
 		update_routing_and_dns (policy, FALSE);
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
+		/* Reset RETRIES_TAG when carrier on. If cable was unplugged
+		 * and plugged again, we should try to reconnect */
+		if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE)
+			reset_retries_all (policy->settings, device);
+
 		/* Device is now available for auto-activation */
 		update_routing_and_dns (policy, FALSE);
 		schedule_activate_check (policy, device, 0);
@@ -963,6 +1057,12 @@ nsps_changed (NMDeviceWimax *device, NMWimaxNsp *nsp, gpointer user_data)
 }
 #endif
 
+static void
+modem_enabled_changed (NMDeviceModem *device, gpointer user_data)
+{
+	schedule_activate_check ((NMPolicy *) (user_data), NM_DEVICE (device), 0);
+}
+
 typedef struct {
 	gulong id;
 	NMDevice *device;
@@ -997,6 +1097,8 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data)
 		_connect_device_signal (policy, device, "nsp-added", nsps_changed);
 		_connect_device_signal (policy, device, "nsp-removed", nsps_changed);
 #endif
+	} else if (NM_IS_DEVICE_MODEM (device)) {
+		_connect_device_signal (policy, device, NM_DEVICE_MODEM_ENABLE_CHANGED, modem_enabled_changed);
 	}
 }
 
@@ -1058,7 +1160,7 @@ connections_loaded (NMSettings *settings, gpointer user_data)
 	// that by calling reset_retries_all() in nm_policy_new()
 	
 	/* Initialize connections' auto-retries */
-	reset_retries_all (settings);
+	reset_retries_all (settings, NULL);
 
 	schedule_activate_all ((NMPolicy *) user_data);
 }
@@ -1122,6 +1224,19 @@ connection_visibility_changed (NMSettings *settings,
 }
 
 static void
+secret_agent_registered (NMSettings *settings,
+                         NMSecretAgent *agent,
+                         gpointer user_data)
+{
+	/* The registered secret agent may provide some missing secrets. Thus we
+	 * reset retries count here and schedule activation, so that the
+	 * connections failed due to missing secrets may re-try auto-connection.
+	 */
+	reset_retries_for_failed_secrets (settings);
+	schedule_activate_all ((NMPolicy *) user_data);
+}
+
+static void
 _connect_manager_signal (NMPolicy *policy, const char *name, gpointer callback)
 {
 	guint id;
@@ -1161,7 +1276,10 @@ nm_policy_new (NMManager *manager,
 	memset (hostname, 0, sizeof (hostname));
 	if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) {
 		/* only cache it if it's a valid hostname */
-		if (strlen (hostname) && strcmp (hostname, "localhost") && strcmp (hostname, "localhost.localdomain"))
+		if (   strlen (hostname)
+		    && strcmp (hostname, "localhost")
+		    && strcmp (hostname, "localhost.localdomain")
+		    && strcmp (hostname, "(none)"))
 			policy->orig_hostname = g_strdup (hostname);
 	}
 
@@ -1186,9 +1304,10 @@ nm_policy_new (NMManager *manager,
 	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, connection_removed);
 	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
 	                          connection_visibility_changed);
+	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered);
 
 	/* Initialize connections' auto-retries */
-	reset_retries_all (policy->settings);
+	reset_retries_all (policy->settings, NULL);
 
 	initialized = TRUE;
 	return policy;
@@ -1232,6 +1351,9 @@ nm_policy_destroy (NMPolicy *policy)
 	}
 	g_slist_free (policy->dev_ids);
 
+	if (policy->reset_retries_id)
+		g_source_remove (policy->reset_retries_id);
+
 	g_free (policy->orig_hostname);
 	g_free (policy->cur_hostname);
 
diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c
index e0795e81..72501c28 100644
--- a/src/nm-udev-manager.c
+++ b/src/nm-udev-manager.c
@@ -384,6 +384,9 @@ is_wireless (GUdevDevice *device)
 	g_assert (ifname);
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd == -1)
+		return FALSE;
+
 	strncpy (iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);
 
 	path = g_udev_device_get_sysfs_path (device);
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 8a5ea106..5ccbdc67 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -49,6 +49,9 @@ typedef struct {
 	NMDBusManager *dbus_mgr;
 	NMSessionMonitor *session_monitor;
 
+	/* Auth chains for checking agent permissions */
+	GSList *chains;
+
 	/* Hashed by owner name, not identifier, since two agents in different
 	 * sessions can use the same identifier.
 	 */
@@ -57,6 +60,14 @@ typedef struct {
 	GHashTable *requests;
 } NMAgentManagerPrivate;
 
+enum {
+        AGENT_REGISTERED,
+
+        LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
 typedef struct _Request Request;
 
 static void request_add_agent (Request *req,
@@ -218,6 +229,59 @@ validate_identifier (const char *identifier, GError **error)
 }
 
 static void
+agent_register_permissions_done (NMAuthChain *chain,
+                                 GError *error,
+                                 DBusGMethodInvocation *context,
+                                 gpointer user_data)
+{
+	NMAgentManager *self = NM_AGENT_MANAGER (user_data);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	NMSecretAgent *agent;
+	const char *sender;
+	GError *local = NULL;
+	NMAuthCallResult result;
+	GHashTableIter iter;
+	Request *req;
+
+	priv->chains = g_slist_remove (priv->chains, chain);
+
+	if (error) {
+		local = g_error_new (NM_AGENT_MANAGER_ERROR,
+		                     NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
+		                     "Failed to request agent permissions: (%d) %s",
+		                     error->code, error->message);
+		dbus_g_method_return_error (context, local);
+		g_error_free (local);
+	} else {
+		agent = nm_auth_chain_steal_data (chain, "agent");
+
+		result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
+		if (result == NM_AUTH_CALL_RESULT_YES)
+			nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE);
+
+		result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
+		if (result == NM_AUTH_CALL_RESULT_YES)
+			nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE);
+
+		sender = nm_secret_agent_get_dbus_owner (agent);
+		g_hash_table_insert (priv->agents, g_strdup (sender), agent);
+		nm_log_dbg (LOGD_AGENTS, "(%s) agent registered",
+		            nm_secret_agent_get_description (agent));
+		dbus_g_method_return (context);
+
+		/* Signal an agent was registered */
+		g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent);
+
+		/* Add this agent to any in-progress secrets requests */
+		g_hash_table_iter_init (&iter, priv->requests);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &req))
+			request_add_agent (req, agent, priv->session_monitor);
+	}
+
+	nm_auth_chain_unref (chain);
+}
+
+static void
 impl_agent_manager_register (NMAgentManager *self,
                              const char *identifier,
                              DBusGMethodInvocation *context)
@@ -227,8 +291,7 @@ impl_agent_manager_register (NMAgentManager *self,
 	gulong sender_uid = G_MAXULONG;
 	GError *error = NULL, *local = NULL;
 	NMSecretAgent *agent;
-	GHashTableIter iter;
-	gpointer data;
+	NMAuthChain *chain;
 
 	if (!nm_auth_get_caller_uid (context, 
 		                         priv->dbus_mgr,
@@ -272,15 +335,16 @@ impl_agent_manager_register (NMAgentManager *self,
 		goto done;
 	}
 
-	g_hash_table_insert (priv->agents, g_strdup (sender), agent);
-	nm_log_dbg (LOGD_AGENTS, "(%s) agent registered",
+	nm_log_dbg (LOGD_AGENTS, "(%s) requesting permissions",
 	            nm_secret_agent_get_description (agent));
-	dbus_g_method_return (context);
 
-	/* Add this agent to any in-progress secrets requests */
-	g_hash_table_iter_init (&iter, priv->requests);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		request_add_agent ((Request *) data, agent, priv->session_monitor);
+	/* Kick off permissions requests for this agent */
+	chain = nm_auth_chain_new (context, NULL, agent_register_permissions_done, self);
+	nm_auth_chain_set_data (chain, "agent", agent, g_object_unref);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
+
+	priv->chains = g_slist_append (priv->chains, chain);
 
 done:
 	if (error)
@@ -945,8 +1009,8 @@ get_start (gpointer user_data)
 			g_clear_error (&error);
 		} else {
 			/* Do we have everything we need? */
-			/* FIXME: handle second check for VPN connections */
-			if ((nm_connection_need_secrets (tmp, NULL) == NULL) && (request_new == FALSE)) {
+			if (   (req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM)
+			    || ((nm_connection_need_secrets (tmp, NULL) == NULL) && (request_new == FALSE))) {
 				nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets sufficient",
 				            req, req->setting_name);
 
@@ -1059,7 +1123,8 @@ nm_agent_manager_get_secrets (NMAgentManager *self,
 	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (req->reqid), req);
 
 	/* Kick off the request */
-	request_add_agents (self, req);
+	if (!(req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM))
+		request_add_agents (self, req);
 	req->idle_id = g_idle_add (get_start, req);
 
 	return req->reqid;
@@ -1277,6 +1342,24 @@ nm_agent_manager_delete_secrets (NMAgentManager *self,
 
 /*************************************************************/
 
+NMSecretAgent *
+nm_agent_manager_get_agent_by_user (NMAgentManager *self, const char *username)
+{
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	NMSecretAgent *agent;
+
+	g_hash_table_iter_init (&iter, priv->agents);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
+		if (g_strcmp0 (nm_secret_agent_get_owner_username (agent), username) == 0)
+			return agent;
+	}
+
+	return NULL;
+}
+
+/*************************************************************/
+
 static void
 name_owner_changed_cb (NMDBusManager *dbus_mgr,
                        const char *name,
@@ -1290,6 +1373,73 @@ name_owner_changed_cb (NMDBusManager *dbus_mgr,
 	}
 }
 
+static void
+agent_permissions_changed_done (NMAuthChain *chain,
+                                GError *error,
+                                DBusGMethodInvocation *context,
+                                gpointer user_data)
+{
+	NMAgentManager *self = NM_AGENT_MANAGER (user_data);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	NMSecretAgent *agent;
+	NMAuthCallResult result;
+
+	priv->chains = g_slist_remove (priv->chains, chain);
+
+	agent = nm_auth_chain_get_data (chain, "agent");
+
+	if (error) {
+		nm_log_dbg (LOGD_AGENTS, "(%s) failed to request updated agent permissions",
+		            nm_secret_agent_get_description (agent));
+		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
+		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
+	} else {
+		nm_log_dbg (LOGD_AGENTS, "(%s) updated agent permissions",
+		            nm_secret_agent_get_description (agent));
+
+		result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
+		nm_secret_agent_add_permission (agent,
+		                                NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED,
+		                                (result == NM_AUTH_CALL_RESULT_YES));
+
+		result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
+		nm_secret_agent_add_permission (agent,
+		                                NM_AUTH_PERMISSION_WIFI_SHARE_OPEN,
+		                                (result == NM_AUTH_CALL_RESULT_YES));
+	}
+
+	nm_auth_chain_unref (chain);
+}
+
+static void
+authority_changed_cb (gpointer user_data)
+{
+	NMAgentManager *self = NM_AGENT_MANAGER (user_data);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	NMSecretAgent *agent;
+
+	/* Recheck the permissions of all secret agents */
+	g_hash_table_iter_init (&iter, priv->agents);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
+		NMAuthChain *chain;
+		const char *sender;
+
+		/* Kick off permissions requests for this agent */
+		sender = nm_secret_agent_get_dbus_owner (agent);
+		chain = nm_auth_chain_new_dbus_sender (sender, agent_permissions_changed_done, self);
+
+		/* Make sure if the agent quits while the permissions call is in progress
+		 * that the object sticks around until our callback.
+		 */
+		nm_auth_chain_set_data (chain, "agent", g_object_ref (agent), g_object_unref);
+		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
+		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
+
+		priv->chains = g_slist_append (priv->chains, chain);
+	}
+}
+
 /*************************************************************/
 
 NMAgentManager *
@@ -1319,6 +1469,8 @@ nm_agent_manager_get (void)
 	                  G_CALLBACK (name_owner_changed_cb),
 	                  singleton);
 
+	nm_auth_changed_func_register (authority_changed_cb, singleton);
+
 	return singleton;
 }
 
@@ -1342,6 +1494,10 @@ dispose (GObject *object)
 	if (!priv->disposed) {
 		priv->disposed = TRUE;
 
+		nm_auth_changed_func_unregister (authority_changed_cb, NM_AGENT_MANAGER (object));
+
+		g_slist_foreach (priv->chains, (GFunc) nm_auth_chain_unref, NULL);
+
 		g_hash_table_destroy (priv->agents);
 		g_hash_table_destroy (priv->requests);
 
@@ -1362,6 +1518,17 @@ nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class)
 	/* virtual methods */
 	object_class->dispose = dispose;
 
+	/* Signals */
+	signals[AGENT_REGISTERED] =
+		g_signal_new ("agent-registered",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMAgentManagerClass, agent_registered),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
 	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (agent_manager_class),
 	                                 &dbus_glib_nm_agent_manager_object_info);
 
diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h
index 788a9175..e49f579d 100644
--- a/src/settings/nm-agent-manager.h
+++ b/src/settings/nm-agent-manager.h
@@ -25,6 +25,7 @@
 #include <glib-object.h>
 #include <nm-connection.h>
 #include "nm-settings-flags.h"
+#include "nm-secret-agent.h"
 
 #define NM_TYPE_AGENT_MANAGER            (nm_agent_manager_get_type ())
 #define NM_AGENT_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AGENT_MANAGER, NMAgentManager))
@@ -39,6 +40,9 @@ typedef struct {
 
 typedef struct {
 	GObjectClass parent;
+
+	/* Signals */
+	void (*agent_registered)   (NMAgentManager *agent_mgr, NMSecretAgent *agent);
 } NMAgentManagerClass;
 
 GType nm_agent_manager_get_type (void);
@@ -85,4 +89,7 @@ guint32 nm_agent_manager_delete_secrets (NMAgentManager *manager,
                                          gboolean filter_by_uid,
                                          gulong uid_filter);
 
+NMSecretAgent *nm_agent_manager_get_agent_by_user (NMAgentManager *manager,
+                                                   const char *username);
+
 #endif /* NM_AGENT_MANAGER_H */
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 2b1156cb..94f046e5 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -20,6 +20,9 @@
 
 #include <config.h>
 
+#include <sys/types.h>
+#include <pwd.h>
+
 #include <glib.h>
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
@@ -41,8 +44,11 @@ typedef struct {
 	char *owner;
 	char *identifier;
 	uid_t owner_uid;
+	char *owner_username;
 	guint32 hash;
 
+	GSList *permissions;
+
 	NMDBusManager *dbus_mgr;
 	DBusGProxy *proxy;
 
@@ -134,6 +140,15 @@ nm_secret_agent_get_owner_uid  (NMSecretAgent *agent)
 	return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner_uid;
 }
 
+const char *
+nm_secret_agent_get_owner_username(NMSecretAgent *agent)
+{
+	g_return_val_if_fail (agent != NULL, NULL);
+	g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL);
+
+	return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner_username;
+}
+
 guint32
 nm_secret_agent_get_hash  (NMSecretAgent *agent)
 {
@@ -143,6 +158,76 @@ nm_secret_agent_get_hash  (NMSecretAgent *agent)
 	return NM_SECRET_AGENT_GET_PRIVATE (agent)->hash;
 }
 
+/**
+ * nm_secret_agent_add_permission:
+ * @agent: A #NMSecretAgent.
+ * @permission: The name of the permission
+ *
+ * Records whether or not the agent has a given permission.
+ */
+void
+nm_secret_agent_add_permission (NMSecretAgent *agent,
+                                const char *permission,
+                                gboolean allowed)
+{
+	NMSecretAgentPrivate *priv;
+	GSList *iter;
+
+	g_return_if_fail (agent != NULL);
+	g_return_if_fail (permission != NULL);
+
+	priv = NM_SECRET_AGENT_GET_PRIVATE (agent);
+
+	/* Check if the permission is already in the list */
+	for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (permission, iter->data) == 0) {
+			/* If the permission is no longer allowed, remove it from the
+			 * list.  If it is now allowed, do nothing since it's already
+			 * in the list.
+			 */
+			if (allowed == FALSE) {
+				g_free (iter->data);
+				priv->permissions = g_slist_delete_link (priv->permissions, iter);
+			}
+			return;
+		}
+	}
+
+	/* New permission that's allowed */
+	if (allowed)
+		priv->permissions = g_slist_prepend (priv->permissions, g_strdup (permission));
+}
+
+/**
+ * nm_secret_agent_has_permission:
+ * @agent: A #NMSecretAgent.
+ * @permission: The name of the permission to check for
+ *
+ * Returns whether or not the agent has the given permission.
+ * 
+ * Returns: %TRUE if the agent has the given permission, %FALSE if it does not
+ * or if the permission was not previous recorded with
+ * nm_secret_agent_add_permission().
+ */
+gboolean
+nm_secret_agent_has_permission (NMSecretAgent *agent, const char *permission)
+{
+	NMSecretAgentPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (agent != NULL, FALSE);
+	g_return_val_if_fail (permission != NULL, FALSE);
+
+	priv = NM_SECRET_AGENT_GET_PRIVATE (agent);
+
+	/* Check if the permission is already in the list */
+	for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (permission, iter->data) == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
 /*************************************************************/
 
 static void
@@ -189,6 +274,9 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
 
 	hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
 
+	/* Mask off the private ONLY_SYSTEM flag if present */
+	flags &= ~NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM;
+
 	r = request_new (self, nm_connection_get_path (connection), setting_name, callback, callback_data);
 	r->call = dbus_g_proxy_begin_call_with_timeout (priv->proxy,
 	                                                "GetSecrets",
@@ -327,11 +415,17 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr,
 	NMSecretAgent *self;
 	NMSecretAgentPrivate *priv;
 	DBusGConnection *bus;
-	char *hash_str;
+	char *hash_str, *username;
+	struct passwd *pw;
 
 	g_return_val_if_fail (owner != NULL, NULL);
 	g_return_val_if_fail (identifier != NULL, NULL);
 
+	pw = getpwuid (owner_uid);
+	g_return_val_if_fail (pw != NULL, NULL);
+	g_return_val_if_fail (pw->pw_name[0] != '\0', NULL);
+	username = g_strdup (pw->pw_name);
+
 	self = (NMSecretAgent *) g_object_new (NM_TYPE_SECRET_AGENT, NULL);
 	if (self) {
 		priv = NM_SECRET_AGENT_GET_PRIVATE (self);
@@ -339,6 +433,7 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr,
 		priv->owner = g_strdup (owner);
 		priv->identifier = g_strdup (identifier);
 		priv->owner_uid = owner_uid;
+		priv->owner_username = g_strdup (username);
 
 		hash_str = g_strdup_printf ("%08u%s", owner_uid, identifier);
 		priv->hash = g_str_hash (hash_str);
@@ -353,6 +448,7 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr,
 		g_assert (priv->proxy);
 	}
 
+	g_free (username);
 	return self;
 }
 
@@ -376,6 +472,10 @@ dispose (GObject *object)
 		g_free (priv->description);
 		g_free (priv->owner);
 		g_free (priv->identifier);
+		g_free (priv->owner_username);
+
+		g_slist_foreach (priv->permissions, (GFunc) g_free, NULL);
+		g_slist_free (priv->permissions);
 
 		g_hash_table_destroy (priv->requests);
 		g_object_unref (priv->proxy);
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 597940b4..29b02be1 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -60,8 +60,17 @@ const char *nm_secret_agent_get_identifier (NMSecretAgent *agent);
 
 uid_t       nm_secret_agent_get_owner_uid  (NMSecretAgent *agent);
 
+const char *nm_secret_agent_get_owner_username (NMSecretAgent *agent);
+
 guint32     nm_secret_agent_get_hash       (NMSecretAgent *agent);
 
+void        nm_secret_agent_add_permission (NMSecretAgent *agent,
+                                            const char *permission,
+                                            gboolean allowed);
+
+gboolean    nm_secret_agent_has_permission (NMSecretAgent *agent,
+                                            const char *permission);
+
 typedef void (*NMSecretAgentCallback) (NMSecretAgent *agent,
                                        gconstpointer call,
                                        GHashTable *new_secrets, /* NULL for save & delete */
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 60de6b06..4b3a56cd 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include <string.h>
+#include <netinet/ether.h>
 
 #include <NetworkManager.h>
 #include <dbus/dbus-glib-lowlevel.h>
@@ -38,8 +39,10 @@
 #include "nm-manager-auth.h"
 #include "nm-marshal.h"
 #include "nm-agent-manager.h"
+#include "NetworkManagerUtils.h"
 
 #define SETTINGS_TIMESTAMPS_FILE  LOCALSTATEDIR"/lib/NetworkManager/timestamps"
+#define SETTINGS_SEEN_BSSIDS_FILE LOCALSTATEDIR"/lib/NetworkManager/seen-bssids"
 
 static void impl_settings_connection_get_settings (NMSettingsConnection *connection,
                                                    DBusGMethodInvocation *context);
@@ -91,7 +94,8 @@ typedef struct {
 	NMSessionMonitor *session_monitor;
 	guint session_changed_id;
 
-	guint64 timestamp; /* Up-to-date timestamp of connection use */
+	guint64 timestamp;   /* Up-to-date timestamp of connection use */
+	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
 } NMSettingsConnectionPrivate;
 
 /**************************************************************/
@@ -238,6 +242,58 @@ session_changed_cb (NMSessionMonitor *self, gpointer user_data)
 
 /**************************************************************/
 
+/* Return TRUE if any active user in the connection's ACL has the given
+ * permission without having to authorize for it via PolicyKit.  Connections
+ * visible to everyone automatically pass the check.
+ */
+gboolean
+nm_settings_connection_check_permission (NMSettingsConnection *self,
+                                         const char *permission)
+{
+	NMSettingsConnectionPrivate *priv;
+	NMSettingConnection *s_con;
+	guint32 num, i;
+	const char *puser;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
+
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	if (priv->visible == FALSE)
+		return FALSE;
+
+	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
+	g_assert (s_con);
+
+	/* Check every user in the ACL for a session */
+	num = nm_setting_connection_get_num_permissions (s_con);
+	if (num == 0) {
+		/* Visible to all so it's OK to auto-activate */
+		return TRUE;
+	}
+
+	for (i = 0; i < num; i++) {
+		/* For each user get their secret agent and check if that agent has the
+		 * required permission.
+		 *
+		 * FIXME: what if the user isn't running an agent?  PolKit needs a bus
+		 * name or a PID but if the user isn't running an agent they won't have
+		 * either.
+		 */
+		if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
+			NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
+
+			if (agent && nm_secret_agent_has_permission (agent, permission))
+				return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+/**************************************************************/
+
 static void
 only_system_secrets_cb (NMSetting *setting,
                         const char *key,
@@ -326,25 +382,14 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 	new_settings = nm_connection_to_hash (new, NM_SETTING_HASH_FLAG_ALL);
 	g_assert (new_settings);
 	if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) {
-		GHashTableIter iter;
-		NMSetting *setting;
-		const char *setting_name;
-		GHashTable *setting_hash;
-
 		/* Copy the connection to keep its secrets around even if NM
 		 * calls nm_connection_clear_secrets().
 		 */
 		update_secrets_cache (self);
 
 		/* And add the transient secrets back */
-		if (transient_secrets) {
-			g_hash_table_iter_init (&iter, transient_secrets);
-			while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
-				setting = nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name);
-				if (setting)
-					nm_setting_update_secrets (setting, setting_hash, NULL);
-			}
-		}
+		if (transient_secrets)
+			nm_connection_update_secrets (NM_CONNECTION (self), NULL, transient_secrets, NULL);
 
 		nm_settings_connection_recheck_visibility (self);
 		success = TRUE;
@@ -455,12 +500,20 @@ commit_changes (NMSettingsConnection *connection,
 }
 
 static void
-remove_timestamp_from_db (NMSettingsConnection *connection)
+remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
 {
-	GKeyFile *timestamps_file;
+	GKeyFile *key_file;
+	const char *db_file;
 
-	timestamps_file = g_key_file_new ();
-	if (g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
+	if (strcmp (db_name, "timestamps") == 0)
+		db_file = SETTINGS_TIMESTAMPS_FILE;
+	else if (strcmp (db_name, "seen-bssids") == 0)
+		db_file = SETTINGS_SEEN_BSSIDS_FILE;
+	else
+		return;
+
+	key_file = g_key_file_new ();
+	if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
 		const char *connection_uuid;
 		char *data;
 		gsize len;
@@ -468,18 +521,18 @@ remove_timestamp_from_db (NMSettingsConnection *connection)
 
 		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
 
-		g_key_file_remove_key (timestamps_file, "timestamps", connection_uuid, NULL);
-		data = g_key_file_to_data (timestamps_file, &len, &error);
+		g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
+		data = g_key_file_to_data (key_file, &len, &error);
 		if (data) {
-			g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
+			g_file_set_contents (db_file, data, len, &error);
 			g_free (data);
 		}
 		if (error) {
-			nm_log_warn (LOGD_SETTINGS, "error writing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
+			nm_log_warn (LOGD_SETTINGS, "error writing %s file '%s': %s", db_name, db_file, error->message);
 			g_error_free (error);
 		}
 	}
-	g_key_file_free (timestamps_file);
+	g_key_file_free (key_file);
 }
 
 static void
@@ -499,7 +552,10 @@ do_delete (NMSettingsConnection *connection,
 	nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0);
 
 	/* Remove timestamp from timestamps database file */
-	remove_timestamp_from_db (connection);
+	remove_entry_from_db (connection, "timestamps");
+
+	/* Remove connection from seen-bssids database file */
+	remove_entry_from_db (connection, "seen-bssids");
 
 	/* Signal the connection is removed and deleted */
 	g_signal_emit (connection, signals[REMOVED], 0);
@@ -980,21 +1036,21 @@ check_writable (NMConnection *connection, GError **error)
 
 static void
 get_settings_auth_cb (NMSettingsConnection *self, 
-	                  DBusGMethodInvocation *context,
-	                  gulong sender_uid,
-	                  GError *error,
-	                  gpointer data)
+                      DBusGMethodInvocation *context,
+                      gulong sender_uid,
+                      GError *error,
+                      gpointer data)
 {
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else {
 		GHashTable *settings;
-	 	NMConnection *dupl_con;
+		NMConnection *dupl_con;
 		NMSettingConnection *s_con;
 		guint64 timestamp;
 
-	 	dupl_con = nm_connection_duplicate (NM_CONNECTION (self));
- 		g_assert (dupl_con);
+		dupl_con = nm_connection_duplicate (NM_CONNECTION (self));
+		g_assert (dupl_con);
 
 		/* Timestamp is not updated in connection's 'timestamp' property,
 		 * because it would force updating the connection and in turn
@@ -1004,7 +1060,7 @@ get_settings_auth_cb (NMSettingsConnection *self,
 		 */
 		timestamp = nm_settings_connection_get_timestamp (self);
 		if (timestamp) {
-			s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (dupl_con), NM_TYPE_SETTING_CONNECTION));
+			s_con = nm_connection_get_setting_connection (NM_CONNECTION (dupl_con));
 			g_assert (s_con);
 			g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
 		}
@@ -1017,7 +1073,7 @@ get_settings_auth_cb (NMSettingsConnection *self,
 		g_assert (settings);
 		dbus_g_method_return (context, settings);
 		g_hash_table_destroy (settings);
- 		g_object_unref (dupl_con);
+		g_object_unref (dupl_con);
 	}
 }
 
@@ -1042,31 +1098,30 @@ con_update_cb (NMSettingsConnection *connection,
 }
 
 static void
-only_agent_secrets_cb (NMSetting *setting,
-                       const char *key,
-                       const GValue *value,
-                       GParamFlags flags,
-                       gpointer user_data)
+secrets_filter_cb (NMSetting *setting,
+                   const char *key,
+                   const GValue *value,
+                   GParamFlags flags,
+                   gpointer user_data)
 {
-	if (flags & NM_SETTING_PARAM_SECRET) {
-		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
+	NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	const char *secret_name = NULL;
+	GHashTableIter iter;
 
-		/* Clear out system-owned or always-ask secrets */
+	if (flags & NM_SETTING_PARAM_SECRET) {
 		if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
-			GHashTableIter iter;
-			const char *secret_name = NULL;
-
 			/* VPNs are special; need to handle each secret separately */
 			g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
-			while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) {
+			while (g_hash_table_iter_next (&iter, (gpointer) &secret_name, NULL)) {
 				secret_flags = NM_SETTING_SECRET_FLAG_NONE;
 				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
-				if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+				if (!(secret_flags & filter_flags))
 					nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
 			}
 		} else {
 			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
-			if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+			if (!(secret_flags & filter_flags))
 				g_object_set (G_OBJECT (setting), key, NULL, NULL);
 		}
 	}
@@ -1081,23 +1136,52 @@ update_auth_cb (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMConnection *new_settings = data;
-	NMConnection *for_agent;
+	NMConnection *for_agent, *dup;
+	NMSettingSecretFlags filter_flags;
+	GHashTable *hash;
+	GError *local = NULL;
 
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else {
+		/* Cache the new secrets since they may get overwritten by the replace
+		 * when transient secrets are copied back.
+		 */
+		dup = nm_connection_duplicate (new_settings);
+
 		/* Update and commit our settings. */
 		nm_settings_connection_replace_and_commit (self,
 		                                           new_settings,
 		                                           con_update_cb,
 		                                           context);
 
+		/* Copy new agent secrets back to the connection */
+		filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED;
+		nm_connection_for_each_setting_value (dup,
+		                                      secrets_filter_cb,
+		                                      GUINT_TO_POINTER (filter_flags));
+		hash = nm_connection_to_hash (dup, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+		g_object_unref (dup);
+
+		if (hash) {
+			if (!nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, &local)) {
+				nm_log_warn (LOGD_SETTINGS, "Failed to update connection secrets: (%d) %s",
+				             local ? local->code : -1,
+				             local && local->message ? local->message : "(unknown)");
+				g_clear_error (&local);
+			}
+			g_hash_table_destroy (hash);
+		}
+
 		/* Dupe the connection and clear out non-agent-owned secrets so we can
 		 * send the agent-owned ones to agents to be saved.  Only send them to
 		 * agents of the same UID as the Update() request sender.
 		 */
 		for_agent = nm_connection_duplicate (NM_CONNECTION (self));
-		nm_connection_for_each_setting_value (for_agent, only_agent_secrets_cb, NULL);
+		filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED;
+		nm_connection_for_each_setting_value (for_agent,
+		                                      secrets_filter_cb,
+		                                      GUINT_TO_POINTER (filter_flags));
 		nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, TRUE, sender_uid);
 		g_object_unref (for_agent);
 	}
@@ -1272,6 +1356,8 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self,
 		update_secrets_cache (self);
 
 		hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+		if (!hash)
+			hash = g_hash_table_new (NULL, NULL);
 		dbus_g_method_return (context, hash);
 		g_hash_table_destroy (hash);
 	}
@@ -1440,6 +1526,181 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
 	g_key_file_free (timestamps_file);
 }
 
+static guint
+mac_hash (gconstpointer v)
+{
+	const guint8 *p = v;
+	guint32 i, h = 5381;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		h = (h << 5) + h + p[i];
+	return h;
+}
+
+static gboolean
+mac_equal (gconstpointer a, gconstpointer b)
+{
+	return memcmp (a, b, ETH_ALEN) == 0;
+}
+
+static guint8 *
+mac_dup (const struct ether_addr *old)
+{
+	guint8 *new;
+
+	g_return_val_if_fail (old != NULL, NULL);
+
+	new = g_malloc0 (ETH_ALEN);
+	memcpy (new, old, ETH_ALEN);
+	return new;
+}
+
+/**
+ * nm_settings_connection_has_seen_bssid:
+ * @connection: the #NMSettingsConnection
+ * @bssid: the BSSID to check the seen BSSID list for
+ *
+ * Returns: TRUE if the given @bssid is in the seen BSSIDs list
+ **/
+gboolean
+nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+                                       const struct ether_addr *bssid)
+{
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (bssid != NULL, FALSE);
+
+	return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->seen_bssids, bssid);
+}
+
+/**
+ * nm_settings_connection_add_seen_bssid:
+ * @connection: the #NMSettingsConnection
+ * @seen_bssid: BSSID to set into the connection and to store into
+ * the seen-bssids database
+ *
+ * Updates the connection and seen-bssids database with the provided BSSID.
+ **/
+void
+nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+                                       const struct ether_addr *seen_bssid)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	const char *connection_uuid;
+	GKeyFile *seen_bssids_file;
+	char *data, *bssid_str;
+	const char **list;
+	gsize len;
+	GError *error = NULL;
+	GHashTableIter iter;
+	guint n;
+
+	g_return_if_fail (seen_bssid != NULL);
+
+	if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
+		return;  /* Already in the list */
+
+	/* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
+	bssid_str = nm_ether_ntop (seen_bssid);
+	g_return_if_fail (bssid_str != NULL);
+	g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str);
+
+	/* Build up a list of all the BSSIDs in string form */
+	n = 0;
+	list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
+	g_hash_table_iter_init (&iter, priv->seen_bssids);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
+		list[n++] = bssid_str;
+
+	/* Save BSSID to seen-bssids file */
+	seen_bssids_file = g_key_file_new ();
+	g_key_file_set_list_separator (seen_bssids_file, ',');
+	if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
+		if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
+			nm_log_warn (LOGD_SETTINGS, "error parsing seen-bssids file '%s': %s",
+			             SETTINGS_SEEN_BSSIDS_FILE, error->message);
+		}
+		g_clear_error (&error);
+	}
+
+	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+	g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
+	g_free (list);
+
+	data = g_key_file_to_data (seen_bssids_file, &len, &error);
+	if (data) {
+		g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
+		g_free (data);
+	}
+	g_key_file_free (seen_bssids_file);
+
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "error saving seen-bssids to file '%s': %s",
+		             SETTINGS_SEEN_BSSIDS_FILE, error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+add_seen_bssid_string (NMSettingsConnection *self, const char *bssid)
+{
+	struct ether_addr mac;
+
+	g_return_if_fail (bssid != NULL);
+	if (ether_aton_r (bssid, &mac)) {
+		g_hash_table_insert (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids,
+		                     mac_dup (&mac),
+		                     g_strdup (bssid));
+	}
+}
+
+/**
+ * nm_settings_connection_read_and_fill_seen_bssids:
+ * @connection: the #NMSettingsConnection
+ *
+ * Retrieves seen BSSIDs of the connection from database file and stores then into the
+ * connection private data.
+ **/
+void
+nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	const char *connection_uuid;
+	GKeyFile *seen_bssids_file;
+	char **tmp_strv = NULL;
+	gsize i, len = 0;
+	NMSettingWireless *s_wifi;
+
+	/* Get seen BSSIDs from database file */
+	seen_bssids_file = g_key_file_new ();
+	g_key_file_set_list_separator (seen_bssids_file, ',');
+	if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
+		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+		tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
+	}
+	g_key_file_free (seen_bssids_file);
+
+	/* Update connection's seen-bssids */
+	if (tmp_strv) {
+		g_hash_table_remove_all (priv->seen_bssids);
+		for (i = 0; i < len; i++)
+			add_seen_bssid_string (connection, tmp_strv[i]);
+		g_strfreev (tmp_strv);
+	} else {
+		/* If this connection didn't have an entry in the seen-bssids database,
+		 * maybe this is the first time we've read it in, so populate the
+		 * seen-bssids list from the deprecated seen-bssids property of the
+		 * wifi setting.
+		 */
+		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
+		if (s_wifi) {
+			len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
+			for (i = 0; i < len; i++)
+				add_seen_bssid_string (connection, nm_setting_wireless_get_seen_bssid (s_wifi, i));
+		}
+	}
+}
+
 /**************************************************************/
 
 static void
@@ -1463,6 +1724,8 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	                                             self);
 
 	priv->agent_mgr = nm_agent_manager_get ();
+
+	priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free);
 }
 
 static void
@@ -1490,6 +1753,8 @@ dispose (GObject *object)
 		nm_agent_manager_cancel_secrets (priv->agent_mgr, GPOINTER_TO_UINT (iter->data));
 	g_slist_free (priv->reqs);
 
+	g_hash_table_destroy (priv->seen_bssids);
+
 	set_visible (self, FALSE);
 
 	if (priv->session_changed_id)
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 116bfdcc..bc9e3c47 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -24,6 +24,7 @@
 
 #include <nm-connection.h>
 #include "nm-settings-flags.h"
+#include <net/ethernet.h>
 
 G_BEGIN_DECLS
 
@@ -116,6 +117,9 @@ gboolean nm_settings_connection_is_visible (NMSettingsConnection *self);
 
 void nm_settings_connection_recheck_visibility (NMSettingsConnection *self);
 
+gboolean nm_settings_connection_check_permission (NMSettingsConnection *self,
+                                                  const char *permission);
+
 void nm_settings_connection_signal_remove (NMSettingsConnection *self);
 
 guint64 nm_settings_connection_get_timestamp (NMSettingsConnection *connection);
@@ -124,6 +128,14 @@ void nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
 
 void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection);
 
+gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+                                                const struct ether_addr *bssid);
+
+void nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+                                            const struct ether_addr *seen_bssid);
+
+void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection);
+
 G_END_DECLS
 
 #endif /* NM_SETTINGS_CONNECTION_H */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index e23e8d13..733e9145 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -135,6 +135,7 @@ enum {
 	CONNECTION_REMOVED,
 	CONNECTION_VISIBILITY_CHANGED,
 	CONNECTIONS_LOADED,
+	AGENT_REGISTERED,
 
 	NEW_CONNECTION, /* exported, not used internally */
 	LAST_SIGNAL
@@ -699,6 +700,18 @@ connection_visibility_changed (NMSettingsConnection *connection,
 	               connection);
 }
 
+static void
+secret_agent_registered (NMAgentManager *agent_mgr,
+                         NMSecretAgent *agent,
+                         gpointer user_data)
+{
+	/* Re-emit for listeners like NMPolicy */
+	g_signal_emit (NM_SETTINGS (user_data),
+	               signals[AGENT_REGISTERED],
+	               0,
+	               agent);
+}
+
 #define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
 #define NM_OPENCONNECT_KEY_GATEWAY "gateway"
 #define NM_OPENCONNECT_KEY_COOKIE "cookie"
@@ -773,6 +786,9 @@ claim_connection (NMSettings *self,
 	/* Read timestamp from look-aside file and put it into the connection's data */
 	nm_settings_connection_read_and_fill_timestamp (connection);
 
+	/* Read seen-bssids from look-aside file and put it into the connection's data */
+	nm_settings_connection_read_and_fill_seen_bssids (connection);
+
 	/* Ensure it's initial visibility is up-to-date */
 	nm_settings_connection_recheck_visibility (connection);
 
@@ -1527,6 +1543,8 @@ nm_settings_init (NMSettings *self)
 	 * recreated often.
 	 */
 	priv->agent_mgr = nm_agent_manager_get ();
+
+	g_signal_connect (priv->agent_mgr, "agent-registered", G_CALLBACK (secret_agent_registered), self);
 }
 
 static void
@@ -1690,6 +1708,16 @@ nm_settings_class_init (NMSettingsClass *class)
 	                              g_cclosure_marshal_VOID__VOID,
 	                              G_TYPE_NONE, 0);
 
+	signals[AGENT_REGISTERED] =
+		g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMSettingsClass, agent_registered),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+
 	signals[NEW_CONNECTION] = 
 	                g_signal_new ("new-connection",
 	                              G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index a5cb4d7c..66d41cce 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -19,7 +19,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2011 Red Hat, Inc.
  * (C) Copyright 2008 Novell, Inc.
  */
 
@@ -31,6 +31,7 @@
 #include "nm-settings-connection.h"
 #include "nm-system-config-interface.h"
 #include "nm-device.h"
+#include "nm-secret-agent.h"
 
 #define NM_TYPE_SETTINGS            (nm_settings_get_type ())
 #define NM_SETTINGS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings))
@@ -48,6 +49,7 @@
 #define NM_SETTINGS_SIGNAL_CONNECTION_REMOVED            "connection-removed"
 #define NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED "connection-visibility-changed"
 #define NM_SETTINGS_SIGNAL_CONNECTIONS_LOADED            "connections-loaded"
+#define NM_SETTINGS_SIGNAL_AGENT_REGISTERED              "agent-registered"
 
 typedef struct {
 	GObject parent_instance;
@@ -68,6 +70,8 @@ typedef struct {
 	void (*connection_visibility_changed) (NMSettings *self, NMSettingsConnection *connection);
 
 	void (*connections_loaded) (NMSettings *self);
+
+	void (*agent_registered) (NMSettings *self, NMSecretAgent *agent);
 } NMSettingsClass;
 
 GType nm_settings_get_type (void);
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
index 433f933b..04d3d3e4 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
 #include <string.h>
@@ -181,12 +181,13 @@ nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self)
 static void
 commit_changes (NMSettingsConnection *connection,
                 NMSettingsConnectionCommitFunc callback,
-	            gpointer user_data)
+                gpointer user_data)
 {
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection);
 	GError *error = NULL;
 	NMConnection *reread;
 	char *unmanaged = NULL, *keyfile = NULL, *routefile = NULL, *route6file = NULL;
+	gboolean same = FALSE;
 
 	/* To ensure we don't rewrite files that are only changed from other
 	 * processes on-disk, read the existing connection back in and only rewrite
@@ -200,28 +201,36 @@ commit_changes (NMSettingsConnection *connection,
 	g_free (routefile);
 	g_free (route6file);
 
-	if (reread && nm_connection_compare (NM_CONNECTION (connection),
-	                                     reread,
-	                                     NM_SETTING_COMPARE_FLAG_EXACT))
-		goto out;
+	if (reread) {
+		same = nm_connection_compare (NM_CONNECTION (connection),
+		                              reread,
+		                              NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+		                                NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS);
+		g_object_unref (reread);
 
-	if (!writer_update_connection (NM_CONNECTION (connection),
-	                               IFCFG_DIR,
-	                               priv->path,
-	                               priv->keyfile,
-	                               &error)) {
+		/* Don't bother writing anything out if in-memory and on-disk data are the same */
+		if (same) {
+			/* But chain up to parent to handle success - emits updated signal */
+			NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data);
+			return;
+		}
+	}
+
+	if (writer_update_connection (NM_CONNECTION (connection),
+	                              IFCFG_DIR,
+	                              priv->path,
+	                              priv->keyfile,
+	                              &error)) {
+		/* Chain up to parent to handle success */
+		NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data);
+	} else {
+		/* Otherwise immediate error */
 		callback (connection, error, user_data);
 		g_error_free (error);
-		return;
 	}
-
-out:
-	if (reread)
-		g_object_unref (reread);
-	NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data);
 }
 
-static void 
+static void
 do_delete (NMSettingsConnection *connection,
 	       NMSettingsConnectionDeleteFunc callback,
 	       gpointer user_data)
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index 7915c467..ed0dceca 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -269,19 +269,24 @@ connection_new_or_changed (SCPluginIfcfg *self,
 
 	/* Successfully read connection changes */
 
-	/* When the connections are the same, nothing is done */
-	if (nm_connection_compare (NM_CONNECTION (existing),
-	                           NM_CONNECTION (new),
-	                           NM_SETTING_COMPARE_FLAG_EXACT)) {
+	old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (existing));
+	new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new));
+
+	/* When interface is unmanaged or the connections and unmanaged specs are the same
+	 * there's nothing to do */
+	if (   (g_strcmp0 (old_unmanaged, new_unmanaged) == 0 && new_unmanaged != NULL)
+	    || (   nm_connection_compare (NM_CONNECTION (existing),
+	                                  NM_CONNECTION (new),
+	                                  NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+	                                    NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+	        && g_strcmp0 (old_unmanaged, new_unmanaged) == 0)) {
+
 		g_object_unref (new);
 		return;
 	}
 
 	PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path);
 
-	old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (existing));
-	new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new));
-
 	if (new_unmanaged) {
 		if (!old_unmanaged) {
 			/* Unexport the connection by telling the settings service it's
@@ -289,6 +294,11 @@ connection_new_or_changed (SCPluginIfcfg *self,
 			 * unmanaged specs have changed.
 			 */
 			nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (existing));
+			/* Remove the path so that claim_connection() doesn't complain later when
+			 * interface gets managed and connection is re-added. */
+			nm_connection_set_path (NM_CONNECTION (existing), NULL);
+
+			g_object_set (existing, NM_IFCFG_CONNECTION_UNMANAGED, new_unmanaged, NULL);
 			g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
 		}
 	} else {
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index a6f9ca85..cdf5889e 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -176,9 +176,9 @@ read_mac_address (shvarFile *ifcfg, const char *key, GByteArray **array, GError
 
 	mac = ether_aton (value);
 	if (!mac) {
-		g_free (value);
 		g_set_error (error, IFCFG_PLUGIN_ERROR, 0,
 		             "%s: the MAC address '%s' was invalid.", key, value);
+		g_free (value);
 		return FALSE;
 	}
 
@@ -574,7 +574,8 @@ read_full_ip4_address (shvarFile *ifcfg,
 	if (!nm_ip4_address_get_prefix (addr)) {
 		if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error))
 			goto error;
-		nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp));
+		if (tmp)
+			nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp));
 	}
 
 	/* Try to autodetermine the prefix for the address' class */
@@ -661,7 +662,8 @@ read_one_ip4_route (shvarFile *ifcfg,
 	/* Prefix */
 	if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error))
 		goto out;
-	nm_ip4_route_set_prefix (route, nm_utils_ip4_netmask_to_prefix (tmp));
+	if (tmp)
+		nm_ip4_route_set_prefix (route, nm_utils_ip4_netmask_to_prefix (tmp));
 
 	/* Validate the prefix */
 	if (  !nm_ip4_route_get_prefix (route)
@@ -788,7 +790,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError
 		if (prefix) {
 			errno = 0;
 			prefix_int = strtol (prefix, NULL, 10);
-			if (errno || prefix_int < 0 || prefix_int > 32) {
+			if (errno || prefix_int <= 0 || prefix_int > 32) {
 				g_set_error (error, IFCFG_PLUGIN_ERROR, 0,
 					     "Invalid IP4 route destination prefix '%s'", prefix);
 				g_free (prefix);
@@ -973,9 +975,9 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro
 
 	const char *pattern_empty = "^\\s*(\\#.*)?$";
 	const char *pattern_to1 = "^\\s*(" IPV6_ADDR_REGEX "|default)"  /* IPv6 or 'default' keyword */
-	                          "(?:/(\\d{1,2}))?";                   /* optional prefix */
+	                          "(?:/(\\d{1,3}))?";                   /* optional prefix */
 	const char *pattern_to2 = "to\\s+(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */
-	                          "(?:/(\\d{1,2}))?";                   /* optional prefix */
+	                          "(?:/(\\d{1,3}))?";                   /* optional prefix */
 	const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")";       /* IPv6 of gateway */
 	const char *pattern_metric = "metric\\s+(\\d+)";                /* metric */
 
@@ -1041,7 +1043,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro
 		if (prefix) {
 			errno = 0;
 			prefix_int = strtol (prefix, NULL, 10);
-			if (errno || prefix_int < 0 || prefix_int > 128) {
+			if (errno || prefix_int <= 0 || prefix_int > 128) {
 				g_set_error (error, IFCFG_PLUGIN_ERROR, 0,
 					     "Invalid IP6 route destination prefix '%s'", prefix);
 				g_free (prefix);
@@ -1228,15 +1230,11 @@ make_ip4_setting (shvarFile *ifcfg,
 		    && !tmp_ip4_0 && !tmp_prefix_0 && !tmp_netmask_0
 		    && !tmp_ip4_1 && !tmp_prefix_1 && !tmp_netmask_1
 		    && !tmp_ip4_2 && !tmp_prefix_2 && !tmp_netmask_2) {
-			if (valid_ip6_config) {
+			if (valid_ip6_config)
 				/* Nope, no IPv4 */
-				g_object_set (s_ip4,
-				              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
-				              NULL);
-				return NM_SETTING (s_ip4);
-			}
-
-			method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+				method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+			else
+				method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 		}
 		g_free (tmp_ip4);
 		g_free (tmp_prefix);
@@ -1260,6 +1258,9 @@ make_ip4_setting (shvarFile *ifcfg,
 	              NM_SETTING_IP4_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV4_FAILURE_FATAL", TRUE),
 	              NULL);
 
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+		return NM_SETTING (s_ip4);
+
 	/* Handle manual settings */
 	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
 		NMIP4Address *addr;
@@ -1418,7 +1419,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	char *value = NULL;
 	char *str_value;
 	char *route6_path = NULL;
-	gboolean bool_value, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE;
+	gboolean ipv6init, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE;
 	char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
 	guint32 i;
 	shvarFile *network_ifcfg;
@@ -1431,26 +1432,6 @@ make_ip6_setting (shvarFile *ifcfg,
 		return NULL;
 	}
 
-	/* Is IPV6 enabled? Set method to "ignored", when not enabled */
-	str_value = svGetValue (ifcfg, "IPV6INIT", FALSE);
-	bool_value = svTrueValue (ifcfg, "IPV6INIT", FALSE);
-	if (!str_value) {
-		network_ifcfg = svNewFile (network_file);
-		if (network_ifcfg) {
-			bool_value = svTrueValue (network_ifcfg, "IPV6INIT", FALSE);
-			svCloseFile (network_ifcfg);
-		}
-	}
-	g_free (str_value);
-
-	if (!bool_value) {
-		/* IPv6 is disabled */
-		g_object_set (s_ip6,
-		              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
-		              NULL);
-		return NM_SETTING (s_ip6);
-	}
-
 	/* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the
 	 * opposite meaning from never-default. The default if IPV6_DEFROUTE is not
 	 * specified is IPV6_DEFROUTE=yes which means that this connection can be used
@@ -1493,23 +1474,39 @@ make_ip6_setting (shvarFile *ifcfg,
 	}
 
 	/* Find out method property */
-	ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
-	ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
-	dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE);
-
-	if (ipv6_autoconf)
-		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
-	else if (dhcp6)
-		method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
+	/* Is IPV6 enabled? Set method to "ignored", when not enabled */
+	str_value = svGetValue (ifcfg, "IPV6INIT", FALSE);
+	ipv6init = svTrueValue (ifcfg, "IPV6INIT", FALSE);
+	if (!str_value) {
+		network_ifcfg = svNewFile (network_file);
+		if (network_ifcfg) {
+			ipv6init = svTrueValue (network_ifcfg, "IPV6INIT", FALSE);
+			svCloseFile (network_ifcfg);
+		}
+	}
+	g_free (str_value);
+
+	if (!ipv6init)
+		method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;  /* IPv6 is disabled */
 	else {
-		/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
-		str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
-		if (!str_value)
-			str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
+		ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
+		ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
+		dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE);
+
+		if (ipv6_autoconf)
+			method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+		else if (dhcp6)
+			method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
+		else {
+			/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
+			str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
+			if (!str_value)
+				str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
 
-		if (!str_value)
-			method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
-		g_free (str_value);
+			if (!str_value)
+				method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+			g_free (str_value);
+		}
 	}
 	/* TODO - handle other methods */
 
@@ -1521,6 +1518,10 @@ make_ip6_setting (shvarFile *ifcfg,
 	              NM_SETTING_IP6_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV6_FAILURE_FATAL", FALSE),
 	              NULL);
 
+	/* Don't bother to read IP, DNS and routes when IPv6 is disabled */
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+		return NM_SETTING (s_ip6);
+
 	if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
 		NMIP6Address *addr;
 		char *val;
@@ -2234,6 +2235,7 @@ eap_peap_reader (const char *eap_method,
                  gboolean phase2,
                  GError **error)
 {
+	char *anon_ident = NULL;
 	char *ca_cert = NULL;
 	char *real_cert_path = NULL;
 	char *inner_auth = NULL;
@@ -2275,6 +2277,10 @@ eap_peap_reader (const char *eap_method,
 	if (svTrueValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE))
 		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
 
+	anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE);
+	if (anon_ident && strlen (anon_ident))
+		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
+
 	inner_auth = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE);
 	if (!inner_auth) {
 		g_set_error (error, IFCFG_PLUGIN_ERROR, 0,
@@ -2324,6 +2330,7 @@ done:
 	g_free (peapver);
 	g_free (real_cert_path);
 	g_free (ca_cert);
+	g_free (anon_ident);
 	return success;
 }
 
@@ -2737,6 +2744,7 @@ make_wireless_setting (shvarFile *ifcfg,
 {
 	NMSettingWireless *s_wireless;
 	GByteArray *array = NULL;
+	GSList *macaddr_blacklist = NULL;
 	char *value;
 
 	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
@@ -2770,6 +2778,33 @@ make_wireless_setting (shvarFile *ifcfg,
 			g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, array, NULL);
 			g_byte_array_free (array, TRUE);
 		}
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: %s", (*error)->message);
+		g_clear_error (error);
+	}
+
+	value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
+	if (value) {
+		char **list = NULL, **iter;
+		struct ether_addr addr;
+
+		list = g_strsplit_set (value, " \t", 0);
+		for (iter = list; iter && *iter; iter++) {
+			if (**iter == '\0')
+				continue;
+			if (!ether_aton_r (*iter, &addr)) {
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: invalid MAC in HWADDR_BLACKLIST '%s'", *iter);
+				continue;
+			}
+			macaddr_blacklist = g_slist_prepend (macaddr_blacklist, *iter);
+		}
+		if (macaddr_blacklist) {
+			macaddr_blacklist = g_slist_reverse (macaddr_blacklist);
+			g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, macaddr_blacklist, NULL);
+			g_slist_free (macaddr_blacklist);
+		}
+		g_free (value);
+		g_strfreev (list);
 	}
 
 	value = svGetValue (ifcfg, "ESSID", TRUE);
@@ -3030,6 +3065,7 @@ make_wired_setting (shvarFile *ifcfg,
 	char *value = NULL;
 	int mtu;
 	GByteArray *mac = NULL;
+	GSList *macaddr_blacklist = NULL;
 	char *nettype;
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
@@ -3162,6 +3198,33 @@ make_wired_setting (shvarFile *ifcfg,
 			g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, mac, NULL);
 			g_byte_array_free (mac, TRUE);
 		}
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: %s", (*error)->message);
+		g_clear_error (error);
+	}
+
+	value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
+	if (value) {
+		char **list = NULL, **iter;
+		struct ether_addr addr;
+
+		list = g_strsplit_set (value, " \t", 0);
+		for (iter = list; iter && *iter; iter++) {
+			if (**iter == '\0')
+				continue;
+			if (!ether_aton_r (*iter, &addr)) {
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: invalid MAC in HWADDR_BLACKLIST '%s'", *iter);
+				continue;
+			}
+			macaddr_blacklist = g_slist_prepend (macaddr_blacklist, *iter);
+		}
+		if (macaddr_blacklist) {
+			macaddr_blacklist = g_slist_reverse (macaddr_blacklist);
+			g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, macaddr_blacklist, NULL);
+			g_slist_free (macaddr_blacklist);
+		}
+		g_free (value);
+		g_strfreev (list);
 	}
 
 	value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
@@ -3246,7 +3309,7 @@ is_wireless_device (const char *iface)
 	g_return_val_if_fail (iface != NULL, FALSE);
 
 	fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (!fd)
+	if (fd == -1)
 		return FALSE;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
index 6d68eca1..27bcbbf9 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
@@ -12,4 +12,4 @@ IEEE_8021X_CA_CERT=test_ca_cert.pem
 IEEE_8021X_PEAP_VERSION=1
 IEEE_8021X_PEAP_FORCE_NEW_LABEL=yes
 IEEE_8021X_INNER_AUTH_METHODS=MSCHAPV2
-
+IEEE_8021X_ANON_IDENTITY=somebody
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index e6013f8d..d1f08aa9 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -2707,6 +2707,7 @@ test_read_wired_8021x_peap_mschapv2 (void)
 	GError *error = NULL;
 	const char *tmp;
 	const char *expected_identity = "David Smith";
+	const char *expected_anon_identity = "somebody";
 	const char *expected_password = "foobar baz";
 	gboolean success = FALSE;
 	const char *expected_ca_cert_path;
@@ -2793,6 +2794,19 @@ test_read_wired_8021x_peap_mschapv2 (void)
 	        NM_SETTING_802_1X_SETTING_NAME,
 	        NM_SETTING_802_1X_IDENTITY);
 
+	/* Anonymous Identity */
+	tmp = nm_setting_802_1x_get_anonymous_identity (s_8021x);
+	ASSERT (tmp != NULL,
+	        "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: missing %s / %s key",
+	        TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2,
+	        NM_SETTING_802_1X_SETTING_NAME,
+	        NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+	ASSERT (strcmp (tmp, expected_anon_identity) == 0,
+	        "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: unexpected %s / %s key value",
+	        TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2,
+	        NM_SETTING_802_1X_SETTING_NAME,
+	        NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+
 	/* Password */
 	tmp = nm_setting_802_1x_get_password (s_8021x);
 	ASSERT (tmp != NULL,
@@ -6491,7 +6505,7 @@ test_write_wired_static (void)
 	struct in6_addr ip6, ip6_1, ip6_2;
 	struct in6_addr route1_dest, route2_dest, route1_nexthop, route2_nexthop;
 	struct in6_addr dns6_1, dns6_2;
-	const guint32 route1_prefix = 64, route2_prefix = 0;
+	const guint32 route1_prefix = 64, route2_prefix = 128;
 	const guint32 route1_metric = 99, route2_metric = 1;
 	NMIP4Address *addr;
 	NMIP6Address *addr6;
@@ -6774,6 +6788,7 @@ test_write_wired_dhcp (void)
 
 	g_object_set (s_ip6,
 	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 
 	/* Save the ifcfg */
@@ -7234,7 +7249,10 @@ test_write_wired_static_routes (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wired-static-routes-write", "failed to verify connection: %s",
@@ -7350,7 +7368,10 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	/* 802.1x setting */
 	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
@@ -7361,6 +7382,7 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void)
 
 	g_object_set (s_8021x,
 	              NM_SETTING_802_1X_IDENTITY, "Bob Saget",
+	              NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "barney",
 	              NM_SETTING_802_1X_PASSWORD, "Kids, it was back in October 2008...",
 	              NM_SETTING_802_1X_PHASE1_PEAPVER, "1",
 	              NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1",
@@ -7508,7 +7530,10 @@ test_write_wired_8021x_tls (NMSetting8021xCKScheme scheme,
 	/* IP6 setting */
 	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
 	g_assert (s_ip6);
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
 	/* 802.1x setting */
@@ -7753,7 +7778,10 @@ test_write_wifi_open (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-open-write", "failed to verify connection: %s",
@@ -7892,7 +7920,10 @@ test_write_wifi_open_hex_ssid (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-open-hex-ssid-write", "failed to verify connection: %s",
@@ -8035,7 +8066,10 @@ test_write_wifi_wep (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wep-write", "failed to verify connection: %s",
@@ -8198,7 +8232,10 @@ test_write_wifi_wep_adhoc (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wep-adhoc-write", "failed to verify connection: %s",
@@ -8351,7 +8388,10 @@ test_write_wifi_wep_passphrase (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wep-passphrase-write", "failed to verify connection: %s",
@@ -8506,7 +8546,10 @@ test_write_wifi_wep_40_ascii (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wep-40-ascii-write", "failed to verify connection: %s",
@@ -8661,7 +8704,10 @@ test_write_wifi_wep_104_ascii (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wep-104-ascii-write", "failed to verify connection: %s",
@@ -8813,7 +8859,10 @@ test_write_wifi_leap (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-leap-write", "failed to verify connection: %s",
@@ -8949,7 +8998,11 @@ test_write_wifi_leap_secret_flags (NMSettingSecretFlags flags)
 	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
 	g_assert (s_ip6);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	success = nm_connection_verify (connection, &error);
 	g_assert_no_error (error);
@@ -9114,7 +9167,10 @@ test_write_wifi_wpa_psk (const char *name,
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        test_name, "failed to verify connection: %s",
@@ -9277,7 +9333,10 @@ test_write_wifi_wpa_psk_adhoc (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wpa-psk-adhoc-write", "failed to verify connection: %s",
@@ -9458,7 +9517,10 @@ test_write_wifi_wpa_eap_tls (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wpa-eap-tls-write", "failed to verify connection: %s",
@@ -9657,7 +9719,10 @@ test_write_wifi_wpa_eap_ttls_tls (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wpa-eap-ttls-tls-write", "failed to verify connection: %s",
@@ -9828,7 +9893,10 @@ test_write_wifi_wpa_eap_ttls_mschapv2 (void)
 	        NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	ASSERT (nm_connection_verify (connection, &error) == TRUE,
 	        "wifi-wpa-eap-ttls-mschapv2-write", "failed to verify connection: %s",
@@ -9970,7 +10038,10 @@ test_write_wifi_wpa_then_open (void)
 	g_assert (s_ip6);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	success = nm_connection_verify (connection, &error);
 	g_assert_no_error (error);
@@ -10154,9 +10225,13 @@ test_write_wifi_dynamic_wep_leap (void)
 	/* IP6 setting */
 	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
 	g_assert (s_ip6);
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+
 	success = nm_connection_verify (connection, &error);
 	g_assert_no_error (error);
 	g_assert (success);
@@ -10688,6 +10763,7 @@ test_write_wired_qeth_dhcp (void)
 
 	g_object_set (s_ip6,
 	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 
 	/* Verify */
@@ -10804,7 +10880,11 @@ test_write_wired_ctc_dhcp (void)
 	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
 	g_assert (s_ip6);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	/* Verify */
 	success = nm_connection_verify (connection, &error);
@@ -10939,6 +11019,7 @@ test_write_permissions (void)
 
 	g_object_set (s_ip6,
 	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 
 	/* Verify */
@@ -11040,7 +11121,11 @@ test_write_wifi_wep_agent_keys (void)
 	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
 	g_assert (s_ip6);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
-	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
 
 	/* Wifi setting */
 	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index 6b66b232..192226ac 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -758,6 +758,7 @@ write_wireless_setting (NMConnection *connection,
 	char buf[33];
 	guint32 mtu, chan, i;
 	gboolean adhoc = FALSE, hex_ssid = FALSE;
+	const GSList *macaddr_blacklist;
 
 	s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
 	if (!s_wireless) {
@@ -786,6 +787,23 @@ write_wireless_setting (NMConnection *connection,
 		g_free (tmp);
 	}
 
+	svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
+	macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+	if (macaddr_blacklist) {
+		const GSList *iter;
+		GString *blacklist_str = g_string_new (NULL);
+
+		for (iter = macaddr_blacklist; iter; iter = g_slist_next (iter)) {
+			g_string_append (blacklist_str, iter->data);
+			g_string_append_c (blacklist_str, ' ');
+
+		}
+		if (blacklist_str->len > 0)
+			g_string_truncate (blacklist_str, blacklist_str->len - 1);
+		svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str->str, FALSE);
+		g_string_free (blacklist_str, TRUE);
+	}
+
 	svSetValue (ifcfg, "MTU", NULL, FALSE);
 	mtu = nm_setting_wireless_get_mtu (s_wireless);
 	if (mtu) {
@@ -934,6 +952,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	guint32 mtu, num_opts, i;
 	const GPtrArray *s390_subchannels;
 	GString *str;
+	const GSList *macaddr_blacklist;
 
 	s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
 	if (!s_wired) {
@@ -961,6 +980,23 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		g_free (tmp);
 	}
 
+	svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
+	macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
+	if (macaddr_blacklist) {
+		const GSList *iter;
+		GString *blacklist_str = g_string_new (NULL);
+
+		for (iter = macaddr_blacklist; iter; iter = g_slist_next (iter)) {
+			g_string_append (blacklist_str, iter->data);
+			g_string_append_c (blacklist_str, ' ');
+
+		}
+		if (blacklist_str->len > 0)
+			g_string_truncate (blacklist_str, blacklist_str->len - 1);
+		svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str->str, FALSE);
+		g_string_free (blacklist_str, TRUE);
+	}
+
 	svSetValue (ifcfg, "MTU", NULL, FALSE);
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	if (mtu) {
@@ -1316,13 +1352,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (utils_has_route_file_new_syntax (route_path)) {
 		shvarFile *routefile;
 
-		g_free (route_path);
 		routefile = utils_get_route_ifcfg (ifcfg->fileName, TRUE);
 		if (!routefile) {
 			g_set_error (error, IFCFG_PLUGIN_ERROR, 0,
-			             "Could not create route file '%s'", routefile->fileName);
+			             "Could not create route file '%s'", route_path);
+			g_free (route_path);
 			goto out;
 		}
+		g_free (route_path);
 
 		num = nm_setting_ip4_config_get_num_routes (s_ip4);
 		for (i = 0; i < 256; i++) {
diff --git a/src/settings/plugins/ifnet/plugin.c b/src/settings/plugins/ifnet/plugin.c
index 69b7bc80..59083688 100644
--- a/src/settings/plugins/ifnet/plugin.c
+++ b/src/settings/plugins/ifnet/plugin.c
@@ -278,7 +278,8 @@ reload_connections (gpointer config)
 			if (auto_refresh && is_true (auto_refresh)) {
 				if (!nm_connection_compare (NM_CONNECTION (old),
 				                            NM_CONNECTION (new),
-				                            NM_SETTING_COMPARE_FLAG_EXACT)) {
+				                            NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+				                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
 					PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Auto refreshing %s", conn_name);
 
 					/* Remove and re-add to disconnect and reconnect with new settings */
diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h
index 6c8f9ceb..7d94a705 100644
--- a/src/settings/plugins/keyfile/common.h
+++ b/src/settings/plugins/keyfile/common.h
@@ -23,9 +23,6 @@
 
 #include <glib.h>
 
-#define SWP_TAG ".swp"
-#define SWPX_TAG ".swpx"
-
 #define KEYFILE_PLUGIN_NAME "keyfile"
 #define KEYFILE_PLUGIN_INFO "(c) 2007 - 2010 Red Hat, Inc.  To report bugs please use the NetworkManager mailing list."
 
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index ffc614bc..af69c200 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -231,7 +231,8 @@ dir_changed (GFileMonitor *monitor,
 			if (tmp) {
 				if (!nm_connection_compare (NM_CONNECTION (connection),
 				                            NM_CONNECTION (tmp),
-				                            NM_SETTING_COMPARE_FLAG_EXACT)) {
+				                            NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+				                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
 					PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path);
 					update_connection_settings (connection, tmp);
 				}
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index a8eaaa8e..c4136e05 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
 #include <errno.h>
@@ -40,6 +40,7 @@
 #include <ctype.h>
 
 #include "nm-dbus-glib-types.h"
+#include "nm-system-config-interface.h"
 #include "reader.h"
 #include "common.h"
 
@@ -735,29 +736,27 @@ get_uchar_array (GKeyFile *keyfile,
                  const char *key)
 {
 	GByteArray *array = NULL;
-	char *p, *tmp_string;
+	char *tmp_string;
 	gint *tmp_list;
 	gsize length;
 	int i;
 
-	/* New format: just a string.  We try parsing the new format if there are
-	 * no ';' in the string or it's not just numbers.
+	/* New format: just a string
+	 * Old format: integer list; e.g. 11;25;38
 	 */
-	p = tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL);
+	tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL);
 	if (tmp_string) {
 		gboolean new_format = FALSE;
+		GRegex *regex;
+		GMatchInfo *match_info;
+		const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*(;[[:space:]]*[[:digit:]]{1,3}[[:space:]]*)*(;[[:space:]]*)?$";
 
-		if (strchr (p, ';') == NULL)
-			new_format = TRUE;
-		else {
+		regex = g_regex_new (pattern, 0, 0, NULL);
+		g_regex_match (regex, tmp_string, 0, &match_info);
+		if (!g_match_info_matches (match_info))
 			new_format = TRUE;
-			while (p && *p) {
-				if (!isdigit (*p++)) {
-					new_format = FALSE;
-					break;
-				}
-			}
-		}
+		g_match_info_free (match_info);
+		g_regex_unref (regex);
 
 		if (new_format) {
 			array = g_byte_array_sized_new (strlen (tmp_string));
@@ -835,6 +834,24 @@ get_cert_path (const char *keyfile_path, GByteArray *cert_path)
 
 #define SCHEME_PATH "file://"
 
+static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
+
+static gboolean
+has_cert_ext (GByteArray *array)
+{
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS (certext); i++) {
+		guint32 extlen = strlen (certext[i]);
+
+		if (array->len <= extlen)
+			continue;
+		if (memcmp (&array->data[array->len - extlen], certext[i], extlen) == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
 static void
 cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
 {
@@ -859,17 +876,31 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 		           && g_utf8_validate ((const char *) array->data, array->len, NULL)) {
 			GByteArray *val;
 			char *path;
+			gboolean exists;
+
+			/* Might be a bare path without the file:// prefix; in that case
+			 * if it's an absolute path, use that, otherwise treat it as a
+			 * relative path to the current directory.
+			 */
 
 			path = get_cert_path (keyfile_path, array);
-			if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+			exists = g_file_test (path, G_FILE_TEST_EXISTS);
+			if (   exists
+			    || memchr (array->data, '/', array->len)
+			    || has_cert_ext (array)) {
 				/* Construct the proper value as required for the PATH scheme */
 				val = g_byte_array_sized_new (strlen (SCHEME_PATH) + array->len + 1);
 				g_byte_array_append (val, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
-				g_byte_array_append (val, array->data, array->len);
+				g_byte_array_append (val, (const guint8 *) path, strlen (path));
 				g_byte_array_append (val, (const guint8 *) "\0", 1);
 				g_object_set (setting, key, val, NULL);
 				g_byte_array_free (val, TRUE);
 				success = TRUE;
+
+				/* Warn if the certificate didn't exist */
+				if (exists == FALSE) {
+					PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "   certificate or key %s does not exist", path);
+				}
 			}
 			g_free (path);
 		}
@@ -881,7 +912,7 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 
 		g_byte_array_free (array, TRUE);
 	} else {
-		g_warning ("%s: ignoring invalid SSID for %s / %s",
+		g_warning ("%s: ignoring invalid key/cert value for %s / %s",
 		           __func__, setting_name, key);
 	}
 }
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
index 0ce03209..302db866 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
@@ -7,8 +7,11 @@ KEYFILES = \
 	ATT_Data_Connect_BT \
 	ATT_Data_Connect_Plain \
 	Test_String_SSID \
+	Test_Intlist_SSID \
 	Test_Wired_TLS_Old \
-	Test_Wired_TLS_New
+	Test_Wired_TLS_New \
+	Test_Wired_TLS_Blob \
+	Test_Wired_TLS_Path_Missing
 
 CERTS = \
 	test-ca-cert.pem \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
index 2c3c1630..083615b0 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
@@ -277,8 +277,11 @@ KEYFILES = \
 	ATT_Data_Connect_BT \
 	ATT_Data_Connect_Plain \
 	Test_String_SSID \
+	Test_Intlist_SSID \
 	Test_Wired_TLS_Old \
-	Test_Wired_TLS_New
+	Test_Wired_TLS_New \
+	Test_Wired_TLS_Blob \
+	Test_Wired_TLS_Path_Missing
 
 CERTS = \
 	test-ca-cert.pem \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID
new file mode 100644
index 00000000..6d2bc0fa
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID
@@ -0,0 +1,11 @@
+[connection]
+id=Test 
+uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa
+type=802-11-wireless
+
+[802-11-wireless]
+ssid=98;108;97;104;49;50;51;52;
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
new file mode 100644
index 00000000..9f4ef62f
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
@@ -0,0 +1,22 @@
+
+[connection]
+id=Wired TLS
+uuid=5ee46013-9469-4c6a-a60a-0c7a1e1c7488
+type=802-3-ethernet
+
+[802-1x]
+eap=tls;
+identity=Bill Smith
+ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80;
+client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key-password=12345testing
+
+[ipv4]
+method=auto
+
+[802-3-ethernet]
+duplex=full
+
+[ipv6]
+method=ignore
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing
new file mode 100644
index 00000000..2b39538e
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing
@@ -0,0 +1,22 @@
+
+[connection]
+id=Wired TLS
+uuid=5ee46013-9469-4c6a-a60a-0c7a1e1c7488
+type=802-3-ethernet
+
+[802-1x]
+eap=tls;
+identity=Bill Smith
+ca-cert=/some/random/cert/path.pem
+client-cert=test-key-and-cert.pem
+private-key=test-key-and-cert.pem
+private-key-password=12345testing
+
+[ipv4]
+method=auto
+
+[802-3-ethernet]
+duplex=full
+
+[ipv6]
+method=ignore
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index bfe5aa43..3bbaaaec 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -1438,6 +1438,131 @@ test_write_string_ssid (void)
 	g_object_unref (connection);
 }
 
+#define TEST_INTLIST_SSID_FILE TEST_KEYFILES_DIR"/Test_Intlist_SSID"
+
+static void
+test_read_intlist_ssid (void)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi;
+	GError *error = NULL;
+	gboolean success;
+	const GByteArray *array;
+	const char *expected_ssid = "blah1234";
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_INTLIST_SSID_FILE, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* SSID */
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+
+	array = nm_setting_wireless_get_ssid (s_wifi);
+	g_assert (array != NULL);
+	g_assert_cmpint (array->len, ==, strlen (expected_ssid));
+	g_assert_cmpint (memcmp (array->data, expected_ssid, strlen (expected_ssid)), ==, 0);
+
+	g_object_unref (connection);
+}
+
+static void
+test_write_intlist_ssid (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingIP4Config *s_ip4;
+	char *uuid, *testfile = NULL;
+	GByteArray *ssid;
+	unsigned char tmpssid[] = { 65, 49, 50, 51, 0, 50, 50 };
+	gboolean success;
+	NMConnection *reread;
+	GError *error = NULL;
+	pid_t owner_grp;
+	uid_t owner_uid;
+	GKeyFile *keyfile;
+	gint *intlist;
+	gsize len = 0, i;
+
+	connection = nm_connection_new ();
+	g_assert (connection);
+
+	/* Connection setting */
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	g_assert (s_con);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Intlist SSID Test",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wireless setting */
+	s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+	g_assert (s_wifi);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (tmpssid));
+	g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid));
+	g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL);
+	g_byte_array_free (ssid, TRUE);
+
+	/* IP4 setting */
+	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
+	g_assert (s_ip4);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	/* Write out the connection */
+	owner_uid = geteuid ();
+	owner_grp = getegid ();
+	success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	g_assert (testfile != NULL);
+
+	/* Ensure the SSID was written out as an int list */
+	keyfile = g_key_file_new ();
+	success = g_key_file_load_from_file (keyfile, testfile, 0, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	intlist = g_key_file_get_integer_list (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &len, &error);
+	g_assert_no_error (error);
+	g_assert (intlist);
+	g_assert_cmpint (len, ==, sizeof (tmpssid));
+
+	for (i = 0; i < len; i++)
+		g_assert_cmpint (intlist[i], ==, tmpssid[i]);
+	g_free (intlist);
+
+	g_key_file_free (keyfile);
+
+	/* Read the connection back in and compare it to the one we just wrote out */
+	reread = nm_keyfile_plugin_connection_from_file (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (reread);
+
+	success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT);
+	g_assert (success);
+
+	g_clear_error (&error);
+	unlink (testfile);
+	g_free (testfile);
+
+	g_object_unref (reread);
+	g_object_unref (connection);
+}
+
 #define TEST_BT_DUN_FILE TEST_KEYFILES_DIR"/ATT_Data_Connect_BT"
 
 static void
@@ -1953,6 +2078,133 @@ test_write_gsm_connection (void)
 	g_object_unref (connection);
 }
 
+#define TEST_WIRED_TLS_BLOB_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Blob"
+
+static void
+test_read_wired_8021x_tls_blob_connection (void)
+{
+	NMConnection *connection;
+	NMSetting *s_wired;
+	NMSetting8021x *s_8021x;
+	GError *error = NULL;
+	const char *tmp;
+	gboolean success;
+	const GByteArray *array;
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_BLOB_FILE, &error);
+	if (connection == NULL) {
+		g_assert (error);
+		g_warning ("Failed to read %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message);
+		g_assert (connection);
+	}
+
+	success = nm_connection_verify (connection, &error);
+	if (!success) {
+		g_assert (error);
+		g_warning ("Failed to verify %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message);
+		g_assert (success);
+	}
+
+	/* ===== Wired Setting ===== */
+	s_wired = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	g_assert (s_wired != NULL);
+
+	/* ===== 802.1x Setting ===== */
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	g_assert (s_8021x != NULL);
+
+	g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1);
+	tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+	g_assert (g_strcmp0 (tmp, "tls") == 0);
+
+	tmp = nm_setting_802_1x_get_identity (s_8021x);
+	g_assert (g_strcmp0 (tmp, "Bill Smith") == 0);
+
+	tmp = nm_setting_802_1x_get_private_key_password (s_8021x);
+	g_assert (g_strcmp0 (tmp, "12345testing") == 0);
+
+	g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB);
+
+	/* Make sure it's not a path, since it's a blob */
+	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
+	g_assert (tmp == NULL);
+
+	/* Validate the path */
+	array = nm_setting_802_1x_get_ca_cert_blob (s_8021x);
+	g_assert (array != NULL);
+	g_assert_cmpint (array->len, ==, 568);
+
+	tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
+	g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+
+	tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
+	g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+
+	g_object_unref (connection);
+}
+
+#define TEST_WIRED_TLS_PATH_MISSING_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Path_Missing"
+
+static void
+test_read_wired_8021x_tls_bad_path_connection (void)
+{
+	NMConnection *connection;
+	NMSetting *s_wired;
+	NMSetting8021x *s_8021x;
+	GError *error = NULL;
+	const char *tmp;
+	char *tmp2;
+	gboolean success;
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_PATH_MISSING_FILE, &error);
+	if (connection == NULL) {
+		g_assert (error);
+		g_warning ("Failed to read %s: %s", TEST_WIRED_TLS_PATH_MISSING_FILE, error->message);
+		g_assert (connection);
+	}
+
+	success = nm_connection_verify (connection, &error);
+	if (!success) {
+		g_assert (error);
+		g_warning ("Failed to verify %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message);
+		g_assert (success);
+	}
+
+	/* ===== Wired Setting ===== */
+	s_wired = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	g_assert (s_wired != NULL);
+
+	/* ===== 802.1x Setting ===== */
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	g_assert (s_8021x != NULL);
+
+	g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1);
+	tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+	g_assert (g_strcmp0 (tmp, "tls") == 0);
+
+	tmp = nm_setting_802_1x_get_identity (s_8021x);
+	g_assert (g_strcmp0 (tmp, "Bill Smith") == 0);
+
+	tmp = nm_setting_802_1x_get_private_key_password (s_8021x);
+	g_assert (g_strcmp0 (tmp, "12345testing") == 0);
+
+	g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH);
+
+	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
+	g_assert_cmpstr (tmp, ==, "/some/random/cert/path.pem");
+
+	tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem");
+
+	tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
+	g_assert_cmpstr (tmp, ==, tmp2);
+
+	tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
+	g_assert_cmpstr (tmp, ==, tmp2);
+
+	g_free (tmp2);
+	g_object_unref (connection);
+}
+
 #define TEST_WIRED_TLS_OLD_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Old"
 
 static void
@@ -2019,6 +2271,7 @@ test_read_wired_8021x_tls_new_connection (void)
 	NMSetting8021x *s_8021x;
 	GError *error = NULL;
 	const char *tmp;
+	char *tmp2;
 	gboolean success;
 
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_NEW_FILE, &error);
@@ -2053,15 +2306,20 @@ test_read_wired_8021x_tls_new_connection (void)
 	tmp = nm_setting_802_1x_get_private_key_password (s_8021x);
 	g_assert (g_strcmp0 (tmp, "12345testing") == 0);
 
+	tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-ca-cert.pem");
 	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "test-ca-cert.pem") == 0);
+	g_assert_cmpstr (tmp, ==, tmp2);
+	g_free (tmp2);
+
+	tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem");
 
 	tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "test-key-and-cert.pem") == 0);
+	g_assert_cmpstr (tmp, ==, tmp2);
 
 	tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
-	g_assert (g_strcmp0 (tmp, "test-key-and-cert.pem") == 0);
+	g_assert_cmpstr (tmp, ==, tmp2);
 
+	g_free (tmp2);
 	g_object_unref (connection);
 }
 
@@ -2153,16 +2411,23 @@ create_wired_tls_connection (NMSetting8021xCKScheme scheme)
 	return connection;
 }
 
+static char *
+get_path (const char *file, gboolean relative)
+{
+	return relative ? g_path_get_basename (file) : g_strdup (file);
+}
+
 static void
 test_write_wired_8021x_tls_connection_path (void)
 {
 	NMConnection *connection;
-	char *tmp;
+	char *tmp, *tmp2;
 	gboolean success;
 	NMConnection *reread;
 	char *testfile = NULL;
 	GError *error = NULL;
 	GKeyFile *keyfile;
+	gboolean relative = FALSE;
 
 	connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH);
 	g_assert (connection != NULL);
@@ -2200,12 +2465,22 @@ test_write_wired_8021x_tls_connection_path (void)
 		g_assert (success);
 	}
 
+	/* Depending on whether this test is being run from 'make check' or
+	 * 'make distcheck' we might be using relative paths (check) or
+	 * absolute ones (distcheck).
+	 */
+	tmp2 = g_path_get_dirname (testfile);
+	if (g_strcmp0 (tmp2, TEST_KEYFILES_DIR) == 0)
+		relative = TRUE;
+
 	/* CA cert */
 	tmp = g_key_file_get_string (keyfile,
 	                             NM_SETTING_802_1X_SETTING_NAME,
 	                             NM_SETTING_802_1X_CA_CERT,
 	                             NULL);
-	g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_CA_CERT) == 0);
+	tmp2 = get_path (TEST_WIRED_TLS_CA_CERT, relative);
+	g_assert_cmpstr (tmp, ==, tmp2);
+	g_free (tmp2);
 	g_free (tmp);
 
 	/* Client cert */
@@ -2213,7 +2488,9 @@ test_write_wired_8021x_tls_connection_path (void)
 	                             NM_SETTING_802_1X_SETTING_NAME,
 	                             NM_SETTING_802_1X_CLIENT_CERT,
 	                             NULL);
-	g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_CLIENT_CERT) == 0);
+	tmp2 = get_path (TEST_WIRED_TLS_CLIENT_CERT, relative);
+	g_assert_cmpstr (tmp, ==, tmp2);
+	g_free (tmp2);
 	g_free (tmp);
 
 	/* Private key */
@@ -2221,7 +2498,9 @@ test_write_wired_8021x_tls_connection_path (void)
 	                             NM_SETTING_802_1X_SETTING_NAME,
 	                             NM_SETTING_802_1X_PRIVATE_KEY,
 	                             NULL);
-	g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_PRIVKEY) == 0);
+	tmp2 = get_path (TEST_WIRED_TLS_PRIVKEY, relative);
+	g_assert_cmpstr (tmp, ==, tmp2);
+	g_free (tmp2);
 	g_free (tmp);
 
 	g_key_file_free (keyfile);
@@ -2334,12 +2613,18 @@ int main (int argc, char **argv)
 	test_read_string_ssid ();
 	test_write_string_ssid ();
 
+	test_read_intlist_ssid ();
+	test_write_intlist_ssid ();
+
 	test_read_bt_dun_connection ();
 	test_write_bt_dun_connection ();
 
 	test_read_gsm_connection ();
 	test_write_gsm_connection ();
 
+	test_read_wired_8021x_tls_blob_connection ();
+	test_read_wired_8021x_tls_bad_path_connection ();
+
 	test_read_wired_8021x_tls_old_connection ();
 	test_read_wired_8021x_tls_new_connection ();
 	test_write_wired_8021x_tls_connection_path ();
diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c
index 7b93a245..f3531172 100644
--- a/src/settings/plugins/keyfile/utils.c
+++ b/src/settings/plugins/keyfile/utils.c
@@ -74,6 +74,11 @@ check_suffix (const char *base, const char *tag)
 	return FALSE;
 }
 
+#define SWP_TAG ".swp"
+#define SWPX_TAG ".swpx"
+#define PEM_TAG ".pem"
+#define DER_TAG ".der"
+
 gboolean
 nm_keyfile_plugin_utils_should_ignore_file (const char *filename)
 {
@@ -88,6 +93,8 @@ nm_keyfile_plugin_utils_should_ignore_file (const char *filename)
 	/* Ignore files with certain patterns */
 	if (   (check_prefix (base, ".") && check_suffix (base, SWP_TAG))   /* vim temporary files: .filename.swp */
 	    || (check_prefix (base, ".") && check_suffix (base, SWPX_TAG))  /* vim temporary files: .filename.swpx */
+	    || check_suffix (base, PEM_TAG)                                 /* 802.1x certificates and keys */
+	    || check_suffix (base, DER_TAG)                                 /* 802.1x certificates and keys */
 	    || check_mkstemp_suffix (base)                                  /* temporary files created by mkstemp() */
 	    || base[strlen (base) - 1] == '~')
 		ignore = TRUE;
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index eeb14556..060093ce 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/keyfile/writer.c
@@ -668,6 +668,16 @@ cert_writer (GKeyFile *file,
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
 		path = objtype->path_func (NM_SETTING_802_1X (setting));
 		g_assert (path);
+
+		/* If the path is rooted in the keyfile directory, just use a
+		 * relative path instead of an absolute one.
+		 */
+		if (g_str_has_prefix (path, keyfile_dir)) {
+			path += strlen (keyfile_dir);
+			while (*path == '/')
+				path++;
+		}
+
 		g_key_file_set_string (file, setting_name, key, path);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		const GByteArray *blob;
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index 81d68bb3..c651e0fa 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -451,7 +451,7 @@ add_string_val (NMSupplicantConfig *self,
 	return success;
 }
 
-#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, ucase, secret) \
+#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, separator, ucase, secret) \
 	if (nm_setting_##setting_name##_get_num_##field_plural (setting)) { \
 		guint32 k; \
 		GString *str = g_string_new (NULL); \
@@ -460,7 +460,7 @@ add_string_val (NMSupplicantConfig *self,
 			if (!str->len) { \
 				g_string_append (str, item); \
 			} else { \
-				g_string_append_c (str, ' '); \
+				g_string_append_c (str, separator); \
 				g_string_append (str, item); \
 			} \
 		} \
@@ -636,9 +636,9 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 	if (   !strcmp (key_mgmt, "wpa-none")
 	    || !strcmp (key_mgmt, "wpa-psk")
 	    || !strcmp (key_mgmt, "wpa-eap")) {
-		ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", TRUE, FALSE);
-		ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", TRUE, FALSE);
-		ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", TRUE, FALSE);
+		ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", ' ', TRUE, FALSE);
+		ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, FALSE);
+		ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", ' ', TRUE, FALSE);
 	}
 
 	/* WEP keys if required */
@@ -742,7 +742,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 		nm_supplicant_config_set_ap_scan (self, 0);
 	}
 
-	ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", TRUE, FALSE);
+	ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE);
 
 	/* Check for PEAP + GTC */
 	num_eap = nm_setting_802_1x_get_num_eap_methods (setting);
@@ -864,6 +864,18 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 		break;
 	}
 
+	/* Subject match */
+	value = nm_setting_802_1x_get_subject_match (setting);
+	if (!add_string_val (self, value, "subject_match", FALSE, FALSE))
+		return FALSE;
+	value = nm_setting_802_1x_get_phase2_subject_match (setting);
+	if (!add_string_val (self, value, "subject_match2", FALSE, FALSE))
+		return FALSE;
+
+	/* altSubjectName match */
+	ADD_STRING_LIST_VAL (setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, FALSE);
+	ADD_STRING_LIST_VAL (setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, FALSE);
+
 	/* Private key */
 	added = FALSE;
 	switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index e9e58f39..857cde51 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -467,7 +467,8 @@ interface_add_done (NMSupplicantInterface *self, char *path)
 	                             G_CALLBACK (wpas_iface_properties_changed),
 	                             self, NULL);
 
-	dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone", G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone",
+	                         G_TYPE_BOOLEAN, G_TYPE_INVALID);
 	dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanDone",
 	                             G_CALLBACK (wpas_iface_scan_done),
 	                             self,
@@ -475,9 +476,11 @@ interface_add_done (NMSupplicantInterface *self, char *path)
 
 	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
 	                                   G_TYPE_NONE,
-	                                   G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
+	                                   DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT,
 	                                   G_TYPE_INVALID);
-	dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", 
+	                         DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT,
+	                         G_TYPE_INVALID);
 	dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSAdded",
 	                             G_CALLBACK (wpas_iface_bss_added),
 	                             self,
@@ -551,6 +554,9 @@ interface_add_cb (DBusGProxy *proxy,
 			/* Interface already added, just get its object path */
 			interface_get (info->interface);
 		} else if (   g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)
+		           || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_EXEC_FAILED)
+		           || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FORK_FAILED)
+		           || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FAILED)
 		           || dbus_g_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
 			/* Supplicant wasn't running and could be launched via service
 			 * activation.  Wait for it to start by moving back to the INIT
@@ -682,12 +688,13 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
 	if (!priv->iface_proxy)
 		return;
 
-	/* Don't try to disconnect if the supplicant interface is already disconnected */
-	if (   priv->state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED
-	    || priv->state == NM_SUPPLICANT_INTERFACE_STATE_INACTIVE) {
-		g_free (priv->net_path);
-		priv->net_path = NULL;
-		return;
+	/* Disconnect from the current AP */
+	if (   (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
+	    && (priv->state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)) {
+		dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect",
+			                     disconnect_cb,
+			                     NULL, NULL,
+			                     G_TYPE_INVALID);
 	}
 
 	/* Remove any network that was added by NetworkManager */
@@ -700,11 +707,6 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
 		g_free (priv->net_path);
 		priv->net_path = NULL;
 	}
-
-	dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect",
-	                         disconnect_cb,
-	                         NULL, NULL,
-	                         G_TYPE_INVALID);
 }
 
 static void
diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c
index 0372fd93..aea5bfec 100644
--- a/src/supplicant-manager/nm-supplicant-settings-verify.c
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.c
@@ -102,6 +102,8 @@ static const struct Opt opt_table[] = {
 	{ "identity",           TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "password",           TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_path",            TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "subject_match",      TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "altsubject_match",   TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_cert",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
@@ -110,6 +112,8 @@ static const struct Opt opt_table[] = {
 	{ "phase2",             TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed },
 	{ "anonymous_identity", TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_path2",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "subject_match2",     TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "altsubject_match2",  TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_cert2",           TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c
index d0868b5f..d92fb0d1 100644
--- a/src/supplicant-manager/tests/test-supplicant-config.c
+++ b/src/supplicant-manager/tests/test-supplicant-config.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 - 2009 Red Hat, Inc.
+ * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
 #include <stdio.h>
@@ -496,11 +496,9 @@ test_wifi_wpa_psk_types (void)
 int main (int argc, char **argv)
 {
 	GError *error = NULL;
-	DBusGConnection *bus;
 	char *base;
 
 	g_type_init ();
-	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
 
 	if (!nm_utils_init (&error))
 		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c
index aa85f87e..9463ed5b 100644
--- a/src/tests/test-dhcp-options.c
+++ b/src/tests/test-dhcp-options.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ * Copyright (C) 2008 - 2011 Red Hat, Inc.
  *
  */
 
@@ -779,13 +779,11 @@ test_ip4_prefix_classless (const char *client)
 int main (int argc, char **argv)
 {
 	GError *error = NULL;
-	DBusGConnection *bus;
 	char *base;
 	const char *clients[2][2] = { {DHCLIENT_PATH, "dhclient"}, {DHCPCD_PATH, "dhcpcd"} };
 	guint32 i;
 
 	g_type_init ();
-	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
 
 	if (!nm_utils_init (&error))
 		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index cf6135a6..067b2dce 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -53,6 +53,17 @@
 
 G_DEFINE_TYPE (NMVPNConnection, nm_vpn_connection, NM_TYPE_VPN_CONNECTION_BASE)
 
+typedef enum {
+	/* Only system secrets */
+	SECRETS_REQ_SYSTEM = 0,
+	/* All existing secrets including agent secrets */
+	SECRETS_REQ_EXISTING = 1,
+	/* New secrets required; ask an agent */
+	SECRETS_REQ_NEW = 2,
+	/* Placeholder for bounds checking */
+	SECRETS_REQ_LAST
+} SecretsReq;
+
 typedef struct {
 	gboolean disposed;
 
@@ -61,6 +72,7 @@ typedef struct {
 	gboolean user_requested;
 	gulong user_uid;
 	guint32 secrets_id;
+	SecretsReq secrets_idx;
 	char *username;
 
 	NMDevice *parent_dev;
@@ -98,6 +110,8 @@ enum {
 	LAST_PROP
 };
 
+static void get_secrets (NMVPNConnection *self, SecretsReq secrets_idx);
+
 static void
 nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
                                  NMVPNConnectionState vpn_state,
@@ -792,84 +806,50 @@ nm_vpn_connection_disconnect (NMVPNConnection *connection,
 /******************************************************************************/
 
 static void
-vpn_secrets_cb (NMSettingsConnection *connection,
-                guint32 call_id,
-                const char *agent_username,
-                const char *setting_name,
-                GError *error,
-                gpointer user_data)
-{
-	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-
-	g_return_if_fail (NM_CONNECTION (connection) == priv->connection);
-	g_return_if_fail (call_id == priv->secrets_id);
-
-	priv->secrets_id = 0;
-
-	if (error)
-		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
-	else
-		really_activate (self, agent_username);
-}
-
-static void
-connection_need_secrets_cb  (DBusGProxy *proxy,
-                             char *setting_name,
-                             GError *error,
-                             gpointer user_data)
+plugin_need_secrets_cb  (DBusGProxy *proxy,
+                         char *setting_name,
+                         GError *error,
+                         gpointer user_data)
 {
 	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *local = NULL;
 
 	if (error) {
-		nm_log_err (LOGD_VPN, "NeedSecrets failed: %s %s",
+		nm_log_err (LOGD_VPN, "(%s/%s) plugin NeedSecrets request #%d failed: %s %s",
+		            nm_connection_get_uuid (priv->connection),
+		            nm_connection_get_id (priv->connection),
+		            priv->secrets_idx + 1,
 		            g_quark_to_string (error->domain),
 		            error->message);
 		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
 		return;
 	}
 
-	if (!setting_name || !strlen (setting_name)) {
-		nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated no additional secrets required",
-				    nm_connection_get_uuid (priv->connection),
-				    nm_connection_get_id (priv->connection));
+	if (setting_name && strlen (setting_name)) {
+		/* More secrets required */
+		nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required",
+		            nm_connection_get_uuid (priv->connection),
+		            nm_connection_get_id (priv->connection));
 
-		/* No secrets required */
-		really_activate (self, priv->username);
+		get_secrets (self, priv->secrets_idx + 1);
 		return;
 	}
 
-	nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional '%s' secrets required",
-			    nm_connection_get_uuid (priv->connection),
-			    nm_connection_get_id (priv->connection),
-			    setting_name);
+	nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated no additional secrets required",
+	            nm_connection_get_uuid (priv->connection),
+	            nm_connection_get_id (priv->connection));
 
-	priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection),
-	                                                       priv->user_requested,
-	                                                       priv->user_uid,
-	                                                       setting_name,
-	                                                       NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION,
-	                                                       NULL,
-	                                                       vpn_secrets_cb,
-	                                                       self,
-	                                                       &local);
-	if (!priv->secrets_id) {
-		if (local)
-			nm_log_err (LOGD_VPN, "failed to get secrets: (%d) %s", local->code, local->message);
-		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
-		g_clear_error (&local);
-	}
+	/* No secrets required; we can start the VPN */
+	really_activate (self, priv->username);
 }
 
 static void
-existing_secrets_cb (NMSettingsConnection *connection,
-                     guint32 call_id,
-                     const char *agent_username,
-                     const char *setting_name,
-                     GError *error,
-                     gpointer user_data)
+get_secrets_cb (NMSettingsConnection *connection,
+                guint32 call_id,
+                const char *agent_username,
+                const char *setting_name,
+                GError *error,
+                gpointer user_data)
 {
 	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
@@ -881,57 +861,77 @@ existing_secrets_cb (NMSettingsConnection *connection,
 	priv->secrets_id = 0;
 
 	if (error) {
-		nm_log_err (LOGD_VPN, "Failed to request existing VPN secrets #2: (%s) %s",
-		            g_quark_to_string (error->domain),
-		            error->message);
+		nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s",
+		            priv->secrets_idx + 1, error->code, error->message);
 		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
 	} else {
 		nm_log_dbg (LOGD_VPN, "(%s/%s) asking service if additional secrets are required",
-			        nm_connection_get_uuid (priv->connection),
-			        nm_connection_get_id (priv->connection));
+		            nm_connection_get_uuid (priv->connection),
+		            nm_connection_get_id (priv->connection));
 
 		/* Cache the username for later */
-		g_free (priv->username);
-		priv->username = g_strdup (agent_username);
+		if (agent_username) {
+			g_free (priv->username);
+			priv->username = g_strdup (agent_username);
+		}
 
 		/* Ask the VPN service if more secrets are required */
 		hash = _hash_with_username (priv->connection, priv->username);
 		org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy,
 		                                                              hash,
-		                                                              connection_need_secrets_cb,
+		                                                              plugin_need_secrets_cb,
 		                                                              self);
 		g_hash_table_destroy (hash);
 	}
 }
 
 static void
-get_existing_secrets (NMVPNConnection *self)
+get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE;
 	GError *error = NULL;
+	gboolean filter_by_uid = priv->user_requested;
+
+	g_return_if_fail (secrets_idx < SECRETS_REQ_LAST);
+	priv->secrets_idx = secrets_idx;
 
-	nm_log_dbg (LOGD_VPN, "(%s/%s) requesting existing VPN secrets",
+	nm_log_dbg (LOGD_VPN, "(%s/%s) requesting VPN secrets pass #%d",
 	            nm_connection_get_uuid (priv->connection),
-	            nm_connection_get_id (priv->connection));
+	            nm_connection_get_id (priv->connection),
+	            priv->secrets_idx + 1);
+
+	switch (priv->secrets_idx) {
+	case SECRETS_REQ_SYSTEM:
+		flags = NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM;
+		filter_by_uid = FALSE;
+		break;
+	case SECRETS_REQ_EXISTING:
+		flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE;
+		break;
+	case SECRETS_REQ_NEW:
+		flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
 
-	/* Just get existing secrets if any so we can ask the VPN service if
-	 * any more are required.
-	 */
 	priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection),
-	                                                       priv->user_requested,
+	                                                       filter_by_uid,
 	                                                       priv->user_uid,
 	                                                       NM_SETTING_VPN_SETTING_NAME,
-	                                                       NM_SETTINGS_GET_SECRETS_FLAG_NONE,
+	                                                       flags,
 	                                                       NULL,
-	                                                       existing_secrets_cb,
+	                                                       get_secrets_cb,
 	                                                       self,
 	                                                       &error);
-	if (priv->secrets_id == 0) {
-		nm_log_err (LOGD_VPN, "Failed to request existing VPN secrets #1: (%s) %s",
-		            g_quark_to_string (error->domain),
-		            error->message);
-		g_error_free (error);
+	if (!priv->secrets_id) {
+		if (error) {
+			nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s",
+			            priv->secrets_idx + 1, error->code, error->message);
+		}
 		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		g_clear_error (&error);
 	}
 }
 
@@ -1002,10 +1002,16 @@ connection_state_changed (NMVPNConnection *self,
 		nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id);
 		priv->secrets_id = 0;
 	}
+	priv->secrets_idx = SECRETS_REQ_SYSTEM;
 
 	switch (state) {
 	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-		get_existing_secrets (self);
+		/* Kick off the secrets requests; first we get existing system secrets
+		 * and ask the plugin if these are sufficient, next we get all existing
+		 * secrets from system and from user agents and ask the plugin again,
+		 * and last we ask the user for new secrets if required.
+		 */
+		get_secrets (self, SECRETS_REQ_SYSTEM);
 		break;
 	case NM_VPN_CONNECTION_STATE_ACTIVATED:
 		/* Secrets no longer needed now that we're connected */