/src/hostap/src/common/ptksa_cache.c
Line | Count | Source |
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 | 0 | #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 | 0 | { |
30 | 0 | ptksa->n_ptksa--; |
31 | |
|
32 | 0 | dl_list_del(&entry->list); |
33 | 0 | bin_clear_free(entry, sizeof(*entry)); |
34 | 0 | } |
35 | | |
36 | | |
37 | | static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx) |
38 | 0 | { |
39 | 0 | struct ptksa_cache *ptksa = eloop_ctx; |
40 | 0 | struct ptksa_cache_entry *e, *next; |
41 | 0 | struct os_reltime now; |
42 | |
|
43 | 0 | if (!ptksa) |
44 | 0 | return; |
45 | | |
46 | 0 | os_get_reltime(&now); |
47 | |
|
48 | 0 | dl_list_for_each_safe(e, next, &ptksa->ptksa, |
49 | 0 | struct ptksa_cache_entry, list) { |
50 | 0 | if (e->expiration > now.sec) |
51 | 0 | continue; |
52 | | |
53 | 0 | wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR, |
54 | 0 | MAC2STR(e->addr)); |
55 | |
|
56 | 0 | if (e->cb && e->ctx) |
57 | 0 | e->cb(e); |
58 | 0 | else |
59 | 0 | ptksa_cache_free_entry(ptksa, e); |
60 | 0 | } |
61 | |
|
62 | 0 | ptksa_cache_set_expiration(ptksa); |
63 | 0 | } |
64 | | |
65 | | |
66 | | static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa) |
67 | 0 | { |
68 | 0 | struct ptksa_cache_entry *e; |
69 | 0 | int sec; |
70 | 0 | struct os_reltime now; |
71 | |
|
72 | 0 | eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); |
73 | |
|
74 | 0 | if (!ptksa || !ptksa->n_ptksa) |
75 | 0 | return; |
76 | | |
77 | 0 | e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list); |
78 | 0 | if (!e) |
79 | 0 | return; |
80 | | |
81 | 0 | os_get_reltime(&now); |
82 | 0 | sec = e->expiration - now.sec; |
83 | 0 | if (sec < 0) |
84 | 0 | sec = 0; |
85 | |
|
86 | 0 | eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL); |
87 | 0 | } |
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 | 0 | { |
97 | 0 | struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache)); |
98 | |
|
99 | 0 | wpa_printf(MSG_DEBUG, "PTKSA: Initializing"); |
100 | |
|
101 | 0 | if (ptksa) |
102 | 0 | dl_list_init(&ptksa->ptksa); |
103 | |
|
104 | 0 | return ptksa; |
105 | 0 | } |
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 | 0 | { |
114 | 0 | struct ptksa_cache_entry *e, *next; |
115 | |
|
116 | 0 | if (!ptksa) |
117 | 0 | return; |
118 | | |
119 | 0 | wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa); |
120 | |
|
121 | 0 | dl_list_for_each_safe(e, next, &ptksa->ptksa, |
122 | 0 | struct ptksa_cache_entry, list) |
123 | 0 | ptksa_cache_free_entry(ptksa, e); |
124 | |
|
125 | 0 | eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); |
126 | 0 | os_free(ptksa); |
127 | 0 | } |
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 | 0 | { |
140 | 0 | struct ptksa_cache_entry *e; |
141 | |
|
142 | 0 | if (!ptksa) |
143 | 0 | return NULL; |
144 | | |
145 | 0 | dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { |
146 | 0 | if ((!addr || ether_addr_equal(e->addr, addr)) && |
147 | 0 | (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) |
148 | 0 | return e; |
149 | 0 | } |
150 | | |
151 | 0 | return NULL; |
152 | 0 | } |
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 | 0 | { |
167 | 0 | struct ptksa_cache_entry *e; |
168 | 0 | int i = 0, ret; |
169 | 0 | char *pos = buf; |
170 | 0 | struct os_reltime now; |
171 | |
|
172 | 0 | if (!ptksa) |
173 | 0 | return 0; |
174 | | |
175 | 0 | os_get_reltime(&now); |
176 | |
|
177 | 0 | ret = os_snprintf(pos, buf + len - pos, |
178 | 0 | "Index / ADDR / Cipher / expiration (secs) / TK / KDK\n"); |
179 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
180 | 0 | return pos - buf; |
181 | 0 | pos += ret; |
182 | |
|
183 | 0 | dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { |
184 | 0 | ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR, |
185 | 0 | i, MAC2STR(e->addr)); |
186 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
187 | 0 | return pos - buf; |
188 | 0 | pos += ret; |
189 | |
|
190 | 0 | ret = os_snprintf(pos, buf + len - pos, " %s %lu ", |
191 | 0 | wpa_cipher_txt(e->cipher), |
192 | 0 | e->expiration - now.sec); |
193 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
194 | 0 | return pos - buf; |
195 | 0 | pos += ret; |
196 | |
|
197 | 0 | ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk, |
198 | 0 | e->ptk.tk_len); |
199 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
200 | 0 | return pos - buf; |
201 | 0 | pos += ret; |
202 | |
|
203 | 0 | ret = os_snprintf(pos, buf + len - pos, " "); |
204 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
205 | 0 | return pos - buf; |
206 | 0 | pos += ret; |
207 | |
|
208 | 0 | ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk, |
209 | 0 | e->ptk.kdk_len); |
210 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
211 | 0 | return pos - buf; |
212 | 0 | pos += ret; |
213 | |
|
214 | 0 | ret = os_snprintf(pos, buf + len - pos, "\n"); |
215 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
216 | 0 | return pos - buf; |
217 | 0 | pos += ret; |
218 | |
|
219 | 0 | i++; |
220 | 0 | } |
221 | | |
222 | 0 | return pos - buf; |
223 | 0 | } |
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 | 0 | { |
235 | 0 | struct ptksa_cache_entry *e, *next; |
236 | 0 | bool removed = false; |
237 | |
|
238 | 0 | if (!ptksa) |
239 | 0 | return; |
240 | | |
241 | 0 | dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry, |
242 | 0 | list) { |
243 | 0 | if ((!addr || ether_addr_equal(e->addr, addr)) && |
244 | 0 | (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) { |
245 | 0 | wpa_printf(MSG_DEBUG, |
246 | 0 | "Flush PTKSA cache entry for " MACSTR, |
247 | 0 | MAC2STR(e->addr)); |
248 | |
|
249 | 0 | ptksa_cache_free_entry(ptksa, e); |
250 | 0 | removed = true; |
251 | 0 | } |
252 | 0 | } |
253 | |
|
254 | 0 | if (removed) |
255 | 0 | ptksa_cache_set_expiration(ptksa); |
256 | 0 | } |
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 | 0 | { |
285 | 0 | struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL; |
286 | 0 | struct os_reltime now; |
287 | 0 | bool set_expiry = false; |
288 | |
|
289 | 0 | if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE) |
290 | 0 | return NULL; |
291 | | |
292 | | /* remove a previous entry if present */ |
293 | 0 | ptksa_cache_flush(ptksa, addr, cipher); |
294 | | |
295 | | /* no place to add another entry */ |
296 | 0 | if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES) |
297 | 0 | return NULL; |
298 | | |
299 | 0 | entry = os_zalloc(sizeof(*entry)); |
300 | 0 | if (!entry) |
301 | 0 | return NULL; |
302 | | |
303 | 0 | dl_list_init(&entry->list); |
304 | 0 | os_memcpy(entry->addr, addr, ETH_ALEN); |
305 | 0 | entry->cipher = cipher; |
306 | 0 | entry->cb = life_time_expiry_cb; |
307 | 0 | entry->ctx = ctx; |
308 | 0 | entry->akmp = akmp; |
309 | |
|
310 | 0 | if (own_addr) |
311 | 0 | os_memcpy(entry->own_addr, own_addr, ETH_ALEN); |
312 | |
|
313 | 0 | os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk)); |
314 | |
|
315 | 0 | os_get_reltime(&now); |
316 | 0 | entry->expiration = now.sec + life_time; |
317 | |
|
318 | 0 | dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) { |
319 | 0 | if (tmp->expiration > entry->expiration) { |
320 | 0 | tmp2 = tmp; |
321 | 0 | break; |
322 | 0 | } |
323 | 0 | } |
324 | |
|
325 | 0 | if (dl_list_empty(&entry->list)) |
326 | 0 | 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 | 0 | if (tmp2) |
333 | 0 | dl_list_add(&tmp2->list, &entry->list); |
334 | 0 | else |
335 | 0 | dl_list_add_tail(&ptksa->ptksa, &entry->list); |
336 | |
|
337 | 0 | ptksa->n_ptksa++; |
338 | 0 | wpa_printf(MSG_DEBUG, |
339 | 0 | "Added PTKSA cache entry addr=" MACSTR " cipher=%u", |
340 | 0 | MAC2STR(addr), cipher); |
341 | |
|
342 | 0 | if (set_expiry) |
343 | 0 | ptksa_cache_set_expiration(ptksa); |
344 | |
|
345 | 0 | return entry; |
346 | 0 | } |
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 */ |