1 /* 2 * WPA Supplicant - RSN PMKSA cache 3 * Copyright (c) 2004-2009, 2011-2015, 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 20 static const int pmksa_cache_max_entries = 32; 21 22 struct 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 33 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 34 35 36 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 37 { 38 bin_clear_free(entry, sizeof(*entry)); 39 } 40 41 42 static 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 53 static 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 71 static 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 79 static 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 */ 125 struct rsn_pmksa_cache_entry * 126 pmksa_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 (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) 145 rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); 146 else if (wpa_key_mgmt_suite_b(akmp)) 147 rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); 148 else 149 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, 150 wpa_key_mgmt_sha256(akmp)); 151 os_get_reltime(&now); 152 entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; 153 entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * 154 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; 155 entry->akmp = akmp; 156 os_memcpy(entry->aa, aa, ETH_ALEN); 157 entry->network_ctx = network_ctx; 158 159 /* Replace an old entry for the same Authenticator (if found) with the 160 * new entry */ 161 pos = pmksa->pmksa; 162 prev = NULL; 163 while (pos) { 164 if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { 165 if (pos->pmk_len == pmk_len && 166 os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 && 167 os_memcmp_const(pos->pmkid, entry->pmkid, 168 PMKID_LEN) == 0) { 169 wpa_printf(MSG_DEBUG, "WPA: reusing previous " 170 "PMKSA entry"); 171 os_free(entry); 172 return pos; 173 } 174 if (prev == NULL) 175 pmksa->pmksa = pos->next; 176 else 177 prev->next = pos->next; 178 179 /* 180 * If OKC is used, there may be other PMKSA cache 181 * entries based on the same PMK. These needs to be 182 * flushed so that a new entry can be created based on 183 * the new PMK. Only clear other entries if they have a 184 * matching PMK and this PMK has been used successfully 185 * with the current AP, i.e., if opportunistic flag has 186 * been cleared in wpa_supplicant_key_neg_complete(). 187 */ 188 wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " 189 "the current AP and any PMKSA cache entry " 190 "that was based on the old PMK"); 191 if (!pos->opportunistic) 192 pmksa_cache_flush(pmksa, network_ctx, pos->pmk, 193 pos->pmk_len); 194 pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); 195 break; 196 } 197 prev = pos; 198 pos = pos->next; 199 } 200 201 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 202 /* Remove the oldest entry to make room for the new entry */ 203 pos = pmksa->pmksa; 204 205 if (pos == pmksa->sm->cur_pmksa) { 206 /* 207 * Never remove the current PMKSA cache entry, since 208 * it's in use, and removing it triggers a needless 209 * deauthentication. 210 */ 211 pos = pos->next; 212 pmksa->pmksa->next = pos ? pos->next : NULL; 213 } else 214 pmksa->pmksa = pos->next; 215 216 if (pos) { 217 wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " 218 "PMKSA cache entry (for " MACSTR ") to " 219 "make room for new one", 220 MAC2STR(pos->aa)); 221 pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); 222 } 223 } 224 225 /* Add the new entry; order by expiration time */ 226 pos = pmksa->pmksa; 227 prev = NULL; 228 while (pos) { 229 if (pos->expiration > entry->expiration) 230 break; 231 prev = pos; 232 pos = pos->next; 233 } 234 if (prev == NULL) { 235 entry->next = pmksa->pmksa; 236 pmksa->pmksa = entry; 237 pmksa_cache_set_expiration(pmksa); 238 } else { 239 entry->next = prev->next; 240 prev->next = entry; 241 } 242 pmksa->pmksa_count++; 243 wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR 244 " network_ctx=%p", MAC2STR(entry->aa), network_ctx); 245 wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); 246 247 return entry; 248 } 249 250 251 /** 252 * pmksa_cache_flush - Flush PMKSA cache entries for a specific network 253 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 254 * @network_ctx: Network configuration context or %NULL to flush all entries 255 * @pmk: PMK to match for or %NYLL to match all PMKs 256 * @pmk_len: PMK length 257 */ 258 void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, 259 const u8 *pmk, size_t pmk_len) 260 { 261 struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; 262 int removed = 0; 263 264 entry = pmksa->pmksa; 265 while (entry) { 266 if ((entry->network_ctx == network_ctx || 267 network_ctx == NULL) && 268 (pmk == NULL || 269 (pmk_len == entry->pmk_len && 270 os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { 271 wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " 272 "for " MACSTR, MAC2STR(entry->aa)); 273 if (prev) 274 prev->next = entry->next; 275 else 276 pmksa->pmksa = entry->next; 277 tmp = entry; 278 entry = entry->next; 279 pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); 280 removed++; 281 } else { 282 prev = entry; 283 entry = entry->next; 284 } 285 } 286 if (removed) 287 pmksa_cache_set_expiration(pmksa); 288 } 289 290 291 /** 292 * pmksa_cache_deinit - Free all entries in PMKSA cache 293 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 294 */ 295 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) 296 { 297 struct rsn_pmksa_cache_entry *entry, *prev; 298 299 if (pmksa == NULL) 300 return; 301 302 entry = pmksa->pmksa; 303 pmksa->pmksa = NULL; 304 while (entry) { 305 prev = entry; 306 entry = entry->next; 307 os_free(prev); 308 } 309 pmksa_cache_set_expiration(pmksa); 310 os_free(pmksa); 311 } 312 313 314 /** 315 * pmksa_cache_get - Fetch a PMKSA cache entry 316 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 317 * @aa: Authenticator address or %NULL to match any 318 * @pmkid: PMKID or %NULL to match any 319 * @network_ctx: Network context or %NULL to match any 320 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 321 */ 322 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 323 const u8 *aa, const u8 *pmkid, 324 const void *network_ctx) 325 { 326 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 327 while (entry) { 328 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && 329 (pmkid == NULL || 330 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && 331 (network_ctx == NULL || network_ctx == entry->network_ctx)) 332 return entry; 333 entry = entry->next; 334 } 335 return NULL; 336 } 337 338 339 static struct rsn_pmksa_cache_entry * 340 pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 341 const struct rsn_pmksa_cache_entry *old_entry, 342 const u8 *aa) 343 { 344 struct rsn_pmksa_cache_entry *new_entry; 345 346 new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, 347 NULL, 0, 348 aa, pmksa->sm->own_addr, 349 old_entry->network_ctx, old_entry->akmp); 350 if (new_entry == NULL) 351 return NULL; 352 353 /* TODO: reorder entries based on expiration time? */ 354 new_entry->expiration = old_entry->expiration; 355 new_entry->opportunistic = 1; 356 357 return new_entry; 358 } 359 360 361 /** 362 * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry 363 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 364 * @network_ctx: Network configuration context 365 * @aa: Authenticator address for the new AP 366 * Returns: Pointer to a new PMKSA cache entry or %NULL if not available 367 * 368 * Try to create a new PMKSA cache entry opportunistically by guessing that the 369 * new AP is sharing the same PMK as another AP that has the same SSID and has 370 * already an entry in PMKSA cache. 371 */ 372 struct rsn_pmksa_cache_entry * 373 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, 374 const u8 *aa) 375 { 376 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; 377 378 wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); 379 if (network_ctx == NULL) 380 return NULL; 381 while (entry) { 382 if (entry->network_ctx == network_ctx) { 383 entry = pmksa_cache_clone_entry(pmksa, entry, aa); 384 if (entry) { 385 wpa_printf(MSG_DEBUG, "RSN: added " 386 "opportunistic PMKSA cache entry " 387 "for " MACSTR, MAC2STR(aa)); 388 } 389 return entry; 390 } 391 entry = entry->next; 392 } 393 return NULL; 394 } 395 396 397 /** 398 * pmksa_cache_get_current - Get the current used PMKSA entry 399 * @sm: Pointer to WPA state machine data from wpa_sm_init() 400 * Returns: Pointer to the current PMKSA cache entry or %NULL if not available 401 */ 402 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) 403 { 404 if (sm == NULL) 405 return NULL; 406 return sm->cur_pmksa; 407 } 408 409 410 /** 411 * pmksa_cache_clear_current - Clear the current PMKSA entry selection 412 * @sm: Pointer to WPA state machine data from wpa_sm_init() 413 */ 414 void pmksa_cache_clear_current(struct wpa_sm *sm) 415 { 416 if (sm == NULL) 417 return; 418 sm->cur_pmksa = NULL; 419 } 420 421 422 /** 423 * pmksa_cache_set_current - Set the current PMKSA entry selection 424 * @sm: Pointer to WPA state machine data from wpa_sm_init() 425 * @pmkid: PMKID for selecting PMKSA or %NULL if not used 426 * @bssid: BSSID for PMKSA or %NULL if not used 427 * @network_ctx: Network configuration context 428 * @try_opportunistic: Whether to allow opportunistic PMKSA caching 429 * Returns: 0 if PMKSA was found or -1 if no matching entry was found 430 */ 431 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 432 const u8 *bssid, void *network_ctx, 433 int try_opportunistic) 434 { 435 struct rsn_pmksa_cache *pmksa = sm->pmksa; 436 wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " 437 "try_opportunistic=%d", network_ctx, try_opportunistic); 438 if (pmkid) 439 wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", 440 pmkid, PMKID_LEN); 441 if (bssid) 442 wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, 443 MAC2STR(bssid)); 444 445 sm->cur_pmksa = NULL; 446 if (pmkid) 447 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, 448 network_ctx); 449 if (sm->cur_pmksa == NULL && bssid) 450 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, 451 network_ctx); 452 if (sm->cur_pmksa == NULL && try_opportunistic && bssid) 453 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, 454 network_ctx, 455 bssid); 456 if (sm->cur_pmksa) { 457 wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", 458 sm->cur_pmksa->pmkid, PMKID_LEN); 459 return 0; 460 } 461 wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); 462 return -1; 463 } 464 465 466 /** 467 * pmksa_cache_list - Dump text list of entries in PMKSA cache 468 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() 469 * @buf: Buffer for the list 470 * @len: Length of the buffer 471 * Returns: number of bytes written to buffer 472 * 473 * This function is used to generate a text format representation of the 474 * current PMKSA cache contents for the ctrl_iface PMKSA command. 475 */ 476 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) 477 { 478 int i, ret; 479 char *pos = buf; 480 struct rsn_pmksa_cache_entry *entry; 481 struct os_reltime now; 482 483 os_get_reltime(&now); 484 ret = os_snprintf(pos, buf + len - pos, 485 "Index / AA / PMKID / expiration (in seconds) / " 486 "opportunistic\n"); 487 if (os_snprintf_error(buf + len - pos, ret)) 488 return pos - buf; 489 pos += ret; 490 i = 0; 491 entry = pmksa->pmksa; 492 while (entry) { 493 i++; 494 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", 495 i, MAC2STR(entry->aa)); 496 if (os_snprintf_error(buf + len - pos, ret)) 497 return pos - buf; 498 pos += ret; 499 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, 500 PMKID_LEN); 501 ret = os_snprintf(pos, buf + len - pos, " %d %d\n", 502 (int) (entry->expiration - now.sec), 503 entry->opportunistic); 504 if (os_snprintf_error(buf + len - pos, ret)) 505 return pos - buf; 506 pos += ret; 507 entry = entry->next; 508 } 509 return pos - buf; 510 } 511 512 513 /** 514 * pmksa_cache_init - Initialize PMKSA cache 515 * @free_cb: Callback function to be called when a PMKSA cache entry is freed 516 * @ctx: Context pointer for free_cb function 517 * @sm: Pointer to WPA state machine data from wpa_sm_init() 518 * Returns: Pointer to PMKSA cache data or %NULL on failure 519 */ 520 struct rsn_pmksa_cache * 521 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 522 void *ctx, enum pmksa_free_reason reason), 523 void *ctx, struct wpa_sm *sm) 524 { 525 struct rsn_pmksa_cache *pmksa; 526 527 pmksa = os_zalloc(sizeof(*pmksa)); 528 if (pmksa) { 529 pmksa->free_cb = free_cb; 530 pmksa->ctx = ctx; 531 pmksa->sm = sm; 532 } 533 534 return pmksa; 535 } 536 537 #endif /* IEEE8021X_EAPOL */ 538