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