1 /* 2 * RSN PTKSA cache implementation 3 * 4 * Copyright (C) 2019 Intel Corporation 5 * 6 * This software may be distributed under the terms of the BSD license. 7 * See README for more details. 8 */ 9 10 #include "includes.h" 11 #include "utils/common.h" 12 #include "eloop.h" 13 #include "common/ptksa_cache.h" 14 15 #define PTKSA_CACHE_MAX_ENTRIES 16 16 17 struct ptksa_cache { 18 struct dl_list ptksa; 19 unsigned int n_ptksa; 20 }; 21 22 #ifdef CONFIG_PTKSA_CACHE 23 24 static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa); 25 26 27 static void ptksa_cache_free_entry(struct ptksa_cache *ptksa, 28 struct ptksa_cache_entry *entry) 29 { 30 ptksa->n_ptksa--; 31 32 dl_list_del(&entry->list); 33 bin_clear_free(entry, sizeof(*entry)); 34 } 35 36 37 static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx) 38 { 39 struct ptksa_cache *ptksa = eloop_ctx; 40 struct ptksa_cache_entry *e, *next; 41 struct os_reltime now; 42 struct dl_list expired; 43 44 if (!ptksa) 45 return; 46 47 os_get_reltime(&now); 48 dl_list_init(&expired); 49 50 /* 51 * Move expired entries from the main ptksa list to a temporary 52 * 'expired' list. This prevents issues if the callback (e->cb) 53 * triggers operations like ptksa_cache_flush(), which would iterate 54 * over ptksa->ptksa. By removing entries first, flush operations 55 * will not double-process or double-free these entries. 56 */ 57 dl_list_for_each_safe(e, next, &ptksa->ptksa, 58 struct ptksa_cache_entry, list) { 59 if (e->expiration > now.sec) 60 continue; 61 62 wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR, 63 MAC2STR(e->addr)); 64 65 dl_list_del(&e->list); 66 ptksa->n_ptksa--; 67 dl_list_add_tail(&expired, &e->list); 68 } 69 70 dl_list_for_each_safe(e, next, &expired, 71 struct ptksa_cache_entry, list) { 72 dl_list_del(&e->list); 73 if (e->cb && e->ctx) 74 e->cb(e); 75 bin_clear_free(e, sizeof(*e)); 76 } 77 78 ptksa_cache_set_expiration(ptksa); 79 } 80 81 82 static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa) 83 { 84 struct ptksa_cache_entry *e; 85 int sec; 86 struct os_reltime now; 87 88 eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); 89 90 if (!ptksa || !ptksa->n_ptksa) 91 return; 92 93 e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list); 94 if (!e) 95 return; 96 97 os_get_reltime(&now); 98 sec = e->expiration - now.sec; 99 if (sec < 0) 100 sec = 0; 101 102 eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL); 103 } 104 105 106 /* 107 * ptksa_cache_init - Initialize PTKSA cache 108 * 109 * Returns: Pointer to PTKSA cache data or %NULL on failure 110 */ 111 struct ptksa_cache * ptksa_cache_init(void) 112 { 113 struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache)); 114 115 wpa_printf(MSG_DEBUG, "PTKSA: Initializing"); 116 117 if (ptksa) 118 dl_list_init(&ptksa->ptksa); 119 120 return ptksa; 121 } 122 123 124 /* 125 * ptksa_cache_deinit - Free all entries in PTKSA cache 126 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() 127 */ 128 void ptksa_cache_deinit(struct ptksa_cache *ptksa) 129 { 130 struct ptksa_cache_entry *e, *next; 131 132 if (!ptksa) 133 return; 134 135 wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa); 136 137 dl_list_for_each_safe(e, next, &ptksa->ptksa, 138 struct ptksa_cache_entry, list) 139 ptksa_cache_free_entry(ptksa, e); 140 141 eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); 142 os_free(ptksa); 143 } 144 145 146 /* 147 * ptksa_cache_get - Fetch a PTKSA cache entry 148 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() 149 * @addr: Peer address or %NULL to match any 150 * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any 151 * Returns: Pointer to PTKSA cache entry or %NULL if no match was found 152 */ 153 struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa, 154 const u8 *addr, u32 cipher) 155 { 156 struct ptksa_cache_entry *e; 157 158 if (!ptksa) 159 return NULL; 160 161 dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { 162 if ((!addr || ether_addr_equal(e->addr, addr)) && 163 (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) 164 return e; 165 } 166 167 return NULL; 168 } 169 170 171 /* 172 * ptksa_cache_list - Dump text list of entries in PTKSA cache 173 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() 174 * @buf: Buffer for the list 175 * @len: Length of the buffer 176 * Returns: Number of bytes written to buffer 177 * 178 * This function is used to generate a text format representation of the 179 * current PTKSA cache contents for the ctrl_iface PTKSA command. 180 */ 181 int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len) 182 { 183 struct ptksa_cache_entry *e; 184 int i = 0, ret; 185 char *pos = buf; 186 struct os_reltime now; 187 188 if (!ptksa) 189 return 0; 190 191 os_get_reltime(&now); 192 193 ret = os_snprintf(pos, buf + len - pos, 194 "Index / ADDR / Cipher / expiration (secs) / TK / KDK\n"); 195 if (os_snprintf_error(buf + len - pos, ret)) 196 return pos - buf; 197 pos += ret; 198 199 dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { 200 ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR, 201 i, MAC2STR(e->addr)); 202 if (os_snprintf_error(buf + len - pos, ret)) 203 return pos - buf; 204 pos += ret; 205 206 ret = os_snprintf(pos, buf + len - pos, " %s %lu ", 207 wpa_cipher_txt(e->cipher), 208 e->expiration - now.sec); 209 if (os_snprintf_error(buf + len - pos, ret)) 210 return pos - buf; 211 pos += ret; 212 213 ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk, 214 e->ptk.tk_len); 215 if (os_snprintf_error(buf + len - pos, ret)) 216 return pos - buf; 217 pos += ret; 218 219 ret = os_snprintf(pos, buf + len - pos, " "); 220 if (os_snprintf_error(buf + len - pos, ret)) 221 return pos - buf; 222 pos += ret; 223 224 ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk, 225 e->ptk.kdk_len); 226 if (os_snprintf_error(buf + len - pos, ret)) 227 return pos - buf; 228 pos += ret; 229 230 ret = os_snprintf(pos, buf + len - pos, "\n"); 231 if (os_snprintf_error(buf + len - pos, ret)) 232 return pos - buf; 233 pos += ret; 234 235 i++; 236 } 237 238 return pos - buf; 239 } 240 241 242 /* 243 * ptksa_cache_flush - Flush PTKSA cache entries 244 * 245 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() 246 * @addr: Peer address or %NULL to match any 247 * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any 248 */ 249 void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) 250 { 251 struct ptksa_cache_entry *e, *next; 252 bool removed = false; 253 254 if (!ptksa) 255 return; 256 257 dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry, 258 list) { 259 if ((!addr || ether_addr_equal(e->addr, addr)) && 260 (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) { 261 wpa_printf(MSG_DEBUG, 262 "Flush PTKSA cache entry for " MACSTR, 263 MAC2STR(e->addr)); 264 265 ptksa_cache_free_entry(ptksa, e); 266 removed = true; 267 } 268 } 269 270 if (removed) 271 ptksa_cache_set_expiration(ptksa); 272 } 273 274 275 /* 276 * ptksa_cache_add - Add a PTKSA cache entry 277 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() 278 * @own_addr: Own MAC address 279 * @addr: Peer address 280 * @cipher: The cipher used 281 * @life_time: The PTK life time in seconds 282 * @ptk: The PTK 283 * @life_time_expiry_cb: Callback for alternative expiration handling 284 * @ctx: Context pointer to save into e->ctx for the callback 285 * @akmp: The key management mechanism that was used to derive the PTK 286 * @auth_alg: The authentication algorithm that was used to derive the PTK 287 * Returns: Pointer to the added PTKSA cache entry or %NULL on error 288 * 289 * This function creates a PTKSA entry and adds it to the PTKSA cache. 290 * If an old entry is already in the cache for the same peer and cipher 291 * this entry will be replaced with the new entry. 292 */ 293 struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, 294 const u8 *own_addr, 295 const u8 *addr, u32 cipher, 296 u32 life_time, 297 const struct wpa_ptk *ptk, 298 void (*life_time_expiry_cb) 299 (struct ptksa_cache_entry *e), 300 void *ctx, u32 akmp, u16 auth_alg) 301 { 302 struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL; 303 struct os_reltime now; 304 bool set_expiry = false; 305 306 if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE) 307 return NULL; 308 309 /* remove a previous entry if present */ 310 ptksa_cache_flush(ptksa, addr, cipher); 311 312 /* no place to add another entry */ 313 if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES) 314 return NULL; 315 316 entry = os_zalloc(sizeof(*entry)); 317 if (!entry) 318 return NULL; 319 320 dl_list_init(&entry->list); 321 os_memcpy(entry->addr, addr, ETH_ALEN); 322 entry->cipher = cipher; 323 entry->cb = life_time_expiry_cb; 324 entry->ctx = ctx; 325 entry->akmp = akmp; 326 entry->auth_alg = auth_alg; 327 328 if (own_addr) 329 os_memcpy(entry->own_addr, own_addr, ETH_ALEN); 330 331 os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk)); 332 333 os_get_reltime(&now); 334 entry->expiration = now.sec + life_time; 335 336 dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) { 337 if (tmp->expiration > entry->expiration) { 338 tmp2 = tmp; 339 break; 340 } 341 } 342 343 if (dl_list_empty(&entry->list)) 344 set_expiry = true; 345 /* 346 * If the expiration is later then all other or the list is empty 347 * entries, add it to the end of the list; 348 * otherwise add it before the relevant entry. 349 */ 350 if (tmp2) 351 dl_list_add(&tmp2->list, &entry->list); 352 else 353 dl_list_add_tail(&ptksa->ptksa, &entry->list); 354 355 ptksa->n_ptksa++; 356 wpa_printf(MSG_DEBUG, 357 "Added PTKSA cache entry addr=" MACSTR " cipher=%u", 358 MAC2STR(addr), cipher); 359 360 if (set_expiry) 361 ptksa_cache_set_expiration(ptksa); 362 363 return entry; 364 } 365 366 #else /* CONFIG_PTKSA_CACHE */ 367 368 struct ptksa_cache * ptksa_cache_init(void) 369 { 370 return (struct ptksa_cache *) 1; 371 } 372 373 374 void ptksa_cache_deinit(struct ptksa_cache *ptksa) 375 { 376 } 377 378 379 struct ptksa_cache_entry * 380 ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) 381 { 382 return NULL; 383 } 384 385 386 int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len) 387 { 388 return -1; 389 } 390 391 392 struct ptksa_cache_entry * 393 ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr, 394 u32 cipher, u32 life_time, const struct wpa_ptk *ptk, 395 void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp, 396 u16 auth_alg) 397 { 398 return NULL; 399 } 400 401 402 void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) 403 { 404 } 405 406 #endif /* CONFIG_PTKSA_CACHE */ 407