1214501Srpaulo/* 2214501Srpaulo * hostapd - PMKSA cache for IEEE 802.11i RSN 3281806Srpaulo * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "utils/includes.h" 10214501Srpaulo 11214501Srpaulo#include "utils/common.h" 12214501Srpaulo#include "utils/eloop.h" 13214501Srpaulo#include "eapol_auth/eapol_auth_sm.h" 14214501Srpaulo#include "eapol_auth/eapol_auth_sm_i.h" 15281806Srpaulo#include "radius/radius_das.h" 16214501Srpaulo#include "sta_info.h" 17214501Srpaulo#include "ap_config.h" 18214501Srpaulo#include "pmksa_cache_auth.h" 19214501Srpaulo 20214501Srpaulo 21214501Srpaulostatic const int pmksa_cache_max_entries = 1024; 22214501Srpaulostatic const int dot11RSNAConfigPMKLifetime = 43200; 23214501Srpaulo 24214501Srpaulostruct rsn_pmksa_cache { 25214501Srpaulo#define PMKID_HASH_SIZE 128 26214501Srpaulo#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) 27214501Srpaulo struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; 28214501Srpaulo struct rsn_pmksa_cache_entry *pmksa; 29214501Srpaulo int pmksa_count; 30214501Srpaulo 31214501Srpaulo void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); 32214501Srpaulo void *ctx; 33214501Srpaulo}; 34214501Srpaulo 35214501Srpaulo 36214501Srpaulostatic void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 37214501Srpaulo 38214501Srpaulo 39214501Srpaulostatic void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 40214501Srpaulo{ 41337817Scy os_free(entry->vlan_desc); 42214501Srpaulo os_free(entry->identity); 43252726Srpaulo wpabuf_free(entry->cui); 44214501Srpaulo#ifndef CONFIG_NO_RADIUS 45214501Srpaulo radius_free_class(&entry->radius_class); 46214501Srpaulo#endif /* CONFIG_NO_RADIUS */ 47281806Srpaulo bin_clear_free(entry, sizeof(*entry)); 48214501Srpaulo} 49214501Srpaulo 50214501Srpaulo 51281806Srpaulovoid pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, 52281806Srpaulo struct rsn_pmksa_cache_entry *entry) 53214501Srpaulo{ 54214501Srpaulo struct rsn_pmksa_cache_entry *pos, *prev; 55281806Srpaulo unsigned int hash; 56214501Srpaulo 57214501Srpaulo pmksa->pmksa_count--; 58214501Srpaulo pmksa->free_cb(entry, pmksa->ctx); 59281806Srpaulo 60281806Srpaulo /* unlink from hash list */ 61281806Srpaulo hash = PMKID_HASH(entry->pmkid); 62281806Srpaulo pos = pmksa->pmkid[hash]; 63214501Srpaulo prev = NULL; 64214501Srpaulo while (pos) { 65214501Srpaulo if (pos == entry) { 66281806Srpaulo if (prev != NULL) 67281806Srpaulo prev->hnext = entry->hnext; 68281806Srpaulo else 69281806Srpaulo pmksa->pmkid[hash] = entry->hnext; 70214501Srpaulo break; 71214501Srpaulo } 72214501Srpaulo prev = pos; 73214501Srpaulo pos = pos->hnext; 74214501Srpaulo } 75214501Srpaulo 76281806Srpaulo /* unlink from entry list */ 77214501Srpaulo pos = pmksa->pmksa; 78214501Srpaulo prev = NULL; 79214501Srpaulo while (pos) { 80214501Srpaulo if (pos == entry) { 81214501Srpaulo if (prev != NULL) 82281806Srpaulo prev->next = entry->next; 83214501Srpaulo else 84281806Srpaulo pmksa->pmksa = entry->next; 85214501Srpaulo break; 86214501Srpaulo } 87214501Srpaulo prev = pos; 88214501Srpaulo pos = pos->next; 89214501Srpaulo } 90281806Srpaulo 91214501Srpaulo _pmksa_cache_free_entry(entry); 92214501Srpaulo} 93214501Srpaulo 94214501Srpaulo 95337817Scy/** 96337817Scy * pmksa_cache_auth_flush - Flush all PMKSA cache entries 97337817Scy * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 98337817Scy */ 99337817Scyvoid pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa) 100337817Scy{ 101337817Scy while (pmksa->pmksa) { 102337817Scy wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " 103337817Scy MACSTR, MAC2STR(pmksa->pmksa->spa)); 104337817Scy pmksa_cache_free_entry(pmksa, pmksa->pmksa); 105337817Scy } 106337817Scy} 107337817Scy 108337817Scy 109214501Srpaulostatic void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 110214501Srpaulo{ 111214501Srpaulo struct rsn_pmksa_cache *pmksa = eloop_ctx; 112281806Srpaulo struct os_reltime now; 113214501Srpaulo 114281806Srpaulo os_get_reltime(&now); 115214501Srpaulo while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { 116214501Srpaulo wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " 117252726Srpaulo MACSTR, MAC2STR(pmksa->pmksa->spa)); 118252726Srpaulo pmksa_cache_free_entry(pmksa, pmksa->pmksa); 119214501Srpaulo } 120214501Srpaulo 121214501Srpaulo pmksa_cache_set_expiration(pmksa); 122214501Srpaulo} 123214501Srpaulo 124214501Srpaulo 125214501Srpaulostatic void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) 126214501Srpaulo{ 127214501Srpaulo int sec; 128281806Srpaulo struct os_reltime now; 129214501Srpaulo 130214501Srpaulo eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 131214501Srpaulo if (pmksa->pmksa == NULL) 132214501Srpaulo return; 133281806Srpaulo os_get_reltime(&now); 134214501Srpaulo sec = pmksa->pmksa->expiration - now.sec; 135214501Srpaulo if (sec < 0) 136214501Srpaulo sec = 0; 137214501Srpaulo eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); 138214501Srpaulo} 139214501Srpaulo 140214501Srpaulo 141214501Srpaulostatic void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, 142214501Srpaulo struct eapol_state_machine *eapol) 143214501Srpaulo{ 144337817Scy struct vlan_description *vlan_desc; 145337817Scy 146214501Srpaulo if (eapol == NULL) 147214501Srpaulo return; 148214501Srpaulo 149214501Srpaulo if (eapol->identity) { 150214501Srpaulo entry->identity = os_malloc(eapol->identity_len); 151214501Srpaulo if (entry->identity) { 152214501Srpaulo entry->identity_len = eapol->identity_len; 153214501Srpaulo os_memcpy(entry->identity, eapol->identity, 154214501Srpaulo eapol->identity_len); 155214501Srpaulo } 156214501Srpaulo } 157214501Srpaulo 158252726Srpaulo if (eapol->radius_cui) 159252726Srpaulo entry->cui = wpabuf_dup(eapol->radius_cui); 160252726Srpaulo 161214501Srpaulo#ifndef CONFIG_NO_RADIUS 162214501Srpaulo radius_copy_class(&entry->radius_class, &eapol->radius_class); 163214501Srpaulo#endif /* CONFIG_NO_RADIUS */ 164214501Srpaulo 165214501Srpaulo entry->eap_type_authsrv = eapol->eap_type_authsrv; 166281806Srpaulo 167337817Scy vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; 168337817Scy if (vlan_desc && vlan_desc->notempty) { 169337817Scy entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); 170337817Scy if (entry->vlan_desc) 171337817Scy *entry->vlan_desc = *vlan_desc; 172337817Scy } else { 173337817Scy entry->vlan_desc = NULL; 174337817Scy } 175337817Scy 176337817Scy entry->acct_multi_session_id = eapol->acct_multi_session_id; 177214501Srpaulo} 178214501Srpaulo 179214501Srpaulo 180337817Scyvoid pmksa_cache_to_eapol_data(struct hostapd_data *hapd, 181337817Scy struct rsn_pmksa_cache_entry *entry, 182214501Srpaulo struct eapol_state_machine *eapol) 183214501Srpaulo{ 184214501Srpaulo if (entry == NULL || eapol == NULL) 185214501Srpaulo return; 186214501Srpaulo 187214501Srpaulo if (entry->identity) { 188214501Srpaulo os_free(eapol->identity); 189214501Srpaulo eapol->identity = os_malloc(entry->identity_len); 190214501Srpaulo if (eapol->identity) { 191214501Srpaulo eapol->identity_len = entry->identity_len; 192214501Srpaulo os_memcpy(eapol->identity, entry->identity, 193214501Srpaulo entry->identity_len); 194214501Srpaulo } 195214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", 196214501Srpaulo eapol->identity, eapol->identity_len); 197214501Srpaulo } 198214501Srpaulo 199252726Srpaulo if (entry->cui) { 200252726Srpaulo wpabuf_free(eapol->radius_cui); 201252726Srpaulo eapol->radius_cui = wpabuf_dup(entry->cui); 202252726Srpaulo } 203252726Srpaulo 204214501Srpaulo#ifndef CONFIG_NO_RADIUS 205214501Srpaulo radius_free_class(&eapol->radius_class); 206214501Srpaulo radius_copy_class(&eapol->radius_class, &entry->radius_class); 207214501Srpaulo#endif /* CONFIG_NO_RADIUS */ 208214501Srpaulo if (eapol->radius_class.attr) { 209214501Srpaulo wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " 210214501Srpaulo "PMKSA", (unsigned long) eapol->radius_class.count); 211214501Srpaulo } 212214501Srpaulo 213214501Srpaulo eapol->eap_type_authsrv = entry->eap_type_authsrv; 214337817Scy#ifndef CONFIG_NO_VLAN 215337817Scy ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); 216337817Scy#endif /* CONFIG_NO_VLAN */ 217281806Srpaulo 218337817Scy eapol->acct_multi_session_id = entry->acct_multi_session_id; 219214501Srpaulo} 220214501Srpaulo 221214501Srpaulo 222214501Srpaulostatic void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, 223214501Srpaulo struct rsn_pmksa_cache_entry *entry) 224214501Srpaulo{ 225214501Srpaulo struct rsn_pmksa_cache_entry *pos, *prev; 226281806Srpaulo int hash; 227214501Srpaulo 228214501Srpaulo /* Add the new entry; order by expiration time */ 229214501Srpaulo pos = pmksa->pmksa; 230214501Srpaulo prev = NULL; 231214501Srpaulo while (pos) { 232214501Srpaulo if (pos->expiration > entry->expiration) 233214501Srpaulo break; 234214501Srpaulo prev = pos; 235214501Srpaulo pos = pos->next; 236214501Srpaulo } 237214501Srpaulo if (prev == NULL) { 238214501Srpaulo entry->next = pmksa->pmksa; 239214501Srpaulo pmksa->pmksa = entry; 240214501Srpaulo } else { 241214501Srpaulo entry->next = prev->next; 242214501Srpaulo prev->next = entry; 243214501Srpaulo } 244214501Srpaulo 245281806Srpaulo hash = PMKID_HASH(entry->pmkid); 246281806Srpaulo entry->hnext = pmksa->pmkid[hash]; 247281806Srpaulo pmksa->pmkid[hash] = entry; 248281806Srpaulo 249214501Srpaulo pmksa->pmksa_count++; 250252726Srpaulo if (prev == NULL) 251252726Srpaulo pmksa_cache_set_expiration(pmksa); 252214501Srpaulo wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, 253214501Srpaulo MAC2STR(entry->spa)); 254214501Srpaulo wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); 255214501Srpaulo} 256214501Srpaulo 257214501Srpaulo 258214501Srpaulo/** 259214501Srpaulo * pmksa_cache_auth_add - Add a PMKSA cache entry 260214501Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 261214501Srpaulo * @pmk: The new pairwise master key 262214501Srpaulo * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 263337817Scy * @pmkid: Calculated PMKID 264281806Srpaulo * @kck: Key confirmation key or %NULL if not yet derived 265281806Srpaulo * @kck_len: KCK length in bytes 266214501Srpaulo * @aa: Authenticator address 267214501Srpaulo * @spa: Supplicant address 268214501Srpaulo * @session_timeout: Session timeout 269214501Srpaulo * @eapol: Pointer to EAPOL state machine data 270214501Srpaulo * @akmp: WPA_KEY_MGMT_* used in key derivation 271214501Srpaulo * Returns: Pointer to the added PMKSA cache entry or %NULL on error 272214501Srpaulo * 273214501Srpaulo * This function create a PMKSA entry for a new PMK and adds it to the PMKSA 274214501Srpaulo * cache. If an old entry is already in the cache for the same Supplicant, 275214501Srpaulo * this entry will be replaced with the new entry. PMKID will be calculated 276214501Srpaulo * based on the PMK. 277214501Srpaulo */ 278214501Srpaulostruct rsn_pmksa_cache_entry * 279214501Srpaulopmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, 280337817Scy const u8 *pmk, size_t pmk_len, const u8 *pmkid, 281281806Srpaulo const u8 *kck, size_t kck_len, 282281806Srpaulo const u8 *aa, const u8 *spa, int session_timeout, 283281806Srpaulo struct eapol_state_machine *eapol, int akmp) 284214501Srpaulo{ 285346981Scy struct rsn_pmksa_cache_entry *entry; 286346981Scy 287346981Scy entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, 288346981Scy aa, spa, session_timeout, eapol, 289346981Scy akmp); 290346981Scy 291346981Scy if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) 292346981Scy return NULL; 293346981Scy 294346981Scy return entry; 295346981Scy} 296346981Scy 297346981Scy 298346981Scy/** 299346981Scy * pmksa_cache_auth_create_entry - Create a PMKSA cache entry 300346981Scy * @pmk: The new pairwise master key 301346981Scy * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 302346981Scy * @pmkid: Calculated PMKID 303346981Scy * @kck: Key confirmation key or %NULL if not yet derived 304346981Scy * @kck_len: KCK length in bytes 305346981Scy * @aa: Authenticator address 306346981Scy * @spa: Supplicant address 307346981Scy * @session_timeout: Session timeout 308346981Scy * @eapol: Pointer to EAPOL state machine data 309346981Scy * @akmp: WPA_KEY_MGMT_* used in key derivation 310346981Scy * Returns: Pointer to the added PMKSA cache entry or %NULL on error 311346981Scy * 312346981Scy * This function creates a PMKSA entry. 313346981Scy */ 314346981Scystruct rsn_pmksa_cache_entry * 315346981Scypmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, 316346981Scy const u8 *kck, size_t kck_len, const u8 *aa, 317346981Scy const u8 *spa, int session_timeout, 318346981Scy struct eapol_state_machine *eapol, int akmp) 319346981Scy{ 320346981Scy struct rsn_pmksa_cache_entry *entry; 321281806Srpaulo struct os_reltime now; 322214501Srpaulo 323337817Scy if (pmk_len > PMK_LEN_MAX) 324214501Srpaulo return NULL; 325214501Srpaulo 326281806Srpaulo if (wpa_key_mgmt_suite_b(akmp) && !kck) 327281806Srpaulo return NULL; 328281806Srpaulo 329214501Srpaulo entry = os_zalloc(sizeof(*entry)); 330214501Srpaulo if (entry == NULL) 331214501Srpaulo return NULL; 332214501Srpaulo os_memcpy(entry->pmk, pmk, pmk_len); 333214501Srpaulo entry->pmk_len = pmk_len; 334337817Scy if (pmkid) 335337817Scy os_memcpy(entry->pmkid, pmkid, PMKID_LEN); 336337817Scy else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) 337281806Srpaulo rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); 338281806Srpaulo else if (wpa_key_mgmt_suite_b(akmp)) 339281806Srpaulo rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); 340281806Srpaulo else 341346981Scy rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); 342281806Srpaulo os_get_reltime(&now); 343214501Srpaulo entry->expiration = now.sec; 344214501Srpaulo if (session_timeout > 0) 345214501Srpaulo entry->expiration += session_timeout; 346214501Srpaulo else 347214501Srpaulo entry->expiration += dot11RSNAConfigPMKLifetime; 348214501Srpaulo entry->akmp = akmp; 349214501Srpaulo os_memcpy(entry->spa, spa, ETH_ALEN); 350214501Srpaulo pmksa_cache_from_eapol_data(entry, eapol); 351214501Srpaulo 352346981Scy return entry; 353346981Scy} 354346981Scy 355346981Scy 356346981Scy/** 357346981Scy * pmksa_cache_auth_add_entry - Add a PMKSA cache entry 358346981Scy * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 359346981Scy * @entry: Pointer to PMKSA cache entry 360346981Scy * 361346981Scy * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is 362346981Scy * already in the cache for the same Supplicant, this entry will be replaced 363346981Scy * with the new entry. PMKID will be calculated based on the PMK. 364346981Scy */ 365346981Scyint pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, 366346981Scy struct rsn_pmksa_cache_entry *entry) 367346981Scy{ 368346981Scy struct rsn_pmksa_cache_entry *pos; 369346981Scy 370346981Scy if (entry == NULL) 371346981Scy return -1; 372346981Scy 373214501Srpaulo /* Replace an old entry for the same STA (if found) with the new entry 374214501Srpaulo */ 375346981Scy pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); 376214501Srpaulo if (pos) 377214501Srpaulo pmksa_cache_free_entry(pmksa, pos); 378214501Srpaulo 379214501Srpaulo if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 380214501Srpaulo /* Remove the oldest entry to make room for the new entry */ 381214501Srpaulo wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " 382214501Srpaulo "entry (for " MACSTR ") to make room for new one", 383214501Srpaulo MAC2STR(pmksa->pmksa->spa)); 384214501Srpaulo pmksa_cache_free_entry(pmksa, pmksa->pmksa); 385214501Srpaulo } 386214501Srpaulo 387214501Srpaulo pmksa_cache_link_entry(pmksa, entry); 388214501Srpaulo 389346981Scy return 0; 390214501Srpaulo} 391214501Srpaulo 392214501Srpaulo 393214501Srpaulostruct rsn_pmksa_cache_entry * 394214501Srpaulopmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, 395214501Srpaulo const struct rsn_pmksa_cache_entry *old_entry, 396214501Srpaulo const u8 *aa, const u8 *pmkid) 397214501Srpaulo{ 398214501Srpaulo struct rsn_pmksa_cache_entry *entry; 399214501Srpaulo 400214501Srpaulo entry = os_zalloc(sizeof(*entry)); 401214501Srpaulo if (entry == NULL) 402214501Srpaulo return NULL; 403214501Srpaulo os_memcpy(entry->pmkid, pmkid, PMKID_LEN); 404214501Srpaulo os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); 405214501Srpaulo entry->pmk_len = old_entry->pmk_len; 406214501Srpaulo entry->expiration = old_entry->expiration; 407214501Srpaulo entry->akmp = old_entry->akmp; 408214501Srpaulo os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); 409214501Srpaulo entry->opportunistic = 1; 410214501Srpaulo if (old_entry->identity) { 411214501Srpaulo entry->identity = os_malloc(old_entry->identity_len); 412214501Srpaulo if (entry->identity) { 413214501Srpaulo entry->identity_len = old_entry->identity_len; 414214501Srpaulo os_memcpy(entry->identity, old_entry->identity, 415214501Srpaulo old_entry->identity_len); 416214501Srpaulo } 417214501Srpaulo } 418252726Srpaulo if (old_entry->cui) 419252726Srpaulo entry->cui = wpabuf_dup(old_entry->cui); 420214501Srpaulo#ifndef CONFIG_NO_RADIUS 421214501Srpaulo radius_copy_class(&entry->radius_class, &old_entry->radius_class); 422214501Srpaulo#endif /* CONFIG_NO_RADIUS */ 423214501Srpaulo entry->eap_type_authsrv = old_entry->eap_type_authsrv; 424337817Scy if (old_entry->vlan_desc) { 425337817Scy entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); 426337817Scy if (entry->vlan_desc) 427337817Scy *entry->vlan_desc = *old_entry->vlan_desc; 428337817Scy } else { 429337817Scy entry->vlan_desc = NULL; 430337817Scy } 431214501Srpaulo entry->opportunistic = 1; 432214501Srpaulo 433214501Srpaulo pmksa_cache_link_entry(pmksa, entry); 434214501Srpaulo 435214501Srpaulo return entry; 436214501Srpaulo} 437214501Srpaulo 438214501Srpaulo 439214501Srpaulo/** 440214501Srpaulo * pmksa_cache_auth_deinit - Free all entries in PMKSA cache 441214501Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 442214501Srpaulo */ 443214501Srpaulovoid pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) 444214501Srpaulo{ 445214501Srpaulo struct rsn_pmksa_cache_entry *entry, *prev; 446214501Srpaulo int i; 447214501Srpaulo 448214501Srpaulo if (pmksa == NULL) 449214501Srpaulo return; 450214501Srpaulo 451214501Srpaulo entry = pmksa->pmksa; 452214501Srpaulo while (entry) { 453214501Srpaulo prev = entry; 454214501Srpaulo entry = entry->next; 455214501Srpaulo _pmksa_cache_free_entry(prev); 456214501Srpaulo } 457214501Srpaulo eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 458281806Srpaulo pmksa->pmksa_count = 0; 459281806Srpaulo pmksa->pmksa = NULL; 460214501Srpaulo for (i = 0; i < PMKID_HASH_SIZE; i++) 461214501Srpaulo pmksa->pmkid[i] = NULL; 462214501Srpaulo os_free(pmksa); 463214501Srpaulo} 464214501Srpaulo 465214501Srpaulo 466214501Srpaulo/** 467214501Srpaulo * pmksa_cache_auth_get - Fetch a PMKSA cache entry 468214501Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 469214501Srpaulo * @spa: Supplicant address or %NULL to match any 470214501Srpaulo * @pmkid: PMKID or %NULL to match any 471214501Srpaulo * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 472214501Srpaulo */ 473214501Srpaulostruct rsn_pmksa_cache_entry * 474214501Srpaulopmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, 475214501Srpaulo const u8 *spa, const u8 *pmkid) 476214501Srpaulo{ 477214501Srpaulo struct rsn_pmksa_cache_entry *entry; 478214501Srpaulo 479281806Srpaulo if (pmkid) { 480281806Srpaulo for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry; 481281806Srpaulo entry = entry->hnext) { 482281806Srpaulo if ((spa == NULL || 483281806Srpaulo os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && 484281806Srpaulo os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) 485281806Srpaulo return entry; 486281806Srpaulo } 487281806Srpaulo } else { 488281806Srpaulo for (entry = pmksa->pmksa; entry; entry = entry->next) { 489281806Srpaulo if (spa == NULL || 490281806Srpaulo os_memcmp(entry->spa, spa, ETH_ALEN) == 0) 491281806Srpaulo return entry; 492281806Srpaulo } 493214501Srpaulo } 494281806Srpaulo 495214501Srpaulo return NULL; 496214501Srpaulo} 497214501Srpaulo 498214501Srpaulo 499214501Srpaulo/** 500214501Srpaulo * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC 501214501Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 502214501Srpaulo * @aa: Authenticator address 503214501Srpaulo * @spa: Supplicant address 504214501Srpaulo * @pmkid: PMKID 505214501Srpaulo * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 506214501Srpaulo * 507214501Srpaulo * Use opportunistic key caching (OKC) to find a PMK for a supplicant. 508214501Srpaulo */ 509214501Srpaulostruct rsn_pmksa_cache_entry * pmksa_cache_get_okc( 510214501Srpaulo struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, 511214501Srpaulo const u8 *pmkid) 512214501Srpaulo{ 513214501Srpaulo struct rsn_pmksa_cache_entry *entry; 514214501Srpaulo u8 new_pmkid[PMKID_LEN]; 515214501Srpaulo 516281806Srpaulo for (entry = pmksa->pmksa; entry; entry = entry->next) { 517214501Srpaulo if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) 518214501Srpaulo continue; 519214501Srpaulo rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, 520346981Scy entry->akmp); 521214501Srpaulo if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) 522214501Srpaulo return entry; 523214501Srpaulo } 524214501Srpaulo return NULL; 525214501Srpaulo} 526214501Srpaulo 527214501Srpaulo 528214501Srpaulo/** 529214501Srpaulo * pmksa_cache_auth_init - Initialize PMKSA cache 530214501Srpaulo * @free_cb: Callback function to be called when a PMKSA cache entry is freed 531214501Srpaulo * @ctx: Context pointer for free_cb function 532214501Srpaulo * Returns: Pointer to PMKSA cache data or %NULL on failure 533214501Srpaulo */ 534214501Srpaulostruct rsn_pmksa_cache * 535214501Srpaulopmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 536214501Srpaulo void *ctx), void *ctx) 537214501Srpaulo{ 538214501Srpaulo struct rsn_pmksa_cache *pmksa; 539214501Srpaulo 540214501Srpaulo pmksa = os_zalloc(sizeof(*pmksa)); 541214501Srpaulo if (pmksa) { 542214501Srpaulo pmksa->free_cb = free_cb; 543214501Srpaulo pmksa->ctx = ctx; 544214501Srpaulo } 545214501Srpaulo 546214501Srpaulo return pmksa; 547214501Srpaulo} 548281806Srpaulo 549281806Srpaulo 550281806Srpaulostatic int das_attr_match(struct rsn_pmksa_cache_entry *entry, 551281806Srpaulo struct radius_das_attrs *attr) 552281806Srpaulo{ 553281806Srpaulo int match = 0; 554281806Srpaulo 555281806Srpaulo if (attr->sta_addr) { 556281806Srpaulo if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0) 557281806Srpaulo return 0; 558281806Srpaulo match++; 559281806Srpaulo } 560281806Srpaulo 561281806Srpaulo if (attr->acct_multi_session_id) { 562281806Srpaulo char buf[20]; 563281806Srpaulo 564337817Scy if (attr->acct_multi_session_id_len != 16) 565281806Srpaulo return 0; 566337817Scy os_snprintf(buf, sizeof(buf), "%016llX", 567337817Scy (unsigned long long) entry->acct_multi_session_id); 568337817Scy if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) 569281806Srpaulo return 0; 570281806Srpaulo match++; 571281806Srpaulo } 572281806Srpaulo 573281806Srpaulo if (attr->cui) { 574281806Srpaulo if (!entry->cui || 575281806Srpaulo attr->cui_len != wpabuf_len(entry->cui) || 576281806Srpaulo os_memcmp(attr->cui, wpabuf_head(entry->cui), 577281806Srpaulo attr->cui_len) != 0) 578281806Srpaulo return 0; 579281806Srpaulo match++; 580281806Srpaulo } 581281806Srpaulo 582281806Srpaulo if (attr->user_name) { 583281806Srpaulo if (!entry->identity || 584281806Srpaulo attr->user_name_len != entry->identity_len || 585281806Srpaulo os_memcmp(attr->user_name, entry->identity, 586281806Srpaulo attr->user_name_len) != 0) 587281806Srpaulo return 0; 588281806Srpaulo match++; 589281806Srpaulo } 590281806Srpaulo 591281806Srpaulo return match; 592281806Srpaulo} 593281806Srpaulo 594281806Srpaulo 595281806Srpauloint pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, 596281806Srpaulo struct radius_das_attrs *attr) 597281806Srpaulo{ 598281806Srpaulo int found = 0; 599281806Srpaulo struct rsn_pmksa_cache_entry *entry, *prev; 600281806Srpaulo 601281806Srpaulo if (attr->acct_session_id) 602281806Srpaulo return -1; 603281806Srpaulo 604281806Srpaulo entry = pmksa->pmksa; 605281806Srpaulo while (entry) { 606281806Srpaulo if (das_attr_match(entry, attr)) { 607281806Srpaulo found++; 608281806Srpaulo prev = entry; 609281806Srpaulo entry = entry->next; 610281806Srpaulo pmksa_cache_free_entry(pmksa, prev); 611281806Srpaulo continue; 612281806Srpaulo } 613281806Srpaulo entry = entry->next; 614281806Srpaulo } 615281806Srpaulo 616281806Srpaulo return found ? 0 : -1; 617281806Srpaulo} 618337817Scy 619337817Scy 620337817Scy/** 621337817Scy * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache 622337817Scy * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 623337817Scy * @buf: Buffer for the list 624337817Scy * @len: Length of the buffer 625337817Scy * Returns: Number of bytes written to buffer 626337817Scy * 627337817Scy * This function is used to generate a text format representation of the 628337817Scy * current PMKSA cache contents for the ctrl_iface PMKSA command. 629337817Scy */ 630337817Scyint pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) 631337817Scy{ 632337817Scy int i, ret; 633337817Scy char *pos = buf; 634337817Scy struct rsn_pmksa_cache_entry *entry; 635337817Scy struct os_reltime now; 636337817Scy 637337817Scy os_get_reltime(&now); 638337817Scy ret = os_snprintf(pos, buf + len - pos, 639337817Scy "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); 640337817Scy if (os_snprintf_error(buf + len - pos, ret)) 641337817Scy return pos - buf; 642337817Scy pos += ret; 643337817Scy i = 0; 644337817Scy entry = pmksa->pmksa; 645337817Scy while (entry) { 646337817Scy ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", 647337817Scy i, MAC2STR(entry->spa)); 648337817Scy if (os_snprintf_error(buf + len - pos, ret)) 649337817Scy return pos - buf; 650337817Scy pos += ret; 651337817Scy pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, 652337817Scy PMKID_LEN); 653337817Scy ret = os_snprintf(pos, buf + len - pos, " %d %d\n", 654337817Scy (int) (entry->expiration - now.sec), 655337817Scy entry->opportunistic); 656337817Scy if (os_snprintf_error(buf + len - pos, ret)) 657337817Scy return pos - buf; 658337817Scy pos += ret; 659337817Scy entry = entry->next; 660337817Scy } 661337817Scy return pos - buf; 662337817Scy} 663346981Scy 664346981Scy 665346981Scy#ifdef CONFIG_PMKSA_CACHE_EXTERNAL 666346981Scy#ifdef CONFIG_MESH 667346981Scy 668346981Scy/** 669346981Scy * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache 670346981Scy * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 671346981Scy * @addr: MAC address of the peer (NULL means any) 672346981Scy * @buf: Buffer for the list 673346981Scy * @len: Length of the buffer 674346981Scy * Returns: Number of bytes written to buffer 675346981Scy * 676346981Scy * This function is used to generate a text format representation of the 677346981Scy * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store 678346981Scy * in external storage. 679346981Scy */ 680346981Scyint pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, 681346981Scy char *buf, size_t len) 682346981Scy{ 683346981Scy int ret; 684346981Scy char *pos, *end; 685346981Scy struct rsn_pmksa_cache_entry *entry; 686346981Scy struct os_reltime now; 687346981Scy 688346981Scy pos = buf; 689346981Scy end = buf + len; 690346981Scy os_get_reltime(&now); 691346981Scy 692346981Scy 693346981Scy /* 694346981Scy * Entry format: 695346981Scy * <BSSID> <PMKID> <PMK> <expiration in seconds> 696346981Scy */ 697346981Scy for (entry = pmksa->pmksa; entry; entry = entry->next) { 698346981Scy if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0) 699346981Scy continue; 700346981Scy 701346981Scy ret = os_snprintf(pos, end - pos, MACSTR " ", 702346981Scy MAC2STR(entry->spa)); 703346981Scy if (os_snprintf_error(end - pos, ret)) 704346981Scy return 0; 705346981Scy pos += ret; 706346981Scy 707346981Scy pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, 708346981Scy PMKID_LEN); 709346981Scy 710346981Scy ret = os_snprintf(pos, end - pos, " "); 711346981Scy if (os_snprintf_error(end - pos, ret)) 712346981Scy return 0; 713346981Scy pos += ret; 714346981Scy 715346981Scy pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, 716346981Scy entry->pmk_len); 717346981Scy 718346981Scy ret = os_snprintf(pos, end - pos, " %d\n", 719346981Scy (int) (entry->expiration - now.sec)); 720346981Scy if (os_snprintf_error(end - pos, ret)) 721346981Scy return 0; 722346981Scy pos += ret; 723346981Scy } 724346981Scy 725346981Scy return pos - buf; 726346981Scy} 727346981Scy 728346981Scy#endif /* CONFIG_MESH */ 729346981Scy#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 730