blob: 918a1cc0c31400acf9db3969c747f07abf259c20 [file] [log] [blame]
Hai Shalom60840252021-02-19 19:02:11 -08001/*
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
17struct ptksa_cache {
18 struct dl_list ptksa;
19 unsigned int n_ptksa;
20};
21
Sunil Ravi99c035e2024-07-12 01:42:03 +000022#ifdef CONFIG_PTKSA_CACHE
23
Hai Shalom60840252021-02-19 19:02:11 -080024static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
25
26
27static 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
37static 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
Sunil Ravi89eba102022-09-13 21:04:37 -070056 if (e->cb && e->ctx)
57 e->cb(e);
58 else
59 ptksa_cache_free_entry(ptksa, e);
Hai Shalom60840252021-02-19 19:02:11 -080060 }
61
62 ptksa_cache_set_expiration(ptksa);
63}
64
65
66static 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 */
95struct 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 */
112void 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 */
137struct 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) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000146 if ((!addr || ether_addr_equal(e->addr, addr)) &&
Hai Shalom60840252021-02-19 19:02:11 -0800147 (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 */
165int 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 */
233void 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) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000243 if ((!addr || ether_addr_equal(e->addr, addr)) &&
Hai Shalom60840252021-02-19 19:02:11 -0800244 (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()
Sunil Ravi89eba102022-09-13 21:04:37 -0700262 * @own_addr: Own MAC address
Hai Shalom60840252021-02-19 19:02:11 -0800263 * @addr: Peer address
264 * @cipher: The cipher used
265 * @life_time: The PTK life time in seconds
266 * @ptk: The PTK
Sunil Ravi89eba102022-09-13 21:04:37 -0700267 * @life_time_expiry_cb: Callback for alternative expiration handling
268 * @ctx: Context pointer to save into e->ctx for the callback
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000269 * @akmp: The key management mechanism that was used to derive the PTK
Hai Shalom60840252021-02-19 19:02:11 -0800270 * 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 */
276struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
Sunil Ravi89eba102022-09-13 21:04:37 -0700277 const u8 *own_addr,
Hai Shalom60840252021-02-19 19:02:11 -0800278 const u8 *addr, u32 cipher,
279 u32 life_time,
Sunil Ravi89eba102022-09-13 21:04:37 -0700280 const struct wpa_ptk *ptk,
281 void (*life_time_expiry_cb)
282 (struct ptksa_cache_entry *e),
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000283 void *ctx, u32 akmp)
Hai Shalom60840252021-02-19 19:02:11 -0800284{
Hai Shaloma20dcd72022-02-04 13:43:00 -0800285 struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
Hai Shalom60840252021-02-19 19:02:11 -0800286 struct os_reltime now;
Sunil Ravi89eba102022-09-13 21:04:37 -0700287 bool set_expiry = false;
Hai Shalom60840252021-02-19 19:02:11 -0800288
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;
Sunil Ravi89eba102022-09-13 21:04:37 -0700306 entry->cb = life_time_expiry_cb;
307 entry->ctx = ctx;
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000308 entry->akmp = akmp;
Sunil Ravi89eba102022-09-13 21:04:37 -0700309
310 if (own_addr)
311 os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
Hai Shalom60840252021-02-19 19:02:11 -0800312
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) {
Hai Shaloma20dcd72022-02-04 13:43:00 -0800319 if (tmp->expiration > entry->expiration) {
320 tmp2 = tmp;
Hai Shalom60840252021-02-19 19:02:11 -0800321 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -0800322 }
Hai Shalom60840252021-02-19 19:02:11 -0800323 }
324
Sunil Ravi89eba102022-09-13 21:04:37 -0700325 if (dl_list_empty(&entry->list))
326 set_expiry = true;
Hai Shalom60840252021-02-19 19:02:11 -0800327 /*
Hai Shaloma20dcd72022-02-04 13:43:00 -0800328 * If the expiration is later then all other or the list is empty
329 * entries, add it to the end of the list;
Hai Shalom60840252021-02-19 19:02:11 -0800330 * otherwise add it before the relevant entry.
331 */
Hai Shaloma20dcd72022-02-04 13:43:00 -0800332 if (tmp2)
333 dl_list_add(&tmp2->list, &entry->list);
Hai Shalom60840252021-02-19 19:02:11 -0800334 else
Hai Shaloma20dcd72022-02-04 13:43:00 -0800335 dl_list_add_tail(&ptksa->ptksa, &entry->list);
Hai Shalom60840252021-02-19 19:02:11 -0800336
337 ptksa->n_ptksa++;
338 wpa_printf(MSG_DEBUG,
339 "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
340 MAC2STR(addr), cipher);
341
Sunil Ravi89eba102022-09-13 21:04:37 -0700342 if (set_expiry)
343 ptksa_cache_set_expiration(ptksa);
344
Hai Shalom60840252021-02-19 19:02:11 -0800345 return entry;
346}
Sunil Ravi99c035e2024-07-12 01:42:03 +0000347
348#else /* CONFIG_PTKSA_CACHE */
349
350struct ptksa_cache * ptksa_cache_init(void)
351{
352 return (struct ptksa_cache *) 1;
353}
354
355
356void ptksa_cache_deinit(struct ptksa_cache *ptksa)
357{
358}
359
360
361struct ptksa_cache_entry *
362ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
363{
364 return NULL;
365}
366
367
368int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
369{
370 return -1;
371}
372
373
374struct ptksa_cache_entry *
375ptksa_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
383void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
384{
385}
386
387#endif /* CONFIG_PTKSA_CACHE */