Coverage Report

Created: 2026-03-19 06:13

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
 * @auth_alg: The authentication algorithm that was used to derive the PTK
271
 * Returns: Pointer to the added PTKSA cache entry or %NULL on error
272
 *
273
 * This function creates a PTKSA entry and adds it to the PTKSA cache.
274
 * If an old entry is already in the cache for the same peer and cipher
275
 * this entry will be replaced with the new entry.
276
 */
277
struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
278
             const u8 *own_addr,
279
             const u8 *addr, u32 cipher,
280
             u32 life_time,
281
             const struct wpa_ptk *ptk,
282
             void (*life_time_expiry_cb)
283
             (struct ptksa_cache_entry *e),
284
             void *ctx, u32 akmp, u16 auth_alg)
285
0
{
286
0
  struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
287
0
  struct os_reltime now;
288
0
  bool set_expiry = false;
289
290
0
  if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
291
0
    return NULL;
292
293
  /* remove a previous entry if present */
294
0
  ptksa_cache_flush(ptksa, addr, cipher);
295
296
  /* no place to add another entry */
297
0
  if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES)
298
0
    return NULL;
299
300
0
  entry = os_zalloc(sizeof(*entry));
301
0
  if (!entry)
302
0
    return NULL;
303
304
0
  dl_list_init(&entry->list);
305
0
  os_memcpy(entry->addr, addr, ETH_ALEN);
306
0
  entry->cipher = cipher;
307
0
  entry->cb = life_time_expiry_cb;
308
0
  entry->ctx = ctx;
309
0
  entry->akmp = akmp;
310
0
  entry->auth_alg = auth_alg;
311
312
0
  if (own_addr)
313
0
    os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
314
315
0
  os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
316
317
0
  os_get_reltime(&now);
318
0
  entry->expiration = now.sec + life_time;
319
320
0
  dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) {
321
0
    if (tmp->expiration > entry->expiration) {
322
0
      tmp2 = tmp;
323
0
      break;
324
0
    }
325
0
  }
326
327
0
  if (dl_list_empty(&entry->list))
328
0
    set_expiry = true;
329
  /*
330
   * If the expiration is later then all other or the list is empty
331
   * entries, add it to the end of the list;
332
   * otherwise add it before the relevant entry.
333
   */
334
0
  if (tmp2)
335
0
    dl_list_add(&tmp2->list, &entry->list);
336
0
  else
337
0
    dl_list_add_tail(&ptksa->ptksa, &entry->list);
338
339
0
  ptksa->n_ptksa++;
340
0
  wpa_printf(MSG_DEBUG,
341
0
       "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
342
0
       MAC2STR(addr), cipher);
343
344
0
  if (set_expiry)
345
0
    ptksa_cache_set_expiration(ptksa);
346
347
0
  return entry;
348
0
}
349
350
#else /* CONFIG_PTKSA_CACHE */
351
352
struct ptksa_cache * ptksa_cache_init(void)
353
{
354
  return (struct ptksa_cache *) 1;
355
}
356
357
358
void ptksa_cache_deinit(struct ptksa_cache *ptksa)
359
{
360
}
361
362
363
struct ptksa_cache_entry *
364
ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
365
{
366
  return NULL;
367
}
368
369
370
int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
371
{
372
  return -1;
373
}
374
375
376
struct ptksa_cache_entry *
377
ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
378
    u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
379
    void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp,
380
    u16 auth_alg)
381
{
382
  return NULL;
383
}
384
385
386
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
387
{
388
}
389
390
#endif /* CONFIG_PTKSA_CACHE */