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