diff options
author | Johannes Berg <johannes.berg@intel.com> | 2017-10-17 15:56:20 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2017-10-18 03:39:44 -0400 |
commit | 51e13359cd5ea34acc62c90627603352956380af (patch) | |
tree | 1e31a3a0e4d5aed3ec1552351990686b1d6b951a | |
parent | 2bdd713b92a9cade239d3c7d15205a09f556624d (diff) |
cfg80211: fix connect/disconnect edge cases
If we try to connect while already connected/connecting, but
this fails, we set ssid_len=0 but leave current_bss hanging,
leading to errors.
Check all of this better, first of all ensuring that we can't
try to connect to a different SSID while connected/ing; ensure
that prev_bssid is set for re-association attempts even in the
case of the driver supporting the connect() method, and don't
reset ssid_len in the failure cases.
While at it, also reset ssid_len while disconnecting unless we
were connected and expect a disconnected event, and warn on a
successful connection without ssid_len being set.
Cc: stable@vger.kernel.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | net/wireless/sme.c | 50 |
1 files changed, 41 insertions, 9 deletions
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 | ||