Coverage Report

Created: 2025-10-28 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */