From patchwork Sun Mar 15 19:04:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Wetzel X-Patchwork-Id: 1255145 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=quarantine dis=none) header.from=wetzel-home.de 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=qmbhhR/8; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=wetzel-home.de header.i=@wetzel-home.de header.a=rsa-sha256 header.s=wetzel-home header.b=YNWR+lvT; 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 48gTRv349Fz9sPJ for ; Mon, 16 Mar 2020 06:06:19 +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:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version: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=W9zHzXKEW+vOseg9QRBfluIZ18qt5ET6Y+CkMxio1uM=; b=qmbhhR/8xPUH4R L/awovKZiiIfMMJRHe/b1cwZLF0ELzH1bEaN/x0MBFIUI8q/tZb1s0XU9EVdcly/IcNAn59pkUOUL EeE5Yg6fjobbT64aOw2flC2V93ninOfzV6VKDRv+p82HjLauG327hwfb8A+iIx2P7foJzjIzCI5yc X9+ED8cJdL4mmFHTBGo47jVcSr1W3tryDoIOKLuCie2hz1NFxFJCH9h5QT8QAlgyrv3D3uvotEsMN UN8jfqlC9l6+EotIU1bbk5Dn6apQBqFxVq7kVJ0Tm6P/7uNjsEcPVBvtRwtuKBDYNuNJ2rUm+DxUW PSQDZqB+A381fffwzLFw==; 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 1jDYaP-0008NI-5s; Sun, 15 Mar 2020 19:06:09 +0000 Received: from 17.mo6.mail-out.ovh.net ([46.105.36.150]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jDYZQ-00060X-7B for hostap@lists.infradead.org; Sun, 15 Mar 2020 19:05:13 +0000 Received: from player755.ha.ovh.net (unknown [10.108.35.128]) by mo6.mail-out.ovh.net (Postfix) with ESMTP id 7271D203334 for ; Sun, 15 Mar 2020 20:04:59 +0100 (CET) Received: from awhome.eu (p4FF9153C.dip0.t-ipconnect.de [79.249.21.60]) (Authenticated sender: postmaster@awhome.eu) by player755.ha.ovh.net (Postfix) with ESMTPSA id CB0AB1083B4BB; Sun, 15 Mar 2020 19:04:57 +0000 (UTC) From: Alexander Wetzel DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=wetzel-home.de; s=wetzel-home; t=1584299088; bh=5PstfaqZxd3PNU8ci1VdvumYCQHpVl0xymyx8h94WJg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=YNWR+lvTS8AlqVQOFVumx9rE+TF+3RQfdKB2KveXPAF81Fe6dt2xOQZWB9jtE1IBG HWVnaRuUDo0liiAhzgP2hjBQ5t1XcgQmi0sG/Bzpkjhs7bcjOMAXg1oar4nw0cwtyl cWnoUPhnW5YcB6Kkloxz2rW0BG/xjmy24NXCgWoI= To: j@w1.fi Subject: [PATCH 5/8] STA: Support Extended Key ID Date: Sun, 15 Mar 2020 20:04:23 +0100 Message-Id: <20200315190426.163478-6-alexander@wetzel-home.de> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200315190426.163478-1-alexander@wetzel-home.de> References: <20200315190426.163478-1-alexander@wetzel-home.de> MIME-Version: 1.0 X-Ovh-Tracer-Id: 8960755885405314240 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: 0 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgedugedrudefuddgvdehucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuqfggjfdpvefjgfevmfevgfenuceurghilhhouhhtmecuhedttdenucenucfjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomheptehlvgigrghnuggvrhcuhggvthiivghluceorghlvgigrghnuggvrhesfigvthiivghlqdhhohhmvgdruggvqeenucffohhmrghinhepphhtkhdrthhknecukfhppedtrddtrddtrddtpdejledrvdegledrvddurdeitdenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepmhhouggvpehsmhhtphdqohhuthdphhgvlhhopehplhgrhigvrhejheehrdhhrgdrohhvhhdrnhgvthdpihhnvghtpedtrddtrddtrddtpdhmrghilhhfrhhomheprghlvgigrghnuggvrhesfigvthiivghlqdhhohhmvgdruggvpdhrtghpthhtohephhhoshhtrghpsehlihhsthhsrdhinhhfrhgruggvrggurdhorhhg X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200315_120508_653673_D86DBDE0 X-CRM114-Status: GOOD ( 22.55 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [46.105.36.150 listed in list.dnswl.org] -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [46.105.36.150 listed in wl.mailspike.net] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders 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: hostap@lists.infradead.org, Alexander Wetzel Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Support Extended Key ID in wpa_supplicant according to IEEE 802.11-2016 for infrastructure (AP) associations. Extended Key ID allows to rekey pairwise keys without the otherwise unavoidable MPDU losses on a busy link. The standard is fully backward compatible, allowing STAs to also connect to APs not supporting it. The standard is not covering how to use it with extensions like FILS or FT and wpa_supplicant sticks to the save settings by default but allows the user to enable non-standard Extended Key ID support for FT and FILS. BASIC Extended Key ID support does nothing not clearly covered by the standard and force it to be off for compatibility when needed. FT0, FILS0 and FILS_CUSTOM are not standardized extensions to also allow Extended Key ID support to be used with FT and/or FILS. FT0, FILS0 and FILS_CUSTOM are not standardized extensions allowing to use Extended Key ID also with FT and/or FILS. FILS0 and FILS_CUSTOM are both detecting when the AP is using the other mode and switch over to it. Signed-off-by: Alexander Wetzel --- src/rsn_supp/wpa.c | 183 ++++++++++++++++++++++-- src/rsn_supp/wpa.h | 14 ++ src/rsn_supp/wpa_ft.c | 27 +++- src/rsn_supp/wpa_i.h | 3 + src/rsn_supp/wpa_ie.c | 7 + wpa_supplicant/ap.c | 1 + wpa_supplicant/config.c | 50 +++++++ wpa_supplicant/config_file.c | 1 + wpa_supplicant/config_ssid.h | 8 ++ wpa_supplicant/ctrl_iface.c | 3 + wpa_supplicant/dbus/dbus_new_handlers.c | 3 +- wpa_supplicant/driver_i.h | 9 +- wpa_supplicant/wpa_cli.c | 2 +- wpa_supplicant/wpa_supplicant.c | 24 +++- wpa_supplicant/wpa_supplicant.conf | 38 +++++ wpa_supplicant/wpas_glue.c | 7 +- 16 files changed, 359 insertions(+), 21 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 5bb47bcbe..dddaff47a 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -184,6 +184,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic; if (pairwise && sm->wpa_deny_ptk0_rekey && + !sm->use_extended_key_id && wpa_sm_get_state(sm) == WPA_COMPLETED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: PTK0 rekey not allowed, reconnecting"); @@ -607,6 +608,94 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, sm->pairwise_cipher, z, z_len); } +#ifdef CONFIG_FILS +static int fils_handle_extended_key_id(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *kde) +{ + struct wpa_ie_data rsn; + + if (sm->extended_key_id & EXT_KEY_ID_FILS && + sm->pairwise_cipher != WPA_CIPHER_TKIP && + !wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &rsn) && + rsn.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) { + sm->use_extended_key_id = 1; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FILS: using Extended Key ID (%s)", + sm->extended_key_id & EXT_KEY_ID_FILS0 ? "FILS0" : + "FILS_CUSTOM"); + if (kde->key_id && sm->extended_key_id & EXT_KEY_ID_FILS0) + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "KeyID in FILS0, using FILS_CUSTOM instead"); + else if (!kde->key_id && + sm->extended_key_id & EXT_KEY_ID_FILS_CUSTOM) + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "No KeyID in FILS_CUSTOM, using FILS0 instead"); + if (kde->key_id) { + if (kde->key_id[0] & 0xfe) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "FILS: Invalid KeyID: %d", + kde->key_id[0]); + return -1; + } + sm->keyidx_active = kde->key_id[0]; + } else { + sm->keyidx_active = 0; + } + } else { + sm->use_extended_key_id = 0; + sm->keyidx_active = 0; + } + return 0; +} +#endif /* CONFIG_FILS */ + +static int handle_extended_key_id(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *kde) +{ + struct wpa_ie_data rsn; + + /* IEEE 802.11-2016 requires the Extended Key ID bit to be set + * in the RSN capabilities for both STAs to use it with CCMP/GCMP. + * How to handle that in combination with FT/FILS is not specified and + * therefore depends on configuration settings. + */ + if (sm->extended_key_id && sm->pairwise_cipher != WPA_CIPHER_TKIP && + (!(wpa_key_mgmt_ft(sm->key_mgmt) || + wpa_key_mgmt_fils(sm->key_mgmt)) || + (wpa_key_mgmt_ft(sm->key_mgmt) && + sm->extended_key_id & EXT_KEY_ID_FT0) || + (wpa_key_mgmt_fils(sm->key_mgmt) && + sm->extended_key_id & EXT_KEY_ID_FILS)) && + !wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &rsn) && + rsn.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) { + if (!kde->key_id) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: No KeyID in Extended Key ID handshake"); + return -1; + } else if (kde->key_id[0] & 0xfe) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Invalid KeyID: %d", kde->key_id[0]); + return -1; + } + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Using Extended Key ID"); + sm->keyidx_active = kde->key_id[0]; + sm->use_extended_key_id = 1; + } else { + if (kde->key_id && kde->key_id[0]) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "Non-zero Extended Key ID KeyID in PTK0 handshake"); + return -1; + } else if (kde->key_id) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "Extended Key ID KeyID in PTK0 handshake"); + } + sm->keyidx_active = 0; + sm->use_extended_key_id = 0; + } + return 0; +} static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, @@ -626,7 +715,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, return; } - if (sm->wpa_deny_ptk0_rekey && wpa_sm_get_state(sm) == WPA_COMPLETED) { + if (sm->wpa_deny_ptk0_rekey && !sm->use_extended_key_id && + wpa_sm_get_state(sm) == WPA_COMPLETED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: PTK0 rekey not allowed, reconnecting"); wpa_sm_reconnect(sm); @@ -762,9 +852,10 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Key negotiation completed with " - MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), + MACSTR " [PTK=%s GTK=%s%s]", MAC2STR(addr), wpa_cipher_txt(sm->pairwise_cipher), - wpa_cipher_txt(sm->group_cipher)); + wpa_cipher_txt(sm->group_cipher), + sm->use_extended_key_id ? " Extended_Key_ID" : ""); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); @@ -859,13 +950,15 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } - if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, - sm->ptk.tk, keylen, + if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc, + rsclen, sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to set PTK to the " - "driver (alg=%d keylen=%d bssid=" MACSTR ")", - alg, keylen, MAC2STR(sm->bssid)); + "WPA: Failed to set PTK to the driver" + "(alg=%d keylen=%d bssid=" MACSTR + " idx=%d use_extended_key_id=%d key_flag=0x%x)", + alg, keylen, MAC2STR(sm->bssid), + sm->keyidx_active, sm->use_extended_key_id, key_flag); return -1; } @@ -879,7 +972,22 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, sm, NULL); } + return 0; +} +static int wpa_supplicant_activate_ptk(struct wpa_sm *sm) +{ + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Activate PTK (idx=%d bssid=" MACSTR ")", + sm->keyidx_active, MAC2STR(sm->bssid)); + + if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, + 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to activate PTK for Tx (idx=%d bssid=" + MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid)); + return -1; + } return 0; } @@ -1582,6 +1690,9 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) goto failed; + if (handle_extended_key_id(sm, &ie)) + goto failed; + if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: ANonce from message 1 of 4-Way Handshake " @@ -1626,6 +1737,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } } #endif /* CONFIG_OCV */ + if (sm->use_extended_key_id) { + if (wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX)) + goto failed; + } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk) < 0) { @@ -1638,8 +1753,13 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { - if (wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX)) + if (sm->use_extended_key_id) { + if (wpa_supplicant_activate_ptk(sm)) + goto failed; + } else if (wpa_supplicant_install_ptk(sm, key, + KEY_FLAG_RX_TX)) { goto failed; + } } if (key_info & WPA_KEY_INFO_SECURE) { @@ -2746,6 +2866,7 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) return NULL; dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; + sm->keyidx_active = 0; sm->ctx = ctx; sm->dot11RSNAConfigPMKLifetime = 43200; @@ -3164,6 +3285,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_DENY_PTK0_REKEY: sm->wpa_deny_ptk0_rekey = value; break; + case WPA_PARAM_EXTENDED_KEY_ID: + sm->extended_key_id = value; + break; default: break; } @@ -3238,6 +3362,18 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm) } +int wpa_sm_extended_key_id(struct wpa_sm *sm) +{ + return sm->extended_key_id; +} + + +int wpa_sm_extended_key_id_active(struct wpa_sm *sm) +{ + return sm->use_extended_key_id; +} + + int wpa_sm_ocv_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; @@ -3268,6 +3404,9 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, #ifdef CONFIG_TESTING_OPTIONS if (sm->test_assoc_ie) { + struct wpa_eapol_ie_parse ie; + struct wpa_ie_data rsn; + wpa_printf(MSG_DEBUG, "TESTING: Replace association WPA/RSN IE"); if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie)) @@ -3275,6 +3414,15 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie), wpabuf_len(sm->test_assoc_ie)); res = wpabuf_len(sm->test_assoc_ie); + + if (wpa_supplicant_parse_ies(wpa_ie, res, &ie) || + wpa_parse_wpa_ie_rsn(ie.rsn_ie, ie.rsn_ie_len, &rsn) || + !(rsn.capabilities & + WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) { + wpa_printf(MSG_DEBUG, + "WPA: Forced own IE disables Extended Key ID"); + sm->extended_key_id = 0; + } } else #endif /* CONFIG_TESTING_OPTIONS */ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); @@ -4253,6 +4401,8 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) capab |= WPA_CAPABILITY_MFPR; if (sm->ocv) capab |= WPA_CAPABILITY_OCVC; + if (sm->extended_key_id & EXT_KEY_ID_FILS) + capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; wpabuf_put_le16(buf, capab); /* PMKID Count */ @@ -4645,6 +4795,7 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) wpa_printf(MSG_DEBUG, "FILS: No GTK KDE"); goto fail; } + maxkeylen = gd.gtk_len = kde.gtk_len - 2; if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd.gtk_len, maxkeylen, @@ -4680,11 +4831,21 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) keylen, (long unsigned int) sm->ptk.tk_len); goto fail; } + + if (elems.rsn_ie) { + /* link in RSN for fils_handle_extended_key_id() */ + kde.rsn_ie = elems.rsn_ie - 2; + kde.rsn_ie_len = elems.rsn_ie_len + 2; + } + if (fils_handle_extended_key_id(sm, &kde)) + goto fail; + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver", sm->ptk.tk, keylen); - if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen, - sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) { + if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, + null_rsc, rsclen, sm->ptk.tk, keylen, + KEY_FLAG_PAIRWISE_RX_TX) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" MACSTR ")", diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 0bd14495a..7202125b1 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -102,6 +102,7 @@ enum wpa_sm_conf_params { WPA_PARAM_OCV, WPA_PARAM_SAE_PWE, WPA_PARAM_DENY_PTK0_REKEY, + WPA_PARAM_EXTENDED_KEY_ID, }; struct rsn_supp_config { @@ -109,6 +110,7 @@ struct rsn_supp_config { int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; + enum ext_key_id_support extended_key_id; void *eap_conf_ctx; const u8 *ssid; size_t ssid_len; @@ -154,6 +156,8 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int wpa_sm_pmf_enabled(struct wpa_sm *sm); +int wpa_sm_extended_key_id(struct wpa_sm *sm); +int wpa_sm_extended_key_id_active(struct wpa_sm *sm); int wpa_sm_ocv_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -300,6 +304,16 @@ static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) return 0; } +static inline int wpa_sm_extended_key_id(struct wpa_sm *sm) +{ + return 0; +} + +static inline int wpa_sm_extended_key_id_active(struct wpa_sm *sm) +{ + return 0; +} + static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm) { return 0; diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 6d627b78f..2caae8334 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -263,6 +263,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, capab |= WPA_CAPABILITY_MFPR; if (sm->ocv) capab |= WPA_CAPABILITY_OCVC; + if (sm->extended_key_id & EXT_KEY_ID_FT0) + capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; WPA_PUT_LE16(pos, capab); pos += 2; @@ -429,8 +431,8 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); - if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), - (u8 *) sm->ptk.tk, keylen, + if (wpa_sm_set_key(sm, alg, bssid, sm->keyidx_active, 1, null_rsc, + sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); return -1; @@ -440,6 +442,26 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) } +static void ft_handle_extended_key_id(struct wpa_sm *sm, + struct wpa_ft_ies *parse) +{ + if (sm->extended_key_id & EXT_KEY_ID_FT0 && + parse->rsn && sm->pairwise_cipher != WPA_CIPHER_TKIP && + parse->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: Using Extended Key ID"); + sm->use_extended_key_id = 1; + } else { + sm->use_extended_key_id = 0; + } + /* There is no standardized way to hand over the KeyID in FT. + * Since FT can only be used for the initial connect we simply + * assume it must be always 0. + */ + sm->keyidx_active = 0; +} + + /** * wpa_ft_prepare_auth_request - Generate over-the-air auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -661,6 +683,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, os_free(ft_ies); } + ft_handle_extended_key_id(sm, &parse); wpa_sm_mark_authenticated(sm, bssid); ret = wpa_ft_install_ptk(sm, bssid); if (ret) { diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 7af678dcd..41c57e3a8 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -26,6 +26,7 @@ struct wpa_sm { u8 snonce[WPA_NONCE_LEN]; u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ int renew_snonce; + int keyidx_active; u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; int rx_replay_counter_set; u8 request_counter[WPA_REPLAY_COUNTER_LEN]; @@ -68,6 +69,8 @@ struct wpa_sm { int wpa_rsc_relaxation; int owe_ptk_workaround; int beacon_prot; + enum ext_key_id_support extended_key_id; + int use_extended_key_id; u8 own_addr[ETH_ALEN]; const char *ifname; diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 03c0d7e85..ed0e1f6c7 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -221,6 +221,13 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, capab |= WPA_CAPABILITY_MFPR; if (sm->ocv) capab |= WPA_CAPABILITY_OCVC; + if (sm->extended_key_id && + (!(wpa_key_mgmt_ft(key_mgmt) || wpa_key_mgmt_fils(key_mgmt)) || + (wpa_key_mgmt_ft(key_mgmt) && + sm->extended_key_id & EXT_KEY_ID_FT0) || + (wpa_key_mgmt_fils(key_mgmt) && + sm->extended_key_id & EXT_KEY_ID_FILS))) + capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; WPA_PUT_LE16(pos, capab); pos += 2; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 87573ef10..5d227519c 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -346,6 +346,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->isolate = !wpa_s->conf->p2p_intra_bss; bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; bss->wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey; + bss->extended_key_id = ssid->extended_key_id; if (ssid->p2p_group) { os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 2b9c3f53e..4de450aa6 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1147,6 +1147,54 @@ static char * wpa_config_write_pairwise(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_extended_key_id(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ +#ifdef CONFIG_NO_WPA + return -1; +#else /* CONFIG_NO_WPA */ + int val = wpa_parse_extended_key_id(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: extended_key_id=%s invalid", + line, value); + return -1; + } + + ssid->extended_key_id = val; + return 0; +#endif /* CONFIG_NO_WPA */ +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_extended_key_id(const struct parse_data *data, + struct wpa_ssid *ssid) +{ +#ifdef CONFIG_NO_WPA + return NULL; +#else /* CONFIG_NO_WPA */ + char *buf; + + if (ssid->extended_key_id == EXT_KEY_ID_DEFAULT) + return NULL; + + buf = os_zalloc(30); + if (buf == NULL) + return NULL; + + if (wpa_write_extended_key_id(buf, buf + 30, + ssid->extended_key_id) < 0) { + os_free(buf); + return NULL; + } + + return buf; +#endif /* CONFIG_NO_WPA */ +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_group(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -2502,6 +2550,7 @@ static const struct parse_data ssid_fields[] = { #endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, { INT_RANGE(wpa_deny_ptk0_rekey, 0, 2) }, + { FUNC(extended_key_id) }, { INT(group_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, @@ -3028,6 +3077,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->group_cipher = DEFAULT_GROUP; ssid->key_mgmt = DEFAULT_KEY_MGMT; ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS; + ssid->extended_key_id = EXT_KEY_ID_DEFAULT; ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; ssid->ht = 1; #ifdef IEEE8021X_EAPOL diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index b8e56f5b2..69c428603 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -921,6 +921,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) #endif /* CONFIG_MESH */ INT(wpa_ptk_rekey); INT(wpa_deny_ptk0_rekey); + STR(extended_key_id); INT(group_rekey); INT(ignore_broadcast_ssid); #ifdef CONFIG_DPP diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 24c7a3d9b..fbe865b9b 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -547,6 +547,14 @@ struct wpa_ssid { unsigned int vht_center_freq1; unsigned int vht_center_freq2; + /** extended_key_id - Extended Key ID support + * + * IEEE 802.11-2016 optionally allows to use keyid 0 and 1 for PTK keys + * with Extended Key ID. This variable controls if and when we want to + * use it. + */ + enum ext_key_id_support extended_key_id; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 077bd6449..664dfa7ed 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -5366,6 +5366,9 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); + if (wpa_sm_extended_key_id(wpa_s->wpa)) + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0, + NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid, MLME_SETPROTECTION_PROTECT_TYPE_NONE, diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index c842c50e9..e0eddb570 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -991,7 +991,7 @@ dbus_bool_t wpas_dbus_getter_global_capabilities( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { - const char *capabilities[11]; + const char *capabilities[12]; size_t num_items = 0; #ifdef CONFIG_FILS struct wpa_global *global = user_data; @@ -1037,6 +1037,7 @@ dbus_bool_t wpas_dbus_getter_global_capabilities( #ifdef CONFIG_OWE capabilities[num_items++] = "owe"; #endif /* CONFIG_OWE */ + capabilities[num_items++] = "extended-key-id"; return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_STRING, diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index d3fb58707..edde6fa99 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -165,7 +165,14 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, params.key_flag = key_flag; if (alg != WPA_ALG_NONE) { - if (key_idx >= 0 && key_idx <= 6) + /* keyidx = 1 can be either a broadcast or - with + * Extended Key ID - an unicast key. Use bit 15 for + * the pairwise keyidx 1 which is hopefully high enough + * to not clash with future extensions. + */ + if (key_idx == 1 && key_flag & KEY_FLAG_PAIRWISE) + wpa_s->keys_cleared &= ~BIT(15); + else if (key_idx >= 0 && key_idx <= 5) wpa_s->keys_cleared &= ~BIT(key_idx); else wpa_s->keys_cleared = 0; diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 22885e646..14cadcedc 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1441,7 +1441,7 @@ static const char *network_fields[] = { "dot11MeshHoldingTimeout", #endif /* CONFIG_MESH */ "wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid", - "wpa_deny_ptk0_rekey", + "wpa_deny_ptk0_rekey", "extended_key_id", "enable_edmg", "edmg_channel", #ifdef CONFIG_P2P "go_p2p_dev_addr", "p2p_client_list", "psk_list", diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 44c34f041..5f8a108c0 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -746,10 +746,15 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); } - if (!(wpa_s->keys_cleared & BIT(0)) && addr && + /* Pairwise key idx 1 for Extended Key ID is tracked in bit 15 */ + if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr && !is_zero_ether_addr(addr)) { - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, - 0, KEY_FLAG_PAIRWISE); + if (!(wpa_s->keys_cleared & (BIT(0)))) + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); + if (!(wpa_s->keys_cleared & (BIT(15)))) + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection( wpa_s, addr, @@ -1633,6 +1638,19 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, sae_pwe = 1; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe); + /* Extended Key ID is only supported in INFRA mode so far */ + if (ssid->mode == WPAS_MODE_INFRA && ssid->extended_key_id && + ssid->proto & WPA_PROTO_RSN && + ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID) { + wpa_msg(wpa_s, MSG_INFO, "Enable Extended Key ID support"); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID, + ssid->extended_key_id); + } else { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID, 0); + } + if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index f3a750e3c..18ba69eb4 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -1098,6 +1098,44 @@ fast_reauth=1 # hex without quotation, e.g., 0102030405) # wep_tx_keyidx: Default WEP key index (TX) (0..3) # +# extended_key_id: +# "Extended Key ID support for Individually Addressed Frames" according to +# IEEE 802.11-2016. +# +# Extended Key ID allows to rekey PTK keys without the impacts PTK0 rekeying +# has. It can only be used when the driver supports it and requires wpa=2 +# with a CCMP/GCMP cipher. +# +# The standard is not regulating if or how Extended Key ID can be used in +# combination with FT or FILS. Enabling it for FT or FILS may therefore be +# incompatible with other implementations. +# +# Available options, can be combined with '+': +# OFF : Disable Extended Key ID support +# BASIC : Enable core Extended Key ID support according to +# IEEE 802.11-2016 when the driver supports it +# PREFER0 : Use key 0 for the initial key when either 0 or 1 can be used +# in AP mode and otherwise ignored +# FT0 : - NOT PART OF IEEE 802.11-2016 - +# Enable Extended Key ID for FT and assume keyid 0 for all FT +# handshakes +# FILS0 : - NOT PART OF IEEE 802.11-2016 - +# Enable Extended Key ID with FILS and assume keyid 0 for all +# FILS handshakes (detects and use FILS_CUSTOM when needed) +# FILS_CUSTOM : - NOT PART OF IEEE 802.11-2016 - +# Enable Extended Key ID with FILS and add the keyid to the +# FILS handshakes (detects and use FILS0 when needed) +# Rules: +# - Either OFF or BASIC must be defined +# - BASIC can be combined with anything except OFF +# - FILS0 and FILS_CUSTOM are mutually exclusive +# +# Example: +# extended_key_id=BASIC+FT0+FILS0 +# +# Default: +# extended_key_id=BASIC +# # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. # diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 39b05b2b9..574a3a827 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -533,7 +533,8 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg, } #endif /* CONFIG_TESTING_GET_GTK */ #ifdef CONFIG_TESTING_OPTIONS - if (addr && !is_broadcast_ether_addr(addr)) { + if (addr && !is_broadcast_ether_addr(addr) && + !(key_flag & KEY_FLAG_MODIFY)) { wpa_s->last_tk_alg = alg; os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN); wpa_s->last_tk_key_idx = key_idx; @@ -1077,7 +1078,8 @@ static int wpa_supplicant_eap_auth_start_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; - if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey) { + if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey && + !wpa_sm_extended_key_id_active(wpa_s->wpa)) { wpa_msg(wpa_s, MSG_INFO, "WPA: PTK0 rekey not allowed, reconnecting"); wpa_supplicant_reconnect(wpa_s); @@ -1318,6 +1320,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, #endif /* IEEE8021X_EAPOL */ conf.ssid = ssid->ssid; conf.ssid_len = ssid->ssid_len; + conf.extended_key_id = ssid->extended_key_id; conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey; conf.wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey; conf.owe_ptk_workaround = ssid->owe_ptk_workaround;