FILS: Flush external-PMKSA when connection fails without ERP keys

External applications can store PMKSA entries persistently and
reconfigure them to wpa_supplicant after restart. This can result in
wpa_supplicant having a PMKSA for FILS authentication without having
matching ERP keys for it which would prevent the previously added
mechanism for dropping FILS PMKSA entries to recover from rejected
association attempts.

Fix this by clearing PMKSA entries configured by external applications
upon FILS connection failure even when ERP keys are not available.

Signed-off-by: Veerendranath Jakkam <vjakkam@codeaurora.org>
This commit is contained in:
Veerendranath Jakkam 2021-07-01 23:39:06 +05:30 committed by Jouni Malinen
parent 80bcd7ecd1
commit b4f7506ff0
6 changed files with 58 additions and 12 deletions

View file

@ -212,7 +212,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
"that was based on the old PMK"); "that was based on the old PMK");
if (!pos->opportunistic) if (!pos->opportunistic)
pmksa_cache_flush(pmksa, entry->network_ctx, pmksa_cache_flush(pmksa, entry->network_ctx,
pos->pmk, pos->pmk_len); pos->pmk, pos->pmk_len,
false);
pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
break; break;
} }
@ -282,9 +283,11 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
* @network_ctx: Network configuration context or %NULL to flush all entries * @network_ctx: Network configuration context or %NULL to flush all entries
* @pmk: PMK to match for or %NULL to match all PMKs * @pmk: PMK to match for or %NULL to match all PMKs
* @pmk_len: PMK length * @pmk_len: PMK length
* @external_only: Flush only PMKSA cache entries configured by external
* applications
*/ */
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len) const u8 *pmk, size_t pmk_len, bool external_only)
{ {
struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
int removed = 0; int removed = 0;
@ -295,7 +298,8 @@ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
network_ctx == NULL) && network_ctx == NULL) &&
(pmk == NULL || (pmk == NULL ||
(pmk_len == entry->pmk_len && (pmk_len == entry->pmk_len &&
os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { os_memcmp(pmk, entry->pmk, pmk_len) == 0)) &&
(!external_only || entry->external)) {
wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
"for " MACSTR, MAC2STR(entry->aa)); "for " MACSTR, MAC2STR(entry->aa));
if (prev) if (prev)

View file

@ -43,6 +43,7 @@ struct rsn_pmksa_cache_entry {
*/ */
void *network_ctx; void *network_ctx;
int opportunistic; int opportunistic;
bool external;
}; };
struct rsn_pmksa_cache; struct rsn_pmksa_cache;
@ -84,7 +85,7 @@ struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
void *network_ctx, const u8 *aa, int akmp); void *network_ctx, const u8 *aa, int akmp);
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len); const u8 *pmk, size_t pmk_len, bool external_only);
#else /* IEEE8021X_EAPOL */ #else /* IEEE8021X_EAPOL */
@ -157,7 +158,8 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
void *network_ctx, void *network_ctx,
const u8 *pmk, size_t pmk_len) const u8 *pmk, size_t pmk_len,
bool external_only)
{ {
} }

View file

@ -2345,6 +2345,16 @@ void wpa_sm_aborted_cached(struct wpa_sm *sm)
} }
void wpa_sm_aborted_external_cached(struct wpa_sm *sm)
{
if (sm && sm->cur_pmksa && sm->cur_pmksa->external) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cancelling external PMKSA caching attempt");
sm->cur_pmksa = NULL;
}
}
static void wpa_eapol_key_dump(struct wpa_sm *sm, static void wpa_eapol_key_dump(struct wpa_sm *sm,
const struct wpa_eapol_key *key, const struct wpa_eapol_key *key,
unsigned int key_data_len, unsigned int key_data_len,
@ -3865,7 +3875,13 @@ void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
{ {
pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false);
}
void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
{
pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true);
} }

View file

@ -180,6 +180,7 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data); struct wpa_ie_data *data);
void wpa_sm_aborted_cached(struct wpa_sm *sm); void wpa_sm_aborted_cached(struct wpa_sm *sm);
void wpa_sm_aborted_external_cached(struct wpa_sm *sm);
int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len); const u8 *buf, size_t len);
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
@ -205,6 +206,7 @@ int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
@ -353,6 +355,10 @@ static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
{ {
} }
static inline void wpa_sm_aborted_external_cached(struct wpa_sm *sm)
{
}
static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len) const u8 *buf, size_t len)
{ {
@ -392,6 +398,11 @@ static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
{ {
} }
static inline void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm,
void *network_ctx)
{
}
static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
void *network_ctx) void *network_ctx)
{ {

View file

@ -10623,6 +10623,8 @@ static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
entry->network_ctx = ssid; entry->network_ctx = ssid;
entry->external = true;
wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
entry = NULL; entry = NULL;
ret = 0; ret = 0;

View file

@ -7549,16 +7549,27 @@ void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
* Check for ERP keys existing to limit when this can be done since * Check for ERP keys existing to limit when this can be done since
* the rejection response is not protected and such triggers should * the rejection response is not protected and such triggers should
* really not allow internal state to be modified unless required to * really not allow internal state to be modified unless required to
* avoid significant issues in functionality. In this case, this is * avoid significant issues in functionality. In addition, drop
* needed to allow recovery from cases where the AP or authentication * externally configure PMKSA entries even without ERP keys since it
* server has dropped PMKSAs and ERP keys. */ * is possible for an external component to add PMKSA entries for FILS
if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) || * authentication without restoring previously generated ERP keys.
eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, *
* In this case, this is needed to allow recovery from cases where the
* AP or authentication server has dropped PMKSAs and ERP keys. */
if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt))
return;
if (eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
&username, &username_len, &username, &username_len,
&realm, &realm_len, &next_seq_num, &realm, &realm_len, &next_seq_num,
&rrk, &rrk_len) != 0 || &rrk, &rrk_len) != 0 ||
!realm) !realm) {
wpa_dbg(wpa_s, MSG_DEBUG,
"FILS: Drop external PMKSA cache entry");
wpa_sm_aborted_external_cached(wpa_s->wpa);
wpa_sm_external_pmksa_cache_flush(wpa_s->wpa, ssid);
return; return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry"); wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry");
wpa_sm_aborted_cached(wpa_s->wpa); wpa_sm_aborted_cached(wpa_s->wpa);