139beb93cSSam Leffler /* 239beb93cSSam Leffler * WPA Supplicant - RSN PMKSA cache 35b9c547cSRui Paulo * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 1039beb93cSSam Leffler 1139beb93cSSam Leffler #include "common.h" 1239beb93cSSam Leffler #include "eloop.h" 1339beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h" 14e28a4053SRui Paulo #include "wpa.h" 15e28a4053SRui Paulo #include "wpa_i.h" 1639beb93cSSam Leffler #include "pmksa_cache.h" 1739beb93cSSam Leffler 18780fb4a2SCy Schubert #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) 1939beb93cSSam Leffler 2039beb93cSSam Leffler static const int pmksa_cache_max_entries = 32; 2139beb93cSSam Leffler 2239beb93cSSam Leffler struct rsn_pmksa_cache { 2339beb93cSSam Leffler struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ 2439beb93cSSam Leffler int pmksa_count; /* number of entries in PMKSA cache */ 2539beb93cSSam Leffler struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ 2639beb93cSSam Leffler 2739beb93cSSam Leffler void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, 28f05cddf9SRui Paulo enum pmksa_free_reason reason); 2939beb93cSSam Leffler void *ctx; 3039beb93cSSam Leffler }; 3139beb93cSSam Leffler 3239beb93cSSam Leffler 3339beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 3439beb93cSSam Leffler 3539beb93cSSam Leffler 3639beb93cSSam Leffler static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 3739beb93cSSam Leffler { 385b9c547cSRui Paulo bin_clear_free(entry, sizeof(*entry)); 3939beb93cSSam Leffler } 4039beb93cSSam Leffler 4139beb93cSSam Leffler 4239beb93cSSam Leffler static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, 4339beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry, 44f05cddf9SRui Paulo enum pmksa_free_reason reason) 4539beb93cSSam Leffler { 46*85732ac8SCy Schubert wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa, 47*85732ac8SCy Schubert entry->pmkid, 48*85732ac8SCy Schubert entry->fils_cache_id_set ? entry->fils_cache_id : 49*85732ac8SCy Schubert NULL); 5039beb93cSSam Leffler pmksa->pmksa_count--; 51f05cddf9SRui Paulo pmksa->free_cb(entry, pmksa->ctx, reason); 5239beb93cSSam Leffler _pmksa_cache_free_entry(entry); 5339beb93cSSam Leffler } 5439beb93cSSam Leffler 5539beb93cSSam Leffler 5639beb93cSSam Leffler static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 5739beb93cSSam Leffler { 5839beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = eloop_ctx; 595b9c547cSRui Paulo struct os_reltime now; 6039beb93cSSam Leffler 615b9c547cSRui Paulo os_get_reltime(&now); 6239beb93cSSam Leffler while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { 6339beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 6439beb93cSSam Leffler pmksa->pmksa = entry->next; 6539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " 6639beb93cSSam Leffler MACSTR, MAC2STR(entry->aa)); 67f05cddf9SRui Paulo pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); 6839beb93cSSam Leffler } 6939beb93cSSam Leffler 7039beb93cSSam Leffler pmksa_cache_set_expiration(pmksa); 7139beb93cSSam Leffler } 7239beb93cSSam Leffler 7339beb93cSSam Leffler 7439beb93cSSam Leffler static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) 7539beb93cSSam Leffler { 7639beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = eloop_ctx; 7739beb93cSSam Leffler pmksa->sm->cur_pmksa = NULL; 7839beb93cSSam Leffler eapol_sm_request_reauth(pmksa->sm->eapol); 7939beb93cSSam Leffler } 8039beb93cSSam Leffler 8139beb93cSSam Leffler 8239beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) 8339beb93cSSam Leffler { 8439beb93cSSam Leffler int sec; 8539beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry; 865b9c547cSRui Paulo struct os_reltime now; 8739beb93cSSam Leffler 8839beb93cSSam Leffler eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 8939beb93cSSam Leffler eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); 9039beb93cSSam Leffler if (pmksa->pmksa == NULL) 9139beb93cSSam Leffler return; 925b9c547cSRui Paulo os_get_reltime(&now); 9339beb93cSSam Leffler sec = pmksa->pmksa->expiration - now.sec; 9439beb93cSSam Leffler if (sec < 0) 9539beb93cSSam Leffler sec = 0; 9639beb93cSSam Leffler eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); 9739beb93cSSam Leffler 9839beb93cSSam Leffler entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : 99*85732ac8SCy Schubert pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); 10039beb93cSSam Leffler if (entry) { 10139beb93cSSam Leffler sec = pmksa->pmksa->reauth_time - now.sec; 10239beb93cSSam Leffler if (sec < 0) 10339beb93cSSam Leffler sec = 0; 10439beb93cSSam Leffler eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, 10539beb93cSSam Leffler NULL); 10639beb93cSSam Leffler } 10739beb93cSSam Leffler } 10839beb93cSSam Leffler 10939beb93cSSam Leffler 11039beb93cSSam Leffler /** 11139beb93cSSam Leffler * pmksa_cache_add - Add a PMKSA cache entry 11239beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 11339beb93cSSam Leffler * @pmk: The new pairwise master key 11439beb93cSSam Leffler * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 115780fb4a2SCy Schubert * @pmkid: Calculated PMKID 1165b9c547cSRui Paulo * @kck: Key confirmation key or %NULL if not yet derived 1175b9c547cSRui Paulo * @kck_len: KCK length in bytes 11839beb93cSSam Leffler * @aa: Authenticator address 11939beb93cSSam Leffler * @spa: Supplicant address 12039beb93cSSam Leffler * @network_ctx: Network configuration context for this PMK 12139beb93cSSam Leffler * @akmp: WPA_KEY_MGMT_* used in key derivation 122*85732ac8SCy Schubert * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised 12339beb93cSSam Leffler * Returns: Pointer to the added PMKSA cache entry or %NULL on error 12439beb93cSSam Leffler * 12539beb93cSSam Leffler * This function create a PMKSA entry for a new PMK and adds it to the PMKSA 12639beb93cSSam Leffler * cache. If an old entry is already in the cache for the same Authenticator, 12739beb93cSSam Leffler * this entry will be replaced with the new entry. PMKID will be calculated 12839beb93cSSam Leffler * based on the PMK and the driver interface is notified of the new PMKID. 12939beb93cSSam Leffler */ 13039beb93cSSam Leffler struct rsn_pmksa_cache_entry * 13139beb93cSSam Leffler pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 132780fb4a2SCy Schubert const u8 *pmkid, const u8 *kck, size_t kck_len, 133*85732ac8SCy Schubert const u8 *aa, const u8 *spa, void *network_ctx, int akmp, 134*85732ac8SCy Schubert const u8 *cache_id) 13539beb93cSSam Leffler { 136*85732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry; 1375b9c547cSRui Paulo struct os_reltime now; 13839beb93cSSam Leffler 139780fb4a2SCy Schubert if (pmk_len > PMK_LEN_MAX) 14039beb93cSSam Leffler return NULL; 14139beb93cSSam Leffler 1425b9c547cSRui Paulo if (wpa_key_mgmt_suite_b(akmp) && !kck) 1435b9c547cSRui Paulo return NULL; 1445b9c547cSRui Paulo 14539beb93cSSam Leffler entry = os_zalloc(sizeof(*entry)); 14639beb93cSSam Leffler if (entry == NULL) 14739beb93cSSam Leffler return NULL; 14839beb93cSSam Leffler os_memcpy(entry->pmk, pmk, pmk_len); 14939beb93cSSam Leffler entry->pmk_len = pmk_len; 150780fb4a2SCy Schubert if (pmkid) 151780fb4a2SCy Schubert os_memcpy(entry->pmkid, pmkid, PMKID_LEN); 152780fb4a2SCy Schubert else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) 1535b9c547cSRui Paulo rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); 1545b9c547cSRui Paulo else if (wpa_key_mgmt_suite_b(akmp)) 1555b9c547cSRui Paulo rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); 1565b9c547cSRui Paulo else 157*85732ac8SCy Schubert rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); 1585b9c547cSRui Paulo os_get_reltime(&now); 15939beb93cSSam Leffler entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; 16039beb93cSSam Leffler entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * 16139beb93cSSam Leffler pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; 16239beb93cSSam Leffler entry->akmp = akmp; 163*85732ac8SCy Schubert if (cache_id) { 164*85732ac8SCy Schubert entry->fils_cache_id_set = 1; 165*85732ac8SCy Schubert os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN); 166*85732ac8SCy Schubert } 16739beb93cSSam Leffler os_memcpy(entry->aa, aa, ETH_ALEN); 16839beb93cSSam Leffler entry->network_ctx = network_ctx; 16939beb93cSSam Leffler 170*85732ac8SCy Schubert return pmksa_cache_add_entry(pmksa, entry); 171*85732ac8SCy Schubert } 172*85732ac8SCy Schubert 173*85732ac8SCy Schubert 174*85732ac8SCy Schubert struct rsn_pmksa_cache_entry * 175*85732ac8SCy Schubert pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, 176*85732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry) 177*85732ac8SCy Schubert { 178*85732ac8SCy Schubert struct rsn_pmksa_cache_entry *pos, *prev; 179*85732ac8SCy Schubert 18039beb93cSSam Leffler /* Replace an old entry for the same Authenticator (if found) with the 18139beb93cSSam Leffler * new entry */ 18239beb93cSSam Leffler pos = pmksa->pmksa; 18339beb93cSSam Leffler prev = NULL; 18439beb93cSSam Leffler while (pos) { 185*85732ac8SCy Schubert if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) { 186*85732ac8SCy Schubert if (pos->pmk_len == entry->pmk_len && 187*85732ac8SCy Schubert os_memcmp_const(pos->pmk, entry->pmk, 188*85732ac8SCy Schubert entry->pmk_len) == 0 && 1895b9c547cSRui Paulo os_memcmp_const(pos->pmkid, entry->pmkid, 1905b9c547cSRui Paulo PMKID_LEN) == 0) { 19139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPA: reusing previous " 19239beb93cSSam Leffler "PMKSA entry"); 19339beb93cSSam Leffler os_free(entry); 19439beb93cSSam Leffler return pos; 19539beb93cSSam Leffler } 19639beb93cSSam Leffler if (prev == NULL) 19739beb93cSSam Leffler pmksa->pmksa = pos->next; 19839beb93cSSam Leffler else 19939beb93cSSam Leffler prev->next = pos->next; 200f05cddf9SRui Paulo 201f05cddf9SRui Paulo /* 202f05cddf9SRui Paulo * If OKC is used, there may be other PMKSA cache 203f05cddf9SRui Paulo * entries based on the same PMK. These needs to be 204f05cddf9SRui Paulo * flushed so that a new entry can be created based on 2055b9c547cSRui Paulo * the new PMK. Only clear other entries if they have a 2065b9c547cSRui Paulo * matching PMK and this PMK has been used successfully 2075b9c547cSRui Paulo * with the current AP, i.e., if opportunistic flag has 2085b9c547cSRui Paulo * been cleared in wpa_supplicant_key_neg_complete(). 209f05cddf9SRui Paulo */ 2105b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " 2115b9c547cSRui Paulo "the current AP and any PMKSA cache entry " 2125b9c547cSRui Paulo "that was based on the old PMK"); 2135b9c547cSRui Paulo if (!pos->opportunistic) 214*85732ac8SCy Schubert pmksa_cache_flush(pmksa, entry->network_ctx, 215*85732ac8SCy Schubert pos->pmk, pos->pmk_len); 2165b9c547cSRui Paulo pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); 21739beb93cSSam Leffler break; 21839beb93cSSam Leffler } 21939beb93cSSam Leffler prev = pos; 22039beb93cSSam Leffler pos = pos->next; 22139beb93cSSam Leffler } 22239beb93cSSam Leffler 22339beb93cSSam Leffler if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 22439beb93cSSam Leffler /* Remove the oldest entry to make room for the new entry */ 22539beb93cSSam Leffler pos = pmksa->pmksa; 226f05cddf9SRui Paulo 227f05cddf9SRui Paulo if (pos == pmksa->sm->cur_pmksa) { 228f05cddf9SRui Paulo /* 229f05cddf9SRui Paulo * Never remove the current PMKSA cache entry, since 230f05cddf9SRui Paulo * it's in use, and removing it triggers a needless 231f05cddf9SRui Paulo * deauthentication. 232f05cddf9SRui Paulo */ 233f05cddf9SRui Paulo pos = pos->next; 234f05cddf9SRui Paulo pmksa->pmksa->next = pos ? pos->next : NULL; 235f05cddf9SRui Paulo } else 23639beb93cSSam Leffler pmksa->pmksa = pos->next; 237f05cddf9SRui Paulo 238f05cddf9SRui Paulo if (pos) { 239f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " 240f05cddf9SRui Paulo "PMKSA cache entry (for " MACSTR ") to " 241f05cddf9SRui Paulo "make room for new one", 24239beb93cSSam Leffler MAC2STR(pos->aa)); 243f05cddf9SRui Paulo pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); 244f05cddf9SRui Paulo } 24539beb93cSSam Leffler } 24639beb93cSSam Leffler 24739beb93cSSam Leffler /* Add the new entry; order by expiration time */ 24839beb93cSSam Leffler pos = pmksa->pmksa; 24939beb93cSSam Leffler prev = NULL; 25039beb93cSSam Leffler while (pos) { 25139beb93cSSam Leffler if (pos->expiration > entry->expiration) 25239beb93cSSam Leffler break; 25339beb93cSSam Leffler prev = pos; 25439beb93cSSam Leffler pos = pos->next; 25539beb93cSSam Leffler } 25639beb93cSSam Leffler if (prev == NULL) { 25739beb93cSSam Leffler entry->next = pmksa->pmksa; 25839beb93cSSam Leffler pmksa->pmksa = entry; 25939beb93cSSam Leffler pmksa_cache_set_expiration(pmksa); 26039beb93cSSam Leffler } else { 26139beb93cSSam Leffler entry->next = prev->next; 26239beb93cSSam Leffler prev->next = entry; 26339beb93cSSam Leffler } 26439beb93cSSam Leffler pmksa->pmksa_count++; 265f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR 266*85732ac8SCy Schubert " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); 267*85732ac8SCy Schubert wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, 268*85732ac8SCy Schubert entry->fils_cache_id_set ? entry->fils_cache_id : NULL, 269*85732ac8SCy Schubert entry->pmk, entry->pmk_len); 27039beb93cSSam Leffler 27139beb93cSSam Leffler return entry; 27239beb93cSSam Leffler } 27339beb93cSSam Leffler 27439beb93cSSam Leffler 27539beb93cSSam Leffler /** 276f05cddf9SRui Paulo * pmksa_cache_flush - Flush PMKSA cache entries for a specific network 277f05cddf9SRui Paulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 278f05cddf9SRui Paulo * @network_ctx: Network configuration context or %NULL to flush all entries 2795b9c547cSRui Paulo * @pmk: PMK to match for or %NYLL to match all PMKs 2805b9c547cSRui Paulo * @pmk_len: PMK length 281f05cddf9SRui Paulo */ 2825b9c547cSRui Paulo void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, 2835b9c547cSRui Paulo const u8 *pmk, size_t pmk_len) 284f05cddf9SRui Paulo { 285f05cddf9SRui Paulo struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; 286f05cddf9SRui Paulo int removed = 0; 287f05cddf9SRui Paulo 288f05cddf9SRui Paulo entry = pmksa->pmksa; 289f05cddf9SRui Paulo while (entry) { 2905b9c547cSRui Paulo if ((entry->network_ctx == network_ctx || 2915b9c547cSRui Paulo network_ctx == NULL) && 2925b9c547cSRui Paulo (pmk == NULL || 2935b9c547cSRui Paulo (pmk_len == entry->pmk_len && 2945b9c547cSRui Paulo os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { 295f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " 296f05cddf9SRui Paulo "for " MACSTR, MAC2STR(entry->aa)); 297f05cddf9SRui Paulo if (prev) 298f05cddf9SRui Paulo prev->next = entry->next; 299f05cddf9SRui Paulo else 300f05cddf9SRui Paulo pmksa->pmksa = entry->next; 301f05cddf9SRui Paulo tmp = entry; 302f05cddf9SRui Paulo entry = entry->next; 303f05cddf9SRui Paulo pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); 304f05cddf9SRui Paulo removed++; 305f05cddf9SRui Paulo } else { 306f05cddf9SRui Paulo prev = entry; 307f05cddf9SRui Paulo entry = entry->next; 308f05cddf9SRui Paulo } 309f05cddf9SRui Paulo } 310f05cddf9SRui Paulo if (removed) 311f05cddf9SRui Paulo pmksa_cache_set_expiration(pmksa); 312f05cddf9SRui Paulo } 313f05cddf9SRui Paulo 314f05cddf9SRui Paulo 315f05cddf9SRui Paulo /** 31639beb93cSSam Leffler * pmksa_cache_deinit - Free all entries in PMKSA cache 31739beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 31839beb93cSSam Leffler */ 31939beb93cSSam Leffler void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) 32039beb93cSSam Leffler { 32139beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry, *prev; 32239beb93cSSam Leffler 32339beb93cSSam Leffler if (pmksa == NULL) 32439beb93cSSam Leffler return; 32539beb93cSSam Leffler 32639beb93cSSam Leffler entry = pmksa->pmksa; 32739beb93cSSam Leffler pmksa->pmksa = NULL; 32839beb93cSSam Leffler while (entry) { 32939beb93cSSam Leffler prev = entry; 33039beb93cSSam Leffler entry = entry->next; 33139beb93cSSam Leffler os_free(prev); 33239beb93cSSam Leffler } 33339beb93cSSam Leffler pmksa_cache_set_expiration(pmksa); 33439beb93cSSam Leffler os_free(pmksa); 33539beb93cSSam Leffler } 33639beb93cSSam Leffler 33739beb93cSSam Leffler 33839beb93cSSam Leffler /** 33939beb93cSSam Leffler * pmksa_cache_get - Fetch a PMKSA cache entry 34039beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 34139beb93cSSam Leffler * @aa: Authenticator address or %NULL to match any 34239beb93cSSam Leffler * @pmkid: PMKID or %NULL to match any 343f05cddf9SRui Paulo * @network_ctx: Network context or %NULL to match any 344*85732ac8SCy Schubert * @akmp: Specific AKMP to search for or 0 for any 34539beb93cSSam Leffler * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 34639beb93cSSam Leffler */ 34739beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 348f05cddf9SRui Paulo const u8 *aa, const u8 *pmkid, 349*85732ac8SCy Schubert const void *network_ctx, 350*85732ac8SCy Schubert int akmp) 35139beb93cSSam Leffler { 35239beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 35339beb93cSSam Leffler while (entry) { 35439beb93cSSam Leffler if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && 35539beb93cSSam Leffler (pmkid == NULL || 356f05cddf9SRui Paulo os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && 357*85732ac8SCy Schubert (!akmp || akmp == entry->akmp) && 358f05cddf9SRui Paulo (network_ctx == NULL || network_ctx == entry->network_ctx)) 35939beb93cSSam Leffler return entry; 36039beb93cSSam Leffler entry = entry->next; 36139beb93cSSam Leffler } 36239beb93cSSam Leffler return NULL; 36339beb93cSSam Leffler } 36439beb93cSSam Leffler 36539beb93cSSam Leffler 36639beb93cSSam Leffler static struct rsn_pmksa_cache_entry * 36739beb93cSSam Leffler pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 36839beb93cSSam Leffler const struct rsn_pmksa_cache_entry *old_entry, 36939beb93cSSam Leffler const u8 *aa) 37039beb93cSSam Leffler { 37139beb93cSSam Leffler struct rsn_pmksa_cache_entry *new_entry; 372*85732ac8SCy Schubert os_time_t old_expiration = old_entry->expiration; 37339beb93cSSam Leffler 37439beb93cSSam Leffler new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, 375780fb4a2SCy Schubert NULL, NULL, 0, 37639beb93cSSam Leffler aa, pmksa->sm->own_addr, 377*85732ac8SCy Schubert old_entry->network_ctx, old_entry->akmp, 378*85732ac8SCy Schubert old_entry->fils_cache_id_set ? 379*85732ac8SCy Schubert old_entry->fils_cache_id : NULL); 38039beb93cSSam Leffler if (new_entry == NULL) 38139beb93cSSam Leffler return NULL; 38239beb93cSSam Leffler 38339beb93cSSam Leffler /* TODO: reorder entries based on expiration time? */ 384*85732ac8SCy Schubert new_entry->expiration = old_expiration; 38539beb93cSSam Leffler new_entry->opportunistic = 1; 38639beb93cSSam Leffler 38739beb93cSSam Leffler return new_entry; 38839beb93cSSam Leffler } 38939beb93cSSam Leffler 39039beb93cSSam Leffler 39139beb93cSSam Leffler /** 39239beb93cSSam Leffler * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry 39339beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 39439beb93cSSam Leffler * @network_ctx: Network configuration context 39539beb93cSSam Leffler * @aa: Authenticator address for the new AP 396*85732ac8SCy Schubert * @akmp: Specific AKMP to search for or 0 for any 39739beb93cSSam Leffler * Returns: Pointer to a new PMKSA cache entry or %NULL if not available 39839beb93cSSam Leffler * 39939beb93cSSam Leffler * Try to create a new PMKSA cache entry opportunistically by guessing that the 40039beb93cSSam Leffler * new AP is sharing the same PMK as another AP that has the same SSID and has 40139beb93cSSam Leffler * already an entry in PMKSA cache. 40239beb93cSSam Leffler */ 40339beb93cSSam Leffler struct rsn_pmksa_cache_entry * 40439beb93cSSam Leffler pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, 405*85732ac8SCy Schubert const u8 *aa, int akmp) 40639beb93cSSam Leffler { 40739beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 40839beb93cSSam Leffler 409f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); 41039beb93cSSam Leffler if (network_ctx == NULL) 41139beb93cSSam Leffler return NULL; 41239beb93cSSam Leffler while (entry) { 413*85732ac8SCy Schubert if (entry->network_ctx == network_ctx && 414*85732ac8SCy Schubert (!akmp || entry->akmp == akmp)) { 41539beb93cSSam Leffler entry = pmksa_cache_clone_entry(pmksa, entry, aa); 41639beb93cSSam Leffler if (entry) { 41739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RSN: added " 41839beb93cSSam Leffler "opportunistic PMKSA cache entry " 41939beb93cSSam Leffler "for " MACSTR, MAC2STR(aa)); 42039beb93cSSam Leffler } 42139beb93cSSam Leffler return entry; 42239beb93cSSam Leffler } 42339beb93cSSam Leffler entry = entry->next; 42439beb93cSSam Leffler } 42539beb93cSSam Leffler return NULL; 42639beb93cSSam Leffler } 42739beb93cSSam Leffler 42839beb93cSSam Leffler 429*85732ac8SCy Schubert static struct rsn_pmksa_cache_entry * 430*85732ac8SCy Schubert pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa, 431*85732ac8SCy Schubert const void *network_ctx, const u8 *cache_id) 432*85732ac8SCy Schubert { 433*85732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry; 434*85732ac8SCy Schubert 435*85732ac8SCy Schubert for (entry = pmksa->pmksa; entry; entry = entry->next) { 436*85732ac8SCy Schubert if (network_ctx == entry->network_ctx && 437*85732ac8SCy Schubert entry->fils_cache_id_set && 438*85732ac8SCy Schubert os_memcmp(cache_id, entry->fils_cache_id, 439*85732ac8SCy Schubert FILS_CACHE_ID_LEN) == 0) 440*85732ac8SCy Schubert return entry; 441*85732ac8SCy Schubert } 442*85732ac8SCy Schubert 443*85732ac8SCy Schubert return NULL; 444*85732ac8SCy Schubert } 445*85732ac8SCy Schubert 446*85732ac8SCy Schubert 44739beb93cSSam Leffler /** 44839beb93cSSam Leffler * pmksa_cache_get_current - Get the current used PMKSA entry 44939beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 45039beb93cSSam Leffler * Returns: Pointer to the current PMKSA cache entry or %NULL if not available 45139beb93cSSam Leffler */ 45239beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) 45339beb93cSSam Leffler { 45439beb93cSSam Leffler if (sm == NULL) 45539beb93cSSam Leffler return NULL; 45639beb93cSSam Leffler return sm->cur_pmksa; 45739beb93cSSam Leffler } 45839beb93cSSam Leffler 45939beb93cSSam Leffler 46039beb93cSSam Leffler /** 46139beb93cSSam Leffler * pmksa_cache_clear_current - Clear the current PMKSA entry selection 46239beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 46339beb93cSSam Leffler */ 46439beb93cSSam Leffler void pmksa_cache_clear_current(struct wpa_sm *sm) 46539beb93cSSam Leffler { 46639beb93cSSam Leffler if (sm == NULL) 46739beb93cSSam Leffler return; 46839beb93cSSam Leffler sm->cur_pmksa = NULL; 46939beb93cSSam Leffler } 47039beb93cSSam Leffler 47139beb93cSSam Leffler 47239beb93cSSam Leffler /** 47339beb93cSSam Leffler * pmksa_cache_set_current - Set the current PMKSA entry selection 47439beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 47539beb93cSSam Leffler * @pmkid: PMKID for selecting PMKSA or %NULL if not used 47639beb93cSSam Leffler * @bssid: BSSID for PMKSA or %NULL if not used 47739beb93cSSam Leffler * @network_ctx: Network configuration context 47839beb93cSSam Leffler * @try_opportunistic: Whether to allow opportunistic PMKSA caching 479*85732ac8SCy Schubert * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used 48039beb93cSSam Leffler * Returns: 0 if PMKSA was found or -1 if no matching entry was found 48139beb93cSSam Leffler */ 48239beb93cSSam Leffler int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 48339beb93cSSam Leffler const u8 *bssid, void *network_ctx, 484*85732ac8SCy Schubert int try_opportunistic, const u8 *fils_cache_id, 485*85732ac8SCy Schubert int akmp) 48639beb93cSSam Leffler { 48739beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = sm->pmksa; 488f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " 489*85732ac8SCy Schubert "try_opportunistic=%d akmp=0x%x", 490*85732ac8SCy Schubert network_ctx, try_opportunistic, akmp); 491f05cddf9SRui Paulo if (pmkid) 492f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", 493f05cddf9SRui Paulo pmkid, PMKID_LEN); 494f05cddf9SRui Paulo if (bssid) 495f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, 496f05cddf9SRui Paulo MAC2STR(bssid)); 497*85732ac8SCy Schubert if (fils_cache_id) 498*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, 499*85732ac8SCy Schubert "RSN: Search for FILS Cache Identifier %02x%02x", 500*85732ac8SCy Schubert fils_cache_id[0], fils_cache_id[1]); 501f05cddf9SRui Paulo 50239beb93cSSam Leffler sm->cur_pmksa = NULL; 50339beb93cSSam Leffler if (pmkid) 504f05cddf9SRui Paulo sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, 505*85732ac8SCy Schubert network_ctx, akmp); 50639beb93cSSam Leffler if (sm->cur_pmksa == NULL && bssid) 507f05cddf9SRui Paulo sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, 508*85732ac8SCy Schubert network_ctx, akmp); 50939beb93cSSam Leffler if (sm->cur_pmksa == NULL && try_opportunistic && bssid) 51039beb93cSSam Leffler sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, 51139beb93cSSam Leffler network_ctx, 512*85732ac8SCy Schubert bssid, akmp); 513*85732ac8SCy Schubert if (sm->cur_pmksa == NULL && fils_cache_id) 514*85732ac8SCy Schubert sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa, 515*85732ac8SCy Schubert network_ctx, 516*85732ac8SCy Schubert fils_cache_id); 51739beb93cSSam Leffler if (sm->cur_pmksa) { 518f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", 51939beb93cSSam Leffler sm->cur_pmksa->pmkid, PMKID_LEN); 52039beb93cSSam Leffler return 0; 52139beb93cSSam Leffler } 522f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); 52339beb93cSSam Leffler return -1; 52439beb93cSSam Leffler } 52539beb93cSSam Leffler 52639beb93cSSam Leffler 52739beb93cSSam Leffler /** 52839beb93cSSam Leffler * pmksa_cache_list - Dump text list of entries in PMKSA cache 529e28a4053SRui Paulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 53039beb93cSSam Leffler * @buf: Buffer for the list 53139beb93cSSam Leffler * @len: Length of the buffer 53239beb93cSSam Leffler * Returns: number of bytes written to buffer 53339beb93cSSam Leffler * 53439beb93cSSam Leffler * This function is used to generate a text format representation of the 53539beb93cSSam Leffler * current PMKSA cache contents for the ctrl_iface PMKSA command. 53639beb93cSSam Leffler */ 537e28a4053SRui Paulo int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) 53839beb93cSSam Leffler { 53939beb93cSSam Leffler int i, ret; 54039beb93cSSam Leffler char *pos = buf; 54139beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry; 5425b9c547cSRui Paulo struct os_reltime now; 543*85732ac8SCy Schubert int cache_id_used = 0; 544*85732ac8SCy Schubert 545*85732ac8SCy Schubert for (entry = pmksa->pmksa; entry; entry = entry->next) { 546*85732ac8SCy Schubert if (entry->fils_cache_id_set) { 547*85732ac8SCy Schubert cache_id_used = 1; 548*85732ac8SCy Schubert break; 549*85732ac8SCy Schubert } 550*85732ac8SCy Schubert } 55139beb93cSSam Leffler 5525b9c547cSRui Paulo os_get_reltime(&now); 55339beb93cSSam Leffler ret = os_snprintf(pos, buf + len - pos, 55439beb93cSSam Leffler "Index / AA / PMKID / expiration (in seconds) / " 555*85732ac8SCy Schubert "opportunistic%s\n", 556*85732ac8SCy Schubert cache_id_used ? " / FILS Cache Identifier" : ""); 5575b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret)) 55839beb93cSSam Leffler return pos - buf; 55939beb93cSSam Leffler pos += ret; 56039beb93cSSam Leffler i = 0; 561e28a4053SRui Paulo entry = pmksa->pmksa; 56239beb93cSSam Leffler while (entry) { 56339beb93cSSam Leffler i++; 56439beb93cSSam Leffler ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", 56539beb93cSSam Leffler i, MAC2STR(entry->aa)); 5665b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret)) 56739beb93cSSam Leffler return pos - buf; 56839beb93cSSam Leffler pos += ret; 56939beb93cSSam Leffler pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, 57039beb93cSSam Leffler PMKID_LEN); 571*85732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, " %d %d", 57239beb93cSSam Leffler (int) (entry->expiration - now.sec), 57339beb93cSSam Leffler entry->opportunistic); 5745b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret)) 57539beb93cSSam Leffler return pos - buf; 57639beb93cSSam Leffler pos += ret; 577*85732ac8SCy Schubert if (entry->fils_cache_id_set) { 578*85732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, " %02x%02x", 579*85732ac8SCy Schubert entry->fils_cache_id[0], 580*85732ac8SCy Schubert entry->fils_cache_id[1]); 581*85732ac8SCy Schubert if (os_snprintf_error(buf + len - pos, ret)) 582*85732ac8SCy Schubert return pos - buf; 583*85732ac8SCy Schubert pos += ret; 584*85732ac8SCy Schubert } 585*85732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, "\n"); 586*85732ac8SCy Schubert if (os_snprintf_error(buf + len - pos, ret)) 587*85732ac8SCy Schubert return pos - buf; 588*85732ac8SCy Schubert pos += ret; 58939beb93cSSam Leffler entry = entry->next; 59039beb93cSSam Leffler } 59139beb93cSSam Leffler return pos - buf; 59239beb93cSSam Leffler } 59339beb93cSSam Leffler 59439beb93cSSam Leffler 595*85732ac8SCy Schubert struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) 596*85732ac8SCy Schubert { 597*85732ac8SCy Schubert return pmksa->pmksa; 598*85732ac8SCy Schubert } 599*85732ac8SCy Schubert 600*85732ac8SCy Schubert 60139beb93cSSam Leffler /** 60239beb93cSSam Leffler * pmksa_cache_init - Initialize PMKSA cache 60339beb93cSSam Leffler * @free_cb: Callback function to be called when a PMKSA cache entry is freed 60439beb93cSSam Leffler * @ctx: Context pointer for free_cb function 60539beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 60639beb93cSSam Leffler * Returns: Pointer to PMKSA cache data or %NULL on failure 60739beb93cSSam Leffler */ 60839beb93cSSam Leffler struct rsn_pmksa_cache * 60939beb93cSSam Leffler pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 610f05cddf9SRui Paulo void *ctx, enum pmksa_free_reason reason), 61139beb93cSSam Leffler void *ctx, struct wpa_sm *sm) 61239beb93cSSam Leffler { 61339beb93cSSam Leffler struct rsn_pmksa_cache *pmksa; 61439beb93cSSam Leffler 61539beb93cSSam Leffler pmksa = os_zalloc(sizeof(*pmksa)); 61639beb93cSSam Leffler if (pmksa) { 61739beb93cSSam Leffler pmksa->free_cb = free_cb; 61839beb93cSSam Leffler pmksa->ctx = ctx; 61939beb93cSSam Leffler pmksa->sm = sm; 62039beb93cSSam Leffler } 62139beb93cSSam Leffler 62239beb93cSSam Leffler return pmksa; 62339beb93cSSam Leffler } 62439beb93cSSam Leffler 6255b9c547cSRui Paulo #endif /* IEEE8021X_EAPOL */ 626