1189251Ssam/* 2189251Ssam * WPA Supplicant - RSN PMKSA cache 3252726Srpaulo * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "eloop.h" 13214734Srpaulo#include "eapol_supp/eapol_supp_sm.h" 14189251Ssam#include "wpa.h" 15189251Ssam#include "wpa_i.h" 16189251Ssam#include "pmksa_cache.h" 17189251Ssam 18189251Ssam#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) 19189251Ssam 20189251Ssamstatic const int pmksa_cache_max_entries = 32; 21189251Ssam 22189251Ssamstruct rsn_pmksa_cache { 23189251Ssam struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ 24189251Ssam int pmksa_count; /* number of entries in PMKSA cache */ 25189251Ssam struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ 26189251Ssam 27189251Ssam void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, 28252726Srpaulo enum pmksa_free_reason reason); 29189251Ssam void *ctx; 30189251Ssam}; 31189251Ssam 32189251Ssam 33189251Ssamstatic void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 34189251Ssam 35189251Ssam 36189251Ssamstatic void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 37189251Ssam{ 38189251Ssam os_free(entry); 39189251Ssam} 40189251Ssam 41189251Ssam 42189251Ssamstatic void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, 43189251Ssam struct rsn_pmksa_cache_entry *entry, 44252726Srpaulo enum pmksa_free_reason reason) 45189251Ssam{ 46252726Srpaulo wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); 47189251Ssam pmksa->pmksa_count--; 48252726Srpaulo pmksa->free_cb(entry, pmksa->ctx, reason); 49189251Ssam _pmksa_cache_free_entry(entry); 50189251Ssam} 51189251Ssam 52189251Ssam 53189251Ssamstatic void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 54189251Ssam{ 55189251Ssam struct rsn_pmksa_cache *pmksa = eloop_ctx; 56189251Ssam struct os_time now; 57189251Ssam 58189251Ssam os_get_time(&now); 59189251Ssam while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { 60189251Ssam struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 61189251Ssam pmksa->pmksa = entry->next; 62189251Ssam wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " 63189251Ssam MACSTR, MAC2STR(entry->aa)); 64252726Srpaulo pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); 65189251Ssam } 66189251Ssam 67189251Ssam pmksa_cache_set_expiration(pmksa); 68189251Ssam} 69189251Ssam 70189251Ssam 71189251Ssamstatic void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) 72189251Ssam{ 73189251Ssam struct rsn_pmksa_cache *pmksa = eloop_ctx; 74189251Ssam pmksa->sm->cur_pmksa = NULL; 75189251Ssam eapol_sm_request_reauth(pmksa->sm->eapol); 76189251Ssam} 77189251Ssam 78189251Ssam 79189251Ssamstatic void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) 80189251Ssam{ 81189251Ssam int sec; 82189251Ssam struct rsn_pmksa_cache_entry *entry; 83189251Ssam struct os_time now; 84189251Ssam 85189251Ssam eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 86189251Ssam eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); 87189251Ssam if (pmksa->pmksa == NULL) 88189251Ssam return; 89189251Ssam os_get_time(&now); 90189251Ssam sec = pmksa->pmksa->expiration - now.sec; 91189251Ssam if (sec < 0) 92189251Ssam sec = 0; 93189251Ssam eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); 94189251Ssam 95189251Ssam entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : 96252726Srpaulo pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); 97189251Ssam if (entry) { 98189251Ssam sec = pmksa->pmksa->reauth_time - now.sec; 99189251Ssam if (sec < 0) 100189251Ssam sec = 0; 101189251Ssam eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, 102189251Ssam NULL); 103189251Ssam } 104189251Ssam} 105189251Ssam 106189251Ssam 107189251Ssam/** 108189251Ssam * pmksa_cache_add - Add a PMKSA cache entry 109189251Ssam * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 110189251Ssam * @pmk: The new pairwise master key 111189251Ssam * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 112189251Ssam * @aa: Authenticator address 113189251Ssam * @spa: Supplicant address 114189251Ssam * @network_ctx: Network configuration context for this PMK 115189251Ssam * @akmp: WPA_KEY_MGMT_* used in key derivation 116189251Ssam * Returns: Pointer to the added PMKSA cache entry or %NULL on error 117189251Ssam * 118189251Ssam * This function create a PMKSA entry for a new PMK and adds it to the PMKSA 119189251Ssam * cache. If an old entry is already in the cache for the same Authenticator, 120189251Ssam * this entry will be replaced with the new entry. PMKID will be calculated 121189251Ssam * based on the PMK and the driver interface is notified of the new PMKID. 122189251Ssam */ 123189251Ssamstruct rsn_pmksa_cache_entry * 124189251Ssampmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 125189251Ssam const u8 *aa, const u8 *spa, void *network_ctx, int akmp) 126189251Ssam{ 127189251Ssam struct rsn_pmksa_cache_entry *entry, *pos, *prev; 128189251Ssam struct os_time now; 129189251Ssam 130214734Srpaulo if (pmk_len > PMK_LEN) 131189251Ssam return NULL; 132189251Ssam 133189251Ssam entry = os_zalloc(sizeof(*entry)); 134189251Ssam if (entry == NULL) 135189251Ssam return NULL; 136189251Ssam os_memcpy(entry->pmk, pmk, pmk_len); 137189251Ssam entry->pmk_len = pmk_len; 138189251Ssam rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, 139189251Ssam wpa_key_mgmt_sha256(akmp)); 140189251Ssam os_get_time(&now); 141189251Ssam entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; 142189251Ssam entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * 143189251Ssam pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; 144189251Ssam entry->akmp = akmp; 145189251Ssam os_memcpy(entry->aa, aa, ETH_ALEN); 146189251Ssam entry->network_ctx = network_ctx; 147189251Ssam 148189251Ssam /* Replace an old entry for the same Authenticator (if found) with the 149189251Ssam * new entry */ 150189251Ssam pos = pmksa->pmksa; 151189251Ssam prev = NULL; 152189251Ssam while (pos) { 153189251Ssam if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { 154189251Ssam if (pos->pmk_len == pmk_len && 155189251Ssam os_memcmp(pos->pmk, pmk, pmk_len) == 0 && 156189251Ssam os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == 157189251Ssam 0) { 158189251Ssam wpa_printf(MSG_DEBUG, "WPA: reusing previous " 159189251Ssam "PMKSA entry"); 160189251Ssam os_free(entry); 161189251Ssam return pos; 162189251Ssam } 163189251Ssam if (prev == NULL) 164189251Ssam pmksa->pmksa = pos->next; 165189251Ssam else 166189251Ssam prev->next = pos->next; 167189251Ssam wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " 168189251Ssam "the current AP"); 169252726Srpaulo pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); 170252726Srpaulo 171252726Srpaulo /* 172252726Srpaulo * If OKC is used, there may be other PMKSA cache 173252726Srpaulo * entries based on the same PMK. These needs to be 174252726Srpaulo * flushed so that a new entry can be created based on 175252726Srpaulo * the new PMK. 176252726Srpaulo */ 177252726Srpaulo pmksa_cache_flush(pmksa, network_ctx); 178189251Ssam break; 179189251Ssam } 180189251Ssam prev = pos; 181189251Ssam pos = pos->next; 182189251Ssam } 183189251Ssam 184189251Ssam if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 185189251Ssam /* Remove the oldest entry to make room for the new entry */ 186189251Ssam pos = pmksa->pmksa; 187252726Srpaulo 188252726Srpaulo if (pos == pmksa->sm->cur_pmksa) { 189252726Srpaulo /* 190252726Srpaulo * Never remove the current PMKSA cache entry, since 191252726Srpaulo * it's in use, and removing it triggers a needless 192252726Srpaulo * deauthentication. 193252726Srpaulo */ 194252726Srpaulo pos = pos->next; 195252726Srpaulo pmksa->pmksa->next = pos ? pos->next : NULL; 196252726Srpaulo } else 197252726Srpaulo pmksa->pmksa = pos->next; 198252726Srpaulo 199252726Srpaulo if (pos) { 200252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " 201252726Srpaulo "PMKSA cache entry (for " MACSTR ") to " 202252726Srpaulo "make room for new one", 203252726Srpaulo MAC2STR(pos->aa)); 204252726Srpaulo pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); 205252726Srpaulo } 206189251Ssam } 207189251Ssam 208189251Ssam /* Add the new entry; order by expiration time */ 209189251Ssam pos = pmksa->pmksa; 210189251Ssam prev = NULL; 211189251Ssam while (pos) { 212189251Ssam if (pos->expiration > entry->expiration) 213189251Ssam break; 214189251Ssam prev = pos; 215189251Ssam pos = pos->next; 216189251Ssam } 217189251Ssam if (prev == NULL) { 218189251Ssam entry->next = pmksa->pmksa; 219189251Ssam pmksa->pmksa = entry; 220189251Ssam pmksa_cache_set_expiration(pmksa); 221189251Ssam } else { 222189251Ssam entry->next = prev->next; 223189251Ssam prev->next = entry; 224189251Ssam } 225189251Ssam pmksa->pmksa_count++; 226252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR 227252726Srpaulo " network_ctx=%p", MAC2STR(entry->aa), network_ctx); 228189251Ssam wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); 229189251Ssam 230189251Ssam return entry; 231189251Ssam} 232189251Ssam 233189251Ssam 234189251Ssam/** 235252726Srpaulo * pmksa_cache_flush - Flush PMKSA cache entries for a specific network 236252726Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 237252726Srpaulo * @network_ctx: Network configuration context or %NULL to flush all entries 238252726Srpaulo */ 239252726Srpaulovoid pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx) 240252726Srpaulo{ 241252726Srpaulo struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; 242252726Srpaulo int removed = 0; 243252726Srpaulo 244252726Srpaulo entry = pmksa->pmksa; 245252726Srpaulo while (entry) { 246252726Srpaulo if (entry->network_ctx == network_ctx || network_ctx == NULL) { 247252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " 248252726Srpaulo "for " MACSTR, MAC2STR(entry->aa)); 249252726Srpaulo if (prev) 250252726Srpaulo prev->next = entry->next; 251252726Srpaulo else 252252726Srpaulo pmksa->pmksa = entry->next; 253252726Srpaulo tmp = entry; 254252726Srpaulo entry = entry->next; 255252726Srpaulo pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); 256252726Srpaulo removed++; 257252726Srpaulo } else { 258252726Srpaulo prev = entry; 259252726Srpaulo entry = entry->next; 260252726Srpaulo } 261252726Srpaulo } 262252726Srpaulo if (removed) 263252726Srpaulo pmksa_cache_set_expiration(pmksa); 264252726Srpaulo} 265252726Srpaulo 266252726Srpaulo 267252726Srpaulo/** 268189251Ssam * pmksa_cache_deinit - Free all entries in PMKSA cache 269189251Ssam * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 270189251Ssam */ 271189251Ssamvoid pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) 272189251Ssam{ 273189251Ssam struct rsn_pmksa_cache_entry *entry, *prev; 274189251Ssam 275189251Ssam if (pmksa == NULL) 276189251Ssam return; 277189251Ssam 278189251Ssam entry = pmksa->pmksa; 279189251Ssam pmksa->pmksa = NULL; 280189251Ssam while (entry) { 281189251Ssam prev = entry; 282189251Ssam entry = entry->next; 283189251Ssam os_free(prev); 284189251Ssam } 285189251Ssam pmksa_cache_set_expiration(pmksa); 286189251Ssam os_free(pmksa); 287189251Ssam} 288189251Ssam 289189251Ssam 290189251Ssam/** 291189251Ssam * pmksa_cache_get - Fetch a PMKSA cache entry 292189251Ssam * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 293189251Ssam * @aa: Authenticator address or %NULL to match any 294189251Ssam * @pmkid: PMKID or %NULL to match any 295252726Srpaulo * @network_ctx: Network context or %NULL to match any 296189251Ssam * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 297189251Ssam */ 298189251Ssamstruct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 299252726Srpaulo const u8 *aa, const u8 *pmkid, 300252726Srpaulo const void *network_ctx) 301189251Ssam{ 302189251Ssam struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 303189251Ssam while (entry) { 304189251Ssam if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && 305189251Ssam (pmkid == NULL || 306252726Srpaulo os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && 307252726Srpaulo (network_ctx == NULL || network_ctx == entry->network_ctx)) 308189251Ssam return entry; 309189251Ssam entry = entry->next; 310189251Ssam } 311189251Ssam return NULL; 312189251Ssam} 313189251Ssam 314189251Ssam 315189251Ssamstatic struct rsn_pmksa_cache_entry * 316189251Ssampmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 317189251Ssam const struct rsn_pmksa_cache_entry *old_entry, 318189251Ssam const u8 *aa) 319189251Ssam{ 320189251Ssam struct rsn_pmksa_cache_entry *new_entry; 321189251Ssam 322189251Ssam new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, 323189251Ssam aa, pmksa->sm->own_addr, 324189251Ssam old_entry->network_ctx, old_entry->akmp); 325189251Ssam if (new_entry == NULL) 326189251Ssam return NULL; 327189251Ssam 328189251Ssam /* TODO: reorder entries based on expiration time? */ 329189251Ssam new_entry->expiration = old_entry->expiration; 330189251Ssam new_entry->opportunistic = 1; 331189251Ssam 332189251Ssam return new_entry; 333189251Ssam} 334189251Ssam 335189251Ssam 336189251Ssam/** 337189251Ssam * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry 338189251Ssam * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 339189251Ssam * @network_ctx: Network configuration context 340189251Ssam * @aa: Authenticator address for the new AP 341189251Ssam * Returns: Pointer to a new PMKSA cache entry or %NULL if not available 342189251Ssam * 343189251Ssam * Try to create a new PMKSA cache entry opportunistically by guessing that the 344189251Ssam * new AP is sharing the same PMK as another AP that has the same SSID and has 345189251Ssam * already an entry in PMKSA cache. 346189251Ssam */ 347189251Ssamstruct rsn_pmksa_cache_entry * 348189251Ssampmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, 349189251Ssam const u8 *aa) 350189251Ssam{ 351189251Ssam struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 352189251Ssam 353252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); 354189251Ssam if (network_ctx == NULL) 355189251Ssam return NULL; 356189251Ssam while (entry) { 357189251Ssam if (entry->network_ctx == network_ctx) { 358189251Ssam entry = pmksa_cache_clone_entry(pmksa, entry, aa); 359189251Ssam if (entry) { 360189251Ssam wpa_printf(MSG_DEBUG, "RSN: added " 361189251Ssam "opportunistic PMKSA cache entry " 362189251Ssam "for " MACSTR, MAC2STR(aa)); 363189251Ssam } 364189251Ssam return entry; 365189251Ssam } 366189251Ssam entry = entry->next; 367189251Ssam } 368189251Ssam return NULL; 369189251Ssam} 370189251Ssam 371189251Ssam 372189251Ssam/** 373189251Ssam * pmksa_cache_get_current - Get the current used PMKSA entry 374189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 375189251Ssam * Returns: Pointer to the current PMKSA cache entry or %NULL if not available 376189251Ssam */ 377189251Ssamstruct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) 378189251Ssam{ 379189251Ssam if (sm == NULL) 380189251Ssam return NULL; 381189251Ssam return sm->cur_pmksa; 382189251Ssam} 383189251Ssam 384189251Ssam 385189251Ssam/** 386189251Ssam * pmksa_cache_clear_current - Clear the current PMKSA entry selection 387189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 388189251Ssam */ 389189251Ssamvoid pmksa_cache_clear_current(struct wpa_sm *sm) 390189251Ssam{ 391189251Ssam if (sm == NULL) 392189251Ssam return; 393189251Ssam sm->cur_pmksa = NULL; 394189251Ssam} 395189251Ssam 396189251Ssam 397189251Ssam/** 398189251Ssam * pmksa_cache_set_current - Set the current PMKSA entry selection 399189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 400189251Ssam * @pmkid: PMKID for selecting PMKSA or %NULL if not used 401189251Ssam * @bssid: BSSID for PMKSA or %NULL if not used 402189251Ssam * @network_ctx: Network configuration context 403189251Ssam * @try_opportunistic: Whether to allow opportunistic PMKSA caching 404189251Ssam * Returns: 0 if PMKSA was found or -1 if no matching entry was found 405189251Ssam */ 406189251Ssamint pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 407189251Ssam const u8 *bssid, void *network_ctx, 408189251Ssam int try_opportunistic) 409189251Ssam{ 410189251Ssam struct rsn_pmksa_cache *pmksa = sm->pmksa; 411252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " 412252726Srpaulo "try_opportunistic=%d", network_ctx, try_opportunistic); 413252726Srpaulo if (pmkid) 414252726Srpaulo wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", 415252726Srpaulo pmkid, PMKID_LEN); 416252726Srpaulo if (bssid) 417252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, 418252726Srpaulo MAC2STR(bssid)); 419252726Srpaulo 420189251Ssam sm->cur_pmksa = NULL; 421189251Ssam if (pmkid) 422252726Srpaulo sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, 423252726Srpaulo network_ctx); 424189251Ssam if (sm->cur_pmksa == NULL && bssid) 425252726Srpaulo sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, 426252726Srpaulo network_ctx); 427189251Ssam if (sm->cur_pmksa == NULL && try_opportunistic && bssid) 428189251Ssam sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, 429189251Ssam network_ctx, 430189251Ssam bssid); 431189251Ssam if (sm->cur_pmksa) { 432252726Srpaulo wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", 433189251Ssam sm->cur_pmksa->pmkid, PMKID_LEN); 434189251Ssam return 0; 435189251Ssam } 436252726Srpaulo wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); 437189251Ssam return -1; 438189251Ssam} 439189251Ssam 440189251Ssam 441189251Ssam/** 442189251Ssam * pmksa_cache_list - Dump text list of entries in PMKSA cache 443214734Srpaulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 444189251Ssam * @buf: Buffer for the list 445189251Ssam * @len: Length of the buffer 446189251Ssam * Returns: number of bytes written to buffer 447189251Ssam * 448189251Ssam * This function is used to generate a text format representation of the 449189251Ssam * current PMKSA cache contents for the ctrl_iface PMKSA command. 450189251Ssam */ 451214734Srpauloint pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) 452189251Ssam{ 453189251Ssam int i, ret; 454189251Ssam char *pos = buf; 455189251Ssam struct rsn_pmksa_cache_entry *entry; 456189251Ssam struct os_time now; 457189251Ssam 458189251Ssam os_get_time(&now); 459189251Ssam ret = os_snprintf(pos, buf + len - pos, 460189251Ssam "Index / AA / PMKID / expiration (in seconds) / " 461189251Ssam "opportunistic\n"); 462189251Ssam if (ret < 0 || ret >= buf + len - pos) 463189251Ssam return pos - buf; 464189251Ssam pos += ret; 465189251Ssam i = 0; 466214734Srpaulo entry = pmksa->pmksa; 467189251Ssam while (entry) { 468189251Ssam i++; 469189251Ssam ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", 470189251Ssam i, MAC2STR(entry->aa)); 471189251Ssam if (ret < 0 || ret >= buf + len - pos) 472189251Ssam return pos - buf; 473189251Ssam pos += ret; 474189251Ssam pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, 475189251Ssam PMKID_LEN); 476189251Ssam ret = os_snprintf(pos, buf + len - pos, " %d %d\n", 477189251Ssam (int) (entry->expiration - now.sec), 478189251Ssam entry->opportunistic); 479189251Ssam if (ret < 0 || ret >= buf + len - pos) 480189251Ssam return pos - buf; 481189251Ssam pos += ret; 482189251Ssam entry = entry->next; 483189251Ssam } 484189251Ssam return pos - buf; 485189251Ssam} 486189251Ssam 487189251Ssam 488189251Ssam/** 489189251Ssam * pmksa_cache_init - Initialize PMKSA cache 490189251Ssam * @free_cb: Callback function to be called when a PMKSA cache entry is freed 491189251Ssam * @ctx: Context pointer for free_cb function 492189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 493189251Ssam * Returns: Pointer to PMKSA cache data or %NULL on failure 494189251Ssam */ 495189251Ssamstruct rsn_pmksa_cache * 496189251Ssampmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 497252726Srpaulo void *ctx, enum pmksa_free_reason reason), 498189251Ssam void *ctx, struct wpa_sm *sm) 499189251Ssam{ 500189251Ssam struct rsn_pmksa_cache *pmksa; 501189251Ssam 502189251Ssam pmksa = os_zalloc(sizeof(*pmksa)); 503189251Ssam if (pmksa) { 504189251Ssam pmksa->free_cb = free_cb; 505189251Ssam pmksa->ctx = ctx; 506189251Ssam pmksa->sm = sm; 507189251Ssam } 508189251Ssam 509189251Ssam return pmksa; 510189251Ssam} 511189251Ssam 512189251Ssam#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ 513