]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
PASN: Add common Authentication frame build/validation functions
authorIlan Peer <ilan.peer@intel.com>
Wed, 16 Dec 2020 11:00:22 +0000 (13:00 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 25 Jan 2021 16:36:40 +0000 (18:36 +0200)
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
src/common/wpa_common.h

index 7f24549e2b9f76d376bf5e9916261d5a1e37bac0..6cb5e8783bf8f5a126c8145b99938ab899bcfaa6 100644 (file)
@@ -1504,6 +1504,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;
 }
 
@@ -3259,3 +3263,431 @@ 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: Buffer in which the header will be added
+ * @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. trans_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->seq_ctrl = 0;
+
+       auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN);
+       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 RSNE for PASN authentication
+ * @buf: Buffer in which the IE will be added
+ * @pmkid: Optional PMKID. Can be NULL.
+ * @akmp: Authentication and key management protocol
+ * @cipher: The cipher suite
+ */
+int 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 rsne_len;
+
+       wpa_printf(MSG_DEBUG, "PASN: Add RSNE");
+
+       rsne_len = sizeof(*hdr) + RSN_SELECTOR_LEN +
+               2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN +
+               2 + RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0);
+
+       if (wpabuf_tailroom(buf) < rsne_len)
+               return -1;
+       hdr = wpabuf_put(buf, rsne_len);
+       hdr->elem_id = WLAN_EID_RSN;
+       hdr->len = rsne_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 */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher);
+       RSN_SELECTOR_PUT(pos, suite);
+       pos += RSN_SELECTOR_LEN;
+
+       /* Add the AKM suite */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+
+       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);
+               return -1;
+       }
+       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");
+
+               WPA_PUT_LE16(pos, 1);
+               pos += 2;
+               os_memcpy(pos, pmkid, PMKID_LEN);
+               pos += PMKID_LEN;
+       } else {
+               WPA_PUT_LE16(pos, 0);
+               pos += 2;
+       }
+
+       /* Group addressed management is not allowed */
+       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+
+       return 0;
+}
+
+
+/*
+ * wpa_pasn_add_parameter_ie - Add PASN Parameters IE for PASN authentication
+ * @buf: Buffer in which the IE will be added
+ * @pasn_group: Finite Cyclic Group ID for PASN authentication
+ * @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. -1 to not
+ *     include the Comeback After field (frames from non-AP STA).
+ */
+void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
+                              u8 wrapped_data_format,
+                              struct wpabuf *pubkey,
+                              struct wpabuf *comeback, int after)
+{
+       struct pasn_parameter_ie *params;
+
+       wpa_printf(MSG_DEBUG, "PASN: Add PASN Parameters element");
+
+       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
+                */
+               if (after >= 0)
+                       params->len += 2;
+               params->len += 1 + wpabuf_len(comeback);
+               params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT;
+
+               if (after >= 0)
+                       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 and group ID %u",
+                          pasn_group);
+
+               /*
+                * 2 octets for the finite cyclic group + 2 octets public key
+                * length + the actual key
+                */
+               params->len += 2 + 1 + wpabuf_len(pubkey);
+               params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT;
+
+               wpabuf_put_le16(buf, pasn_group);
+               wpabuf_put_u8(buf, wpabuf_len(pubkey));
+               wpabuf_put_buf(buf, pubkey);
+       }
+}
+
+/*
+ * wpa_pasn_add_wrapped_data - Add a Wrapped Data IE to PASN Authentication
+ * frame. If needed, the Wrapped Data IE would be fragmented.
+ *
+ * @buf: Buffer in which the IE will be added
+ * @wrapped_data_buf: Buffer holding the wrapped data
+ */
+int 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 0;
+
+       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 0;
+
+       if (data_len <= 254)
+               len = 1 + data_len;
+       else
+               len = 255;
+
+       if (wpabuf_tailroom(buf) < 3 + data_len)
+               return -1;
+
+       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) {
+               if (wpabuf_tailroom(buf) < 1 + data_len)
+                       return -1;
+               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;
+       }
+
+       return 0;
+}
+
+
+/*
+ * 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: 0x%0x",
+                          data->key_mgmt);
+               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
+ * @from_ap: Whether this was received from an AP
+ * @pasn_params: On successful return would hold the parsed PASN parameters.
+ * Returns: -1 for invalid data; otherwise 0
+ *
+ * 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.).
+ */
+int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
+                               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)) ||
+           len < 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_WRAPPED_DATA_NO:
+       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 (from_ap) {
+                       if (len < 2) {
+                               wpa_printf(MSG_DEBUG,
+                                          "PASN: Invalid Parameters IE: Truncated Comeback After");
+                               return -1;
+                       }
+                       pasn_params->after = WPA_GET_LE16(pos);
+                       pos += 2;
+                       len -= 2;
+               }
+
+               if (len < 1 || len < 1 + *pos) {
+                       wpa_printf(MSG_DEBUG,
+                                  "PASN: Invalid Parameters IE: comeback len");
+                       return -1;
+               }
+
+               pasn_params->comeback_len = *pos++;
+               len--;
+               pasn_params->comeback = pos;
+               len -=  pasn_params->comeback_len;
+               pos += pasn_params->comeback_len;
+       }
+
+       if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) {
+               if (len < 3 || len < 3 + pos[2]) {
+                       wpa_printf(MSG_DEBUG,
+                                  "PASN: Invalid Parameters IE: group and key");
+                       return -1;
+               }
+
+               pasn_params->group = WPA_GET_LE16(pos);
+               pos += 2;
+               len -= 2;
+               pasn_params->pubkey_len = *pos++;
+               len--;
+               pasn_params->pubkey = pos;
+               len -= pasn_params->pubkey_len;
+               pos += 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 */
index ff44ab2d980a961c63953b44dce2897ee2706b68..4fe8077a6a85a43a01c33122ec92358c87235f0e 100644 (file)
@@ -535,6 +535,16 @@ struct pasn_parameter_ie {
        u8 wrapped_data_format; /* WPA_PASN_WRAPPED_DATA_* */
 } STRUCT_PACKED;
 
+struct wpa_pasn_params_data {
+       u8 wrapped_data_format;
+       u16 after;
+       u8 comeback_len;
+       const u8 *comeback;
+       u16 group;
+       u8 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);
+
+int 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, int after);
+
+int 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, bool from_ap,
+                               struct wpa_pasn_params_data *pasn_params);
+
 #endif /* WPA_COMMON_H */