From: Benjamin Berg <benjamin.berg@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap