PMKSA: Make deauthentication due to cache entry removal more granular
Expiry can always trigger a deauthentication, but otherwise, deauthentication should only happen when the *current* cache entry is removed and not being replaced. It should not happen when the current PMK just happens to match the PMK of the entry being removed, since multiple entries can have the same PMK when OKC is used and these entries are often removed at different times. This fixes an issue where eviction of the oldest inactive entry due to adding a newer entry to a full cache caused a deauthentication when the entry being removed had the same PMK as the current entry. Signed-hostap: Dan Williams <dcbw@redhat.com>
This commit is contained in:
parent
b7593d35c1
commit
6aaac006af
3 changed files with 43 additions and 35 deletions
|
@ -25,7 +25,7 @@ struct rsn_pmksa_cache {
|
||||||
struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
|
struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
|
||||||
|
|
||||||
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
|
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
|
||||||
int replace);
|
enum pmksa_free_reason reason);
|
||||||
void *ctx;
|
void *ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,11 +41,11 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
|
||||||
|
|
||||||
static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||||
struct rsn_pmksa_cache_entry *entry,
|
struct rsn_pmksa_cache_entry *entry,
|
||||||
int replace)
|
enum pmksa_free_reason reason)
|
||||||
{
|
{
|
||||||
wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
|
wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
|
||||||
pmksa->pmksa_count--;
|
pmksa->pmksa_count--;
|
||||||
pmksa->free_cb(entry, pmksa->ctx, replace);
|
pmksa->free_cb(entry, pmksa->ctx, reason);
|
||||||
_pmksa_cache_free_entry(entry);
|
_pmksa_cache_free_entry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
|
||||||
pmksa->pmksa = entry->next;
|
pmksa->pmksa = entry->next;
|
||||||
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, 0);
|
pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pmksa_cache_set_expiration(pmksa);
|
pmksa_cache_set_expiration(pmksa);
|
||||||
|
@ -164,22 +164,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
||||||
pmksa->pmksa = pos->next;
|
pmksa->pmksa = pos->next;
|
||||||
else
|
else
|
||||||
prev->next = pos->next;
|
prev->next = pos->next;
|
||||||
if (pos == pmksa->sm->cur_pmksa) {
|
|
||||||
/* We are about to replace the current PMKSA
|
|
||||||
* cache entry. This happens when the PMKSA
|
|
||||||
* caching attempt fails, so we don't want to
|
|
||||||
* force pmksa_cache_free_entry() to disconnect
|
|
||||||
* at this point. Let's just make sure the old
|
|
||||||
* PMKSA cache entry will not be used in the
|
|
||||||
* future.
|
|
||||||
*/
|
|
||||||
wpa_printf(MSG_DEBUG, "RSN: replacing current "
|
|
||||||
"PMKSA entry");
|
|
||||||
pmksa->sm->cur_pmksa = NULL;
|
|
||||||
}
|
|
||||||
wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
|
wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
|
||||||
"the current AP");
|
"the current AP");
|
||||||
pmksa_cache_free_entry(pmksa, pos, 1);
|
pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If OKC is used, there may be other PMKSA cache
|
* If OKC is used, there may be other PMKSA cache
|
||||||
|
@ -214,7 +201,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
||||||
"PMKSA cache entry (for " MACSTR ") to "
|
"PMKSA cache entry (for " MACSTR ") to "
|
||||||
"make room for new one",
|
"make room for new one",
|
||||||
MAC2STR(pos->aa));
|
MAC2STR(pos->aa));
|
||||||
pmksa_cache_free_entry(pmksa, pos, 0);
|
pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +252,7 @@ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx)
|
||||||
pmksa->pmksa = entry->next;
|
pmksa->pmksa = entry->next;
|
||||||
tmp = entry;
|
tmp = entry;
|
||||||
entry = entry->next;
|
entry = entry->next;
|
||||||
pmksa_cache_free_entry(pmksa, tmp, 0);
|
pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
|
||||||
removed++;
|
removed++;
|
||||||
} else {
|
} else {
|
||||||
prev = entry;
|
prev = entry;
|
||||||
|
@ -507,7 +494,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
|
||||||
*/
|
*/
|
||||||
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, int replace),
|
void *ctx, enum pmksa_free_reason reason),
|
||||||
void *ctx, struct wpa_sm *sm)
|
void *ctx, struct wpa_sm *sm)
|
||||||
{
|
{
|
||||||
struct rsn_pmksa_cache *pmksa;
|
struct rsn_pmksa_cache *pmksa;
|
||||||
|
|
|
@ -38,11 +38,17 @@ struct rsn_pmksa_cache_entry {
|
||||||
|
|
||||||
struct rsn_pmksa_cache;
|
struct rsn_pmksa_cache;
|
||||||
|
|
||||||
|
enum pmksa_free_reason {
|
||||||
|
PMKSA_FREE,
|
||||||
|
PMKSA_REPLACE,
|
||||||
|
PMKSA_EXPIRE,
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
|
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
|
||||||
|
|
||||||
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, int replace),
|
void *ctx, enum pmksa_free_reason reason),
|
||||||
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,
|
||||||
|
@ -66,7 +72,7 @@ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx);
|
||||||
|
|
||||||
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, int replace),
|
void *ctx, int reason),
|
||||||
void *ctx, struct wpa_sm *sm)
|
void *ctx, struct wpa_sm *sm)
|
||||||
{
|
{
|
||||||
return (void *) -1;
|
return (void *) -1;
|
||||||
|
|
|
@ -1935,25 +1935,40 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
|
||||||
|
|
||||||
|
|
||||||
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
|
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
|
||||||
void *ctx, int replace)
|
void *ctx, enum pmksa_free_reason reason)
|
||||||
{
|
{
|
||||||
struct wpa_sm *sm = ctx;
|
struct wpa_sm *sm = ctx;
|
||||||
|
int deauth = 0;
|
||||||
|
|
||||||
if (sm->cur_pmksa == entry ||
|
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
|
||||||
|
MACSTR " reason=%d", MAC2STR(entry->aa), reason);
|
||||||
|
|
||||||
|
if (sm->cur_pmksa == entry) {
|
||||||
|
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
|
||||||
|
"RSN: %s current PMKSA entry",
|
||||||
|
reason == PMKSA_REPLACE ? "replaced" : "removed");
|
||||||
|
pmksa_cache_clear_current(sm);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If an entry is simply being replaced, there's no need to
|
||||||
|
* deauthenticate because it will be immediately re-added.
|
||||||
|
* This happens when EAP authentication is completed again
|
||||||
|
* (reauth or failed PMKSA caching attempt).
|
||||||
|
*/
|
||||||
|
if (reason != PMKSA_REPLACE)
|
||||||
|
deauth = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason == PMKSA_EXPIRE &&
|
||||||
(sm->pmk_len == entry->pmk_len &&
|
(sm->pmk_len == entry->pmk_len &&
|
||||||
os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
|
os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
|
||||||
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
|
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
|
||||||
"RSN: removed current PMKSA entry");
|
"RSN: deauthenticating due to expired PMK");
|
||||||
sm->cur_pmksa = NULL;
|
pmksa_cache_clear_current(sm);
|
||||||
|
deauth = 1;
|
||||||
if (replace) {
|
}
|
||||||
/* A new entry is being added, so no need to
|
|
||||||
* deauthenticate in this case. This happens when EAP
|
|
||||||
* authentication is completed again (reauth or failed
|
|
||||||
* PMKSA caching attempt). */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (deauth) {
|
||||||
os_memset(sm->pmk, 0, sizeof(sm->pmk));
|
os_memset(sm->pmk, 0, sizeof(sm->pmk));
|
||||||
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
|
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue