From 0e794989e56959e37039cedb23b1e100fe2bca1f Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Wed, 1 Apr 2020 16:07:25 +0300
Subject: [PATCH] Beacon frame protection event for incorrect protection

Define a driver interface event for Beacon frame protection failures.
Report such events over the control interface and send a
WNM-Notification Request frame to the AP as well.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
 src/common/ieee802_11_defs.h |  7 +++++++
 src/common/wpa_ctrl.h        |  2 ++
 src/drivers/driver.h         | 16 ++++++++++++++++
 src/drivers/driver_common.c  |  1 +
 wpa_supplicant/events.c      | 35 +++++++++++++++++++++++++++++++++++
 5 files changed, 61 insertions(+)

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 8dc469881..7f31e09a1 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1881,6 +1881,13 @@ enum wnm_sleep_mode_subelement_id {
 	WNM_SLEEP_SUBELEM_BIGTK = 2,
 };
 
+/* WNM notification type (IEEE P802.11-REVmd/D3.0, Table 9-430) */
+enum wnm_notification_Type {
+	WNM_NOTIF_TYPE_FIRMWARE_UPDATE = 0,
+	WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE = 2,
+	WNM_NOTIF_TYPE_VENDOR_SPECIFIC = 221,
+};
+
 /* Channel Switch modes (802.11h) */
 #define CHAN_SWITCH_MODE_ALLOW_TX	0
 #define CHAN_SWITCH_MODE_BLOCK_TX	1
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 0b0413068..ca1c35f85 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -95,6 +95,8 @@ extern "C" {
 /** SAE authentication failed due to unknown password identifier */
 #define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
 	"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
+/** Unprotected Beacon frame dropped */
+#define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
 
 /** IP subnet status change notification
  *
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6ffa7fece..032bbd892 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4970,6 +4970,15 @@ enum wpa_event_type {
 	  * EVENT_UPDATE_DH - Notification of updated DH information
 	  */
 	EVENT_UPDATE_DH,
+
+	/**
+	 * EVENT_UNPROT_BEACON - Unprotected Beacon frame received
+	 *
+	 * This event should be called when a Beacon frame is dropped due to it
+	 * not being protected correctly. union wpa_event_data::unprot_beacon
+	 * is required to provide more details of the frame.
+	 */
+	EVENT_UNPROT_BEACON,
 };
 
 
@@ -5822,6 +5831,13 @@ union wpa_event_data {
 		const u8 *ie;
 		size_t ie_len;
 	} update_dh;
+
+	/**
+	 * struct unprot_beacon - Data for EVENT_UNPROT_BEACON
+	 */
+	struct unprot_beacon {
+		const u8 *sa;
+	} unprot_beacon;
 };
 
 /**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 2e03b6676..63846db2e 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -89,6 +89,7 @@ const char * event_to_string(enum wpa_event_type event)
 	E2S(INTERFACE_MAC_CHANGED);
 	E2S(WDS_STA_INTERFACE_STATUS);
 	E2S(UPDATE_DH);
+	E2S(UNPROT_BEACON);
 	}
 
 	return "UNKNOWN";
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index e8b8a9c98..19a883bee 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4467,6 +4467,38 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_event_unprot_beacon(struct wpa_supplicant *wpa_s,
+				     struct unprot_beacon *data)
+{
+	struct wpabuf *buf;
+	int res;
+
+	if (!data || wpa_s->wpa_state != WPA_COMPLETED ||
+	    os_memcmp(data->sa, wpa_s->bssid, ETH_ALEN) != 0)
+		return;
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_UNPROT_BEACON MACSTR,
+		MAC2STR(data->sa));
+
+	buf = wpabuf_alloc(4);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog Token */
+	wpabuf_put_u8(buf, WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE);
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send WNM-Notification Request frame");
+
+	wpabuf_free(buf);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -5271,6 +5303,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 						 data->sta_opmode.rx_nss);
 #endif /* CONFIG_AP */
 		break;
+	case EVENT_UNPROT_BEACON:
+		wpas_event_unprot_beacon(wpa_s, &data->unprot_beacon);
+		break;
 	default:
 		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
 		break;