From patchwork Wed May 20 20:55:30 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Wetzel X-Patchwork-Id: 6449291 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id D48F19F318 for ; Wed, 20 May 2015 20:53:53 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B8B8F203E5 for ; Wed, 20 May 2015 20:53:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4BA70202EC for ; Wed, 20 May 2015 20:53:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753469AbbETUxt (ORCPT ); Wed, 20 May 2015 16:53:49 -0400 Received: from mo4-p05-ob.smtp.rzone.de ([81.169.146.181]:62836 "EHLO mo4-p05-ob.smtp.rzone.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753246AbbETUxr (ORCPT ); Wed, 20 May 2015 16:53:47 -0400 X-RZG-AUTH: :O2kGeEG7b/pS1Fq8X2W2hRftj4tVN9ef41igLGMe7s7FdEeznrRAQWySqbgW8uD7gqKqxrV8zjU= X-RZG-CLASS-ID: mo05 Received: from mail.wetzel-home.de (p4FC26A60.dip0.t-ipconnect.de [79.194.106.96]) by smtp.strato.de (RZmta 37.6 DYNA|AUTH) with ESMTPSA id U0615fr4KKriNHs (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (curve sect571r1 with 571 ECDH bits, eq. 15360 bits RSA)) (Client did not present a certificate); Wed, 20 May 2015 22:53:44 +0200 (CEST) Received: from localhost (gandalf.mordor [127.0.0.1]) by mail.wetzel-home.de (Postfix) with ESMTP id 0284E95AA; Wed, 20 May 2015 22:53:44 +0200 (CEST) X-Virus-Scanned: amavisd-new at wetzel-home.de Received: from mail.wetzel-home.de ([127.0.0.1]) by localhost (mail.wetzel-home.de [127.0.0.1]) (amavisd-new, port 10024) with LMTP id mtJAP_TBtzTJ; Wed, 20 May 2015 22:53:43 +0200 (CEST) Received: from [10.16.2.202] (unknown [10.16.2.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wetzel-home.de (Postfix) with ESMTPSA id 66C2995A9; Wed, 20 May 2015 22:53:43 +0200 (CEST) Message-ID: <555CF4C2.7040002@web.de> Date: Wed, 20 May 2015 22:55:30 +0200 From: Alexander Wetzel User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.6.0 MIME-Version: 1.0 To: Johannes Berg CC: "Peer, Ilan" , Emmanuel Grumbach , Jouni Malinen , linux-wireless Subject: Re: mac80211 drops packet with old IV after rekeying - workaround patch for CCMP References: <1431674716.2426.2.camel@sipsolutions.net> <1431714949.2117.0.camel@sipsolutions.net> <1431806229.2120.6.camel@sipsolutions.net> <20150517160513.GA13175@w1.fi> <1431890756.2129.13.camel@sipsolutions.net> <1431893157.2129.18.camel@sipsolutions.net> (sfid-20150517_221304_420222_D8022C07) <1431894140.2129.20.camel@sipsolutions.net> <1431961331.10489.1.camel@sipsolutions.net> <555A41EA.4090905@web.de> <1431986157.10489.12.camel@sipsolutions.net> In-Reply-To: <1431986157.10489.12.camel@sipsolutions.net> Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_HI,T_RP_MATCHES_RCVD,T_TVD_MIME_EPI,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP I've verified that turning off hardware encryption on the AP and the STA is indeed preventing the issue. As soon as one of them is using the hardware encryption I can trigger the problem. (In my setup it seems to be mostly caused by the AP, since I needed sometimes as much as three rekeys to get the freeze when the AP was using Software and the STA hardware encryption.) So confident that we finally found the root of the evil I tried to write some code catching the races, see the attachment. It's probably not the best fix, but the only one I could think of and deploy myself with the knowledge I gathered here and the last days. It's only for CCMP for now and I've not checked on assumptions what some parts of the code are for. This is just "works for me". (It survived now 30 rekeys under my test load, when previously nearly every rekey did freeze it.) I think there is no need any longer to generate captures, but if you want them anyway I can of course still work on that. The decision to check if the pn is <= 10 as indicator to switch over to the "correct" pn counter is at best questionable. (Using 1 does not work. I have problems with the inital key here and the code was not switching over to the correct rx_pn counter and I expect that we also may lose some frames here.) I'm not even sure that it's safe to assume that pn is always starting at one, since wpa_supplicant is dumping some sequence number on rekey I would have assumed to be a more or less random start value. But in the debug prints the real value is always starting at one. So I'm using that for now. I'll now try that in my environment, keeping the insane low rekey interval at two minutes for at least some weeks. What was really surprising me here is, that this is such a generic issue and I'm finding that in my home environment. For my understanding that should break many (all?) EAP Wlan's. (I'm using EAP-TLS and that did make the WLAN basically unusable and any sane person would have switched back to PSK...) For completeness, here is the original openwrt bug report I opened: https://dev.openwrt.org/ticket/18966 Alexander diff -ur linux-4.0.4-gentoo/net/mac80211/key.h linux-4.0.4-gentoo_patched/net/mac80211/key.h --- linux-4.0.4-gentoo/net/mac80211/key.h 2015-04-13 00:12:50.000000000 +0200 +++ linux-4.0.4-gentoo_patched/net/mac80211/key.h 2015-05-20 13:16:06.370256697 +0200 @@ -84,12 +84,14 @@ * Management frames. */ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; + u8 rx_pn_old[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; struct crypto_aead *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; struct { atomic64_t tx_pn; u8 rx_pn[IEEE80211_CMAC_PN_LEN]; + u8 rx_pn_old[IEEE80211_CMAC_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ @@ -97,6 +99,7 @@ struct { atomic64_t tx_pn; u8 rx_pn[IEEE80211_GMAC_PN_LEN]; + u8 rx_pn_old[IEEE80211_GMAC_PN_LEN]; struct crypto_aead *tfm; u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ @@ -109,12 +112,14 @@ * Management frames. */ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN]; + u8 rx_pn_old[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN]; struct crypto_aead *tfm; u32 replays; /* dot11RSNAStatsGCMPReplays */ } gcmp; struct { /* generic cipher scheme */ u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN]; + u8 rx_pn_old[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN]; } gen; } u; diff -ur linux-4.0.4-gentoo/net/mac80211/wpa.c linux-4.0.4-gentoo_patched/net/mac80211/wpa.c --- linux-4.0.4-gentoo/net/mac80211/wpa.c 2015-04-13 00:12:50.000000000 +0200 +++ linux-4.0.4-gentoo_patched/net/mac80211/wpa.c 2015-05-20 21:43:25.529721066 +0200 @@ -495,6 +495,7 @@ struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 pn[IEEE80211_CCMP_PN_LEN]; + static u8 zero[IEEE80211_CCMP_PN_LEN]; int data_len; int queue; @@ -525,6 +526,31 @@ return RX_DROP_UNUSABLE; } + /* HACK: try to work around race when replacing PSK with enabled hardware offload on AP or STA */ + if (unlikely(memcmp(key->u.ccmp.rx_pn[queue], zero, IEEE80211_CCMP_PN_LEN) == 0 )) { + + printk ("DDD - rx_pn is zero, virgin key detected! pl=%x\n", pn[IEEE80211_CCMP_PN_LEN-1]); + print_hex_dump_debug("cnt: ", DUMP_PREFIX_OFFSET, IEEE80211_CCMP_PN_LEN, 1, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN, false); + print_hex_dump_debug("pn : ", DUMP_PREFIX_OFFSET, IEEE80211_CCMP_PN_LEN, 1, pn, IEEE80211_CCMP_PN_LEN, false); + + if ((memcmp(pn, zero, IEEE80211_CCMP_PN_LEN -1) == 0) && pn[IEEE80211_CCMP_PN_LEN-1] <= 10) { + /* pn is <=10 , we can start using the new counter */ + printk ("DDD - set new pn\n"); + memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); + } else if (memcmp(pn, key->u.ccmp.rx_pn_old[queue], IEEE80211_CCMP_PN_LEN) <= 0) { + printk ("DDD - attack!\n"); + /* reply attack during rekey operation, guess it will really hard to do that... */ + key->u.ccmp.replays++; /* we count it as an reply anyway...*/ + return RX_DROP_UNUSABLE; + } else { + printk ("DDD - prevent poisening \n"); + memcpy(key->u.ccmp.rx_pn_old[queue], pn, IEEE80211_CCMP_PN_LEN); + } + } else { + + memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); + } + if (!(status->flag & RX_FLAG_DECRYPTED)) { u8 aad[2 * AES_BLOCK_SIZE]; u8 b_0[AES_BLOCK_SIZE]; @@ -539,8 +565,6 @@ return RX_DROP_UNUSABLE; } - memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); - /* Remove CCMP header and MIC */ if (pskb_trim(skb, skb->len - mic_len)) return RX_DROP_UNUSABLE;