pmksa_cache.c revision fb45fd5cfed8bdccd0859c7fc05449fc187e2d06
1/* 2 * WPA Supplicant - RSN PMKSA cache 3 * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10 11#include "common.h" 12#include "eloop.h" 13#include "eapol_supp/eapol_supp_sm.h" 14#include "wpa.h" 15#include "wpa_i.h" 16#include "pmksa_cache.h" 17 18#ifdef IEEE8021X_EAPOL 19 20static const int pmksa_cache_max_entries = 32; 21 22struct rsn_pmksa_cache { 23 struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ 24 int pmksa_count; /* number of entries in PMKSA cache */ 25 struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ 26 27 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, 28 enum pmksa_free_reason reason); 29 void *ctx; 30}; 31 32 33static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 34 35 36static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 37{ 38 bin_clear_free(entry, sizeof(*entry)); 39} 40 41 42static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, 43 struct rsn_pmksa_cache_entry *entry, 44 enum pmksa_free_reason reason) 45{ 46 wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); 47 pmksa->pmksa_count--; 48 pmksa->free_cb(entry, pmksa->ctx, reason); 49 _pmksa_cache_free_entry(entry); 50} 51 52 53static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 54{ 55 struct rsn_pmksa_cache *pmksa = eloop_ctx; 56 struct os_reltime now; 57 58 os_get_reltime(&now); 59 while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { 60 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 61 pmksa->pmksa = entry->next; 62 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " 63 MACSTR, MAC2STR(entry->aa)); 64 pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); 65 } 66 67 pmksa_cache_set_expiration(pmksa); 68} 69 70 71static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) 72{ 73 struct rsn_pmksa_cache *pmksa = eloop_ctx; 74 pmksa->sm->cur_pmksa = NULL; 75 eapol_sm_request_reauth(pmksa->sm->eapol); 76} 77 78 79static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) 80{ 81 int sec; 82 struct rsn_pmksa_cache_entry *entry; 83 struct os_reltime now; 84 85 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 86 eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); 87 if (pmksa->pmksa == NULL) 88 return; 89 os_get_reltime(&now); 90 sec = pmksa->pmksa->expiration - now.sec; 91 if (sec < 0) 92 sec = 0; 93 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); 94 95 entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : 96 pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); 97 if (entry) { 98 sec = pmksa->pmksa->reauth_time - now.sec; 99 if (sec < 0) 100 sec = 0; 101 eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, 102 NULL); 103 } 104} 105 106 107/** 108 * pmksa_cache_add - Add a PMKSA cache entry 109 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 110 * @pmk: The new pairwise master key 111 * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 112 * @kck: Key confirmation key or %NULL if not yet derived 113 * @kck_len: KCK length in bytes 114 * @aa: Authenticator address 115 * @spa: Supplicant address 116 * @network_ctx: Network configuration context for this PMK 117 * @akmp: WPA_KEY_MGMT_* used in key derivation 118 * Returns: Pointer to the added PMKSA cache entry or %NULL on error 119 * 120 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA 121 * cache. If an old entry is already in the cache for the same Authenticator, 122 * this entry will be replaced with the new entry. PMKID will be calculated 123 * based on the PMK and the driver interface is notified of the new PMKID. 124 */ 125struct rsn_pmksa_cache_entry * 126pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 127 const u8 *kck, size_t kck_len, 128 const u8 *aa, const u8 *spa, void *network_ctx, int akmp) 129{ 130 struct rsn_pmksa_cache_entry *entry, *pos, *prev; 131 struct os_reltime now; 132 133 if (pmk_len > PMK_LEN) 134 return NULL; 135 136 if (wpa_key_mgmt_suite_b(akmp) && !kck) 137 return NULL; 138 139 entry = os_zalloc(sizeof(*entry)); 140 if (entry == NULL) 141 return NULL; 142 os_memcpy(entry->pmk, pmk, pmk_len); 143 entry->pmk_len = pmk_len; 144 if (wpa_key_mgmt_suite_b(akmp)) 145 rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); 146 else 147 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, 148 wpa_key_mgmt_sha256(akmp)); 149 os_get_reltime(&now); 150 entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; 151 entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * 152 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; 153 entry->akmp = akmp; 154 os_memcpy(entry->aa, aa, ETH_ALEN); 155 entry->network_ctx = network_ctx; 156 157 /* Replace an old entry for the same Authenticator (if found) with the 158 * new entry */ 159 pos = pmksa->pmksa; 160 prev = NULL; 161 while (pos) { 162 if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { 163 if (pos->pmk_len == pmk_len && 164 os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 && 165 os_memcmp_const(pos->pmkid, entry->pmkid, 166 PMKID_LEN) == 0) { 167 wpa_printf(MSG_DEBUG, "WPA: reusing previous " 168 "PMKSA entry"); 169 os_free(entry); 170 return pos; 171 } 172 if (prev == NULL) 173 pmksa->pmksa = pos->next; 174 else 175 prev->next = pos->next; 176 177 /* 178 * If OKC is used, there may be other PMKSA cache 179 * entries based on the same PMK. These needs to be 180 * flushed so that a new entry can be created based on 181 * the new PMK. Only clear other entries if they have a 182 * matching PMK and this PMK has been used successfully 183 * with the current AP, i.e., if opportunistic flag has 184 * been cleared in wpa_supplicant_key_neg_complete(). 185 */ 186 wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " 187 "the current AP and any PMKSA cache entry " 188 "that was based on the old PMK"); 189 if (!pos->opportunistic) 190 pmksa_cache_flush(pmksa, network_ctx, pos->pmk, 191 pos->pmk_len); 192 pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); 193 break; 194 } 195 prev = pos; 196 pos = pos->next; 197 } 198 199 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 200 /* Remove the oldest entry to make room for the new entry */ 201 pos = pmksa->pmksa; 202 203 if (pos == pmksa->sm->cur_pmksa) { 204 /* 205 * Never remove the current PMKSA cache entry, since 206 * it's in use, and removing it triggers a needless 207 * deauthentication. 208 */ 209 pos = pos->next; 210 pmksa->pmksa->next = pos ? pos->next : NULL; 211 } else 212 pmksa->pmksa = pos->next; 213 214 if (pos) { 215 wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " 216 "PMKSA cache entry (for " MACSTR ") to " 217 "make room for new one", 218 MAC2STR(pos->aa)); 219 pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); 220 } 221 } 222 223 /* Add the new entry; order by expiration time */ 224 pos = pmksa->pmksa; 225 prev = NULL; 226 while (pos) { 227 if (pos->expiration > entry->expiration) 228 break; 229 prev = pos; 230 pos = pos->next; 231 } 232 if (prev == NULL) { 233 entry->next = pmksa->pmksa; 234 pmksa->pmksa = entry; 235 pmksa_cache_set_expiration(pmksa); 236 } else { 237 entry->next = prev->next; 238 prev->next = entry; 239 } 240 pmksa->pmksa_count++; 241 wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR 242 " network_ctx=%p", MAC2STR(entry->aa), network_ctx); 243 wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); 244 245 return entry; 246} 247 248 249/** 250 * pmksa_cache_flush - Flush PMKSA cache entries for a specific network 251 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 252 * @network_ctx: Network configuration context or %NULL to flush all entries 253 * @pmk: PMK to match for or %NYLL to match all PMKs 254 * @pmk_len: PMK length 255 */ 256void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, 257 const u8 *pmk, size_t pmk_len) 258{ 259 struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; 260 int removed = 0; 261 262 entry = pmksa->pmksa; 263 while (entry) { 264 if ((entry->network_ctx == network_ctx || 265 network_ctx == NULL) && 266 (pmk == NULL || 267 (pmk_len == entry->pmk_len && 268 os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { 269 wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " 270 "for " MACSTR, MAC2STR(entry->aa)); 271 if (prev) 272 prev->next = entry->next; 273 else 274 pmksa->pmksa = entry->next; 275 tmp = entry; 276 entry = entry->next; 277 pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); 278 removed++; 279 } else { 280 prev = entry; 281 entry = entry->next; 282 } 283 } 284 if (removed) 285 pmksa_cache_set_expiration(pmksa); 286} 287 288 289/** 290 * pmksa_cache_deinit - Free all entries in PMKSA cache 291 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 292 */ 293void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) 294{ 295 struct rsn_pmksa_cache_entry *entry, *prev; 296 297 if (pmksa == NULL) 298 return; 299 300 entry = pmksa->pmksa; 301 pmksa->pmksa = NULL; 302 while (entry) { 303 prev = entry; 304 entry = entry->next; 305 os_free(prev); 306 } 307 pmksa_cache_set_expiration(pmksa); 308 os_free(pmksa); 309} 310 311 312/** 313 * pmksa_cache_get - Fetch a PMKSA cache entry 314 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 315 * @aa: Authenticator address or %NULL to match any 316 * @pmkid: PMKID or %NULL to match any 317 * @network_ctx: Network context or %NULL to match any 318 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 319 */ 320struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 321 const u8 *aa, const u8 *pmkid, 322 const void *network_ctx) 323{ 324 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 325 while (entry) { 326 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && 327 (pmkid == NULL || 328 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && 329 (network_ctx == NULL || network_ctx == entry->network_ctx)) 330 return entry; 331 entry = entry->next; 332 } 333 return NULL; 334} 335 336 337static struct rsn_pmksa_cache_entry * 338pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 339 const struct rsn_pmksa_cache_entry *old_entry, 340 const u8 *aa) 341{ 342 struct rsn_pmksa_cache_entry *new_entry; 343 344 new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, 345 NULL, 0, 346 aa, pmksa->sm->own_addr, 347 old_entry->network_ctx, old_entry->akmp); 348 if (new_entry == NULL) 349 return NULL; 350 351 /* TODO: reorder entries based on expiration time? */ 352 new_entry->expiration = old_entry->expiration; 353 new_entry->opportunistic = 1; 354 355 return new_entry; 356} 357 358 359/** 360 * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry 361 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 362 * @network_ctx: Network configuration context 363 * @aa: Authenticator address for the new AP 364 * Returns: Pointer to a new PMKSA cache entry or %NULL if not available 365 * 366 * Try to create a new PMKSA cache entry opportunistically by guessing that the 367 * new AP is sharing the same PMK as another AP that has the same SSID and has 368 * already an entry in PMKSA cache. 369 */ 370struct rsn_pmksa_cache_entry * 371pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, 372 const u8 *aa) 373{ 374 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 375 376 wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); 377 if (network_ctx == NULL) 378 return NULL; 379 while (entry) { 380 if (entry->network_ctx == network_ctx) { 381 entry = pmksa_cache_clone_entry(pmksa, entry, aa); 382 if (entry) { 383 wpa_printf(MSG_DEBUG, "RSN: added " 384 "opportunistic PMKSA cache entry " 385 "for " MACSTR, MAC2STR(aa)); 386 } 387 return entry; 388 } 389 entry = entry->next; 390 } 391 return NULL; 392} 393 394 395/** 396 * pmksa_cache_get_current - Get the current used PMKSA entry 397 * @sm: Pointer to WPA state machine data from wpa_sm_init() 398 * Returns: Pointer to the current PMKSA cache entry or %NULL if not available 399 */ 400struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) 401{ 402 if (sm == NULL) 403 return NULL; 404 return sm->cur_pmksa; 405} 406 407 408/** 409 * pmksa_cache_clear_current - Clear the current PMKSA entry selection 410 * @sm: Pointer to WPA state machine data from wpa_sm_init() 411 */ 412void pmksa_cache_clear_current(struct wpa_sm *sm) 413{ 414 if (sm == NULL) 415 return; 416 sm->cur_pmksa = NULL; 417} 418 419 420/** 421 * pmksa_cache_set_current - Set the current PMKSA entry selection 422 * @sm: Pointer to WPA state machine data from wpa_sm_init() 423 * @pmkid: PMKID for selecting PMKSA or %NULL if not used 424 * @bssid: BSSID for PMKSA or %NULL if not used 425 * @network_ctx: Network configuration context 426 * @try_opportunistic: Whether to allow opportunistic PMKSA caching 427 * Returns: 0 if PMKSA was found or -1 if no matching entry was found 428 */ 429int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 430 const u8 *bssid, void *network_ctx, 431 int try_opportunistic) 432{ 433 struct rsn_pmksa_cache *pmksa = sm->pmksa; 434 wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " 435 "try_opportunistic=%d", network_ctx, try_opportunistic); 436 if (pmkid) 437 wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", 438 pmkid, PMKID_LEN); 439 if (bssid) 440 wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, 441 MAC2STR(bssid)); 442 443 sm->cur_pmksa = NULL; 444 if (pmkid) 445 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, 446 network_ctx); 447 if (sm->cur_pmksa == NULL && bssid) 448 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, 449 network_ctx); 450 if (sm->cur_pmksa == NULL && try_opportunistic && bssid) 451 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, 452 network_ctx, 453 bssid); 454 if (sm->cur_pmksa) { 455 wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", 456 sm->cur_pmksa->pmkid, PMKID_LEN); 457 return 0; 458 } 459 wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); 460 return -1; 461} 462 463 464/** 465 * pmksa_cache_list - Dump text list of entries in PMKSA cache 466 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 467 * @buf: Buffer for the list 468 * @len: Length of the buffer 469 * Returns: number of bytes written to buffer 470 * 471 * This function is used to generate a text format representation of the 472 * current PMKSA cache contents for the ctrl_iface PMKSA command. 473 */ 474int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) 475{ 476 int i, ret; 477 char *pos = buf; 478 struct rsn_pmksa_cache_entry *entry; 479 struct os_reltime now; 480 481 os_get_reltime(&now); 482 ret = os_snprintf(pos, buf + len - pos, 483 "Index / AA / PMKID / expiration (in seconds) / " 484 "opportunistic\n"); 485 if (os_snprintf_error(buf + len - pos, ret)) 486 return pos - buf; 487 pos += ret; 488 i = 0; 489 entry = pmksa->pmksa; 490 while (entry) { 491 i++; 492 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", 493 i, MAC2STR(entry->aa)); 494 if (os_snprintf_error(buf + len - pos, ret)) 495 return pos - buf; 496 pos += ret; 497 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, 498 PMKID_LEN); 499 ret = os_snprintf(pos, buf + len - pos, " %d %d\n", 500 (int) (entry->expiration - now.sec), 501 entry->opportunistic); 502 if (os_snprintf_error(buf + len - pos, ret)) 503 return pos - buf; 504 pos += ret; 505 entry = entry->next; 506 } 507 return pos - buf; 508} 509 510 511/** 512 * pmksa_cache_init - Initialize PMKSA cache 513 * @free_cb: Callback function to be called when a PMKSA cache entry is freed 514 * @ctx: Context pointer for free_cb function 515 * @sm: Pointer to WPA state machine data from wpa_sm_init() 516 * Returns: Pointer to PMKSA cache data or %NULL on failure 517 */ 518struct rsn_pmksa_cache * 519pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 520 void *ctx, enum pmksa_free_reason reason), 521 void *ctx, struct wpa_sm *sm) 522{ 523 struct rsn_pmksa_cache *pmksa; 524 525 pmksa = os_zalloc(sizeof(*pmksa)); 526 if (pmksa) { 527 pmksa->free_cb = free_cb; 528 pmksa->ctx = ctx; 529 pmksa->sm = sm; 530 } 531 532 return pmksa; 533} 534 535#endif /* IEEE8021X_EAPOL */ 536