diff options
author | David S. Miller <davem@davemloft.net> | 2017-10-27 00:50:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-10-27 00:50:06 -0400 |
commit | 9618aec3349b7669b6bf123c7c6121789cb82861 (patch) | |
tree | 6a4d6f63a900b5e8a5ccaf1c34c0635bf042d276 | |
parent | 78e0ea6791d7baafb8a0ca82b1bd0c7b3453c919 (diff) | |
parent | cfbb0d90a7abb289edc91833d0905931f8805f12 (diff) |
Merge tag 'mac80211-for-davem-2017-10-25' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211
Johannes Berg says:
====================
pull-request: mac80211 2017-10-25
Here are:
* follow-up fixes for the WoWLAN security issue, to fix a
partial TKIP key material problem and to use crypto_memneq()
* a change for better enforcement of FQ's memory limit
* a disconnect/connect handling fix, and
* a user rate mask validation fix
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/fq_impl.h | 9 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 12 | ||||
-rw-r--r-- | net/mac80211/key.c | 37 | ||||
-rw-r--r-- | net/wireless/sme.c | 50 |
4 files changed, 88 insertions, 20 deletions
diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h index 4e6131cd3f43..ac1a2317941e 100644 --- a/include/net/fq_impl.h +++ b/include/net/fq_impl.h | |||
@@ -146,6 +146,7 @@ static void fq_tin_enqueue(struct fq *fq, | |||
146 | fq_flow_get_default_t get_default_func) | 146 | fq_flow_get_default_t get_default_func) |
147 | { | 147 | { |
148 | struct fq_flow *flow; | 148 | struct fq_flow *flow; |
149 | bool oom; | ||
149 | 150 | ||
150 | lockdep_assert_held(&fq->lock); | 151 | lockdep_assert_held(&fq->lock); |
151 | 152 | ||
@@ -167,8 +168,8 @@ static void fq_tin_enqueue(struct fq *fq, | |||
167 | } | 168 | } |
168 | 169 | ||
169 | __skb_queue_tail(&flow->queue, skb); | 170 | __skb_queue_tail(&flow->queue, skb); |
170 | 171 | oom = (fq->memory_usage > fq->memory_limit); | |
171 | if (fq->backlog > fq->limit || fq->memory_usage > fq->memory_limit) { | 172 | while (fq->backlog > fq->limit || oom) { |
172 | flow = list_first_entry_or_null(&fq->backlogs, | 173 | flow = list_first_entry_or_null(&fq->backlogs, |
173 | struct fq_flow, | 174 | struct fq_flow, |
174 | backlogchain); | 175 | backlogchain); |
@@ -183,8 +184,10 @@ static void fq_tin_enqueue(struct fq *fq, | |||
183 | 184 | ||
184 | flow->tin->overlimit++; | 185 | flow->tin->overlimit++; |
185 | fq->overlimit++; | 186 | fq->overlimit++; |
186 | if (fq->memory_usage > fq->memory_limit) | 187 | if (oom) { |
187 | fq->overmemory++; | 188 | fq->overmemory++; |
189 | oom = (fq->memory_usage > fq->memory_limit); | ||
190 | } | ||
188 | } | 191 | } |
189 | } | 192 | } |
190 | 193 | ||
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a354f1939e49..fb15d3b97cb2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -2727,12 +2727,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, | |||
2727 | if (!ieee80211_sdata_running(sdata)) | 2727 | if (!ieee80211_sdata_running(sdata)) |
2728 | return -ENETDOWN; | 2728 | return -ENETDOWN; |
2729 | 2729 | ||
2730 | if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) { | ||
2731 | ret = drv_set_bitrate_mask(local, sdata, mask); | ||
2732 | if (ret) | ||
2733 | return ret; | ||
2734 | } | ||
2735 | |||
2736 | /* | 2730 | /* |
2737 | * If active validate the setting and reject it if it doesn't leave | 2731 | * If active validate the setting and reject it if it doesn't leave |
2738 | * at least one basic rate usable, since we really have to be able | 2732 | * at least one basic rate usable, since we really have to be able |
@@ -2748,6 +2742,12 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, | |||
2748 | return -EINVAL; | 2742 | return -EINVAL; |
2749 | } | 2743 | } |
2750 | 2744 | ||
2745 | if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) { | ||
2746 | ret = drv_set_bitrate_mask(local, sdata, mask); | ||
2747 | if (ret) | ||
2748 | return ret; | ||
2749 | } | ||
2750 | |||
2751 | for (i = 0; i < NUM_NL80211_BANDS; i++) { | 2751 | for (i = 0; i < NUM_NL80211_BANDS; i++) { |
2752 | struct ieee80211_supported_band *sband = wiphy->bands[i]; | 2752 | struct ieee80211_supported_band *sband = wiphy->bands[i]; |
2753 | int j; | 2753 | int j; |
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ae995c8480db..938049395f90 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -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) |
@@ -634,8 +668,7 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
634 | * Silently accept key re-installation without really installing the | 668 | * Silently accept key re-installation without really installing the |
635 | * new version of the key to avoid nonce reuse or replay issues. | 669 | * new version of the key to avoid nonce reuse or replay issues. |
636 | */ | 670 | */ |
637 | if (old_key && key->conf.keylen == old_key->conf.keylen && | 671 | if (ieee80211_key_identical(sdata, old_key, key)) { |
638 | !memcmp(key->conf.key, old_key->conf.key, key->conf.keylen)) { | ||
639 | ieee80211_key_free_unused(key); | 672 | ieee80211_key_free_unused(key); |
640 | ret = 0; | 673 | ret = 0; |
641 | goto out; | 674 | goto out; |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 0a49b88070d0..b6533ecbf5b1 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -522,11 +522,6 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev, | |||
522 | return -EOPNOTSUPP; | 522 | return -EOPNOTSUPP; |
523 | 523 | ||
524 | if (wdev->current_bss) { | 524 | if (wdev->current_bss) { |
525 | if (!prev_bssid) | ||
526 | return -EALREADY; | ||
527 | if (prev_bssid && | ||
528 | !ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid)) | ||
529 | return -ENOTCONN; | ||
530 | cfg80211_unhold_bss(wdev->current_bss); | 525 | cfg80211_unhold_bss(wdev->current_bss); |
531 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | 526 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
532 | wdev->current_bss = NULL; | 527 | wdev->current_bss = NULL; |
@@ -1063,11 +1058,35 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
1063 | 1058 | ||
1064 | ASSERT_WDEV_LOCK(wdev); | 1059 | ASSERT_WDEV_LOCK(wdev); |
1065 | 1060 | ||
1066 | if (WARN_ON(wdev->connect_keys)) { | 1061 | /* |
1067 | kzfree(wdev->connect_keys); | 1062 | * If we have an ssid_len, we're trying to connect or are |
1068 | wdev->connect_keys = NULL; | 1063 | * already connected, so reject a new SSID unless it's the |
1064 | * same (which is the case for re-association.) | ||
1065 | */ | ||
1066 | if (wdev->ssid_len && | ||
1067 | (wdev->ssid_len != connect->ssid_len || | ||
1068 | memcmp(wdev->ssid, connect->ssid, wdev->ssid_len))) | ||
1069 | return -EALREADY; | ||
1070 | |||
1071 | /* | ||
1072 | * If connected, reject (re-)association unless prev_bssid | ||
1073 | * matches the current BSSID. | ||
1074 | */ | ||
1075 | if (wdev->current_bss) { | ||
1076 | if (!prev_bssid) | ||
1077 | return -EALREADY; | ||
1078 | if (!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid)) | ||
1079 | return -ENOTCONN; | ||
1069 | } | 1080 | } |
1070 | 1081 | ||
1082 | /* | ||
1083 | * Reject if we're in the process of connecting with WEP, | ||
1084 | * this case isn't very interesting and trying to handle | ||
1085 | * it would make the code much more complex. | ||
1086 | */ | ||
1087 | if (wdev->connect_keys) | ||
1088 | return -EINPROGRESS; | ||
1089 | |||
1071 | cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, | 1090 | cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, |
1072 | rdev->wiphy.ht_capa_mod_mask); | 1091 | rdev->wiphy.ht_capa_mod_mask); |
1073 | 1092 | ||
@@ -1118,7 +1137,12 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
1118 | 1137 | ||
1119 | if (err) { | 1138 | if (err) { |
1120 | wdev->connect_keys = NULL; | 1139 | wdev->connect_keys = NULL; |
1121 | wdev->ssid_len = 0; | 1140 | /* |
1141 | * This could be reassoc getting refused, don't clear | ||
1142 | * ssid_len in that case. | ||
1143 | */ | ||
1144 | if (!wdev->current_bss) | ||
1145 | wdev->ssid_len = 0; | ||
1122 | return err; | 1146 | return err; |
1123 | } | 1147 | } |
1124 | 1148 | ||
@@ -1145,6 +1169,14 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | |||
1145 | else if (wdev->ssid_len) | 1169 | else if (wdev->ssid_len) |
1146 | err = rdev_disconnect(rdev, dev, reason); | 1170 | err = rdev_disconnect(rdev, dev, reason); |
1147 | 1171 | ||
1172 | /* | ||
1173 | * Clear ssid_len unless we actually were fully connected, | ||
1174 | * in which case cfg80211_disconnected() will take care of | ||
1175 | * this later. | ||
1176 | */ | ||
1177 | if (!wdev->current_bss) | ||
1178 | wdev->ssid_len = 0; | ||
1179 | |||
1148 | return err; | 1180 | return err; |
1149 | } | 1181 | } |
1150 | 1182 | ||