diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 8cd4a2f06..f3a491710 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1754,6 +1754,7 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_HS20 { INT(update_identifier) }, #endif /* CONFIG_HS20 */ + { INT_RANGE(mac_addr, 0, 1) }, }; #undef OFFSET @@ -2211,6 +2212,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) #ifdef CONFIG_IEEE80211W ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; #endif /* CONFIG_IEEE80211W */ + ssid->mac_addr = -1; } @@ -3287,6 +3289,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->wmm_ac_params[2] = ac_vi; config->wmm_ac_params[3] = ac_vo; config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY; + config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -3909,6 +3912,9 @@ static const struct global_parse_data global_fields[] = { { STR(osu_dir), 0 }, { STR(wowlan_triggers), 0 }, { INT(p2p_search_delay), 0}, + { INT(mac_addr), 0 }, + { INT(rand_addr_lifetime), 0 }, + { INT(preassoc_mac_addr), 0 }, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 52add9da3..75257c552 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -27,6 +27,7 @@ #define DEFAULT_ACCESS_NETWORK_TYPE 15 #define DEFAULT_SCAN_CUR_FREQ 0 #define DEFAULT_P2P_SEARCH_DELAY 500 +#define DEFAULT_RAND_ADDR_LIFETIME 60 #include "config_ssid.h" #include "wps/wps.h" @@ -1051,6 +1052,31 @@ struct wpa_config { * resources. */ unsigned int p2p_search_delay; + + /** + * mac_addr - MAC address policy default + * + * 0 = use permanent MAC address + * 1 = use random MAC address for each ESS connection + * + * By default, permanent MAC address is used unless policy is changed by + * the per-network mac_addr parameter. Global mac_addr=1 can be used to + * change this default behavior. + */ + int mac_addr; + + /** + * rand_addr_lifetime - Lifetime of random MAC address in seconds + */ + unsigned int rand_addr_lifetime; + + /** + * preassoc_mac_addr - Pre-association MAC address policy + * + * 0 = use permanent MAC address + * 1 = use random MAC address + */ + int preassoc_mac_addr; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 73ad57a50..5c8f04509 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -742,6 +742,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) #ifdef CONFIG_HS20 INT(update_identifier); #endif /* CONFIG_HS20 */ + write_int(f, "mac_addr", ssid->mac_addr, -1); #undef STR #undef INT @@ -1179,6 +1180,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY) fprintf(f, "p2p_search_delay=%u\n", config->p2p_search_delay); + + if (config->mac_addr) + fprintf(f, "mac_addr=%d\n", config->mac_addr); + + if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME) + fprintf(f, "rand_addr_lifetime=%u\n", + config->rand_addr_lifetime); + + if (config->preassoc_mac_addr) + fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 26b91bd18..b5dbf6ee3 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -653,6 +653,18 @@ struct wpa_ssid { #endif /* CONFIG_HS20 */ unsigned int wps_run; + + /** + * mac_addr - MAC address policy + * + * 0 = use permanent MAC address + * 1 = use random MAC address for each ESS connection + * + * Internally, special value -1 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). + */ + int mac_addr; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index c2b75f31c..510b802ca 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2480,6 +2480,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( struct wpa_ssid *remove_ssid = ssid; id = ssid->id; ssid = ssid->next; + if (wpa_s->last_ssid == remove_ssid) + wpa_s->last_ssid = NULL; wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } @@ -2498,6 +2500,9 @@ static int wpa_supplicant_ctrl_iface_remove_network( return -1; } + if (wpa_s->last_ssid == ssid) + wpa_s->last_ssid = NULL; + if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 39862681c..3a89674fa 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -597,6 +597,7 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) { struct gas_query_pending *query = work->ctx; struct gas_query *gas = query->gas; + struct wpa_supplicant *wpa_s = gas->wpa_s; if (deinit) { if (work->started) { @@ -609,6 +610,14 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) return; } + if (wpas_update_random_addr_disassoc(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to assign random MAC address for GAS"); + gas_query_free(query, 1); + radio_work_done(work); + return; + } + gas->work = work; if (gas_query_tx(gas, query, query->req) < 0) { diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index ec808772c..debceb913 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -158,6 +158,13 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) return; } + if (wpas_update_random_addr_disassoc(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to assign random MAC address for a scan"); + radio_work_done(work); + return; + } + wpa_supplicant_notify_scanning(wpa_s, 1); if (wpa_s->clear_driver_scan_cache) diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 9e3fe8462..92af1124f 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1380,6 +1380,55 @@ void wpas_connect_work_done(struct wpa_supplicant *wpa_s) } +int wpas_update_random_addr(struct wpa_supplicant *wpa_s) +{ + struct os_reltime now; + u8 addr[ETH_ALEN]; + + os_get_reltime(&now); + if (wpa_s->last_mac_addr_change.sec != 0 && + !os_reltime_expired(&now, &wpa_s->last_mac_addr_change, + wpa_s->conf->rand_addr_lifetime)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Previously selected random MAC address has not yet expired"); + return 0; + } + + if (random_mac_addr(addr) < 0) + return -1; + + if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to set random MAC address"); + return -1; + } + + os_get_reltime(&wpa_s->last_mac_addr_change); + wpa_s->mac_addr_changed = 1; + + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return -1; + } + + wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, + MAC2STR(addr)); + + return 0; +} + + +int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING || + !wpa_s->conf->preassoc_mac_addr) + return 0; + + return wpas_update_random_addr(wpa_s); +} + + static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); /** @@ -1395,6 +1444,29 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, { struct wpa_connect_work *cwork; + if (wpa_s->last_ssid == ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + } else if (ssid->mac_addr == 1 || + (ssid->mac_addr == -1 && wpa_s->conf->mac_addr == 1)) { + if (wpas_update_random_addr(wpa_s) < 0) + return; + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + } else if (wpa_s->mac_addr_changed) { + if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not restore permanent MAC address"); + return; + } + wpa_s->mac_addr_changed = 0; + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return; + } + wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); + } + wpa_s->last_ssid = ssid; + #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; @@ -2662,6 +2734,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) return -1; } + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + return 0; } diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 2a0dc204f..f2eaaa891 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -332,6 +332,23 @@ fast_reauth=1 # 1: Scan current operating frequency if another VIF on the same radio # is already associated. +# MAC address policy default +# 0 = use permanent MAC address +# 1 = use random MAC address for each ESS connection +# +# By default, permanent MAC address is used unless policy is changed by +# the per-network mac_addr parameter. Global mac_addr=1 can be used to +# change this default behavior. +#mac_addr=0 + +# Lifetime of random MAC address in seconds (default: 60) +#rand_addr_lifetime=60 + +# MAC address policy for pre-association operations (scanning, ANQP) +# 0 = use permanent MAC address +# 1 = use random MAC address +#preassoc_mac_addr=0 + # Interworking (IEEE 802.11u) # Enable Interworking @@ -962,6 +979,11 @@ fast_reauth=1 # Beacon interval (default: 100 TU) #beacon_int=100 +# MAC address policy +# 0 = use permanent MAC address +# 1 = use random MAC address for each ESS connection +#mac_addr=0 + # disable_ht: Whether HT (802.11n) should be disabled. # 0 = HT enabled (if AP supports it) # 1 = HT disabled diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index be779d884..2b6ef79ad 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -421,6 +421,7 @@ struct wpa_supplicant { int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; + struct wpa_ssid *last_ssid; struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; @@ -609,6 +610,9 @@ struct wpa_supplicant { unsigned int last_eapol_matches_bssid:1; unsigned int eap_expected_failure:1; unsigned int reattach:1; /* reassociation to the same BSS requested */ + unsigned int mac_addr_changed:1; + + struct os_reltime last_mac_addr_change; struct ibss_rsn *ibss_rsn; @@ -958,6 +962,8 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(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 wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response