diff mbox series

[2/2] wpa_supplicant: EAPOL MAC address customisation with eapol_dest_addr.

Message ID 20241023163503.530897-3-tim@seoss.co.uk
State New
Headers show
Series [1/2] Improve MKPDU 802.1X conformance, don't require pae group dest address | expand

Commit Message

Tim Small Oct. 23, 2024, 4:35 p.m. UTC
wpa_supplicant previously hard-coded the destination MAC address for
EAPOL packets to 01:80:c2:00:00:03 (the "PAE Group Address"). The PAE
Group Address continues to be the default value for the newly introduced
wpa_supplicant per-network eapol_dest_addr configuration setting, but
alternative multicast addresses (e.g. 01:80:c2:00:00:1f - the "EDE-CC
PEP Address") can now be specified so that outgoing packets can reach
the desired destination station(s) in a wider variety of operating
environments.

For example third party ISP switches providing layer 2 forwarding
services to a customer should filter or terminate packets which use the
PAE Group Address according to 802.1D ("Ethernet MAC bridges").  This
will effectively prevent a customer creating their own secure 802.1X +
MACsec links atop the ISP-provided layer 2 network.  The same ISP
switches should instead forward packets which use the ECE-CC PEP Address
(or a variety of other multicast addresses which may be better suited to
the particular usage scenario).
---
 src/ap/ap_config.h           |  1 +
 src/ap/wpa_auth_kay.c        |  1 +
 src/common/ieee802_1x_defs.h |  8 ++++++
 src/pae/ieee802_1x_kay.c     | 12 ++++-----
 src/pae/ieee802_1x_kay.h     |  5 +++-
 wpa_supplicant/config.c      | 51 ++++++++++++++++++++++++++++++++++++
 wpa_supplicant/config_ssid.h | 11 ++++++++
 wpa_supplicant/wpas_kay.c    |  1 +
 8 files changed, 83 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index d42076785d..57e38c8be7 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -301,6 +301,7 @@  struct hostapd_bss_config {
 	int eapol_version;
 	int eap_server; /* Use internal EAP server instead of external
 			 * RADIUS server */
+	u8 eapol_dest_addr[ETH_ALEN];
 	struct hostapd_eap_user *eap_user;
 	char *eap_user_sqlite;
 	char *eap_sim_db;
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index 625f405127..107924fa90 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -331,6 +331,7 @@  int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
 				  hapd->conf->macsec_port,
 				  hapd->conf->mka_priority,
 				  hapd->conf->macsec_csindex,
+				  hapd->conf->eapol_dest_addr,
 				  hapd->conf->iface,
 				  hapd->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
index e7acff108e..b193472a76 100644
--- a/src/common/ieee802_1x_defs.h
+++ b/src/common/ieee802_1x_defs.h
@@ -83,4 +83,12 @@  enum confidentiality_offset {
 #define DEFAULT_PRIO_GROUP_CA_MEMBER   0x70
 #define DEFAULT_PRIO_NOT_KEY_SERVER    0xFF
 
+/*
+ * Nearest non-TPMR (non Two Port MAC Relay) Bridge group address,
+ * also referred to as IEEE Std 802.1X PAE address
+ * IEEE Std 802.1X-2020 - Table 11-1
+ */
+
+#define PAE_GROUP_ADDRESS { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
+
 #endif /* IEEE802_1X_DEFS_H */
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 230c69d197..c76f7501f0 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -2451,10 +2451,6 @@  ieee802_1x_kay_decide_macsec_use(
 	return 0;
 }
 
-static const u8 pae_group_addr[ETH_ALEN] = {
-	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
-};
-
 
 /**
  * ieee802_1x_kay_encode_mkpdu -
@@ -2468,7 +2464,8 @@  ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
 	struct ieee802_1x_hdr *eapol_hdr;
 
 	ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
-	os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
+	os_memcpy(ether_hdr->dest, participant->kay->eapol_dest_addr,
+		  sizeof(ether_hdr->dest));
 	os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
 		  sizeof(ether_hdr->dest));
 	ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
@@ -3495,7 +3492,8 @@  struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
 		    u8 macsec_offload, u16 port, u8 priority,
-		    u32 macsec_csindex, const char *ifname, const u8 *addr)
+		    u32 macsec_csindex,
+		    const u8 *eapol_dest_addr, const char *ifname, const u8 *addr)
 {
 	struct ieee802_1x_kay *kay;
 
@@ -3536,6 +3534,8 @@  ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
 	kay->mka_version = MKA_VERSION_ID;
 
+	os_memcpy(kay->eapol_dest_addr, eapol_dest_addr, ETH_ALEN);
+
 	os_memcpy(kay->algo_agility, mka_algo_agility,
 		  sizeof(kay->algo_agility));
 
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 11464f7fc6..fd16db3a07 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -234,6 +234,8 @@  struct ieee802_1x_kay {
 
 	enum validate_frames vf;
 	enum confidentiality_offset co;
+
+	u8 eapol_dest_addr[ETH_ALEN];
 };
 
 
@@ -243,7 +245,8 @@  struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
 		    u8 macsec_offload, u16 port, u8 priority,
-		    u32 macsec_csindex, const char *ifname, const u8 *addr);
+		    u32 macsec_csindex,
+		    const u8 *eapol_dest_addr, const char *ifname, const u8 *addr);
 void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
 
 struct ieee802_1x_mka_participant *
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 13043afe94..3c097476a2 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -21,6 +21,9 @@ 
 #include "config.h"
 
 
+static const u8 pae_group_addr[ETH_ALEN] = PAE_GROUP_ADDRESS;
+
+
 #if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
 #define NO_CONFIG_WRITE
 #endif
@@ -1663,6 +1666,30 @@  static int wpa_config_parse_eap(const struct parse_data *data,
 }
 
 
+static int wpa_config_parse_eapol_dest_addr(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	wpa_printf(MSG_DEBUG, "value: '%s'", value);
+
+	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+	    os_strcmp(value, "default") == 0) {
+		os_memcpy(ssid->eapol_dest_addr, pae_group_addr, ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "EAPOL using PAE (default) destination MAC address" MACSTR,
+			MAC2STR(ssid->eapol_dest_addr));
+		return 0;
+	}
+	if (hwaddr_aton2(value, ssid->eapol_dest_addr) == -1) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL destination MAC address '%s'.",
+			   line, value);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "EAPOL destination MAC address " MACSTR,
+		   MAC2STR(ssid->eapol_dest_addr));
+	return 0;
+}
+
+
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_eap(const struct parse_data *data,
 				   struct wpa_ssid *ssid)
@@ -1697,6 +1724,28 @@  static char * wpa_config_write_eap(const struct parse_data *data,
 
 	return buf;
 }
+
+
+static char * wpa_config_write_eapol_dest_addr(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	char *value;
+	int res;
+
+	if (is_zero_ether_addr(ssid->eapol_dest_addr))
+		return NULL;
+
+	value = os_malloc(20);
+	if (value == NULL)
+		return NULL;
+	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->eapol_dest_addr));
+	if (os_snprintf_error(20, res)) {
+		os_free(value);
+		return NULL;
+	}
+	value[20 - 1] = '\0';
+	return value;
+}
 #endif /* NO_CONFIG_WRITE */
 
 
@@ -2549,6 +2598,7 @@  static const struct parse_data ssid_fields[] = {
 	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
+	{ FUNC(eapol_dest_addr) },
 	{ STR_LENe(identity, identity) },
 	{ STR_LENe(anonymous_identity, anonymous_identity) },
 	{ STR_LENe(imsi_identity, imsi_identity) },
@@ -3236,6 +3286,7 @@  void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
 	ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
 	ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
+	os_memcpy(ssid->eapol_dest_addr, pae_group_addr, ETH_ALEN);
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_MESH
 	ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index d64c305082..1909a718d6 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -418,6 +418,17 @@  struct wpa_ssid {
 	 */
 	unsigned int eap_workaround;
 
+	/**
+	 * eapol_dest_addr - mac addr for EAPOL packets (802.11AE-2018+ etc.)
+	 * EAPOL packets may have their destination MAC address set to any
+	 * non-individual (i.g. multi-cast) address, including the ethernet
+	 * broadcast address (ff:ff:ff:ff:ff:ff).  Choice of destination
+	 * address is dictated by which types of entity (should) filter them
+	 * out vs. act on their contents vs. relay them.
+	 * See 802.11X-2020 Table 11-1
+	 */
+        u8 eapol_dest_addr[ETH_ALEN];
+
 #endif /* IEEE8021X_EAPOL */
 
 	/**
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index 600b3bc545..4712bcb221 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -249,6 +249,7 @@  int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 				  ssid->macsec_replay_window,
 				  ssid->macsec_offload, ssid->macsec_port,
 				  ssid->mka_priority, ssid->macsec_csindex,
+				  ssid->eapol_dest_addr,
 				  wpa_s->ifname, wpa_s->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
 	if (res == NULL)