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@xxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap