SAE: Do not expire the current PMKSA cache entry

There is no convenient mechanism for reauthenticating and generating a
new PMK during an association with SAE. As such, forced PMK update would
mean having to disassociate and reauthenticate which is not really
desired especially when the default PMKLifetime is only 12 hours.

Postpone PMKSA cache entry expiration of the currently used entry with
SAE until the association is lost. In addition, do not try to force the
EAPOL state machine to perform reauthentication for SAE since that won't
work.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2021-10-18 20:02:35 +03:00 committed by Jouni Malinen
parent f332f69513
commit b0f457b619
3 changed files with 66 additions and 7 deletions

View file

@ -26,6 +26,8 @@ struct rsn_pmksa_cache {
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
enum pmksa_free_reason reason); enum pmksa_free_reason reason);
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx);
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 rsn_pmksa_cache *pmksa = eloop_ctx;
struct os_reltime now; struct os_reltime now;
struct rsn_pmksa_cache_entry *prev = NULL, *tmp;
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
os_get_reltime(&now); os_get_reltime(&now);
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { while (entry && entry->expiration <= now.sec) {
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; if (wpa_key_mgmt_sae(entry->akmp) &&
pmksa->pmksa = entry->next; 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 " wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(entry->aa)); 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); pmksa_cache_set_expiration(pmksa);
@ -91,13 +114,32 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
return; return;
os_get_reltime(&now); os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec; sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0) if (sec < 0) {
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); eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); 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; sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0) if (sec < 0)
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 * struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason), 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 *ctx, struct wpa_sm *sm)
{ {
struct rsn_pmksa_cache *pmksa; 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)); pmksa = os_zalloc(sizeof(*pmksa));
if (pmksa) { if (pmksa) {
pmksa->free_cb = free_cb; pmksa->free_cb = free_cb;
pmksa->is_current_cb = is_current_cb;
pmksa->ctx = ctx; pmksa->ctx = ctx;
pmksa->sm = sm; pmksa->sm = sm;
} }

View file

@ -59,6 +59,8 @@ enum pmksa_free_reason {
struct rsn_pmksa_cache * struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason), 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 *ctx, struct wpa_sm *sm);
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(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 * static inline struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason), 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 *ctx, struct wpa_sm *sm)
{ {
return (void *) -1; return (void *) -1;

View file

@ -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 * wpa_sm_init - Initialize WPA state machine
* @ctx: Context pointer for callbacks; this needs to be an allocated buffer * @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->dot11RSNAConfigPMKReauthThreshold = 70;
sm->dot11RSNAConfigSATimeout = 60; 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) { if (sm->pmksa == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"RSN: PMKSA cache initialization failed"); "RSN: PMKSA cache initialization failed");