diff options
Diffstat (limited to 'src/leap/bitmask/backend.py')
| -rw-r--r-- | src/leap/bitmask/backend.py | 278 | 
1 files changed, 221 insertions, 57 deletions
| 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)) | 
