Blob Blame History Raw
From 4033935dd9098938838d6d7934ceb65f92a1fa3c Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@qca.qualcomm.com>
Date: Wed, 22 May 2013 13:24:30 +0300
Subject: [PATCH] Fix OKC-based PMKSA cache entry clearing

Commit c3fea272747f738f5723fc577371fe03711d988f added a call to clear
all other PMKSA cache entries for the same network if the PMKSA cache
entry of the current AP changed. This was needed to fix OKC cases since
the other APs would likely use the new PMK in the future. However, this
ended up clearing entries in cases where that is not desired and this
resulted in needing additional full EAP authentication with networks
that did not support OKC if wpa_supplicant was configured to try to use
it.

Make PMKSA cache entry flushing more limited so that the other entries
are removed only if they used the old PMK that was replaced for the
current AP and only if that PMK had previously been used successfully
(i.e., opportunistic flag was already cleared back to 0 in
wpa_supplicant_key_neg_complete()). This is still enough to fix the
issue described in that older commit while not causing problems for
standard PMKSA caching operations even if OKC is enabled in
wpa_supplicant configuration.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
---
 src/rsn_supp/pmksa_cache.c | 27 ++++++++++++++++++++-------
 src/rsn_supp/pmksa_cache.h |  3 ++-
 src/rsn_supp/wpa.c         |  2 +-
 3 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index df67583..93056ea 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -160,25 +160,31 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 				os_free(entry);
 				return pos;
 			}
 			if (prev == NULL)
 				pmksa->pmksa = pos->next;
 			else
 				prev->next = pos->next;
-			wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
-				   "the current AP");
-			pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
 
 			/*
 			 * If OKC is used, there may be other PMKSA cache
 			 * entries based on the same PMK. These needs to be
 			 * flushed so that a new entry can be created based on
-			 * the new PMK.
+			 * the new PMK. Only clear other entries if they have a
+			 * matching PMK and this PMK has been used successfully
+			 * with the current AP, i.e., if opportunistic flag has
+			 * been cleared in wpa_supplicant_key_neg_complete().
 			 */
-			pmksa_cache_flush(pmksa, network_ctx);
+			wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+				   "the current AP and any PMKSA cache entry "
+				   "that was based on the old PMK");
+			if (!pos->opportunistic)
+				pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
+						  pos->pmk_len);
+			pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
 			break;
 		}
 		prev = pos;
 		pos = pos->next;
 	}
 
 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
@@ -231,23 +237,30 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 }
 
 
 /**
  * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  * @network_ctx: Network configuration context or %NULL to flush all entries
+ * @pmk: PMK to match for or %NYLL to match all PMKs
+ * @pmk_len: PMK length
  */
-void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx)
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+		       const u8 *pmk, size_t pmk_len)
 {
 	struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
 	int removed = 0;
 
 	entry = pmksa->pmksa;
 	while (entry) {
-		if (entry->network_ctx == network_ctx || network_ctx == NULL) {
+		if ((entry->network_ctx == network_ctx ||
+		     network_ctx == NULL) &&
+		    (pmk == NULL ||
+		     (pmk_len == entry->pmk_len &&
+		      os_memcmp(pmk, entry->pmk, pmk_len) == 0))) {
 			wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
 				   "for " MACSTR, MAC2STR(entry->aa));
 			if (prev)
 				prev->next = entry->next;
 			else
 				pmksa->pmksa = entry->next;
 			tmp = entry;
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6f3dfb3..d5aa229 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -62,15 +62,16 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
 void pmksa_cache_clear_current(struct wpa_sm *sm);
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 			    const u8 *bssid, void *network_ctx,
 			    int try_opportunistic);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
 			      void *network_ctx, const u8 *aa);
-void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+		       const u8 *pmk, size_t pmk_len);
 
 #else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
 
 static inline struct rsn_pmksa_cache *
 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
 				 void *ctx, int reason),
 		 void *ctx, struct wpa_sm *sm)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index e50404c..365a710 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2618,15 +2618,15 @@ void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
 	os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
 }
 
 
 void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
 {
 #ifndef CONFIG_NO_WPA2
-	pmksa_cache_flush(sm->pmksa, network_ctx);
+	pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0);
 #endif /* CONFIG_NO_WPA2 */
 }
 
 
 #ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 {
-- 
1.8.3.1