diff mbox series

[07/14] WPA: Add PTKSA cache implementation

Message ID 20200224091529.15259-8-ilan.peer@intel.com
State Deferred
Headers show
Series Support base Pre association Security Negotiation (PASN) | expand

Commit Message

Peer, Ilan Feb. 24, 2020, 9:15 a.m. UTC
In order to be able to perform secure LTF measurements,
both the initiator and the responder need to first derive
TK and HLTK and store them, so they would later be available
for the secure LTF negotiation.

Add a basic implementation of a PTKSA cache that stores
derived TK/HLTK which can later be used for secure LTF negotiation,
and add it to the build configuration.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/common/Makefile      |   4 +-
 src/common/ptksa_cache.c | 321 +++++++++++++++++++++++++++++++++++++++
 src/common/ptksa_cache.h |  84 ++++++++++
 3 files changed, 408 insertions(+), 1 deletion(-)
 create mode 100644 src/common/ptksa_cache.c
 create mode 100644 src/common/ptksa_cache.h
diff mbox series

Patch

diff --git a/src/common/Makefile b/src/common/Makefile
index ccb280e901..b75f72da43 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -13,13 +13,15 @@  CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_SAE
 CFLAGS += -DCONFIG_SUITE
 CFLAGS += -DCONFIG_SUITEB
+CFLAGS += -DCONFIG_PTKSA_CACHE
 
 LIB_OBJS= \
 	gas.o \
 	hw_features_common.o \
 	ieee802_11_common.o \
 	sae.o \
-	wpa_common.o
+	wpa_common.o \
+	ptksa_cache.o
 
 libcommon.a: $(LIB_OBJS)
 	$(AR) crT $@ $?
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
new file mode 100644
index 0000000000..851ec6e2e3
--- /dev/null
+++ b/src/common/ptksa_cache.c
@@ -0,0 +1,321 @@ 
+/*
+ * WPA Supplicant - 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;
+	size_t n_ptksa;
+};
+
+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));
+
+		ptksa_cache_free_entry(ptksa, e);
+	}
+
+	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=%zu", 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) {
+		if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+		    (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 / HLTK\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.hltk,
+				       e->ptk.hltk_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;
+	int removed = 0;
+
+	if (!ptksa)
+		return;
+
+	dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
+			      list) {
+		if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+		    (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 = 1;
+		}
+	}
+
+	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()
+ * @addr: peer address
+ * @cipher: the cipher used
+ * @life_time: the PTK life time in seconds
+ * @ptk: the PTK
+ * 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,
+					  const u8 *addr,
+					  u32 cipher,
+					  u32 life_time,
+					  struct wpa_ptk *ptk)
+{
+	struct ptksa_cache_entry *entry, *tmp;
+	struct os_reltime now;
+
+	if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
+		return NULL;
+
+	/* remove a previous entry if exits */
+	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;
+
+	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)
+		if (tmp->expiration > entry->expiration)
+			break;
+
+	/*
+	 * If the list was empty add to the head; otherwise if the expiration is
+	 * later then all other entries, add it to the end of the list;
+	 * otherwise add it before the relevant entry
+	 */
+	if (!tmp)
+		dl_list_add(&ptksa->ptksa, &entry->list);
+	else if (tmp->expiration < entry->expiration)
+		dl_list_add(&tmp->list, &entry->list);
+	else
+		dl_list_add_tail(&tmp->list, &entry->list);
+
+	ptksa->n_ptksa++;
+	wpa_printf(MSG_DEBUG,
+		   "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
+		   MAC2STR(addr), cipher);
+
+	return entry;
+}
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
new file mode 100644
index 0000000000..bb327807af
--- /dev/null
+++ b/src/common/ptksa_cache.h
@@ -0,0 +1,84 @@ 
+/*
+ * wpa_supplicant - RSN PTKSA cache interface
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PTKSA_CACHE_H
+#define PTKSA_CACHE_H
+
+#include "wpa_common.h"
+#include "defs.h"
+#include "list.h"
+
+/**
+ * struct ptksa_cache_entry - PTKSA cache entry
+ */
+struct ptksa_cache_entry {
+	struct dl_list list;
+	struct wpa_ptk ptk;
+	os_time_t expiration;
+	u32 cipher;
+	u8 addr[ETH_ALEN];
+};
+
+#ifdef CONFIG_PTKSA_CACHE
+
+struct ptksa_cache;
+
+struct ptksa_cache *ptksa_cache_init(void);
+void ptksa_cache_deinit(struct ptksa_cache *ptksa);
+struct ptksa_cache_entry *ptksa_cache_get(struct ptksa_cache *ptksa,
+					  const u8 *addr,
+					  u32 cipher);
+int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len);
+struct ptksa_cache_entry *ptksa_cache_add(struct ptksa_cache *ptksa,
+					  const u8 *addr,
+					  u32 cipher,
+					  u32 life_time,
+					  struct wpa_ptk *ptk);
+void ptksa_cache_flush(struct ptksa_cache *ptksa,
+		       const u8 *addr, u32 cipher);
+
+#else /* CONFIG_PTKSA_CACHE */
+
+static inline struct ptksa_cache *ptksa_cache_init(void)
+{
+	return (struct ptksa_cache *)1;
+}
+
+static inline void ptksa_cache_deinit(struct ptksa_cache *ptksa)
+{}
+
+static inline struct ptksa_cache_entry *
+ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr,
+		u32 cipher)
+{
+	return NULL;
+}
+
+static inline int ptksa_cache_list(struct ptksa_cache *ptksa,
+				   char *buf, size_t len)
+{
+	return -1;
+}
+
+static inline struct ptksa_cache_entry *
+ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *addr,
+		u32 cipher,
+		u32 life_time,
+		struct wpa_ptk *ptk)
+{
+	return NULL;
+}
+
+static inline void ptksa_cache_flush(struct ptksa_cache *ptksa,
+				     const u8 *addr,
+				     u32 cipher)
+{}
+
+#endif /* CONFIG_PTKSA_CACHE */
+#endif /* PTKSA_CACHE_H */