[tor-commits] [bridgedb/develop] Avoid giving out bridges with broken tor versions.

phw at torproject.org phw at torproject.org
Tue Jun 23 23:22:07 UTC 2020


commit 8d169fc45c8cfd7c8c9028d125bff4dbb3afafc6
Author: Philipp Winter <phw at nymity.ch>
Date:   Fri Jun 12 10:05:26 2020 -0700

    Avoid giving out bridges with broken tor versions.
    
    This patch makes BridgeDB avoid giving out bridges that are affected by
    the following bug: <https://bugs.torproject.org/28912>
    
    This fixes <https://bugs.torproject.org/29184>.
---
 CHANGELOG                     |  5 +++++
 bridgedb.conf                 | 10 ++++++++++
 bridgedb/bridges.py           | 15 +++++++++++++++
 bridgedb/main.py              |  7 +++++++
 bridgedb/parse/versions.py    | 29 +++++++++++++++++++++++++++++
 bridgedb/test/test_bridges.py | 27 +++++++++++++++++++++++++++
 6 files changed, 93 insertions(+)

diff --git a/CHANGELOG b/CHANGELOG
index 754e2f0..c712436 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+    * FIXES https://bugs.torproject.org/29184
+    Add a new configuration option, BLACKLISTED_TOR_VERSIONS, which contains a
+    list of Tor versions.  BridgeDB won't hand out bridges whose Tor version
+    is present in this blacklist.
+
     * FIXES https://bugs.torproject.org/19774
     Add a favicon to BridgeDB's web UI.
 
diff --git a/bridgedb.conf b/bridgedb.conf
index 31eb3fa..6d4839d 100644
--- a/bridgedb.conf
+++ b/bridgedb.conf
@@ -309,6 +309,16 @@ DEFAULT_TRANSPORT = 'obfs4'
 #   Accept-Language,[Kk]lingon
 BLACKLISTED_REQUEST_HEADERS_FILE="blacklisted-request-headers.csv"
 
+# List of tuples that specify blacklisted tor version ranges.  The first
+# element marks the start of the range and the second element marks the end.
+# Both the start *and* the end version are blocked too.  If you want to block a
+# single version, have the start and end range be identical.  BridgeDB won't
+# distribute bridges whose version falls within any version ranges.
+BLACKLISTED_TOR_VERSIONS = [
+    ('0.3.4', '0.3.4.9'), # See <https://bugs.torproject.org/29184>.
+    ('0.3.5', '0.3.5.6')
+]
+
 # Decoy bridges that we are handing out to bots that we detected using the
 # regular expressions in BLACKLISTED_REQUEST_HEADERS_FILE.  The CSV file must
 # have the following format:
diff --git a/bridgedb/bridges.py b/bridgedb/bridges.py
index cd01948..6ec864d 100644
--- a/bridgedb/bridges.py
+++ b/bridgedb/bridges.py
@@ -1825,3 +1825,18 @@ class Bridge(BridgeBackwardsCompatibility):
             logging.info("Removing dead transport for bridge %s: %s %s:%s %s" %
                          (self, pt.methodname, pt.address, pt.port, pt.arguments))
             self.transports.remove(pt)
+
+    def runsVersion(self, version_tuples):
+        """Return ``True`` if this bridge runs any of the given versions.
+
+        :param list version_tuples: A list of tuples that contain a minimum and
+            maximum version number (as :class:`stem.version.Version` objects),
+            each.
+        :rtype: bool
+        :returns: ``True`` if this bridge runs any of the given Tor versions
+            and ``False`` otherwise.
+        """
+        for min_version, max_version in version_tuples:
+            if min_version <= self.software <= max_version:
+                return True
+        return False
diff --git a/bridgedb/main.py b/bridgedb/main.py
index 70001a9..44d0668 100644
--- a/bridgedb/main.py
+++ b/bridgedb/main.py
@@ -38,6 +38,7 @@ from bridgedb.distributors.https.distributor import HTTPSDistributor
 from bridgedb.distributors.moat.distributor import MoatDistributor
 from bridgedb.parse import descriptors
 from bridgedb.parse.blacklist import parseBridgeBlacklistFile
+from bridgedb.parse.versions import parseVersionsList
 
 import bridgedb.Storage
 
@@ -211,6 +212,10 @@ def load(state, hashring, clear=False):
             elif bridge in blacklist.keys():
                 logging.warn("Not distributing blacklisted Bridge %s %s:%s: %s" %
                              (bridge, bridge.address, bridge.orPort, blacklist[bridge]))
+            # Skip bridges that are running a blacklisted version of Tor.
+            elif bridge.runsVersion(state.BLACKLISTED_TOR_VERSIONS):
+                logging.warn("Not distributing bridge %s because it runs blacklisted "
+                             "Tor version %s." % (router.fingerprint, bridge.software))
             else:
                 # If the bridge is not running, then it is skipped during the
                 # insertion process.
@@ -418,6 +423,8 @@ def run(options, reactor=reactor):
             proxy.loadProxiesFromFile(proxyfile, proxies, removeStale=True)
         metrics.setProxies(proxies)
 
+        state.BLACKLISTED_TOR_VERSIONS = parseVersionsList(state.BLACKLISTED_TOR_VERSIONS)
+
         logging.info("Reloading blacklisted request headers...")
         antibot.loadBlacklistedRequestHeaders(config.BLACKLISTED_REQUEST_HEADERS_FILE)
         logging.info("Reloading decoy bridges...")
diff --git a/bridgedb/parse/versions.py b/bridgedb/parse/versions.py
index 335e04c..832969e 100644
--- a/bridgedb/parse/versions.py
+++ b/bridgedb/parse/versions.py
@@ -23,6 +23,10 @@ bridgedb.parse.versions
 ..
 """
 
+import logging
+
+import stem.version
+
 from twisted import version as _txversion
 
 # The twisted.python.util.Version class was moved in Twisted==14.0.0 to
@@ -127,3 +131,28 @@ class Version(_Version):
                str(self.minor),
                str(self.micro),
                str(prerelease))
+
+
+def parseVersionsList(versions_list):
+    """Turn the given version strings into stem objects.
+
+    :param list versions_list: A list of tuples. Each tuple contains a minimum
+        and maximum version number as strings.
+    :rtype: list
+    :returns: A list of tuples. Each tuple contains a minimum and maximum
+        version number as :class:`stem.version.Version` objects.
+    """
+    parsed = []
+    for v1, v2 in versions_list:
+        # We're dealing with an already-parsed version list.
+        if isinstance(v1, stem.version.Version):
+            return versions_list
+        try:
+            parsed.append(tuple([stem.version.Version(v1),
+                                 stem.version.Version(v2)]))
+        except ValueError:
+            logging.error("Couldn't parse BLACKLISTED_TOR_VERSIONS; "
+                          "probably because of badly formatted "
+                          "configuration file. Ignoring config option.")
+            return []
+    return parsed
diff --git a/bridgedb/test/test_bridges.py b/bridgedb/test/test_bridges.py
index fd14e87..82d9584 100644
--- a/bridgedb/test/test_bridges.py
+++ b/bridgedb/test/test_bridges.py
@@ -19,6 +19,8 @@ import hashlib
 import os
 import warnings
 
+import stem.version
+
 from twisted.trial import unittest
 
 from bridgedb import bridges
@@ -1837,3 +1839,28 @@ class BridgeTests(unittest.TestCase):
         self.assertTrue(len(self.bridge.transports), 3)
         self.assertNotIn('scramblesuit',
                          [pt.methodname for pt in self.bridge.transports])
+
+    def test_runsVersions(self):
+        """Calling runsVersions() should tell us if a bridge is running any of
+        the given versions.
+        """
+        self.bridge.software = stem.version.Version("0.1.2.3")
+
+        t1 = tuple([stem.version.Version("0.1.2.3"),
+                    stem.version.Version("0.1.2.3")])
+        self.assertTrue(self.bridge.runsVersion([t1]))
+
+        t2 = tuple([stem.version.Version("0.1.2"),
+                    stem.version.Version("0.1.2")])
+        self.assertFalse(self.bridge.runsVersion([t2]))
+
+        t3 = tuple([stem.version.Version("0.1.2"),
+                    stem.version.Version("0.1.2.5")])
+        self.assertTrue(self.bridge.runsVersion([t3]))
+
+        t4 = tuple([stem.version.Version("0.2.2"),
+                    stem.version.Version("0.3.1")])
+        self.assertFalse(self.bridge.runsVersion([t4]))
+
+        self.assertTrue(self.bridge.runsVersion([t1, t2, t3, t4]))
+        self.assertFalse(self.bridge.runsVersion([t2, t4]))





More information about the tor-commits mailing list