[PATCH 5/6] FT: Implement basic cache expiration and limiting

Benjamin Berg benjamin at sipsolutions.net
Mon Sep 19 08:47:43 PDT 2016


From: Benjamin Berg <benjamin.berg at open-mesh.com>

Add a simple expiry mechanism limitting both the age and number of cached
elements to a hard-coded value. Without this the caches would always grow
potentially crashing hostapd at some point.

Signed-off-by: Benjamin Berg <benjamin.berg at open-mesh.com>
---
 src/ap/wpa_auth_ft.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 2 deletions(-)

diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index c45ff89..a31c40e 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -150,6 +150,11 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
 	return pos - buf;
 }
 
+#define MAX_R0_PMK_CACHE_AGE 360
+#define MAX_R1_PMK_CACHE_AGE 360
+
+#define MAX_R0_PMK_CACHE_ENTRIES 100
+#define MAX_R1_PMK_CACHE_ENTRIES 100
 
 struct wpa_ft_pmk_r0_sa {
 	struct wpa_ft_pmk_r0_sa *next;
@@ -157,6 +162,9 @@ struct wpa_ft_pmk_r0_sa {
 	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
 	u8 spa[ETH_ALEN];
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+
+	struct os_reltime added;
+
 	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
 	int pmk_r1_pushed;
 };
@@ -167,12 +175,21 @@ struct wpa_ft_pmk_r1_sa {
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 	u8 spa[ETH_ALEN];
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+
+	struct os_reltime added;
+
 	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
 };
 
 struct wpa_ft_pmk_cache {
 	struct wpa_ft_pmk_r0_sa *pmk_r0;
 	struct wpa_ft_pmk_r1_sa *pmk_r1;
+
+	int pmk_r0_count;
+	int pmk_r1_count;
+
+	struct os_reltime pmk_r0_oldest;
+	struct os_reltime pmk_r1_oldest;
 };
 
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
@@ -209,6 +226,48 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
 	os_free(cache);
 }
 
+static void wpa_ft_pmk_r0_cache_clean(struct wpa_authenticator *wpa_auth,
+				     int max_age, int max_entries)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct os_reltime now;
+	struct wpa_ft_pmk_r0_sa *r0, *last;
+	int pos = 0;
+
+	os_get_reltime(&now);
+
+	if ((now.sec - cache->pmk_r0_oldest.sec <= max_age) &&
+	    (cache->pmk_r0_count <= max_entries))
+		return;
+
+	/* Need to remove items, iterate until we find the last valid element */
+	last = NULL;
+	r0 = cache->pmk_r0;
+
+	while (r0 && pos < max_entries && (now.sec - r0->added.sec) <= max_age) {
+		last = r0;
+		r0 = r0->next;
+		pos++;
+	}
+
+	if (last) {
+		cache->pmk_r0_oldest = last->added;
+		last->next = NULL;
+	} else {
+		cache->pmk_r0_oldest = now;
+	}
+
+	cache->pmk_r0_count = pos;
+	if (pos == 0)
+		cache->pmk_r0 = NULL;
+
+	while (r0) {
+		last = r0;
+		r0 = r0->next;
+		os_memset(last->pmk_r0, 0, PMK_LEN);
+		os_free(last);
+	}
+}
 
 static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0,
@@ -217,7 +276,9 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
 
-	/* TODO: add expiration and limit on number of entries in cache */
+	wpa_ft_pmk_r0_cache_clean(wpa_auth,
+				  MAX_R0_PMK_CACHE_AGE,
+				  MAX_R0_PMK_CACHE_ENTRIES);
 
 	r0 = os_zalloc(sizeof(*r0));
 	if (r0 == NULL)
@@ -227,9 +288,15 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 	os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
 	os_memcpy(r0->spa, spa, ETH_ALEN);
 	r0->pairwise = pairwise;
+	os_get_reltime(&r0->added);
 
 	r0->next = cache->pmk_r0;
 	cache->pmk_r0 = r0;
+	cache->pmk_r0_count++;
+
+	if (r0->next == NULL) {
+		cache->pmk_r0_oldest = r0->added;
+	}
 
 	return 0;
 }
@@ -260,6 +327,50 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 }
 
 
+static void wpa_ft_pmk_r1_cache_clean(struct wpa_authenticator *wpa_auth,
+				     int max_age, int max_entries)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct os_reltime now;
+	struct wpa_ft_pmk_r1_sa *r1, *last;
+	int pos = 0;
+
+	os_get_reltime(&now);
+
+	if ((now.sec - cache->pmk_r1_oldest.sec <= max_age) &&
+	    (cache->pmk_r1_count <= max_entries))
+		return;
+
+	/* Need to remove items, iterate until we find the last valid element */
+	last = NULL;
+	r1 = cache->pmk_r1;
+
+	while (r1 && pos < max_entries && (now.sec - r1->added.sec) <= max_age) {
+		last = r1;
+		r1 = r1->next;
+		pos++;
+	}
+
+	if (last) {
+		cache->pmk_r1_oldest = last->added;
+		last->next = NULL;
+	} else {
+		cache->pmk_r1_oldest = now;
+	}
+
+	cache->pmk_r1_count = pos;
+	if (pos == 0)
+		cache->pmk_r1 = NULL;
+
+	while (r1) {
+		last = r1;
+		r1 = r1->next;
+		os_memset(last->pmk_r1, 0, PMK_LEN);
+		os_free(last);
+	}
+}
+
+
 static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r1,
 			       const u8 *pmk_r1_name, int pairwise)
@@ -267,7 +378,9 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r1_sa *r1;
 
-	/* TODO: add expiration and limit on number of entries in cache */
+	wpa_ft_pmk_r1_cache_clean(wpa_auth,
+				  MAX_R1_PMK_CACHE_AGE,
+				  MAX_R1_PMK_CACHE_ENTRIES);
 
 	r1 = os_zalloc(sizeof(*r1));
 	if (r1 == NULL)
@@ -277,9 +390,16 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 	os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 	os_memcpy(r1->spa, spa, ETH_ALEN);
 	r1->pairwise = pairwise;
+	os_get_reltime(&r1->added);
 
 	r1->next = cache->pmk_r1;
 	cache->pmk_r1 = r1;
+	cache->pmk_r1_count++;
+
+	if (r1->next == NULL) {
+		cache->pmk_r1_oldest = r1->added;
+	}
+
 
 	return 0;
 }
-- 
2.9.3




More information about the Hostap mailing list