summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/mediatek/mt76/mac80211.c
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2018-01-25 11:44:24 +0100
committerKalle Valo <kvalo@codeaurora.org>2018-01-26 11:20:52 +0200
commit30ce7f4456ae40e970d9e82fe63c5e55147af0c0 (patch)
tree06b61298712b3cc88b4fbcbaff9d72fc04576655 /drivers/net/wireless/mediatek/mt76/mac80211.c
parent3675302de8e946b9f8db44f55abbac1742f95706 (diff)
mt76: validate rx CCMP PN
Apparently hardware does not perform CCMP PN validation in hardware, so we need to take care of this in the driver. This is important for protecting against replay attacks. Since validation of fragmented frames is more complex, the CCMP header for those is preserved. To keep the counter in sync, the first fragment is verified by both mt76 and mac80211, and all other fragments only by mac80211. Signed-off-by: Felix Fietkau <nbd@nbd.name> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mac80211.c')
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 77f1be161009..5fcb2deb89a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,6 +384,27 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
}
EXPORT_SYMBOL_GPL(mt76_get_survey);
+void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ struct ieee80211_key_conf *key)
+{
+ struct ieee80211_key_seq seq;
+ int i;
+
+ wcid->rx_check_pn = false;
+
+ if (!key)
+ return;
+
+ if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+ wcid->rx_check_pn = true;
+
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
+ }
+}
+EXPORT_SYMBOL(mt76_wcid_key_setup);
+
static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -410,6 +431,45 @@ static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
return wcid_to_sta(mstat.wcid);
}
+static int
+mt76_check_ccmp_pn(struct sk_buff *skb)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+ struct mt76_wcid *wcid = status->wcid;
+ struct ieee80211_hdr *hdr;
+ int ret;
+
+ if (!(status->flag & RX_FLAG_DECRYPTED))
+ return 0;
+
+ if (!wcid || !wcid->rx_check_pn)
+ return 0;
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ /*
+ * Validate the first fragment both here and in mac80211
+ * All further fragments will be validated by mac80211 only.
+ */
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_frag(hdr) &&
+ !ieee80211_is_first_frag(hdr->frame_control))
+ return 0;
+ }
+
+ BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
+ ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
+ sizeof(status->iv));
+ if (ret <= 0)
+ return -EINVAL; /* replay */
+
+ memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
+
+ if (status->flag & RX_FLAG_IV_STRIPPED)
+ status->flag |= RX_FLAG_PN_VALIDATED;
+
+ return 0;
+}
+
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
int queue)
{
@@ -421,6 +481,11 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
napi = &dev->napi[queue];
while ((skb = __skb_dequeue(frames)) != NULL) {
+ if (mt76_check_ccmp_pn(skb)) {
+ dev_kfree_skb(skb);
+ continue;
+ }
+
sta = mt76_rx_convert(skb);
ieee80211_rx_napi(dev->hw, sta, skb, napi);
}