wifi: implement MAC address randomization in NetworkManager instead of supplicant

'wireless.mac-address-randomization' broke 'wireless.cloned-mac-address',
because we would always set 'PreassocMacAddr=1'. The reason is that
supplicant would set 'wpa_s->mac_addr_changed' during scanning, and
later during association it would either set a random MAC address or
reset the permanent MAC address [1].

Anyway, 'wireless.mac-address-randomization' conflicts with
'wireless.cloned-mac-address'. Instead of letting supplicant set the
MAC address, manage the MAC addresses entirely from NetworkManager.
Supplicant should not touch it.

[1] https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.c?id=f885b8e97cf39b56fe7ca6577890f2d20df7ae08#n1663
This commit is contained in:
Thomas Haller 2016-06-20 12:28:04 +02:00
parent b7d76b2277
commit 767abfa690
8 changed files with 105 additions and 109 deletions

View file

@ -602,7 +602,9 @@ ipv6.ip6-privacy=0
</varlistentry>
<varlistentry>
<term><varname>wifi.mac-address-randomization</varname></term>
<listitem><para>If left unspecified, MAC address randomization is disabled.</para></listitem>
<listitem><para>If left unspecified, MAC address randomization is disabled.
This setting is deprecated for <literal>wifi.cloned-mac-address</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>wifi.powersave</varname></term>

View file

@ -11009,6 +11009,7 @@ _set_state_full (NMDevice *self,
if (nm_device_get_act_request (self))
nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
nm_device_take_down (self, TRUE);
nm_device_hw_addr_reset (self);
set_nm_ipv6ll (self, FALSE);
restore_ip6_properties (self);
}
@ -11409,9 +11410,13 @@ nm_device_update_hw_address (NMDevice *self)
_LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr);
_notify (self, PROP_HW_ADDRESS);
if (!priv->hw_addr_initial) {
/* when we get a hw_addr the first time, always update our inital
* hw-address as well. */
if ( !priv->hw_addr_initial
|| ( priv->hw_addr_type == HW_ADDR_TYPE_UNSET
&& priv->state < NM_DEVICE_STATE_PREPARE
&& !nm_device_is_activating (self))) {
/* when we get a hw_addr the first time or while the device
* is not activated (with no explict hw address set), always
* update our inital hw-address as well. */
nm_device_update_initial_hw_address (self);
}
}
@ -11433,10 +11438,14 @@ nm_device_update_initial_hw_address (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
priv->hw_addr_type = HW_ADDR_TYPE_UNSET;
if ( priv->hw_addr
&& !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) {
if ( priv->hw_addr_initial
&& priv->hw_addr_type != HW_ADDR_TYPE_UNSET) {
/* once we have the initial hw address set, we only allow
* update if the currenty type is "unset". */
return;
}
g_free (priv->hw_addr_initial);
priv->hw_addr_initial = g_strdup (priv->hw_addr);
_LOGD (LOGD_DEVICE, "hw-addr: update initial MAC address %s",
@ -11515,11 +11524,26 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool
a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address",
self);
if ( !a
|| ( !NM_CLONED_MAC_IS_SPECIAL (a)
&& !nm_utils_hwaddr_valid (a, ETH_ALEN)))
addr = NM_CLONED_MAC_PERMANENT;
else
/* default is permanent. */
addr = NM_CLONED_MAC_PERMANENT;
if (!a) {
if (is_wifi) {
NMSettingMacRandomization v;
/* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */
a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
"wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
self);
v = _nm_utils_ascii_str_to_int64 (a, 10,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS)
addr = NM_CLONED_MAC_RANDOM;
}
} else if ( NM_CLONED_MAC_IS_SPECIAL (a)
|| nm_utils_hwaddr_valid (a, ETH_ALEN))
addr = *out_addr = g_steal_pointer (&a);
}
@ -11547,6 +11571,7 @@ _hw_addr_set (NMDevice *self,
const char *cur_addr;
guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX];
guint hw_addr_len;
gboolean was_up;
nm_assert (NM_IS_DEVICE (self));
nm_assert (addr);
@ -11571,8 +11596,11 @@ _hw_addr_set (NMDevice *self,
_LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr);
/* Can't change MAC address while device is up */
nm_device_take_down (self, FALSE);
was_up = nm_device_is_up (self);
if (was_up) {
/* Can't change MAC address while device is up */
nm_device_take_down (self, FALSE);
}
success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len);
if (success) {
@ -11592,8 +11620,10 @@ _hw_addr_set (NMDevice *self,
detail, addr);
}
if (!nm_device_bring_up (self, TRUE, NULL))
return FALSE;
if (was_up) {
if (!nm_device_bring_up (self, TRUE, NULL))
return FALSE;
}
return success;
}
@ -11610,7 +11640,8 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr)
if (!addr)
g_return_val_if_reached (FALSE);
/* this is called by NMDeviceVlan to take the MAC address from the parent.
/* this is called by NMDeviceVlan to take the MAC address from the parent
* and by NMDeviceWifi to set a random MAC address during scanning.
* In this case, it's like setting it to PERMANENT. */
priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT;
@ -11634,8 +11665,10 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean
addr = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp);
if (nm_streq (addr, NM_CLONED_MAC_PRESERVE))
return TRUE;
if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) {
/* "preserve" means to reset the initial MAC address. */
return nm_device_hw_addr_reset (self);
}
if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) {
addr = nm_device_get_permanent_hw_address (self, TRUE);

View file

@ -62,6 +62,8 @@ _LOG_DECLARE_SELF(NMDeviceWifi);
#define SCAN_INTERVAL_STEP 20
#define SCAN_INTERVAL_MAX 120
#define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5
#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
@ -119,6 +121,9 @@ struct _NMDeviceWifiPrivate {
guint reacquire_iface_id;
NMDeviceWifiCapabilities capabilities;
gint32 hw_addr_scan_expire;
char *hw_addr_scan;
};
static gboolean check_scanning_allowed (NMDeviceWifi *self);
@ -169,6 +174,8 @@ static void ap_add_remove (NMDeviceWifi *self,
static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
static void _hw_addr_set_scanning (NMDeviceWifi *self);
/*****************************************************************/
static void
@ -482,7 +489,8 @@ deactivate (NMDevice *device)
/* Clear any critical protocol notification in the Wi-Fi stack */
nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
nm_device_hw_addr_reset (device);
g_clear_pointer (&priv->hw_addr_scan, g_free);
_hw_addr_set_scanning (self);
/* Ensure we're in infrastructure mode after deactivation; some devices
* (usually older ones) don't scan well in adhoc mode.
@ -1012,6 +1020,38 @@ impl_device_wifi_get_all_access_points (NMDeviceWifi *self,
g_ptr_array_unref (paths);
}
static void
_hw_addr_set_scanning (NMDeviceWifi *self)
{
NMDevice *device = (NMDevice *) self;
NMDeviceWifiPrivate *priv;
guint32 now;
g_return_if_fail (NM_IS_DEVICE_WIFI (self));
if ( nm_device_is_activating (device)
|| nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
return;
priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
now = nm_utils_get_monotonic_timestamp_s ();
if ( !priv->hw_addr_scan
|| now >= priv->hw_addr_scan_expire) {
/* the random MAC address for scanning expires after a while.
*
* We don't bother with to update the MAC address exactly when
* it expires, instead on the next scan request, we will generate
* a new one.*/
priv->hw_addr_scan_expire = now + (SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN * 60);
g_free (priv->hw_addr_scan);
priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth ();
}
nm_device_hw_addr_set (device, priv->hw_addr_scan);
}
static void
request_scan_cb (NMDevice *device,
GDBusMethodInvocation *context,
@ -1321,6 +1361,8 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options)
_LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan");
}
_hw_addr_set_scanning (self);
if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
/* success */
backoff = TRUE;
@ -2195,9 +2237,6 @@ 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 (priv->sup_iface, NULL);
@ -2212,20 +2251,9 @@ 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,
mac_randomization_support,
mac_randomization_fallback,
error)) {
g_prefix_error (error, "802-11-wireless: ");
goto error;
@ -2314,6 +2342,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
return NM_ACT_STAGE_RETURN_FAILURE;
}
/* forget the temporary MAC address used during scanning */
g_clear_pointer (&priv->hw_addr_scan, g_free);
/* Set spoof MAC to the interface */
if (!nm_device_hw_addr_set_cloned (device, connection, TRUE))
return NM_ACT_STAGE_RETURN_FAILURE;
@ -2956,6 +2987,8 @@ finalize (GObject *object)
g_hash_table_unref (priv->aps);
g_free (priv->hw_addr_scan);
G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
}

View file

@ -47,7 +47,6 @@ typedef struct
GHashTable *config;
GHashTable *blobs;
guint32 ap_scan;
NMSettingMacRandomization mac_randomization;
gboolean fast_required;
gboolean dispose_has_run;
} NMSupplicantConfigPrivate;
@ -85,7 +84,6 @@ 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;
}
@ -272,32 +270,6 @@ nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self)
return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan;
}
const char *
nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self)
{
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.
*/
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
nm_supplicant_config_fast_required (NMSupplicantConfig *self)
{
@ -385,8 +357,6 @@ gboolean
nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
NMSettingWireless * setting,
guint32 fixed_freq,
NMSupplicantFeature mac_randomization_support,
NMSettingMacRandomization mac_randomization_fallback,
GError **error)
{
NMSupplicantConfigPrivate *priv;
@ -477,23 +447,6 @@ 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) {
/* Don't use randomization, unless explicitly enabled.
* Randomization can work badly with captive portals. */
priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER;
}
}
if ( priv->mac_randomization != NM_SETTING_MAC_RANDOMIZATION_NEVER
&& mac_randomization_support != NM_SUPPLICANT_FEATURE_YES) {
g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
"cannot enable mac-randomization due to missing supplicant support");
return FALSE;
}
return TRUE;
}

View file

@ -54,8 +54,6 @@ NMSupplicantConfig *nm_supplicant_config_new (void);
guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self);
const char *nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self);
gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self);
GVariant *nm_supplicant_config_to_variant (NMSupplicantConfig *self);
@ -65,8 +63,6 @@ GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self);
gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self,
NMSettingWireless *setting,
guint32 fixed_freq,
NMSupplicantFeature mac_randomization_support,
NMSettingMacRandomization mac_randomization_fallback,
GError **error);
gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,

View file

@ -503,12 +503,6 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
priv->ap_support = ap_support;
}
NMSupplicantFeature
nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self)
{
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mac_randomization_support;
}
static void
set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
{
@ -563,7 +557,7 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
g_variant_new ("(ssv)",
WPAS_DBUS_IFACE_INTERFACE,
"PreassocMacAddr",
g_variant_new_string ("1")),
g_variant_new_string ("0")),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->init_cancellable,
@ -1223,9 +1217,7 @@ set_mac_randomization_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user
return;
}
_LOGI ("config: set MAC randomization to %s",
nm_supplicant_config_get_mac_randomization (priv->cfg));
_LOGT ("config: set MAC randomization to 0");
add_network (self);
}
@ -1256,23 +1248,20 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
nm_supplicant_config_get_ap_scan (priv->cfg));
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_variant_new_string ("0")),
G_DBUS_CALL_FLAGS_NONE,
-1,
priv->assoc_cancellable,
(GAsyncReadyCallback) set_mac_randomization_cb,
self);
} else {
} else
add_network (self);
}
}
gboolean

View file

@ -165,6 +165,4 @@ NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterfac
void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
NMSupplicantFeature apmode);
NMSupplicantFeature nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self);
#endif /* NM_SUPPLICANT_INTERFACE_H */

View file

@ -160,8 +160,6 @@ test_wifi_open (void)
g_assert (nm_supplicant_config_add_setting_wireless (config,
s_wifi,
0,
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
&error));
g_assert_no_error (error);
g_test_assert_expected_messages ();
@ -265,8 +263,6 @@ test_wifi_wep_key (const char *detail,
g_assert (nm_supplicant_config_add_setting_wireless (config,
s_wifi,
0,
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
&error));
g_assert_no_error (error);
g_test_assert_expected_messages ();
@ -410,8 +406,6 @@ test_wifi_wpa_psk (const char *detail,
g_assert (nm_supplicant_config_add_setting_wireless (config,
s_wifi,
0,
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
&error));
g_assert_no_error (error);
g_test_assert_expected_messages ();
@ -557,8 +551,6 @@ test_wifi_eap (void)
g_assert (nm_supplicant_config_add_setting_wireless (config,
s_wifi,
0,
NM_SUPPLICANT_FEATURE_UNKNOWN,
NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
&error));
g_assert_no_error (error);
g_test_assert_expected_messages ();