From b4f7506ff0ba23f43c0219532a2c847d2834bd00 Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Thu, 1 Jul 2021 23:39:06 +0530 Subject: [PATCH] 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 --- src/rsn_supp/pmksa_cache.c | 10 +++++++--- src/rsn_supp/pmksa_cache.h | 6 ++++-- src/rsn_supp/wpa.c | 18 +++++++++++++++++- src/rsn_supp/wpa.h | 11 +++++++++++ wpa_supplicant/ctrl_iface.c | 2 ++ wpa_supplicant/wpa_supplicant.c | 23 +++++++++++++++++------ 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index cb2a8674a..97a01a2f8 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -212,7 +212,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, "that was based on the old PMK"); if (!pos->opportunistic) 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); 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 * @pmk: PMK to match for or %NULL to match all PMKs * @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, - 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; int removed = 0; @@ -295,7 +298,8 @@ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, network_ctx == NULL) && (pmk == NULL || (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 " "for " MACSTR, MAC2STR(entry->aa)); if (prev) diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index 83faa0584..ae7bc13fa 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -43,6 +43,7 @@ struct rsn_pmksa_cache_entry { */ void *network_ctx; int opportunistic; + bool external; }; struct rsn_pmksa_cache; @@ -84,7 +85,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa, int akmp); 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 */ @@ -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, void *network_ctx, - const u8 *pmk, size_t pmk_len) + const u8 *pmk, size_t pmk_len, + bool external_only) { } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 78e2380b1..e01cd5217 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -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, const struct wpa_eapol_key *key, 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) { - 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); } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index f377acba2..ff8a85b6e 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -180,6 +180,7 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); 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, const u8 *buf, size_t len); 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_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); @@ -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, 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, void *network_ctx) { diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 4e8b30aba..8a6a829b6 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -10623,6 +10623,8 @@ static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s, entry->network_ctx = ssid; + entry->external = true; + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); entry = NULL; ret = 0; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 9b8dca5bc..cc184dba5 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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 * the rejection response is not protected and such triggers should * really not allow internal state to be modified unless required to - * avoid significant issues in functionality. 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) || - eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + * avoid significant issues in functionality. In addition, drop + * externally configure PMKSA entries even without ERP keys since it + * is possible for an external component to add PMKSA entries for FILS + * authentication without restoring previously generated ERP keys. + * + * 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, &realm, &realm_len, &next_seq_num, &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; + } wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry"); wpa_sm_aborted_cached(wpa_s->wpa);