[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