From a73c432400eb6a614706fc615a505ed78d4031e3 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 27 Mar 2014 17:16:04 -0300 Subject: Move EIP to the backend. - Add backend eip management - Connect gui with new eip signals - remove old unused code - remove Qt dependency from backend services. - use Signaler to emit status/state changes from openvpn --- src/leap/bitmask/backend.py | 278 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 221 insertions(+), 57 deletions(-) (limited to 'src/leap/bitmask/backend.py') diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 00a399ee..591b5da5 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -33,8 +33,13 @@ from leap.bitmask.crypto.srpauth import SRPAuth from leap.bitmask.crypto.srpregister import SRPRegister from leap.bitmask.provider import get_provider_path from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper +from leap.bitmask.services.eip import eipconfig +from leap.bitmask.services.eip import get_openvpn_management from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper +from leap.bitmask.services.eip import vpnlauncher, vpnprocess +from leap.bitmask.services.eip import linuxvpnlauncher, darwinvpnlauncher + # Frontend side from PySide import QtCore @@ -248,6 +253,140 @@ class Register(object): logger.error("Could not load provider configuration.") +class EIP(object): + """ + Interfaces with setup and launch of EIP + """ + + zope.interface.implements(ILEAPService) + + def __init__(self, signaler=None): + """ + Constructor for the EIP component + + :param signaler: Object in charge of handling communication + back to the frontend + :type signaler: Signaler + """ + object.__init__(self) + self.key = "eip" + self._signaler = signaler + self._eip_bootstrapper = EIPBootstrapper(signaler) + self._eip_setup_defer = None + self._provider_config = ProviderConfig() + + self._vpn = vpnprocess.VPN(signaler=signaler) + + def setup_eip(self, domain): + """ + Initiates the setup for a provider + + :param domain: URL for the provider + :type domain: unicode + + :returns: the defer for the operation running in a thread. + :rtype: twisted.internet.defer.Deferred + """ + config = self._provider_config + if get_provider_config(config, domain): + eb = self._eip_bootstrapper + d = eb.run_eip_setup_checks(self._provider_config, + download_if_needed=True) + self._eip_setup_defer = d + return d + else: + raise Exception("No provider setup loaded") + + def cancel_setup_eip(self): + """ + Cancel the ongoing setup eip defer (if any). + """ + d = self._eip_setup_defer + if d is not None: + d.cancel() + + def _start_eip(self): + """ + Starts EIP + """ + provider_config = self._provider_config + eip_config = eipconfig.EIPConfig() + domain = provider_config.get_domain() + + loaded = eipconfig.load_eipconfig_if_needed( + provider_config, eip_config, domain) + + if not loaded: + if self._signaler is not None: + self._signaler.signal(self._signaler.EIP_CONNECTION_ABORTED) + logger.error("Tried to start EIP but cannot find any " + "available provider!") + return + + host, port = get_openvpn_management() + self._vpn.start(eipconfig=eip_config, + providerconfig=provider_config, + socket_host=host, socket_port=port) + + def start(self): + """ + Starts the service. + """ + signaler = self._signaler + + if not self._provider_config.loaded(): + # This means that the user didn't call setup_eip first. + self._signaler.signal(signaler.BACKEND_BAD_CALL) + return + + try: + self._start_eip() + except vpnprocess.OpenVPNAlreadyRunning: + signaler.signal(signaler.EIP_OPEN_VPN_ALREADY_RUNNING) + except vpnprocess.AlienOpenVPNAlreadyRunning: + signaler.signal(signaler.EIP_ALIEN_OPEN_VPN_ALREADY_RUNNING) + except vpnlauncher.OpenVPNNotFoundException: + signaler.signal(signaler.EIP_OPEN_VPN_NOT_FOUND_ERROR) + except vpnlauncher.VPNLauncherException: + # TODO: this seems to be used for 'gateway not found' only. + # see vpnlauncher.py + signaler.signal(signaler.EIP_VPN_LAUNCHER_EXCEPTION) + except linuxvpnlauncher.EIPNoPolkitAuthAgentAvailable: + signaler.signal(signaler.EIP_NO_POLKIT_AGENT_ERROR) + except linuxvpnlauncher.EIPNoPkexecAvailable: + signaler.signal(signaler.EIP_NO_PKEXEC_ERROR) + except darwinvpnlauncher.EIPNoTunKextLoaded: + signaler.signal(signaler.EIP_NO_TUN_KEXT_ERROR) + except Exception as e: + logger.error("Unexpected problem: {0!r}".format(e)) + else: + # TODO: are we connected here? + signaler.signal(signaler.EIP_CONNECTED) + + def stop(self, shutdown=False): + """ + Stops the service. + """ + self._vpn.terminate(shutdown) + + def terminate(self): + """ + Terminates the service, not necessarily in a nice way. + """ + self._vpn.killit() + + def status(self): + """ + Returns a json object with the current status for the service. + + :rtype: object (list, str, dict) + """ + # XXX: Use a namedtuple or a specific object instead of a json + # object, since parsing it will be problematic otherwise. + # It has to be something easily serializable though. + pass + + class Authenticate(object): """ Interfaces with setup and bootstrapping operations for a provider @@ -355,57 +494,6 @@ class Authenticate(object): self._signaler.signal(signal) -class EIP(object): - """ - Interfaces with setup and launch of EIP - """ - - zope.interface.implements(ILEAPComponent) - - def __init__(self, signaler=None): - """ - Constructor for the EIP component - - :param signaler: Object in charge of handling communication - back to the frontend - :type signaler: Signaler - """ - object.__init__(self) - self.key = "eip" - self._eip_bootstrapper = EIPBootstrapper(signaler) - self._eip_setup_defer = None - self._provider_config = ProviderConfig() - - def setup_eip(self, domain): - """ - Initiates the setup for a provider - - :param domain: URL for the provider - :type domain: unicode - - :returns: the defer for the operation running in a thread. - :rtype: twisted.internet.defer.Deferred - """ - config = self._provider_config - if get_provider_config(config, domain): - log.msg("") - eb = self._eip_bootstrapper - d = eb.run_eip_setup_checks(self._provider_config, - download_if_needed=True) - self._eip_setup_defer = d - return d - else: - raise Exception("No provider setup loaded") - - def cancel_setup_eip(self): - """ - Cancel the ongoing setup eip defer (if any). - """ - d = self._eip_setup_defer - if d is not None: - d.cancel() - - class Signaler(QtCore.QObject): """ Signaler object, handles converting string commands to Qt signals. @@ -437,7 +525,7 @@ class Signaler(QtCore.QObject): srp_registration_failed = QtCore.Signal(object) srp_registration_taken = QtCore.Signal(object) - # Signals for EIP + # Signals for EIP bootstrapping eip_download_config = QtCore.Signal(object) eip_download_client_certificate = QtCore.Signal(object) @@ -458,6 +546,35 @@ class Signaler(QtCore.QObject): srp_status_logged_in = QtCore.Signal(object) srp_status_not_logged_in = QtCore.Signal(object) + # Signals for EIP + eip_connected = QtCore.Signal(object) + eip_disconnected = QtCore.Signal(object) + eip_connection_died = QtCore.Signal(object) + eip_connection_aborted = QtCore.Signal(object) + + # EIP problems + eip_no_polkit_agent_error = QtCore.Signal(object) + eip_no_tun_kext_error = QtCore.Signal(object) + eip_no_pkexec_error = QtCore.Signal(object) + eip_openvpn_not_found_error = QtCore.Signal(object) + eip_openvpn_already_running = QtCore.Signal(object) + eip_alien_openvpn_already_running = QtCore.Signal(object) + eip_vpn_launcher_exception = QtCore.Signal(object) + + # signals from parsing openvpn output + eip_network_unreachable = QtCore.Signal(object) + eip_process_restart_tls = QtCore.Signal(object) + eip_process_restart_ping = QtCore.Signal(object) + + # signals from vpnprocess.py + eip_state_changed = QtCore.Signal(dict) + eip_status_changed = QtCore.Signal(dict) + eip_process_finished = QtCore.Signal(int) + + # This signal is used to warn the backend user that is doing something + # wrong + backend_bad_call = QtCore.Signal(object) + #################### # These will exist both in the backend AND the front end. # The frontend might choose to not "interpret" all the signals @@ -497,11 +614,27 @@ class Signaler(QtCore.QObject): EIP_DOWNLOAD_CLIENT_CERTIFICATE = "eip_download_client_certificate" EIP_CANCELLED_SETUP = "eip_cancelled_setup" - # TODO change the name of "download_config" signal to - # something less confusing (config_ready maybe) - EIP_DOWNLOAD_CONFIG = "eip_download_config" - EIP_DOWNLOAD_CLIENT_CERTIFICATE = "eip_download_client_certificate" - EIP_CANCELLED_SETUP = "eip_cancelled_setup" + EIP_CONNECTED = "eip_connected" + EIP_DISCONNECTED = "eip_disconnected" + EIP_CONNECTION_DIED = "eip_connection_died" + EIP_CONNECTION_ABORTED = "eip_connection_aborted" + EIP_NO_POLKIT_AGENT_ERROR = "eip_no_polkit_agent_error" + EIP_NO_TUN_KEXT_ERROR = "eip_no_tun_kext_error" + EIP_NO_PKEXEC_ERROR = "eip_no_pkexec_error" + EIP_OPENVPN_NOT_FOUND_ERROR = "eip_openvpn_not_found_error" + EIP_OPENVPN_ALREADY_RUNNING = "eip_openvpn_already_running" + EIP_ALIEN_OPENVPN_ALREADY_RUNNING = "eip_alien_openvpn_already_running" + EIP_VPN_LAUNCHER_EXCEPTION = "eip_vpn_launcher_exception" + + EIP_NETWORK_UNREACHABLE = "eip_network_unreachable" + EIP_PROCESS_RESTART_TLS = "eip_process_restart_tls" + EIP_PROCESS_RESTART_PING = "eip_process_restart_ping" + + EIP_STATE_CHANGED = "eip_state_changed" + EIP_STATUS_CHANGED = "eip_status_changed" + EIP_PROCESS_FINISHED = "eip_process_finished" + + BACKEND_BAD_CALL = "backend_bad_call" def __init__(self): """ @@ -530,6 +663,26 @@ class Signaler(QtCore.QObject): self.EIP_DOWNLOAD_CLIENT_CERTIFICATE, self.EIP_CANCELLED_SETUP, + self.EIP_CONNECTED, + self.EIP_DISCONNECTED, + self.EIP_CONNECTION_DIED, + self.EIP_CONNECTION_ABORTED, + self.EIP_NO_POLKIT_AGENT_ERROR, + self.EIP_NO_TUN_KEXT_ERROR, + self.EIP_NO_PKEXEC_ERROR, + self.EIP_OPENVPN_NOT_FOUND_ERROR, + self.EIP_OPENVPN_ALREADY_RUNNING, + self.EIP_ALIEN_OPENVPN_ALREADY_RUNNING, + self.EIP_VPN_LAUNCHER_EXCEPTION, + + self.EIP_NETWORK_UNREACHABLE, + self.EIP_PROCESS_RESTART_TLS, + self.EIP_PROCESS_RESTART_PING, + + self.EIP_STATE_CHANGED, + self.EIP_STATUS_CHANGED, + self.EIP_PROCESS_FINISHED, + self.SRP_AUTH_OK, self.SRP_AUTH_ERROR, self.SRP_AUTH_SERVER_ERROR, @@ -543,6 +696,8 @@ class Signaler(QtCore.QObject): self.SRP_NOT_LOGGED_IN_ERROR, self.SRP_STATUS_LOGGED_IN, self.SRP_STATUS_NOT_LOGGED_IN, + + self.BACKEND_BAD_CALL, ] for sig in signals: @@ -728,6 +883,15 @@ class Backend(object): def cancel_setup_eip(self): self._call_queue.put(("eip", "cancel_setup_eip", None)) + def start_eip(self): + self._call_queue.put(("eip", "start", None)) + + def stop_eip(self, shutdown=False): + self._call_queue.put(("eip", "stop", None, shutdown)) + + def terminate_eip(self): + self._call_queue.put(("eip", "terminate", None)) + def login(self, provider, username, password): self._call_queue.put(("authenticate", "login", None, provider, username, password)) -- cgit v1.2.3