blob: 3b5c0b8e5c99cc5dda228d960f420d3f08577736 [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
22static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
23
24
25static void ptksa_cache_free_entry(struct ptksa_cache *ptksa,
26 struct ptksa_cache_entry *entry)
27{
28 ptksa->n_ptksa--;
29
30 dl_list_del(&entry->list);
31 bin_clear_free(entry, sizeof(*entry));
32}
33
34
35static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
36{
37 struct ptksa_cache *ptksa = eloop_ctx;
38 struct ptksa_cache_entry *e, *next;
39 struct os_reltime now;
40
41 if (!ptksa)
42 return;
43
44 os_get_reltime(&now);
45
46 dl_list_for_each_safe(e, next, &ptksa->ptksa,
47 struct ptksa_cache_entry, list) {
48 if (e->expiration > now.sec)
49 continue;
50
51 wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
52 MAC2STR(e->addr));
53
Sunil Ravi89eba102022-09-13 21:04:37 -070054 if (e->cb && e->ctx)
55 e->cb(e);
56 else
57 ptksa_cache_free_entry(ptksa, e);
Hai Shalom60840252021-02-19 19:02:11 -080058 }
59
60 ptksa_cache_set_expiration(ptksa);
61}
62
63
64static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa)
65{
66 struct ptksa_cache_entry *e;
67 int sec;
68 struct os_reltime now;
69
70 eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
71
72 if (!ptksa || !ptksa->n_ptksa)
73 return;
74
75 e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list);
76 if (!e)
77 return;
78
79 os_get_reltime(&now);
80 sec = e->expiration - now.sec;
81 if (sec < 0)
82 sec = 0;
83
84 eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL);
85}
86
87
88/*
89 * ptksa_cache_init - Initialize PTKSA cache
90 *
91 * Returns: Pointer to PTKSA cache data or %NULL on failure
92 */
93struct ptksa_cache * ptksa_cache_init(void)
94{
95 struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache));
96
97 wpa_printf(MSG_DEBUG, "PTKSA: Initializing");
98
99 if (ptksa)
100 dl_list_init(&ptksa->ptksa);
101
102 return ptksa;
103}
104
105
106/*
107 * ptksa_cache_deinit - Free all entries in PTKSA cache
108 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
109 */
110void ptksa_cache_deinit(struct ptksa_cache *ptksa)
111{
112 struct ptksa_cache_entry *e, *next;
113
114 if (!ptksa)
115 return;
116
117 wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa);
118
119 dl_list_for_each_safe(e, next, &ptksa->ptksa,
120 struct ptksa_cache_entry, list)
121 ptksa_cache_free_entry(ptksa, e);
122
123 eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
124 os_free(ptksa);
125}
126
127
128/*
129 * ptksa_cache_get - Fetch a PTKSA cache entry
130 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
131 * @addr: Peer address or %NULL to match any
132 * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
133 * Returns: Pointer to PTKSA cache entry or %NULL if no match was found
134 */
135struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa,
136 const u8 *addr, u32 cipher)
137{
138 struct ptksa_cache_entry *e;
139
140 if (!ptksa)
141 return NULL;
142
143 dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
Sunil Ravieb83e2a2024-06-28 17:34:56 +0000144 if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
Hai Shalom60840252021-02-19 19:02:11 -0800145 (cipher == WPA_CIPHER_NONE || cipher == e->cipher))
146 return e;
147 }
148
149 return NULL;
150}
151
152
153/*
154 * ptksa_cache_list - Dump text list of entries in PTKSA cache
155 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
156 * @buf: Buffer for the list
157 * @len: Length of the buffer
158 * Returns: Number of bytes written to buffer
159 *
160 * This function is used to generate a text format representation of the
161 * current PTKSA cache contents for the ctrl_iface PTKSA command.
162 */
163int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
164{
165 struct ptksa_cache_entry *e;
166 int i = 0, ret;
167 char *pos = buf;
168 struct os_reltime now;
169
170 if (!ptksa)
171 return 0;
172
173 os_get_reltime(&now);
174
175 ret = os_snprintf(pos, buf + len - pos,
176 "Index / ADDR / Cipher / expiration (secs) / TK / KDK\n");
177 if (os_snprintf_error(buf + len - pos, ret))
178 return pos - buf;
179 pos += ret;
180
181 dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
182 ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR,
183 i, MAC2STR(e->addr));
184 if (os_snprintf_error(buf + len - pos, ret))
185 return pos - buf;
186 pos += ret;
187
188 ret = os_snprintf(pos, buf + len - pos, " %s %lu ",
189 wpa_cipher_txt(e->cipher),
190 e->expiration - now.sec);
191 if (os_snprintf_error(buf + len - pos, ret))
192 return pos - buf;
193 pos += ret;
194
195 ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk,
196 e->ptk.tk_len);
197 if (os_snprintf_error(buf + len - pos, ret))
198 return pos - buf;
199 pos += ret;
200
201 ret = os_snprintf(pos, buf + len - pos, " ");
202 if (os_snprintf_error(buf + len - pos, ret))
203 return pos - buf;
204 pos += ret;
205
206 ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk,
207 e->ptk.kdk_len);
208 if (os_snprintf_error(buf + len - pos, ret))
209 return pos - buf;
210 pos += ret;
211
212 ret = os_snprintf(pos, buf + len - pos, "\n");
213 if (os_snprintf_error(buf + len - pos, ret))
214 return pos - buf;
215 pos += ret;
216
217 i++;
218 }
219
220 return pos - buf;
221}
222
223
224/*
225 * ptksa_cache_flush - Flush PTKSA cache entries
226 *
227 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
228 * @addr: Peer address or %NULL to match any
229 * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
230 */
231void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
232{
233 struct ptksa_cache_entry *e, *next;
234 bool removed = false;
235
236 if (!ptksa)
237 return;
238
239 dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
240 list) {
Sunil Ravieb83e2a2024-06-28 17:34:56 +0000241 if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
Hai Shalom60840252021-02-19 19:02:11 -0800242 (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) {
243 wpa_printf(MSG_DEBUG,
244 "Flush PTKSA cache entry for " MACSTR,
245 MAC2STR(e->addr));
246
247 ptksa_cache_free_entry(ptksa, e);
248 removed = true;
249 }
250 }
251
252 if (removed)
253 ptksa_cache_set_expiration(ptksa);
254}
255
256
257/*
258 * ptksa_cache_add - Add a PTKSA cache entry
259 * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
Sunil Ravi89eba102022-09-13 21:04:37 -0700260 * @own_addr: Own MAC address
Hai Shalom60840252021-02-19 19:02:11 -0800261 * @addr: Peer address
262 * @cipher: The cipher used
263 * @life_time: The PTK life time in seconds
264 * @ptk: The PTK
Sunil Ravi89eba102022-09-13 21:04:37 -0700265 * @life_time_expiry_cb: Callback for alternative expiration handling
266 * @ctx: Context pointer to save into e->ctx for the callback
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000267 * @akmp: The key management mechanism that was used to derive the PTK
Hai Shalom60840252021-02-19 19:02:11 -0800268 * Returns: Pointer to the added PTKSA cache entry or %NULL on error
269 *
270 * This function creates a PTKSA entry and adds it to the PTKSA cache.
271 * If an old entry is already in the cache for the same peer and cipher
272 * this entry will be replaced with the new entry.
273 */
274struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
Sunil Ravi89eba102022-09-13 21:04:37 -0700275 const u8 *own_addr,
Hai Shalom60840252021-02-19 19:02:11 -0800276 const u8 *addr, u32 cipher,
277 u32 life_time,
Sunil Ravi89eba102022-09-13 21:04:37 -0700278 const struct wpa_ptk *ptk,
279 void (*life_time_expiry_cb)
280 (struct ptksa_cache_entry *e),
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000281 void *ctx, u32 akmp)
Hai Shalom60840252021-02-19 19:02:11 -0800282{
Hai Shaloma20dcd72022-02-04 13:43:00 -0800283 struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
Hai Shalom60840252021-02-19 19:02:11 -0800284 struct os_reltime now;
Sunil Ravi89eba102022-09-13 21:04:37 -0700285 bool set_expiry = false;
Hai Shalom60840252021-02-19 19:02:11 -0800286
287 if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
288 return NULL;
289
290 /* remove a previous entry if present */
291 ptksa_cache_flush(ptksa, addr, cipher);
292
293 /* no place to add another entry */
294 if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES)
295 return NULL;
296
297 entry = os_zalloc(sizeof(*entry));
298 if (!entry)
299 return NULL;
300
301 dl_list_init(&entry->list);
302 os_memcpy(entry->addr, addr, ETH_ALEN);
303 entry->cipher = cipher;
Sunil Ravi89eba102022-09-13 21:04:37 -0700304 entry->cb = life_time_expiry_cb;
305 entry->ctx = ctx;
Sunil Ravi38ad1ed2023-01-17 23:58:31 +0000306 entry->akmp = akmp;
Sunil Ravi89eba102022-09-13 21:04:37 -0700307
308 if (own_addr)
309 os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
Hai Shalom60840252021-02-19 19:02:11 -0800310
311 os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
312
313 os_get_reltime(&now);
314 entry->expiration = now.sec + life_time;
315
316 dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) {
Hai Shaloma20dcd72022-02-04 13:43:00 -0800317 if (tmp->expiration > entry->expiration) {
318 tmp2 = tmp;
Hai Shalom60840252021-02-19 19:02:11 -0800319 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -0800320 }
Hai Shalom60840252021-02-19 19:02:11 -0800321 }
322
Sunil Ravi89eba102022-09-13 21:04:37 -0700323 if (dl_list_empty(&entry->list))
324 set_expiry = true;
Hai Shalom60840252021-02-19 19:02:11 -0800325 /*
Hai Shaloma20dcd72022-02-04 13:43:00 -0800326 * If the expiration is later then all other or the list is empty
327 * entries, add it to the end of the list;
Hai Shalom60840252021-02-19 19:02:11 -0800328 * otherwise add it before the relevant entry.
329 */
Hai Shaloma20dcd72022-02-04 13:43:00 -0800330 if (tmp2)
331 dl_list_add(&tmp2->list, &entry->list);
Hai Shalom60840252021-02-19 19:02:11 -0800332 else
Hai Shaloma20dcd72022-02-04 13:43:00 -0800333 dl_list_add_tail(&ptksa->ptksa, &entry->list);
Hai Shalom60840252021-02-19 19:02:11 -0800334
335 ptksa->n_ptksa++;
336 wpa_printf(MSG_DEBUG,
337 "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
338 MAC2STR(addr), cipher);
339
Sunil Ravi89eba102022-09-13 21:04:37 -0700340 if (set_expiry)
341 ptksa_cache_set_expiration(ptksa);
342
Hai Shalom60840252021-02-19 19:02:11 -0800343 return entry;
344}