mac80211: fix TKIP races, make API easier to use
[linux-2.6-microblaze.git] / net / mac80211 / tkip.c
index 757e4eb..de570b3 100644 (file)
@@ -101,6 +101,7 @@ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx,
                p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i;
        }
        ctx->state = TKIP_STATE_PHASE1_DONE;
+       ctx->p1k_iv32 = tsc_IV32;
 }
 
 static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
@@ -140,60 +141,72 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
 /* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets
  * of the IV. Returns pointer to the octet following IVs (i.e., beginning of
  * the packet payload). */
-u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16)
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key)
 {
-       pos = write_tkip_iv(pos, iv16);
+       lockdep_assert_held(&key->u.tkip.txlock);
+
+       pos = write_tkip_iv(pos, key->u.tkip.tx.iv16);
        *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;
        put_unaligned_le32(key->u.tkip.tx.iv32, pos);
        return pos + 4;
 }
 
-void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
-                       struct sk_buff *skb, enum ieee80211_tkip_key_type type,
-                       u8 *outkey)
+static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32)
+{
+       struct ieee80211_sub_if_data *sdata = key->sdata;
+       struct tkip_ctx *ctx = &key->u.tkip.tx;
+       const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+
+       lockdep_assert_held(&key->u.tkip.txlock);
+
+       /*
+        * Update the P1K when the IV32 is different from the value it
+        * had when we last computed it (or when not initialised yet).
+        * This might flip-flop back and forth if packets are processed
+        * out-of-order due to the different ACs, but then we have to
+        * just compute the P1K more often.
+        */
+       if (ctx->p1k_iv32 != iv32 || ctx->state == TKIP_STATE_NOT_INIT)
+               tkip_mixing_phase1(tk, ctx, sdata->vif.addr, iv32);
+}
+
+void ieee80211_get_tkip_p1k(struct ieee80211_key_conf *keyconf,
+                           struct sk_buff *skb, u16 *p1k)
 {
        struct ieee80211_key *key = (struct ieee80211_key *)
                        container_of(keyconf, struct ieee80211_key, conf);
+       struct tkip_ctx *ctx = &key->u.tkip.tx;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       u8 *data;
-       const u8 *tk;
-       struct tkip_ctx *ctx;
-       u16 iv16;
-       u32 iv32;
-
-       data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
-       iv16 = data[2] | (data[0] << 8);
-       iv32 = get_unaligned_le32(&data[4]);
-
-       tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
-       ctx = &key->u.tkip.tx;
-
-#ifdef CONFIG_MAC80211_TKIP_DEBUG
-       printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n",
-                       iv16, iv32);
-
-       if (iv32 != ctx->iv32) {
-               printk(KERN_DEBUG "skb: iv32 = 0x%08x key: iv32 = 0x%08x\n",
-                       iv32, ctx->iv32);
-               printk(KERN_DEBUG "Wrap around of iv16 in the middle of a "
-                       "fragmented packet\n");
-       }
-#endif
-
-       /* Update the p1k only when the iv16 in the packet wraps around, this
-        * might occur after the wrap around of iv16 in the key in case of
-        * fragmented packets. */
-       if (iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
-               tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32);
-
-       if (type == IEEE80211_TKIP_P1_KEY) {
-               memcpy(outkey, ctx->p1k, sizeof(u16) * 5);
-               return;
-       }
+       const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+       u32 iv32 = get_unaligned_le32(&data[4]);
+       unsigned long flags;
+
+       spin_lock_irqsave(&key->u.tkip.txlock, flags);
+       ieee80211_compute_tkip_p1k(key, iv32);
+       memcpy(p1k, ctx->p1k, sizeof(ctx->p1k));
+       spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
+}
+EXPORT_SYMBOL(ieee80211_get_tkip_p1k);
 
-       tkip_mixing_phase2(tk, ctx, iv16, outkey);
+void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
+                           struct sk_buff *skb, u8 *p2k)
+{
+       struct ieee80211_key *key = (struct ieee80211_key *)
+                       container_of(keyconf, struct ieee80211_key, conf);
+       const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+       struct tkip_ctx *ctx = &key->u.tkip.tx;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+       u32 iv32 = get_unaligned_le32(&data[4]);
+       u16 iv16 = data[2] | (data[0] << 8);
+       unsigned long flags;
+
+       spin_lock_irqsave(&key->u.tkip.txlock, flags);
+       ieee80211_compute_tkip_p1k(key, iv32);
+       tkip_mixing_phase2(tk, ctx, iv16, p2k);
+       spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
 }
-EXPORT_SYMBOL(ieee80211_get_tkip_key);
+EXPORT_SYMBOL(ieee80211_get_tkip_p2k);
 
 /*
  * Encrypt packet payload with TKIP using @key. @pos is a pointer to the
@@ -204,19 +217,15 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key);
  */
 int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
                                struct ieee80211_key *key,
-                               u8 *pos, size_t payload_len, u8 *ta)
+                               struct sk_buff *skb,
+                               u8 *payload, size_t payload_len)
 {
        u8 rc4key[16];
-       struct tkip_ctx *ctx = &key->u.tkip.tx;
-       const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
-
-       /* Calculate per-packet key */
-       if (ctx->iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
-               tkip_mixing_phase1(tk, ctx, ta, ctx->iv32);
 
-       tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
+       ieee80211_get_tkip_p2k(&key->conf, skb, rc4key);
 
-       return ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len);
+       return ieee80211_wep_encrypt_data(tfm, rc4key, 16,
+                                         payload, payload_len);
 }
 
 /* Decrypt packet payload with TKIP using @key. @pos is a pointer to the