pmksa_cache_auth.c revision d80a401aed31d06f261efd19223cf55d1a2a8228
1/* 2 * hostapd - PMKSA cache for IEEE 802.11i RSN 3 * Copyright (c) 2004-2008, 2012-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 "utils/includes.h" 10 11#include "utils/common.h" 12#include "utils/eloop.h" 13#include "eapol_auth/eapol_auth_sm.h" 14#include "eapol_auth/eapol_auth_sm_i.h" 15#include "radius/radius_das.h" 16#include "sta_info.h" 17#include "ap_config.h" 18#include "pmksa_cache_auth.h" 19 20 21static const int pmksa_cache_max_entries = 1024; 22static const int dot11RSNAConfigPMKLifetime = 43200; 23 24struct rsn_pmksa_cache { 25#define PMKID_HASH_SIZE 128 26#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) 27 struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; 28 struct rsn_pmksa_cache_entry *pmksa; 29 int pmksa_count; 30 31 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); 32 void *ctx; 33}; 34 35 36static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); 37 38 39static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) 40{ 41 os_free(entry->identity); 42 wpabuf_free(entry->cui); 43#ifndef CONFIG_NO_RADIUS 44 radius_free_class(&entry->radius_class); 45#endif /* CONFIG_NO_RADIUS */ 46 bin_clear_free(entry, sizeof(*entry)); 47} 48 49 50void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, 51 struct rsn_pmksa_cache_entry *entry) 52{ 53 struct rsn_pmksa_cache_entry *pos, *prev; 54 unsigned int hash; 55 56 pmksa->pmksa_count--; 57 pmksa->free_cb(entry, pmksa->ctx); 58 59 /* unlink from hash list */ 60 hash = PMKID_HASH(entry->pmkid); 61 pos = pmksa->pmkid[hash]; 62 prev = NULL; 63 while (pos) { 64 if (pos == entry) { 65 if (prev != NULL) 66 prev->hnext = entry->hnext; 67 else 68 pmksa->pmkid[hash] = entry->hnext; 69 break; 70 } 71 prev = pos; 72 pos = pos->hnext; 73 } 74 75 /* unlink from entry list */ 76 pos = pmksa->pmksa; 77 prev = NULL; 78 while (pos) { 79 if (pos == entry) { 80 if (prev != NULL) 81 prev->next = entry->next; 82 else 83 pmksa->pmksa = entry->next; 84 break; 85 } 86 prev = pos; 87 pos = pos->next; 88 } 89 90 _pmksa_cache_free_entry(entry); 91} 92 93 94static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 95{ 96 struct rsn_pmksa_cache *pmksa = eloop_ctx; 97 struct os_reltime now; 98 99 os_get_reltime(&now); 100 while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { 101 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " 102 MACSTR, MAC2STR(pmksa->pmksa->spa)); 103 pmksa_cache_free_entry(pmksa, pmksa->pmksa); 104 } 105 106 pmksa_cache_set_expiration(pmksa); 107} 108 109 110static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) 111{ 112 int sec; 113 struct os_reltime now; 114 115 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 116 if (pmksa->pmksa == NULL) 117 return; 118 os_get_reltime(&now); 119 sec = pmksa->pmksa->expiration - now.sec; 120 if (sec < 0) 121 sec = 0; 122 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); 123} 124 125 126static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, 127 struct eapol_state_machine *eapol) 128{ 129 if (eapol == NULL) 130 return; 131 132 if (eapol->identity) { 133 entry->identity = os_malloc(eapol->identity_len); 134 if (entry->identity) { 135 entry->identity_len = eapol->identity_len; 136 os_memcpy(entry->identity, eapol->identity, 137 eapol->identity_len); 138 } 139 } 140 141 if (eapol->radius_cui) 142 entry->cui = wpabuf_dup(eapol->radius_cui); 143 144#ifndef CONFIG_NO_RADIUS 145 radius_copy_class(&entry->radius_class, &eapol->radius_class); 146#endif /* CONFIG_NO_RADIUS */ 147 148 entry->eap_type_authsrv = eapol->eap_type_authsrv; 149 entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; 150 151 entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; 152 entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; 153} 154 155 156void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, 157 struct eapol_state_machine *eapol) 158{ 159 if (entry == NULL || eapol == NULL) 160 return; 161 162 if (entry->identity) { 163 os_free(eapol->identity); 164 eapol->identity = os_malloc(entry->identity_len); 165 if (eapol->identity) { 166 eapol->identity_len = entry->identity_len; 167 os_memcpy(eapol->identity, entry->identity, 168 entry->identity_len); 169 } 170 wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", 171 eapol->identity, eapol->identity_len); 172 } 173 174 if (entry->cui) { 175 wpabuf_free(eapol->radius_cui); 176 eapol->radius_cui = wpabuf_dup(entry->cui); 177 } 178 179#ifndef CONFIG_NO_RADIUS 180 radius_free_class(&eapol->radius_class); 181 radius_copy_class(&eapol->radius_class, &entry->radius_class); 182#endif /* CONFIG_NO_RADIUS */ 183 if (eapol->radius_class.attr) { 184 wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " 185 "PMKSA", (unsigned long) eapol->radius_class.count); 186 } 187 188 eapol->eap_type_authsrv = entry->eap_type_authsrv; 189 ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; 190 191 eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; 192 eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; 193} 194 195 196static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, 197 struct rsn_pmksa_cache_entry *entry) 198{ 199 struct rsn_pmksa_cache_entry *pos, *prev; 200 int hash; 201 202 /* Add the new entry; order by expiration time */ 203 pos = pmksa->pmksa; 204 prev = NULL; 205 while (pos) { 206 if (pos->expiration > entry->expiration) 207 break; 208 prev = pos; 209 pos = pos->next; 210 } 211 if (prev == NULL) { 212 entry->next = pmksa->pmksa; 213 pmksa->pmksa = entry; 214 } else { 215 entry->next = prev->next; 216 prev->next = entry; 217 } 218 219 hash = PMKID_HASH(entry->pmkid); 220 entry->hnext = pmksa->pmkid[hash]; 221 pmksa->pmkid[hash] = entry; 222 223 pmksa->pmksa_count++; 224 if (prev == NULL) 225 pmksa_cache_set_expiration(pmksa); 226 wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, 227 MAC2STR(entry->spa)); 228 wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); 229} 230 231 232/** 233 * pmksa_cache_auth_add - Add a PMKSA cache entry 234 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 235 * @pmk: The new pairwise master key 236 * @pmk_len: PMK length in bytes, usually PMK_LEN (32) 237 * @kck: Key confirmation key or %NULL if not yet derived 238 * @kck_len: KCK length in bytes 239 * @aa: Authenticator address 240 * @spa: Supplicant address 241 * @session_timeout: Session timeout 242 * @eapol: Pointer to EAPOL state machine data 243 * @akmp: WPA_KEY_MGMT_* used in key derivation 244 * Returns: Pointer to the added PMKSA cache entry or %NULL on error 245 * 246 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA 247 * cache. If an old entry is already in the cache for the same Supplicant, 248 * this entry will be replaced with the new entry. PMKID will be calculated 249 * based on the PMK. 250 */ 251struct rsn_pmksa_cache_entry * 252pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, 253 const u8 *pmk, size_t pmk_len, 254 const u8 *kck, size_t kck_len, 255 const u8 *aa, const u8 *spa, int session_timeout, 256 struct eapol_state_machine *eapol, int akmp) 257{ 258 struct rsn_pmksa_cache_entry *entry, *pos; 259 struct os_reltime now; 260 261 if (pmk_len > PMK_LEN_MAX) 262 return NULL; 263 264 if (wpa_key_mgmt_suite_b(akmp) && !kck) 265 return NULL; 266 267 entry = os_zalloc(sizeof(*entry)); 268 if (entry == NULL) 269 return NULL; 270 os_memcpy(entry->pmk, pmk, pmk_len); 271 entry->pmk_len = pmk_len; 272 if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) 273 rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); 274 else if (wpa_key_mgmt_suite_b(akmp)) 275 rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); 276 else 277 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, 278 wpa_key_mgmt_sha256(akmp)); 279 os_get_reltime(&now); 280 entry->expiration = now.sec; 281 if (session_timeout > 0) 282 entry->expiration += session_timeout; 283 else 284 entry->expiration += dot11RSNAConfigPMKLifetime; 285 entry->akmp = akmp; 286 os_memcpy(entry->spa, spa, ETH_ALEN); 287 pmksa_cache_from_eapol_data(entry, eapol); 288 289 /* Replace an old entry for the same STA (if found) with the new entry 290 */ 291 pos = pmksa_cache_auth_get(pmksa, spa, NULL); 292 if (pos) 293 pmksa_cache_free_entry(pmksa, pos); 294 295 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { 296 /* Remove the oldest entry to make room for the new entry */ 297 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " 298 "entry (for " MACSTR ") to make room for new one", 299 MAC2STR(pmksa->pmksa->spa)); 300 pmksa_cache_free_entry(pmksa, pmksa->pmksa); 301 } 302 303 pmksa_cache_link_entry(pmksa, entry); 304 305 return entry; 306} 307 308 309struct rsn_pmksa_cache_entry * 310pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, 311 const struct rsn_pmksa_cache_entry *old_entry, 312 const u8 *aa, const u8 *pmkid) 313{ 314 struct rsn_pmksa_cache_entry *entry; 315 316 entry = os_zalloc(sizeof(*entry)); 317 if (entry == NULL) 318 return NULL; 319 os_memcpy(entry->pmkid, pmkid, PMKID_LEN); 320 os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); 321 entry->pmk_len = old_entry->pmk_len; 322 entry->expiration = old_entry->expiration; 323 entry->akmp = old_entry->akmp; 324 os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); 325 entry->opportunistic = 1; 326 if (old_entry->identity) { 327 entry->identity = os_malloc(old_entry->identity_len); 328 if (entry->identity) { 329 entry->identity_len = old_entry->identity_len; 330 os_memcpy(entry->identity, old_entry->identity, 331 old_entry->identity_len); 332 } 333 } 334 if (old_entry->cui) 335 entry->cui = wpabuf_dup(old_entry->cui); 336#ifndef CONFIG_NO_RADIUS 337 radius_copy_class(&entry->radius_class, &old_entry->radius_class); 338#endif /* CONFIG_NO_RADIUS */ 339 entry->eap_type_authsrv = old_entry->eap_type_authsrv; 340 entry->vlan_id = old_entry->vlan_id; 341 entry->opportunistic = 1; 342 343 pmksa_cache_link_entry(pmksa, entry); 344 345 return entry; 346} 347 348 349/** 350 * pmksa_cache_auth_deinit - Free all entries in PMKSA cache 351 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 352 */ 353void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) 354{ 355 struct rsn_pmksa_cache_entry *entry, *prev; 356 int i; 357 358 if (pmksa == NULL) 359 return; 360 361 entry = pmksa->pmksa; 362 while (entry) { 363 prev = entry; 364 entry = entry->next; 365 _pmksa_cache_free_entry(prev); 366 } 367 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); 368 pmksa->pmksa_count = 0; 369 pmksa->pmksa = NULL; 370 for (i = 0; i < PMKID_HASH_SIZE; i++) 371 pmksa->pmkid[i] = NULL; 372 os_free(pmksa); 373} 374 375 376/** 377 * pmksa_cache_auth_get - Fetch a PMKSA cache entry 378 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 379 * @spa: Supplicant address or %NULL to match any 380 * @pmkid: PMKID or %NULL to match any 381 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 382 */ 383struct rsn_pmksa_cache_entry * 384pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, 385 const u8 *spa, const u8 *pmkid) 386{ 387 struct rsn_pmksa_cache_entry *entry; 388 389 if (pmkid) { 390 for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry; 391 entry = entry->hnext) { 392 if ((spa == NULL || 393 os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && 394 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) 395 return entry; 396 } 397 } else { 398 for (entry = pmksa->pmksa; entry; entry = entry->next) { 399 if (spa == NULL || 400 os_memcmp(entry->spa, spa, ETH_ALEN) == 0) 401 return entry; 402 } 403 } 404 405 return NULL; 406} 407 408 409/** 410 * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC 411 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() 412 * @aa: Authenticator address 413 * @spa: Supplicant address 414 * @pmkid: PMKID 415 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found 416 * 417 * Use opportunistic key caching (OKC) to find a PMK for a supplicant. 418 */ 419struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( 420 struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, 421 const u8 *pmkid) 422{ 423 struct rsn_pmksa_cache_entry *entry; 424 u8 new_pmkid[PMKID_LEN]; 425 426 for (entry = pmksa->pmksa; entry; entry = entry->next) { 427 if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) 428 continue; 429 rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, 430 wpa_key_mgmt_sha256(entry->akmp)); 431 if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) 432 return entry; 433 } 434 return NULL; 435} 436 437 438/** 439 * pmksa_cache_auth_init - Initialize PMKSA cache 440 * @free_cb: Callback function to be called when a PMKSA cache entry is freed 441 * @ctx: Context pointer for free_cb function 442 * Returns: Pointer to PMKSA cache data or %NULL on failure 443 */ 444struct rsn_pmksa_cache * 445pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, 446 void *ctx), void *ctx) 447{ 448 struct rsn_pmksa_cache *pmksa; 449 450 pmksa = os_zalloc(sizeof(*pmksa)); 451 if (pmksa) { 452 pmksa->free_cb = free_cb; 453 pmksa->ctx = ctx; 454 } 455 456 return pmksa; 457} 458 459 460static int das_attr_match(struct rsn_pmksa_cache_entry *entry, 461 struct radius_das_attrs *attr) 462{ 463 int match = 0; 464 465 if (attr->sta_addr) { 466 if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0) 467 return 0; 468 match++; 469 } 470 471 if (attr->acct_multi_session_id) { 472 char buf[20]; 473 474 if (attr->acct_multi_session_id_len != 17) 475 return 0; 476 os_snprintf(buf, sizeof(buf), "%08X+%08X", 477 entry->acct_multi_session_id_hi, 478 entry->acct_multi_session_id_lo); 479 if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) 480 return 0; 481 match++; 482 } 483 484 if (attr->cui) { 485 if (!entry->cui || 486 attr->cui_len != wpabuf_len(entry->cui) || 487 os_memcmp(attr->cui, wpabuf_head(entry->cui), 488 attr->cui_len) != 0) 489 return 0; 490 match++; 491 } 492 493 if (attr->user_name) { 494 if (!entry->identity || 495 attr->user_name_len != entry->identity_len || 496 os_memcmp(attr->user_name, entry->identity, 497 attr->user_name_len) != 0) 498 return 0; 499 match++; 500 } 501 502 return match; 503} 504 505 506int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, 507 struct radius_das_attrs *attr) 508{ 509 int found = 0; 510 struct rsn_pmksa_cache_entry *entry, *prev; 511 512 if (attr->acct_session_id) 513 return -1; 514 515 entry = pmksa->pmksa; 516 while (entry) { 517 if (das_attr_match(entry, attr)) { 518 found++; 519 prev = entry; 520 entry = entry->next; 521 pmksa_cache_free_entry(pmksa, prev); 522 continue; 523 } 524 entry = entry->next; 525 } 526 527 return found ? 0 : -1; 528} 529