wifi: implement MAC address randomization
authorDan Williams <dcbw@redhat.com>
Wed, 7 Oct 2015 21:03:16 +0000 (16:03 -0500)
committerThomas Haller <thaller@redhat.com>
Wed, 18 Nov 2015 14:37:42 +0000 (15:37 +0100)
If the supplicant supports it and the connection requests it, tell
the supplicant to randomize the MAC address for the association.

In addition, like both iOS, Android, and other OSs always randomize
the MAC address when performing a WiFi scan.

man/NetworkManager.conf.xml.in
src/devices/wifi/nm-device-wifi.c
src/supplicant-manager/nm-supplicant-config.c
src/supplicant-manager/nm-supplicant-config.h
src/supplicant-manager/nm-supplicant-interface.c
src/supplicant-manager/nm-supplicant-interface.h
src/supplicant-manager/nm-supplicant-types.h
src/supplicant-manager/tests/test-supplicant-config.c

index 3fd1b12..b7e228a 100644 (file)
@@ -610,6 +610,10 @@ ipv6.ip6-privacy=1
         <term><varname>vpn.timeout</varname></term>
         <listitem><para>If left unspecified, default value of 60 seconds is used.</para></listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>wifi.mac-address-randomization</varname></term>
+        <listitem><para>If left unspecified, MAC address randomization is disabled.</para></listitem>
+      </varlistentry>
     </para>
   </refsect1>
 
index 3ba532d..6327cba 100644 (file)
@@ -49,6 +49,7 @@
 #include "nm-enum-types.h"
 #include "nm-connection-provider.h"
 #include "nm-core-internal.h"
+#include "nm-config.h"
 
 #include "nmdbus-device-wifi.h"
 
@@ -2201,6 +2202,9 @@ build_supplicant_config (NMDeviceWifi *self,
        NMSupplicantConfig *config = NULL;
        NMSettingWireless *s_wireless;
        NMSettingWirelessSecurity *s_wireless_sec;
+       NMSupplicantFeature mac_randomization_support;
+       NMSettingMacRandomization mac_randomization_fallback;
+       gs_free char *svalue = NULL;
 
        g_return_val_if_fail (self != NULL, NULL);
 
@@ -2217,9 +2221,20 @@ build_supplicant_config (NMDeviceWifi *self,
                _LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
        }
 
+       mac_randomization_support = nm_supplicant_interface_get_mac_randomization_support (priv->sup_iface);
+       svalue = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+                                                       "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
+                                                       NM_DEVICE (self));
+       mac_randomization_fallback = _nm_utils_ascii_str_to_int64 (svalue, 10,
+                                                                  NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
+                                                                  NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
+                                                                  NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
+
        if (!nm_supplicant_config_add_setting_wireless (config,
                                                        s_wireless,
-                                                       fixed_freq)) {
+                                                       fixed_freq,
+                                                       mac_randomization_support,
+                                                       mac_randomization_fallback)) {
                _LOGE (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
                goto error;
        }
index 65d6e2d..28047b6 100644 (file)
@@ -39,7 +39,7 @@ G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT)
 
 typedef struct {
        char *value;
-       guint32 len;    
+       guint32 len;
        OptType type;
 } ConfigOption;
 
@@ -48,6 +48,7 @@ typedef struct
        GHashTable *config;
        GHashTable *blobs;
        guint32    ap_scan;
+       NMSettingMacRandomization mac_randomization;
        gboolean   fast_required;
        gboolean   dispose_has_run;
 } NMSupplicantConfigPrivate;
@@ -85,6 +86,7 @@ nm_supplicant_config_init (NMSupplicantConfig * self)
                                             (GDestroyNotify) blob_free);
 
        priv->ap_scan = 1;
+       priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT;
        priv->dispose_has_run = FALSE;
 }
 
@@ -240,14 +242,30 @@ nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self)
        return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan;
 }
 
-void
-nm_supplicant_config_set_ap_scan (NMSupplicantConfig * self,
-                                  guint32 ap_scan)
+const char *
+nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self)
 {
-       g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (self));
-       g_return_if_fail (ap_scan <= 2);
+       g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 0);
+
+       /**
+        * mac_addr - MAC address policy default
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address for each ESS connection
+        * 2 = like 1, but maintain OUI (with local admin bit set)
+        *
+        * By default, permanent MAC address is used unless policy is changed by
+        * the per-network mac_addr parameter.
+        */
 
-       NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan = ap_scan;
+       switch (NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->mac_randomization) {
+       case NM_SETTING_MAC_RANDOMIZATION_ALWAYS:
+               return "1";
+       case NM_SETTING_MAC_RANDOMIZATION_NEVER:
+       case NM_SETTING_MAC_RANDOMIZATION_DEFAULT:
+       default:
+               return "0";
+       }
 }
 
 gboolean
@@ -336,7 +354,9 @@ wifi_freqs_to_string (gboolean bg_band)
 gboolean
 nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
                                            NMSettingWireless * setting,
-                                           guint32 fixed_freq)
+                                           guint32 fixed_freq,
+                                           NMSupplicantFeature mac_randomization_support,
+                                           NMSettingMacRandomization mac_randomization_fallback)
 {
        NMSupplicantConfigPrivate *priv;
        gboolean is_adhoc, is_ap;
@@ -441,6 +461,21 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
                }
        }
 
+       priv->mac_randomization = nm_setting_wireless_get_mac_address_randomization (setting);
+       if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) {
+               priv->mac_randomization = mac_randomization_fallback;
+               if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) {
+                       /* the value is unconfigured. For now, that means we don't use randomization.*/
+                       priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER;
+               }
+       }
+
+       if (   priv->mac_randomization != NM_SETTING_MAC_RANDOMIZATION_NEVER
+           && mac_randomization_support != NM_SUPPLICANT_FEATURE_YES) {
+               nm_log_warn (LOGD_SUPPLICANT, "MAC address randomization is not supported");
+               return FALSE;
+       }
+
        return TRUE;
 }
 
@@ -805,7 +840,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
                /* Wired 802.1x must always use eapol_flags=0 */
                if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE))
                        return FALSE;
-               nm_supplicant_config_set_ap_scan (self, 0);
+               priv->ap_scan = 0;
        }
 
        ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE);
index 0cd3243..b87edb4 100644 (file)
@@ -54,8 +54,7 @@ NMSupplicantConfig *nm_supplicant_config_new (void);
 
 guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self);
 
-void nm_supplicant_config_set_ap_scan (NMSupplicantConfig *self,
-                                       guint32 ap_scan);
+const char *nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self);
 
 gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self);
 
@@ -65,7 +64,9 @@ GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self);
 
 gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self,
                                                     NMSettingWireless *setting,
-                                                    guint32 fixed_freq);
+                                                    guint32 fixed_freq,
+                                                    NMSupplicantFeature mac_randomization_support,
+                                                    NMSettingMacRandomization mac_randomization_fallback);
 
 gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
                                                              NMSettingWirelessSecurity *setting,
index 64a06b6..8267fb9 100644 (file)
@@ -487,6 +487,23 @@ nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *se
        return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mac_randomization_support;
 }
 
+static void
+set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+       gs_unref_variant GVariant *variant = NULL;
+       gs_free_error GError *error = NULL;
+
+       variant = _nm_dbus_proxy_call_finish (proxy, result,
+                                             G_VARIANT_TYPE ("()"),
+                                             &error);
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               return;
+       if (error)
+               nm_log_warn (LOGD_SUPPLICANT, "Failed to enable scan MAC address randomization");
+
+       iface_check_ready (NM_SUPPLICANT_INTERFACE (user_data));
+}
+
 static void
 iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
@@ -512,8 +529,23 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
                if (strstr (data, "ProbeRequest"))
                        priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
 
-               if (strstr (data, "PreassocMacAddr"))
+               if (strstr (data, "PreassocMacAddr")) {
                        priv->mac_randomization_support = NM_SUPPLICANT_FEATURE_YES;
+
+                       /* Turn on MAC randomization during scans by default */
+                       priv->ready_count++;
+                       g_dbus_proxy_call (priv->iface_proxy,
+                                          DBUS_INTERFACE_PROPERTIES ".Set",
+                                          g_variant_new ("(ssv)",
+                                                         WPAS_DBUS_IFACE_INTERFACE,
+                                                         "PreassocMacAddr",
+                                                         g_variant_new_string ("1")),
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          priv->init_cancellable,
+                                          (GAsyncReadyCallback) set_preassoc_scan_mac_cb,
+                                          self);
+               }
        }
 
        iface_check_ready (self);
@@ -1125,6 +1157,51 @@ add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
        call_select_network (self);
 }
 
+static void
+add_network (NMSupplicantInterface *self)
+{
+       NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+       g_dbus_proxy_call (priv->iface_proxy,
+                          "AddNetwork",
+                          g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          priv->assoc_cancellable,
+                          (GAsyncReadyCallback) add_network_cb,
+                          self);
+}
+
+static void
+set_mac_randomization_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+       NMSupplicantInterface *self;
+       NMSupplicantInterfacePrivate *priv;
+       gs_unref_variant GVariant *reply = NULL;
+       gs_free_error GError *error = NULL;
+
+       reply = g_dbus_proxy_call_finish (proxy, result, &error);
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               return;
+
+       self = NM_SUPPLICANT_INTERFACE (user_data);
+       priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+       if (!reply) {
+               g_dbus_error_strip_remote_error (error);
+               nm_log_warn (LOGD_SUPPLICANT, "Couldn't send MAC randomization mode to "
+                            "the supplicant interface: %s.",
+                            error->message);
+               emit_error_helper (self, error);
+               return;
+       }
+
+       nm_log_info (LOGD_SUPPLICANT, "Config: set MAC randomization to %s",
+                    nm_supplicant_config_get_mac_randomization (priv->cfg));
+
+       add_network (self);
+}
+
 static void
 set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
@@ -1151,14 +1228,24 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
        nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d",
                     nm_supplicant_config_get_ap_scan (priv->cfg));
 
-       g_dbus_proxy_call (priv->iface_proxy,
-                          "AddNetwork",
-                          g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          -1,
-                          priv->assoc_cancellable,
-                          (GAsyncReadyCallback) add_network_cb,
-                          self);
+       if (priv->mac_randomization_support == NM_SUPPLICANT_FEATURE_YES) {
+               const char *mac_randomization = nm_supplicant_config_get_mac_randomization (priv->cfg);
+
+               /* Enable/disable association MAC address randomization */
+               g_dbus_proxy_call (priv->iface_proxy,
+                                  DBUS_INTERFACE_PROPERTIES ".Set",
+                                  g_variant_new ("(ssv)",
+                                                 WPAS_DBUS_IFACE_INTERFACE,
+                                                 "MacAddr",
+                                                 g_variant_new_string (mac_randomization)),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  priv->assoc_cancellable,
+                                  (GAsyncReadyCallback) set_mac_randomization_cb,
+                                  self);
+       } else {
+               add_network (self);
+       }
 }
 
 gboolean
index 4cfdf5f..7737178 100644 (file)
@@ -67,12 +67,6 @@ enum {
 #define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error"
 #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request"
 
-typedef enum {
-       NM_SUPPLICANT_FEATURE_UNKNOWN = 0,  /* Can't detect whether supported or not */
-       NM_SUPPLICANT_FEATURE_NO = 1,       /* Feature definitely not supported */
-       NM_SUPPLICANT_FEATURE_YES = 2,      /* Feature definitely supported */
-} NMSupplicantFeature;
-
 struct _NMSupplicantInterface {
        GObject parent;
 };
index 1c16e49..02682f5 100644 (file)
@@ -29,4 +29,10 @@ typedef struct _NMSupplicantManager NMSupplicantManager;
 typedef struct _NMSupplicantInterface NMSupplicantInterface;
 typedef struct _NMSupplicantConfig NMSupplicantConfig;
 
+typedef enum {
+       NM_SUPPLICANT_FEATURE_UNKNOWN = 0,  /* Can't detect whether supported or not */
+       NM_SUPPLICANT_FEATURE_NO = 1,       /* Feature definitely not supported */
+       NM_SUPPLICANT_FEATURE_YES = 2,      /* Feature definitely supported */
+} NMSupplicantFeature;
+
 #endif  /* NM_SUPPLICANT_TYPES_H */
index 11b027a..01f5535 100644 (file)
@@ -158,7 +158,11 @@ test_wifi_open (void)
                               "*added 'bssid' value '11:22:33:44:55:66'*");
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
                               "*added 'freq_list' value *");
-       g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0));
+       g_assert (nm_supplicant_config_add_setting_wireless (config,
+                                                            s_wifi,
+                                                            0,
+                                                            NM_SUPPLICANT_FEATURE_UNKNOWN,
+                                                            NM_SETTING_MAC_RANDOMIZATION_DEFAULT));
        g_test_assert_expected_messages ();
 
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
@@ -256,7 +260,11 @@ test_wifi_wep_key (const char *detail,
                               "*added 'bssid' value '11:22:33:44:55:66'*");
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
                               "*added 'freq_list' value *");
-       g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0));
+       g_assert (nm_supplicant_config_add_setting_wireless (config,
+                                                            s_wifi,
+                                                            0,
+                                                            NM_SUPPLICANT_FEATURE_UNKNOWN,
+                                                            NM_SETTING_MAC_RANDOMIZATION_DEFAULT));
        g_test_assert_expected_messages ();
 
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
@@ -393,7 +401,11 @@ test_wifi_wpa_psk (const char *detail,
                               "*added 'bssid' value '11:22:33:44:55:66'*");
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
                               "*added 'freq_list' value *");
-       g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0));
+       g_assert (nm_supplicant_config_add_setting_wireless (config,
+                                                            s_wifi,
+                                                            0,
+                                                            NM_SUPPLICANT_FEATURE_UNKNOWN,
+                                                            NM_SETTING_MAC_RANDOMIZATION_DEFAULT));
        g_test_assert_expected_messages ();
 
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
@@ -532,7 +544,11 @@ test_wifi_eap (void)
                               "*added 'bssid' value '11:22:33:44:55:66'*");
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
                               "*added 'freq_list' value *");
-       g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0));
+       g_assert (nm_supplicant_config_add_setting_wireless (config,
+                                                            s_wifi,
+                                                            0,
+                                                            NM_SUPPLICANT_FEATURE_UNKNOWN,
+                                                            NM_SETTING_MAC_RANDOMIZATION_DEFAULT));
        g_test_assert_expected_messages ();
 
        g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,