diff mbox series

[13/14] AP: Add support for PASN processing to the SME

Message ID 20200224091529.15259-14-ilan.peer@intel.com
State Deferred
Headers show
Series Support base Pre association Security Negotiation (PASN) | expand

Commit Message

Peer, Ilan Feb. 24, 2020, 9:15 a.m. UTC
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/ap/ieee802_11.c | 517 +++++++++++++++++++++++++++++++++++++++++++-
 src/ap/sta_info.c   |  20 ++
 src/ap/sta_info.h   |  20 ++
 3 files changed, 556 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 26b5a06ed2..7a16ec66b6 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2224,6 +2224,496 @@  ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
 }
 
 
+#ifdef CONFIG_PASN
+
+static struct wpabuf *pasn_get_wrapped_data(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+	switch (sta->pasn->akmp) {
+	case WPA_KEY_MGMT_PASN:
+		/* no wrapped data */
+		return NULL;
+	case WPA_KEY_MGMT_SAE:
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+	default:
+		wpa_printf(MSG_ERROR,
+			   "PASN: TODO: Wrapped data for akmp=0x%x",
+			   sta->pasn->akmp);
+		return NULL;
+	}
+}
+
+
+static int
+pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
+		 struct rsn_pmksa_cache_entry *pmksa,
+		 struct wpa_pasn_params_data *pasn_data,
+		 struct wpabuf *wrapped_data,
+		 struct wpabuf *secret)
+{
+	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+	u8 pmk[PMK_LEN_MAX];
+	u8 pmk_len;
+	int ret;
+
+	os_memset(pmk, 0, sizeof(pmk));
+	pmk_len = 0;
+
+	if (!pmksa)
+		wpa_printf(MSG_DEBUG, "PASN: no valid PMKSA entry");
+
+	if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) {
+		wpa_printf(MSG_DEBUG, "PASN: using default PMK");
+
+		pmk_len = WPA_PASN_PMK_LEN;
+		os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
+	} else if (pmksa) {
+		wpa_printf(MSG_DEBUG, "PASN: using PMKSA entry");
+
+		pmk_len = pmksa->pmk_len;
+		os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len);
+	} else {
+		/* TODO: derive PMK based on wrapped data */
+		wpa_printf(MSG_DEBUG,
+			   "PASN: missing implementation to derive PMK");
+
+		return -1;
+	}
+
+	ret = pasn_pmk_to_ptk(pmk, pmk_len,
+			      sta->addr, hapd->own_addr,
+			      wpabuf_head(secret), wpabuf_len(secret),
+			      &sta->pasn->ptk, sta->pasn->akmp,
+			      sta->pasn->cipher,
+			      WPA_HLTK_MAX_LEN);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to derive PTK");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+	return 0;
+}
+
+
+static int handle_auth_pasn_resp(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 struct rsn_pmksa_cache_entry *pmksa,
+				 u16 status)
+{
+	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len, data_len;
+	u8 *ptr;
+	const u8 *data, *rsn_ie;
+	size_t rsn_ie_len;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: building frame 2: status=%u", status);
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
+				   sta->addr, 2, status);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		goto done;
+
+	wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
+			  sta->pasn->akmp, sta->pasn->cipher);
+
+	/* No need to derive PMK if PMKSA is given */
+	if (!pmksa)
+		wrapped_data_buf = pasn_get_wrapped_data(hapd, sta);
+	else
+		sta->pasn->wrapped_data_format = WPA_PASN_NO_WRAPPED_DATA;
+
+	/* Get public key */
+	pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0);
+	pubkey = wpabuf_zeropad(pubkey,
+				crypto_ecdh_prime_len(sta->pasn->ecdh));
+	if (!pubkey) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to get pubkey");
+		goto fail;
+	}
+
+	wpa_pasn_add_parameter_ie(buf, sta->pasn->group,
+				  sta->pasn->wrapped_data_format,
+				  pubkey, NULL, 0);
+
+	wpa_pasn_add_wrapped_data(buf, wrapped_data_buf);
+
+	wpabuf_free(wrapped_data_buf);
+	wrapped_data_buf = NULL;
+	wpabuf_free(pubkey);
+	pubkey = NULL;
+
+	/* Add the mic */
+	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, mic_len);
+	ptr = wpabuf_put(buf, mic_len);
+
+	os_memset(ptr, 0, mic_len);
+
+	data = wpabuf_head(buf) + IEEE80211_HDRLEN;
+	data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+	rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len);
+	if (!rsn_ie || !rsn_ie_len)
+		goto fail;
+
+	/*
+	 * Note: the wpa_auth_get_wpa_ie() might return not only the RSN IE but
+	 * also the MD IE etc. Thus, do not use the returned length but instead
+	 * use the length specified in the IE header.
+	 */
+	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
+		       hapd->own_addr, sta->addr,
+		       rsn_ie, rsn_ie[1] + 2,
+		       data, data_len,
+		       mic);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed mic calculation");
+		goto fail;
+	}
+
+	os_memcpy(ptr, mic, mic_len);
+
+done:
+	wpa_printf(MSG_DEBUG, "PASN: building frame 2: success");
+
+	wpa_printf(MSG_DEBUG, "PANS: resp: STA=" MACSTR, MAC2STR(sta->addr));
+
+	ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
+				    NULL, 0, 0);
+	if (ret)
+		wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+
+	wpabuf_free(buf);
+	return ret;
+fail:
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+	wpabuf_free(buf);
+	return -1;
+}
+
+
+static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
+			       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn_data;
+	struct wpa_pasn_params_data pasn_params;
+	struct rsn_pmksa_cache_entry *pmksa;
+	struct wpabuf *wrapped_data = NULL, *secret = NULL;
+	int *groups = hapd->conf->pasn_groups;
+	int default_groups[] = { 19, 0 };
+	u16 status = WLAN_STATUS_SUCCESS;
+	int ret;
+	u32 i;
+
+	if (!groups)
+		groups = default_groups;
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed parsing authentication frame");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				   &rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed parsing RNSE");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed validating RSNE");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) ||
+	    !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) {
+		wpa_printf(MSG_DEBUG, "PASN: mismatch in akmp/cipher");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	sta->pasn->akmp = rsn_data.key_mgmt;
+	sta->pasn->cipher = rsn_data.pairwise_cipher;
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: No PASN parameters element found");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto send_resp;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  &pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed validation pasn parameters IE");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto send_resp;
+	}
+
+	for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
+		;
+
+	if (!pasn_params.group || groups[i] != pasn_params.group) {
+		wpa_printf(MSG_DEBUG, "PASN: requested group=%hu not allowed",
+			   pasn_params.group);
+		status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		goto send_resp;
+	}
+
+	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group);
+	if (!sta->pasn->ecdh) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed init ECDH");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	sta->pasn->group = pasn_params.group;
+
+	secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, 0,
+					 pasn_params.pubkey,
+					 pasn_params.pubkey_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive secret");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	if (pasn_params.wrapped_data_format != WPA_PASN_NO_WRAPPED_DATA) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: missing wrapped data");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto send_resp;
+		}
+	}
+
+	sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
+
+	if (rsn_data.num_pmkid) {
+		wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
+
+		pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+					   rsn_data.pmkid);
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
+		pmksa = NULL;
+	}
+
+	ret = pasn_derive_keys(hapd, sta, pmksa, &pasn_params,
+			       wrapped_data, secret);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to derive keys");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
+				   ((u8 *)mgmt) + IEEE80211_HDRLEN,
+				   len - IEEE80211_HDRLEN,
+				   sta->pasn->hash);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to compute hash");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+send_resp:
+	ret = handle_auth_pasn_resp(hapd, sta, pmksa, status);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to send response");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: success handling transaction == 1");
+	}
+
+	wpabuf_free(secret);
+	wpabuf_free(wrapped_data);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		ap_free_sta(hapd, sta);
+}
+
+
+static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
+			       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wpa_pasn_params_data pasn_params;
+	struct wpabuf *wrapped_data = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len;
+	int ret;
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed parsing authentication frame");
+		goto fail;
+	}
+
+	/* check that the MIC IE exists. Save it and zero out the memory */
+	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
+	if (!elems.mic || elems.mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: invalid mic. Expecting len=%u", mic_len);
+		goto fail;
+	} else {
+		os_memcpy(mic, elems.mic, mic_len);
+		os_memset((u8 *)elems.mic, 0, mic_len);
+	}
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: No PASN parameters element found");
+		goto fail;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  &pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed validation pasn parameters IE");
+		goto fail;
+	}
+
+	if (pasn_params.pubkey || pasn_params.pubkey_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Public key should not be included");
+		goto fail;
+	}
+
+	/* verify the MIC */
+	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
+		       sta->addr, hapd->own_addr,
+		       sta->pasn->hash, mic_len * 2,
+		       (u8 *)&mgmt->u.auth,
+		       len - offsetof(struct ieee80211_mgmt, u.auth),
+		       out_mic);
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: frame MIC:  ", mic, mic_len);
+	if (ret || os_memcmp(mic, out_mic, mic_len)) {
+		wpa_printf(MSG_DEBUG, "PASN: failed MIC verification");
+		goto fail;
+	}
+
+	if (pasn_params.wrapped_data_format != WPA_PASN_NO_WRAPPED_DATA) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: missing wrapped data");
+			goto fail;
+		}
+
+		/* TODO: handle wrapped data */
+		wpabuf_free(wrapped_data);
+	}
+
+	wpa_printf(MSG_INFO,
+		   "PASN: success handling transaction == 3. Store PTK");
+
+	ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200,
+			&sta->pasn->ptk);
+fail:
+	ap_free_sta(hapd, sta);
+}
+
+
+static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
+			     const struct ieee80211_mgmt *mgmt, size_t len,
+			     u16 trans_seq, u16 status)
+{
+	if (hapd->conf->wpa != WPA_PROTO_RSN) {
+		wpa_printf(MSG_INFO, "PASN: RSN is not configured");
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
+		   MAC2STR(sta->addr));
+
+	if (trans_seq == 1) {
+		if (sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: not expecting transaction == 1");
+			return;
+		}
+
+		if (status != WLAN_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: failure status in transaction == 1");
+			return;
+		}
+
+		sta->pasn = os_zalloc(sizeof(*sta->pasn));
+		if (!sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: failed to allocate pasn context");
+			return;
+		}
+
+		handle_auth_pasn_1(hapd, sta, mgmt, len);
+	} else if (trans_seq == 3) {
+		if (!sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: not expecting transaction == 3");
+			return;
+		}
+
+		if (status != WLAN_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: failure status in transaction == 3");
+			ap_free_sta_pasn(hapd, sta);
+			return;
+		}
+
+		handle_auth_pasn_3(hapd, sta, mgmt, len);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: invalid transaction. ignore");
+	}
+}
+
+#endif /* CONFIG_PASN */
+
+
 static void handle_auth(struct hostapd_data *hapd,
 			const struct ieee80211_mgmt *mgmt, size_t len,
 			int rssi, int from_queue)
@@ -2310,6 +2800,11 @@  static void handle_auth(struct hostapd_data *hapd,
 	       hapd->conf->fils_dh_group &&
 	       auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+	      (hapd->conf->wpa &&
+	       (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
+	       auth_alg == WLAN_AUTH_PASN) ||
+#endif /* CONFIG_PASN*/
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
 		wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
@@ -2319,6 +2814,9 @@  static void handle_auth(struct hostapd_data *hapd,
 	}
 
 	if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
+#ifdef CONFIG_PASN
+	      (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
+#endif /* CONFIG_PASN*/
 	      (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
 		wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
 			   auth_transaction);
@@ -2440,6 +2938,14 @@  static void handle_auth(struct hostapd_data *hapd,
 			return;
 		}
 #endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+		if (auth_alg == WLAN_AUTH_PASN && sta->flags & WLAN_STA_ASSOC) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: auth: existing station: " MACSTR,
+				   MAC2STR(sta->addr));
+			return;
+		}
+#endif /* CONFIG_PASN */
 	} else {
 #ifdef CONFIG_MESH
 		if (hapd->conf->mesh & MESH_ENABLED) {
@@ -2500,11 +3006,14 @@  static void handle_auth(struct hostapd_data *hapd,
 	 * to allow the original connection work until the attempt can complete
 	 * (re)association, so that unprotected Authentication frame cannot be
 	 * used to bypass PMF protection.
+	 *
+	 * PASN authentication does not require adding/removing station to the
+	 * driver so skip this flow in case of PASN authentication.
 	 */
 	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)) {
+	    !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
 		/*
 		 * If a station that is already associated to the AP, is trying
 		 * to authenticate again, remove the STA entry, in order to make
@@ -2611,6 +3120,12 @@  static void handle_auth(struct hostapd_data *hapd,
 				 handle_auth_fils_finish);
 		return;
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+	case WLAN_AUTH_PASN:
+		handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
+				 status_code);
+		return;
+#endif /* CONFIG_PASN */
 	}
 
  fail:
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 87a9fd409c..bb2566d65b 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -155,6 +155,22 @@  void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
 	sta_ip6addr_del(hapd, sta);
 }
 
+#ifdef CONFIG_PASN
+
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	wpa_printf(MSG_DEBUG, "PASN: Free PASN context: " MACSTR,
+		   MAC2STR(sta->addr));
+
+	if (sta->pasn) {
+		if (sta->pasn->ecdh)
+			crypto_ecdh_deinit(sta->pasn->ecdh);
+		os_free(sta->pasn);
+		sta->pasn = NULL;
+	}
+}
+
+#endif /* CONFIG_PASN */
 
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
@@ -369,6 +385,10 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
 #endif /* CONFIG_WNM_AP */
 
+#ifdef CONFIG_PASN
+	ap_free_sta_pasn(hapd, sta);
+#endif /* CONFIG_PASN */
+
 	os_free(sta->ifname_wds);
 
 #ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 8ff6ac62fe..1957d8ecd4 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -14,6 +14,7 @@ 
 #include "vlan.h"
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/sha384.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -62,6 +63,19 @@  struct pending_eapol_rx {
 	struct os_reltime rx_time;
 };
 
+struct pasn_data {
+	int akmp;
+	int cipher;
+	u16 group;
+	u8 trans_seq;
+	u8 status;
+	u8 wrapped_data_format;
+
+	u8 hash[SHA384_MAC_LEN];
+	struct wpa_ptk ptk;
+	struct crypto_ecdh *ecdh;
+};
+
 struct sta_info {
 	struct sta_info *next; /* next entry in sta list */
 	struct sta_info *hnext; /* next entry in hash table list */
@@ -283,6 +297,10 @@  struct sta_info {
 	unsigned int airtime_weight;
 	struct os_reltime backlogged_until;
 #endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_PASN
+	struct pasn_data *pasn;
+#endif /* CONFIG_PASN */
 };
 
 
@@ -359,4 +377,6 @@  void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
 int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
 						   struct sta_info *sta);
 
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+
 #endif /* STA_INFO_H */