[PATCH 5/5] mesh: Add support for PMKSA caching

Masashi Honma masashi.honma at gmail.com
Wed Mar 9 01:16:14 PST 2016


This patch add functionality of mesh SAE PMKSA caching. If my STA
already has peer's PMKSA cache, skip SAE authentication and start AMPE
with the cache.

If the peer does not support PMKSA cache or does not have my STA's
PMKSA cache, AMPE will fail and the PMKSA cache of the peer will be
removed. Then STA retries with ordinally SAE authentication.

If the peer does not support PMKSA cache and my STA using
no_auto_peer=1, my STA could not retry SAE authentication. Because
NEW_PEER_CANDIDATE event can not start SAE authentication when
no_auto_peer=1. So this patch extends MESH_PEER_ADD command to use
duration(sec). Throughout the duration, my STA can start SAE
authentication triggered by NEW_PEER_CANDIDATE even though
no_auto_peer=1.

This commit requires commit 70c93963edefa37ef84b73efb9d04ea10268341c.
Without the commit, chosen PMK comparison will fail.

Signed-off-by: Masashi Honma <masashi.honma at gmail.com>
---
 src/ap/hostapd.h            |  1 +
 src/ap/ieee802_11.c         | 28 +++++++++++++++++++++-------
 src/ap/ieee802_11.h         |  1 +
 src/ap/sta_info.h           |  1 +
 src/ap/wpa_auth.c           | 29 +++++++++++++++++++++++++++++
 src/ap/wpa_auth.h           |  6 ++++++
 wpa_supplicant/ctrl_iface.c | 12 +++++++++++-
 wpa_supplicant/mesh.c       |  4 ++--
 wpa_supplicant/mesh.h       |  2 +-
 wpa_supplicant/mesh_mpm.c   | 33 ++++++++++++++++++++++++++++++---
 wpa_supplicant/mesh_mpm.h   |  3 ++-
 wpa_supplicant/mesh_rsn.c   | 32 ++++++++++++++++++++++++++++++++
 wpa_supplicant/wpa_cli.c    |  2 +-
 13 files changed, 138 insertions(+), 16 deletions(-)

diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 0f31dd4..629931a 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -262,6 +262,7 @@ struct hostapd_data {
 				 struct sta_info *sta);
 	struct wpabuf *mesh_pending_auth;
 	struct os_reltime mesh_pending_auth_time;
+	u8 mesh_required_peer[ETH_ALEN];
 #endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index f3d8968..1853829 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -537,6 +537,18 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd,
 }
 
 
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta->flags |= WLAN_STA_AUTH;
+	sta->auth_alg = WLAN_AUTH_SAE;
+	mlme_authenticate_indication(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+	sta->sae->state = SAE_ACCEPTED;
+	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+			       sta->sae->pmk, sta->sae->pmkid);
+}
+
+
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *bssid, u8 auth_transaction)
 {
@@ -659,13 +671,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 
 			sae_set_retransmit_timer(hapd, sta);
 		} else {
-			sta->flags |= WLAN_STA_AUTH;
-			sta->auth_alg = WLAN_AUTH_SAE;
-			mlme_authenticate_indication(hapd, sta);
-			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-			sta->sae->state = SAE_ACCEPTED;
-			wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-					       sta->sae->pmk, sta->sae->pmkid);
+			sae_accept_sta(hapd, sta);
 		}
 		break;
 	case SAE_ACCEPTED:
@@ -674,6 +680,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 				   ") doing reauthentication",
 				   MAC2STR(sta->addr));
 			ap_free_sta(hapd, sta);
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
 		} else {
 			if (sae_check_big_sync(sta))
 				return WLAN_STATUS_SUCCESS;
@@ -711,6 +718,13 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 		sta->sae->sync = 0;
 	}
 
+	if (sta->mesh_sae_pmksa_caching) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Cancel to use mesh PMK Caching. Because peer starts SAE authentication.");
+		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		sta->mesh_sae_pmksa_caching = 0;
+	}
+
 	if (auth_transaction == 1) {
 		const u8 *token = NULL, *pos, *end;
 		size_t token_len = 0;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 0020ff5..390b5f5 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -102,6 +102,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
 #ifdef CONFIG_SAE
 void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 				struct sta_info *sta);
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
 #else /* CONFIG_SAE */
 static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 					      struct sta_info *sta)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index c382ffd..d79a5d3 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -163,6 +163,7 @@ struct sta_info {
 
 #ifdef CONFIG_SAE
 	struct sae_data *sae;
+	unsigned int mesh_sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 
 	u32 session_timeout; /* valid only if session_timeout_set == 1 */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index b6a1568..0a31cca 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3372,6 +3372,35 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
 }
 
 
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
+		return NULL;
+
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk)
+{
+	if (sm == NULL)
+		return;
+
+	sm->pmksa = pmksa;
+	if (sm->pmksa->pmk)
+		os_memcpy(pmk, sm->pmksa->pmk, PMK_LEN);
+	if (sm->pmksa->pmkid) {
+		os_memcpy(pmkid, sm->pmksa->pmkid, PMKID_LEN);
+		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed,
+			  sm->pmksa->pmkid, PMKID_LEN);
+	}
+}
+
+
 /*
  * Remove and free the group from wpa_authenticator. This is triggered by a
  * callback to make sure nobody is currently iterating the group list while it
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 35bf604..29d55f7 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -292,6 +292,12 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
 			size_t len);
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk);
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
 void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 				  struct wpa_state_machine *sm, int ack);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 07ae965..0f6f19b 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2736,11 +2736,21 @@ static int wpa_supplicant_ctrl_iface_mesh_peer_add(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
 	u8 addr[ETH_ALEN];
+	int duration;
+	char *pos;
+
+	pos = os_strstr(cmd, " duration=");
+	if (pos) {
+		*pos = '\0';
+		duration = atoi(pos + 10);
+	} else {
+		duration = -1;
+	}
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
 
-	return wpas_mesh_peer_add(wpa_s, addr);
+	return wpas_mesh_peer_add(wpa_s, addr, duration);
 }
 
 #endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index a5b04ca..d49cea2 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -605,7 +605,7 @@ int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, u8 *addr)
 }
 
 
-int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, u8 *addr)
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, u8 *addr, int duration)
 {
-	return mesh_mpm_connect_peer(wpa_s, addr);
+	return mesh_mpm_connect_peer(wpa_s, addr, duration);
 }
diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h
index eeebe5e..0bcd062 100644
--- a/wpa_supplicant/mesh.h
+++ b/wpa_supplicant/mesh.h
@@ -19,7 +19,7 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
 			    size_t len);
 int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, u8 *addr);
-int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, u8 *addr);
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, u8 *addr, int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 8f3e4db..a1f1478 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -18,6 +18,7 @@
 #include "driver_i.h"
 #include "mesh_mpm.h"
 #include "mesh_rsn.h"
+#include "ap/wpa_auth.h"
 
 struct mesh_peer_mgmt_ie {
 	const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
@@ -419,6 +420,7 @@ static void plink_timer(void *eloop_ctx, void *user_data)
 	struct sta_info *sta = user_data;
 	u16 reason = 0;
 	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
 	switch (sta->plink_state) {
 	case PLINK_OPEN_RCVD:
@@ -448,6 +450,13 @@ static void plink_timer(void *eloop_ctx, void *user_data)
 		break;
 	case PLINK_HOLDING:
 		/* holding timer */
+
+		if (sta->mesh_sae_pmksa_caching) {
+			wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR
+				   " looks does not support mesh SAE PMKSA caching. So remove the cache.",
+				   MAC2STR(sta->addr));
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		}
 		mesh_mpm_fsm_restart(wpa_s, sta);
 		break;
 	default:
@@ -513,7 +522,17 @@ int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
 }
 
 
-int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+static void peer_add_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	os_memset(hapd->mesh_required_peer, 0, ETH_ALEN);
+}
+
+
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	struct hostapd_data *hapd;
@@ -550,6 +569,9 @@ int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
 		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
 	} else {
 		mesh_rsn_auth_sae_sta(wpa_s, sta);
+		os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
+		eloop_register_timeout(duration == -1 ? 10 : duration, 0,
+				       peer_add_timer, wpa_s, NULL);
 	}
 
 	return 0;
@@ -705,7 +727,8 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
 	if (!sta)
 		return;
 
-	if (ssid && ssid->no_auto_peer) {
+	if (ssid && ssid->no_auto_peer &&
+	    os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0) {
 		wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
 			MACSTR " because of no_auto_peer", MAC2STR(addr));
 		if (data->mesh_pending_auth) {
@@ -785,7 +808,10 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
 	hapd->num_plinks++;
 
 	sta->flags |= WLAN_STA_ASSOC;
+	sta->mesh_sae_pmksa_caching = 0;
 
+	eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
+	peer_add_timer(wpa_s, NULL);
 	eloop_cancel_timeout(plink_timer, wpa_s, sta);
 
 	/* Send ctrl event */
@@ -1065,7 +1091,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
 	 * open mesh, then go ahead and add the peer before proceeding.
 	 */
 	if (!sta && action_field == PLINK_OPEN &&
-	    !(mconf->security & MESH_CONF_SEC_AMPE))
+	    (!(mconf->security & MESH_CONF_SEC_AMPE) ||
+	    wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
 		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
 	if (!sta) {
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
index cbecd37..5fc1e61 100644
--- a/wpa_supplicant/mesh_mpm.h
+++ b/wpa_supplicant/mesh_mpm.h
@@ -19,7 +19,8 @@ void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
 			      struct sta_info *sta,
 			      enum mesh_plink_state state);
 int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
-int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 5d88274..d02adff 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -291,6 +291,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
 {
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct rsn_pmksa_cache_entry *pmksa;
 	unsigned int rnd;
 	int ret;
 
@@ -306,6 +307,27 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
 			return -1;
 	}
 
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+	if (pmksa) {
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm =
+				wpa_auth_sta_init(hapd->wpa_auth,
+						  sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "mesh: Failed to initialize WPA state machine");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "AUTH: mesh PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+		wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
+					 sta->sae->pmkid, sta->sae->pmk);
+		sae_accept_sta(hapd, sta);
+		sta->mesh_sae_pmksa_caching = 1;
+		return 0;
+	}
+	sta->mesh_sae_pmksa_caching = 0;
+
 	if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
 		return -1;
 
@@ -513,6 +535,16 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
 	const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
 				   (elems->mic - 2) - cat };
+	struct hostapd_data *hapd;
+
+	if (!sta->sae) {
+		hapd = wpa_s->ifmsh->bss[0];
+		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+			wpa_printf(MSG_ERROR, "Mesh RSN: SAE is not prepared yet");
+			return -1;
+		}
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+	}
 
 	if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) {
 		wpa_msg(wpa_s, MSG_DEBUG,
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 4f9d865..0a29bfd 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -3229,7 +3229,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	  "<addr> = Remove a mesh peer" },
 	{ "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
 	  cli_cmd_flag_none,
-	  "<addr> = Add a mesh peer" },
+	  "<addr> [duration=] = Add a mesh peer" },
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
-- 
2.5.0




More information about the Hostap mailing list