qcacld-3.0: Send preauth status for SAE hybrid roaming

For WPA3 SAE LFR-3.0 roaming, authentication is offloaded to the
wpa_supplicant via the host driver due to the target crypto
inavailability limitations. The target does candidate selection
and sends the WMI_ROAM_PREAUTH_START_CMDID to the driver.

Once the SAE authentication is done, the supplicant sends the
external auth NL command with the authentication status, PMKID
derived. On receiving this event, send the PMKID, bssid, auth
status to the target over WMI_ROAM_PREAUTH_STATUS_CMDID command.

The host driver decides if the external authentication event is
received for preauth candidate or for initial connection by
checking the connected state information of the csr session.

When the authentication fails with the first candidate, the
target triggers the preauth start for the next candidate. This
happens till there are no candidates available to roam.

There could be a situation where the firmware sends preauth
event for candidate1 and before the supplicant could send the
external auth command to the host driver, the candidate1 preauth
timer could have expired and firmware sends the preauth start for
second candidate. After SAE is done with second candidate also,
the supplicant sends external auth event for second candidate. So
in this situation the firmware should decide to choose the
latest candidate. The host driver here is a mere passthrough and
doesn't maintain timer for the preauth event for each BSSID to
drop the expired candidate preauth events.

Change-Id: I799a05e55a59677dfc2ddf5b55ebc888ccfea03e
CRs-Fixed: 2507003
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 9220ee1..b29d955 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -21618,12 +21618,35 @@
 			hdd_debug("external_auth: Failed to cache PMKID");
 	}
 }
+
+/**
+ * wlan_hdd_extauth_copy_pmkid() - Copy the pmkid received from the
+ * external authentication command received from the userspace.
+ * @params: pointer to auth params
+ * @pmkid: Pointer to destination pmkid buffer to be filled
+ *
+ * The caller should ensure that destination pmkid buffer is not NULL.
+ *
+ * Return: None
+ */
+static void
+wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params,
+			    uint8_t *pmkid)
+{
+	qdf_mem_copy(pmkid, params->pmkid, PMKID_LEN);
+}
+
 #else
 static void
 wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter,
 			     mac_handle_t mac_handle,
 			     struct cfg80211_external_auth_params *params)
 {}
+
+static void
+wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params,
+			    uint8_t *pmkid)
+{}
 #endif
 /**
  * __wlan_hdd_cfg80211_external_auth() - Handle external auth
@@ -21649,6 +21672,7 @@
 	int ret;
 	mac_handle_t mac_handle;
 	struct qdf_mac_addr peer_mac_addr;
+	uint8_t pmkid[PMKID_LEN] = {0};
 
 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
 		hdd_err("Command not allowed in FTM mode");
@@ -21669,8 +21693,9 @@
 
 	wlan_hdd_extauth_cache_pmkid(adapter, mac_handle, params);
 
+	wlan_hdd_extauth_copy_pmkid(params, pmkid);
 	sme_handle_sae_msg(mac_handle, adapter->vdev_id, params->status,
-			   peer_mac_addr);
+			   peer_mac_addr, pmkid);
 
 	return ret;
 }
diff --git a/core/mac/src/include/sir_params.h b/core/mac/src/include/sir_params.h
index 1dfeb31..8d7bc3d 100644
--- a/core/mac/src/include/sir_params.h
+++ b/core/mac/src/include/sir_params.h
@@ -366,6 +366,7 @@
 /* (SIR_HAL_ITC_MSG_TYPES_BEGIN + 137) is unused */
 
 #define SIR_HAL_ROAM_SCAN_OFFLOAD_REQ      (SIR_HAL_ITC_MSG_TYPES_BEGIN + 138)
+#define SIR_HAL_ROAM_PRE_AUTH_STATUS_IND   (SIR_HAL_ITC_MSG_TYPES_BEGIN + 139)
 
 #define SIR_HAL_TRAFFIC_STATS_IND          (SIR_HAL_ITC_MSG_TYPES_BEGIN + 141)
 
diff --git a/core/mac/src/sys/legacy/src/utils/src/mac_trace.c b/core/mac/src/sys/legacy/src/utils/src/mac_trace.c
index 49b407e..9da76df 100644
--- a/core/mac/src/sys/legacy/src/utils/src/mac_trace.c
+++ b/core/mac/src/sys/legacy/src/utils/src/mac_trace.c
@@ -461,6 +461,7 @@
 		CASE_RETURN_STRING(WMA_AGGR_QOS_REQ);
 		CASE_RETURN_STRING(WMA_AGGR_QOS_RSP);
 		CASE_RETURN_STRING(WMA_ROAM_SCAN_OFFLOAD_REQ);
+		CASE_RETURN_STRING(WMA_ROAM_PRE_AUTH_STATUS);
 #ifdef WLAN_FEATURE_PACKET_FILTERING
 		CASE_RETURN_STRING(WMA_8023_MULTICAST_LIST_REQ);
 		CASE_RETURN_STRING(WMA_RECEIVE_FILTER_SET_FILTER_REQ);
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index ff327c2..b142cbd 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -2633,19 +2633,22 @@
  * @session_id: session id
  * @sae_status: status of SAE authentication
  * @peer_mac_addr: mac address of the peer to be authenticated
+ * @pmkid: PMKID derived at the end of SAE authentication
  *
  * Return: QDF_STATUS
  */
 QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle,
 			      uint8_t session_id,
 			      uint8_t sae_status,
-			      struct qdf_mac_addr peer_mac_addr);
+			      struct qdf_mac_addr peer_mac_addr,
+			      const uint8_t *pmkid);
 #else
 static inline
 QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle,
 			      uint8_t session_id,
 			      uint8_t sae_status,
-			      struct qdf_mac_addr peer_mac_addr)
+			      struct qdf_mac_addr peer_mac_addr,
+			      const uint8_t *pmkid)
 {
 	return QDF_STATUS_SUCCESS;
 }
diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c
index a2408ca..94f8506 100644
--- a/core/sme/src/common/sme_api.c
+++ b/core/sme/src/common/sme_api.c
@@ -14847,42 +14847,95 @@
 QDF_STATUS sme_handle_sae_msg(mac_handle_t mac_handle,
 			      uint8_t session_id,
 			      uint8_t sae_status,
-			      struct qdf_mac_addr peer_mac_addr)
+			      struct qdf_mac_addr peer_mac_addr,
+			      const uint8_t *pmkid)
 {
 	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
 	struct mac_context *mac = MAC_CONTEXT(mac_handle);
 	struct sir_sae_msg *sae_msg;
 	struct scheduler_msg sch_msg = {0};
+	struct wmi_roam_auth_status_params *params;
+
+	if (!CSR_IS_SESSION_VALID(mac, session_id)) {
+		sme_err("Invalid session id: %d", session_id);
+		return false;
+	}
 
 	qdf_status = sme_acquire_global_lock(&mac->sme);
-	if (QDF_IS_STATUS_SUCCESS(qdf_status)) {
+	if (QDF_IS_STATUS_ERROR(qdf_status))
+		return qdf_status;
+
+	if (!CSR_IS_ROAM_JOINED(mac, session_id)) {
 		sae_msg = qdf_mem_malloc(sizeof(*sae_msg));
 		if (!sae_msg) {
 			qdf_status = QDF_STATUS_E_NOMEM;
-		} else {
-			sae_msg->message_type = eWNI_SME_SEND_SAE_MSG;
-			sae_msg->length = sizeof(*sae_msg);
-			sae_msg->session_id = session_id;
-			sae_msg->sae_status = sae_status;
-			qdf_mem_copy(sae_msg->peer_mac_addr,
-				     peer_mac_addr.bytes,
-				     QDF_MAC_ADDR_SIZE);
-			sme_debug("SAE: sae_status %d session_id %d Peer: "
-				  QDF_MAC_ADDR_STR, sae_msg->sae_status,
-				  sae_msg->session_id,
-				  QDF_MAC_ADDR_ARRAY(sae_msg->peer_mac_addr));
-
-			sch_msg.type = eWNI_SME_SEND_SAE_MSG;
-			sch_msg.bodyptr = sae_msg;
-
-			qdf_status =
-				scheduler_post_message(QDF_MODULE_ID_SME,
-						       QDF_MODULE_ID_PE,
-						       QDF_MODULE_ID_PE,
-						       &sch_msg);
+			goto error;
 		}
-		sme_release_global_lock(&mac->sme);
+
+		sae_msg->message_type = eWNI_SME_SEND_SAE_MSG;
+		sae_msg->length = sizeof(*sae_msg);
+		sae_msg->session_id = session_id;
+		sae_msg->sae_status = sae_status;
+		qdf_mem_copy(sae_msg->peer_mac_addr,
+			     peer_mac_addr.bytes,
+			     QDF_MAC_ADDR_SIZE);
+		sme_debug("SAE: sae_status %d session_id %d Peer: "
+			  QDF_MAC_ADDR_STR, sae_msg->sae_status,
+			  sae_msg->session_id,
+			  QDF_MAC_ADDR_ARRAY(sae_msg->peer_mac_addr));
+
+		sch_msg.type = eWNI_SME_SEND_SAE_MSG;
+		sch_msg.bodyptr = sae_msg;
+
+		qdf_status = scheduler_post_message(QDF_MODULE_ID_SME,
+						    QDF_MODULE_ID_PE,
+						    QDF_MODULE_ID_PE,
+						    &sch_msg);
+		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+			qdf_mem_free(sae_msg);
+			goto error;
+		}
+	} else {
+		/*
+		 * For WPA3 SAE roaming, external auth offload is enabled. The
+		 * firmware will send preauth start event after candidate
+		 * selection. The supplicant will perform the SAE authentication
+		 * and will send the auth status, PMKID in the external auth
+		 * cmd.
+		 *
+		 * csr roam state is CSR_ROAM_STATE_JOINED. So this SAE
+		 * external auth event is for wpa3 roam pre-auth offload.
+		 *
+		 * Post the preauth status to WMA.
+		 */
+		params = qdf_mem_malloc(sizeof(*params));
+		if (!params) {
+			qdf_status = QDF_STATUS_E_NOMEM;
+			goto error;
+		}
+
+		params->vdev_id = session_id;
+		params->preauth_status = sae_status;
+		qdf_copy_macaddr(&params->bssid, &peer_mac_addr);
+
+		qdf_mem_zero(params->pmkid, PMKID_LEN);
+		if (pmkid)
+			qdf_mem_copy(params->pmkid, pmkid, PMKID_LEN);
+
+		sch_msg.type = WMA_ROAM_PRE_AUTH_STATUS;
+		sch_msg.bodyptr = params;
+
+		qdf_status = scheduler_post_message(QDF_MODULE_ID_SME,
+						    QDF_MODULE_ID_WMA,
+						    QDF_MODULE_ID_WMA,
+						    &sch_msg);
+		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+			sme_err("WMA_ROAM_PRE_AUTH_STATUS cmd posting failed");
+			qdf_mem_free(params);
+		}
 	}
+error:
+	sme_release_global_lock(&mac->sme);
 
 	return qdf_status;
 }
diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h
index 10b0731..2db4670 100644
--- a/core/wma/inc/wma.h
+++ b/core/wma/inc/wma.h
@@ -2241,6 +2241,24 @@
 QDF_STATUS wma_process_roaming_config(tp_wma_handle wma_handle,
 				     struct roam_offload_scan_req *roam_req);
 
+#ifdef WLAN_FEATURE_ROAM_OFFLOAD
+/**
+ * wma_send_roam_preauth_status() - Send the preauth status to wmi
+ * @handle: WMA handle
+ * @roam_req: Pointer to wmi_roam_auth_status_params from sae
+ *
+ * Return: None
+ */
+void
+wma_send_roam_preauth_status(tp_wma_handle wma_handle,
+			     struct wmi_roam_auth_status_params *params);
+#else
+static inline void
+wma_send_roam_preauth_status(tp_wma_handle wma_handle,
+			     struct wmi_roam_auth_status_params *params)
+{}
+#endif
+
 #ifdef WMI_INTERFACE_EVENT_LOGGING
 static inline void wma_print_wmi_cmd_log(uint32_t count,
 					 qdf_abstract_print *print,
diff --git a/core/wma/inc/wma_types.h b/core/wma/inc/wma_types.h
index 9e35557..d8bac2c 100644
--- a/core/wma/inc/wma_types.h
+++ b/core/wma/inc/wma_types.h
@@ -227,6 +227,7 @@
 #endif
 
 #define WMA_ROAM_SCAN_OFFLOAD_REQ   SIR_HAL_ROAM_SCAN_OFFLOAD_REQ
+#define WMA_ROAM_PRE_AUTH_STATUS    SIR_HAL_ROAM_PRE_AUTH_STATUS_IND
 
 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
 #define WMA_ROAM_OFFLOAD_SYNCH_IND  SIR_HAL_ROAM_OFFLOAD_SYNCH_IND
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index 1c3eaf5..c13f97c 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -8615,6 +8615,11 @@
 		wma_process_roaming_config(wma_handle, msg->bodyptr);
 		break;
 
+	case WMA_ROAM_PRE_AUTH_STATUS:
+		wma_send_roam_preauth_status(wma_handle, msg->bodyptr);
+		qdf_mem_free(msg->bodyptr);
+		break;
+
 	case WMA_RATE_UPDATE_IND:
 		wma_process_rate_update_indicate(wma_handle,
 				(tSirRateUpdateInd *) msg->bodyptr);
diff --git a/core/wma/src/wma_scan_roam.c b/core/wma/src/wma_scan_roam.c
index a27e0f6..c27494e 100644
--- a/core/wma/src/wma_scan_roam.c
+++ b/core/wma/src/wma_scan_roam.c
@@ -1629,6 +1629,23 @@
 		WMA_LOGE("failed to send idle roam parameters");
 }
 
+void
+wma_send_roam_preauth_status(tp_wma_handle wma_handle,
+			     struct wmi_roam_auth_status_params *params)
+{
+	QDF_STATUS status;
+
+	if (!wma_handle || !wma_handle->wmi_handle) {
+		WMA_LOGE("WMA is closed, cannot send roam prauth status");
+		return;
+	}
+
+	status = wmi_unified_send_roam_preauth_status(wma_handle->wmi_handle,
+						      params);
+	if (QDF_IS_STATUS_ERROR(status))
+		WMA_LOGE("failed to send disconnect roam preauth status");
+}
+
 #else
 static inline void
 wma_send_disconnect_roam_params(tp_wma_handle wma_handle,