From patchwork Mon Feb 24 09:15:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peer, Ilan" X-Patchwork-Id: 1242952 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20170209 header.b=d7hR6Pzn; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48Qxww2v0Dz9sPk for ; Mon, 24 Feb 2020 20:44:32 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=5Vj232sT4owKhjbWWQ2KL4Q7R91BAcNVyt5mvnH0YAU=; b=d7hR6PznueJgQEQvLoiM0mO+hQ MREOWaPXAB8HKpfGIuK8M2rYGBHR+iWUZ1OMeVaqFAHOCDwX/s/SBIkdFpz/DSeEG86s+Sp23ptMs rGbnbHJX69fMOIj/CjiUkdeIQNCO5WEJcSxBKRPR1zHeZRfSalOhCME/qK1QzxXPryMB+Vwy0FP5E pGX6cXG8WlsyGWl1wI5D+YIqy6ALJcBxQSx663XVHPj8/I2Jb2Ot3y9yzRFwgvPg581U6uXK86M9g K0Tk/cddkQ1nTuYyI5AWfHqHX+yOE+lB7ovo5LKVKVvex01FmND7MOwGpTiVekAuMWGf1xYsKOq/z 9m0CS6bQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1j6AHr-0005WJ-2K; Mon, 24 Feb 2020 09:44:27 +0000 Received: from mga17.intel.com ([192.55.52.151]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1j69qJ-0007oi-2l for hostap@lists.infradead.org; Mon, 24 Feb 2020 09:16:03 +0000 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Feb 2020 01:15:44 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.70,479,1574150400"; d="scan'208";a="225914488" Received: from jed01681.jer.intel.com ([10.12.190.127]) by orsmga007.jf.intel.com with ESMTP; 24 Feb 2020 01:15:42 -0800 From: Ilan Peer To: hostap@lists.infradead.org Subject: [PATCH 07/14] WPA: Add PTKSA cache implementation Date: Mon, 24 Feb 2020 11:15:22 +0200 Message-Id: <20200224091529.15259-8-ilan.peer@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200224091529.15259-1-ilan.peer@intel.com> References: <20200224091529.15259-1-ilan.peer@intel.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200224_011559_343762_AA11C166 X-CRM114-Status: GOOD ( 23.30 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [192.55.52.151 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ilan Peer MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org 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 --- 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 --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 */