cfg80211: fix connect/disconnect edge cases
authorJohannes Berg <johannes.berg@intel.com>
Tue, 17 Oct 2017 19:56:20 +0000 (21:56 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 18 Oct 2017 07:39:44 +0000 (09:39 +0200)
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>
net/wireless/sme.c

index 0a49b88070d0b2d6a6fbcafe6e8816fded775261..b6533ecbf5b1903510b1bf4318b8dee4c4839896 100644 (file)
@@ -522,11 +522,6 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
                return -EOPNOTSUPP;
 
        if (wdev->current_bss) {
-               if (!prev_bssid)
-                       return -EALREADY;
-               if (prev_bssid &&
-                   !ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
-                       return -ENOTCONN;
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
                wdev->current_bss = NULL;
@@ -1063,11 +1058,35 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (WARN_ON(wdev->connect_keys)) {
-               kzfree(wdev->connect_keys);
-               wdev->connect_keys = NULL;
+       /*
+        * If we have an ssid_len, we're trying to connect or are
+        * already connected, so reject a new SSID unless it's the
+        * same (which is the case for re-association.)
+        */
+       if (wdev->ssid_len &&
+           (wdev->ssid_len != connect->ssid_len ||
+            memcmp(wdev->ssid, connect->ssid, wdev->ssid_len)))
+               return -EALREADY;
+
+       /*
+        * If connected, reject (re-)association unless prev_bssid
+        * matches the current BSSID.
+        */
+       if (wdev->current_bss) {
+               if (!prev_bssid)
+                       return -EALREADY;
+               if (!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
+                       return -ENOTCONN;
        }
 
+       /*
+        * Reject if we're in the process of connecting with WEP,
+        * this case isn't very interesting and trying to handle
+        * it would make the code much more complex.
+        */
+       if (wdev->connect_keys)
+               return -EINPROGRESS;
+
        cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
                                  rdev->wiphy.ht_capa_mod_mask);
 
@@ -1118,7 +1137,12 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
 
        if (err) {
                wdev->connect_keys = NULL;
-               wdev->ssid_len = 0;
+               /*
+                * This could be reassoc getting refused, don't clear
+                * ssid_len in that case.
+                */
+               if (!wdev->current_bss)
+                       wdev->ssid_len = 0;
                return err;
        }
 
@@ -1145,6 +1169,14 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
        else if (wdev->ssid_len)
                err = rdev_disconnect(rdev, dev, reason);
 
+       /*
+        * Clear ssid_len unless we actually were fully connected,
+        * in which case cfg80211_disconnected() will take care of
+        * this later.
+        */
+       if (!wdev->current_bss)
+               wdev->ssid_len = 0;
+
        return err;
 }