diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index a9952716b..0cd515982 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -26,6 +26,8 @@ struct rsn_pmksa_cache { void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason); + bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx); void *ctx; }; @@ -57,14 +59,35 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; struct os_reltime now; + struct rsn_pmksa_cache_entry *prev = NULL, *tmp; + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; os_get_reltime(&now); - while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { - struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; - pmksa->pmksa = entry->next; + while (entry && entry->expiration <= now.sec) { + if (wpa_key_mgmt_sae(entry->akmp) && + pmksa->is_current_cb(entry, pmksa->ctx)) { + /* Do not expire the currently used PMKSA entry for SAE + * since there is no convenient mechanism for + * reauthenticating during an association with SAE. The + * expired entry will be removed after this association + * has been lost. */ + wpa_printf(MSG_DEBUG, + "RSN: postpone PMKSA cache entry expiration for SAE with " + MACSTR, MAC2STR(entry->aa)); + prev = entry; + entry = entry->next; + continue; + } + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(entry->aa)); - pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); @@ -91,13 +114,32 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) return; os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; - if (sec < 0) + if (sec < 0) { sec = 0; + if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) && + pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) { + /* Do not continue polling for the current PMKSA entry + * from SAE to expire every second. Use the expiration + * time to the following entry, if any, and wait at + * maximum 10 minutes to check again. + */ + entry = pmksa->pmksa->next; + if (entry) { + sec = entry->expiration - now.sec; + if (sec < 0) + sec = 0; + else if (sec > 600) + sec = 600; + } else { + sec = 600; + } + } + } eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); - if (entry) { + if (entry && !wpa_key_mgmt_sae(entry->akmp)) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) sec = 0; @@ -653,6 +695,8 @@ struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), + bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; @@ -660,6 +704,7 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, pmksa = os_zalloc(sizeof(*pmksa)); if (pmksa) { pmksa->free_cb = free_cb; + pmksa->is_current_cb = is_current_cb; pmksa->ctx = ctx; pmksa->sm = sm; } diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index 5f460cc06..b80126859 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -59,6 +59,8 @@ enum pmksa_free_reason { struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), + bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, @@ -93,6 +95,8 @@ void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa); static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), + bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx, struct wpa_sm *sm) { return (void *) -1; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 1bb9cc6bc..8aa649ac4 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -2903,6 +2903,15 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } +static bool wpa_sm_pmksa_is_current_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) +{ + struct wpa_sm *sm = ctx; + + return sm->cur_pmksa == entry; +} + + /** * wpa_sm_init - Initialize WPA state machine * @ctx: Context pointer for callbacks; this needs to be an allocated buffer @@ -2926,7 +2935,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) sm->dot11RSNAConfigPMKReauthThreshold = 70; sm->dot11RSNAConfigSATimeout = 60; - sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); + sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, + wpa_sm_pmksa_is_current_cb, sm, sm); if (sm->pmksa == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "RSN: PMKSA cache initialization failed");