Skip to content

Commit

Permalink
mac80211: fix CCMP races
Browse files Browse the repository at this point in the history
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Jul 8, 2011
1 parent 523b02e commit aba83a0
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 25 deletions.
14 changes: 8 additions & 6 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
u8 seq[6] = {0};
struct key_params params;
struct ieee80211_key *key = NULL;
u64 pn64;
u32 iv32;
u16 iv16;
int err = -ENOENT;
Expand Down Expand Up @@ -256,12 +257,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
params.seq_len = 6;
break;
case WLAN_CIPHER_SUITE_CCMP:
seq[0] = key->u.ccmp.tx_pn[5];
seq[1] = key->u.ccmp.tx_pn[4];
seq[2] = key->u.ccmp.tx_pn[3];
seq[3] = key->u.ccmp.tx_pn[2];
seq[4] = key->u.ccmp.tx_pn[1];
seq[5] = key->u.ccmp.tx_pn[0];
pn64 = atomic64_read(&key->u.ccmp.tx_pn);
seq[0] = pn64;
seq[1] = pn64 >> 8;
seq[2] = pn64 >> 16;
seq[3] = pn64 >> 24;
seq[4] = pn64 >> 32;
seq[5] = pn64 >> 40;
params.seq = seq;
params.seq_len = 6;
break;
Expand Down
6 changes: 4 additions & 2 deletions net/mac80211/debugfs_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const u8 *tpn;
u64 pn;
char buf[20];
int len;
struct ieee80211_key *key = file->private_data;
Expand All @@ -94,9 +95,10 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
key->u.tkip.tx.iv16);
break;
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",
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;
case WLAN_CIPHER_SUITE_AES_CMAC:
tpn = key->u.aes_cmac.tx_pn;
Expand Down
5 changes: 1 addition & 4 deletions net/mac80211/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct ieee80211_key {
struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
} tkip;
struct {
u8 tx_pn[6];
atomic64_t tx_pn;
/*
* Last received packet number. The first
* NUM_RX_DATA_QUEUES counters are used with Data
Expand All @@ -92,12 +92,9 @@ struct ieee80211_key {
u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
/* scratch buffers for virt_to_page() (crypto API) */
#ifndef AES_BLOCK_LEN
#define AES_BLOCK_LEN 16
#endif
u8 tx_crypto_buf[6 * AES_BLOCK_LEN];
u8 rx_crypto_buf[6 * AES_BLOCK_LEN];
} ccmp;
struct {
u8 tx_pn[6];
Expand Down
32 changes: 19 additions & 13 deletions net/mac80211/wpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/gfp.h>
#include <asm/unaligned.h>
#include <net/mac80211.h>
#include <crypto/aes.h>

#include "ieee80211_i.h"
#include "michael.h"
Expand Down Expand Up @@ -290,6 +291,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
unsigned int hdrlen;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;

memset(scratch, 0, 6 * AES_BLOCK_LEN);

b_0 = scratch + 3 * AES_BLOCK_LEN;
aad = scratch + 4 * AES_BLOCK_LEN;

Expand Down Expand Up @@ -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_tx_info *info = IEEE80211_SKB_CB(skb);
int hdrlen, len, tail;
u8 *pos, *pn;
int i;
u8 *pos;
u8 pn[6];
u64 pn64;
u8 scratch[6 * AES_BLOCK_LEN];

if (info->control.hw_key &&
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
Expand Down Expand Up @@ -409,14 +414,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
hdr = (struct ieee80211_hdr *) pos;
pos += hdrlen;

/* PN = PN + 1 */
pn = key->u.ccmp.tx_pn;
pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn);

for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
pn[i]++;
if (pn[i])
break;
}
pn[5] = pn64;
pn[4] = pn64 >> 8;
pn[3] = pn64 >> 16;
pn[2] = pn64 >> 24;
pn[1] = pn64 >> 32;
pn[0] = pn64 >> 40;

ccmp_pn2hdr(pos, pn, key->conf.keyidx);

Expand All @@ -425,8 +430,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;

pos += CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len,
ccmp_special_blocks(skb, pn, scratch, 0);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
pos, skb_put(skb, CCMP_MIC_LEN));

return 0;
Expand Down Expand Up @@ -482,11 +487,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
}

if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 scratch[6 * AES_BLOCK_LEN];
/* 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(
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 + skb->len - CCMP_MIC_LEN,
skb->data + hdrlen + CCMP_HDR_LEN))
Expand Down

0 comments on commit aba83a0

Please sign in to comment.