diff mbox series

[03/14] PASN: Add common authentication frame build/validation functions

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

Commit Message

Peer, Ilan Feb. 24, 2020, 9:15 a.m. UTC
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 <ilan.peer@intel.com>
---
 src/common/wpa_common.c | 404 ++++++++++++++++++++++++++++++++++++++++
 src/common/wpa_common.h |  29 +++
 2 files changed, 433 insertions(+)

Comments

Jouni Malinen Feb. 29, 2020, 10:19 p.m. UTC | #1
On Mon, Feb 24, 2020 at 11:15:18AM +0200, Ilan Peer wrote:
> Add helper functions to construct a PASN authentication frame and
> validate its content, which are common to both wpa_supplicant and
> hostapd.

This depends on the previous patch and I'm not convinced that this
design is yet stable enough in P802.11az/D2.0.
diff mbox series

Patch

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 */