Cumulative patch from commit fb09ed338919db09f3990196171fa73b37e7a17f

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 */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index e1d45cf..ed8d466 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -128,6 +128,15 @@
 			elems->vendor_ht_cap = pos;
 			elems->vendor_ht_cap_len = elen;
 			break;
+		case VENDOR_VHT_TYPE:
+			if (elen > 4 &&
+			    (pos[4] == VENDOR_VHT_SUBTYPE ||
+			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
+				elems->vendor_vht = pos;
+				elems->vendor_vht_len = elen;
+			} else
+				return -1;
+			break;
 		default:
 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
 				   "information element ignored "
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 2357afc..05fe32b 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -35,6 +35,7 @@
 	const u8 *vht_operation;
 	const u8 *vht_opmode_notif;
 	const u8 *vendor_ht_cap;
+	const u8 *vendor_vht;
 	const u8 *p2p;
 	const u8 *wfd;
 	const u8 *link_id;
@@ -71,6 +72,7 @@
 	u8 vht_capabilities_len;
 	u8 vht_operation_len;
 	u8 vendor_ht_cap_len;
+	u8 vendor_vht_len;
 	u8 p2p_len;
 	u8 wfd_len;
 	u8 interworking_len;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index dfe0faf..803b8cc 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1155,6 +1155,9 @@
 };
 
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+#define VENDOR_VHT_TYPE		0x04
+#define VENDOR_VHT_SUBTYPE	0x08
+#define VENDOR_VHT_SUBTYPE2	0x00
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
 
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 1f747eb..59a3412 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -42,6 +42,8 @@
 #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
 /** EAP peer certificate from TLS */
 #define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP peer certificate alternative subject name component from TLS */
+#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
 /** EAP TLS certificate chain validation error */
 #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
 /** EAP status */
@@ -194,6 +196,9 @@
 /* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
 #define GAS_QUERY_DONE "GAS-QUERY-DONE "
 
+/* parameters: <addr> <result> */
+#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
+
 #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
 
@@ -277,6 +282,7 @@
 	VENDOR_ELEM_P2P_INV_RESP = 10,
 	VENDOR_ELEM_P2P_ASSOC_REQ = 11,
 	VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+	VENDOR_ELEM_ASSOC_REQ = 13,
 	NUM_VENDOR_ELEM_FRAMES
 };
 
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index a4f954c..9ae95a6 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -41,9 +41,13 @@
 	TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
 	TLS_FAIL_BAD_CERTIFICATE = 7,
 	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
-	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9
+	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+	TLS_FAIL_DOMAIN_MISMATCH = 10,
 };
 
+
+#define TLS_MAX_ALT_SUBJECT 10
+
 union tls_event_data {
 	struct {
 		int depth;
@@ -59,6 +63,8 @@
 		const struct wpabuf *cert;
 		const u8 *hash;
 		size_t hash_len;
+		const char *altsubject[TLS_MAX_ALT_SUBJECT];
+		int num_altsubject;
 	} peer_cert;
 
 	struct {
@@ -102,7 +108,11 @@
  * @altsubject_match: String to match in the alternative subject of the peer
  * certificate or %NULL to allow all alternative subjects
  * @suffix_match: String to suffix match in the dNSName or CN of the peer
- * certificate or %NULL to allow all domain names
+ * certificate or %NULL to allow all domain names. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
  * @client_cert: File or reference name for client X.509 certificate in PEM or
  * DER format
  * @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -146,6 +156,7 @@
 	const char *subject_match;
 	const char *altsubject_match;
 	const char *suffix_match;
+	const char *domain_match;
 	const char *client_cert;
 	const u8 *client_cert_blob;
 	size_t client_cert_blob_len;
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index f2eacb5..65db6fc 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -58,6 +58,7 @@
 	gnutls_certificate_credentials_t xcred;
 
 	char *suffix_match;
+	char *domain_match;
 	unsigned int flags;
 };
 
@@ -280,6 +281,7 @@
 	wpabuf_free(conn->push_buf);
 	wpabuf_free(conn->pull_buf);
 	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
 	os_free(conn);
 }
 
@@ -363,6 +365,21 @@
 			return -1;
 	}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (params->domain_match) {
+		conn->domain_match = os_strdup(params->domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+#else /* < 3.3.0 */
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported");
+		return -1;
+	}
+#endif /* >= 3.3.0 */
+
 	conn->flags = params->flags;
 
 	if (params->openssl_ciphers) {
@@ -1111,6 +1128,25 @@
 				goto out;
 			}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+			if (conn->domain_match &&
+			    !gnutls_x509_crt_check_hostname2(
+				    cert, conn->domain_match,
+				    GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain match '%s' not found",
+					   conn->domain_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain mismatch",
+					TLS_FAIL_DOMAIN_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* >= 3.3.0 */
+
 			/* TODO: validate altsubject_match.
 			 * For now, any such configuration is rejected in
 			 * tls_connection_set_params() */
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 86375d1..0c955da 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -205,6 +205,11 @@
 		return -1;
 	}
 
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+		return -1;
+	}
+
 	if (params->openssl_ciphers) {
 		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
 		return -1;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 5433ebb..e3ca068 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -96,7 +96,7 @@
 	ENGINE *engine;        /* functional reference to the engine */
 	EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
-	char *subject_match, *altsubject_match, *suffix_match;
+	char *subject_match, *altsubject_match, *suffix_match, *domain_match;
 	int read_alerts, write_alerts, failed;
 
 	tls_session_ticket_cb session_ticket_cb;
@@ -1098,6 +1098,7 @@
 	os_free(conn->subject_match);
 	os_free(conn->altsubject_match);
 	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
 	os_free(conn->session_ticket);
 	os_free(conn);
 }
@@ -1190,7 +1191,8 @@
 
 
 #ifndef CONFIG_NATIVE_WINDOWS
-static int domain_suffix_match(const u8 *val, size_t len, const char *match)
+static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+			       int full)
 {
 	size_t i, match_len;
 
@@ -1203,7 +1205,7 @@
 	}
 
 	match_len = os_strlen(match);
-	if (match_len > len)
+	if (match_len > len || (full && match_len != len))
 		return 0;
 
 	if (os_strncasecmp((const char *) val + len - match_len, match,
@@ -1222,7 +1224,7 @@
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
-static int tls_match_suffix(X509 *cert, const char *match)
+static int tls_match_suffix(X509 *cert, const char *match, int full)
 {
 #ifdef CONFIG_NATIVE_WINDOWS
 	/* wincrypt.h has conflicting X509_NAME definition */
@@ -1235,7 +1237,8 @@
 	int dns_name = 0;
 	X509_NAME *name;
 
-	wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match);
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "": "suffix ", match);
 
 	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 
@@ -1248,8 +1251,10 @@
 				  gen->d.dNSName->data,
 				  gen->d.dNSName->length);
 		if (domain_suffix_match(gen->d.dNSName->data,
-					gen->d.dNSName->length, match) == 1) {
-			wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found");
+					gen->d.dNSName->length, match, full) ==
+		    1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
 			return 1;
 		}
 	}
@@ -1276,13 +1281,16 @@
 			continue;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
 				  cn->data, cn->length);
-		if (domain_suffix_match(cn->data, cn->length, match) == 1) {
-			wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found");
+		if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+		{
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
 			return 1;
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found");
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "": "suffix ");
 	return 0;
 #endif /* CONFIG_NATIVE_WINDOWS */
 }
@@ -1377,6 +1385,11 @@
 	struct wpabuf *cert = NULL;
 	union tls_event_data ev;
 	struct tls_context *context = conn->context;
+	char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_altsubject = 0;
+	GENERAL_NAME *gen;
+	void *ext;
+	stack_index_t i;
 #ifdef CONFIG_SHA256
 	u8 hash[32];
 #endif /* CONFIG_SHA256 */
@@ -1403,8 +1416,52 @@
 #endif /* CONFIG_SHA256 */
 	ev.peer_cert.depth = depth;
 	ev.peer_cert.subject = subject;
+
+	ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		char *pos;
+
+		if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != GEN_EMAIL &&
+		    gen->type != GEN_DNS &&
+		    gen->type != GEN_URI)
+			continue;
+
+		pos = os_malloc(10 + gen->d.ia5->length + 1);
+		if (pos == NULL)
+			break;
+		altsubject[num_altsubject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+		pos += gen->d.ia5->length;
+		*pos = '\0';
+	}
+
+	for (alt = 0; alt < num_altsubject; alt++)
+		ev.peer_cert.altsubject[alt] = altsubject[alt];
+	ev.peer_cert.num_altsubject = num_altsubject;
+
 	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
 	wpabuf_free(cert);
+	for (alt = 0; alt < num_altsubject; alt++)
+		os_free(altsubject[alt]);
 }
 
 
@@ -1416,7 +1473,7 @@
 	SSL *ssl;
 	struct tls_connection *conn;
 	struct tls_context *context;
-	char *match, *altmatch, *suffix_match;
+	char *match, *altmatch, *suffix_match, *domain_match;
 	const char *err_str;
 
 	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -1444,6 +1501,7 @@
 	match = conn->subject_match;
 	altmatch = conn->altsubject_match;
 	suffix_match = conn->suffix_match;
+	domain_match = conn->domain_match;
 
 	if (!preverify_ok && !conn->ca_cert_verify)
 		preverify_ok = 1;
@@ -1513,13 +1571,21 @@
 				       "AltSubject mismatch",
 				       TLS_FAIL_ALTSUBJECT_MISMATCH);
 	} else if (depth == 0 && suffix_match &&
-		   !tls_match_suffix(err_cert, suffix_match)) {
+		   !tls_match_suffix(err_cert, suffix_match, 0)) {
 		wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
 			   suffix_match);
 		preverify_ok = 0;
 		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 				       "Domain suffix mismatch",
 				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+	} else if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
 	} else
 		openssl_tls_cert_event(conn, err_cert, depth, buf);
 
@@ -1783,7 +1849,8 @@
 static int tls_connection_set_subject_match(struct tls_connection *conn,
 					    const char *subject_match,
 					    const char *altsubject_match,
-					    const char *suffix_match)
+					    const char *suffix_match,
+					    const char *domain_match)
 {
 	os_free(conn->subject_match);
 	conn->subject_match = NULL;
@@ -1809,6 +1876,14 @@
 			return -1;
 	}
 
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -3273,7 +3348,8 @@
 	if (tls_connection_set_subject_match(conn,
 					     params->subject_match,
 					     params->altsubject_match,
-					     params->suffix_match))
+					     params->suffix_match,
+					     params->domain_match))
 		return -1;
 
 	if (engine_id && ca_cert_id) {
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
index a43b487..31a2c94 100644
--- a/src/crypto/tls_schannel.c
+++ b/src/crypto/tls_schannel.c
@@ -707,6 +707,11 @@
 		return -1;
 	}
 
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+		return -1;
+	}
+
 	if (params->openssl_ciphers) {
 		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
 		return -1;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 2c0c685..b8a7c51 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1040,6 +1040,7 @@
 	 * See NL80211_MESHCONF_* for all the mesh config parameters.
 	 */
 	unsigned int flags;
+	int peer_link_timeout;
 };
 
 struct wpa_driver_mesh_join_params {
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index c180f15..3ed9851 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -4214,6 +4214,48 @@
 }
 
 
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+				    struct wpa_driver_associate_params *params)
+{
+	if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+		return -1;
+
+	if (params->htcaps && params->htcaps_mask) {
+		int sz = sizeof(struct ieee80211_ht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+			    params->htcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+			    params->htcaps) ||
+		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+			    params->htcaps_mask))
+			return -1;
+	}
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (params->disable_vht) {
+		wpa_printf(MSG_DEBUG, "  * VHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+			return -1;
+	}
+
+	if (params->vhtcaps && params->vhtcaps_mask) {
+		int sz = sizeof(struct ieee80211_vht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+			    params->vhtcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+			    params->vhtcaps) ||
+		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+			    params->vhtcaps_mask))
+			return -1;
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	return 0;
+}
+
+
 static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
 				   struct wpa_driver_associate_params *params)
 {
@@ -4274,6 +4316,9 @@
 			goto fail;
 	}
 
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -4455,41 +4500,9 @@
 			return -1;
 	}
 
-	if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
 		return -1;
 
-	if (params->htcaps && params->htcaps_mask) {
-		int sz = sizeof(struct ieee80211_ht_capabilities);
-		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
-		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
-			    params->htcaps_mask, sz);
-		if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
-			    params->htcaps) ||
-		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-			    params->htcaps_mask))
-			return -1;
-	}
-
-#ifdef CONFIG_VHT_OVERRIDES
-	if (params->disable_vht) {
-		wpa_printf(MSG_DEBUG, "  * VHT disabled");
-		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
-			return -1;
-	}
-
-	if (params->vhtcaps && params->vhtcaps_mask) {
-		int sz = sizeof(struct ieee80211_vht_capabilities);
-		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
-		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
-			    params->vhtcaps_mask, sz);
-		if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
-			    params->vhtcaps) ||
-		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-			    params->vhtcaps_mask))
-			return -1;
-	}
-#endif /* CONFIG_VHT_OVERRIDES */
-
 	if (params->p2p)
 		wpa_printf(MSG_DEBUG, "  * P2P group");
 
@@ -5219,6 +5232,8 @@
 
 	data.inactive_msec = (unsigned long) -1;
 	ret = i802_read_sta_data(priv, &data, addr);
+	if (ret == -ENOENT)
+		return -ENOENT;
 	if (ret || data.inactive_msec == (unsigned long) -1)
 		return -1;
 	return data.inactive_msec / 1000;
@@ -7818,7 +7833,8 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	struct nlattr *container;
-	int ret = 0;
+	int ret = -1;
+	u32 timeout;
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
@@ -7866,6 +7882,22 @@
 	    nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
 			params->max_peer_links))
 		goto fail;
+
+	/*
+	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+	 * the timer could disconnect stations even in that case.
+	 *
+	 * Set 0xffffffff instead of 0 because NL80211_MESHCONF_PLINK_TIMEOUT
+	 * does not allow 0.
+	 */
+	timeout = params->conf.peer_link_timeout;
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) || timeout == 0)
+		timeout = 0xffffffff;
+	if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, timeout)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+		goto fail;
+	}
+
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 5c71603..6e52bde 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -520,11 +520,11 @@
 				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
 		}
 		drv->extended_capa_mask =
-			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
 		if (drv->extended_capa_mask) {
 			os_memcpy(drv->extended_capa_mask,
-				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
 		} else {
 			os_free(drv->extended_capa);
 			drv->extended_capa = NULL;
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index c35b4d7..3911f48 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -266,7 +266,7 @@
 				goto fail;
 
 			if (wpa_driver_nl80211_scan(bss, params)) {
-				wpa_driver_nl80211_set_mode(bss, drv->nlmode);
+				wpa_driver_nl80211_set_mode(bss, old_mode);
 				goto fail;
 			}
 
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 31c1a29..62cd4a1 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1858,6 +1858,8 @@
 		sm->eapol_cb->notify_cert(sm->eapol_ctx,
 					  data->peer_cert.depth,
 					  data->peer_cert.subject,
+					  data->peer_cert.altsubject,
+					  data->peer_cert.num_altsubject,
 					  hash_hex, data->peer_cert.cert);
 		break;
 	case TLS_ALERT:
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index bc207e7..8c4a42f 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -228,10 +228,13 @@
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 * @depth: Depth in certificate chain (0 = server)
 	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
 	 * @cert_hash: SHA-256 hash of the certificate
 	 * @cert: Peer certificate
 	 */
 	void (*notify_cert)(void *ctx, int depth, const char *subject,
+			    const char *altsubject[], int num_altsubject,
 			    const char *cert_hash, const struct wpabuf *cert);
 
 	/**
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 826ddca..903412d 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -230,6 +230,21 @@
 	char *domain_suffix_match;
 
 	/**
+	 * domain_match - Constraint for server domain name
+	 *
+	 * If set, this FQDN is used as a full match requirement for the
+	 * server certificate in SubjectAltName dNSName element(s). If a
+	 * matching dNSName is found, this constraint is met. If no dNSName
+	 * values are present, this constraint is matched against SubjectName CN
+	 * using same full match comparison. This behavior is similar to
+	 * domain_suffix_match, but has the requirement of a full match, i.e.,
+	 * no subdomains or wildcard matches are allowed. Case-insensitive
+	 * comparison is used, so "Example.com" matches "example.com", but would
+	 * not match "test.Example.com".
+	 */
+	char *domain_match;
+
+	/**
 	 * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
 	 *
 	 * This file can have one or more trusted CA certificates. If ca_cert2
@@ -333,6 +348,14 @@
 	char *domain_suffix_match2;
 
 	/**
+	 * domain_match2 - Constraint for server domain name
+	 *
+	 * This field is like domain_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *domain_match2;
+
+	/**
 	 * eap_methods - Allowed EAP methods
 	 *
 	 * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 3641a2c..8710781 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -91,6 +91,7 @@
 	params->subject_match = (char *) config->subject_match;
 	params->altsubject_match = (char *) config->altsubject_match;
 	params->suffix_match = config->domain_suffix_match;
+	params->domain_match = config->domain_match;
 	params->engine = config->engine;
 	params->engine_id = config->engine_id;
 	params->pin = config->pin;
@@ -113,6 +114,7 @@
 	params->subject_match = (char *) config->subject_match2;
 	params->altsubject_match = (char *) config->altsubject_match2;
 	params->suffix_match = config->domain_suffix_match2;
+	params->domain_match = config->domain_match2;
 	params->engine = config->engine2;
 	params->engine_id = config->engine2_id;
 	params->pin = config->pin2;
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 6fbc27b..b5c028b 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -995,6 +995,7 @@
 				 resp, out_data)) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
 			   "frame");
+		wpabuf_free(resp);
 		return -1;
 	}
 	wpabuf_free(resp);
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 56ac7f4..6745100 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -819,6 +819,9 @@
 	encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
 	wpabuf_free(plain);
 
+	if (!encr)
+		return -1;
+
 	if (data->ssl.tls_out && piggyback) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data "
 			   "(len=%d) with last Phase 1 Message (len=%d "
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
index 0209fad..9e9dc93 100644
--- a/src/eap_server/eap_server_methods.c
+++ b/src/eap_server/eap_server_methods.c
@@ -153,7 +153,7 @@
  * eap_server_get_name - Get EAP method name for the given EAP type
  * @vendor: EAP Vendor-Id (0 = IETF)
  * @type: EAP method type
- * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ * Returns: EAP method name, e.g., TLS, or "unknown" if not found
  *
  * This function maps EAP type numbers into EAP type names based on the list of
  * EAP methods included in the build.
@@ -167,5 +167,5 @@
 		if (m->vendor == vendor && m->method == type)
 			return m->name;
 	}
-	return NULL;
+	return "unknown";
 }
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 941a269..621318e 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1962,13 +1962,14 @@
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
-				 const char *cert_hash,
+				 const char *altsubject[],
+				 int num_altsubject, const char *cert_hash,
 				 const struct wpabuf *cert)
 {
 	struct eapol_sm *sm = ctx;
 	if (sm->ctx->cert_cb)
-		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
-				 cert_hash, cert);
+		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+				 num_altsubject, cert_hash, cert);
 }
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index e089e88..d8ae9d4 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -248,10 +248,13 @@
 	 * @ctx: Callback context (ctx)
 	 * @depth: Depth in certificate chain (0 = server)
 	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
 	 * @cert_hash: SHA-256 hash of the certificate
 	 * @cert: Peer certificate
 	 */
 	void (*cert_cb)(void *ctx, int depth, const char *subject,
+			const char *altsubject[], int num_altsubject,
 			const char *cert_hash, const struct wpabuf *cert);
 
 	/**
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 1382c53..34f5685 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -658,7 +658,8 @@
 	}
 
 	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
-		if (conf->acct_server == NULL || radius->acct_sock < 0) {
+		if (conf->acct_server == NULL || radius->acct_sock < 0 ||
+		    conf->acct_server->shared_secret == NULL) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
@@ -672,7 +673,8 @@
 		s = radius->acct_sock;
 		conf->acct_server->requests++;
 	} else {
-		if (conf->auth_server == NULL || radius->auth_sock < 0) {
+		if (conf->auth_server == NULL || radius->auth_sock < 0 ||
+		    conf->auth_server->shared_secret == NULL) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 9655f4c..39ceea8 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -42,6 +42,7 @@
 		RADIUS_ATTR_CALLING_STATION_ID,
 		RADIUS_ATTR_NAS_IDENTIFIER,
 		RADIUS_ATTR_ACCT_SESSION_ID,
+		RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
 		RADIUS_ATTR_EVENT_TIMESTAMP,
 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
@@ -129,6 +130,12 @@
 		attrs.acct_session_id_len = len;
 	}
 
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_multi_session_id = buf;
+		attrs.acct_multi_session_id_len = len;
+	}
+
 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 				    &buf, &len, NULL) == 0) {
 		attrs.cui = buf;
@@ -147,6 +154,12 @@
 			   "%s:%d", abuf, from_port);
 		error = 503;
 		break;
+	case RADIUS_DAS_MULTI_SESSION_MATCH:
+		wpa_printf(MSG_INFO,
+			   "DAS: Multiple sessions match for request from %s:%d",
+			   abuf, from_port);
+		error = 508;
+		break;
 	case RADIUS_DAS_SUCCESS:
 		error = 0;
 		break;
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index e3ed540..ce731d4 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -14,7 +14,8 @@
 enum radius_das_res {
 	RADIUS_DAS_SUCCESS,
 	RADIUS_DAS_NAS_MISMATCH,
-	RADIUS_DAS_SESSION_NOT_FOUND
+	RADIUS_DAS_SESSION_NOT_FOUND,
+	RADIUS_DAS_MULTI_SESSION_MATCH,
 };
 
 struct radius_das_attrs {
@@ -30,6 +31,8 @@
 	size_t user_name_len;
 	const u8 *acct_session_id;
 	size_t acct_session_id_len;
+	const u8 *acct_multi_session_id;
+	size_t acct_multi_session_id_len;
 	const u8 *cui;
 	size_t cui_len;
 };
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 9f5a90c..89957b1 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -247,22 +247,48 @@
 
 static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
-		   wps->wps->ap_auth_type);
+	u16 auth_type = wps->wps->ap_auth_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Authentication Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	if (auth_type & WPS_AUTH_WPA2PSK)
+		auth_type = WPS_AUTH_WPA2PSK;
+	else if (auth_type & WPS_AUTH_WPAPSK)
+		auth_type = WPS_AUTH_WPAPSK;
+	else if (auth_type & WPS_AUTH_OPEN)
+		auth_type = WPS_AUTH_OPEN;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, wps->wps->ap_auth_type);
+	wpabuf_put_be16(msg, auth_type);
 	return 0;
 }
 
 
 static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
-		   wps->wps->ap_encr_type);
+	u16 encr_type = wps->wps->ap_encr_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Encryption Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+		if (encr_type & WPS_ENCR_AES)
+			encr_type = WPS_ENCR_AES;
+		else if (encr_type & WPS_ENCR_TKIP)
+			encr_type = WPS_ENCR_TKIP;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, wps->wps->ap_encr_type);
+	wpabuf_put_be16(msg, encr_type);
 	return 0;
 }