diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 54 |
1 files changed, 50 insertions, 4 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index a98fc2b5e0dc..938049395f90 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | 6 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
7 | * Copyright 2015 Intel Deutschland GmbH | 7 | * Copyright 2015-2017 Intel Deutschland GmbH |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/export.h> | 20 | #include <linux/export.h> |
21 | #include <net/mac80211.h> | 21 | #include <net/mac80211.h> |
22 | #include <crypto/algapi.h> | ||
22 | #include <asm/unaligned.h> | 23 | #include <asm/unaligned.h> |
23 | #include "ieee80211_i.h" | 24 | #include "ieee80211_i.h" |
24 | #include "driver-ops.h" | 25 | #include "driver-ops.h" |
@@ -609,6 +610,39 @@ void ieee80211_key_free_unused(struct ieee80211_key *key) | |||
609 | ieee80211_key_free_common(key); | 610 | ieee80211_key_free_common(key); |
610 | } | 611 | } |
611 | 612 | ||
613 | static bool ieee80211_key_identical(struct ieee80211_sub_if_data *sdata, | ||
614 | struct ieee80211_key *old, | ||
615 | struct ieee80211_key *new) | ||
616 | { | ||
617 | u8 tkip_old[WLAN_KEY_LEN_TKIP], tkip_new[WLAN_KEY_LEN_TKIP]; | ||
618 | u8 *tk_old, *tk_new; | ||
619 | |||
620 | if (!old || new->conf.keylen != old->conf.keylen) | ||
621 | return false; | ||
622 | |||
623 | tk_old = old->conf.key; | ||
624 | tk_new = new->conf.key; | ||
625 | |||
626 | /* | ||
627 | * In station mode, don't compare the TX MIC key, as it's never used | ||
628 | * and offloaded rekeying may not care to send it to the host. This | ||
629 | * is the case in iwlwifi, for example. | ||
630 | */ | ||
631 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | ||
632 | new->conf.cipher == WLAN_CIPHER_SUITE_TKIP && | ||
633 | new->conf.keylen == WLAN_KEY_LEN_TKIP && | ||
634 | !(new->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) { | ||
635 | memcpy(tkip_old, tk_old, WLAN_KEY_LEN_TKIP); | ||
636 | memcpy(tkip_new, tk_new, WLAN_KEY_LEN_TKIP); | ||
637 | memset(tkip_old + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, 0, 8); | ||
638 | memset(tkip_new + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, 0, 8); | ||
639 | tk_old = tkip_old; | ||
640 | tk_new = tkip_new; | ||
641 | } | ||
642 | |||
643 | return !crypto_memneq(tk_old, tk_new, new->conf.keylen); | ||
644 | } | ||
645 | |||
612 | int ieee80211_key_link(struct ieee80211_key *key, | 646 | int ieee80211_key_link(struct ieee80211_key *key, |
613 | struct ieee80211_sub_if_data *sdata, | 647 | struct ieee80211_sub_if_data *sdata, |
614 | struct sta_info *sta) | 648 | struct sta_info *sta) |
@@ -620,9 +654,6 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
620 | 654 | ||
621 | pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; | 655 | pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; |
622 | idx = key->conf.keyidx; | 656 | idx = key->conf.keyidx; |
623 | key->local = sdata->local; | ||
624 | key->sdata = sdata; | ||
625 | key->sta = sta; | ||
626 | 657 | ||
627 | mutex_lock(&sdata->local->key_mtx); | 658 | mutex_lock(&sdata->local->key_mtx); |
628 | 659 | ||
@@ -633,6 +664,20 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
633 | else | 664 | else |
634 | old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); | 665 | old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); |
635 | 666 | ||
667 | /* | ||
668 | * Silently accept key re-installation without really installing the | ||
669 | * new version of the key to avoid nonce reuse or replay issues. | ||
670 | */ | ||
671 | if (ieee80211_key_identical(sdata, old_key, key)) { | ||
672 | ieee80211_key_free_unused(key); | ||
673 | ret = 0; | ||
674 | goto out; | ||
675 | } | ||
676 | |||
677 | key->local = sdata->local; | ||
678 | key->sdata = sdata; | ||
679 | key->sta = sta; | ||
680 | |||
636 | increment_tailroom_need_count(sdata); | 681 | increment_tailroom_need_count(sdata); |
637 | 682 | ||
638 | ieee80211_key_replace(sdata, sta, pairwise, old_key, key); | 683 | ieee80211_key_replace(sdata, sta, pairwise, old_key, key); |
@@ -648,6 +693,7 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
648 | ret = 0; | 693 | ret = 0; |
649 | } | 694 | } |
650 | 695 | ||
696 | out: | ||
651 | mutex_unlock(&sdata->local->key_mtx); | 697 | mutex_unlock(&sdata->local->key_mtx); |
652 | 698 | ||
653 | return ret; | 699 | return ret; |