diff --git a/components/mlme/dispatcher/inc/wlan_mlme_api.h b/components/mlme/dispatcher/inc/wlan_mlme_api.h index b7a410e0a1..1c673fdb05 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_api.h +++ b/components/mlme/dispatcher/inc/wlan_mlme_api.h @@ -2520,6 +2520,15 @@ wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, void wlan_mlme_get_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, struct wlan_mlme_sae_single_pmk *pmksa); + +/** + * wlan_mlme_is_sae_single_pmk_enabled() - Get is SAE single pmk feature enabled + * @psoc: Pointer to Global psoc + * + * Return: True if SAE single PMK is enabled + */ +bool wlan_mlme_is_sae_single_pmk_enabled(struct wlan_objmgr_psoc *psoc); + /** * wlan_mlme_clear_sae_single_pmk_info - API to clear mlme_pmkid_info ap caps * @vdev: vdev object @@ -2536,6 +2545,12 @@ wlan_mlme_set_sae_single_pmk_bss_cap(struct wlan_objmgr_psoc *psoc, { } +static inline +bool wlan_mlme_is_sae_single_pmk_enabled(struct wlan_objmgr_psoc *psoc) +{ + return false; +} + static inline void wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, struct mlme_pmk_info *sae_single_pmk) diff --git a/components/mlme/dispatcher/src/wlan_mlme_api.c b/components/mlme/dispatcher/src/wlan_mlme_api.c index d988894727..b5c63d827d 100644 --- a/components/mlme/dispatcher/src/wlan_mlme_api.c +++ b/components/mlme/dispatcher/src/wlan_mlme_api.c @@ -3936,6 +3936,17 @@ void wlan_mlme_update_sae_single_pmk(struct wlan_objmgr_vdev *vdev, mlme_priv->mlme_roam.sae_single_pmk.pmk_info = *sae_single_pmk; } +bool wlan_mlme_is_sae_single_pmk_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_mlme_psoc_ext_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_ext_obj(psoc); + if (!mlme_obj) + return cfg_default(CFG_SAE_SINGLE_PMK); + + return mlme_obj->cfg.lfr.sae_single_pmk_feature_enabled; +} + void wlan_mlme_get_sae_single_pmk_info(struct wlan_objmgr_vdev *vdev, struct wlan_mlme_sae_single_pmk *pmksa) { diff --git a/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_fw_sync.c b/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_fw_sync.c index 94b93d6f32..1bba907806 100644 --- a/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_fw_sync.c +++ b/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_fw_sync.c @@ -478,7 +478,7 @@ static QDF_STATUS cm_process_roam_keys(struct wlan_objmgr_vdev *vdev, if (roaming_info->auth_status == ROAM_AUTH_STATUS_AUTHENTICATED || QDF_HAS_PARAM(akm, WLAN_CRYPTO_KEY_MGMT_SAE) || QDF_HAS_PARAM(akm, WLAN_CRYPTO_KEY_MGMT_OWE)) { - struct wlan_crypto_pmksa *pmkid_cache; + struct wlan_crypto_pmksa *pmkid_cache, *pmksa; cm_csr_set_ss_none(vdev_id); /* @@ -513,6 +513,89 @@ static QDF_STATUS cm_process_roam_keys(struct wlan_objmgr_vdev *vdev, if (cm_lookup_pmkid_using_bssid(psoc, vdev_id, pmkid_cache)) { + /* + * Consider two APs: AP1, AP2 + * Both APs configured with EAP 802.1x security mode + * and OKC is enabled in both APs by default. Initially + * DUT successfully associated with AP1, and generated + * PMK1 by performing full EAP and added an entry for + * AP1 in pmk table. At this stage, pmk table has only + * one entry for PMK1 (1. AP1-->PMK1). Now DUT try to + * roam to AP2 using PMK1 (as OKC is enabled) but + * session timeout happens on AP2 just before 4 way + * handshake completion in FW. At this point of time + * DUT not in authenticated state. Due to this DUT + * performs full EAP with AP2 and generates PMK2. As + * there is no previous entry of AP2 (AP2-->PMK1) in pmk + * table. When host gets pmk delete command for BSSID of + * AP2, the BSSID match fails. Hence host will not + * delete pmk entry of AP1 as well. + * At this point of time, the PMK table has two entry + * 1. AP1-->PMK1 and 2. AP2 --> PMK2. + * Ideally, if OKC is enabled then whenever timeout + * occurs in a mobility domain, then the driver should + * clear all APs cache entries related to that domain + * but as the BSSID doesn't exist yet in the driver + * cache there is no way of clearing the cache entries, + * without disturbing the legacy roaming. + * Now security profile for both APs changed to FT-RSN. + * DUT first disassociate with AP2 and successfully + * associated with AP2 and perform full EAP and + * generates PMK3. DUT first deletes PMK entry for AP2 + * and then adds a new entry for AP2. + * At this point of time pmk table has two entry + * AP2--> PMK3 and AP1-->PMK1. Now DUT roamed to AP1 + * using PMK3 but sends stale entry of AP1 (PMK1) to + * fw via RSO command. This override PMK for both APs + * with PMK1 (as FW uses mlme session PMK for both APs + * in case of FT roaming) and next time when FW try to + * roam to AP2 using PMK1, AP2 rejects PMK1 (As AP2 is + * expecting PMK3) and initiates full EAP with AP2, + * which is wrong. + * To address this issue update pmk table entry for + * roamed AP1 with pmk3 value comes to host via roam + * sync indication event. By this host override stale + * entry (if any) with the latest valid pmk for that AP + * at a point of time. + */ + if (roaming_info->pmk_len) { + pmksa = qdf_mem_malloc(sizeof(*pmksa)); + if (!pmksa) { + status = QDF_STATUS_E_NOMEM; + qdf_mem_zero(pmkid_cache, + sizeof(*pmkid_cache)); + qdf_mem_free(pmkid_cache); + goto end; + } + + /* + * This pmksa buffer is to update the + * crypto table + */ + wlan_vdev_get_bss_peer_mac(vdev, &pmksa->bssid); + qdf_mem_copy(pmksa->pmkid, + roaming_info->pmkid, PMKID_LEN); + qdf_mem_copy(pmksa->pmk, roaming_info->pmk, + roaming_info->pmk_len); + pmksa->pmk_len = roaming_info->pmk_len; + status = wlan_crypto_set_del_pmksa(vdev, + pmksa, true); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_zero(pmksa, sizeof(*pmksa)); + qdf_mem_free(pmksa); + } + + /* update the pmkid_cache buffer to + * update the global session pmk + */ + qdf_mem_copy(pmkid_cache->pmkid, + roaming_info->pmkid, PMKID_LEN); + qdf_mem_copy(pmkid_cache->pmk, + roaming_info->pmk, + roaming_info->pmk_len); + pmkid_cache->pmk_len = roaming_info->pmk_len; + } + wlan_cm_set_psk_pmk(pdev, vdev_id, pmkid_cache->pmk, pmkid_cache->pmk_len); diff --git a/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c b/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c index 08974ed1b1..c6af9d6190 100644 --- a/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c +++ b/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c @@ -4083,10 +4083,13 @@ cm_store_sae_single_pmk_to_global_cache(struct wlan_objmgr_psoc *psoc, struct cm_roam_values_copy src_cfg; struct qdf_mac_addr bssid; uint8_t vdev_id = wlan_vdev_get_id(vdev); + int32_t akm; + akm = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); wlan_cm_roam_cfg_get_value(psoc, vdev_id, IS_SINGLE_PMK, &src_cfg); - if (!src_cfg.bool_value) + if (!src_cfg.bool_value || + !QDF_HAS_PARAM(akm, WLAN_CRYPTO_KEY_MGMT_SAE)) return; /* * Mark the AP as single PMK capable in Crypto Table diff --git a/core/mac/src/sys/legacy/src/utils/src/parser_api.c b/core/mac/src/sys/legacy/src/utils/src/parser_api.c index 50030b4d3e..086c8c7caf 100644 --- a/core/mac/src/sys/legacy/src/utils/src/parser_api.c +++ b/core/mac/src/sys/legacy/src/utils/src/parser_api.c @@ -6673,15 +6673,18 @@ QDF_STATUS populate_dot11f_twt_extended_caps(struct mac_context *mac_ctx, * Return: None */ static void -wlan_fill_single_pmk_ap_cap_from_scan_entry(struct bss_description *bss_desc, +wlan_fill_single_pmk_ap_cap_from_scan_entry(struct mac_context *mac_ctx, + struct bss_description *bss_desc, struct scan_cache_entry *scan_entry) { - bss_desc->is_single_pmk = util_scan_entry_single_pmk(scan_entry); + bss_desc->is_single_pmk = + util_scan_entry_single_pmk(mac_ctx->psoc, scan_entry) && + mac_ctx->mlme_cfg->lfr.sae_single_pmk_feature_enabled; } - #else static inline void -wlan_fill_single_pmk_ap_cap_from_scan_entry(struct bss_description *bss_desc, +wlan_fill_single_pmk_ap_cap_from_scan_entry(struct mac_context *mac_ctx, + struct bss_description *bss_desc, struct scan_cache_entry *scan_entry) { } @@ -7262,7 +7265,8 @@ wlan_fill_bss_desc_from_scan_entry(struct mac_context *mac_ctx, bss_desc->mbo_oce_enabled_ap = util_scan_entry_mbo_oce(scan_entry) ? true : false; - wlan_fill_single_pmk_ap_cap_from_scan_entry(bss_desc, scan_entry); + wlan_fill_single_pmk_ap_cap_from_scan_entry(mac_ctx, bss_desc, + scan_entry); qdf_mem_copy(&bss_desc->mbssid_info, &scan_entry->mbssid_info, sizeof(struct scan_mbssid_info)); diff --git a/core/sme/src/csr/csr_api_roam.c b/core/sme/src/csr/csr_api_roam.c index 8350effbc5..693221cc1e 100644 --- a/core/sme/src/csr/csr_api_roam.c +++ b/core/sme/src/csr/csr_api_roam.c @@ -7479,6 +7479,7 @@ csr_clear_other_bss_sae_single_pmk_entry(struct mac_context *mac, { struct wlan_objmgr_vdev *vdev; struct cm_roam_values_copy src_cfg; + uint32_t akm; wlan_cm_roam_cfg_get_value(mac->psoc, vdev_id, IS_SINGLE_PMK, &src_cfg); @@ -7492,6 +7493,12 @@ csr_clear_other_bss_sae_single_pmk_entry(struct mac_context *mac, return; } + akm = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + if (!QDF_HAS_PARAM(akm, WLAN_CRYPTO_KEY_MGMT_SAE)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return; + } + wlan_crypto_selective_clear_sae_single_pmk_entries(vdev, (struct qdf_mac_addr *)bss_desc->bssId); @@ -7506,6 +7513,7 @@ csr_delete_current_bss_sae_single_pmk_entry(struct mac_context *mac, struct wlan_objmgr_vdev *vdev; struct wlan_crypto_pmksa pmksa; struct cm_roam_values_copy src_cfg; + uint32_t akm; wlan_cm_roam_cfg_get_value(mac->psoc, vdev_id, IS_SINGLE_PMK, &src_cfg); @@ -7520,6 +7528,12 @@ csr_delete_current_bss_sae_single_pmk_entry(struct mac_context *mac, return; } + akm = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); + if (!QDF_HAS_PARAM(akm, WLAN_CRYPTO_KEY_MGMT_SAE)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID); + return; + } + qdf_copy_macaddr(&pmksa.bssid, (struct qdf_mac_addr *)bss_desc->bssId); wlan_crypto_set_del_pmksa(vdev, &pmksa, false); @@ -16897,6 +16911,89 @@ csr_process_roam_sync_callback(struct mac_context *mac_ctx, if (cm_lookup_pmkid_using_bssid(mac_ctx->psoc, session->vdev_id, pmkid_cache)) { + /* + * Consider two APs: AP1, AP2 + * Both APs configured with EAP 802.1x security mode + * and OKC is enabled in both APs by default. Initially + * DUT successfully associated with AP1, and generated + * PMK1 by performing full EAP and added an entry for + * AP1 in pmk table. At this stage, pmk table has only + * one entry for PMK1 (1. AP1-->PMK1). Now DUT try to + * roam to AP2 using PMK1 (as OKC is enabled) but + * session timeout happens on AP2 just before 4 way + * handshake completion in FW. At this point of time + * DUT not in authenticated state. Due to this DUT + * performs full EAP with AP2 and generates PMK2. As + * there is no previous entry of AP2 (AP2-->PMK1) in pmk + * table. When host gets pmk delete command for BSSID of + * AP2, the BSSID match fails. Hence host will not + * delete pmk entry of AP1 as well. + * At this point of time, the PMK table has two entry + * 1. AP1-->PMK1 and 2. AP2 --> PMK2. + * Ideally, if OKC is enabled then whenever timeout + * occurs in a mobility domain, then the driver should + * clear all APs cache entries related to that domain + * but as the BSSID doesn't exist yet in the driver + * cache there is no way of clearing the cache entries, + * without disturbing the legacy roaming. + * Now security profile for both APs changed to FT-RSN. + * DUT first disassociate with AP2 and successfully + * associated with AP2 and perform full EAP and + * generates PMK3. DUT first deletes PMK entry for AP2 + * and then adds a new entry for AP2. + * At this point of time pmk table has two entry + * AP2--> PMK3 and AP1-->PMK1. Now DUT roamed to AP1 + * using PMK3 but sends stale entry of AP1 (PMK1) to + * fw via RSO command. This override PMK for both APs + * with PMK1 (as FW uses mlme session PMK for both APs + * in case of FT roaming) and next time when FW try to + * roam to AP2 using PMK1, AP2 rejects PMK1 (As AP2 is + * expecting PMK3) and initiates full EAP with AP2, + * which is wrong. + * To address this issue update pmk table entry for + * roamed AP1 with PMK3 value comes to host via roam + * sync indication event. By this host override stale + * entry (if any) with the latest valid pmk for that AP + * at a point of time. + */ + if (roam_synch_data->pmk_len) { + pmksa = qdf_mem_malloc(sizeof(*pmksa)); + if (!pmksa) { + status = QDF_STATUS_E_NOMEM; + qdf_mem_zero(pmkid_cache, + sizeof(*pmkid_cache)); + qdf_mem_free(pmkid_cache); + goto end; + } + + /* + * This pmksa buffer is to update the + * crypto table + */ + wlan_vdev_get_bss_peer_mac(vdev, &pmksa->bssid); + qdf_mem_copy(pmksa->pmkid, + roam_synch_data->pmkid, PMKID_LEN); + qdf_mem_copy(pmksa->pmk, roam_synch_data->pmk, + roam_synch_data->pmk_len); + pmksa->pmk_len = roam_synch_data->pmk_len; + status = wlan_crypto_set_del_pmksa(vdev, + pmksa, true); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_zero(pmksa, sizeof(*pmksa)); + qdf_mem_free(pmksa); + pmksa = NULL; + } + + /* update the pmkid_cache buffer to + * update the global session pmk + */ + qdf_mem_copy(pmkid_cache->pmkid, + roam_synch_data->pmkid, PMKID_LEN); + qdf_mem_copy(pmkid_cache->pmk, + roam_synch_data->pmk, + roam_synch_data->pmk_len); + pmkid_cache->pmk_len = roam_synch_data->pmk_len; + } wlan_cm_set_psk_pmk(mac_ctx->pdev, session_id, pmkid_cache->pmk, pmkid_cache->pmk_len);