[PATCH v2 09/20] PASN: Add common authentication frame build/validation functions
Ilan Peer
ilan.peer at intel.com
Wed Dec 16 06:00:22 EST 2020
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 at intel.com>
---
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 f8b38eab90..7031208189 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1505,6 +1505,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;
}
@@ -3262,3 +3266,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 0117ae719e..a094686d4e 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -535,6 +535,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);
@@ -635,4 +645,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 */
--
2.17.1
More information about the Hostap
mailing list