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/wpa.c | |
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/wpa.c')
-rw-r--r-- | net/mac80211/wpa.c | 32 |
1 files changed, 19 insertions, 13 deletions
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)) |