mac80211: do not accept/forward invalid EAPOL frames

EAPOL frames are used for authentication and key management between the
AP and each individual STA associated in the BSS. Those frames are not
supposed to be sent by one associated STA to another associated STA
(either unicast for broadcast/multicast).

Similarly, in 802.11 they're supposed to be sent to the authenticator
(AP) address.

Since it is possible for unexpected EAPOL frames to result in misbehavior
in supplicant implementations, it is better for the AP to not allow such
cases to be forwarded to other clients either directly, or indirectly if
the AP interface is part of a bridge.

Accept EAPOL (control port) frames only if they're transmitted to the
own address, or, due to interoperability concerns, to the PAE group
address.

Disable forwarding of EAPOL (or well, the configured control port
protocol) frames back to wireless medium in all cases. Previously, these
frames were accepted from fully authenticated and authorized stations
and also from unauthenticated stations for one of the cases.

Additionally, to avoid forwarding by the bridge, rewrite the PAE group
address case to the local MAC address.

Cc: stable@vger.kernel.org
Co-developed-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Link: https://lore.kernel.org/r/20210511200110.cb327ed0cabe.Ib7dcffa2a31f0913d660de65ba3c8aca75b1d10f@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2021-05-11 20:02:50 +02:00
parent 7e44a0b597
commit a8c4d76a8d

View File

@ -2531,13 +2531,13 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
/* /*
* Allow EAPOL frames to us/the PAE group address regardless * Allow EAPOL frames to us/the PAE group address regardless of
* of whether the frame was encrypted or not. * whether the frame was encrypted or not, and always disallow
* all other destination addresses for them.
*/ */
if (ehdr->h_proto == rx->sdata->control_port_protocol && if (unlikely(ehdr->h_proto == rx->sdata->control_port_protocol))
(ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) ||
ether_addr_equal(ehdr->h_dest, pae_group_addr))) ether_addr_equal(ehdr->h_dest, pae_group_addr);
return true;
if (ieee80211_802_1x_port_control(rx) || if (ieee80211_802_1x_port_control(rx) ||
ieee80211_drop_unencrypted(rx, fc)) ieee80211_drop_unencrypted(rx, fc))
@ -2562,8 +2562,28 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
cfg80211_rx_control_port(dev, skb, noencrypt); cfg80211_rx_control_port(dev, skb, noencrypt);
dev_kfree_skb(skb); dev_kfree_skb(skb);
} else { } else {
struct ethhdr *ehdr = (void *)skb_mac_header(skb);
memset(skb->cb, 0, sizeof(skb->cb)); memset(skb->cb, 0, sizeof(skb->cb));
/*
* 802.1X over 802.11 requires that the authenticator address
* be used for EAPOL frames. However, 802.1X allows the use of
* the PAE group address instead. If the interface is part of
* a bridge and we pass the frame with the PAE group address,
* then the bridge will forward it to the network (even if the
* client was not associated yet), which isn't supposed to
* happen.
* To avoid that, rewrite the destination address to our own
* address, so that the authenticator (e.g. hostapd) will see
* the frame, but bridge won't forward it anywhere else. Note
* that due to earlier filtering, the only other address can
* be the PAE group address.
*/
if (unlikely(skb->protocol == sdata->control_port_protocol &&
!ether_addr_equal(ehdr->h_dest, sdata->vif.addr)))
ether_addr_copy(ehdr->h_dest, sdata->vif.addr);
/* deliver to local stack */ /* deliver to local stack */
if (rx->list) if (rx->list)
list_add_tail(&skb->list, rx->list); list_add_tail(&skb->list, rx->list);
@ -2603,6 +2623,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if ((sdata->vif.type == NL80211_IFTYPE_AP || if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
ehdr->h_proto != rx->sdata->control_port_protocol &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
if (is_multicast_ether_addr(ehdr->h_dest) && if (is_multicast_ether_addr(ehdr->h_dest) &&
ieee80211_vif_get_num_mcast_if(sdata) != 0) { ieee80211_vif_get_num_mcast_if(sdata) != 0) {