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 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);
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;
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;
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;
}
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,
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;
}
+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
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");