diff mbox series

wpa_supplicant: Add support for pregenerated MAC

Message ID 20211110191635.1175508-1-amo@semihalf.com
State New
Headers show
Series wpa_supplicant: Add support for pregenerated MAC | expand

Commit Message

Andrzej Ostruszka Nov. 10, 2021, 7:16 p.m. UTC
Add new 'mac_addr' policy (3) with which supplicant expects to also
obtain 'mac_value' with pregenerated value of MAC address to be used for
given SSID.

The main difference between this policy and policy 1 is the ability to
control persistence of the MAC address used.  For example if there is
a requirement to always use the same (but random) MAC address for given
SSID (even if user removes/forgets the network) this could be handled
outside of the wpa_supplicant by using some SSID based hashing scheme to
generate MAC (or by just storing the randomly generated one) and
providing it to wpa_supplicant together with mac_addr=3 policy.

Signed-off-by: Andrzej Ostruszka <amo@semihalf.com>
Change-Id: I4046cf1cd08c84350a44703fe1fa0ecab4c82870
---
 wpa_supplicant/config.c                 | 46 ++++++++++++++++++++++++-
 wpa_supplicant/config_ssid.h            |  9 +++++
 wpa_supplicant/dbus/dbus_new_handlers.c | 22 +++++++++++-
 wpa_supplicant/wpa_supplicant.c         | 16 +++++++--
 wpa_supplicant/wpa_supplicant_i.h       |  3 +-
 5 files changed, 90 insertions(+), 6 deletions(-)

Comments

worker@cksn.tk Nov. 11, 2021, 6:57 a.m. UTC | #1
does this fix the problem that the user privacy setting will change the 
device-MAC address all the time? or what does it do in detail? i will 
reference this post here, so we also trying to fix some MAC/SSID/PSK 
issues: 
https://community.ui.com/questions/Does-Ubiquiti-have-any-plan-to-implement-WPA2-PPSK/1e42db2c-5fbf-42d7-a37e-793cdcc20b80

volker.

On 10.11.21 20:16, Andrzej Ostruszka wrote:
> Add new 'mac_addr' policy (3) with which supplicant expects to also
> obtain 'mac_value' with pregenerated value of MAC address to be used for
> given SSID.
>
> The main difference between this policy and policy 1 is the ability to
> control persistence of the MAC address used.  For example if there is
> a requirement to always use the same (but random) MAC address for given
> SSID (even if user removes/forgets the network) this could be handled
> outside of the wpa_supplicant by using some SSID based hashing scheme to
> generate MAC (or by just storing the randomly generated one) and
> providing it to wpa_supplicant together with mac_addr=3 policy.
>
> Signed-off-by: Andrzej Ostruszka <amo@semihalf.com>
> Change-Id: I4046cf1cd08c84350a44703fe1fa0ecab4c82870
> ---
>   wpa_supplicant/config.c                 | 46 ++++++++++++++++++++++++-
>   wpa_supplicant/config_ssid.h            |  9 +++++
>   wpa_supplicant/dbus/dbus_new_handlers.c | 22 +++++++++++-
>   wpa_supplicant/wpa_supplicant.c         | 16 +++++++--
>   wpa_supplicant/wpa_supplicant_i.h       |  3 +-
>   5 files changed, 90 insertions(+), 6 deletions(-)
>
> diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
> index bf97de698..1d17a3a32 100644
> --- a/wpa_supplicant/config.c
> +++ b/wpa_supplicant/config.c
> @@ -2321,6 +2321,49 @@ static char * wpa_config_write_peerkey(const struct parse_data *data,
>   #endif /* NO_CONFIG_WRITE */
>   
>   
> +static int wpa_config_parse_mac_value(const struct parse_data *data,
> +                                      struct wpa_ssid *ssid, int line,
> +                                      const char *value)
> +{
> +	u8 mac_value[ETH_ALEN];
> +
> +	if (hwaddr_aton(value, mac_value) == 0) {
> +		if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
> +			return 1;
> +		os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
> +		return 0;
> +	}
> +
> +	wpa_printf(MSG_ERROR, "Line %d: Invalid MAC address '%s'",
> +		   line, value);
> +	return -1;
> +}
> +
> +#ifndef NO_CONFIG_WRITE
> +static char * wpa_config_write_mac_value(const struct parse_data *data,
> +                                         struct wpa_ssid *ssid)
> +{
> +	const size_t size = 3 * ETH_ALEN;
> +	char *value;
> +	int res;
> +
> +	if (ssid->mac_addr != 3)
> +		return NULL;
> +
> +	value = os_malloc(size);
> +	if (value == NULL)
> +		return NULL;
> +	res = os_snprintf(value, size, MACSTR, MAC2STR(ssid->mac_value));
> +	if (os_snprintf_error(size, res)) {
> +		os_free(value);
> +		return NULL;
> +	}
> +	value[size-1] = '\0';
> +	return value;
> +}
> +#endif /* NO_CONFIG_WRITE */
> +
> +
>   /* Helper macros for network block parser */
>   
>   #ifdef OFFSET
> @@ -2618,7 +2661,8 @@ static const struct parse_data ssid_fields[] = {
>   	{ INT(update_identifier) },
>   	{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
>   #endif /* CONFIG_HS20 */
> -	{ INT_RANGE(mac_addr, 0, 2) },
> +	{ INT_RANGE(mac_addr, 0, 3) },
> +	{ FUNC_KEY(mac_value) },
>   	{ INT_RANGE(pbss, 0, 2) },
>   	{ INT_RANGE(wps_disabled, 0, 1) },
>   	{ INT_RANGE(fils_dh_group, 0, 65535) },
> diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
> index 339eead1c..61a5139ec 100644
> --- a/wpa_supplicant/config_ssid.h
> +++ b/wpa_supplicant/config_ssid.h
> @@ -954,6 +954,7 @@ struct wpa_ssid {
>   	 * 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)
> +	 * 3 = use dedicated/pregenerated MAC address
>   	 *
>   	 * Internally, special value -1 is used to indicate that the parameter
>   	 * was not specified in the configuration (i.e., default behavior is
> @@ -961,6 +962,14 @@ struct wpa_ssid {
>   	 */
>   	int mac_addr;
>   
> +	/**
> +	 * mac_value - specific MAC address to be used
> +	 *
> +	 * When mac_addr policy is equal to 3 this is the value of the MAC
> +	 * address that should be used.
> +	 */
> +	u8 mac_value[ETH_ALEN];
> +
>   	/**
>   	 * no_auto_peer - Do not automatically peer with compatible mesh peers
>   	 *
> diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
> index db9f30c9a..c4a7ceb44 100644
> --- a/wpa_supplicant/dbus/dbus_new_handlers.c
> +++ b/wpa_supplicant/dbus/dbus_new_handlers.c
> @@ -148,7 +148,7 @@ static const char * const dont_quote[] = {
>   #ifdef CONFIG_P2P
>   	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
>   #endif /* CONFIG_P2P */
> -	NULL
> +	"mac_value", NULL
>   };
>   
>   static dbus_bool_t should_quote_opt(const char *key)
> @@ -202,6 +202,8 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
>   	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
>   	DBusMessageIter	iter_dict;
>   	char *value = NULL;
> +	bool mac_addr3_set = false;
> +	bool mac_value_set = false;
>   
>   	if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
>   		return FALSE;
> @@ -311,12 +313,30 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
>   		else if (os_strcmp(entry.key, "priority") == 0)
>   			wpa_config_update_prio_list(wpa_s->conf);
>   
> +		/*
> +		 * MAC address policy "3" needs to come with mac_value in
> +		 * the message so make sure that it is present (checked after
> +		 * the loop - here we just note what has been supplied).
> +		 */
> +		if (os_strcmp(entry.key, "mac_addr") == 0 &&
> +		    atoi(value) == 3)
> +			mac_addr3_set = true;
> +		if (os_strcmp(entry.key, "mac_value") == 0)
> +			mac_value_set = true;
> +
>   	skip_update:
>   		os_free(value);
>   		value = NULL;
>   		wpa_dbus_dict_entry_clear(&entry);
>   	}
>   
> +	if (mac_addr3_set && !mac_value_set) {
> +		wpa_printf(MSG_ERROR, "Invalid mac_addr policy config");
> +		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
> +				     "Invalid mac_addr policy config");
> +		return FALSE;
> +	}
> +
>   	return TRUE;
>   
>   error:
> diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
> index b80f1d4f0..c6f32e75f 100644
> --- a/wpa_supplicant/wpa_supplicant.c
> +++ b/wpa_supplicant/wpa_supplicant.c
> @@ -2058,13 +2058,16 @@ void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
>   }
>   
>   
> -int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
> +int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
> +			    struct wpa_ssid *ssid)
>   {
>   	struct os_reltime now;
>   	u8 addr[ETH_ALEN];
>   
>   	os_get_reltime(&now);
>   	if (wpa_s->last_mac_addr_style == style &&
> +	    /* pregenerated addresses do not expire */
> +	    wpa_s->last_mac_addr_style != 3 &&
>   	    wpa_s->last_mac_addr_change.sec != 0 &&
>   	    !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
>   				wpa_s->conf->rand_addr_lifetime)) {
> @@ -2083,6 +2086,13 @@ int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
>   		if (random_mac_addr_keep_oui(addr) < 0)
>   			return -1;
>   		break;
> +	case 3:
> +		if (!ssid) {
> +			wpa_msg(wpa_s, MSG_ERROR, "Invalid 'ssid' for address policy 3");
> +			return -1;
> +		}
> +		os_memcpy(addr, ssid->mac_value, ETH_ALEN);
> +		break;
>   	default:
>   		return -1;
>   	}
> @@ -2116,7 +2126,7 @@ int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
>   	    !wpa_s->conf->preassoc_mac_addr)
>   		return 0;
>   
> -	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
> +	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr, NULL);
>   }
>   
>   
> @@ -2251,7 +2261,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
>   #endif /* CONFIG_SAE */
>   
>   	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
> -		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
> +		if (wpas_update_random_addr(wpa_s, rand_style, ssid) < 0)
>   			return;
>   		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
>   	} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
> diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
> index cbc955159..ee8049a4c 100644
> --- a/wpa_supplicant/wpa_supplicant_i.h
> +++ b/wpa_supplicant/wpa_supplicant_i.h
> @@ -1631,7 +1631,8 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
>   void wpas_request_connection(struct wpa_supplicant *wpa_s);
>   void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
>   int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
> -int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
> +int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
> +			    struct wpa_ssid *ssid);
>   int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
>   void add_freq(int *freqs, int *num_freqs, int freq);
>
Andrzej Ostruszka Dec. 7, 2021, 12:28 p.m. UTC | #2
My apologies Volker for not getting back to you earlier but somehow
I did not receive your e-mail.  I've just noticed now in patchwork that
you actually commented on this patch so I'll copy paste your question
below and try to answer.

> does this fix the problem that the user privacy setting will change the
> device-MAC address all the time? or what does it do in detail?

If you consider changing MAC as a problem then this actually might
aggravate it (or resolve - you would have to be more specific in your
question).

The context of this patch is that in Chrome OS we are adding MAC address
randomization and instead of changing MAC ourselves we have decided to
use WPA supplicant support for that and enhance it a bit.

Previously supplicant supported only completely random MAC so when you
are reconnecting to the same network the MAC will change.  This might be
OK for open/public networks but does not work well for cases where
stable IP/MAC is desired.  So the proposal is to add another 'mac_addr'
policy value with which one would be able to provide "stable" MAC to be
used for given network.

Concrete implementation we are working on right now is that 'shill'
(Chrome OS "network manager") will generate random MAC for given network
and provide it to supplicant over DBus via:

  { mac_addr: 3, mac_value:"AA:BB:CC:XX:YY:ZZ" }

arguments of AddNetwork method and for this network this MAC
will be used (until device reset, or the user decides to change the MAC
policy e.g. to not randomize at all).

Hope this answers your question

With regards
Andrzej
diff mbox series

Patch

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index bf97de698..1d17a3a32 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2321,6 +2321,49 @@  static char * wpa_config_write_peerkey(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_mac_value(const struct parse_data *data,
+                                      struct wpa_ssid *ssid, int line,
+                                      const char *value)
+{
+	u8 mac_value[ETH_ALEN];
+
+	if (hwaddr_aton(value, mac_value) == 0) {
+		if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
+			return 1;
+		os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR, "Line %d: Invalid MAC address '%s'",
+		   line, value);
+	return -1;
+}
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_mac_value(const struct parse_data *data,
+                                         struct wpa_ssid *ssid)
+{
+	const size_t size = 3 * ETH_ALEN;
+	char *value;
+	int res;
+
+	if (ssid->mac_addr != 3)
+		return NULL;
+
+	value = os_malloc(size);
+	if (value == NULL)
+		return NULL;
+	res = os_snprintf(value, size, MACSTR, MAC2STR(ssid->mac_value));
+	if (os_snprintf_error(size, res)) {
+		os_free(value);
+		return NULL;
+	}
+	value[size-1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -2618,7 +2661,8 @@  static const struct parse_data ssid_fields[] = {
 	{ INT(update_identifier) },
 	{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
 #endif /* CONFIG_HS20 */
-	{ INT_RANGE(mac_addr, 0, 2) },
+	{ INT_RANGE(mac_addr, 0, 3) },
+	{ FUNC_KEY(mac_value) },
 	{ INT_RANGE(pbss, 0, 2) },
 	{ INT_RANGE(wps_disabled, 0, 1) },
 	{ INT_RANGE(fils_dh_group, 0, 65535) },
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 339eead1c..61a5139ec 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -954,6 +954,7 @@  struct wpa_ssid {
 	 * 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)
+	 * 3 = use dedicated/pregenerated MAC address
 	 *
 	 * Internally, special value -1 is used to indicate that the parameter
 	 * was not specified in the configuration (i.e., default behavior is
@@ -961,6 +962,14 @@  struct wpa_ssid {
 	 */
 	int mac_addr;
 
+	/**
+	 * mac_value - specific MAC address to be used
+	 *
+	 * When mac_addr policy is equal to 3 this is the value of the MAC
+	 * address that should be used.
+	 */
+	u8 mac_value[ETH_ALEN];
+
 	/**
 	 * no_auto_peer - Do not automatically peer with compatible mesh peers
 	 *
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index db9f30c9a..c4a7ceb44 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -148,7 +148,7 @@  static const char * const dont_quote[] = {
 #ifdef CONFIG_P2P
 	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
 #endif /* CONFIG_P2P */
-	NULL
+	"mac_value", NULL
 };
 
 static dbus_bool_t should_quote_opt(const char *key)
@@ -202,6 +202,8 @@  dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 	DBusMessageIter	iter_dict;
 	char *value = NULL;
+	bool mac_addr3_set = false;
+	bool mac_value_set = false;
 
 	if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
 		return FALSE;
@@ -311,12 +313,30 @@  dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 		else if (os_strcmp(entry.key, "priority") == 0)
 			wpa_config_update_prio_list(wpa_s->conf);
 
+		/*
+		 * MAC address policy "3" needs to come with mac_value in
+		 * the message so make sure that it is present (checked after
+		 * the loop - here we just note what has been supplied).
+		 */
+		if (os_strcmp(entry.key, "mac_addr") == 0 &&
+		    atoi(value) == 3)
+			mac_addr3_set = true;
+		if (os_strcmp(entry.key, "mac_value") == 0)
+			mac_value_set = true;
+
 	skip_update:
 		os_free(value);
 		value = NULL;
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
+	if (mac_addr3_set && !mac_value_set) {
+		wpa_printf(MSG_ERROR, "Invalid mac_addr policy config");
+		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+				     "Invalid mac_addr policy config");
+		return FALSE;
+	}
+
 	return TRUE;
 
 error:
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index b80f1d4f0..c6f32e75f 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2058,13 +2058,16 @@  void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
 }
 
 
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+			    struct wpa_ssid *ssid)
 {
 	struct os_reltime now;
 	u8 addr[ETH_ALEN];
 
 	os_get_reltime(&now);
 	if (wpa_s->last_mac_addr_style == style &&
+	    /* pregenerated addresses do not expire */
+	    wpa_s->last_mac_addr_style != 3 &&
 	    wpa_s->last_mac_addr_change.sec != 0 &&
 	    !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
 				wpa_s->conf->rand_addr_lifetime)) {
@@ -2083,6 +2086,13 @@  int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
 		if (random_mac_addr_keep_oui(addr) < 0)
 			return -1;
 		break;
+	case 3:
+		if (!ssid) {
+			wpa_msg(wpa_s, MSG_ERROR, "Invalid 'ssid' for address policy 3");
+			return -1;
+		}
+		os_memcpy(addr, ssid->mac_value, ETH_ALEN);
+		break;
 	default:
 		return -1;
 	}
@@ -2116,7 +2126,7 @@  int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
 	    !wpa_s->conf->preassoc_mac_addr)
 		return 0;
 
-	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr, NULL);
 }
 
 
@@ -2251,7 +2261,7 @@  void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_SAE */
 
 	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
-		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+		if (wpas_update_random_addr(wpa_s, rand_style, ssid) < 0)
 			return;
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index cbc955159..ee8049a4c 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1631,7 +1631,8 @@  int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
 void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+			    struct wpa_ssid *ssid);
 int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
 void add_freq(int *freqs, int *num_freqs, int freq);