From patchwork Mon Feb 24 09:15:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilan Peer X-Patchwork-Id: 1242948 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=AbjtSr0q; 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 48Qxvs1j0lz9sPk for ; Mon, 24 Feb 2020 20:43:37 +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=Ee4Abj5qk+TBrC+9vJo8TQDD7UoEpn5MD9nHXN5NFI4=; b=AbjtSr0qqWBmgXOCfWbavEQ/bf phY/Euc1lMqj7rXYHzlJ6IrlhcwFOUKs35Rt2Yb5dWFwX1GhZ5Wt8G+zyyw+QJmFVzpSLKceXcuYy 1cTni+bmpOremXrb53eG20sQQFu0r5wEbCYbNa7QsGEMbg//maRyei4yLVz3cnGvei25/KWuNJzcO NcIrfJKDfDOtd+CMRxRqhtxVna9ifvFYTltkvCfzKKs+oidilL4+cfxqvSFc1raH9BXATwpkKGPiW 5IbCTY8Eiqxs8QbjEoOcS1YKJcw2oXMn12gNeDWT/MhFcEBTQqNU3DsdOYmFlufZ4A7iEK3jhdB2y lqgogGLw==; 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 1j6AGw-0004ia-T5; Mon, 24 Feb 2020 09:43:30 +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 1j69qA-0007ne-By for hostap@lists.infradead.org; Mon, 24 Feb 2020 09:15:56 +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:38 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.70,479,1574150400"; d="scan'208";a="225914451" Received: from jed01681.jer.intel.com ([10.12.190.127]) by orsmga007.jf.intel.com with ESMTP; 24 Feb 2020 01:15:37 -0800 From: Ilan Peer To: hostap@lists.infradead.org Subject: [PATCH 03/14] PASN: Add common authentication frame build/validation functions Date: Mon, 24 Feb 2020 11:15:18 +0200 Message-Id: <20200224091529.15259-4-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_011550_510574_27DE1CDC X-CRM114-Status: GOOD ( 19.85 ) 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 Add helper functions to construct a PASN authentication frame and validate its content, which are common to both wpa_supplicant and hostapd. Signed-off-by: Ilan Peer --- src/common/wpa_common.c | 404 ++++++++++++++++++++++++++++++++++++++++ src/common/wpa_common.h | 29 +++ 2 files changed, 433 insertions(+) diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 1a67f44720..485b71bd87 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1496,6 +1496,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) #endif /* CONFIG_DPP */ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) return WPA_KEY_MGMT_OSEN; +#ifdef CONFIG_PASN + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN) + return WPA_KEY_MGMT_PASN; +#endif /* CONFIG_PASN */ return 0; } @@ -3218,3 +3222,403 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) return ret; } + + +#ifdef CONFIG_PASN +/* + * wpa_pasn_build_auth_header - Add the MAC header and initialize authentication + * frame for PASN + * + * @buf: pointer to a buffer in which the data would be set + * @bssid: the BSSID of the AP + * @src: source address + * @dst: destination address + * @trans_seq: authentication transaction sequence number + * @status: authentication status + */ +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status) +{ + struct ieee80211_mgmt *auth; + + wpa_printf(MSG_DEBUG, "PASN: Add authentication header. tran_seq=%u", + trans_seq); + + auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.auth.variable)); + + auth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + + os_memcpy(auth->da, dst, ETH_ALEN); + os_memcpy(auth->sa, src, ETH_ALEN); + os_memcpy(auth->bssid, bssid, ETH_ALEN); + + auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN); + auth->seq_ctrl = 0; + auth->u.auth.auth_transaction = host_to_le16(trans_seq); + auth->u.auth.status_code = host_to_le16(status); +} + +/* + * wpa_pasn_add_rsne - Add an RNSE IE for PASN authentication + * + * @buf: pointer to a buffer in which the data would be set + * @pmkid: optional PMKID. Can be NULL. + * @akmp: authentication and key management protocol + * @cipher: the cipher suite + */ +void wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, + int akmp, int cipher) +{ + struct rsn_ie_hdr *hdr; + u32 suite; + u16 capab; + u8 *pos; + u8 rsn_ie_len; + + wpa_printf(MSG_DEBUG, "PASN: Add RSN IE"); + + rsn_ie_len = sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0); + + hdr = wpabuf_put(buf, rsn_ie_len); + hdr->elem_id = WLAN_EID_RSN; + hdr->len = rsn_ie_len - 2; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *)(hdr + 1); + + /* Group addressed data is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + + /* Add the pairwise cipher */ + *pos++ = 1; + *pos++ = 0; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher); + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Add the AKM suite */ + *pos++ = 1; + *pos++ = 0; + + switch (akmp) { + case WPA_KEY_MGMT_PASN: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); + break; +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + break; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + break; + case WPA_KEY_MGMT_FILS_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + break; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + break; + case WPA_KEY_MGMT_FT_IEEE8021X: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_ERROR, "PASN: invalid AKMP=0x%x", + akmp); + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities: PASN mandates both MFP capable and required */ + capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + wpa_printf(MSG_DEBUG, "PASN: Adding PMKID"); + + *pos++ = 1; + *pos++ = 0; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } else { + *pos++ = 0; + *pos++ = 0; + } + + /* Group addressed management is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; +} + + +/* + * wpa_pasn_add_parameter_ie - Add PASN parameters IE for PASN authentication + * + * @buf: pointer to a buffer in which the data would be set + * @wrapped_data_format: format of the data in the wrapped data IE + * @pubkey: a buffer holding the local public key. Can be NULL + * @comeback: a buffer holding the comeback token. Can be NULL + * @after: if comeback is set, defined the comeback time in seconds + */ +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + struct wpabuf *pubkey, + struct wpabuf *comeback, u16 after) +{ + struct pasn_parameter_ie *params; + + wpa_printf(MSG_DEBUG, "PASN: Add PASN parameters IE"); + + params = wpabuf_put(buf, sizeof(*params)); + + params->id = WLAN_EID_EXTENSION; + params->len = sizeof(*params) - 2; + params->id_ext = WLAN_EID_EXT_PASN_PARAMS; + params->control = 0; + params->wrapped_data_format = wrapped_data_format; + + if (comeback) { + wpa_printf(MSG_DEBUG, "PASN: Adding comeback data"); + + /* + * 2 octets for the 'after' field + 1 octet for the length + + * actual cookie data + */ + params->len += 3 + wpabuf_len(comeback); + params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT; + + wpabuf_put_le16(buf, after); + wpabuf_put_u8(buf, wpabuf_len(comeback)); + wpabuf_put_buf(buf, comeback); + } + + if (pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Adding public key"); + + /* + * 2 octets for the finite cyclic group + 2 octets public key + * length + the actual key + */ + params->len += 2 + 2 + wpabuf_len(pubkey); + params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT; + + wpabuf_put_le16(buf, pasn_group); + wpabuf_put_le16(buf, wpabuf_len(pubkey)); + wpabuf_put_buf(buf, pubkey); + } +} + +/* + * wpa_pasn_add_wrapped_data - Adds a wrapped data IE to PASN authentication + * frame. If needed, the wrapped data IE would be fragmented. + * + * @buf: pointer to a buffer in which the data would be set + * @wrapped_data_buf: buffer holding the wrapped data + */ +void wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf) +{ + const u8 *data; + size_t data_len; + u8 len; + + if (!wrapped_data_buf) + return; + + wpa_printf(MSG_DEBUG, "PASN: Add wrapped data"); + + data = wpabuf_head_u8(wrapped_data_buf); + data_len = wpabuf_len(wrapped_data_buf); + + /* nothing to add */ + if (!data_len) + return; + + if (data_len <= 254) + len = 1 + data_len; + else + len = 255; + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, len); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_data(buf, data, len - 1); + + data += len - 1; + data_len -= len - 1; + + while (data_len) { + wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); + len = data_len > 255 ? 255 : data_len; + wpabuf_put_u8(buf, len); + wpabuf_put_data(buf, data, len); + data += len; + data_len -= len; + } +} + +/* + * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE + * + * @data: parsed representation of an RSNE + * Returns -1 for invalid data; otherwise 0 + */ +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) +{ + u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + + if (data->proto != WPA_PROTO_RSN) + return -1; + + if ((data->capabilities & capab) != capab) { + wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities"); + return -1; + } + + if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher"); + return -1; + } + + if (!data->has_pairwise || !data->pairwise_cipher || + (data->pairwise_cipher & (data->pairwise_cipher - 1))) { + wpa_printf(MSG_DEBUG, "PASN: No valid pairwise suite"); + return -1; + } + + switch (data->key_mgmt) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + /* fall through */ +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + /* fall through */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + /* fall through */ +#endif /* CONFIG_IEEE80211R */ + case WPA_KEY_MGMT_PASN: + break; + default: + wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: %u %u", + data->key_mgmt, WPA_KEY_MGMT_SAE); + return -1; + } + + if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher"); + return -1; + } + + if (data->num_pmkid > 1) { + wpa_printf(MSG_DEBUG, "PASN: Invalid number of PMKIDs"); + return -1; + } + + return 0; +} + + +/* + * wpa_pasn_parse_parameter_ie - Validates PASN parameters IE + * + * @data: pointer to the PASN parameters IE (starting with the EID). + * @len: length of the data in the PASN parameters IE + * @pasn_params: On successful return would hold the parsed PASN parameters. + * + * Note: on successful return, the pointers in &pasn_params point to the data in + * the IE and are not locally allocated (so they should not be freed etc.) + * + * Return -1 for invalid data; otherwise 0 + */ +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, + struct wpa_pasn_params_data *pasn_params) +{ + struct pasn_parameter_ie *params = (struct pasn_parameter_ie *)data; + const u8 *pos = (const u8 *)(params + 1); + + if (!pasn_params) { + wpa_printf(MSG_DEBUG, "PASN: Invalid params"); + return -1; + } + + if (!params || ((size_t)(params->len + 2) < sizeof(*params)) || + params->len + 2 != len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE. len=(%u, %u)", + params ? params->len : 0, len); + return -1; + } + + os_memset(pasn_params, 0, sizeof(*pasn_params)); + + switch (params->wrapped_data_format) { + case WPA_PASN_NO_WRAPPED_DATA: + case WPA_PASN_WRAPPED_DATA_SAE: + case WPA_PASN_WRAPPED_DATA_FILS_SK: + case WPA_PASN_WRAPPED_DATA_FT: + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Invalid wrapped data format"); + return -1; + } + + pasn_params->wrapped_data_format = params->wrapped_data_format; + + len -= sizeof(*params); + + if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) { + if (len < 3 || len < *(pos + 2) + 3) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE: comeback len"); + return -1; + } + + pasn_params->after = WPA_GET_LE16(pos); + pasn_params->comeback_len = *(pos + 2); + pasn_params->comeback = pos + 3; + + len -= 3 + pasn_params->comeback_len; + pos += 3 + pasn_params->comeback_len; + } + + if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) { + if (len < 4 || len < WPA_GET_LE16(pos + 2) + 4) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE: group and key"); + return -1; + } + + pasn_params->group = WPA_GET_LE16(pos); + pasn_params->pubkey_len = WPA_GET_LE16(pos + 2); + pasn_params->pubkey = pos + 4; + + len -= 4 + pasn_params->pubkey_len; + pos += 4 + pasn_params->pubkey_len; + } + + if (len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE. Bytes left=%u", len); + return -1; + } + + return 0; +} +#endif /* CONFIG_PASN */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 3ecb85e8c2..22bf484a73 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -514,6 +514,16 @@ struct pasn_parameter_ie { u8 wrapped_data_format; } STRUCT_PACKED; +struct wpa_pasn_params_data { + u8 wrapped_data_format; + u16 after; + u8 comeback_len; + const u8 *comeback; + u16 group; + u16 pubkey_len; + const u8 *pubkey; +}; + int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, int use_sha384); @@ -609,4 +619,23 @@ int pasn_mic(const u8 *kck, int akmp, int cipher, int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, u8 *hash); +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status); + +void wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, + int akmp, int cipher); + +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + struct wpabuf *pubkey, + struct wpabuf *comeback, u16 after); + +void wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf); + +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, + struct wpa_pasn_params_data *pasn_params); + #endif /* WPA_COMMON_H */