Commit aba83a0b authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: fix CCMP races

Since we can process multiple packets at the
same time for different ACs, but the PN is
allocated from a single counter, we need to
use an atomic value there. Use atomic64_t to
make this cheaper on 64-bit platforms, other
platforms will support this through software
emulation, see lib/atomic64.c.

We also need to use an on-stack scratch buf
so that multiple packets won't corrupt each
others scratch buffers.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 523b02ea
...@@ -209,6 +209,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -209,6 +209,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
u8 seq[6] = {0}; u8 seq[6] = {0};
struct key_params params; struct key_params params;
struct ieee80211_key *key = NULL; struct ieee80211_key *key = NULL;
u64 pn64;
u32 iv32; u32 iv32;
u16 iv16; u16 iv16;
int err = -ENOENT; int err = -ENOENT;
...@@ -256,12 +257,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -256,12 +257,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
params.seq_len = 6; params.seq_len = 6;
break; break;
case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP:
seq[0] = key->u.ccmp.tx_pn[5]; pn64 = atomic64_read(&key->u.ccmp.tx_pn);
seq[1] = key->u.ccmp.tx_pn[4]; seq[0] = pn64;
seq[2] = key->u.ccmp.tx_pn[3]; seq[1] = pn64 >> 8;
seq[3] = key->u.ccmp.tx_pn[2]; seq[2] = pn64 >> 16;
seq[4] = key->u.ccmp.tx_pn[1]; seq[3] = pn64 >> 24;
seq[5] = key->u.ccmp.tx_pn[0]; seq[4] = pn64 >> 32;
seq[5] = pn64 >> 40;
params.seq = seq; params.seq = seq;
params.seq_len = 6; params.seq_len = 6;
break; break;
......
...@@ -79,6 +79,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, ...@@ -79,6 +79,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
const u8 *tpn; const u8 *tpn;
u64 pn;
char buf[20]; char buf[20];
int len; int len;
struct ieee80211_key *key = file->private_data; struct ieee80211_key *key = file->private_data;
...@@ -94,9 +95,10 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, ...@@ -94,9 +95,10 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
key->u.tkip.tx.iv16); key->u.tkip.tx.iv16);
break; break;
case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP:
tpn = key->u.ccmp.tx_pn; pn = atomic64_read(&key->u.ccmp.tx_pn);
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
break; break;
case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_AES_CMAC:
tpn = key->u.aes_cmac.tx_pn; tpn = key->u.aes_cmac.tx_pn;
......
...@@ -82,7 +82,7 @@ struct ieee80211_key { ...@@ -82,7 +82,7 @@ struct ieee80211_key {
struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
} tkip; } tkip;
struct { struct {
u8 tx_pn[6]; atomic64_t tx_pn;
/* /*
* Last received packet number. The first * Last received packet number. The first
* NUM_RX_DATA_QUEUES counters are used with Data * NUM_RX_DATA_QUEUES counters are used with Data
...@@ -92,12 +92,9 @@ struct ieee80211_key { ...@@ -92,12 +92,9 @@ struct ieee80211_key {
u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6];
struct crypto_cipher *tfm; struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */ u32 replays; /* dot11RSNAStatsCCMPReplays */
/* scratch buffers for virt_to_page() (crypto API) */
#ifndef AES_BLOCK_LEN #ifndef AES_BLOCK_LEN
#define AES_BLOCK_LEN 16 #define AES_BLOCK_LEN 16
#endif #endif
u8 tx_crypto_buf[6 * AES_BLOCK_LEN];
u8 rx_crypto_buf[6 * AES_BLOCK_LEN];
} ccmp; } ccmp;
struct { struct {
u8 tx_pn[6]; u8 tx_pn[6];
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <crypto/aes.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "michael.h" #include "michael.h"
...@@ -290,6 +291,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, ...@@ -290,6 +291,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
unsigned int hdrlen; unsigned int hdrlen;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
memset(scratch, 0, 6 * AES_BLOCK_LEN);
b_0 = scratch + 3 * AES_BLOCK_LEN; b_0 = scratch + 3 * AES_BLOCK_LEN;
aad = scratch + 4 * AES_BLOCK_LEN; aad = scratch + 4 * AES_BLOCK_LEN;
...@@ -380,8 +383,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ...@@ -380,8 +383,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
struct ieee80211_key *key = tx->key; struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int hdrlen, len, tail; int hdrlen, len, tail;
u8 *pos, *pn; u8 *pos;
int i; u8 pn[6];
u64 pn64;
u8 scratch[6 * AES_BLOCK_LEN];
if (info->control.hw_key && if (info->control.hw_key &&
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
...@@ -409,14 +414,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ...@@ -409,14 +414,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
hdr = (struct ieee80211_hdr *) pos; hdr = (struct ieee80211_hdr *) pos;
pos += hdrlen; pos += hdrlen;
/* PN = PN + 1 */ pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn);
pn = key->u.ccmp.tx_pn;
for (i = CCMP_PN_LEN - 1; i >= 0; i--) { pn[5] = pn64;
pn[i]++; pn[4] = pn64 >> 8;
if (pn[i]) pn[3] = pn64 >> 16;
break; pn[2] = pn64 >> 24;
} pn[1] = pn64 >> 32;
pn[0] = pn64 >> 40;
ccmp_pn2hdr(pos, pn, key->conf.keyidx); ccmp_pn2hdr(pos, pn, key->conf.keyidx);
...@@ -425,8 +430,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ...@@ -425,8 +430,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0; return 0;
pos += CCMP_HDR_LEN; pos += CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0); ccmp_special_blocks(skb, pn, scratch, 0);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len, ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
pos, skb_put(skb, CCMP_MIC_LEN)); pos, skb_put(skb, CCMP_MIC_LEN));
return 0; return 0;
...@@ -482,11 +487,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) ...@@ -482,11 +487,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
} }
if (!(status->flag & RX_FLAG_DECRYPTED)) { if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 scratch[6 * AES_BLOCK_LEN];
/* hardware didn't decrypt/verify MIC */ /* hardware didn't decrypt/verify MIC */
ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1); ccmp_special_blocks(skb, pn, scratch, 1);
if (ieee80211_aes_ccm_decrypt( if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf, key->u.ccmp.tfm, scratch,
skb->data + hdrlen + CCMP_HDR_LEN, data_len, skb->data + hdrlen + CCMP_HDR_LEN, data_len,
skb->data + skb->len - CCMP_MIC_LEN, skb->data + skb->len - CCMP_MIC_LEN,
skb->data + hdrlen + CCMP_HDR_LEN)) skb->data + hdrlen + CCMP_HDR_LEN))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment