Fetch the built-in bridges from Tor's Censorship Circumvention API, rather than hardcode them

This commit is contained in:
Miguel Jacq 2021-11-23 15:11:50 +11:00
parent 1d0d30458c
commit 19072503a9
No known key found for this signature in database
GPG key ID: EEA4341C6D97A0B6
8 changed files with 71 additions and 63 deletions

View file

@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from .censorship import CensorshipCircumvention
from .meek import Meek
from stem.control import Controller from stem.control import Controller
from stem import ProtocolError, SocketClosed from stem import ProtocolError, SocketClosed
from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure
@ -125,6 +127,13 @@ class PortNotAvailable(Exception):
""" """
class TorErrorGettingBridges(Exception):
"""
This exception is raised if onionshare tried to fetch bridges from the Tor
CensorshipCircumvention API, but failed to retrieve valid bridges for some reason.
"""
class Onion(object): class Onion(object):
""" """
Onion is an abstraction layer for connecting to the Tor control port and Onion is an abstraction layer for connecting to the Tor control port and
@ -258,9 +267,7 @@ class Onion(object):
and cmdline[2] == self.tor_torrc and cmdline[2] == self.tor_torrc
): ):
self.common.log( self.common.log(
"Onion", "Onion", "connect", "found a stale tor process, killing it"
"connect",
"found a stale tor process, killing it",
) )
proc.terminate() proc.terminate()
proc.wait() proc.wait()
@ -321,45 +328,69 @@ class Onion(object):
# Bridge support # Bridge support
if self.settings.get("bridges_enabled"): if self.settings.get("bridges_enabled"):
f.write("\nUseBridges 1\n")
if self.settings.get("bridges_type") == "built-in": if self.settings.get("bridges_type") == "built-in":
if self.settings.get("bridges_builtin_pt") == "obfs4": # Use the CensorshipCircumvention API to fetch the latest built-in bridges
with open( self.common.log(
self.common.get_resource_path("torrc_template-obfs4") "Onion",
) as o: "connect",
f.write(o.read()) "Trying to automatically obtain built-in bridges via Meek",
elif self.settings.get("bridges_builtin_pt") == "meek-azure": )
with open( meek = Meek(self.common)
self.common.get_resource_path( meek.start()
"torrc_template-meek_lite_azure" self.censorship_circumvention = CensorshipCircumvention(
) self.common, meek
) as o: )
f.write(o.read()) builtin_bridges = (
elif self.settings.get("bridges_builtin_pt") == "snowflake": self.censorship_circumvention.request_builtin_bridges()
with open( )
self.common.get_resource_path( meek.cleanup()
"torrc_template-snowflake" if builtin_bridges:
) self.common.log(
) as o: "Onion",
f.write(o.read()) "connect",
f"Obtained bridges: {builtin_bridges}",
)
if (
self.settings.get("bridges_builtin_pt") == "obfs4"
and "obfs4" in builtin_bridges
):
for line in builtin_bridges["obfs4"]:
f.write(f"Bridge {line}\n")
elif (
self.settings.get("bridges_builtin_pt") == "meek-azure"
and "meek" in builtin_bridges
):
for line in builtin_bridges["meek"]:
# Meek bridge needs to be defined as "meek_lite", not "meek"
line = line.replace("meek", "meek_lite")
f.write(f"Bridge {line}\n")
elif (
self.settings.get("bridges_builtin_pt") == "snowflake"
and "snowflake" in builtin_bridges
):
for line in builtin_bridges["snowflake"]:
f.write(f"Bridge {line}\n")
else:
self.common.log(
"Onion",
"connect",
"Error getting built-in bridges via Meek",
)
raise TorErrorGettingBridges()
elif self.settings.get("bridges_type") == "moat": elif self.settings.get("bridges_type") == "moat":
for line in self.settings.get("bridges_moat").split("\n"): for line in self.settings.get("bridges_moat").split("\n"):
if line.strip() != "": if line.strip() != "":
f.write(f"Bridge {line}\n") f.write(f"Bridge {line}\n")
f.write("\nUseBridges 1\n")
elif self.settings.get("bridges_type") == "custom": elif self.settings.get("bridges_type") == "custom":
for line in self.settings.get("bridges_custom").split("\n"): for line in self.settings.get("bridges_custom").split("\n"):
if line.strip() != "": if line.strip() != "":
f.write(f"Bridge {line}\n") f.write(f"Bridge {line}\n")
f.write("\nUseBridges 1\n")
# Execute a tor subprocess # Execute a tor subprocess
self.common.log( self.common.log("Onion", "connect", f"starting {self.tor_path} subprocess")
"Onion",
"connect",
f"starting {self.tor_path} subprocess",
)
start_ts = time.time() start_ts = time.time()
if self.common.platform == "Windows": if self.common.platform == "Windows":
# In Windows, hide console window when opening tor.exe subprocess # In Windows, hide console window when opening tor.exe subprocess
@ -385,19 +416,11 @@ class Onion(object):
) )
# Wait for the tor controller to start # Wait for the tor controller to start
self.common.log( self.common.log("Onion", "connect", f"tor pid: {self.tor_proc.pid}")
"Onion",
"connect",
f"tor pid: {self.tor_proc.pid}",
)
time.sleep(2) time.sleep(2)
# Connect to the controller # Connect to the controller
self.common.log( self.common.log("Onion", "connect", "authenticating to tor controller")
"Onion",
"connect",
"authenticating to tor controller",
)
try: try:
if ( if (
self.common.platform == "Windows" self.common.platform == "Windows"

View file

@ -1,3 +0,0 @@
# Enable built-in meek-azure bridge
Bridge meek_lite 0.0.2.0:3 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
UseBridges 1

View file

@ -1,17 +0,0 @@
# Enable built-in obfs4-bridge
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
Bridge obfs4 144.217.20.138:80 FB70B257C162BF1038CA669D568D76F5B7F0BABB cert=vYIV5MgrghGQvZPIi1tJwnzorMgqgmlKaB77Y3Z9Q/v94wZBOAXkW+fdx4aSxLVnKO+xNw iat-mode=0
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
Bridge obfs4 [2a0c:4d80:42:702::1]:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
UseBridges 1

View file

@ -1,3 +0,0 @@
# Enable built-in snowflake bridge
Bridge snowflake 192.0.2.3:1 2B280B23E1107BB62ABFC40DDCC8824814F80A72
UseBridges 1

View file

@ -38,6 +38,7 @@ from onionshare_cli.onion import (
TorTooOldEphemeral, TorTooOldEphemeral,
TorTooOldStealth, TorTooOldStealth,
PortNotAvailable, PortNotAvailable,
TorErrorGettingBridges,
) )
@ -507,5 +508,7 @@ class GuiCommon:
return strings._("error_stealth_not_supported") return strings._("error_stealth_not_supported")
elif type(e) is PortNotAvailable: elif type(e) is PortNotAvailable:
return strings._("error_port_not_available") return strings._("error_port_not_available")
elif type(e) is TorErrorGettingBridges:
return strings._("error_getting_bridges")
return None return None

View file

@ -223,6 +223,7 @@
"error_port_not_available": "OnionShare port not available", "error_port_not_available": "OnionShare port not available",
"history_receive_read_message_button": "Read Message", "history_receive_read_message_button": "Read Message",
"error_tor_protocol_error": "There was an error with Tor: {}", "error_tor_protocol_error": "There was an error with Tor: {}",
"error_getting_bridges": "Could not obtain bridges from the Tor API",
"moat_contact_label": "Contacting BridgeDB...", "moat_contact_label": "Contacting BridgeDB...",
"moat_captcha_label": "Solve the CAPTCHA to request a bridge.", "moat_captcha_label": "Solve the CAPTCHA to request a bridge.",
"moat_captcha_placeholder": "Enter the characters from the image", "moat_captcha_placeholder": "Enter the characters from the image",
@ -232,4 +233,4 @@
"moat_captcha_error": "The solution is not correct. Please try again.", "moat_captcha_error": "The solution is not correct. Please try again.",
"moat_solution_empty_error": "You must enter the characters from the image", "moat_solution_empty_error": "You must enter the characters from the image",
"mode_tor_not_connected_label": "OnionShare is not connected to the Tor network" "mode_tor_not_connected_label": "OnionShare is not connected to the Tor network"
} }

View file

@ -37,6 +37,7 @@ from onionshare_cli.onion import (
TorTooOldEphemeral, TorTooOldEphemeral,
TorTooOldStealth, TorTooOldStealth,
PortNotAvailable, PortNotAvailable,
TorErrorGettingBridges,
) )
from . import strings from . import strings
@ -104,6 +105,7 @@ class OnionThread(QtCore.QThread):
TorTooOldEphemeral, TorTooOldEphemeral,
TorTooOldStealth, TorTooOldStealth,
PortNotAvailable, PortNotAvailable,
TorErrorGettingBridges,
) as e: ) as e:
message = self.mode.common.gui.get_translated_tor_error(e) message = self.mode.common.gui.get_translated_tor_error(e)
self.error.emit(message) self.error.emit(message)

View file

@ -36,6 +36,7 @@ from onionshare_cli.onion import (
TorTooOldEphemeral, TorTooOldEphemeral,
TorTooOldStealth, TorTooOldStealth,
PortNotAvailable, PortNotAvailable,
TorErrorGettingBridges,
) )
from . import strings from . import strings
@ -310,6 +311,7 @@ class TorConnectionThread(QtCore.QThread):
TorTooOldEphemeral, TorTooOldEphemeral,
TorTooOldStealth, TorTooOldStealth,
PortNotAvailable, PortNotAvailable,
TorErrorGettingBridges,
) as e: ) as e:
message = self.common.gui.get_translated_tor_error(e) message = self.common.gui.get_translated_tor_error(e)
self.common.log( self.common.log(