From d867e11811eb0e264bb5e62f8904ecddf7027ba9 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 4 Apr 2020 21:50:37 +0300 Subject: [PATCH] FT: Remove and re-add STA entry after FT protocol success with PMF Allow STA entry to be removed and re-added to the driver with PMF is used with FT. Previously, this case resulted in cfg80211 rejecting STA entry update after successful FT protocol use if the association had not been dropped and it could not be dropped for the PMF case in handle_auth(). Signed-off-by: Jouni Malinen --- src/ap/ieee802_11.c | 24 +----------------------- src/ap/sta_info.c | 30 ++++++++++++++++++++++++++++++ src/ap/sta_info.h | 1 + src/ap/wpa_auth.c | 2 +- src/ap/wpa_auth.h | 1 + src/ap/wpa_auth_ft.c | 16 ++++++++++++++-- src/ap/wpa_auth_glue.c | 29 +++++++++++++++++++++++++++++ src/ap/wpa_auth_i.h | 2 +- 8 files changed, 78 insertions(+), 27 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 045a6cbcd..2a5f6e5ec 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2526,32 +2526,10 @@ static void handle_auth(struct hostapd_data *hapd, (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(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. - */ - ap_sta_set_authorized(hapd, sta, 0); - 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, - sta->supported_rates, - sta->supported_rates_len, - 0, NULL, NULL, NULL, 0, - sta->flags, 0, 0, 0, 0)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); + if (ap_sta_re_add(hapd, sta) < 0) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } - - sta->added_unassoc = 1; } switch (auth_alg) { diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 903be28d4..93f1f0c20 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1497,3 +1497,33 @@ int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); } + + +int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) +{ + /* + * 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. + */ + ap_sta_set_authorized(hapd, sta, 0); + 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, + sta->supported_rates, + sta->supported_rates_len, + 0, NULL, NULL, NULL, 0, + sta->flags, 0, 0, 0, 0)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + return -1; + } + + sta->added_unassoc = 1; + return 0; +} diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 8ff6ac62f..308aa29d9 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -358,5 +358,6 @@ void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, struct sta_info *sta); int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, struct sta_info *sta); +int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index e0ffb2718..070236a89 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1836,7 +1836,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) #ifdef CONFIG_IEEE80211R_AP wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration after association"); - wpa_ft_install_ptk(sm); + wpa_ft_install_ptk(sm, 1); /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 868aaa1fa..fafabe9c5 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -301,6 +301,7 @@ struct wpa_auth_callbacks { int *bandwidth, int *seg1_idx); #ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*add_sta_ft)(void *ctx, const u8 *sta_addr); int (*set_vlan)(void *ctx, const u8 *sta_addr, struct vlan_description *vlan); int (*get_vlan)(void *ctx, const u8 *sta_addr, diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 476a2be69..6f6c18eec 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -2747,7 +2747,16 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, } -void wpa_ft_install_ptk(struct wpa_state_machine *sm) +static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (!wpa_auth->cb->add_sta_ft) + return -1; + return wpa_auth->cb->add_sta_ft(wpa_auth->cb_ctx, addr); +} + + +void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry) { enum wpa_alg alg; int klen; @@ -2769,6 +2778,9 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) return; } + if (!retry) + wpa_auth_add_sta_ft(sm->wpa_auth, sm->addr); + /* 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 @@ -3140,7 +3152,7 @@ pmk_r1_derived: sm->pairwise = pairwise; sm->PTK_valid = TRUE; sm->tk_already_set = FALSE; - wpa_ft_install_ptk(sm); + wpa_ft_install_ptk(sm, 0); if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 7a1ed24e8..79880e478 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1038,6 +1038,34 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) } +static int hostapd_wpa_auth_add_sta_ft(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + (sta->flags & WLAN_STA_MFP) && ap_sta_is_authorized(sta) && + !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { + /* We could not do this in handle_auth() since there was a + * PMF-enabled association for the STA and the new + * authentication attempt was not yet fully processed. Now that + * we are ready to configure the TK to the driver, + * authentication has succeeded and we can clean up the driver + * STA entry to avoid issues with any maintained state from the + * previous association. */ + wpa_printf(MSG_DEBUG, + "FT: Remove and re-add driver STA entry after successful FT authentication"); + return ap_sta_re_add(hapd, sta); + } + + return 0; +} + + static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr, struct vlan_description *vlan) { @@ -1399,6 +1427,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211R_AP .send_ft_action = hostapd_wpa_auth_send_ft_action, .add_sta = hostapd_wpa_auth_add_sta, + .add_sta_ft = hostapd_wpa_auth_add_sta_ft, .add_tspec = hostapd_wpa_auth_add_tspec, .set_vlan = hostapd_wpa_auth_set_vlan, .get_vlan = hostapd_wpa_auth_get_vlan, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index bc59d6a4c..813612e74 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -300,7 +300,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); -void wpa_ft_install_ptk(struct wpa_state_machine *sm); +void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry); int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0, const u8 *pmk_r0_name); #endif /* CONFIG_IEEE80211R_AP */