2020-12-16 13:00:26 +02:00
|
|
|
/*
|
|
|
|
* RSN PTKSA cache implementation
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019 Intel Corporation
|
|
|
|
*
|
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
|
* See README for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
#include "utils/common.h"
|
|
|
|
#include "eloop.h"
|
|
|
|
#include "common/ptksa_cache.h"
|
|
|
|
|
|
|
|
#define PTKSA_CACHE_MAX_ENTRIES 16
|
|
|
|
|
|
|
|
struct ptksa_cache {
|
|
|
|
struct dl_list ptksa;
|
|
|
|
unsigned int n_ptksa;
|
|
|
|
};
|
|
|
|
|
2024-02-01 08:38:33 +05:30
|
|
|
#ifdef CONFIG_PTKSA_CACHE
|
|
|
|
|
2020-12-16 13:00:26 +02:00
|
|
|
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
|
|
|
|
|
|
|
|
|
|
|
|
static void ptksa_cache_free_entry(struct ptksa_cache *ptksa,
|
|
|
|
struct ptksa_cache_entry *entry)
|
|
|
|
{
|
|
|
|
ptksa->n_ptksa--;
|
|
|
|
|
|
|
|
dl_list_del(&entry->list);
|
|
|
|
bin_clear_free(entry, sizeof(*entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
|
|
|
|
{
|
|
|
|
struct ptksa_cache *ptksa = eloop_ctx;
|
|
|
|
struct ptksa_cache_entry *e, *next;
|
|
|
|
struct os_reltime now;
|
|
|
|
|
|
|
|
if (!ptksa)
|
|
|
|
return;
|
|
|
|
|
|
|
|
os_get_reltime(&now);
|
|
|
|
|
|
|
|
dl_list_for_each_safe(e, next, &ptksa->ptksa,
|
|
|
|
struct ptksa_cache_entry, list) {
|
|
|
|
if (e->expiration > now.sec)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
|
|
|
|
MAC2STR(e->addr));
|
|
|
|
|
2022-07-08 11:41:01 +05:30
|
|
|
if (e->cb && e->ctx)
|
|
|
|
e->cb(e);
|
|
|
|
else
|
|
|
|
ptksa_cache_free_entry(ptksa, e);
|
2020-12-16 13:00:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ptksa_cache_set_expiration(ptksa);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa)
|
|
|
|
{
|
|
|
|
struct ptksa_cache_entry *e;
|
|
|
|
int sec;
|
|
|
|
struct os_reltime now;
|
|
|
|
|
|
|
|
eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
|
|
|
|
|
|
|
|
if (!ptksa || !ptksa->n_ptksa)
|
|
|
|
return;
|
|
|
|
|
|
|
|
e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list);
|
|
|
|
if (!e)
|
|
|
|
return;
|
|
|
|
|
|
|
|
os_get_reltime(&now);
|
|
|
|
sec = e->expiration - now.sec;
|
|
|
|
if (sec < 0)
|
|
|
|
sec = 0;
|
|
|
|
|
|
|
|
eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_init - Initialize PTKSA cache
|
|
|
|
*
|
|
|
|
* Returns: Pointer to PTKSA cache data or %NULL on failure
|
|
|
|
*/
|
|
|
|
struct ptksa_cache * ptksa_cache_init(void)
|
|
|
|
{
|
|
|
|
struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache));
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "PTKSA: Initializing");
|
|
|
|
|
|
|
|
if (ptksa)
|
|
|
|
dl_list_init(&ptksa->ptksa);
|
|
|
|
|
|
|
|
return ptksa;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_deinit - Free all entries in PTKSA cache
|
|
|
|
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
|
|
|
|
*/
|
|
|
|
void ptksa_cache_deinit(struct ptksa_cache *ptksa)
|
|
|
|
{
|
|
|
|
struct ptksa_cache_entry *e, *next;
|
|
|
|
|
|
|
|
if (!ptksa)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa);
|
|
|
|
|
|
|
|
dl_list_for_each_safe(e, next, &ptksa->ptksa,
|
|
|
|
struct ptksa_cache_entry, list)
|
|
|
|
ptksa_cache_free_entry(ptksa, e);
|
|
|
|
|
|
|
|
eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
|
|
|
|
os_free(ptksa);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_get - Fetch a PTKSA cache entry
|
|
|
|
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
|
|
|
|
* @addr: Peer address or %NULL to match any
|
|
|
|
* @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
|
|
|
|
* Returns: Pointer to PTKSA cache entry or %NULL if no match was found
|
|
|
|
*/
|
|
|
|
struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa,
|
|
|
|
const u8 *addr, u32 cipher)
|
|
|
|
{
|
|
|
|
struct ptksa_cache_entry *e;
|
|
|
|
|
|
|
|
if (!ptksa)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 23:15:36 +02:00
|
|
|
if ((!addr || ether_addr_equal(e->addr, addr)) &&
|
2020-12-16 13:00:26 +02:00
|
|
|
(cipher == WPA_CIPHER_NONE || cipher == e->cipher))
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_list - Dump text list of entries in PTKSA cache
|
|
|
|
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
|
|
|
|
* @buf: Buffer for the list
|
|
|
|
* @len: Length of the buffer
|
|
|
|
* Returns: Number of bytes written to buffer
|
|
|
|
*
|
|
|
|
* This function is used to generate a text format representation of the
|
|
|
|
* current PTKSA cache contents for the ctrl_iface PTKSA command.
|
|
|
|
*/
|
|
|
|
int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct ptksa_cache_entry *e;
|
|
|
|
int i = 0, ret;
|
|
|
|
char *pos = buf;
|
|
|
|
struct os_reltime now;
|
|
|
|
|
|
|
|
if (!ptksa)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
os_get_reltime(&now);
|
|
|
|
|
|
|
|
ret = os_snprintf(pos, buf + len - pos,
|
|
|
|
"Index / ADDR / Cipher / expiration (secs) / TK / KDK\n");
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
|
|
|
|
ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR,
|
|
|
|
i, MAC2STR(e->addr));
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
ret = os_snprintf(pos, buf + len - pos, " %s %lu ",
|
|
|
|
wpa_cipher_txt(e->cipher),
|
|
|
|
e->expiration - now.sec);
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk,
|
|
|
|
e->ptk.tk_len);
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
ret = os_snprintf(pos, buf + len - pos, " ");
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk,
|
|
|
|
e->ptk.kdk_len);
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
ret = os_snprintf(pos, buf + len - pos, "\n");
|
|
|
|
if (os_snprintf_error(buf + len - pos, ret))
|
|
|
|
return pos - buf;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos - buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_flush - Flush PTKSA cache entries
|
|
|
|
*
|
|
|
|
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
|
|
|
|
* @addr: Peer address or %NULL to match any
|
|
|
|
* @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
|
|
|
|
*/
|
|
|
|
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
|
|
|
|
{
|
|
|
|
struct ptksa_cache_entry *e, *next;
|
|
|
|
bool removed = false;
|
|
|
|
|
|
|
|
if (!ptksa)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
|
|
|
|
list) {
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 23:15:36 +02:00
|
|
|
if ((!addr || ether_addr_equal(e->addr, addr)) &&
|
2020-12-16 13:00:26 +02:00
|
|
|
(cipher == WPA_CIPHER_NONE || cipher == e->cipher)) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"Flush PTKSA cache entry for " MACSTR,
|
|
|
|
MAC2STR(e->addr));
|
|
|
|
|
|
|
|
ptksa_cache_free_entry(ptksa, e);
|
|
|
|
removed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (removed)
|
|
|
|
ptksa_cache_set_expiration(ptksa);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptksa_cache_add - Add a PTKSA cache entry
|
|
|
|
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
|
2022-03-20 14:56:00 +05:30
|
|
|
* @own_addr: Own MAC address
|
2020-12-16 13:00:26 +02:00
|
|
|
* @addr: Peer address
|
|
|
|
* @cipher: The cipher used
|
|
|
|
* @life_time: The PTK life time in seconds
|
|
|
|
* @ptk: The PTK
|
2022-07-08 11:41:01 +05:30
|
|
|
* @life_time_expiry_cb: Callback for alternative expiration handling
|
|
|
|
* @ctx: Context pointer to save into e->ctx for the callback
|
2022-09-21 10:14:55 +05:30
|
|
|
* @akmp: The key management mechanism that was used to derive the PTK
|
2020-12-16 13:00:26 +02:00
|
|
|
* Returns: Pointer to the added PTKSA cache entry or %NULL on error
|
|
|
|
*
|
|
|
|
* This function creates a PTKSA entry and adds it to the PTKSA cache.
|
|
|
|
* If an old entry is already in the cache for the same peer and cipher
|
|
|
|
* this entry will be replaced with the new entry.
|
|
|
|
*/
|
|
|
|
struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
|
2022-03-20 14:56:00 +05:30
|
|
|
const u8 *own_addr,
|
2020-12-16 13:00:26 +02:00
|
|
|
const u8 *addr, u32 cipher,
|
|
|
|
u32 life_time,
|
2022-07-08 11:41:01 +05:30
|
|
|
const struct wpa_ptk *ptk,
|
|
|
|
void (*life_time_expiry_cb)
|
|
|
|
(struct ptksa_cache_entry *e),
|
2022-09-21 10:14:55 +05:30
|
|
|
void *ctx, u32 akmp)
|
2020-12-16 13:00:26 +02:00
|
|
|
{
|
2021-04-16 08:13:12 +02:00
|
|
|
struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
|
2020-12-16 13:00:26 +02:00
|
|
|
struct os_reltime now;
|
2022-07-21 20:17:26 +05:30
|
|
|
bool set_expiry = false;
|
2020-12-16 13:00:26 +02:00
|
|
|
|
|
|
|
if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* remove a previous entry if present */
|
|
|
|
ptksa_cache_flush(ptksa, addr, cipher);
|
|
|
|
|
|
|
|
/* no place to add another entry */
|
|
|
|
if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
entry = os_zalloc(sizeof(*entry));
|
|
|
|
if (!entry)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dl_list_init(&entry->list);
|
|
|
|
os_memcpy(entry->addr, addr, ETH_ALEN);
|
|
|
|
entry->cipher = cipher;
|
2022-07-08 11:41:01 +05:30
|
|
|
entry->cb = life_time_expiry_cb;
|
|
|
|
entry->ctx = ctx;
|
2022-09-21 10:14:55 +05:30
|
|
|
entry->akmp = akmp;
|
2022-07-08 11:41:01 +05:30
|
|
|
|
2022-03-20 14:56:00 +05:30
|
|
|
if (own_addr)
|
|
|
|
os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
|
2020-12-16 13:00:26 +02:00
|
|
|
|
|
|
|
os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
|
|
|
|
|
|
|
|
os_get_reltime(&now);
|
|
|
|
entry->expiration = now.sec + life_time;
|
|
|
|
|
|
|
|
dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) {
|
2021-04-16 08:13:12 +02:00
|
|
|
if (tmp->expiration > entry->expiration) {
|
|
|
|
tmp2 = tmp;
|
2020-12-16 13:00:26 +02:00
|
|
|
break;
|
2021-04-16 08:13:12 +02:00
|
|
|
}
|
2020-12-16 13:00:26 +02:00
|
|
|
}
|
|
|
|
|
2022-07-21 20:17:26 +05:30
|
|
|
if (dl_list_empty(&entry->list))
|
|
|
|
set_expiry = true;
|
2020-12-16 13:00:26 +02:00
|
|
|
/*
|
2021-04-16 08:13:12 +02:00
|
|
|
* If the expiration is later then all other or the list is empty
|
|
|
|
* entries, add it to the end of the list;
|
2020-12-16 13:00:26 +02:00
|
|
|
* otherwise add it before the relevant entry.
|
|
|
|
*/
|
2021-04-16 08:13:12 +02:00
|
|
|
if (tmp2)
|
|
|
|
dl_list_add(&tmp2->list, &entry->list);
|
2020-12-16 13:00:26 +02:00
|
|
|
else
|
2021-04-16 08:13:12 +02:00
|
|
|
dl_list_add_tail(&ptksa->ptksa, &entry->list);
|
2020-12-16 13:00:26 +02:00
|
|
|
|
|
|
|
ptksa->n_ptksa++;
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"Added PTKSA cache entry addr=" MACSTR " cipher=%u",
|
|
|
|
MAC2STR(addr), cipher);
|
|
|
|
|
2022-07-21 20:17:26 +05:30
|
|
|
if (set_expiry)
|
|
|
|
ptksa_cache_set_expiration(ptksa);
|
|
|
|
|
2020-12-16 13:00:26 +02:00
|
|
|
return entry;
|
|
|
|
}
|
2024-02-01 08:38:33 +05:30
|
|
|
|
|
|
|
#else /* CONFIG_PTKSA_CACHE */
|
|
|
|
|
|
|
|
struct ptksa_cache * ptksa_cache_init(void)
|
|
|
|
{
|
|
|
|
return (struct ptksa_cache *) 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ptksa_cache_deinit(struct ptksa_cache *ptksa)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct ptksa_cache_entry *
|
|
|
|
ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct ptksa_cache_entry *
|
|
|
|
ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
|
|
|
|
u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
|
|
|
|
void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_PTKSA_CACHE */
|