mac80211: Encrypt "Group addressed privacy" action frames
authorMasashi Honma <masashi.honma@gmail.com>
Wed, 22 Jun 2016 10:55:20 +0000 (19:55 +0900)
committerJohannes Berg <johannes@sipsolutions.net>
Thu, 30 Jun 2016 10:06:20 +0000 (12:06 +0200)
Previously, the action frames to group address was not encrypted. But
[1] "Table 8-38 Category values" indicates "Mesh" and "Multihop" category
action frames should be encrypted (Group addressed privacy == yes). And the
encyption key should be MGTK ([1] 10.13 Group addressed robust management frame
procedures). So this patch modifies the code to make it suitable for spec.

[1] IEEE Std 802.11-2012

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
include/linux/ieee80211.h
net/mac80211/rx.c
net/mac80211/tx.c

index 1daebb3..a80516f 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/types.h>
 #include <linux/if_ether.h>
+#include <linux/etherdevice.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
@@ -2486,6 +2487,35 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
        return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
 }
 
+/**
+ * _ieee80211_is_group_privacy_action - check if frame is a group addressed
+ * privacy action frame
+ * @hdr: the frame
+ */
+static inline bool _ieee80211_is_group_privacy_action(struct ieee80211_hdr *hdr)
+{
+       struct ieee80211_mgmt *mgmt = (void *)hdr;
+
+       if (!ieee80211_is_action(hdr->frame_control) ||
+           !is_multicast_ether_addr(hdr->addr1))
+               return false;
+
+       return mgmt->u.action.category == WLAN_CATEGORY_MESH_ACTION ||
+              mgmt->u.action.category == WLAN_CATEGORY_MULTIHOP_ACTION;
+}
+
+/**
+ * ieee80211_is_group_privacy_action - check if frame is a group addressed
+ * privacy action frame
+ * @skb: the skb containing the frame, length will be checked
+ */
+static inline bool ieee80211_is_group_privacy_action(struct sk_buff *skb)
+{
+       if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+               return false;
+       return _ieee80211_is_group_privacy_action((void *)skb->data);
+}
+
 /**
  * ieee80211_tu_to_usec - convert time units (TU) to microseconds
  * @tu: the TUs
index 9a1eb70..2e8a902 100644 (file)
@@ -1624,8 +1624,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                if (mmie_keyidx < NUM_DEFAULT_KEYS ||
                    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
                        return RX_DROP_MONITOR; /* unexpected BIP keyidx */
-               if (rx->sta)
+               if (rx->sta) {
+                       if (ieee80211_is_group_privacy_action(skb) &&
+                           test_sta_flag(rx->sta, WLAN_STA_MFP))
+                               return RX_DROP_MONITOR;
+
                        rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+               }
                if (!rx->key)
                        rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
        } else if (!ieee80211_has_protected(fc)) {
index 44ec605..fa8d38e 100644 (file)
@@ -593,6 +593,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
        else if (tx->sta &&
                 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))
                tx->key = key;
+       else if (ieee80211_is_group_privacy_action(tx->skb) &&
+               (key = rcu_dereference(tx->sdata->default_multicast_key)))
+               tx->key = key;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
                 is_multicast_ether_addr(hdr->addr1) &&
                 ieee80211_is_robust_mgmt_frame(tx->skb) &&
@@ -625,7 +628,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                case WLAN_CIPHER_SUITE_GCMP_256:
                        if (!ieee80211_is_data_present(hdr->frame_control) &&
                            !ieee80211_use_mfp(hdr->frame_control, tx->sta,
-                                              tx->skb))
+                                              tx->skb) &&
+                           !ieee80211_is_group_privacy_action(tx->skb))
                                tx->key = NULL;
                        else
                                skip_hw = (tx->key->conf.flags &