diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-07-06 15:59:39 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-08 11:11:19 -0400 |
commit | aba83a0b301c32dbb91c017f33307611e1a1d384 (patch) | |
tree | 9f8478ddf2160c9b4c50666e037e6239ea52e274 /net/mac80211 | |
parent | 523b02ea23b175dd3e46e3daf1bc9354376640a3 (diff) |
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>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 14 | ||||
-rw-r--r-- | net/mac80211/debugfs_key.c | 6 | ||||
-rw-r--r-- | net/mac80211/key.h | 5 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 32 |
4 files changed, 32 insertions, 25 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 295ab747663f..3000b4c3b525 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -209,6 +209,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
209 | u8 seq[6] = {0}; | 209 | u8 seq[6] = {0}; |
210 | struct key_params params; | 210 | struct key_params params; |
211 | struct ieee80211_key *key = NULL; | 211 | struct ieee80211_key *key = NULL; |
212 | u64 pn64; | ||
212 | u32 iv32; | 213 | u32 iv32; |
213 | u16 iv16; | 214 | u16 iv16; |
214 | int err = -ENOENT; | 215 | int err = -ENOENT; |
@@ -256,12 +257,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
256 | params.seq_len = 6; | 257 | params.seq_len = 6; |
257 | break; | 258 | break; |
258 | case WLAN_CIPHER_SUITE_CCMP: | 259 | case WLAN_CIPHER_SUITE_CCMP: |
259 | seq[0] = key->u.ccmp.tx_pn[5]; | 260 | pn64 = atomic64_read(&key->u.ccmp.tx_pn); |
260 | seq[1] = key->u.ccmp.tx_pn[4]; | 261 | seq[0] = pn64; |
261 | seq[2] = key->u.ccmp.tx_pn[3]; | 262 | seq[1] = pn64 >> 8; |
262 | seq[3] = key->u.ccmp.tx_pn[2]; | 263 | seq[2] = pn64 >> 16; |
263 | seq[4] = key->u.ccmp.tx_pn[1]; | 264 | seq[3] = pn64 >> 24; |
264 | seq[5] = key->u.ccmp.tx_pn[0]; | 265 | seq[4] = pn64 >> 32; |
266 | seq[5] = pn64 >> 40; | ||
265 | params.seq = seq; | 267 | params.seq = seq; |
266 | params.seq_len = 6; | 268 | params.seq_len = 6; |
267 | break; | 269 | break; |
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 33c58b85c911..4433760db4c7 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c | |||
@@ -79,6 +79,7 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, | |||
79 | size_t count, loff_t *ppos) | 79 | size_t count, loff_t *ppos) |
80 | { | 80 | { |
81 | const u8 *tpn; | 81 | const u8 *tpn; |
82 | u64 pn; | ||
82 | char buf[20]; | 83 | char buf[20]; |
83 | int len; | 84 | int len; |
84 | struct ieee80211_key *key = file->private_data; | 85 | 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 | key->u.tkip.tx.iv16); | 95 | key->u.tkip.tx.iv16); |
95 | break; | 96 | break; |
96 | case WLAN_CIPHER_SUITE_CCMP: | 97 | case WLAN_CIPHER_SUITE_CCMP: |
97 | tpn = key->u.ccmp.tx_pn; | 98 | pn = atomic64_read(&key->u.ccmp.tx_pn); |
98 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", | 99 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", |
99 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); | 100 | (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), |
101 | (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); | ||
100 | break; | 102 | break; |
101 | case WLAN_CIPHER_SUITE_AES_CMAC: | 103 | case WLAN_CIPHER_SUITE_AES_CMAC: |
102 | tpn = key->u.aes_cmac.tx_pn; | 104 | tpn = key->u.aes_cmac.tx_pn; |
diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 1493c3e56b9f..05ce4c0203fc 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h | |||
@@ -82,7 +82,7 @@ struct ieee80211_key { | |||
82 | struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; | 82 | struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; |
83 | } tkip; | 83 | } tkip; |
84 | struct { | 84 | struct { |
85 | u8 tx_pn[6]; | 85 | atomic64_t tx_pn; |
86 | /* | 86 | /* |
87 | * Last received packet number. The first | 87 | * Last received packet number. The first |
88 | * NUM_RX_DATA_QUEUES counters are used with Data | 88 | * NUM_RX_DATA_QUEUES counters are used with Data |
@@ -92,12 +92,9 @@ struct ieee80211_key { | |||
92 | u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; | 92 | u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; |
93 | struct crypto_cipher *tfm; | 93 | struct crypto_cipher *tfm; |
94 | u32 replays; /* dot11RSNAStatsCCMPReplays */ | 94 | u32 replays; /* dot11RSNAStatsCCMPReplays */ |
95 | /* scratch buffers for virt_to_page() (crypto API) */ | ||
96 | #ifndef AES_BLOCK_LEN | 95 | #ifndef AES_BLOCK_LEN |
97 | #define AES_BLOCK_LEN 16 | 96 | #define AES_BLOCK_LEN 16 |
98 | #endif | 97 | #endif |
99 | u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; | ||
100 | u8 rx_crypto_buf[6 * AES_BLOCK_LEN]; | ||
101 | } ccmp; | 98 | } ccmp; |
102 | struct { | 99 | struct { |
103 | u8 tx_pn[6]; | 100 | u8 tx_pn[6]; |
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 4ded2ae48a5f..7691e4edc74a 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/gfp.h> | 15 | #include <linux/gfp.h> |
16 | #include <asm/unaligned.h> | 16 | #include <asm/unaligned.h> |
17 | #include <net/mac80211.h> | 17 | #include <net/mac80211.h> |
18 | #include <crypto/aes.h> | ||
18 | 19 | ||
19 | #include "ieee80211_i.h" | 20 | #include "ieee80211_i.h" |
20 | #include "michael.h" | 21 | #include "michael.h" |
@@ -290,6 +291,8 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | |||
290 | unsigned int hdrlen; | 291 | unsigned int hdrlen; |
291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 292 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
292 | 293 | ||
294 | memset(scratch, 0, 6 * AES_BLOCK_LEN); | ||
295 | |||
293 | b_0 = scratch + 3 * AES_BLOCK_LEN; | 296 | b_0 = scratch + 3 * AES_BLOCK_LEN; |
294 | aad = scratch + 4 * AES_BLOCK_LEN; | 297 | aad = scratch + 4 * AES_BLOCK_LEN; |
295 | 298 | ||
@@ -380,8 +383,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | |||
380 | struct ieee80211_key *key = tx->key; | 383 | struct ieee80211_key *key = tx->key; |
381 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 384 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
382 | int hdrlen, len, tail; | 385 | int hdrlen, len, tail; |
383 | u8 *pos, *pn; | 386 | u8 *pos; |
384 | int i; | 387 | u8 pn[6]; |
388 | u64 pn64; | ||
389 | u8 scratch[6 * AES_BLOCK_LEN]; | ||
385 | 390 | ||
386 | if (info->control.hw_key && | 391 | if (info->control.hw_key && |
387 | !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { | 392 | !(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 | hdr = (struct ieee80211_hdr *) pos; | 414 | hdr = (struct ieee80211_hdr *) pos; |
410 | pos += hdrlen; | 415 | pos += hdrlen; |
411 | 416 | ||
412 | /* PN = PN + 1 */ | 417 | pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn); |
413 | pn = key->u.ccmp.tx_pn; | ||
414 | 418 | ||
415 | for (i = CCMP_PN_LEN - 1; i >= 0; i--) { | 419 | pn[5] = pn64; |
416 | pn[i]++; | 420 | pn[4] = pn64 >> 8; |
417 | if (pn[i]) | 421 | pn[3] = pn64 >> 16; |
418 | break; | 422 | pn[2] = pn64 >> 24; |
419 | } | 423 | pn[1] = pn64 >> 32; |
424 | pn[0] = pn64 >> 40; | ||
420 | 425 | ||
421 | ccmp_pn2hdr(pos, pn, key->conf.keyidx); | 426 | ccmp_pn2hdr(pos, pn, key->conf.keyidx); |
422 | 427 | ||
@@ -425,8 +430,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | |||
425 | return 0; | 430 | return 0; |
426 | 431 | ||
427 | pos += CCMP_HDR_LEN; | 432 | pos += CCMP_HDR_LEN; |
428 | ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0); | 433 | ccmp_special_blocks(skb, pn, scratch, 0); |
429 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len, | 434 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, |
430 | pos, skb_put(skb, CCMP_MIC_LEN)); | 435 | pos, skb_put(skb, CCMP_MIC_LEN)); |
431 | 436 | ||
432 | return 0; | 437 | return 0; |
@@ -482,11 +487,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) | |||
482 | } | 487 | } |
483 | 488 | ||
484 | if (!(status->flag & RX_FLAG_DECRYPTED)) { | 489 | if (!(status->flag & RX_FLAG_DECRYPTED)) { |
490 | u8 scratch[6 * AES_BLOCK_LEN]; | ||
485 | /* hardware didn't decrypt/verify MIC */ | 491 | /* hardware didn't decrypt/verify MIC */ |
486 | ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1); | 492 | ccmp_special_blocks(skb, pn, scratch, 1); |
487 | 493 | ||
488 | if (ieee80211_aes_ccm_decrypt( | 494 | if (ieee80211_aes_ccm_decrypt( |
489 | key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf, | 495 | key->u.ccmp.tfm, scratch, |
490 | skb->data + hdrlen + CCMP_HDR_LEN, data_len, | 496 | skb->data + hdrlen + CCMP_HDR_LEN, data_len, |
491 | skb->data + skb->len - CCMP_MIC_LEN, | 497 | skb->data + skb->len - CCMP_MIC_LEN, |
492 | skb->data + hdrlen + CCMP_HDR_LEN)) | 498 | skb->data + hdrlen + CCMP_HDR_LEN)) |