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: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
committed by
John W. Linville
parent
523b02ea23
commit
aba83a0b30
@@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 @@
|
|||||||
#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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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))
|
||||||
|
Reference in New Issue
Block a user