mirror of
https://github.com/onionshare/onionshare.git
synced 2024-10-01 01:35:40 -04:00
Start refactoring Onion to allow for managing a separate onion service for each tab
This commit is contained in:
parent
81584e12ff
commit
28bc37d16f
@ -36,7 +36,7 @@ class ModeSettings:
|
||||
"enabled": False,
|
||||
"mode": None,
|
||||
"private_key": None,
|
||||
"hidservauth": None,
|
||||
"hidservauth_string": None,
|
||||
"password": None,
|
||||
},
|
||||
"general": {
|
||||
@ -45,6 +45,7 @@ class ModeSettings:
|
||||
"autostop_timer": False,
|
||||
"legacy": False,
|
||||
"client_auth": False,
|
||||
"service_id": None,
|
||||
},
|
||||
"share": {"autostop_sharing": True, "filenames": []},
|
||||
"receive": {"data_dir": self.build_default_receive_data_dir()},
|
||||
|
@ -152,14 +152,8 @@ class Onion(object):
|
||||
|
||||
def __init__(self, common):
|
||||
self.common = common
|
||||
|
||||
self.common.log("Onion", "__init__")
|
||||
|
||||
self.stealth = False
|
||||
self.service_id = None
|
||||
self.scheduled_key = None
|
||||
self.scheduled_auth_cookie = None
|
||||
|
||||
# Is bundled tor supported?
|
||||
if (
|
||||
self.common.platform == "Windows" or self.common.platform == "Darwin"
|
||||
@ -570,48 +564,32 @@ class Onion(object):
|
||||
else:
|
||||
return False
|
||||
|
||||
def start_onion_service(self, port, await_publication, save_scheduled_key=False):
|
||||
def start_onion_service(
|
||||
self, mode_settings, port, await_publication, save_scheduled_key=False
|
||||
):
|
||||
"""
|
||||
Start a onion service on port 80, pointing to the given port, and
|
||||
return the onion hostname.
|
||||
"""
|
||||
self.common.log("Onion", "start_onion_service")
|
||||
# Settings may have changed in the frontend but not updated in our settings object,
|
||||
# such as persistence. Reload the settings now just to be sure.
|
||||
self.settings.load()
|
||||
|
||||
self.auth_string = None
|
||||
self.common.log("Onion", "start_onion_service", f"port={port}")
|
||||
|
||||
if not self.supports_ephemeral:
|
||||
raise TorTooOld(strings._("error_ephemeral_not_supported"))
|
||||
if self.stealth and not self.supports_stealth:
|
||||
if mode_settings.get("general", "client_auth") and not self.supports_stealth:
|
||||
raise TorTooOld(strings._("error_stealth_not_supported"))
|
||||
|
||||
if not save_scheduled_key:
|
||||
print(f"Setting up onion service on port {port}.")
|
||||
|
||||
if self.stealth:
|
||||
if self.settings.get("hidservauth_string"):
|
||||
hidservauth_string = self.settings.get("hidservauth_string").split()[2]
|
||||
basic_auth = {"onionshare": hidservauth_string}
|
||||
else:
|
||||
if self.scheduled_auth_cookie:
|
||||
basic_auth = {"onionshare": self.scheduled_auth_cookie}
|
||||
else:
|
||||
basic_auth = {"onionshare": None}
|
||||
if mode_settings.get("general", "client_auth") and mode_settings.get(
|
||||
"general", "hidservauth_string"
|
||||
):
|
||||
auth_cookie = mode_settings.get("persistent", "hidservauth_string").split()[
|
||||
2
|
||||
]
|
||||
basic_auth = {"onionshare": auth_cookie}
|
||||
else:
|
||||
basic_auth = None
|
||||
|
||||
if self.settings.get("private_key"):
|
||||
key_content = self.settings.get("private_key")
|
||||
if self.is_v2_key(key_content):
|
||||
key_type = "RSA1024"
|
||||
else:
|
||||
# Assume it was a v3 key. Stem will throw an error if it's something illegible
|
||||
key_type = "ED25519-V3"
|
||||
|
||||
elif self.scheduled_key:
|
||||
key_content = self.scheduled_key
|
||||
if mode_settings.get("persistent", "private_key"):
|
||||
key_content = mode_settings.get("persistent", "private_key")
|
||||
if self.is_v2_key(key_content):
|
||||
key_type = "RSA1024"
|
||||
else:
|
||||
@ -621,9 +599,7 @@ class Onion(object):
|
||||
else:
|
||||
key_type = "NEW"
|
||||
# Work out if we can support v3 onion services, which are preferred
|
||||
if self.supports_v3_onions and not self.settings.get(
|
||||
"use_legacy_v2_onions"
|
||||
):
|
||||
if self.supports_v3_onions and not mode_settings.get("general", "legacy"):
|
||||
key_content = "ED25519-V3"
|
||||
else:
|
||||
# fall back to v2 onion services
|
||||
@ -634,87 +610,62 @@ class Onion(object):
|
||||
if (
|
||||
key_type == "NEW"
|
||||
and key_content == "ED25519-V3"
|
||||
and not self.settings.get("use_legacy_v2_onions")
|
||||
and not mode_settings.get("general", "legacy")
|
||||
):
|
||||
basic_auth = None
|
||||
self.stealth = False
|
||||
|
||||
debug_message = f"key_type={key_type}"
|
||||
if key_type == "NEW":
|
||||
debug_message += f", key_content={key_content}"
|
||||
self.common.log("Onion", "start_onion_service", debug_message)
|
||||
try:
|
||||
if basic_auth != None:
|
||||
res = self.c.create_ephemeral_hidden_service(
|
||||
{80: port},
|
||||
await_publication=await_publication,
|
||||
basic_auth=basic_auth,
|
||||
key_type=key_type,
|
||||
key_content=key_content,
|
||||
)
|
||||
else:
|
||||
# if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg
|
||||
res = self.c.create_ephemeral_hidden_service(
|
||||
{80: port},
|
||||
await_publication=await_publication,
|
||||
key_type=key_type,
|
||||
key_content=key_content,
|
||||
)
|
||||
res = self.c.create_ephemeral_hidden_service(
|
||||
{80: port},
|
||||
await_publication=await_publication,
|
||||
basic_auth=basic_auth,
|
||||
key_type=key_type,
|
||||
key_content=key_content,
|
||||
)
|
||||
|
||||
except ProtocolError as e:
|
||||
raise TorErrorProtocolError(
|
||||
strings._("error_tor_protocol_error").format(e.args[0])
|
||||
)
|
||||
|
||||
self.service_id = res.service_id
|
||||
onion_host = self.service_id + ".onion"
|
||||
onion_host = res.service_id + ".onion"
|
||||
|
||||
# A new private key was generated and is in the Control port response.
|
||||
if self.settings.get("save_private_key"):
|
||||
if not self.settings.get("private_key"):
|
||||
self.settings.set("private_key", res.private_key)
|
||||
# Save the service_id
|
||||
if not mode_settings.get("general", "service_id"):
|
||||
mode_settings.set("general", "service_id", res.service_id)
|
||||
|
||||
# If we were scheduling a future share, register the private key for later re-use
|
||||
if save_scheduled_key:
|
||||
self.scheduled_key = res.private_key
|
||||
else:
|
||||
self.scheduled_key = None
|
||||
# Save the private key and hidservauth string if persistence is enabled
|
||||
if mode_settings.get("persistent", "enabled"):
|
||||
if not mode_settings.get("persistent", "private_key"):
|
||||
mode_settings.set("persistent", "private_key", res.private_key)
|
||||
if mode_settings.get("general", "client_auth") and not mode_settings.get(
|
||||
"persistent", "hidservauth_string"
|
||||
):
|
||||
auth_cookie = list(res.client_auth.values())[0]
|
||||
auth_string = f"HidServAuth {onion_host} {auth_cookie}"
|
||||
mode_settings.set("persistent", "hidservauth_string", auth_string)
|
||||
|
||||
if self.stealth:
|
||||
# Similar to the PrivateKey, the Control port only returns the ClientAuth
|
||||
# in the response if it was responsible for creating the basic_auth password
|
||||
# in the first place.
|
||||
# If we sent the basic_auth (due to a saved hidservauth_string in the settings),
|
||||
# there is no response here, so use the saved value from settings.
|
||||
if self.settings.get("save_private_key"):
|
||||
if self.settings.get("hidservauth_string"):
|
||||
self.auth_string = self.settings.get("hidservauth_string")
|
||||
else:
|
||||
auth_cookie = list(res.client_auth.values())[0]
|
||||
self.auth_string = f"HidServAuth {onion_host} {auth_cookie}"
|
||||
self.settings.set("hidservauth_string", self.auth_string)
|
||||
else:
|
||||
if not self.scheduled_auth_cookie:
|
||||
auth_cookie = list(res.client_auth.values())[0]
|
||||
self.auth_string = f"HidServAuth {onion_host} {auth_cookie}"
|
||||
if save_scheduled_key:
|
||||
# Register the HidServAuth for the scheduled share
|
||||
self.scheduled_auth_cookie = auth_cookie
|
||||
else:
|
||||
self.scheduled_auth_cookie = None
|
||||
else:
|
||||
self.auth_string = (
|
||||
f"HidServAuth {onion_host} {self.scheduled_auth_cookie}"
|
||||
)
|
||||
if not save_scheduled_key:
|
||||
# We've used the scheduled share's HidServAuth. Reset it to None for future shares
|
||||
self.scheduled_auth_cookie = None
|
||||
return onion_host
|
||||
|
||||
if onion_host is not None:
|
||||
self.settings.save()
|
||||
return onion_host
|
||||
else:
|
||||
raise TorErrorProtocolError(strings._("error_tor_protocol_error_unknown"))
|
||||
def stop_onion_service(self, mode_settings):
|
||||
"""
|
||||
Stop a specific onion service
|
||||
"""
|
||||
onion_host = mode_settings.get("general", "service_id")
|
||||
self.common.log("Onion", "stop_onion_service", f"onion host: {onion_host}")
|
||||
|
||||
try:
|
||||
self.c.remove_ephemeral_hidden_service(
|
||||
mode_settings.get("general", "service_id")
|
||||
)
|
||||
except:
|
||||
self.common.log(
|
||||
"Onion", "stop_onion_service", f"failed to remove {onion_host}"
|
||||
)
|
||||
|
||||
def cleanup(self, stop_tor=True):
|
||||
"""
|
||||
@ -725,22 +676,20 @@ class Onion(object):
|
||||
# Cleanup the ephemeral onion services, if we have any
|
||||
try:
|
||||
onions = self.c.list_ephemeral_hidden_services()
|
||||
for onion in onions:
|
||||
for service_id in onions:
|
||||
onion_host = f"{service_id}.onion"
|
||||
try:
|
||||
self.common.log(
|
||||
"Onion", "cleanup", f"trying to remove onion {onion}"
|
||||
"Onion", "cleanup", f"trying to remove onion {onion_host}"
|
||||
)
|
||||
self.c.remove_ephemeral_hidden_service(onion)
|
||||
self.c.remove_ephemeral_hidden_service(service_id)
|
||||
except:
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"cleanup",
|
||||
f"could not remove onion {onion}.. moving on anyway",
|
||||
"Onion", "cleanup", f"failed to remove onion {onion_host}"
|
||||
)
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
self.service_id = None
|
||||
|
||||
if stop_tor:
|
||||
# Stop tor process
|
||||
|
@ -82,12 +82,18 @@ class OnionShare(object):
|
||||
return
|
||||
|
||||
self.onion_host = self.onion.start_onion_service(
|
||||
self.port, await_publication, save_scheduled_key
|
||||
mode_settings, self.port, await_publication, save_scheduled_key
|
||||
)
|
||||
|
||||
if mode_settings.get("general", "client_auth"):
|
||||
self.auth_string = self.onion.auth_string
|
||||
|
||||
def stop_onion_service(self, mode_settings):
|
||||
"""
|
||||
Stop the onion service
|
||||
"""
|
||||
self.onion.stop_onion_service(mode_settings)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Shut everything down and clean up temporary files, etc.
|
||||
|
@ -362,7 +362,7 @@ class Tab(QtWidgets.QWidget):
|
||||
|
||||
def stop_server_finished(self):
|
||||
# When the server stopped, cleanup the ephemeral onion service
|
||||
self.common.gui.onion.cleanup(stop_tor=False)
|
||||
self.get_mode().app.stop_onion_service(self.settings)
|
||||
|
||||
def timer_callback(self):
|
||||
"""
|
||||
|
@ -20,7 +20,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import time
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from onionshare.onion import *
|
||||
from onionshare import strings
|
||||
from onionshare.onion import (
|
||||
TorTooOld,
|
||||
TorErrorInvalidSetting,
|
||||
TorErrorAutomatic,
|
||||
TorErrorSocketPort,
|
||||
TorErrorSocketFile,
|
||||
TorErrorMissingPassword,
|
||||
TorErrorUnreadableCookieFile,
|
||||
TorErrorAuthError,
|
||||
TorErrorProtocolError,
|
||||
BundledTorTimeout,
|
||||
)
|
||||
|
||||
|
||||
class OnionThread(QtCore.QThread):
|
||||
@ -64,7 +76,7 @@ class OnionThread(QtCore.QThread):
|
||||
time.sleep(0.2)
|
||||
self.success_early.emit()
|
||||
# Unregister the onion so we can use it in the next OnionThread
|
||||
self.mode.app.onion.cleanup(False)
|
||||
self.mode.app.start_onion_service(self.mode.settings)
|
||||
else:
|
||||
self.mode.app.start_onion_service(
|
||||
self.mode.settings, await_publication=True
|
||||
|
Loading…
Reference in New Issue
Block a user