diff --git a/onionshare/onion.py b/onionshare/onion.py index b52429d3..3da2935d 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -33,6 +33,14 @@ class NoTor(Exception): """ pass +class TorTooOld(Exception): + """ + This exception is raised if onionshare needs to use a feature of Tor or stem + (like stealth ephemeral onion services) but the version you have installed + is too old. + """ + pass + class Onion(object): """ Onion is an abstraction layer for connecting to the Tor control port and @@ -48,8 +56,9 @@ class Onion(object): onion services are supported. If not, it falls back to modifying the Tor configuration. """ - def __init__(self, transparent_torification=False): + def __init__(self, transparent_torification=False, stealth=False): self.transparent_torification = transparent_torification + self.stealth = stealth # files and dirs to delete on shutdown self.cleanup_filenames = [] @@ -89,17 +98,42 @@ class Onion(object): list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None) self.supports_ephemeral = callable(list_ephemeral_hidden_services) and tor_version >= '0.2.7.1' + # do the versions of stem and tor that I'm using support stealth onion services? + try: + res = self.c.create_ephemeral_hidden_service({1:1}, basic_auth={'onionshare':None}, await_publication=False) + tmp_service_id = res.content()[0][2].split('=')[1] + self.c.remove_ephemeral_hidden_service(tmp_service_id) + self.supports_stealth = True + except TypeError: + # ephemeral stealth onion services are not supported + self.supports_stealth = False + def start(self, port): """ Start a onion service on port 80, pointing to the given port, and return the onion hostname. """ - print(strings._("connecting_ctrlport").format(int(port))) + self.auth_string = None + if self.stealth and not self.supports_stealth: + raise TorTooOld(strings._('error_stealth_not_supported')) + + print(strings._("config_onion_service").format(int(port))) if self.supports_ephemeral: print(strings._('using_ephemeral')) - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication = True) + + if self.stealth: + basic_auth = {'onionshare':None} + else: + basic_auth = None + + res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth) self.service_id = res.content()[0][2].split('=')[1] onion_host = self.service_id + '.onion' + + if self.stealth: + auth_cookie = res.content()[2][2].split('=')[1].split(':')[1] + self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) + return onion_host else: diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 6dcc5ba0..57983354 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -27,7 +27,7 @@ class OnionShare(object): OnionShare is the main application class. Pass in options and run start_onion_service and it will do the magic. """ - def __init__(self, debug=False, local_only=False, stay_open=False, transparent_torification=False): + def __init__(self, debug=False, local_only=False, stay_open=False, transparent_torification=False, stealth=False): self.port = None self.onion = None self.hidserv_dir = None @@ -49,6 +49,9 @@ class OnionShare(object): # traffic automatically goes through Tor self.transparent_torification = transparent_torification + # use stealth onion service + self.stealth = stealth + def choose_port(self): """ Pick an un-used port in the range 17600-17650 to bind to. @@ -76,10 +79,13 @@ class OnionShare(object): return if not self.onion: - self.onion = onion.Onion(self.transparent_torification) + self.onion = onion.Onion(self.transparent_torification, self.stealth) self.onion_host = self.onion.start(self.port) + if self.stealth: + self.auth_string = self.onion.auth_string + def cleanup(self): """ Shut everything down and clean up temporary files, etc. @@ -115,6 +121,7 @@ def main(cwd=None): parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only")) parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open")) parser.add_argument('--transparent', action='store_true', dest='transparent_torification', help=strings._("help_transparent_torification")) + parser.add_argument('--stealth', action='store_true', dest='stealth', help=strings._("help_stealth")) parser.add_argument('--debug', action='store_true', dest='debug', help=strings._("help_debug")) parser.add_argument('filename', metavar='filename', nargs='+', help=strings._('help_filename')) args = parser.parse_args() @@ -127,6 +134,7 @@ def main(cwd=None): debug = bool(args.debug) stay_open = bool(args.stay_open) transparent_torification = bool(args.transparent_torification) + stealth = bool(args.stealth) # validation valid = True @@ -139,11 +147,13 @@ def main(cwd=None): # start the onionshare app try: - app = OnionShare(debug, local_only, stay_open, transparent_torification) + app = OnionShare(debug, local_only, stay_open, transparent_torification, stealth) app.choose_port() app.start_onion_service() except onion.NoTor as e: sys.exit(e.args[0]) + except onion.TorTooOld as e: + sys.exit(e.args[0]) # prepare files to share print(strings._("preparing_files")) @@ -171,8 +181,13 @@ def main(cwd=None): # Wait for web.generate_slug() to finish running time.sleep(0.2) - print(strings._("give_this_url")) - print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug)) + if(stealth): + print(strings._("give_this_url_stealth")) + print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug)) + print(app.auth_string) + else: + print(strings._("give_this_url")) + print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug)) print('') print(strings._("ctrlc_to_stop")) diff --git a/resources/locale/en.json b/resources/locale/en.json index 42cdc385..1fc1e7ab 100644 --- a/resources/locale/en.json +++ b/resources/locale/en.json @@ -1,5 +1,5 @@ { - "connecting_ctrlport": "Connecting to Tor control port to set up onion service on port {0:d}.", + "config_onion_service": "Configuring onion service on port {0:d}.", "cant_connect_ctrlport": "Can't connect to Tor control port on port {0:s}. OnionShare requires Tor Browser to be running in the background to work. If you don't have it you can get it from https://www.torproject.org/.", "cant_connect_socksport": "Can't connect to Tor SOCKS5 server on port {0:s}. OnionShare requires Tor Browser to be running in the background to work. If you don't have it you can get it from https://www.torproject.org/.", "ctrlport_missing_password": "Connected to Tor control port on port {0:s}, but you require a password. You must have the TOR_AUTHENTICATION_PASSWORD environment variable set. Or just open Tor Browser in the background.", @@ -10,6 +10,7 @@ "wait_for_hs_nope": "Not ready yet.", "wait_for_hs_yup": "Ready!", "give_this_url": "Give this URL to the person you're sending the file to:", + "give_this_url_stealth": "Give this URL and HidServAuth line to the person you're sending the file to:", "ctrlc_to_stop": "Press Ctrl-C to stop server", "not_a_file": "{0:s} is not a file.", "download_page_loaded": "Download page loaded", @@ -19,10 +20,10 @@ "large_filesize": "Warning: Sending large files could take hours", "error_tails_invalid_port": "Invalid value, port must be an integer", "error_tails_unknown_root": "Unknown error with Tails root process", - "help_tails_port": "Tails only: port for opening firewall, starting onion service", "help_local_only": "Do not attempt to use tor: for development only", "help_stay_open": "Keep onion service running after download has finished", "help_transparent_torification": "My system is transparently torified", + "help_stealth": "Create stealth onion service (advanced)", "help_debug": "Log errors to disk", "help_filename": "List of files or folders to share", "gui_drag_and_drop": "Drag and drop\nfiles here", @@ -52,5 +53,6 @@ "gui_quit_warning_quit": "Quit", "gui_quit_warning_dont_quit": "Don't Quit", "error_rate_limit": "An attacker might be trying to guess your URL. To prevent this, OnionShare has automatically stopped the server. To share the files you must start it again and share the new URL.", - "zip_progress_bar_format": "Crunching files: %p%" + "zip_progress_bar_format": "Crunching files: %p%", + "error_stealth_not_supported": "Your versions of tor or stem are too old. You need to upgrade them to create stealth onion services." }