commit 8d169fc45c8cfd7c8c9028d125bff4dbb3afafc6 Author: Philipp Winter phw@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]))
tor-commits@lists.torproject.org