Browse Source

qcacld-3.0: Update pmk cache entry for roamed AP

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 roams 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.

Fix is to 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.

Also add new api to get the sae single pmk ini value and
update the bss desc of single_pmk cap after intersecting the ini
value.

Change-Id: I12fecbae69d0d2a11f2a39eee6be960752d831bd
CRs-Fixed: 2889847
abhinav kumar 4 years ago
parent
commit
17a2fc170b

+ 15 - 0
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)

+ 11 - 0
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)
 {

+ 84 - 1
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);

+ 4 - 1
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

+ 9 - 5
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));

+ 97 - 0
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);