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