diff --git a/external/wpa_supplicant/src/ap/ap_drv_ops.c b/external/wpa_supplicant/src/ap/ap_drv_ops.c index 34104e1ed7..cce15ce8b6 100644 --- a/external/wpa_supplicant/src/ap/ap_drv_ops.c +++ b/external/wpa_supplicant/src/ap/ap_drv_ops.c @@ -36,6 +36,11 @@ u32 hostapd_sta_flags_to_drv(u32 flags) if (flags & WLAN_STA_MFP) { res |= WPA_STA_MFP; } + if (flags & WLAN_STA_AUTH) + res |= WPA_STA_AUTHENTICATED; + if (flags & WLAN_STA_ASSOC) + res |= WPA_STA_ASSOCIATED; + return res; } @@ -351,7 +356,13 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u1 return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, reassoc, status, ie, len); } -int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo, u8 vht_opmode) +int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len, u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set) { struct hostapd_sta_add_params params; @@ -375,6 +386,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capa params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; + params.support_p2p_ps = supp_p2p_ps; + params.set = set; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } diff --git a/external/wpa_supplicant/src/ap/ap_drv_ops.h b/external/wpa_supplicant/src/ap/ap_drv_ops.h index 20a09fbea5..80f243bf9d 100644 --- a/external/wpa_supplicant/src/ap/ap_drv_ops.h +++ b/external/wpa_supplicant/src/ap/ap_drv_ops.h @@ -27,7 +27,12 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, int int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, const u8 *addr, int aid, int val); -int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo, u8 vht_opmode); +int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, + u16 capability, const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set); + int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); diff --git a/external/wpa_supplicant/src/ap/ieee802_11.c b/external/wpa_supplicant/src/ap/ieee802_11.c index 99780baa29..1a6fdff617 100644 --- a/external/wpa_supplicant/src/ap/ieee802_11.c +++ b/external/wpa_supplicant/src/ap/ieee802_11.c @@ -240,16 +240,20 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 return 0; } -static void send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len) +static int send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; - + int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) { - return; + return -1; } reply = (struct ieee80211_mgmt *)buf; @@ -268,10 +272,14 @@ static void send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 * wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long)ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) { - wpa_printf(MSG_INFO, "send_auth_reply: send"); + wpa_printf(MSG_INFO, "send_auth_reply: send failed"); } + else + reply_res = WLAN_STATUS_SUCCESS; os_free(buf); + + return reply_res; } #ifdef CONFIG_IEEE80211R @@ -279,18 +287,26 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 { struct hostapd_data *hapd = ctx; struct sta_info *sta; + int reply_res; + + reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, + auth_transaction, status, ies, ies_len); - send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, status, ies, ies_len); - - if (status != WLAN_STATUS_SUCCESS) { + sta = ap_get_sta(hapd, dst); + if (sta == NULL) { return; } - sta = ap_get_sta(hapd, dst); - if (sta == NULL) { + if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || + status != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; return; } + if (status != WLAN_STATUS_SUCCESS) + return; + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); @@ -342,33 +358,41 @@ static struct wpabuf *auth_build_sae_confirm(struct hostapd_data *hapd, struct s static int auth_sae_send_commit(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, int update) { struct wpabuf *data; - + int reply_res; + data = auth_build_sae_commit(hapd, sta, update); if (data == NULL) { return WLAN_STATUS_UNSPECIFIED_FAILURE; } - send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); + wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } static int auth_sae_send_confirm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid) { struct wpabuf *data; - + int reply_res; + data = auth_build_sae_confirm(hapd, sta); if (data == NULL) { return WLAN_STATUS_UNSPECIFIED_FAILURE; } - send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); + wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } static int use_sae_anti_clogging(struct hostapd_data *hapd) @@ -658,17 +682,22 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, const u8 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { - u16 resp = WLAN_STATUS_SUCCESS; + int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { - if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) { - return; + if (auth_transaction != 1 || + status_code != WLAN_STATUS_SUCCESS) { + resp = -1; + goto remove_sta; } + sta->sae = os_zalloc(sizeof(*sta->sae)); - if (sta->sae == NULL) { - return; + if (!sta->sae) { + resp = -1; + goto remove_sta; } + sta->sae->state = SAE_NOTHING; sta->sae->sync = 0; } @@ -697,7 +726,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con sta->sae->tmp->anti_clogging_token = wpabuf_alloc_copy(pos, end - pos); if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); - return; + + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } /* @@ -707,9 +738,11 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ - if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); + if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); - return; + + goto remove_sta; } sta->sae->state = SAE_COMMITTED; sta->sae->sync = 0; @@ -718,18 +751,21 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con } if (status_code != WLAN_STATUS_SUCCESS) { - return; + goto remove_sta; } resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *)mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); - return; + goto remove_sta; } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); - return; + + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; + } if (resp != WLAN_STATUS_SUCCESS) { @@ -750,7 +786,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm, status=%u)", status_code); if (status_code != WLAN_STATUS_SUCCESS) { - return; + goto remove_sta; } if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, ((u8 *)mgmt) + len - mgmt->u.auth.variable) < 0) { @@ -762,7 +798,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unexpected SAE authentication transaction %u (status=%u)", auth_transaction, status_code); if (status_code != WLAN_STATUS_SUCCESS) { - return; + goto remove_sta; } resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } @@ -771,6 +807,13 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, con if (resp != WLAN_STATUS_SUCCESS) { send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *)"", data ? wpabuf_len(data) : 0); } +remove_sta: + if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + wpabuf_free(data); } @@ -815,7 +858,7 @@ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt * u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; - int res; + int res, reply_res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; @@ -962,6 +1005,46 @@ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt * ap_sta_no_session_timeout(hapd, sta); } + /* + * If the driver supports full AP client state, add a station to the + * driver before sending authentication reply to make sure the driver + * has resources, and not to go through the entire authentication and + * association handshake, and fail it at the end. + * + * If this is not the first transaction, in a multi-step authentication + * algorithm, the station already exists in the driver + * (sta->added_unassoc = 1) so skip it. + * + * In mesh mode, the station was already added to the driver when the + * NEW_PEER_CANDIDATE event is received. + */ + if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + !(hapd->conf->mesh & MESH_ENABLED) && + !(sta->added_unassoc)) { + /* + * If a station that is already associated to the AP, is trying + * to authenticate again, remove the STA entry, in order to make + * sure the STA PS state gets cleared and configuration gets + * updated. To handle this, station's added_unassoc flag is + * cleared once the station has completed association. + */ + hostapd_drv_sta_remove(hapd, sta->addr); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | + WLAN_STA_AUTHORIZED); + + if (hostapd_sta_add(hapd, sta->addr, 0, 0, 0, 0, 0, + NULL, NULL, sta->flags, 0, 0, 0, 0)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + sta->added_unassoc = 1; + } + switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); @@ -1020,7 +1103,15 @@ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt * os_free(radius_cui); hostapd_free_psk_list(psk); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); + reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, + resp_ies_len); + + if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + reply_res != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) @@ -1394,7 +1485,78 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, u16 reason_co } } -static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 status_code, int reassoc, const u8 *ies, size_t ies_len) + +static int add_associated_sta(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; + int set = 1; + + /* + * Remove the STA entry to ensure the STA PS state gets cleared and + * configuration gets updated. This is relevant for cases, such as + * FT-over-the-DS, where a station re-associates back to the same AP but + * skips the authentication flow, or if working with a driver that + * does not support full AP client state. + * + * Skip this if the STA has already completed FT reassociation and the + * TK has been configured since the TX/RX PN must not be reset to 0 for + * the same key. + */ + + if (!sta->added_unassoc && + (!(sta->flags & WLAN_STA_AUTHORIZED) || + !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) { + hostapd_drv_sta_remove(hapd, sta->addr); + wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); + set = 0; + } + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +#endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ + + /* + * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags + * will be set when the ACK frame for the (Re)Association Response frame + * is processed (TX status driver event). + */ + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags | WLAN_STA_ASSOC, sta->qosinfo, + sta->vht_opmode, sta->p2p_ie ? 1 : 0, + set)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, + "Could not %s STA to kernel driver", + set ? "set" : "add"); + + if (sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + + return -1; + } + + sta->added_unassoc = 0; + + return 0; +} + + +static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, + u16 status_code, int reassoc, const u8 *ies, + size_t ies_len) { int send_len; u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; @@ -1505,13 +1667,15 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; } + return WLAN_STATUS_SUCCESS; } static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { u16 capab_info, listen_interval, seq_ctrl, fc; - u16 resp = WLAN_STATUS_SUCCESS; + u16 resp = WLAN_STATUS_SUCCESS, reply_res; const u8 *pos; int left, i; struct sta_info *sta; @@ -1555,6 +1719,13 @@ static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt #ifdef CONFIG_IEEE80211R if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); + + /* + * Mark station as authenticated, to avoid adding station + * entry in the driver as associated and not authenticated + */ + sta->flags |= WLAN_STA_AUTH; + } else #endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { @@ -1661,7 +1832,38 @@ static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt sta->timeout_next = STA_NULLFUNC; fail: - send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + /* + * In case of a successful response, add the station to the driver. + * Otherwise, the kernel may ignore Data frames before we process the + * ACK frame (TX status). In case of a failure, this station will be + * removed. + * + * Note that this is not compliant with the IEEE 802.11 standard that + * states that a non-AP station should transition into the + * authenticated/associated state only after the station acknowledges + * the (Re)Association Response frame. However, still do this as: + * + * 1. In case the station does not acknowledge the (Re)Association + * Response frame, it will be removed. + * 2. Data frames will be dropped in the kernel until the station is + * set into authorized state, and there are no significant known + * issues with processing other non-Data Class 3 frames during this + * window. + */ + if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + + reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + /* + * Remove the station in case tranmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } static void handle_disassoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) @@ -1697,6 +1899,7 @@ static void handle_disassoc(struct hostapd_data *hapd, const struct ieee80211_mg } ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { sta->timeout_next = STA_DEAUTH; @@ -1982,29 +2185,39 @@ static void handle_auth_cb(struct hostapd_data *hapd, const struct ieee80211_mgm u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long)len); + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); return; } auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); + goto fail; + } - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); - return; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long)len); + goto fail; } if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; + if (sta->added_unassoc) + hostapd_set_sta_flags(hapd, sta); + return; + } + +fail: + if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; } } @@ -2030,13 +2243,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mg u16 status; struct sta_info *sta; int new_assoc = 1; - struct ieee80211_ht_capabilities ht_cap; - struct ieee80211_vht_capabilities vht_cap; - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { - wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long)len); - return; - } sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2044,16 +2250,30 @@ static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mg return; } - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); - sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + wpa_printf(MSG_INFO, + "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + hostapd_drv_sta_remove(hapd, sta->addr); return; + } - if (reassoc) { + if (reassoc) status = le_to_host16(mgmt->u.reassoc_resp.status_code); - } else { + else status = le_to_host16(mgmt->u.assoc_resp.status_code); + + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + /* The STA is added only in case of SUCCESS */ + if (status == WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); + + return ; } if (status != WLAN_STATUS_SUCCESS) { @@ -2089,32 +2309,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mg sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - /* - * Remove the STA entry in order to make sure the STA PS state gets - * cleared and configuration gets updated in case of reassociation back - * to the same AP. - */ - hostapd_drv_sta_remove(hapd, sta->addr); - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) { - hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); - } -#endif /* CONFIG_IEEE80211N */ -#ifdef CONFIG_IEEE80211AC - if (sta->flags & WLAN_STA_VHT) { - hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); - } -#endif /* CONFIG_IEEE80211AC */ - - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, sta->flags, sta->qosinfo, sta->vht_opmode)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); - - ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); - - return; - } - if (sta->flags & WLAN_STA_WDS) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/external/wpa_supplicant/src/ap/sta_info.c b/external/wpa_supplicant/src/ap/sta_info.c index e0bbe26b77..842bf64141 100644 --- a/external/wpa_supplicant/src/ap/sta_info.c +++ b/external/wpa_supplicant/src/ap/sta_info.c @@ -166,8 +166,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) } ap_sta_ip6addr_del(hapd, sta); - if (!hapd->iface->driver_ap_teardown && !(sta->flags & WLAN_STA_PREAUTH)) { + if (!hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) { hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; } #ifndef CONFIG_NO_VLAN @@ -178,6 +180,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) */ if (hapd->iface->driver_ap_teardown && !(sta->flags & WLAN_STA_PREAUTH)) { hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; } vlan_remove_dynamic(hapd, sta->vlan_id_bound); } @@ -578,6 +581,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR " from kernel driver.", MAC2STR(sta->addr)); return -1; } + sta->added_unassoc = 0; return 0; } diff --git a/external/wpa_supplicant/src/ap/sta_info.h b/external/wpa_supplicant/src/ap/sta_info.h index 9d7fd42e59..27330592db 100644 --- a/external/wpa_supplicant/src/ap/sta_info.h +++ b/external/wpa_supplicant/src/ap/sta_info.h @@ -85,7 +85,8 @@ struct sta_info { unsigned int hs20_deauth_requested: 1; unsigned int session_timeout_set: 1; unsigned int radius_das_match: 1; - + unsigned int added_unassoc:1; + u16 auth_alg; enum { diff --git a/external/wpa_supplicant/src/ap/wpa_auth.c b/external/wpa_supplicant/src/ap/wpa_auth.c index 6d461a1dcd..d4187e82af 100644 --- a/external/wpa_supplicant/src/ap/wpa_auth.c +++ b/external/wpa_supplicant/src/ap/wpa_auth.c @@ -1483,6 +1483,9 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) #else /* CONFIG_IEEE80211R */ break; #endif /* CONFIG_IEEE80211R */ + case WPA_DRV_STA_REMOVED: + sm->tk_already_set = FALSE; + return 0; } #ifdef CONFIG_IEEE80211R @@ -1615,6 +1618,20 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) sm->TimeoutCtr = 0; } +static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm) +{ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, + "WPA: Failed to get random data for ANonce"); + sm->Disconnect = TRUE; + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce, + WPA_NONCE_LEN); + sm->TimeoutCtr = 0; + return 0; +} + SM_STATE(WPA_PTK, INITPMK) { u8 msk[2 * PMK_LEN]; @@ -2086,10 +2103,14 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, AUTHENTICATION); } else if (sm->ReAuthenticationRequest) { SM_ENTER(WPA_PTK, AUTHENTICATION2); - } else if (sm->PTKRequest) { - SM_ENTER(WPA_PTK, PTKSTART); - } else - switch (sm->wpa_ptk_state) { + } + else if (sm->PTKRequest) { + if (wpa_auth_sm_ptk_update(sm) < 0) + SM_ENTER(WPA_PTK, DISCONNECTED); + else + SM_ENTER(WPA_PTK, PTKSTART); + } else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: break; case WPA_PTK_DISCONNECT: @@ -2782,6 +2803,13 @@ int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) return sm->wpa; } +int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm) +{ + if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + return 0; + return sm->tk_already_set; +} + int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { if (sm == NULL || sm->pmksa != entry) { diff --git a/external/wpa_supplicant/src/ap/wpa_auth.h b/external/wpa_supplicant/src/ap/wpa_auth.h index 0d434f9ee3..1708367d53 100644 --- a/external/wpa_supplicant/src/ap/wpa_auth.h +++ b/external/wpa_supplicant/src/ap/wpa_auth.h @@ -227,7 +227,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); typedef enum { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL, WPA_ASSOC_FT + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED } wpa_event; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); @@ -240,6 +240,7 @@ int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry *wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); diff --git a/external/wpa_supplicant/src/ap/wpa_auth_ft.c b/external/wpa_supplicant/src/ap/wpa_auth_ft.c index fe153ee196..0a78d183a6 100644 --- a/external/wpa_supplicant/src/ap/wpa_auth_ft.c +++ b/external/wpa_supplicant/src/ap/wpa_auth_ft.c @@ -705,7 +705,14 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); return; } - + if (sm->tk_already_set) { + /* Must avoid TK reconfiguration to prevent clearing of TX/RX + * PN in the driver */ + wpa_printf(MSG_DEBUG, + "FT: Do not re-install same PTK to the driver"); + return; + } + /* FIX: add STA entry to kernel/driver here? The set_key will fail * most likely without this.. At the moment, STA entry is added only * after association has been completed. This function will be called @@ -718,6 +725,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; + sm->tk_already_set = TRUE; } static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) @@ -805,6 +813,7 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, sm->pairwise = pairwise; sm->PTK_valid = TRUE; + sm->tk_already_set = FALSE; wpa_ft_install_ptk(sm); buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; diff --git a/external/wpa_supplicant/src/ap/wpa_auth_i.h b/external/wpa_supplicant/src/ap/wpa_auth_i.h index 78051032ca..610152e3f2 100644 --- a/external/wpa_supplicant/src/ap/wpa_auth_i.h +++ b/external/wpa_supplicant/src/ap/wpa_auth_i.h @@ -63,6 +63,7 @@ struct wpa_state_machine { struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; + Boolean tk_already_set; int keycount; Boolean Pair; struct wpa_key_replay_counter { diff --git a/external/wpa_supplicant/src/common/qca-vendor.h b/external/wpa_supplicant/src/common/qca-vendor.h index f8fe63c065..d0bca7430d 100644 --- a/external/wpa_supplicant/src/common/qca-vendor.h +++ b/external/wpa_supplicant/src/common/qca-vendor.h @@ -242,11 +242,14 @@ enum qca_wlan_vendor_acs_hw_mode { * after roaming, rather than having the user space wpa_supplicant do it. * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. + * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports + * simultaneous off-channel operations. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, + QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; diff --git a/external/wpa_supplicant/src/common/wpa_common.h b/external/wpa_supplicant/src/common/wpa_common.h index 8f78eaeb14..b1abefc25e 100644 --- a/external/wpa_supplicant/src/common/wpa_common.h +++ b/external/wpa_supplicant/src/common/wpa_common.h @@ -207,8 +207,21 @@ struct wpa_ptk { size_t kck_len; size_t kek_len; size_t tk_len; + int installed; /* 1 if key has already been installed to driver */ }; +struct wpa_gtk { + u8 gtk[WPA_GTK_MAX_LEN]; + size_t gtk_len; +}; + +#ifdef CONFIG_IEEE80211W +struct wpa_igtk { + u8 igtk[WPA_IGTK_MAX_LEN]; + size_t igtk_len; +}; +#endif /* CONFIG_IEEE80211W */ + /* WPA IE version 1 * 00-50-f2:1 (OUI:OUI type) * 0x01 0x00 (version; little endian) diff --git a/external/wpa_supplicant/src/drivers/driver.h b/external/wpa_supplicant/src/drivers/driver.h index 97074626c5..2fcd540949 100644 --- a/external/wpa_supplicant/src/drivers/driver.h +++ b/external/wpa_supplicant/src/drivers/driver.h @@ -1213,8 +1213,16 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL /** Driver supports automatic band selection */ #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL + /** Driver supports simultaneous off-channel operations */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL +/** Driver supports full AP client state */ +#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL + u64 flags; +#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ + (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE) + #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 #define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002 unsigned int smps_modes; @@ -1336,6 +1344,7 @@ struct hostapd_sta_add_params { size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; + int support_p2p_ps; }; struct mac_address { @@ -1430,6 +1439,7 @@ struct wpa_bss_params { #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) #define WPA_STA_AUTHENTICATED BIT(5) +#define WPA_STA_ASSOCIATED BIT(6) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -2277,12 +2287,17 @@ struct wpa_driver_ops { * @params: Station parameters * Returns: 0 on success, -1 on failure * - * This function is used to add a station entry to the driver once the - * station has completed association. This is only used if the driver + * This function is used to add or set (params->set 1) a station + * entry in the driver. Adding STA entries is used only if the driver * does not take care of association processing. * - * With TDLS, this function is also used to add or set (params->set 1) - * TDLS peer entries. + * With drivers that don't support full AP client state, this function + * is used to add a station entry to the driver once the station has + * completed association. + * + * With TDLS, this function is used to add or set (params->set 1) + * TDLS peer entries (even with drivers that do not support full AP + * client state). */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); diff --git a/external/wpa_supplicant/src/drivers/driver_nl80211.c b/external/wpa_supplicant/src/drivers/driver_nl80211.c index 31e6df821b..8397866d3f 100644 --- a/external/wpa_supplicant/src/drivers/driver_nl80211.c +++ b/external/wpa_supplicant/src/drivers/driver_nl80211.c @@ -3270,6 +3270,9 @@ static u32 sta_flags_nl80211(int flags) if (flags & WPA_STA_AUTHENTICATED) { f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); } + if (flags & WPA_STA_ASSOCIATED) + f |= BIT(NL80211_STA_FLAG_ASSOCIATED); + return f; } @@ -3317,7 +3320,19 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params goto fail; } - if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) { + + /* + * Set the below properties only in one of the following cases: + * 1. New station is added, already associated. + * 2. Set WPA_STA_TDLS_PEER station. + * 3. Set an already added unassociated station, if driver supports + * full AP client state. (Set these properties after station became + * associated will be rejected by the driver). + */ + if (!params->set || (params->flags & WPA_STA_TDLS_PEER) || + (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED))) { + wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, params->supp_rates_len); wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates) || nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability)) { @@ -3344,7 +3359,14 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params goto fail; } } + if (is_ap_interface(drv->nlmode) && + nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS, + params->support_p2p_ps ? + NL80211_P2P_PS_SUPPORTED : + NL80211_P2P_PS_UNSUPPORTED)) + goto fail; } + if (!params->set) { if (params->aid) { wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); @@ -3355,9 +3377,13 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params /* * cfg80211 validates that AID is non-zero, so we have * to make this a non-zero value for the TDLS case where - * a dummy STA entry is used for now. + * a dummy STA entry is used for now and for a station + * that is still not associated. */ - wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)", + (params->flags & WPA_STA_TDLS_PEER) ? + "TDLS" : "UNASSOC_STA"); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1)) { goto fail; } @@ -3371,6 +3397,15 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid)) { goto fail; } + } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED)) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) || + nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval)) + goto fail; } if (params->vht_opmode_enabled) { @@ -3397,6 +3432,35 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params os_memset(&upd, 0, sizeof(upd)); upd.set = sta_flags_nl80211(params->flags); upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); + /* + * If the driver doesn't support full AP client state, ignore ASSOC/AUTH + * flags, as nl80211 driver moves a new station, by default, into + * associated state. + * + * On the other hand, if the driver supports that feature and the + * station is added in unauthenticated state, set the + * authenticated/associated bits in the mask to prevent moving this + * station to associated state before it is actually associated. + * + * This is irrelevant for mesh mode where the station is added to the + * driver as authenticated already, and ASSOCIATED isn't part of the + * nl80211 API. + */ + if (!is_mesh_interface(drv->nlmode)) { + if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state"); + upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED)); + } else if (!params->set && + !(params->flags & WPA_STA_TDLS_PEER)) { + if (!(params->flags & WPA_STA_AUTHENTICATED)) + upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (!(params->flags & WPA_STA_ASSOCIATED)) + upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); + } + } + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", upd.set, upd.mask); if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) { goto fail; diff --git a/external/wpa_supplicant/src/drivers/driver_nl80211_capa.c b/external/wpa_supplicant/src/drivers/driver_nl80211_capa.c index ba83558cb2..1a10a89d6d 100644 --- a/external/wpa_supplicant/src/drivers/driver_nl80211_capa.c +++ b/external/wpa_supplicant/src/drivers/driver_nl80211_capa.c @@ -430,6 +430,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, struct nlattr if (flags & NL80211_FEATURE_HT_IBSS) { capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS; } + + if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE) + capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; } static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, struct nlattr *tb) @@ -793,6 +796,9 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) { drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; } + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; + } int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) @@ -872,6 +878,16 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); + /* + * To enable offchannel simultaneous support in wpa_supplicant, the + * underlying driver needs to support the same along with offchannel TX. + * Offchannel TX support is needed since remain_on_channel and + * action_tx use some common data structures and hence cannot be + * scheduled simultaneously. + */ + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; + return 0; } diff --git a/external/wpa_supplicant/src/rsn_supp/tdls.c b/external/wpa_supplicant/src/rsn_supp/tdls.c index a3fda4d92b..98b7670a2e 100644 --- a/external/wpa_supplicant/src/rsn_supp/tdls.c +++ b/external/wpa_supplicant/src/rsn_supp/tdls.c @@ -108,6 +108,7 @@ struct wpa_tdls_peer { u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ } tpk; int tpk_set; + int tk_set; /* TPK-TK configured to the driver */ int tpk_success; int tpk_in_progress; @@ -182,6 +183,20 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) u8 rsc[6]; enum wpa_alg alg; + if (peer->tk_set) { + /* + * This same TPK-TK has already been configured to the driver + * and this new configuration attempt (likely due to an + * unexpected retransmitted frame) would result in clearing + * the TX/RX sequence number which can break security, so must + * not allow that to happen. + */ + wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR + " has already been configured to the driver - do not reconfigure", + MAC2STR(peer->addr)); + return -1; + } + os_memset(rsc, 0, 6); switch (peer->cipher) { @@ -197,10 +212,14 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) return -1; } + wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR, + MAC2STR(peer->addr)); + if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " "driver"); return -1; } + peer->tk_set = 1; return 0; } @@ -594,7 +613,7 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) peer->cipher = 0; peer->qos_info = 0; peer->wmm_capable = 0; - peer->tpk_set = peer->tpk_success = 0; + peer->tk_set = peer->tpk_set = peer->tpk_success = 0; peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); @@ -1032,6 +1051,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, struct wpa_tdls_peer *peer) wpa_tdls_peer_free(sm, peer); return -1; } + peer->tk_set = 0; /* A new nonce results in a new TK */ wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", peer->inonce, WPA_NONCE_LEN); os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); @@ -1536,6 +1556,19 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, i return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid, peer->capability, peer->supp_rates, peer->supp_rates_len, peer->ht_capabilities, peer->vht_capabilities, peer->qos_info, peer->wmm_capable, peer->ext_capab, peer->ext_capab_len, peer->supp_channels, peer->supp_channels_len, peer->supp_oper_classes, peer->supp_oper_classes_len); } +static int tdls_nonce_set(const u8 *nonce) +{ + int i; + + for (i = 0; i < WPA_NONCE_LEN; i++) { + if (nonce[i]) + return 1; + } + + return 0; +} + + static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_tdls_peer *peer; @@ -1776,7 +1809,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 || + !tdls_nonce_set(peer->inonce)) { /* * There is no point in updating the RNonce for every obtained * TPK M1 frame (e.g., retransmission due to timeout) with the @@ -1790,6 +1824,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const wpa_msg(sm->ctx->ctx, MSG_WARNING, "TDLS: Failed to get random data for responder nonce"); goto error; } + peer->tk_set = 0; /* A new nonce results in a new TK */ } #if 0 /* get version info from RSNIE received from Peer */ diff --git a/external/wpa_supplicant/src/rsn_supp/wpa.c b/external/wpa_supplicant/src/rsn_supp/wpa.c index d8578d6a4f..7f17ec3df8 100644 --- a/external/wpa_supplicant/src/rsn_supp/wpa.c +++ b/external/wpa_supplicant/src/rsn_supp/wpa.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2015, Jouni Malinen + * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,6 +23,8 @@ #include "wpa_ie.h" #include "peerkey.h" +static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -33,9 +36,13 @@ * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: >= 0 on success, < 0 on failure */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) { + int ret = -1; #ifdef CONFIG_SUPPLICANT_EXCESS_LOG size_t mic_len = wpa_mic_len(sm->key_mgmt); #endif @@ -59,10 +66,12 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, int ve wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); - wpa_sm_ether_send(sm, dest, proto, msg, msg_len); +// wpa_sm_ether_send(sm, dest, proto, msg, msg_len); + ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); + return ret; } /** @@ -268,7 +277,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_ad * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { @@ -345,9 +354,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, cons os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ++ ETH_P_EAPOL, rbuf, rlen, key_mic); } static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) @@ -421,7 +429,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char os_memset(buf, 0, sizeof(buf)); } sm->tptk_set = 1; - + kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; @@ -444,7 +452,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char } #endif /* CONFIG_P2P */ - if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, kde, kde_len, ptk)) { + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + kde, kde_len, ptk) < 0) { goto failed; } @@ -509,7 +518,12 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_ int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; - u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (sm->ptk.installed) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Do not re-install same PTK to the driver"); + return 0; + } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); @@ -541,7 +555,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_ /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); - + sm->ptk.installed = 1; + if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, sm, NULL); @@ -576,11 +591,24 @@ struct wpa_gtk_data { int gtk_len; }; -static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, const u8 *key_rsc) +static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, + const u8 *key_rsc, int wnm_sleep) { const u8 *_gtk = gd->gtk; u8 gtk_buf[32]; + /* Detect possible key reinstallation */ + if ((sm->gtk.gtk_len == (size_t) gd->gtk_len && + os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) || + (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len && + os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk, + sm->gtk_wnm_sleep.gtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)", + gd->keyidx, gd->tx, gd->gtk_len); + return 0; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); @@ -604,6 +632,15 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_da } os_memset(gtk_buf, 0, sizeof(gtk_buf)); + if (wnm_sleep) { + sm->gtk_wnm_sleep.gtk_len = gd->gtk_len; + os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk, + sm->gtk_wnm_sleep.gtk_len); + } else { + sm->gtk.gtk_len = gd->gtk_len; + os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len); + } + return 0; } @@ -621,10 +658,40 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, int tx) return tx; } +static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, + const u8 *rsc) +{ + int rsclen; + + if (!sm->wpa_rsc_relaxation) + return 0; + + rsclen = wpa_cipher_rsc_len(sm->group_cipher); + + /* + * Try to detect RSC (endian) corruption issue where the AP sends + * the RSC bytes in EAPOL-Key message in the wrong order, both if + * it's actually a 6-byte field (as it should be) and if it treats + * it as an 8-byte field. + * An AP model known to have this bug is the Sapido RB-1632. + */ + if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", + rsc[0], rsc[1], rsc[2], rsc[3], + rsc[4], rsc[5], rsc[6], rsc[7]); + + return 1; + } + + return 0; +} + + static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; - + const u8 *key_rsc; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x * GTK KDE format: @@ -648,7 +715,14 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + + + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && + (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); os_memset(&gd, 0, sizeof(gd)); return -1; @@ -659,6 +733,58 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol return 0; } +#ifdef CONFIG_IEEE80211W +static int wpa_supplicant_install_igtk(struct wpa_sm *sm, + const struct wpa_igtk_kde *igtk, + int wnm_sleep) +{ + size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); + u16 keyidx = WPA_GET_LE16(igtk->keyid); + + /* Detect possible key reinstallation */ + if ((sm->igtk.igtk_len == len && + os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) || + (sm->igtk_wnm_sleep.igtk_len == len && + os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk, + sm->igtk_wnm_sleep.igtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)", + keyidx); + return 0; + } + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x", + keyidx, MAC2STR(igtk->pn)); + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); + if (keyidx > 4095) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); + return -1; + } + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } + + if (wnm_sleep) { + sm->igtk_wnm_sleep.igtk_len = len; + os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk, + sm->igtk_wnm_sleep.igtk_len); + } else { + sm->igtk.igtk_len = len; + os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len); + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { #ifdef CONFIG_IEEE80211W @@ -669,23 +795,15 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) if (ie->igtk) { size_t len; const struct wpa_igtk_kde *igtk; - u16 keyidx; + len = wpa_cipher_key_len(sm->mgmt_group_cipher); if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) { return -1; } igtk = (const struct wpa_igtk_kde *)ie->igtk; - keyidx = WPA_GET_LE16(igtk->keyid); - wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d " "pn %02x%02x%02x%02x%02x%02x", keyidx, MAC2STR(igtk->pn)); - wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); - if (keyidx > 4095) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KeyID %d", keyidx); - return -1; - } - if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, len) < 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure IGTK to the driver"); - return -1; - } + + if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0) + return -1; } return 0; @@ -865,7 +983,7 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, const unsigned char *sr * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, struct wpa_ptk *ptk) { @@ -901,9 +1019,10 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, cons } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, key_mic); + } static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, size_t key_data_len) @@ -957,7 +1076,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_ea } #endif /* CONFIG_P2P */ - if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk)) { + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + &sm->ptk) < 0) { goto failed; } @@ -1140,9 +1260,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, const struct wpa_eapol_ } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, + sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len, u16 ver) @@ -1150,6 +1269,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char u16 key_info; int rekey, ret; struct wpa_gtk_data gd; + const u8 *key_rsc; if (!sm->msg_3_of_4_ok) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group Key Handshake started prior to completion of 4-way handshake"); @@ -1175,7 +1295,12 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char goto failed; } - if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) { + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) || + wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0) { goto failed; } os_memset(&gd, 0, sizeof(gd)); @@ -1749,7 +1874,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) */ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { - int clear_ptk = 1; + int clear_keys = 1; if (sm == NULL) { return; @@ -1776,11 +1901,11 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); - clear_ptk = 0; + clear_keys = 0; } #endif /* CONFIG_IEEE80211R */ - if (clear_ptk) { + if (clear_keys) { /* * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. @@ -1790,7 +1915,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) os_memset(&sm->ptk, 0, sizeof(sm->ptk)); sm->tptk_set = 0; os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + os_memset(&sm->gtk, 0, sizeof(sm->gtk)); + os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); +#ifdef CONFIG_IEEE80211W + os_memset(&sm->igtk, 0, sizeof(sm->igtk)); + os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); +#endif /* CONFIG_IEEE80211W */ } + #ifdef CONFIG_TDLS wpa_tdls_assoc(sm); #endif /* CONFIG_TDLS */ @@ -1820,7 +1952,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ - +#ifdef CONFIG_IEEE80211R + sm->ft_reassoc_completed = 0; +#endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); @@ -1936,6 +2070,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) } sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; + sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -1946,6 +2081,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; + sm->wpa_rsc_relaxation = 0; } } @@ -2292,6 +2428,12 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + os_memset(&sm->gtk, 0, sizeof(sm->gtk)); + os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); +#ifdef CONFIG_IEEE80211W + os_memset(&sm->igtk, 0, sizeof(sm->igtk)); + os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); +#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); @@ -2349,7 +2491,8 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) os_memcpy(gd.gtk, buf + 13, gd.gtk_len); wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", gd.gtk, gd.gtk_len); - if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) { os_memset(&gd, 0, sizeof(gd)); wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; @@ -2357,24 +2500,10 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) os_memset(&gd, 0, sizeof(gd)); #ifdef CONFIG_IEEE80211W } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { - struct wpa_igtk_kde igd; - u16 keyidx; - - os_memset(&igd, 0, sizeof(igd)); - keylen = wpa_cipher_key_len(sm->mgmt_group_cipher); - os_memcpy(igd.keyid, buf + 2, 2); - os_memcpy(igd.pn, buf + 4, 6); - - keyidx = WPA_GET_LE16(igd.keyid); - os_memcpy(igd.igtk, buf + 10, keylen); - - wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", igd.igtk, keylen); - if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), broadcast_ether_addr, keyidx, 0, igd.pn, sizeof(igd.pn), igd.igtk, keylen) < 0) { - wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " "WNM mode"); - os_memset(&igd, 0, sizeof(igd)); - return -1; - } - os_memset(&igd, 0, sizeof(igd)); + const struct wpa_igtk_kde *igtk; + igtk = (const struct wpa_igtk_kde *) (buf + 2); + if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) + return -1; #endif /* CONFIG_IEEE80211W */ } else { wpa_printf(MSG_DEBUG, "Unknown element id"); diff --git a/external/wpa_supplicant/src/rsn_supp/wpa.h b/external/wpa_supplicant/src/rsn_supp/wpa.h index 1586da6b58..93fc59920c 100644 --- a/external/wpa_supplicant/src/rsn_supp/wpa.h +++ b/external/wpa_supplicant/src/rsn_supp/wpa.h @@ -77,6 +77,7 @@ struct rsn_supp_config { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; }; #ifndef CONFIG_NO_WPA diff --git a/external/wpa_supplicant/src/rsn_supp/wpa_ft.c b/external/wpa_supplicant/src/rsn_supp/wpa_ft.c index d9fe1fb8a9..2333eabde7 100644 --- a/external/wpa_supplicant/src/rsn_supp/wpa_ft.c +++ b/external/wpa_supplicant/src/rsn_supp/wpa_ft.c @@ -134,6 +134,7 @@ static u8 *wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, u16 capab; sm->ft_completed = 0; + sm->ft_reassoc_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); @@ -596,6 +597,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_le return -1; } + if (sm->ft_reassoc_completed) { + wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission"); + return 0; + } + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; @@ -675,6 +681,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_le return -1; } + sm->ft_reassoc_completed = 1; + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) { return -1; } diff --git a/external/wpa_supplicant/src/rsn_supp/wpa_i.h b/external/wpa_supplicant/src/rsn_supp/wpa_i.h index 6659e42773..5336953873 100644 --- a/external/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/external/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -30,6 +30,12 @@ struct wpa_sm { u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; int rx_replay_counter_set; u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + struct wpa_gtk gtk; + struct wpa_gtk gtk_wnm_sleep; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk igtk; + struct wpa_igtk igtk_wnm_sleep; +#endif /* CONFIG_IEEE80211W */ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ @@ -60,7 +66,8 @@ struct wpa_sm { size_t ssid_len; int wpa_ptk_rekey; int p2p; - + int wpa_rsc_relaxation; + u8 own_addr[ETH_ALEN]; const char *ifname; const char *bridge_ifname; @@ -121,6 +128,7 @@ struct wpa_sm { size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; int ft_completed; + int ft_reassoc_completed; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; @@ -305,7 +313,10 @@ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, const u8 *pmk, size return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic); + int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk); int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, struct wpa_ptk *ptk); diff --git a/external/wpa_supplicant/wpa_supplicant/config.c b/external/wpa_supplicant/wpa_supplicant/config.c index 9a08e6aa24..9d80de7a41 100644 --- a/external/wpa_supplicant/wpa_supplicant/config.c +++ b/external/wpa_supplicant/wpa_supplicant/config.c @@ -3390,6 +3390,7 @@ struct wpa_config *wpa_config_alloc_empty(const char *ctrl_interface, const char config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; config->cert_in_cb = DEFAULT_CERT_IN_CB; + config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION; if (ctrl_interface) { config->ctrl_interface = os_strdup(ctrl_interface); @@ -4016,6 +4017,7 @@ static const struct global_parse_data global_fields[] = { {INT(passive_scan), 0}, {INT(reassoc_same_bss_optim), 0}, {INT(wps_priority), 0}, + { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 }, }; #undef FUNC diff --git a/external/wpa_supplicant/wpa_supplicant/config.h b/external/wpa_supplicant/wpa_supplicant/config.h index 6f62a81ba6..c962a5dfa6 100644 --- a/external/wpa_supplicant/wpa_supplicant/config.h +++ b/external/wpa_supplicant/wpa_supplicant/config.h @@ -34,6 +34,7 @@ #define DEFAULT_KEY_MGMT_OFFLOAD 1 #define DEFAULT_CERT_IN_CB 1 #define DEFAULT_P2P_GO_CTWINDOW 0 +#define DEFAULT_WPA_RSC_RELAXATION 1 #include "config_ssid.h" #include "wps/wps.h" @@ -1182,6 +1183,16 @@ struct wpa_config { * by executing the WPS protocol. */ int wps_priority; + + /** + * wpa_rsc_relaxation - RSC relaxation on GTK installation + * + * Values: + * 0 - use the EAPOL-Key RSC value on GTK installation + * 1 - use the null RSC if a bogus RSC value is detected in message 3 + * of 4-Way Handshake or message 1 of Group Key Handshake. + */ + int wpa_rsc_relaxation; }; /* Prototypes for common functions from config.c */ diff --git a/external/wpa_supplicant/wpa_supplicant/config_file.c b/external/wpa_supplicant/wpa_supplicant/config_file.c index 66b3f5efc0..6572169c9d 100644 --- a/external/wpa_supplicant/wpa_supplicant/config_file.c +++ b/external/wpa_supplicant/wpa_supplicant/config_file.c @@ -1331,6 +1331,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_priority) { fprintf(f, "wps_priority=%d\n", config->wps_priority); } + if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION) + fprintf(f, "wpa_rsc_relaxation=%d\n", + config->wpa_rsc_relaxation); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/external/wpa_supplicant/wpa_supplicant/ctrl_iface.c b/external/wpa_supplicant/wpa_supplicant/ctrl_iface.c index b9e289798d..142650f318 100644 --- a/external/wpa_supplicant/wpa_supplicant/ctrl_iface.c +++ b/external/wpa_supplicant/wpa_supplicant/ctrl_iface.c @@ -6641,6 +6641,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) } eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpa_s->wnmsleep_used = 0; } static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) diff --git a/external/wpa_supplicant/wpa_supplicant/events.c b/external/wpa_supplicant/wpa_supplicant/events.c index a98d73102d..58680e5f14 100644 --- a/external/wpa_supplicant/wpa_supplicant/events.c +++ b/external/wpa_supplicant/wpa_supplicant/events.c @@ -302,6 +302,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->key_mgmt = 0; wpas_rrm_reset(wpa_s); + wpa_s->wnmsleep_used = 0; } static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) diff --git a/external/wpa_supplicant/wpa_supplicant/wnm_sta.c b/external/wpa_supplicant/wpa_supplicant/wnm_sta.c index bb56c99a87..1a0b90e234 100644 --- a/external/wpa_supplicant/wpa_supplicant/wnm_sta.c +++ b/external/wpa_supplicant/wpa_supplicant/wnm_sta.c @@ -33,11 +33,14 @@ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, u16 *b } /* set the TFS IE to driver */ -static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, const u8 *addr, u8 *buf, u16 *buf_len, enum wnm_oper oper) +static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *buf, u16 buf_len, + enum wnm_oper oper) { + u16 len = buf_len; wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); - return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); + return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); } /* MLME-SLEEPMODE.request */ @@ -117,7 +120,9 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 in if (res < 0) { wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " "(action=%d, intval=%d)", action, intval); } - + else + wpa_s->wnmsleep_used = 1; + os_free(wnmsleep_ie); os_free(wnmtfs_ie); os_free(mgmt); @@ -125,7 +130,9 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 in return res; } -static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, u8 *tfsresp_ie_start, u8 *tfsresp_ie_end) +static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, + const u8 *tfsresp_ie_start, + const u8 *tfsresp_ie_end) { wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, wpa_s->bssid, NULL, NULL); /* remove GTK/IGTK ?? */ @@ -136,7 +143,10 @@ static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, u8 *tfsre tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - tfsresp_ie_start; wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); /* pass the TFS Resp IE(s) to driver for processing */ - if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, tfsresp_ie_start, &tfsresp_ie_len, WNM_SLEEP_TFS_RESP_IE_SET)) { + if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, + tfsresp_ie_start, + tfsresp_ie_len, + WNM_SLEEP_TFS_RESP_IE_SET)) { wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); } } @@ -197,14 +207,20 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, const u8 * * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ - u8 *pos = (u8 *)frm; /* point to payload after the action field */ + const u8 *pos = frm; /* point to payload after the action field */ u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ - u8 *tfsresp_ie_start = NULL; - u8 *tfsresp_ie_end = NULL; + const u8 *tfsresp_ie_start = NULL; + const u8 *tfsresp_ie_end = NULL; size_t left; + if (!wpa_s->wnmsleep_used) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); + return; + } + if (len < 3) { return; } @@ -242,7 +258,10 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, const u8 * return; } - if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { + wpa_s->wnmsleep_used = 0; + + if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " "frame (action=%d, intval=%d)", wnmsleep_ie->action_type, wnmsleep_ie->intval); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, tfsresp_ie_end); diff --git a/external/wpa_supplicant/wpa_supplicant/wpa_supplicant_i.h b/external/wpa_supplicant/wpa_supplicant/wpa_supplicant_i.h index a55b203a86..16ea29ac3b 100644 --- a/external/wpa_supplicant/wpa_supplicant/wpa_supplicant_i.h +++ b/external/wpa_supplicant/wpa_supplicant/wpa_supplicant_i.h @@ -648,7 +648,8 @@ struct wpa_supplicant { unsigned int reattach: 1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed: 1; unsigned int added_vif: 1; - + unsigned int wnmsleep_used:1; + struct os_reltime last_mac_addr_change; int last_mac_addr_style; diff --git a/external/wpa_supplicant/wpa_supplicant/wpas_glue.c b/external/wpa_supplicant/wpa_supplicant/wpas_glue.c index f9ce49f4b7..8aff7664f7 100644 --- a/external/wpa_supplicant/wpa_supplicant/wpas_glue.c +++ b/external/wpa_supplicant/wpa_supplicant/wpas_glue.c @@ -993,6 +993,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, struct wpa } } #endif /* CONFIG_P2P */ + conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation; } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); }