Cumulative patch from commit fb09ed338919db09f3990196171fa73b37e7a17f (DO NOT MERGE)
fb09ed3 Interworking: Notify the ANQP parsing status
d10b01d HS20: Provide appropriate permission to the OSU related files
73f1ee0 HS20: Fix TrustRoot path for PolicyUpdate node in PPS MO
54a0ac0 HS20: Return result of cmd_sub_rem in hs20-osu-client
b62b0cb WNM: Fix possible memory leak by free buf
9bd0273 EAP: Fix possible memory leak in eap_ttls_process_decrypted()
b760e64 eap_server: Avoid NULL pointer dereference in eap_fast_encrypt_phase2()
948d3a8 hostapd: Remove unused variable from hostapd_get_hw_features
dd09e42 Fix memory leak in wpa_supplicant global bgscan configuration
30f459c wpa_cli: Fix NULL dereference on printf string argument
414f23d Avoid NULL string in printf on EAP method names in authenticator
b72b2ad P2P: Stop p2p_listen/find on wpas_p2p_invite
7b7b444 nl80211: Fix reading of the extended capabilities mask
7e608d1 P2P: Use the correct wpa_s interface to handle P2P state flush
fd83335 AP: Enable HT Tx STBC for AP/GO if supported by driver
d90bfa9 Move external_scan_running to wpa_radio
0c5f01f Clear reattach flag in fast associate flow
8ad8bc5 NFC: Redirect NFC commands on global control interface
57ae1f5 P2P: Fix P2P invitation with NFC
07565ab WNM: Fix the length of WNM_BSS_QUERY control interface command
2d9c99e Retry scan-for-connect if driver trigger fails
911942e Add a test framework for various wpa_supplicant failure cases
6b46bfa WPS: Re-fix an interoperability issue with mixed mode and AP Settings
1648cc6 ACS: Allow subset of channels to be configured
95ff306 nl80211: Allow HT/VHT to be disabled for IBSS
7451a21 mesh: Return negative value on join failed
5a2a6de mesh: Make inactivity timer configurable
b9749ba AP: Expire STA without entry in kernel
a114c72 AP: Remove redundant condition for STA expiration
0d787f0 Fix RADIUS client with out-of-memory and missing shared secret
0efcad2 Print in debug log whether attached monitor is for global interface
8266e6c HS 2.0: Try to use same BSS entry for storing GAS results
6c69991 Make wpa_supplicant FLUSH command more likely to clear all BSS entries
2dbe63a Write reason for scan only_new_results into debug log
242b83a eapol_test: Fix cert_cb() function arguments
a8826b1 Interworking: Avoid busy loop in scan result mismatch corner cases
edd5939 Interworking: Start ANQP fetch from eloop callback
cbc210d RADIUS DAS: Allow PMKSA cache entry to be removed without association
4e871ed RADIUS DAS: Support Acct-Multi-Session-Id as a session identifier
b52c0d4 Add authMultiSessionId into hostapd STA info
861beb7 RADIUS DAS: Check for single session match for Disconnect-Request
783b2a9 Interworking: Fix INTERWORKING_CONNECT with zero-length SSID BSS entry
1fef85c nl80211: Fix AP-scan-in-STA-mode error path behavior
cebee30 Add domain_match network profile parameter
d07d3fb Add peer certificate alt subject name information to EAP events
98a4cd4 D-Bus: Clear cached EAP data on network profile changes
483dd6a Include peer certificate always in EAP events
dd5f902 Get rid of a compiler warning
d29fa3a Extend VENDOR_ELEM parameters to cover non-P2P Association Request
e7d0e97 hostapd: Add vendor specific VHT extension for the 2.4 GHz band
Change-Id: I45436c49986cd6bddbd869db3f474871a29ce1dc
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 97cf26f..e4c834c 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -455,6 +455,22 @@
}
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ int *entry;
+
+ if (!iface->conf->chanlist)
+ return 1;
+
+ for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+ if (*entry == chan->chan)
+ return 1;
+ }
+ return 0;
+}
+
+
static void acs_survey_all_chans_intereference_factor(
struct hostapd_iface *iface)
{
@@ -467,6 +483,9 @@
if (!acs_usable_chan(chan))
continue;
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
chan->chan, chan->freq);
@@ -543,6 +562,8 @@
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
+ if (!is_in_chanlist(iface, chan))
+ continue;
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 58af6cb..e5215c5 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -551,6 +551,8 @@
int mesh;
int radio_measurements;
+
+ int vendor_vht;
};
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4a8703a..b0a74e0 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -379,6 +379,10 @@
#endif /* CONFIG_P2P */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
+ if (hapd->conf->vendor_vht) {
+ buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
resp = os_zalloc(buflen);
if (resp == NULL)
return NULL;
@@ -446,8 +450,12 @@
pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
&hapd->cs_c_off_proberesp);
#ifdef CONFIG_IEEE80211AC
- pos = hostapd_eid_vht_capabilities(hapd, pos);
- pos = hostapd_eid_vht_operation(hapd, pos);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ pos = hostapd_eid_vht_capabilities(hapd, pos);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+ }
+ if (hapd->conf->vendor_vht)
+ pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
/* Wi-Fi Alliance WMM */
@@ -776,6 +784,14 @@
#endif /* CONFIG_P2P */
if (hapd->conf->vendor_elements)
tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht) {
+ tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -865,8 +881,12 @@
tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
&hapd->cs_c_off_beacon);
#ifdef CONFIG_IEEE80211AC
- tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
- tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ }
+ if (hapd->conf->vendor_vht)
+ tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
/* Wi-Fi Alliance WMM */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 6e4169b..b641503 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -15,6 +15,8 @@
#include "radius/radius_client.h"
#include "radius/radius_das.h"
#include "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
#include "hostapd.h"
#include "authsrv.h"
#include "sta_info.h"
@@ -614,51 +616,190 @@
static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
- struct radius_das_attrs *attr)
+ struct radius_das_attrs *attr,
+ int *multi)
{
- struct sta_info *sta = NULL;
+ struct sta_info *selected, *sta;
char buf[128];
+ int num_attr = 0;
+ int count;
- if (attr->sta_addr)
+ *multi = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next)
+ sta->radius_das_match = 1;
+
+ if (attr->sta_addr) {
+ num_attr++;
sta = ap_get_sta(hapd, attr->sta_addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No Calling-Station-Id match");
+ return NULL;
+ }
- if (sta == NULL && attr->acct_session_id &&
- attr->acct_session_id_len == 17) {
+ selected = sta;
for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta != selected)
+ sta->radius_das_match = 0;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+ }
+
+ if (attr->acct_session_id) {
+ num_attr++;
+ if (attr->acct_session_id_len != 17) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
os_snprintf(buf, sizeof(buf), "%08X-%08X",
sta->acct_session_id_hi,
sta->acct_session_id_lo);
- if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
- break;
+ if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
}
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
}
- if (sta == NULL && attr->cui) {
+ if (attr->acct_multi_session_id) {
+ num_attr++;
+ if (attr->acct_multi_session_id_len != 17) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
+ if (!sta->eapol_sm ||
+ !sta->eapol_sm->acct_multi_session_id_hi) {
+ sta->radius_das_match = 0;
+ continue;
+ }
+ os_snprintf(buf, sizeof(buf), "%08X+%08X",
+ sta->eapol_sm->acct_multi_session_id_hi,
+ sta->eapol_sm->acct_multi_session_id_lo);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
+ 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id match");
+ }
+
+ if (attr->cui) {
+ num_attr++;
+ count = 0;
+
for (sta = hapd->sta_list; sta; sta = sta->next) {
struct wpabuf *cui;
+
+ if (!sta->radius_das_match)
+ continue;
cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
- if (cui && wpabuf_len(cui) == attr->cui_len &&
+ if (!cui || wpabuf_len(cui) != attr->cui_len ||
os_memcmp(wpabuf_head(cui), attr->cui,
- attr->cui_len) == 0)
- break;
+ attr->cui_len) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
}
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Chargeable-User-Identity match");
}
- if (sta == NULL && attr->user_name) {
+ if (attr->user_name) {
+ num_attr++;
+ count = 0;
+
for (sta = hapd->sta_list; sta; sta = sta->next) {
u8 *identity;
size_t identity_len;
+
+ if (!sta->radius_das_match)
+ continue;
identity = ieee802_1x_get_identity(sta->eapol_sm,
&identity_len);
- if (identity &&
- identity_len == attr->user_name_len &&
+ if (!identity ||
+ identity_len != attr->user_name_len ||
os_memcmp(identity, attr->user_name, identity_len)
- == 0)
- break;
+ != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after User-Name check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: User-Name match");
+ }
+
+ if (num_attr == 0) {
+ /*
+ * In theory, we could match all current associations, but it
+ * seems safer to just reject requests that do not include any
+ * session identification attributes.
+ */
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No session identification attributes included");
+ return NULL;
+ }
+
+ selected = NULL;
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->radius_das_match) {
+ if (selected) {
+ *multi = 1;
+ return NULL;
+ }
+ selected = sta;
}
}
- return sta;
+ return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ if (!hapd->wpa_auth)
+ return -1;
+ return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
}
@@ -667,14 +808,29 @@
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ int multi;
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
- sta = hostapd_das_find_sta(hapd, attr);
- if (sta == NULL)
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (sta == NULL) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: PMKSA cache entry matched");
+ return RADIUS_DAS_SUCCESS;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - disconnecting", MAC2STR(sta->addr));
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
hostapd_drv_sta_deauth(hapd, sta->addr,
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 6b0a72d..05431d3 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -75,7 +75,7 @@
int hostapd_get_hw_features(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
- int ret = 0, i, j;
+ int i, j;
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
@@ -138,7 +138,7 @@
}
}
- return ret;
+ return 0;
}
@@ -641,12 +641,31 @@
static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
{
- u32 hw = iface->current_mode->vht_capab;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ u32 hw = mode->vht_capab;
u32 conf = iface->conf->vht_capab;
wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
hw, conf);
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ iface->conf->bss[0]->vendor_vht &&
+ mode->vht_capab == 0 && iface->hw_features) {
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ if (iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &iface->hw_features[i];
+ hw = mode->vht_capab;
+ wpa_printf(MSG_DEBUG,
+ "update hw vht capab based on 5 GHz band: 0x%x",
+ hw);
+ break;
+ }
+ }
+ }
+
#define VHT_CAP_CHECK(cap) \
do { \
if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 3d4488a..89911b1 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1327,6 +1327,13 @@
"mandatory VHT PHY - reject association");
return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
}
+
+ if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
+ resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
+ elems.vendor_vht_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_P2P
@@ -1616,8 +1623,10 @@
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
- p = hostapd_eid_vht_capabilities(hapd, p);
- p = hostapd_eid_vht_operation(hapd, p);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ p = hostapd_eid_vht_capabilities(hapd, p);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
#endif /* CONFIG_IEEE80211AC */
p = hostapd_eid_ext_capab(hapd, p);
@@ -1625,6 +1634,11 @@
if (sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
if (sta->flags & WLAN_STA_WMM)
p = hostapd_eid_wmm(hapd, p);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index beaeac5..41c27d9 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -51,6 +51,7 @@
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id);
@@ -62,6 +63,9 @@
struct ieee80211_vht_capabilities *neg_vht_cap);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab, size_t ht_capab_len);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len);
+
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 437cf50..159693f 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -22,12 +22,25 @@
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
- hapd->conf->disable_11ac)
+ if (!mode)
return eid;
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+ mode->vht_capab == 0 && hapd->iface->hw_features) {
+ int i;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ if (hapd->iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &hapd->iface->hw_features[i];
+ break;
+ }
+ }
+ }
+
*pos++ = WLAN_EID_VHT_CAP;
*pos++ = sizeof(*cap);
@@ -37,8 +50,7 @@
hapd->iface->conf->vht_capab);
/* Supported MCS set comes from hw */
- os_memcpy(&cap->vht_supported_mcs_set,
- hapd->iface->current_mode->vht_mcs_set, 8);
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
pos += sizeof(*cap);
@@ -51,9 +63,6 @@
struct ieee80211_vht_operation *oper;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
- return eid;
-
*pos++ = WLAN_EID_VHT_OPERATION;
*pos++ = sizeof(*oper);
@@ -109,6 +118,66 @@
}
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len)
+{
+ const u8 *vht_capab;
+ unsigned int vht_capab_len;
+
+ if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+ hapd->conf->disable_11ac)
+ goto no_capab;
+
+ /* The VHT Capabilities element embedded in vendor VHT */
+ vht_capab = ie + 5;
+ if (vht_capab[0] != WLAN_EID_VHT_CAP)
+ goto no_capab;
+ vht_capab_len = vht_capab[1];
+ if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+ (int) vht_capab_len > ie + len - vht_capab - 2)
+ goto no_capab;
+ vht_capab += 2;
+
+ if (sta->vht_capabilities == NULL) {
+ sta->vht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+ if (sta->vht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+ os_memcpy(sta->vht_capabilities, vht_capab,
+ sizeof(struct ieee80211_vht_capabilities));
+ return WLAN_STATUS_SUCCESS;
+
+no_capab:
+ sta->flags &= ~WLAN_STA_VENDOR_VHT;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = (5 + /* The Vendor OUI, type and subtype */
+ 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation));
+
+ WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+ pos += 4;
+ *pos++ = VENDOR_VHT_SUBTYPE;
+ pos = hostapd_eid_vht_capabilities(hapd, pos);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+
+ return pos;
+}
+
+
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_oper_notif)
{
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 2287b28..9d257cc 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1211,15 +1211,11 @@
if (eap_type >= 0)
sm->eap_type_authsrv = eap_type;
os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
- eap_type >= 0 ? eap_server_get_name(0, eap_type) :
- "??",
- eap_type);
+ eap_server_get_name(0, eap_type), eap_type);
break;
case EAP_CODE_RESPONSE:
os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
- eap_type >= 0 ? eap_server_get_name(0, eap_type) :
- "??",
- eap_type);
+ eap_server_get_name(0, eap_type), eap_type);
break;
case EAP_CODE_SUCCESS:
os_strlcpy(buf, "EAP Success", sizeof(buf));
@@ -2487,15 +2483,23 @@
return len;
len += ret;
+ if (sm->acct_multi_session_id_hi) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "authMultiSessionId=%08X+%08X\n",
+ sm->acct_multi_session_id_hi,
+ sm->acct_multi_session_id_lo);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
name1 = eap_server_get_name(0, sm->eap_type_authsrv);
name2 = eap_server_get_name(0, sm->eap_type_supp);
ret = os_snprintf(buf + len, buflen - len,
"last_eap_type_as=%d (%s)\n"
"last_eap_type_sta=%d (%s)\n",
- sm->eap_type_authsrv,
- name1 ? name1 : "",
- sm->eap_type_supp,
- name2 ? name2 : "");
+ sm->eap_type_authsrv, name1,
+ sm->eap_type_supp, name2);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 4270382..650e9a8 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
#include "sta_info.h"
#include "ap_config.h"
#include "pmksa_cache_auth.h"
@@ -452,3 +453,74 @@
return pmksa;
}
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+ struct radius_das_attrs *attr)
+{
+ int match = 0;
+
+ if (attr->sta_addr) {
+ if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->acct_multi_session_id) {
+ char buf[20];
+
+ if (attr->acct_multi_session_id_len != 17)
+ return 0;
+ os_snprintf(buf, sizeof(buf), "%08X+%08X",
+ entry->acct_multi_session_id_hi,
+ entry->acct_multi_session_id_lo);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->cui) {
+ if (!entry->cui ||
+ attr->cui_len != wpabuf_len(entry->cui) ||
+ os_memcmp(attr->cui, wpabuf_head(entry->cui),
+ attr->cui_len) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->user_name) {
+ if (!entry->identity ||
+ attr->user_name_len != entry->identity_len ||
+ os_memcmp(attr->user_name, entry->identity,
+ attr->user_name_len) != 0)
+ return 0;
+ match++;
+ }
+
+ return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr)
+{
+ int found = 0;
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (attr->acct_session_id)
+ return -1;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (das_attr_match(entry, attr)) {
+ found++;
+ prev = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, prev);
+ continue;
+ }
+ entry = entry->next;
+ }
+
+ return found ? 0 : -1;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 519555f..8b7be12 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -61,5 +61,7 @@
struct eapol_state_machine *eapol);
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr);
#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 1c2197a..bb43218 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -370,8 +370,14 @@
* but do not disconnect the station now.
*/
next_time = hapd->conf->ap_max_inactivity + fuzz;
- } else if (inactive_sec < hapd->conf->ap_max_inactivity &&
- sta->flags & WLAN_STA_ASSOC) {
+ } else if (inactive_sec == -ENOENT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has lost its driver entry",
+ MAC2STR(sta->addr));
+
+ if (hapd->conf->skip_inactivity_poll)
+ sta->timeout_next = STA_DISASSOC;
+ } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
/* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago",
@@ -1101,7 +1107,7 @@
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1119,6 +1125,7 @@
(flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
(flags & WLAN_STA_GAS ? "[GAS]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
+ (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
"[WNM_SLEEP_MODE]" : ""));
if (os_snprintf_error(buflen, res))
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 588a9e2..57551ab 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -35,6 +35,7 @@
#define WLAN_STA_VHT BIT(18)
#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -84,6 +85,7 @@
unsigned int remediation:1;
unsigned int hs20_deauth_requested:1;
unsigned int session_timeout_set:1;
+ unsigned int radius_das_match:1;
u16 auth_alg;
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 7e8fb5c..4c8bc10 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -564,8 +564,11 @@
if (url) {
/* Session Information URL */
url_len = os_strlen(url);
- if (url_len > 255)
+ if (url_len > 255) {
+ os_free(buf);
return -1;
+ }
+
*pos++ = url_len;
os_memcpy(pos, url, url_len);
pos += url_len;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 059b884..f71b028 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3340,3 +3340,10 @@
return 0;
}
#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr)
+{
+ return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 757e49e..b34b84d 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -315,4 +315,8 @@
int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr);
+
#endif /* WPA_AUTH_H */