diff options
87 files changed, 1900 insertions, 435 deletions
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index 9de9438b..f0e46044 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,5 +1,24 @@ ChangeLog for hostapd +2014-10-09 - v2.3 + * fixed number of minor issues identified in static analyzer warnings + * fixed DFS and channel switch operation for multi-BSS cases + * started to use constant time comparison for various password and hash + values to reduce possibility of any externally measurable timing + differences + * extended explicit clearing of freed memory and expired keys to avoid + keeping private data in memory longer than necessary + * added support for number of new RADIUS attributes from RFC 7268 + (Mobility-Domain-Id, WLAN-HESSID, WLAN-Pairwise-Cipher, + WLAN-Group-Cipher, WLAN-AKM-Suite, WLAN-Group-Mgmt-Pairwise-Cipher) + * fixed GET_CONFIG wpa_pairwise_cipher value + * added code to clear bridge FDB entry on station disconnection + * fixed PMKSA cache timeout from Session-Timeout for WPA/WPA2 cases + * fixed OKC PMKSA cache entry fetch to avoid a possible infinite loop + in case the first entry does not match + * fixed hostapd_cli action script execution to use more robust mechanism + (CVE-2014-3686) + 2014-06-04 - v2.2 * fixed SAE confirm-before-commit validation to avoid a potential segmentation fault in an unexpected message sequence that could be diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 4c0e3f83..44de8260 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -3178,7 +3178,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_config * hostapd_config_read(const char *fname) { struct hostapd_config *conf; - struct hostapd_bss_config *bss; FILE *f; char buf[512], *pos; int line = 0; @@ -3207,9 +3206,11 @@ struct hostapd_config * hostapd_config_read(const char *fname) return NULL; } - bss = conf->last_bss = conf->bss[0]; + conf->last_bss = conf->bss[0]; while (fgets(buf, sizeof(buf), f)) { + struct hostapd_bss_config *bss; + bss = conf->last_bss; line++; diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9ce78292..591c3957 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1010,7 +1010,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, return pos - buf; pos += ret; - ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise, " "); if (ret < 0) return pos - buf; diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index d1275509..c7da69e0 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -761,7 +761,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { bss->disable_11n = 1; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " - "allowed, disabling HT capabilites"); + "allowed, disabling HT capabilities"); } if (full_config && conf->ieee80211n && diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 20419f32..a6ec20bd 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -18,10 +18,12 @@ #include "dfs.h" -static int dfs_get_used_n_chans(struct hostapd_iface *iface) +static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) { int n_chans = 1; + *seg1 = 0; + if (iface->conf->ieee80211n && iface->conf->secondary_channel) n_chans = 2; @@ -35,6 +37,10 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface) case VHT_CHANWIDTH_160MHZ: n_chans = 8; break; + case VHT_CHANWIDTH_80P80MHZ: + n_chans = 4; + *seg1 = 4; + break; default: break; } @@ -170,10 +176,10 @@ static int dfs_find_channel(struct hostapd_iface *iface, { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; - int i, channel_idx = 0, n_chans; + int i, channel_idx = 0, n_chans, n_chans1; mode = iface->current_mode; - n_chans = dfs_get_used_n_chans(iface); + n_chans = dfs_get_used_n_chans(iface, &n_chans1); wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); for (i = 0; i < mode->num_channels; i++) { @@ -246,12 +252,15 @@ static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, /* Return start channel idx we will use for mode->channels[idx] */ -static int dfs_get_start_chan_idx(struct hostapd_iface *iface) +static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int channel_no = iface->conf->channel; int res = -1, i; + int chan_seg1 = -1; + + *seg1_start = -1; /* HT40- */ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) @@ -270,9 +279,15 @@ static int dfs_get_start_chan_idx(struct hostapd_iface *iface) channel_no = iface->conf->vht_oper_centr_freq_seg0_idx - 14; break; + case VHT_CHANWIDTH_80P80MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 6; + chan_seg1 = + iface->conf->vht_oper_centr_freq_seg1_idx - 6; + break; default: wpa_printf(MSG_INFO, - "DFS only VHT20/40/80/160 is supported now"); + "DFS only VHT20/40/80/160/80+80 is supported now"); channel_no = -1; break; } @@ -288,6 +303,23 @@ static int dfs_get_start_chan_idx(struct hostapd_iface *iface) } } + if (res != -1 && chan_seg1 > -1) { + int found = 0; + + /* Get idx for seg1 */ + mode = iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == chan_seg1) { + *seg1_start = i; + found = 1; + break; + } + } + if (!found) + res = -1; + } + if (res == -1) { wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d", @@ -511,17 +543,17 @@ static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, int chan_width, int cf1, int cf2) { - int start_chan_idx; + int start_chan_idx, start_chan_idx1; struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; - int n_chans, i, j, frequency = freq, radar_n_chans = 1; + int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; u8 radar_chan; int res = 0; /* Our configuration */ mode = iface->current_mode; - start_chan_idx = dfs_get_start_chan_idx(iface); - n_chans = dfs_get_used_n_chans(iface); + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Check we are on DFS channel(s) */ if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) @@ -604,19 +636,20 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, int hostapd_handle_dfs(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; - int res, n_chans, start_chan_idx; + int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; int skip_radar = 0; iface->cac_started = 0; do { /* Get start (first) channel for current configuration */ - start_chan_idx = dfs_get_start_chan_idx(iface); + start_chan_idx = dfs_get_start_chan_idx(iface, + &start_chan_idx1); if (start_chan_idx == -1) return -1; /* Get number of used channels, depend on width */ - n_chans = dfs_get_used_n_chans(iface); + n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Setup CAC time */ iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, @@ -928,20 +961,25 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int hostapd_is_dfs_required(struct hostapd_iface *iface) { - int n_chans, start_chan_idx; + int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; if (!iface->conf->ieee80211h || !iface->current_mode || iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; /* Get start (first) channel for current configuration */ - start_chan_idx = dfs_get_start_chan_idx(iface); + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); if (start_chan_idx == -1) return -1; /* Get number of used channels, depend on width */ - n_chans = dfs_get_used_n_chans(iface); + n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Check if any of configured channels require DFS */ - return dfs_check_chans_radar(iface, start_chan_idx, n_chans); + res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); + if (res) + return res; + if (start_chan_idx1 >= 0 && n_chans1 > 0) + res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); + return res; } diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index c687ba03..09eb89cf 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -340,6 +340,9 @@ skip_wpa_check: sta->auth_alg, req_ies, req_ies_len); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); + + if (sta->auth_alg == WLAN_AUTH_FT) + ap_sta_set_authorized(hapd, sta, 1); #else /* CONFIG_IEEE80211R */ /* Keep compiler silent about unused variables */ if (status) { @@ -350,6 +353,8 @@ skip_wpa_check: sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + hostapd_set_sta_flags(hapd, sta); + if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 59567dd6..31423915 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -693,10 +693,10 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) u8 if_addr[ETH_ALEN]; wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", - __func__, hapd, hapd->conf->iface, first); + __func__, hapd, conf->iface, first); #ifdef EAP_SERVER_TNC - if (hapd->conf->tnc && tncs_global_init() < 0) { + if (conf->tnc && tncs_global_init() < 0) { wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); return -1; } @@ -704,37 +704,37 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s was already started", - __func__, hapd->conf->iface); + __func__, conf->iface); return -1; } hapd->started = 1; if (!first || first == -1) { - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { + if (hostapd_mac_comp_empty(conf->bssid) == 0) { /* Allocate the next available BSSID. */ do { inc_byte_array(hapd->own_addr, ETH_ALEN); } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } else { /* Allocate the configured BSSID. */ - os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); + os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) == 0) { wpa_printf(MSG_ERROR, "BSS '%s' may not have " "BSSID set to the MAC address of " - "the radio", hapd->conf->iface); + "the radio", conf->iface); return -1; } } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, - hapd->conf->iface, hapd->own_addr, hapd, + conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr, - hapd->conf->bridge[0] ? hapd->conf->bridge : - NULL, first == -1)) { + conf->bridge[0] ? conf->bridge : NULL, + first == -1)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); hapd->interface_added = 0; @@ -749,7 +749,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); - if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + if (hostapd_setup_encryption(conf->iface, hapd)) return -1; /* @@ -783,9 +783,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR " and ssid \"%s\"", - hapd->conf->iface, MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); + conf->iface, MAC2STR(hapd->own_addr), + wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { @@ -810,17 +809,17 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->conf->radius_das_port) { + if (conf->radius_das_port) { struct radius_das_conf das_conf; os_memset(&das_conf, 0, sizeof(das_conf)); - das_conf.port = hapd->conf->radius_das_port; - das_conf.shared_secret = hapd->conf->radius_das_shared_secret; + das_conf.port = conf->radius_das_port; + das_conf.shared_secret = conf->radius_das_shared_secret; das_conf.shared_secret_len = - hapd->conf->radius_das_shared_secret_len; - das_conf.client_addr = &hapd->conf->radius_das_client_addr; - das_conf.time_window = hapd->conf->radius_das_time_window; + conf->radius_das_shared_secret_len; + das_conf.client_addr = &conf->radius_das_client_addr; + das_conf.time_window = conf->radius_das_time_window; das_conf.require_event_timestamp = - hapd->conf->radius_das_require_event_timestamp; + conf->radius_das_require_event_timestamp; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; hapd->radius_das = radius_das_init(&das_conf); @@ -847,7 +846,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if ((hapd->conf->wpa || hapd->conf->osen) && hostapd_setup_wpa(hapd)) + if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd)) return -1; if (accounting_init(hapd)) { @@ -855,8 +854,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->conf->ieee802_11f && - (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { + if (conf->ieee802_11f && + (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) { wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " "failed."); return -1; @@ -881,7 +880,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (!hapd->conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) + if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) return -1; if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index e4681e90..2d09b67b 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1622,6 +1622,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) break; + sta->session_timeout_set = !!session_timeout_set; + sta->session_timeout = session_timeout; + /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { @@ -2396,6 +2399,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, size_t len; /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; + unsigned int session_timeout; #ifdef CONFIG_HS20 if (remediation && !sta->remediation) { @@ -2430,9 +2434,13 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (sta->session_timeout_set) + session_timeout = sta->session_timeout; + else + session_timeout = dot11RSNAConfigPMKLifetime; if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 4720b59c..9de4cffe 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -37,14 +37,12 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { - if (entry == NULL) - return; os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS radius_free_class(&entry->radius_class); #endif /* CONFIG_NO_RADIUS */ - os_free(entry); + bin_clear_free(entry, sizeof(*entry)); } @@ -52,38 +50,42 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; + unsigned int hash; pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx); - pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + + /* unlink from hash list */ + hash = PMKID_HASH(entry->pmkid); + pos = pmksa->pmkid[hash]; prev = NULL; while (pos) { if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - pmksa->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } + if (prev != NULL) + prev->hnext = entry->hnext; + else + pmksa->pmkid[hash] = entry->hnext; break; } prev = pos; pos = pos->hnext; } + /* unlink from entry list */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos == entry) { if (prev != NULL) - prev->next = pos->next; + prev->next = entry->next; else - pmksa->pmksa = pos->next; + pmksa->pmksa = entry->next; break; } prev = pos; pos = pos->next; } + _pmksa_cache_free_entry(entry); } @@ -188,6 +190,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; + int hash; /* Add the new entry; order by expiration time */ pos = pmksa->pmksa; @@ -205,8 +208,10 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, entry->next = prev->next; prev->next = entry; } - entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; - pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + hash = PMKID_HASH(entry->pmkid); + entry->hnext = pmksa->pmkid[hash]; + pmksa->pmkid[hash] = entry; pmksa->pmksa_count++; if (prev == NULL) @@ -342,6 +347,8 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) _pmksa_cache_free_entry(prev); } eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + pmksa->pmksa_count = 0; + pmksa->pmksa = NULL; for (i = 0; i < PMKID_HASH_SIZE; i++) pmksa->pmkid[i] = NULL; os_free(pmksa); @@ -361,18 +368,22 @@ pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, { struct rsn_pmksa_cache_entry *entry; - if (pmkid) - entry = pmksa->pmkid[PMKID_HASH(pmkid)]; - else - entry = pmksa->pmksa; - while (entry) { - if ((spa == NULL || - os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; + if (pmkid) { + for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry; + entry = entry->hnext) { + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) + return entry; + } + } else { + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) + return entry; + } } + return NULL; } @@ -394,15 +405,13 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache_entry *entry; u8 new_pmkid[PMKID_LEN]; - entry = pmksa->pmksa; - while (entry) { + for (entry = pmksa->pmksa; entry; entry = entry->next) { if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, wpa_key_mgmt_sha256(entry->akmp)); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; - entry = entry->next; } return NULL; } diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 6a570fa5..f0f01b4a 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -961,12 +961,12 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, dev_addr = addr; } else dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); -#endif /* CONFIG_P2P */ if (dev_addr) os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); else +#endif /* CONFIG_P2P */ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); if (hapd->sta_authorized_cb) diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 03db98f6..faf32d85 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -60,6 +60,7 @@ struct sta_info { unsigned int qos_map_enabled:1; unsigned int remediation:1; unsigned int hs20_deauth_requested:1; + unsigned int session_timeout_set:1; u16 auth_alg; @@ -135,6 +136,8 @@ struct sta_info { #ifdef CONFIG_SAE struct sae_data *sae; #endif /* CONFIG_SAE */ + + u32 session_timeout; /* valid only if session_timeout_set == 1 */ }; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 2bb8aab8..1a16b5c8 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1390,7 +1390,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { - if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + if (aes_wrap(sm->PTK.kek, 16, + (key_data_len - 8) / 8, buf, (u8 *) (key + 1))) { os_free(hdr); os_free(buf); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 8a6ca71c..781f15fb 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -344,7 +344,8 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + if (aes_wrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; @@ -459,7 +460,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); - if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { + if (aes_wrap(sm->PTK.kek, 16, key_len / 8, key, subelem + 13)) { os_free(subelem); return NULL; } @@ -491,7 +492,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; *pos++ = WPA_IGTK_LEN; - if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + if (aes_wrap(sm->PTK.kek, 16, WPA_IGTK_LEN / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { os_free(subelem); return NULL; @@ -1336,7 +1337,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, frame = (struct ft_r0kh_r1kh_pull_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + if (aes_unwrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); @@ -1376,7 +1378,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, r.pairwise = host_to_le16(pairwise); os_memset(r.pad, 0, sizeof(r.pad)); - if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + if (aes_wrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { os_memset(pmk_r0, 0, PMK_LEN); return -1; @@ -1464,7 +1467,8 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, frame = (struct ft_r0kh_r1kh_resp_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, frame->nonce, f.nonce) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); @@ -1530,7 +1534,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, frame = (struct ft_r0kh_r1kh_push_frame *) data; /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, frame->timestamp, f.timestamp) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); @@ -1727,7 +1732,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, WPA_PUT_LE32(f.timestamp, now.sec); f.pairwise = host_to_le16(pairwise); os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + if (aes_wrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, f.timestamp, frame.timestamp) < 0) return; diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index e4205f49..92d687ac 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -32,6 +32,13 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event * + * @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use + * internal BSS-selection. This command uses + * @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy + * for the current connection (i.e., changes policy set by the nl80211 + * Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be + * included to indicate which BSS to use in case roaming is disabled. + * * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency * ranges to avoid to reduce issues due to interference or internal * co-existence information in the driver. The event data structure is @@ -51,12 +58,13 @@ enum qca_radiotap_vendor_ids { enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, QCA_NL80211_VENDOR_SUBCMD_TEST = 1, - /* subcmds 2..9 not yet allocated */ + /* subcmds 2..8 not yet allocated */ + QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9, QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, - /* 14..33 - reserved for QCA */ + /* 14..49 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, }; @@ -71,6 +79,10 @@ enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4, + /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined + * by enum qca_roaming_policy. */ + QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, + QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -96,4 +108,9 @@ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211AD, }; +enum qca_roaming_policy { + QCA_ROAMING_NOT_ALLOWED, + QCA_ROAMING_ALLOWED_WITHIN_ESS, +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/version.h b/src/common/version.h index 1f254329..726289d9 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.3-devel" VERSION_STR_POSTFIX +#define VERSION_STR "2.3" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index d91594e3..4812f8df 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -345,9 +345,10 @@ int wpa_ctrl_detach(struct wpa_ctrl *ctrl); * @reply_len: Length of the reply buffer * Returns: 0 on success, -1 on failure * - * This function will receive a pending control interface message. This - * function will block if no messages are available. The received response will - * be written to reply and reply_len is set to the actual length of the reply. + * This function will receive a pending control interface message. The received + * response will be written to reply and reply_len is set to the actual length + * of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() * must have been used to register the control interface as an event monitor. */ diff --git a/src/crypto/aes-unwrap.c b/src/crypto/aes-unwrap.c index 9dd51602..ec793d9d 100644 --- a/src/crypto/aes-unwrap.c +++ b/src/crypto/aes-unwrap.c @@ -1,5 +1,5 @@ /* - * AES key unwrap (128-bit KEK, RFC3394) + * AES key unwrap (RFC3394) * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * @@ -14,26 +14,29 @@ #include "aes_wrap.h" /** - * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394) * @kek: Key encryption key (KEK) + * @kek_len: Length of KEK in octets * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 * bytes * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits * @plain: Plaintext key, n * 64 bits * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) */ -int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) { - u8 a[8], *r, b[16]; + u8 a[8], *r, b[AES_BLOCK_SIZE]; int i, j; void *ctx; + unsigned int t; /* 1) Initialize variables. */ os_memcpy(a, cipher, 8); r = plain; os_memcpy(r, cipher + 8, 8 * n); - ctx = aes_decrypt_init(kek, 16); + ctx = aes_decrypt_init(kek, kek_len); if (ctx == NULL) return -1; @@ -48,7 +51,11 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { os_memcpy(b, a, 8); - b[7] ^= n * j + i; + t = n * j + i; + b[7] ^= t; + b[6] ^= t >> 8; + b[5] ^= t >> 16; + b[4] ^= t >> 24; os_memcpy(b + 8, r, 8); aes_decrypt(ctx, b, b); diff --git a/src/crypto/aes-wrap.c b/src/crypto/aes-wrap.c index 89d6f94b..7ed34e80 100644 --- a/src/crypto/aes-wrap.c +++ b/src/crypto/aes-wrap.c @@ -1,5 +1,5 @@ /* - * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * AES Key Wrap Algorithm (RFC3394) * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * @@ -14,19 +14,21 @@ #include "aes_wrap.h" /** - * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: 16-octet Key encryption key (KEK) + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (RFC3394) + * @kek: Key encryption key (KEK) + * @kek_len: Length of KEK in octets * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 * bytes * @plain: Plaintext key to be wrapped, n * 64 bits * @cipher: Wrapped key, (n + 1) * 64 bits * Returns: 0 on success, -1 on failure */ -int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) { - u8 *a, *r, b[16]; + u8 *a, *r, b[AES_BLOCK_SIZE]; int i, j; void *ctx; + unsigned int t; a = cipher; r = cipher + 8; @@ -35,7 +37,7 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) os_memset(a, 0xa6, 8); os_memcpy(r, plain, 8 * n); - ctx = aes_encrypt_init(kek, 16); + ctx = aes_encrypt_init(kek, kek_len); if (ctx == NULL) return -1; @@ -53,7 +55,11 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) os_memcpy(b + 8, r, 8); aes_encrypt(ctx, b, b); os_memcpy(a, b, 8); - a[7] ^= n * j + i; + t = n * j + i; + a[7] ^= t; + a[6] ^= t >> 8; + a[5] ^= t >> 16; + a[4] ^= t >> 24; os_memcpy(r, b + 8, 8); r += 8; } diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h index 0433c043..6b3727c7 100644 --- a/src/crypto/aes_wrap.h +++ b/src/crypto/aes_wrap.h @@ -1,7 +1,7 @@ /* * AES-based functions * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - AES Key Wrap Algorithm (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption @@ -18,8 +18,10 @@ #ifndef AES_WRAP_H #define AES_WRAP_H -int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); -int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, + u8 *cipher); +int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n, + const u8 *cipher, u8 *plain); int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f02aaacb..b4c59d18 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -40,7 +40,7 @@ static BIGNUM * get_group5_prime(void) { -#if OPENSSL_VERSION_NUMBER < 0x00908000 +#if OPENSSL_VERSION_NUMBER < 0x00908000 || defined(OPENSSL_IS_BORINGSSL) static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, @@ -130,7 +130,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } pkey[i] = next | 1; - DES_set_key(&pkey, &ks); + DES_set_key((DES_cblock *) &pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); } @@ -199,8 +199,10 @@ static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) switch (keylen) { case 16: return EVP_aes_128_ecb(); +#ifndef OPENSSL_IS_BORINGSSL case 24: return EVP_aes_192_ecb(); +#endif /* OPENSSL_IS_BORINGSSL */ case 32: return EVP_aes_256_ecb(); } @@ -378,9 +380,11 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, case 16: cipher = EVP_aes_128_cbc(); break; +#ifndef OPENSSL_IS_BORINGSSL case 24: cipher = EVP_aes_192_cbc(); break; +#endif /* OPENSSL_IS_BORINGSSL */ case 32: cipher = EVP_aes_256_cbc(); break; @@ -1067,6 +1071,7 @@ void crypto_ec_deinit(struct crypto_ec *e) if (e == NULL) return; BN_clear_free(e->order); + BN_clear_free(e->prime); EC_GROUP_free(e->group); BN_CTX_free(e->bnctx); os_free(e); diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index d2d66003..e1534224 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -38,14 +38,26 @@ #define OPENSSL_SUPPORTS_CTX_APP_DATA #endif -#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT -#ifdef SSL_OP_NO_TICKET +#if OPENSSL_VERSION_NUMBER < 0x10000000L +/* ERR_remove_thread_state replaces ERR_remove_state and the latter is + * deprecated. However, OpenSSL 0.9.8 doesn't include + * ERR_remove_thread_state. */ +#define ERR_remove_thread_state(tid) ERR_remove_state(0) +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* * Session ticket override patch was merged into OpenSSL 0.9.9 tree on * 2008-11-15. This version uses a bit different API compared to the old patch. */ #define CONFIG_OPENSSL_TICKET_OVERRIDE #endif + +#if defined(OPENSSL_IS_BORINGSSL) +/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ +typedef size_t stack_index_t; +#else +typedef int stack_index_t; #endif #ifdef SSL_set_tlsext_status_type @@ -853,7 +865,7 @@ void tls_deinit(void *ssl_ctx) ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); + ERR_remove_thread_state(NULL); ERR_free_strings(); EVP_cleanup(); os_free(tls_global->ocsp_stapling_response); @@ -1102,7 +1114,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, { GENERAL_NAME *gen; void *ext; - int i, found = 0; + int found = 0; + stack_index_t i; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); @@ -1204,6 +1217,7 @@ static int tls_match_suffix(X509 *cert, const char *match) GENERAL_NAME *gen; void *ext; int i; + stack_index_t j; int dns_name = 0; X509_NAME *name; @@ -1211,8 +1225,8 @@ static int tls_match_suffix(X509 *cert, const char *match) ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { - gen = sk_GENERAL_NAME_value(ext, i); + for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) { + gen = sk_GENERAL_NAME_value(ext, j); if (gen->type != GEN_DNS) continue; dns_name++; @@ -1639,7 +1653,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { BIO *bio = BIO_from_keystore(&ca_cert[11]); STACK_OF(X509_INFO) *stack = NULL; - int i; + stack_index_t i; if (bio) { stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); @@ -3386,9 +3400,15 @@ unsigned int tls_capabilities(void *tls_ctx) * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ +#ifdef OPENSSL_IS_BORINGSSL +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + const SSL_CIPHER **cipher, void *arg) +#else /* OPENSSL_IS_BORINGSSL */ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg) +#endif /* OPENSSL_IS_BORINGSSL */ { struct tls_connection *conn = arg; int ret; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 1f3693a3..bb57fcf1 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -411,6 +411,25 @@ enum wps_mode { */ }; +struct hostapd_freq_params { + int mode; + int freq; + int channel; + /* for HT */ + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ + + /* for VHT */ + int vht_enabled; + + /* valid for both HT and VHT, center_freq2 is non-zero + * only for bandwidth 80 and an 80+80 channel */ + int center_freq1, center_freq2; + int bandwidth; +}; + /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). @@ -443,11 +462,9 @@ struct wpa_driver_associate_params { size_t ssid_len; /** - * freq - Frequency of the channel the selected AP is using - * Frequency that the selected AP is using (in MHz as - * reported in the scan results) + * freq - channel parameters */ - int freq; + struct hostapd_freq_params freq; /** * freq_hint - Frequency of the channel the proposed AP is using @@ -1126,25 +1143,6 @@ struct hostapd_sta_add_params { size_t supp_oper_classes_len; }; -struct hostapd_freq_params { - int mode; - int freq; - int channel; - /* for HT */ - int ht_enabled; - int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, - * secondary channel below primary, 1 = HT40 - * enabled, secondary channel above primary */ - - /* for VHT */ - int vht_enabled; - - /* valid for both HT and VHT, center_freq2 is non-zero - * only for bandwidth 80 and an 80+80 channel */ - int center_freq1, center_freq2; - int bandwidth; -}; - struct mac_address { u8 addr[ETH_ALEN]; }; @@ -2549,6 +2547,7 @@ struct wpa_driver_ops { * @dialog_token: Dialog Token to use in the message (if needed) * @status_code: Status Code or Reason Code to use (if needed) * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield) + * @initiator: Is the current end the TDLS link initiator * @buf: TDLS IEs to add to the message * @len: Length of buf in octets * Returns: 0 on success, negative (<0) on failure @@ -2558,7 +2557,7 @@ struct wpa_driver_ops { */ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *buf, size_t len); + int initiator, const u8 *buf, size_t len); /** * tdls_oper - Ask the driver to perform high-level TDLS operations @@ -2850,6 +2849,30 @@ struct wpa_driver_ops { */ int (*status)(void *priv, char *buf, size_t buflen); + /** + * roaming - Set roaming policy for driver-based BSS selection + * @priv: Private driver interface data + * @allowed: Whether roaming within ESS is allowed + * @bssid: Forced BSSID if roaming is disabled or %NULL if not set + * Returns: Length of written status information or -1 on failure + * + * This optional callback can be used to update roaming policy from the + * associate() command (bssid being set there indicates that the driver + * should not roam before getting this roaming() call to allow roaming. + * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION + * capability, roaming policy is handled within wpa_supplicant and there + * is no need to implement or react to this callback. + */ + int (*roaming)(void *priv, int allowed, const u8 *bssid); + + /** + * set_mac_addr - Set MAC address + * @priv: Private driver interface data + * @addr: MAC address to use or %NULL for setting back to permanent + * Returns: 0 on success, -1 on failure + */ + int (*set_mac_addr)(void *priv, const u8 *addr); + #ifdef CONFIG_MACSEC int (*macsec_init)(void *priv, struct macsec_init_params *params); @@ -4293,6 +4316,9 @@ void wpa_scan_results_free(struct wpa_scan_results *res); /* Convert wpa_event_type to a string for logging */ const char * event_to_string(enum wpa_event_type event); +/* Convert chan_width to a string for logging and control interfaces */ +const char * channel_width_to_string(enum chan_width width); + /* NULL terminated array of linked in driver wrappers */ extern struct wpa_driver_ops *wpa_drivers[]; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 413552ca..5c68b464 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -86,3 +86,24 @@ const char * event_to_string(enum wpa_event_type event) return "UNKNOWN"; #undef E2S } + + +const char * channel_width_to_string(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + return "20 MHz (no HT)"; + case CHAN_WIDTH_20: + return "20 MHz"; + case CHAN_WIDTH_40: + return "40 MHz"; + case CHAN_WIDTH_80: + return "80 MHz"; + case CHAN_WIDTH_80P80: + return "80+80 MHz"; + case CHAN_WIDTH_160: + return "160 MHz"; + default: + return "unknown"; + } +} diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 5f307765..6401087c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -19,6 +19,9 @@ #include <netlink/genl/genl.h> #include <netlink/genl/family.h> #include <netlink/genl/ctrl.h> +#ifdef CONFIG_LIBNL3_ROUTE +#include <netlink/route/neighbour.h> +#endif /* CONFIG_LIBNL3_ROUTE */ #include <linux/rtnetlink.h> #include <netpacket/packet.h> #include <linux/filter.h> @@ -253,6 +256,7 @@ struct wpa_driver_nl80211_data { struct dl_list list; struct dl_list wiphy_list; char phyname[32]; + u8 perm_addr[ETH_ALEN]; void *ctx; int ifindex; int if_removed; @@ -307,8 +311,11 @@ struct wpa_driver_nl80211_data { unsigned int start_iface_up:1; unsigned int test_use_roc_tx:1; unsigned int ignore_deauth_event:1; + unsigned int roaming_vendor_cmd_avail:1; unsigned int dfs_vendor_cmd_avail:1; unsigned int have_low_prio_scan:1; + unsigned int force_connect_cmd:1; + unsigned int addr_changed:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -324,6 +331,8 @@ struct wpa_driver_nl80211_data { int eapol_sock; /* socket for EAPOL frames */ + struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ + int default_if_indices[16]; int *if_indices; int num_if_indices; @@ -349,7 +358,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode); -static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq); +static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, + struct hostapd_freq_params *freq); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, @@ -420,6 +430,10 @@ static int wpa_driver_nl80211_authenticate_retry( static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); static int i802_set_iface_flags(struct i802_bss *bss, int up); +static struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, + int flags, uint8_t cmd); +static struct nl_msg * nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, + int ifindex, int flags, uint8_t cmd); static const char * nl80211_command_to_string(enum nl80211_commands cmd) { @@ -580,6 +594,20 @@ static int is_p2p_net_interface(enum nl80211_iftype nlmode) } +static struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex) +{ + struct i802_bss *bss; + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->ifindex == ifindex) + return bss; + } + + return NULL; +} + + static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) { if (drv->associated) @@ -1244,8 +1272,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, } extra[sizeof(extra) - 1] = '\0'; - wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_flags=0x%x (%s%s%s%s)", - ifi->ifi_index, ifname, extra, ifi->ifi_flags, + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, ifi->ifi_family, + ifi->ifi_flags, (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", @@ -1297,6 +1326,28 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, "event since interface %s is marked " "removed", drv->first_bss->ifname); } else { + struct i802_bss *bss; + u8 addr[ETH_ALEN]; + + /* Re-read MAC address as it may have changed */ + bss = get_bss_ifindex(drv, ifi->ifi_index); + if (bss && + linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: %s: failed to re-read MAC address", + bss->ifname); + } else if (bss && + os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Own MAC address on ifindex %d (%s) changed from " + MACSTR " to " MACSTR, + ifi->ifi_index, bss->ifname, + MAC2STR(bss->addr), + MAC2STR(addr)); + os_memcpy(bss->addr, addr, ETH_ALEN); + } + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, @@ -1341,6 +1392,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct rtattr *attr; u32 brid = 0; char ifname[IFNAMSIZ + 1]; + char extra[100], *pos, *end; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { @@ -1349,6 +1401,9 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, return; } + extra[0] = '\0'; + pos = extra; + end = pos + sizeof(extra); ifname[0] = '\0'; attrlen = len; @@ -1363,10 +1418,28 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, break; case IFLA_MASTER: brid = nla_get_u32((struct nlattr *) attr); + pos += os_snprintf(pos, end - pos, " master=%u", brid); + break; + case IFLA_OPERSTATE: + pos += os_snprintf(pos, end - pos, " operstate=%u", + nla_get_u32((struct nlattr *) attr)); + break; + case IFLA_LINKMODE: + pos += os_snprintf(pos, end - pos, " linkmode=%u", + nla_get_u32((struct nlattr *) attr)); break; } attr = RTA_NEXT(attr, attrlen); } + extra[sizeof(extra) - 1] = '\0'; + + wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, ifi->ifi_family, + ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid)) wpa_driver_nl80211_event_dellink(drv, ifname); @@ -1388,6 +1461,17 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, const struct ieee80211_mgmt *mgmt; union wpa_event_data event; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore auth event when using driver SME"); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.auth)) { @@ -1454,6 +1538,17 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, union wpa_event_data event; u16 status; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore assoc event when using driver SME"); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.assoc_resp)) { @@ -1656,10 +1751,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, return; ifidx = nla_get_u32(ifindex); - for (bss = drv->first_bss; bss; bss = bss->next) - if (bss->ifindex == ifidx) - break; - + bss = get_bss_ifindex(drv, ifidx); if (bss == NULL) { wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", ifidx); @@ -3993,12 +4085,16 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) continue; } vinfo = nla_data(nl); - if (vinfo->subcmd == - QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY) + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: drv->dfs_vendor_cmd_avail = 1; - if (vinfo->subcmd == - QCA_NL80211_VENDOR_SUBCMD_DO_ACS) + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + } wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", vinfo->vendor_id, vinfo->subcmd); @@ -4981,6 +5077,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr)) return -1; + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, @@ -5036,6 +5133,8 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", bss->ifname, bss->brname, strerror(errno)); + if (drv->rtnl_sk) + nl80211_handle_destroy(drv->rtnl_sk); } if (bss->added_bridge) { if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) @@ -5069,6 +5168,16 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) if (!drv->start_iface_up) (void) i802_set_iface_flags(bss, 0); + + if (drv->addr_changed) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + drv->perm_addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore permanent MAC address"); + } + } + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { if (!drv->hostapd || !drv->start_mode_ap) wpa_driver_nl80211_set_mode(bss, @@ -7845,6 +7954,43 @@ static int wpa_driver_nl80211_sta_add(void *priv, } +static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) +{ +#ifdef CONFIG_LIBNL3_ROUTE + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_addr; + int err; + + rn = rtnl_neigh_alloc(); + if (!rn) + return; + + rtnl_neigh_set_family(rn, AF_BRIDGE); + rtnl_neigh_set_ifindex(rn, bss->ifindex); + nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN); + if (!nl_addr) { + rtnl_neigh_put(rn); + return; + } + rtnl_neigh_set_lladdr(rn, nl_addr); + + err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0); + if (err < 0) { + wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for " + MACSTR " ifindex=%d failed: %s", MAC2STR(addr), + bss->ifindex, nl_geterror(err)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for " + MACSTR, MAC2STR(addr)); + } + + nl_addr_put(nl_addr); + rtnl_neigh_put(rn); +#endif /* CONFIG_LIBNL3_ROUTE */ +} + + static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, int deauth, u16 reason_code) { @@ -7874,6 +8020,10 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR " --> %d (%s)", bss->ifname, MAC2STR(addr), ret, strerror(-ret)); + + if (drv->rtnl_sk) + rtnl_neigh_delete_fdb_entry(bss, addr); + if (ret == -ENOENT) return 0; return ret; @@ -8648,6 +8798,11 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, struct nlattr *flags; struct nl80211_sta_flag_update upd; + wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR + " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d", + bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and, + !!(total_flags & WPA_STA_AUTHORIZED)); + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; @@ -8698,9 +8853,6 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { enum nl80211_iftype nlmode, old_mode; - struct hostapd_freq_params freq = { - .freq = params->freq, - }; if (params->p2p) { wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " @@ -8715,7 +8867,7 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, return -1; } - if (nl80211_set_channel(drv->first_bss, &freq, 0)) { + if (nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) { if (old_mode != nlmode) wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); nl80211_remove_monitor_interface(drv); @@ -8769,7 +8921,7 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, params->freq)) { + if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, ¶ms->freq)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; @@ -8793,8 +8945,16 @@ retry: os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); + wpa_printf(MSG_DEBUG, " * ht_enabled=%d", params->freq.ht_enabled); + wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d", + params->freq.sec_channel_offset); + wpa_printf(MSG_DEBUG, " * vht_enabled=%d", params->freq.vht_enabled); + wpa_printf(MSG_DEBUG, " * center_freq1=%d", params->freq.center_freq1); + wpa_printf(MSG_DEBUG, " * center_freq2=%d", params->freq.center_freq2); + wpa_printf(MSG_DEBUG, " * bandwidth=%d", params->freq.bandwidth); + if (nl80211_put_freq_params(msg, ¶ms->freq) < 0) + goto nla_put_failure; if (params->beacon_int > 0) { wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int); @@ -8954,10 +9114,10 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->bssid_hint); } - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - drv->assoc_freq = params->freq; + if (params->freq.freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq.freq); + drv->assoc_freq = params->freq.freq; } else drv->assoc_freq = 0; @@ -9460,13 +9620,11 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, } -static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq) +static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, + struct hostapd_freq_params *freq) { - struct hostapd_freq_params freq_params; - os_memset(&freq_params, 0, sizeof(freq_params)); - freq_params.freq = freq; return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC, - &freq_params); + freq); } @@ -10246,6 +10404,22 @@ static void *i802_init(struct hostapd_data *hapd, i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; +#ifdef CONFIG_LIBNL3_ROUTE + if (bss->added_if_into_bridge) { + drv->rtnl_sk = nl_socket_alloc(); + if (drv->rtnl_sk == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock"); + goto failed; + } + + if (nl_connect(drv->rtnl_sk, NETLINK_ROUTE)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s", + strerror(errno)); + goto failed; + } + } +#endif /* CONFIG_LIBNL3_ROUTE */ + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); if (drv->eapol_sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", @@ -10262,6 +10436,7 @@ static void *i802_init(struct hostapd_data *hapd, if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->own_addr)) goto failed; + os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN); memcpy(bss->addr, params->own_addr, ETH_ALEN); @@ -11221,6 +11396,7 @@ static int nl80211_set_param(void *priv, const char *param) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; + drv->force_connect_cmd = 1; } if (os_strstr(param, "no_offchannel_tx=1")) { @@ -11712,60 +11888,18 @@ static int nl80211_start_radar_detection(void *priv, nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); - if (freq->vht_enabled) { - switch (freq->bandwidth) { - case 20: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_20); - break; - case 40: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_40); - break; - case 80: - if (freq->center_freq2) - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_80P80); - else - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_80); - break; - case 160: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_160); - break; - default: - return -1; - } - NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); - if (freq->center_freq2) - NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, - freq->center_freq2); - } else if (freq->ht_enabled) { - switch (freq->sec_channel_offset) { - case -1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40MINUS); - break; - case 1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40PLUS); - break; - default: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT20); - break; - } - } + if (nl80211_put_freq_params(msg, freq) < 0) + goto nla_put_failure; ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " "%d (%s)", ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } @@ -11773,7 +11907,8 @@ nla_put_failure: static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, - u32 peer_capab, const u8 *buf, size_t len) + u32 peer_capab, int initiator, const u8 *buf, + size_t len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -11804,6 +11939,8 @@ static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, */ NLA_PUT_U32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, peer_capab); } + if (initiator) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_INITIATOR); NLA_PUT(msg, NL80211_ATTR_IE, len, buf); return send_and_recv_msgs(drv, msg, NULL, NULL); @@ -12216,6 +12353,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) res = os_snprintf(pos, end - pos, "phyname=%s\n" + "perm_addr=" MACSTR "\n" "drv_ifindex=%d\n" "operstate=%d\n" "scan_state=%s\n" @@ -12232,6 +12370,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "eapol_tx_sock=%d\n" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", drv->phyname, + MAC2STR(drv->perm_addr), drv->ifindex, drv->operstate, scan_state_str(drv->scan_state), @@ -12629,8 +12768,9 @@ static int nl80211_key_mgmt_set_pmk(void *priv, const u8 *pmk, wpa_hexdump(MSG_DEBUG, " * PMK", pmk, NL80211_KEY_LEN_PMK); if (!(drv->capa.key_mgmt_offload_support & - WPA_DRIVER_KEY_MGMT_OFFLOAD_SUPPORT_PMKSA)) + WPA_DRIVER_KEY_MGMT_OFFLOAD_SUPPORT_PMKSA)) { return 0; + } msg = nlmsg_alloc(); if (!msg) @@ -12720,6 +12860,107 @@ nla_put_failure: } +static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + + wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed); + + if (!drv->roaming_vendor_cmd_avail) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore roaming policy change since driver does not provide command for setting it"); + return -1; + } + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ROAMING) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY, + allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS : + QCA_ROAMING_NOT_ALLOWED) || + (bssid && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) { + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + return send_and_recv_msgs(drv, msg, NULL, NULL); +} + + +static int nl80211_set_mac_addr(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int new_addr = addr != NULL; + + if (!addr) + addr = drv->perm_addr; + + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0) + return -1; + + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: failed to set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, + 1) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after failed set_mac_addr"); + } + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + drv->addr_changed = new_addr; + os_memcpy(bss->addr, addr, ETH_ALEN); + + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after set_mac_addr"); + } + + return 0; +} + + +static struct nl_msg * +nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex, + int flags, uint8_t cmd) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (!nl80211_cmd(drv, msg, flags, cmd) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) { + nlmsg_free(msg); + return NULL; + } + + return msg; +} + + +static struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, + uint8_t cmd) +{ + return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd); +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -12813,4 +13054,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_wowlan = nl80211_set_wowlan, .key_mgmt_set_pmk = nl80211_key_mgmt_set_pmk, .do_acs = wpa_driver_do_acs, + .roaming = nl80211_roaming, + .set_mac_addr = nl80211_set_mac_addr, }; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 3608b522..66edfa73 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1478,7 +1478,7 @@ static int wpa_driver_test_associate( struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", - __func__, priv, params->freq, params->pairwise_suite, + __func__, priv, params->freq.freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 8d354244..50aa5208 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -2120,7 +2120,8 @@ int wpa_driver_wext_associate(void *priv, if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ - if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + if (params->freq.freq && + wpa_driver_wext_set_freq(drv, params->freq.freq) < 0) ret = -1; if (!drv->cfg80211 && wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 40aaba59..cdb913e3 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -36,6 +36,10 @@ ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +ifdef CONFIG_LIBNL3_ROUTE + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index db8561ae..9fa70d9c 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -31,6 +31,10 @@ ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +ifdef CONFIG_LIBNL3_ROUTE + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 735ca3aa..a39fbc3b 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -503,6 +503,9 @@ * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. + * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA + * counters which will be updated to the current value. This attribute + * is used during CSA period. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -719,6 +722,22 @@ * QoS mapping is relevant for IP packets, it is only valid during an * association. This is cleared on disassociation and AP restart. * + * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given + * %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO + * and %NL80211_ATTR_ADMITTED_TIME parameters. + * Note that the action frame handshake with the AP shall be handled by + * userspace via the normal management RX/TX framework, this only sets + * up the TX TS in the driver/device. + * If the admitted time attribute is not added then the request just checks + * if a subsequent setup could be successful, the intent is to use this to + * avoid setting up a session with the AP when local restrictions would + * make that impossible. However, the subsequent "real" setup may still + * fail even if the check was successful. + * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID + * and %NL80211_ATTR_MAC parameters. It isn't necessary to call this + * before removing a station entry entirely, or before disassociating + * or similar, cleanup will happen in the driver/device in this case. + * * @NL80211_CMD_AUTHORIZATION_EVENT: Indicates that the device offloaded * the establishment of temporal keys for an RSN connection. This is * used as part of key managment offload, where a device operating as a @@ -910,7 +929,10 @@ enum nl80211_commands { NL80211_CMD_SET_QOS_MAP, - NL80211_CMD_AUTHORIZATION_EVENT, + NL80211_CMD_ADD_TX_TS, + NL80211_CMD_DEL_TX_TS, + + NL80211_CMD_AUTHORIZATION_EVENT, NL80211_CMD_KEY_MGMT_SET_PMK, /* add new commands above here */ @@ -1548,10 +1570,10 @@ enum nl80211_commands { * operation). * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information * for the time while performing a channel switch. - * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter - * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). - * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter - * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel + * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel + * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP). * * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. @@ -1599,6 +1621,11 @@ enum nl80211_commands { * advertise values that cannot always be met. In such cases, an attempt * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. * + * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which + * should be updated when the frame is transmitted. + * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum + * supported number of csa counters. + * * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * @@ -1606,6 +1633,34 @@ enum nl80211_commands { * creation then the new interface will be owned by the netlink socket * that created it and will be destroyed when the socket is closed * + * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is + * the TDLS link initiator. + * + * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection + * shall support Radio Resource Measurements (11k). This attribute can be + * used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests. + * User space applications are expected to use this flag only if the + * underlying device supports these minimal RRM features: + * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES, + * %NL80211_FEATURE_QUIET, + * If this flag is used, driver must add the Power Capabilities IE to the + * association request. In addition, it must also set the RRM capability + * flag in the association request's Capability Info field. + * + * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout + * estimation algorithm (dynack). In order to activate dynack + * %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower + * drivers to indicate dynack capability. Dynack is automatically disabled + * setting valid value for coverage class. + * + * @NL80211_ATTR_TSID: a TSID value (u8 attribute) + * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute) + * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds + * (per second) (u16 attribute) + * + * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see + * &enum nl80211_smps_mode. + * * @NL80211_ATTR_AUTHORIZATION_STATUS: Status of key management offload. * @NL80211_ATTR_KEY_REPLAY_CTR: Key Replay Counter value last used in a * valid EAPOL-Key frame. @@ -1957,6 +2012,23 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_PEER_CAPABILITY, + NL80211_ATTR_IFACE_SOCKET_OWNER, + + NL80211_ATTR_CSA_C_OFFSETS_TX, + NL80211_ATTR_MAX_CSA_COUNTERS, + + NL80211_ATTR_TDLS_INITIATOR, + + NL80211_ATTR_USE_RRM, + + NL80211_ATTR_WIPHY_DYN_ACK, + + NL80211_ATTR_TSID, + NL80211_ATTR_USER_PRIO, + NL80211_ATTR_ADMITTED_TIME, + + NL80211_ATTR_SMPS_MODE, + NL80211_ATTR_AUTHORIZATION_STATUS, NL80211_ATTR_KEY_REPLAY_CTR, NL80211_ATTR_PSK, @@ -1968,9 +2040,7 @@ enum nl80211_attrs { NL80211_ATTR_PTK_KCK, NL80211_ATTR_PTK_KEK, - NL80211_ATTR_IFACE_SOCKET_OWNER, - - /* add attributes here, update the policy in nl80211.c */ + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 @@ -2238,6 +2308,8 @@ enum nl80211_sta_bss_param { * Contains a nested array of signal strength attributes (u8, dBm) * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the + * 802.11 header (u32, kbps) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2269,6 +2341,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_BYTES64, NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + NL80211_STA_INFO_EXPECTED_THROUGHPUT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -3086,14 +3159,20 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * (if @NL80211_BSS_PRESP_DATA is present then this is known to be + * from a probe response, otherwise it may be from the same beacon + * that the NL80211_BSS_BEACON_TSF will be from) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the * raw information elements from the probe response/beacon (bin); - * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are - * from a Probe Response frame; otherwise they are from a Beacon frame. + * if the %NL80211_BSS_BEACON_IES attribute is present and the data is + * different then the IEs here are from a Probe Response frame; otherwise + * they are from a Beacon frame. * However, if the driver does not indicate the source of the IEs, these * IEs may be from either frame subtype. + * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the + * data here is known to be from a probe response, without any heuristics. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon @@ -3105,6 +3184,10 @@ enum nl80211_bss_scan_width { * yet been received * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel * (u32, enum nl80211_bss_scan_width) + * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64) + * (not present if no beacon frame has been received yet) + * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and + * @NL80211_BSS_TSF is known to be from a probe response (flag attribute) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3122,6 +3205,8 @@ enum nl80211_bss { NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, NL80211_BSS_CHAN_WIDTH, + NL80211_BSS_BEACON_TSF, + NL80211_BSS_PRESP_DATA, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -3738,6 +3823,8 @@ enum nl80211_iface_limit_attrs { * different channels may be used within this group. * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap * of supported channel widths for radar detection. + * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap + * of supported regulatory regions for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -3771,6 +3858,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -3944,6 +4032,8 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only + * here to reserve the value for API/ABI compatibility) * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * mode @@ -3982,13 +4072,33 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the * lifetime of a BSS. + * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter + * Set IE to probe requests. + * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE + * to probe requests. + * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period + * requests sent to it by an AP. + * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the + * current tx power value into the TPC Report IE in the spectrum + * management TPC Report action frame, and in the Radio Measurement Link + * Measurement Report action frame. + * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout + * estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used + * to enable dynack. + * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial + * multiplexing powersave, ie. can turn off all but one chain + * even on HT connections that should be using more chains. + * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial + * multiplexing powersave, ie. can turn off all but one chain + * and then wake the rest up as required after, for example, + * rts/cts handshake. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, - /* bit 4 is reserved - don't use */ + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, NL80211_FEATURE_SAE = 1 << 5, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, @@ -4003,6 +4113,13 @@ enum nl80211_feature_flags { NL80211_FEATURE_USERSPACE_MPM = 1 << 16, NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18, + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19, + NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20, + NL80211_FEATURE_QUIET = 1 << 21, + NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22, + NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23, + NL80211_FEATURE_STATIC_SMPS = 1 << 24, + NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25, }; /** @@ -4077,6 +4194,25 @@ enum nl80211_acl_policy { }; /** + * enum nl80211_smps_mode - SMPS mode + * + * Requested SMPS mode (for AP mode) + * + * @NL80211_SMPS_OFF: SMPS off (use all antennas). + * @NL80211_SMPS_STATIC: static SMPS (use a single antenna) + * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and + * turn on other antennas after CTS/RTS). + */ +enum nl80211_smps_mode { + NL80211_SMPS_OFF, + NL80211_SMPS_STATIC, + NL80211_SMPS_DYNAMIC, + + __NL80211_SMPS_AFTER_LAST, + NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1 +}; + +/** * enum nl80211_radar_event - type of radar event for DFS operation * * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index fdcff7fa..631c363f 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -106,9 +106,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, case 21: nid = NID_secp521r1; break; +#ifndef OPENSSL_IS_BORINGSSL case 25: nid = NID_X9_62_prime192v1; break; +#endif /* OPENSSL_IS_BORINGSSL */ case 26: nid = NID_secp224r1; break; diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 4691e722..2692bced 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -161,8 +161,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, return 0; } - if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8, - pac_opaque, buf) < 0) { + if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt " "PAC-Opaque"); os_free(buf); @@ -731,8 +731,8 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, os_free(pac_buf); return NULL; } - if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf, - pac_opaque) < 0) { + if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + pac_len / 8, pac_buf, pac_opaque) < 0) { os_free(pac_buf); os_free(pac_opaque); return NULL; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index c87848c4..d9d4375a 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -287,7 +287,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, struct eap_pax_hdr *resp; u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; const u8 *pos; - size_t len, left; + size_t len, left, cid_len; int i; if (data->state != PAX_STD_1) @@ -320,7 +320,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); return; } - data->cid_len = WPA_GET_BE16(pos); + cid_len = WPA_GET_BE16(pos); + if (cid_len > 1500) { + wpa_printf(MSG_INFO, "EAP-PAX: Too long CID"); + return; + } + data->cid_len = cid_len; os_free(data->cid); data->cid = os_malloc(data->cid_len); if (data->cid == NULL) { diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index cf3506d9..70258be2 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -255,12 +255,14 @@ SM_STATE(SUPP_PAE, CONNECTING) * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ -#ifdef CONFIG_WPS - /* Reduce latency on starting WPS negotiation. */ - sm->startWhen = 1; -#else /* CONFIG_WPS */ - sm->startWhen = 3; -#endif /* CONFIG_WPS */ + if (sm->conf.wps) { + /* Reduce latency on starting WPS negotiation. */ + wpa_printf(MSG_DEBUG, + "EAPOL: Using shorter startWhen for WPS"); + sm->startWhen = 1; + } else { + sm->startWhen = 2; + } } eapol_enable_timer_tick(sm); sm->eapolEap = FALSE; @@ -1242,7 +1244,7 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, return 0; } #ifdef CONFIG_WPS - if (sm->conf.workaround && + if (sm->conf.wps && sm->conf.workaround && plen < len - sizeof(*hdr) && hdr->type == IEEE802_1X_TYPE_EAP_PACKET && len - sizeof(*hdr) > sizeof(struct eap_hdr)) { @@ -1491,6 +1493,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm, sm->conf.required_keys = conf->required_keys; sm->conf.fast_reauth = conf->fast_reauth; sm->conf.workaround = conf->workaround; + sm->conf.wps = conf->wps; #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { /* Using EAP Proxy, so skip EAP state machine update */ diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index d76c8c21..5b37314f 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -58,6 +58,11 @@ struct eapol_config { * external_sim - Use external processing for SIM/USIM operations */ int external_sim; + + /** + * wps - Whether this connection is used for WPS + */ + int wps; }; struct eapol_sm; diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 43264caa..4305bbeb 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -267,7 +267,8 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) return; } - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) @@ -1837,8 +1838,17 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->go_neg_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->go_neg_peer->status = P2P_SC_SUCCESS; + /* + * Set new timeout to make sure a previously set one does not expire + * too quickly while waiting for the GO Negotiation to complete. + */ + p2p_set_timeout(p2p, 0, 500000); p2p_connect_send(p2p, p2p->go_neg_peer); } @@ -1848,6 +1858,10 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->invite_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); @@ -2493,7 +2507,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->max_disc_int = 3; p2p->max_disc_tu = -1; - os_get_random(&p2p->next_tie_breaker, 1); + if (os_get_random(&p2p->next_tie_breaker, 1) < 0) + p2p->next_tie_breaker = 0; p2p->next_tie_breaker &= 0x01; if (cfg->sd_request) p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; @@ -3070,7 +3085,8 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) * make it less likely to hit cases where we could end up in * sync with peer not listening. */ - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; timeout += r % 100000; } p2p_set_timeout(p2p, 0, timeout); diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index df540ef5..23acce76 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -450,7 +450,8 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels) { unsigned int r; - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; r %= num_channels; return channels[r]; } diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index 0f961282..c8fa2651 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -1451,7 +1451,7 @@ ieee802_1x_mka_encode_dist_sak_body( os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN); sak_pos = CS_ID_LEN; } - if (aes_wrap(participant->kek.key, + if (aes_wrap(participant->kek.key, 16, cipher_suite_tbl[cs_index].sak_len / 8, sak->key, body->sak + sak_pos)) { wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); @@ -1611,7 +1611,7 @@ ieee802_1x_mka_decode_dist_sak_body( wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); return -1; } - if (aes_unwrap(participant->kek.key, sak_len >> 3, wrap_sak, + if (aes_unwrap(participant->kek.key, 16, sak_len >> 3, wrap_sak, unwrap_sak)) { wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); os_free(unwrap_sak); diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 10056a64..e2766e2f 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -1080,19 +1080,23 @@ radius_change_server(struct radius_client_data *radius, switch (nserv->addr.af) { case AF_INET: claddrlen = sizeof(claddr); - getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); - wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", - inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); + if (getsockname(sel_sock, (struct sockaddr *) &claddr, + &claddrlen) == 0) { + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntoa(claddr.sin_addr), + ntohs(claddr.sin_port)); + } break; #ifdef CONFIG_IPV6 case AF_INET6: { claddrlen = sizeof(claddr6); - getsockname(sel_sock, (struct sockaddr *) &claddr6, - &claddrlen); - wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", - inet_ntop(AF_INET6, &claddr6.sin6_addr, - abuf, sizeof(abuf)), - ntohs(claddr6.sin6_port)); + if (getsockname(sel_sock, (struct sockaddr *) &claddr6, + &claddrlen) == 0) { + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntop(AF_INET6, &claddr6.sin6_addr, + abuf, sizeof(abuf)), + ntohs(claddr6.sin6_port)); + } break; } #endif /* CONFIG_IPV6 */ diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 24348a39..00394b49 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -1926,7 +1926,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, if (inet_ntop(AF_INET6, &cli->addr6, abuf, sizeof(abuf)) == NULL) abuf[0] = '\0'; - if (inet_ntop(AF_INET6, &cli->mask6, abuf, + if (inet_ntop(AF_INET6, &cli->mask6, mbuf, sizeof(mbuf)) == NULL) mbuf[0] = '\0'; } @@ -2048,8 +2048,6 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) sess = s; break; } - if (sess) - break; } if (sess) break; diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index b5a87fc5..885291a2 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -35,7 +35,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { - os_free(entry); + bin_clear_free(entry, sizeof(*entry)); } diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 93ae1432..c4e5fedc 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -218,26 +218,29 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *buf, size_t len) + int initiator, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, - status_code, peer_capab, buf, len); + status_code, peer_capab, initiator, buf, + len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *msg, size_t msg_len) + int initiator, const u8 *msg, size_t msg_len) { struct wpa_tdls_peer *peer; wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " - "dialog_token=%u status_code=%u peer_capab=%u msg_len=%u", + "dialog_token=%u status_code=%u peer_capab=%u initiator=%d " + "msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, - peer_capab, (unsigned int) msg_len); + peer_capab, initiator, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, - status_code, peer_capab, msg, msg_len)) { + status_code, peer_capab, initiator, msg, + msg_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to send message " "(action_code=%u)", action_code); return -1; @@ -333,6 +336,7 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) peer->sm_tmr.dialog_token, peer->sm_tmr.status_code, peer->sm_tmr.peer_capab, + peer->initiator, peer->sm_tmr.buf, peer->sm_tmr.buf_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to retry " @@ -793,7 +797,7 @@ skip_ies: /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, - reason_code, 0, rbuf, pos - rbuf); + reason_code, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return 0; @@ -968,17 +972,19 @@ skip_ftie: * appropriate status code mentioning reason for error/failure. * @dst - MAC addr of Peer station * @tdls_action - TDLS frame type for which error code is sent + * @initiator - was this end the initiator of the connection * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, - u8 tdls_action, u8 dialog_token, u16 status) + u8 tdls_action, u8 dialog_token, int initiator, + u16 status) { wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR " (action=%u status=%u)", MAC2STR(dst), tdls_action, status); return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, - 0, NULL, 0); + 0, initiator, NULL, 0); } @@ -1184,7 +1190,7 @@ skip_ies: MAC2STR(peer->addr)); status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, - 1, 0, 0, rbuf, pos - rbuf); + 1, 0, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); return status; @@ -1274,7 +1280,8 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, skip_ies: status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, - dtoken, 0, 0, rbuf, pos - rbuf); + dtoken, 0, 0, peer->initiator, rbuf, + pos - rbuf); os_free(rbuf); return status; @@ -1371,7 +1378,8 @@ skip_ies: peer_capab |= TDLS_PEER_WMM; status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, - dtoken, 0, peer_capab, rbuf, pos - rbuf); + dtoken, 0, peer_capab, peer->initiator, + rbuf, pos - rbuf); os_free(rbuf); return status; @@ -1430,8 +1438,8 @@ static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, hdr->len = (pos - (u8 *) hdr) - 2; peer->rsnie_i_len = pos - peer->rsnie_i; - wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response", - (u8 *) hdr, hdr->len + 2); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response", + (u8 *) hdr, hdr->len + 2); skip_rsn_ies: buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1457,7 +1465,7 @@ skip_rsn_ies: wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); skip_ies: status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, - dialog_token, 0, 0, rbuf, pos - rbuf); + dialog_token, 0, 0, 0, rbuf, pos - rbuf); os_free(rbuf); return status; @@ -1528,7 +1536,7 @@ int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " MACSTR, MAC2STR(addr)); return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, - 1, 0, 0, NULL, 0); + 1, 0, 0, 1, NULL, 0); } @@ -1713,8 +1721,8 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, peer->supp_rates, peer->supp_rates_len, peer->ht_capabilities, peer->vht_capabilities, - peer->qos_info, peer->ext_capab, - peer->ext_capab_len, + peer->qos_info, peer->wmm_capable, + peer->ext_capab, peer->ext_capab_len, peer->supp_channels, peer->supp_channels_len, peer->supp_oper_classes, @@ -1858,6 +1866,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0); wpa_tdls_send_tpk_m1(sm, peer); } @@ -2036,10 +2046,18 @@ skip_rsn: wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) + goto skip_add_peer; +#endif /* CONFIG_TDLS_TESTING */ + /* add supported rates, capabilities, and qos_info to the TDLS peer */ if (wpa_tdls_addset_peer(sm, peer, 1) < 0) goto error; +#ifdef CONFIG_TDLS_TESTING +skip_add_peer: +#endif /* CONFIG_TDLS_TESTING */ peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); @@ -2051,7 +2069,7 @@ skip_rsn_check: return 0; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, status); if (peer) wpa_tdls_peer_free(sm, peer); @@ -2362,7 +2380,7 @@ skip_rsn: return ret; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); wpa_tdls_disable_peer_link(sm, peer); return -1; @@ -2587,7 +2605,7 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) /* add the peer to the driver as a "setup in progress" peer */ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, - NULL, 0, NULL, 0, NULL, 0, NULL, 0)) { + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { wpa_tdls_disable_peer_link(sm, peer); return -1; } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 56718b1b..7c89473e 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1311,7 +1311,8 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, (unsigned long) maxkeylen); return -1; } - if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, key_data, gd->gtk)) { + if (aes_unwrap(sm->ptk.kek, 16, maxkeylen / 8, key_data, + gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - could not decrypt " "GTK"); @@ -1519,7 +1520,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, "WPA: No memory for AES-UNWRAP buffer"); return -1; } - if (aes_unwrap(sm->ptk.kek, *key_data_len / 8, + if (aes_unwrap(sm->ptk.kek, 16, *key_data_len / 8, key_data, buf)) { os_free(buf); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 413b46e6..0b5b97de 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -55,14 +55,14 @@ struct wpa_sm_ctx { int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *buf, size_t len); + int initiator, const u8 *buf, size_t len); int (*tdls_oper)(void *ctx, int oper, const u8 *peer); int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u8 qosinfo, const u8 *ext_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 4a75b926..3b3c9d0d 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -566,7 +566,7 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } gtk_len = gtk_elem_len - 19; - if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { + if (aes_unwrap(sm->ptk.kek, 16, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; @@ -645,7 +645,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, return -1; } - if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { + if (aes_unwrap(sm->ptk.kek, 16, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) + { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt IGTK"); return -1; diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 03892af3..aeabc244 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -268,12 +268,14 @@ static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *buf, size_t len) + int initiator, const u8 *buf, + size_t len) { if (sm->ctx->send_tdls_mgmt) return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, dialog_token, status_code, - peer_capab, buf, len); + peer_capab, initiator, buf, + len); return -1; } @@ -291,16 +293,16 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, - const u8 *supp_channels, size_t supp_channels_len, - const u8 *supp_oper_classes, + u8 qosinfo, int wmm, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len) { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, aid, capability, supp_rates, supp_rates_len, ht_capab, - vht_capab, qosinfo, + vht_capab, qosinfo, wmm, ext_capab, ext_capab_len, supp_channels, supp_channels_len, diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c index a0663925..d5ff5b5c 100644 --- a/src/utils/browser-android.c +++ b/src/utils/browser-android.c @@ -75,7 +75,7 @@ int hs20_web_browser(const char *url) os_memset(&data, 0, sizeof(data)); ret = os_snprintf(cmd, sizeof(cmd), - "am start -a android.intent.action.VIEW -d '%s' " + "start -a android.intent.action.VIEW -d %s " "-n com.android.browser/.BrowserActivity", url); if (ret < 0 || (size_t) ret >= sizeof(cmd)) { wpa_printf(MSG_ERROR, "Too long URL"); @@ -94,7 +94,7 @@ int hs20_web_browser(const char *url) return -1; } - if (system(cmd) != 0) { + if (os_exec("/system/bin/am", cmd, 1) != 0) { wpa_printf(MSG_INFO, "Failed to launch Android browser"); eloop_cancel_timeout(browser_timeout, NULL, NULL); http_server_deinit(http); @@ -109,7 +109,7 @@ int hs20_web_browser(const char *url) eloop_destroy(); wpa_printf(MSG_INFO, "Closing Android browser"); - if (system("input keyevent 3") != 0) { + if (os_exec("/system/bin/input", "keyevent 3", 1) != 0) { wpa_printf(MSG_INFO, "Failed to inject keyevent"); } diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c index 2884d341..a080e2cb 100644 --- a/src/utils/browser-system.c +++ b/src/utils/browser-system.c @@ -92,7 +92,7 @@ int hs20_web_browser(const char *url) return -1; } - if (system(cmd) != 0) { + if (os_exec("/usr/bin/x-www-browser", url, 0) != 0) { wpa_printf(MSG_INFO, "Failed to launch browser"); eloop_cancel_timeout(browser_timeout, NULL, NULL); http_server_deinit(http); diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c index eeb8f650..ce3054bb 100644 --- a/src/utils/browser-wpadebug.c +++ b/src/utils/browser-wpadebug.c @@ -76,7 +76,7 @@ int hs20_web_browser(const char *url) os_memset(&data, 0, sizeof(data)); ret = os_snprintf(cmd, sizeof(cmd), - "am start -a android.action.MAIN " + "start -a android.action.MAIN " "-c android.intent.category.LAUNCHER " "-n w1.fi.wpadebug/.WpaWebViewActivity " "-e w1.fi.wpadebug.URL '%s'", url); @@ -97,7 +97,7 @@ int hs20_web_browser(const char *url) return -1; } - if (system(cmd) != 0) { + if (os_exec("/system/bin/am", cmd, 1) != 0) { wpa_printf(MSG_INFO, "Failed to launch wpadebug browser"); eloop_cancel_timeout(browser_timeout, NULL, NULL); http_server_deinit(http); @@ -112,10 +112,11 @@ int hs20_web_browser(const char *url) eloop_destroy(); wpa_printf(MSG_INFO, "Closing Android browser"); - if (system("am start -a android.action.MAIN " - "-c android.intent.category.LAUNCHER " - "-n w1.fi.wpadebug/.WpaWebViewActivity " - "-e w1.fi.wpadebug.URL FINISH") != 0) { + if (os_exec("/system/bin/am", + "start -a android.action.MAIN " + "-c android.intent.category.LAUNCHER " + "-n w1.fi.wpadebug/.WpaWebViewActivity " + "-e w1.fi.wpadebug.URL FINISH", 1) != 0) { wpa_printf(MSG_INFO, "Failed to close wpadebug browser"); } diff --git a/src/utils/common.c b/src/utils/common.c index 5b017e57..99020049 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -362,7 +362,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) *txt++ = '\\'; *txt++ = '\\'; break; - case '\e': + case '\033': *txt++ = '\\'; *txt++ = 'e'; break; @@ -427,7 +427,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) pos++; break; case 'e': - buf[len++] = '\e'; + buf[len++] = '\033'; pos++; break; case 'x': @@ -846,3 +846,23 @@ void bin_clear_free(void *bin, size_t len) os_free(bin); } } + + +int random_mac_addr(u8 *addr) +{ + if (os_get_random(addr, ETH_ALEN) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} + + +int random_mac_addr_keep_oui(u8 *addr) +{ + if (os_get_random(addr + 3, 3) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} diff --git a/src/utils/common.h b/src/utils/common.h index 2bc8fe14..14d9ad1e 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -538,6 +538,9 @@ void int_array_add_unique(int **res, int a); void str_clear_free(char *str); void bin_clear_free(void *bin, size_t len); +int random_mac_addr(u8 *addr); +int random_mac_addr_keep_oui(u8 *addr); + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index 8692f9f8..3a042f93 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -1177,9 +1177,10 @@ static int ocsp_resp_cb(SSL *s, void *arg) if (status == V_OCSP_CERTSTATUS_GOOD) return 1; - if (status == V_OCSP_CERTSTATUS_REVOKED) + if (status == V_OCSP_CERTSTATUS_REVOKED) { ctx->last_err = "Server certificate has been revoked"; return 0; + } if (ctx->ocsp == MANDATORY_OCSP) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); ctx->last_err = "OCSP status unknown"; diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index f7d41b4d..9f5a90ce 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -175,6 +175,12 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) } wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + if (wps->wps->ap && random_pool_ready() != 1) { + wpa_printf(MSG_INFO, + "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used"); + return NULL; + } + msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; @@ -268,8 +274,12 @@ static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg) char hex[65]; u8 psk[32]; /* Generate a random per-device PSK */ - if (random_get_bytes(psk, sizeof(psk)) < 0) + if (random_pool_ready() != 1 || + random_get_bytes(psk, sizeof(psk)) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); return -1; + } wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK", psk, sizeof(psk)); wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)", diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 00c8299a..b90cc25e 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1640,8 +1640,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) !wps->wps->registrar->disable_auto_conf) { u8 r[16]; /* Generate a random passphrase */ - if (random_get_bytes(r, sizeof(r)) < 0) + if (random_pool_ready() != 1 || + random_get_bytes(r, sizeof(r)) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); return -1; + } os_free(wps->new_psk); wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len); if (wps->new_psk == NULL) @@ -1674,7 +1678,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk = os_malloc(wps->new_psk_len); if (wps->new_psk == NULL) return -1; - if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { + if (random_pool_ready() != 1 || + random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); os_free(wps->new_psk); wps->new_psk = NULL; return -1; diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index 5558a5ed..f09e7a00 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,70 @@ ChangeLog for wpa_supplicant +2014-10-09 - v2.3 + * fixed number of minor issues identified in static analyzer warnings + * fixed wfd_dev_info to be more careful and not read beyond the buffer + when parsing invalid information for P2P-DEVICE-FOUND + * extended P2P and GAS query operations to support drivers that have + maximum remain-on-channel time below 1000 ms (500 ms is the current + minimum supported value) + * added p2p_search_delay parameter to make the default p2p_find delay + configurable + * improved P2P operating channel selection for various multi-channel + concurrency cases + * fixed some TDLS failure cases to clean up driver state + * fixed dynamic interface addition cases with nl80211 to avoid adding + ifindex values to incorrect interface to skip foreign interface events + properly + * added TDLS workaround for some APs that may add extra data to the + end of a short frame + * fixed EAP-AKA' message parser with multiple AT_KDF attributes + * added configuration option (p2p_passphrase_len) to allow longer + passphrases to be generated for P2P groups + * fixed IBSS channel configuration in some corner cases + * improved HT/VHT/QoS parameter setup for TDLS + * modified D-Bus interface for P2P peers/groups + * started to use constant time comparison for various password and hash + values to reduce possibility of any externally measurable timing + differences + * extended explicit clearing of freed memory and expired keys to avoid + keeping private data in memory longer than necessary + * added optional scan_id parameter to the SCAN command to allow manual + scan requests for active scans for specific configured SSIDs + * fixed CTRL-EVENT-REGDOM-CHANGE event init parameter value + * added option to set Hotspot 2.0 Rel 2 update_identifier in network + configuration to support external configuration + * modified Android PNO functionality to send Probe Request frames only + for hidden SSIDs (based on scan_ssid=1) + * added generic mechanism for adding vendor elements into frames at + runtime (VENDOR_ELEM_ADD, VENDOR_ELEM_GET, VENDOR_ELEM_REMOVE) + * added fields to show unrecognized vendor elements in P2P_PEER + * removed EAP-TTLS/MSCHAPv2 interoperability workaround so that + MS-CHAP2-Success is required to be present regardless of + eap_workaround configuration + * modified EAP fast session resumption to allow results to be used only + with the same network block that generated them + * extended freq_list configuration to apply for sched_scan as well as + normal scan + * modified WPS to merge mixed-WPA/WPA2 credentials from a single session + * fixed nl80211/RTM_DELLINK processing when a P2P GO interface is + removed from a bridge + * fixed number of small P2P issues to make negotiations more robust in + corner cases + * added experimental support for using temporary, random local MAC + address (mac_addr and preassoc_mac_addr parameters); this is disabled + by default (i.e., previous behavior of using permanent address is + maintained if configuration is not changed) + * added D-Bus interface for setting/clearing WFD IEs + * fixed TDLS AID configuration for VHT + * modified -m<conf> configuration file to be used only for the P2P + non-netdev management device and do not load this for the default + station interface or load the station interface configuration for + the P2P management interface + * fixed external MAC address changes while wpa_supplicant is running + * started to enable HT (if supported by the driver) for IBSS + * fixed wpa_cli action script execution to use more robust mechanism + (CVE-2014-3686) + 2014-06-04 - v2.2 * added DFS indicator to get_capability freq * added/fixed nl80211 functionality diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 115b34db..2907a2fa 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -514,7 +514,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, } if (ssid->frequency == 0) ssid->frequency = 2462; /* default channel 11 */ - params.freq = ssid->frequency; + params.freq.freq = ssid->frequency; params.wpa_proto = ssid->proto; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 73d3ed65..75234326 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -516,6 +516,8 @@ static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, if (changes & WPA_BSS_RATES_CHANGED_FLAG) wpas_notify_bss_rates_changed(wpa_s, bss->id); + + wpas_notify_bss_seen(wpa_s, bss->id); } diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index e2685489..93c7e5e1 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, 2) }, }; #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; config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; if (ctrl_interface) @@ -3910,6 +3913,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 }, { INT(key_mgmt_offload), 0}, }; diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 27de0a82..41b16d1d 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 #define DEFAULT_KEY_MGMT_OFFLOAD 1 #include "config_ssid.h" @@ -1063,6 +1064,33 @@ struct wpa_config { * rekeying operation. */ int key_mgmt_offload; + + /** + * 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. 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 + * 2 = like 1, but maintain OUI (with local admin bit set) + */ + int preassoc_mac_addr; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 56e7c006..459907a7 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -20,6 +20,9 @@ #include "eap_peer/eap_methods.h" #include "eap_peer/eap.h" +#ifdef ANDROID +#include <sys/stat.h> +#endif static int newline_terminated(const char *buf, size_t buflen) { @@ -742,6 +745,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 @@ -1183,6 +1187,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->key_mgmt_offload) fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload); + + 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 */ @@ -1198,12 +1212,21 @@ int wpa_config_write(const char *name, struct wpa_config *config) struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ int ret = 0; + int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */ + char *tmp_name = os_malloc(tmp_len); + + if (tmp_name == NULL) + tmp_name = (char *)name; + else + os_snprintf(tmp_name, tmp_len, "%s.tmp", name); - wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", tmp_name); - f = fopen(name, "w"); + f = fopen(tmp_name, "w"); if (f == NULL) { - wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); + wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", tmp_name); + if (tmp_name != name) + os_free(tmp_name); return -1; } @@ -1238,6 +1261,17 @@ int wpa_config_write(const char *name, struct wpa_config *config) fclose(f); + if (tmp_name != name) { + int chmod_ret = 0; +#ifdef ANDROID + chmod_ret = chmod(tmp_name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif + if (chmod_ret != 0 || rename(tmp_name, name) != 0) + ret = -1; + + os_free(tmp_name); + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", name, ret ? "un" : ""); return ret; diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 26b91bd1..f50b2d41 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -653,6 +653,19 @@ 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 + * 2 = like 1, but maintain OUI (with local admin bit set) + * + * 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 594c4218..c0190ca0 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -44,6 +44,7 @@ #include "autoscan.h" #include "wnm_sta.h" #include "offchannel.h" +#include "drivers/driver.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -1542,6 +1543,11 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; + ret = os_snprintf(pos, end - pos, "freq=%u\n", + wpa_s->assoc_freq); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; if (ssid) { u8 *_ssid = ssid->ssid; size_t ssid_len = ssid->ssid_len; @@ -1979,9 +1985,9 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, static int wpa_supplicant_ctrl_iface_list_networks( - struct wpa_supplicant *wpa_s, char *buf, size_t buflen) + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { - char *pos, *end; + char *pos, *end, *prev; struct wpa_ssid *ssid; int ret; @@ -1994,12 +2000,24 @@ static int wpa_supplicant_ctrl_iface_list_networks( pos += ret; ssid = wpa_s->conf->ssid; + + /* skip over ssids until we find next one */ + if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) { + int last_id = atoi(cmd + 8); + if (last_id != -1) { + while (ssid != NULL && ssid->id <= last_id) { + ssid = ssid->next; + } + } + } + while (ssid) { + prev = pos; ret = os_snprintf(pos, end - pos, "%d\t%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); if (ret < 0 || ret >= end - pos) - return pos - buf; + return prev - buf; pos += ret; if (ssid->bssid_set) { ret = os_snprintf(pos, end - pos, "\t" MACSTR, @@ -2008,7 +2026,7 @@ static int wpa_supplicant_ctrl_iface_list_networks( ret = os_snprintf(pos, end - pos, "\tany"); } if (ret < 0 || ret >= end - pos) - return pos - buf; + return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? @@ -2019,11 +2037,11 @@ static int wpa_supplicant_ctrl_iface_list_networks( ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (ret < 0 || ret >= end - pos) - return pos - buf; + return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) - return pos - buf; + return prev - buf; pos += ret; ssid = ssid->next; @@ -2482,6 +2500,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); } @@ -2500,6 +2520,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; @@ -2574,9 +2597,10 @@ static int wpa_supplicant_ctrl_iface_update_network( static int wpa_supplicant_ctrl_iface_set_network( struct wpa_supplicant *wpa_s, char *cmd) { - int id; + int id, ret, prev_bssid_set; struct wpa_ssid *ssid; char *name, *value; + u8 prev_bssid[ETH_ALEN]; /* cmd: "<network id> <variable name> <value>" */ name = os_strchr(cmd, ' '); @@ -2602,8 +2626,15 @@ static int wpa_supplicant_ctrl_iface_set_network( return -1; } - return wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name, - value); + prev_bssid_set = ssid->bssid_set; + os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN); + ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name, + value); + if (ret == 0 && + (ssid->bssid_set != prev_bssid_set || + os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0)) + wpas_notify_network_bssid_set_changed(wpa_s, ssid); + return ret; } @@ -5557,28 +5588,6 @@ static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd #endif /* CONFIG_WNM */ -/* Get string representation of channel width */ -static const char * channel_width_name(enum chan_width width) -{ - switch (width) { - case CHAN_WIDTH_20_NOHT: - return "20 MHz (no HT)"; - case CHAN_WIDTH_20: - return "20 MHz"; - case CHAN_WIDTH_40: - return "40 MHz"; - case CHAN_WIDTH_80: - return "80 MHz"; - case CHAN_WIDTH_80P80: - return "80+80 MHz"; - case CHAN_WIDTH_160: - return "160 MHz"; - default: - return "unknown"; - } -} - - static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -5603,7 +5612,7 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", - channel_width_name(si.chanwidth)); + channel_width_to_string(si.chanwidth)); if (ret < 0 || ret > end - pos) return -1; pos += ret; @@ -6459,7 +6468,7 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->vendor_elem[frame] = NULL; } else { os_memmove(ie, ie + len, - wpabuf_len(wpa_s->vendor_elem[frame]) - len); + end - (ie + len)); wpa_s->vendor_elem[frame]->used -= len; } os_free(buf); @@ -6858,9 +6867,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_log_level( wpa_s, buf + 9, reply, reply_size); + } else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_networks( + wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( - wpa_s, reply, reply_size); + wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index e9f65890..5e58c5ba 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -1960,6 +1960,9 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_BSS_PROP_IES: prop = "IEs"; break; + case WPAS_DBUS_BSS_PROP_AGE: + prop = "Age"; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -2094,6 +2097,12 @@ static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { wpas_dbus_getter_global_capabilities, NULL }, +#ifdef CONFIG_WIFI_DISPLAY + { "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay", + wpas_dbus_getter_global_wfd_ies, + wpas_dbus_setter_global_wfd_ies + }, +#endif /* CONFIG_WIFI_DISPLAY */ { NULL, NULL, NULL, NULL, NULL } }; @@ -2384,6 +2393,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { wpas_dbus_getter_bss_ies, NULL }, + { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u", + wpas_dbus_getter_bss_age, + NULL + }, { NULL, NULL, NULL, NULL, NULL } }; @@ -2518,6 +2531,13 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_signal_poll, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, { @@ -2941,6 +2961,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { wpas_dbus_getter_process_credentials, wpas_dbus_setter_process_credentials }, + { "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_config_methods, + wpas_dbus_setter_config_methods + }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 881d351d..5f32bbf6 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -41,6 +41,7 @@ enum wpas_dbus_bss_prop { WPAS_DBUS_BSS_PROP_RSN, WPAS_DBUS_BSS_PROP_WPS, WPAS_DBUS_BSS_PROP_IES, + WPAS_DBUS_BSS_PROP_AGE, }; #define WPAS_DBUS_OBJECT_PATH_MAX 150 diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 6dc5f76d..9f6c4a39 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -27,6 +27,7 @@ #include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" #include "dbus_common_i.h" +#include "drivers/driver.h" static const char *debug_strings[] = { "excessive", "msgdump", "debug", "info", "warning", "error", NULL @@ -1401,6 +1402,88 @@ out: } +/** + * wpas_dbus_handler_signal_poll - Request immediate signal properties + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "SignalPoll" method call of a network device. Requests + * that wpa_supplicant read signal properties like RSSI, noise, and link + * speed and return them. + */ +DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + struct wpa_signal_info si; + DBusMessage *reply = NULL; + DBusMessageIter iter, iter_dict, variant_iter; + int ret; + + ret = wpa_drv_signal_poll(wpa_s, &si); + if (ret) { + return dbus_message_new_error(message, DBUS_ERROR_FAILED, + "Failed to read signal"); + } + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + goto nomem; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) + goto nomem; + if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + goto nomem; + + if (!wpa_dbus_dict_append_int32(&iter_dict, "rssi", si.current_signal)) + goto nomem; + if (!wpa_dbus_dict_append_int32(&iter_dict, "linkspeed", + si.current_txrate / 1000)) + goto nomem; + if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", si.current_noise)) + goto nomem; + if (!wpa_dbus_dict_append_uint32(&iter_dict, "frequency", si.frequency)) + goto nomem; + + if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { + if (!wpa_dbus_dict_append_string(&iter_dict, "width", + channel_width_to_string(si.chanwidth))) + goto nomem; + } + + if (si.center_frq1 > 0 && si.center_frq2 > 0) { + if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1", + si.center_frq1)) + goto nomem; + if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq2", + si.center_frq2)) + goto nomem; + } + + if (si.avg_signal) { + if (!wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi", + si.avg_signal)) + goto nomem; + } + + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) + goto nomem; + if (!dbus_message_iter_close_container(&iter, &variant_iter)) + goto nomem; + + return reply; + +nomem: + if (reply) + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); + return reply; +} + + /* * wpas_dbus_handler_disconnect - Terminate the current connection * @message: Pointer to incoming dbus message @@ -3955,6 +4038,35 @@ dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, /** + * wpas_dbus_getter_bss_age - Return time in seconds since BSS was last seen + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for BSS age + */ +dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct bss_handler_args *args = user_data; + struct wpa_bss *res; + struct os_reltime now, diff = { 0, 0 }; + u32 age; + + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; + + os_get_reltime(&now); + os_reltime_sub(&now, &res->last_update, &diff); + age = diff.sec > 0 ? diff.sec : 0; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &age, + error); +} + + +/** * wpas_dbus_getter_enabled - Check whether network is enabled or disabled * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 461970d3..f6a83cdf 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -87,6 +87,9 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -268,6 +271,9 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -292,6 +298,14 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data); + DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message, diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index c9ecc7b4..7867f0c8 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -26,6 +26,7 @@ #include "ap/wps_hostapd.h" #include "../p2p_supplicant.h" +#include "../wifi_display.h" /** * Parses out the mac address from the peer object path. @@ -2589,3 +2590,77 @@ DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( return NULL; } + + +#ifdef CONFIG_WIFI_DISPLAY + +dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_global *global = user_data; + struct wpabuf *ie; + dbus_bool_t ret; + + ie = wifi_display_get_wfd_ie(global); + if (ie == NULL) + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_BYTE, + NULL, 0, error); + + ret = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + wpabuf_head(ie), + wpabuf_len(ie), error); + wpabuf_free(ie); + + return ret; +} + + +dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_global *global = user_data; + DBusMessageIter variant, array; + struct wpabuf *ie = NULL; + const u8 *data; + int len; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + goto err; + + dbus_message_iter_recurse(iter, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) + goto err; + + dbus_message_iter_recurse(&variant, &array); + dbus_message_iter_get_fixed_array(&array, &data, &len); + if (len == 0) { + wifi_display_enable(global, 0); + wifi_display_deinit(global); + + return TRUE; + } + + ie = wpabuf_alloc(len); + if (ie == NULL) + goto err; + + wpabuf_put_data(ie, data, len); + if (wifi_display_subelem_set_from_ies(global, ie) != 0) + goto err; + + if (global->wifi_display == 0) + wifi_display_enable(global, 1); + + wpabuf_free(ie); + + return TRUE; +err: + wpabuf_free(ie); + + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; +} + +#endif /* CONFIG_WIFI_DISPLAY */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index 67dbfc95..6e67c89e 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -210,5 +210,16 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( DBusMessage *message, struct wpa_supplicant *wpa_s); +#ifdef CONFIG_WIFI_DISPLAY + +dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +#endif /* CONFIG_WIFI_DISPLAY */ #endif /* DBUS_NEW_HANDLERS_P2P_H */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index 4ad5e7e0..8ecf7dba 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -389,3 +389,60 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, return TRUE; } + + +/** + * wpas_dbus_getter_config_methods - Get current WPS configuration methods + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "ConfigMethods" property. Returned boolean will be true if + * providing the relevant string worked, or false otherwise. + */ +dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods = wpa_s->conf->config_methods; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &methods, error); +} + + +/** + * wpas_dbus_setter_config_methods - Set WPS configuration methods + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter for "ConfigMethods" property. Sets the methods string, apply such + * change and returns true on success. Returns false otherwise. + */ +dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *new_methods; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + new_methods = os_strdup(methods); + if (!new_methods) + return FALSE; + + os_free(wpa_s->conf->config_methods); + wpa_s->conf->config_methods = new_methods; + + wpa_s->conf->changed_parameters |= CFG_CHANGED_CONFIG_METHODS; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 5136c6bb..05dfc911 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -532,14 +532,14 @@ static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu) static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, - u32 peer_capab, const u8 *buf, - size_t len) + u32 peer_capab, int initiator, + const u8 *buf, size_t len) { if (wpa_s->driver->send_tdls_mgmt) { return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst, action_code, dialog_token, status_code, peer_capab, - buf, len); + initiator, buf, len); } return -1; } @@ -632,6 +632,22 @@ static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s, data, data_len, buf); } +static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed, + const u8 *bssid) +{ + if (!wpa_s->driver->roaming) + return -1; + return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid); +} + +static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->set_mac_addr) + return -1; + return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr); +} + #ifdef CONFIG_MACSEC diff --git a/wpa_supplicant/eap_proxy_dummy.mak b/wpa_supplicant/eap_proxy_dummy.mak new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/wpa_supplicant/eap_proxy_dummy.mak diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a9e6439c..46d27678 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -582,7 +582,7 @@ static int freq_allowed(int *freqs, int freq) } -static int ht_supported(const struct hostapd_hw_modes *mode) +int ht_supported(const struct hostapd_hw_modes *mode) { if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { /* @@ -600,7 +600,7 @@ static int ht_supported(const struct hostapd_hw_modes *mode) } -static int vht_supported(const struct hostapd_hw_modes *mode) +int vht_supported(const struct hostapd_hw_modes *mode) { if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) { /* @@ -3000,15 +3000,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_s->own_scan_running = 1; if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id) { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u", - wpa_s->manual_scan_id); + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_SCAN_STARTED "id=%u", + wpa_s->manual_scan_id); } else { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_SCAN_STARTED); } } else { wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); wpa_s->external_scan_running = 1; - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } break; case EVENT_SCAN_RESULTS: diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 39862681..3a89674f 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/main.c b/wpa_supplicant/main.c index d2e839dc..e5964684 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -331,7 +331,8 @@ int main(int argc, char *argv[]) if (wpa_s->global->p2p == NULL && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpas_p2p_add_p2pdev_interface(wpa_s) < 0) + wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < + 0) exitcode = -1; #endif /* CONFIG_P2P */ } diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 3b730cf1..617ce849 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -345,6 +345,12 @@ void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, } +void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id); +} + + void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name) { wpas_dbus_signal_blob_added(wpa_s, name); @@ -627,3 +633,18 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, "status='%s' parameter='%s'", status, parameter); } + + +void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (wpa_s->current_ssid != ssid) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Network bssid config changed for the current network - within-ESS roaming %s", + ssid->bssid_set ? "disabled" : "enabled"); + + wpa_drv_roaming(wpa_s, !ssid->bssid_set, + ssid->bssid_set ? ssid->bssid : NULL); +} diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 58675ac0..7feb5304 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -71,6 +71,7 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, unsigned int id); void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, unsigned int id); +void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id); void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name); void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name); @@ -127,5 +128,7 @@ void wpas_notify_preq(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len, u32 ssi_signal); void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); +void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); #endif /* NOTIFY_H */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 480e0ba3..4c2d838e 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -462,11 +462,16 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, (ssid && ssid->mode == WPAS_MODE_INFRA)) { wpa_s->reassociate = 0; wpa_s->disconnected = 1; - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); gtype = "client"; } else gtype = "GO"; + + if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) + wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); + + if (os_strcmp(gtype, "client") == 0) + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; wpa_msg_global(wpa_s->parent, MSG_INFO, @@ -524,9 +529,6 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, */ wpa_s->global->p2p_go_wait_client.sec = 0; - if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) - wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); - if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { struct wpa_global *global; char *ifname; @@ -3800,7 +3802,8 @@ static void wpas_p2p_debug_print(void *ctx, int level, const char *msg) } -int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s) +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, + const char *conf_p2p_dev) { struct wpa_interface iface; struct wpa_supplicant *p2pdev_wpa_s; @@ -3831,8 +3834,8 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s) * If a P2P Device configuration file was given, use it as the interface * configuration file (instead of using parent's configuration file. */ - if (wpa_s->conf_p2p_dev) { - iface.confname = wpa_s->conf_p2p_dev; + if (conf_p2p_dev) { + iface.confname = conf_p2p_dev; iface.ctrl_interface = NULL; } else { iface.confname = wpa_s->confname; @@ -4108,11 +4111,8 @@ static void wpas_p2p_deinit_global(struct wpa_global *global) struct wpa_supplicant *wpa_s, *tmp; wpa_s = global->ifaces; - if (wpa_s) - wpas_p2p_service_flush(wpa_s); - if (global->p2p == NULL) - return; + wpas_p2p_service_flush(global->p2p_init_wpa_s); /* Remove remaining P2P group interfaces */ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) @@ -5062,7 +5062,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " "channel: %d MHz", freq); } else { - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; freq = 2412 + (r % 3) * 25; wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " "channel: %d MHz", freq); @@ -5079,7 +5080,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " "channel: %d MHz", freq); } else { - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; freq = 5180 + (r % 4) * 20; if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { wpa_printf(MSG_DEBUG, "P2P: Could not select " @@ -5109,7 +5111,8 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, unsigned int i, r; /* first try some random selection of the social channels */ - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; for (i = 0; i < 3; i++) { params->freq = 2412 + ((r + i) % 3) * 25; @@ -6403,8 +6406,10 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) * Pick one of the social channels randomly as the * listen channel. */ - os_get_random((u8 *) &r, sizeof(r)); - channel = 1 + (r % 3) * 5; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + channel = 1; + else + channel = 1 + (r % 3) * 5; channel_forced = 0; } ret = p2p_set_listen_channel(p2p, reg_class, channel, @@ -6428,8 +6433,10 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) * Use random operation channel from (1, 6, 11) *if no other preference is indicated. */ - os_get_random((u8 *) &r, sizeof(r)); - op_channel = 1 + (r % 3) * 5; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + op_channel = 1; + else + op_channel = 1 + (r % 3) * 5; cfg_op_channel = 0; } ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel, diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index d79ec03f..8e23c188 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -16,7 +16,8 @@ struct p2p_peer_info; struct p2p_channels; struct wps_event_fail; -int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s); +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, + const char *conf_p2p_dev); struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index d9b0551a..c70d82c2 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) @@ -1245,6 +1252,13 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; + /* See if user specified frequencies. If so, scan only those. */ + if (wpa_s->conf->freq_list && !params.freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Optimize scan based on conf->freq_list"); + int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); + } + scan_params = ¶ms; scan: diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 83a1471f..e6163192 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -743,7 +743,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; - params.freq = wpa_s->sme.freq; + params.freq.freq = wpa_s->sme.freq; params.bg_scan_period = wpa_s->current_ssid ? wpa_s->current_ssid->bg_scan_period : -1; params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? @@ -781,7 +781,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", - params.freq); + params.freq.freq); wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); @@ -1319,7 +1319,10 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) wpa_s->sme.sa_query_trans_id = nbuf; wpa_s->sme.sa_query_count++; - os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Could not generate SA Query ID"); + return; + } timeout = sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c index b6f92363..6dc41dec 100644 --- a/wpa_supplicant/wifi_display.c +++ b/wpa_supplicant/wifi_display.c @@ -36,6 +36,34 @@ void wifi_display_deinit(struct wpa_global *global) } +struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global) +{ + struct wpabuf *ie; + size_t len; + int i; + + if (global->p2p == NULL) + return NULL; + + len = 0; + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + if (global->wfd_subelem[i]) + len += wpabuf_len(global->wfd_subelem[i]); + } + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + if (global->wfd_subelem[i]) + wpabuf_put_buf(ie, global->wfd_subelem[i]); + } + + return ie; +} + + static int wifi_display_update_wfd_ie(struct wpa_global *global) { struct wpabuf *ie, *buf; @@ -238,6 +266,60 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd) } +int wifi_display_subelem_set_from_ies(struct wpa_global *global, + struct wpabuf *ie) +{ + int subelements[MAX_WFD_SUBELEMS] = {}; + const u8 *pos, *end; + int len, subelem; + struct wpabuf *e; + + wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu", + ie, ie ? (unsigned long) wpabuf_len(ie) : 0); + + if (ie == NULL || wpabuf_len(ie) < 6) + return -1; + + pos = wpabuf_head(ie); + end = pos + wpabuf_len(ie); + + while (end > pos) { + if (pos + 3 > end) + break; + + len = WPA_GET_BE16(pos + 1) + 3; + + wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d", + *pos, len - 3); + + if (pos + len > end) + break; + + subelem = *pos; + if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) { + e = wpabuf_alloc_copy(pos, len); + if (e == NULL) + return -1; + + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = e; + subelements[subelem] = 1; + } + + pos += len; + } + + for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) { + if (subelements[subelem] == 0) { + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = NULL; + } + } + + return wifi_display_update_wfd_ie(global); +} + + int wifi_display_subelem_get(struct wpa_global *global, char *cmd, char *buf, size_t buflen) { diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h index 75548175..0966bdb9 100644 --- a/wpa_supplicant/wifi_display.h +++ b/wpa_supplicant/wifi_display.h @@ -13,7 +13,10 @@ int wifi_display_init(struct wpa_global *global); void wifi_display_deinit(struct wpa_global *global); void wifi_display_enable(struct wpa_global *global, int enabled); +struct wpabuf *wifi_display_get_wfd_ie(struct wpa_global *global); int wifi_display_subelem_set(struct wpa_global *global, char *cmd); +int wifi_display_subelem_set_from_ies(struct wpa_global *global, + struct wpabuf *ie); int wifi_display_subelem_get(struct wpa_global *global, char *cmd, char *buf, size_t buflen); char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index eca84beb..bc6cf2e0 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -3192,15 +3192,29 @@ static void wpa_cli_action_process(const char *msg) { const char *pos; char *copy = NULL, *id, *pos2; + const char *ifname = ctrl_ifname; + char ifname_buf[100]; pos = msg; + if (os_strncmp(pos, "IFNAME=", 7) == 0) { + const char *end; + end = os_strchr(pos + 7, ' '); + if (end && (unsigned int) (end - pos) < sizeof(ifname_buf)) { + pos += 7; + os_memcpy(ifname_buf, pos, end - pos); + ifname_buf[end - pos] = '\0'; + ifname = ifname_buf; + pos = end + 1; + } + } if (*pos == '<') { + const char *prev = pos; /* skip priority */ pos = os_strchr(pos, '>'); if (pos) pos++; else - pos = msg; + pos = prev; } if (str_match(pos, WPA_EVENT_CONNECTED)) { @@ -3237,37 +3251,37 @@ static void wpa_cli_action_process(const char *msg) if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) { wpa_cli_connected = 1; wpa_cli_last_id = new_id; - wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); + wpa_cli_exec(action_file, ifname, "CONNECTED"); } } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { if (wpa_cli_connected) { wpa_cli_connected = 0; - wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); + wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPS_EVENT_SUCCESS)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPS_EVENT_FAIL)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, AP_STA_CONNECTED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, AP_STA_DISCONNECTED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; @@ -3399,7 +3413,7 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) return; } while (wpa_ctrl_pending(ctrl) > 0) { - char buf[256]; + char buf[4096]; size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; @@ -3463,10 +3477,18 @@ static int tokenize_cmd(char *cmd, char *argv[]) static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) { - if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { - printf("Connection to wpa_supplicant lost - trying to " - "reconnect\n"); - wpa_cli_close_connection(); + if (ctrl_conn) { + int res; + char *prefix = ifname_prefix; + + ifname_prefix = NULL; + res = _wpa_ctrl_command(ctrl_conn, "PING", 0); + ifname_prefix = prefix; + if (res) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); + } } if (!ctrl_conn) wpa_cli_reconnect(); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e9337570..a7615635 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -300,6 +300,7 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; eapol_conf.external_sim = wpa_s->conf->external_sim; + eapol_conf.wps = wpa_s->key_mgmt == WPA_KEY_MGMT_WPS; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); #endif /* IEEE8021X_EAPOL */ @@ -405,11 +406,6 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->confanother); wpa_s->confanother = NULL; -#ifdef CONFIG_P2P - os_free(wpa_s->conf_p2p_dev); - wpa_s->conf_p2p_dev = NULL; -#endif /* CONFIG_P2P */ - wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; @@ -1382,6 +1378,68 @@ void wpas_connect_work_done(struct wpa_supplicant *wpa_s) } +int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style) +{ + struct os_reltime now; + u8 addr[ETH_ALEN]; + + os_get_reltime(&now); + if (wpa_s->last_mac_addr_style == style && + 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; + } + + switch (style) { + case 1: + if (random_mac_addr(addr) < 0) + return -1; + break; + case 2: + os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN); + if (random_mac_addr_keep_oui(addr) < 0) + return -1; + break; + default: + 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; + wpa_s->last_mac_addr_style = style; + + 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, wpa_s->conf->preassoc_mac_addr); +} + + static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); /** @@ -1396,6 +1454,34 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { struct wpa_connect_work *cwork; + int rand_style; + + if (ssid->mac_addr == -1) + rand_style = wpa_s->conf->mac_addr; + else + rand_style = ssid->mac_addr; + + if (wpa_s->last_ssid == ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + } else if (rand_style > 0) { + if (wpas_update_random_addr(wpa_s, rand_style) < 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); @@ -1755,7 +1841,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) MAC2STR(bss->bssid), bss->freq, ssid->bssid_set); params.bssid = bss->bssid; - params.freq = bss->freq; + params.freq.freq = bss->freq; } params.bssid_hint = bss->bssid; params.freq_hint = bss->freq; @@ -1771,8 +1857,23 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && - params.freq == 0) - params.freq = ssid->frequency; /* Initial channel for IBSS */ + params.freq.freq == 0) { + enum hostapd_hw_mode hw_mode; + u8 channel; + + params.freq.freq = ssid->frequency; + + hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); + for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == hw_mode) { + struct hostapd_hw_modes *mode; + + mode = &wpa_s->hw.modes[i]; + params.freq.ht_enabled = ht_supported(mode); + break; + } + } + } if (ssid->mode == WPAS_MODE_IBSS) { if (ssid->beacon_int) @@ -1873,13 +1974,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (wpa_s->num_multichan_concurrent < 2) { int freq, num; num = get_shared_radio_freqs(wpa_s, &freq, 1); - if (num > 0 && freq > 0 && freq != params.freq) { + if (num > 0 && freq > 0 && freq != params.freq.freq) { wpa_printf(MSG_DEBUG, "Assoc conflicting freq found (%d != %d)", - freq, params.freq); - if (wpas_p2p_handle_frequency_conflicts(wpa_s, - params.freq, - ssid) < 0) { + freq, params.freq.freq); + if (wpas_p2p_handle_frequency_conflicts( + wpa_s, params.freq.freq, ssid) < 0) { wpas_connect_work_done(wpa_s); return; } @@ -2686,6 +2786,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; } @@ -2733,6 +2835,7 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, MAC2STR(wpa_s->own_addr)); + os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN); wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); if (wpa_s->bridge_ifname[0]) { @@ -3532,13 +3635,6 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->confanother = os_rel2abs_path(iface->confanother); wpa_config_read(wpa_s->confanother, wpa_s->conf); -#ifdef CONFIG_P2P - wpa_s->conf_p2p_dev = os_rel2abs_path(iface->conf_p2p_dev); -#ifndef ANDROID_P2P - wpa_config_read(wpa_s->conf_p2p_dev, wpa_s->conf); -#endif -#endif /* CONFIG_P2P */ - /* * Override ctrl_interface and driver_param if set on command * line. diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 416914dc..1b629123 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -332,6 +332,25 @@ 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 +# 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. 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 +# 2 = like 1, but maintain OUI (with local admin bit set) +#preassoc_mac_addr=0 + # Interworking (IEEE 802.11u) # Enable Interworking @@ -982,6 +1001,12 @@ 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 +# 2 = like 1, but maintain OUI (with local admin bit set) +#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 57e2f6f3..7b3fb6ed 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -67,7 +67,7 @@ struct wpa_interface { #ifdef CONFIG_P2P /** - * conf_p2p_dev - Additional configuration file used to hold the + * conf_p2p_dev - Configuration file used to hold the * P2P Device configuration parameters. * * This can also be %NULL. In such a case, if a P2P Device dedicated @@ -393,6 +393,7 @@ struct wpa_supplicant { struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_addr[ETH_ALEN]; + unsigned char perm_addr[ETH_ALEN]; char ifname[100]; #ifdef CONFIG_CTRL_IFACE_DBUS char *dbus_path; @@ -409,10 +410,6 @@ struct wpa_supplicant { char *confname; char *confanother; -#ifdef CONFIG_P2P - char *conf_p2p_dev; -#endif /* CONFIG_P2P */ - struct wpa_config *conf; int countermeasures; struct os_reltime last_michael_mic_error; @@ -423,6 +420,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; @@ -611,6 +609,10 @@ 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; + int last_mac_addr_style; struct ibss_rsn *ibss_rsn; @@ -961,6 +963,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 style); +int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response @@ -988,6 +992,8 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid); +int ht_supported(const struct hostapd_hw_modes *mode); +int vht_supported(const struct hostapd_hw_modes *mode); /* eap_register.c */ int eap_register_methods(void); diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index d6f3b715..5312be94 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -142,11 +142,29 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, if (pmksa_cache_get_current(wpa_s->wpa) && type == IEEE802_1X_TYPE_EAPOL_START) { - /* Trying to use PMKSA caching - do not send EAPOL-Start frames - * since they will trigger full EAPOL authentication. */ - wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " - "EAPOL-Start"); - return -1; + /* + * We were trying to use PMKSA caching and sending EAPOL-Start + * would abort that and trigger full EAPOL authentication. + * However, we've already waited for the AP/Authenticator to + * start 4-way handshake or EAP authentication, and apparently + * it has not done so since the startWhen timer has reached zero + * to get the state machine sending EAPOL-Start. This is not + * really supposed to happen, but an interoperability issue with + * a deployed AP has been identified where the connection fails + * due to that AP failing to operate correctly if PMKID is + * included in the Association Request frame. To work around + * this, assume PMKSA caching failed and try to initiate full + * EAP authentication. + */ + if (!wpa_s->current_ssid || + wpa_s->current_ssid->eap_workaround) { + wpa_printf(MSG_DEBUG, + "RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication"); + } else { + wpa_printf(MSG_DEBUG, + "RSN: PMKSA caching - do not send EAPOL-Start"); + return -1; + } } if (is_zero_ether_addr(wpa_s->bssid)) { @@ -562,11 +580,13 @@ static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, - const u8 *buf, size_t len) + int initiator, const u8 *buf, + size_t len) { struct wpa_supplicant *wpa_s = ctx; return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token, - status_code, peer_capab, buf, len); + status_code, peer_capab, initiator, buf, + len); } @@ -582,7 +602,7 @@ static int wpa_supplicant_tdls_peer_addset( const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, + u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len) { @@ -597,10 +617,10 @@ static int wpa_supplicant_tdls_peer_addset( params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; /* - * TDLS Setup frames do not contain WMM IEs, hence need to depend on - * qosinfo to check if the peer is WMM capable. + * Don't rely only on qosinfo for WMM capability. It may be 0 even when + * present. Allow the WMM IE to also indicate QoS support. */ - if (qosinfo) + if (wmm || qosinfo) params.flags |= WPA_STA_WMM; params.ht_capabilities = ht_capab; diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 40f235f7..e1671856 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -289,7 +289,7 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, (ssid->group_cipher != new_ssid->group_cipher && !(ssid->group_cipher & new_ssid->group_cipher & WPA_CIPHER_CCMP))) - continue; + continue; /* * Some existing WPS APs will send two creds in case they are |