diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/backend.py | 42 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/eip_status.py | 90 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 25 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/eip_status.ui | 27 | ||||
| -rw-r--r-- | src/leap/bitmask/services/eip/conductor.py | 41 | ||||
| -rw-r--r-- | src/leap/bitmask/services/eip/vpnprocess.py | 21 | 
6 files changed, 173 insertions, 73 deletions
| diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 4ec20be7..3c97c797 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -80,13 +80,13 @@ class ILEAPService(ILEAPComponent):      Interface that every Service needs to implement      """ -    def start(self): +    def start(self, *args, **kwargs):          """          Start the service.          """          pass -    def stop(self): +    def stop(self, *args, **kwargs):          """          Stops the service.          """ @@ -378,9 +378,12 @@ class EIP(object):          if d is not None:              d.cancel() -    def _start_eip(self): +    def _start_eip(self, restart=False):          """          Start EIP + +        :param restart: whether is is a restart. +        :type restart: bool          """          provider_config = self._provider_config          eip_config = eipconfig.EIPConfig() @@ -404,9 +407,10 @@ class EIP(object):          host, port = get_openvpn_management()          self._vpn.start(eipconfig=eip_config,                          providerconfig=provider_config, -                        socket_host=host, socket_port=port) +                        socket_host=host, socket_port=port, +                        restart=restart) -    def start(self): +    def start(self, *args, **kwargs):          """          Start the service.          """ @@ -419,7 +423,7 @@ class EIP(object):              return          try: -            self._start_eip() +            self._start_eip(*args, **kwargs)          except vpnprocess.OpenVPNAlreadyRunning:              signaler.signal(signaler.EIP_OPENVPN_ALREADY_RUNNING)          except vpnprocess.AlienOpenVPNAlreadyRunning: @@ -440,11 +444,6 @@ class EIP(object):              logger.error("Unexpected problem: {0!r}".format(e))          else:              logger.debug('EIP: no errors') -            # TODO: are we connected here? -            # kali -- no, we are not! CONNECTED should be passed only -            # by the vpn observer. Currently handled by the state updater -            # in eip_status -            #signaler.signal(signaler.EIP_CONNECTED)      def _do_stop(self, shutdown=False, restart=False):          """ @@ -539,6 +538,12 @@ class EIP(object):              self._signaler.signal(self._signaler.EIP_GET_INITIALIZED_PROVIDERS,                                    filtered_domains) +    def tear_fw_down(self): +        """ +        Tear the firewall down. +        """ +        self._vpn.tear_down_firewall() +      def get_gateways_list(self, domain):          """          Signal a list of gateways for the given provider. @@ -1181,6 +1186,7 @@ class Signaler(QtCore.QObject):      eip_state_changed = QtCore.Signal(dict)      eip_status_changed = QtCore.Signal(dict)      eip_process_finished = QtCore.Signal(int) +    eip_tear_fw_down = QtCore.Signal(object)      # signals whether the needed files to start EIP exist or not      eip_can_start = QtCore.Signal(object) @@ -1282,6 +1288,7 @@ class Signaler(QtCore.QObject):      EIP_STATE_CHANGED = "eip_state_changed"      EIP_STATUS_CHANGED = "eip_status_changed"      EIP_PROCESS_FINISHED = "eip_process_finished" +    EIP_TEAR_FW_DOWN = "eip_tear_fw_down"      EIP_CAN_START = "eip_can_start"      EIP_CANNOT_START = "eip_cannot_start" @@ -1717,7 +1724,7 @@ class Backend(object):          """          self._call_queue.put(("eip", "cancel_setup_eip", None)) -    def eip_start(self): +    def eip_start(self, restart=False):          """          Start the EIP service. @@ -1738,8 +1745,11 @@ class Backend(object):              eip_state_changed -> str              eip_status_changed -> tuple of str (download, upload)              eip_vpn_launcher_exception + +        :param restart: whether is is a restart. +        :type restart: bool          """ -        self._call_queue.put(("eip", "start", None)) +        self._call_queue.put(("eip", "start", None, restart))      def eip_stop(self, shutdown=False, restart=False, failed=False):          """ @@ -1805,6 +1815,12 @@ class Backend(object):          self._call_queue.put(("eip", "can_start",                                None, domain)) +    def tear_fw_down(self): +        """ +        Signal the need to tear the fw down. +        """ +        self._call_queue.put(("eip", "tear_fw_down", None)) +      def user_login(self, provider, username, password):          """          Execute the whole authentication process for a user diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index e7795083..280ce79e 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -31,6 +31,7 @@ from leap.common.check import leap_assert_type  from ui_eip_status import Ui_EIPStatus +QtDelayedCall = QtCore.QTimer.singleShot  logger = logging.getLogger(__name__) @@ -66,6 +67,9 @@ class EIPStatusWidget(QtGui.QWidget):          self._service_name = get_service_display_name(EIP_SERVICE)          self.ui.eip_bandwidth.hide() +        self.hide_fw_down_button() +        self.ui.btnFwDown.clicked.connect( +            self._on_fw_down_button_clicked)          # Set the EIP status icons          self.CONNECTING_ICON = None @@ -81,6 +85,7 @@ class EIPStatusWidget(QtGui.QWidget):          self._provider = ""          self.is_restart = False +        self.is_cold_start = True          # Action for the systray          self._eip_disabled_action = QtGui.QAction( @@ -109,8 +114,12 @@ class EIPStatusWidget(QtGui.QWidget):          signaler.eip_state_changed.connect(self.update_vpn_state)          signaler.eip_status_changed.connect(self.update_vpn_status) -        signaler.eip_network_unreachable.connect( -            self._on_eip_network_unreachable) + +        # XXX we cannot connect this signal now because +        # it interferes with the proper notifications during restarts +        # without available network. +        #signaler.eip_network_unreachable.connect( +            #self._on_eip_network_unreachable)      def _make_status_clickable(self):          """ @@ -273,6 +282,8 @@ class EIPStatusWidget(QtGui.QWidget):          Disables the start/stop button.          """          self.set_startstop_enabled(False) +        msg = self.tr("Encrypted Internet is starting") +        self.set_eip_message(msg)      @QtCore.Slot()      def disable_eip_start(self): @@ -314,10 +325,19 @@ class EIPStatusWidget(QtGui.QWidget):          if self.isVisible():              self._eip_status_menu.menuAction().setVisible(True) -    # XXX disable (later) -------------------------- +    def set_eip_message(self, message): +        """ +        Set the EIP Widget main message. + +        :param message: the message to set in the widget +        :type message: str or unicode +        """ +        self.ui.lblEIPMessage.setText(message) +        self.ui.lblEIPMessage.show() +      def set_eip_status(self, status, error=False):          """ -        Sets the status label at the VPN stage to status +        Set the status label at the VPN stage to status.          :param status: status message          :type status: str or unicode @@ -362,6 +382,31 @@ class EIPStatusWidget(QtGui.QWidget):          self.ui.btnEipStartStop.clicked.connect(              self.eipconnection.qtsigs.do_connect_signal) +    def hide_fw_down_button(self): +        """ +        Hide firewall-down button. +        """ +        self.ui.btnFwDown.hide() + +    def show_fw_down_button(self): +        """ +        Enable firewall-down button. +        """ +        self.ui.btnFwDown.show() + +    def _on_fw_down_button_clicked(self): +        """ +        Raise a signal for tearing down the firewall, and hide the button +        afterwards. +        """ +        self.eip_conductor._backend.tear_fw_down() +        QtDelayedCall(50, self.hide_fw_down_button) + +        # XXX do actual check +        msg = "Traffic is being routed in the clear." +        self.set_eip_message(msg) +        self.set_eip_status("") +      @QtCore.Slot(dict)      def eip_stopped(self, restart=False, failed=False):          """ @@ -382,7 +427,7 @@ class EIPStatusWidget(QtGui.QWidget):          clear_traffic = self.tr("Traffic is being routed in the clear.")          unreachable_net = self.tr("Network is unreachable.") -        failed_msg = self.tr("Cannot start Encrypted Proxy.") +        failed_msg = self.tr("Cannot start Encrypted Internet")          if restart:              msg = unreachable_net @@ -390,18 +435,19 @@ class EIPStatusWidget(QtGui.QWidget):              msg = failed_msg          else:              msg = clear_traffic -        self.ui.lblEIPMessage.setText(msg) +        self.set_eip_message(msg)          self.ui.lblEIPStatus.show() +        self.show()      def eip_failed_to_restart(self):          """          Update EIP messages.          """ -        msg = self.tr("Could not restart Encrypted Proxy") +        msg = self.tr("Could not restart Encrypted Internet")          self.ui.lblEIPMessage.setText(msg)          self.ui.lblEIPStatus.show() - -        self.set_eip_status(self.tr("You can start the service manually.")) +        self.set_eip_status(self.tr("You can launch the service manually.")) +        self.show_fw_down_button()      @QtCore.Slot(dict)      def update_vpn_status(self, data=None): @@ -462,10 +508,18 @@ class EIPStatusWidget(QtGui.QWidget):              # XXX should be handled by the state machine too.              # --- is this currently being sent?              self.eipconnection.qtsigs.connected_signal.emit() +            self._on_eip_connected() +            self.is_cold_start = False          # XXX should lookup vpn_state map in EIPConnection          elif vpn_state == "AUTH":              self.set_eip_status(self.tr("Authenticating...")) +            # we wipe up any previous error info in the EIP message +            # when we detect vpn authentication is happening +            msg = self.tr("Encrypted Internet is starting") +            self.set_eip_message(msg) +            # on the first-run path, we hadn't showed the button yet. +            self.eip_button.show()          elif vpn_state == "GET_CONFIG":              self.set_eip_status(self.tr("Retrieving configuration..."))          elif vpn_state == "WAIT": @@ -478,10 +532,10 @@ class EIPStatusWidget(QtGui.QWidget):              # Put the following calls in Qt's event queue, otherwise              # the UI won't update properly              #self.send_disconnect_signal() -            QtCore.QTimer.singleShot( +            QtDelayedCall(                  0, self.eipconnection.qtsigns.do_disconnect_signal.emit)              msg = self.tr("Unable to start VPN, it's already running.") -            QtCore.QTimer.singleShot(0, partial(self.set_eip_status, msg)) +            QtDelayedCall(0, partial(self.set_eip_status, msg))          else:              self.set_eip_status(vpn_state) @@ -523,8 +577,10 @@ class EIPStatusWidget(QtGui.QWidget):      def set_provider(self, provider):          self._provider = provider +          self.ui.lblEIPMessage.setText( -            self.tr("Route traffic through: {0}").format(self._provider)) +            self.tr("Routing traffic through: <b>{0}</b>").format( +                provider))      def aborted(self):          """ @@ -544,6 +600,9 @@ class EIPStatusWidget(QtGui.QWidget):          TRIGGERS:              Signaler.eip_connection_aborted          """ +        # TODO this name is very misleading, since there's a generic signal +        # that's called connection_aborted / connection_died... +        # should rename to something more specific about missing config.          logger.error("Tried to start EIP but cannot find any "                       "available provider!") @@ -619,6 +678,13 @@ class EIPStatusWidget(QtGui.QWidget):          self.aborted() +    def _on_eip_connected(self): +        """ +        Reconnect the disconnecting signal when we are just connected, +        so that we restore the disconnecting -> stop behaviour. +        """ +        self.eip_conductor.reconnect_stop_signal() +      @QtCore.Slot()      def _on_eip_network_unreachable(self):          """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index e7406106..a3b81fde 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -158,8 +158,6 @@ class MainWindow(QtGui.QMainWindow):          self._eip_conductor.connect_signals()          self._eip_conductor.qtsigs.connected_signal.connect(              self._on_eip_connection_connected) -        self._eip_conductor.qtsigs.disconnected_signal.connect( -            self._on_eip_connection_disconnected)          self._eip_conductor.qtsigs.connected_signal.connect(              self._maybe_run_soledad_setup_checks) @@ -1448,6 +1446,7 @@ class MainWindow(QtGui.QMainWindow):          Enables the EIP start action in the systray menu.          """          self._action_eip_startstop.setEnabled(True) +        self._eip_status.enable_eip_start()      @QtCore.Slot()      def _on_eip_connection_connected(self): @@ -1468,25 +1467,6 @@ class MainWindow(QtGui.QMainWindow):          # check for connectivity          self._check_name_resolution(domain) -    @QtCore.Slot() -    def _on_eip_connection_disconnected(self): -        """ -        TRIGGERS: -            self._eip_conductor.qtsigs.disconnected_signal - -        Workaround for updating the eip_status widget with -        the provider when the eip connection disconnects. -        """ -        # TODO -        # We should move this to the conductor<->widget interface. -        # To do that, we need to subscribe to logged_user, -        # for example by using the observer pattern or a proxy object. -        user = self._logged_user -        if user: -            domain = self._login_widget.get_selected_provider() -            full_user_id = make_address(user, domain) -            self._eip_status.set_provider(full_user_id) -      def _check_name_resolution(self, domain):          # FIXME this has to be moved to backend !!!          # Should move to netchecks module. @@ -1563,7 +1543,10 @@ class MainWindow(QtGui.QMainWindow):              should_start = self._provides_eip_and_enabled()          if should_start and not self._already_started_eip: +            if self._eip_status.is_cold_start: +                self._backend.tear_fw_down()              # XXX this should be handled by the state machine. +            self._enable_eip_start_action()              self._eip_status.set_eip_status(                  self.tr("Starting..."))              self._eip_status.eip_button.setEnabled(False) diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui index 64821ad6..01d6b371 100644 --- a/src/leap/bitmask/gui/ui/eip_status.ui +++ b/src/leap/bitmask/gui/ui/eip_status.ui @@ -28,7 +28,7 @@       <property name="verticalSpacing">        <number>0</number>       </property> -     <item row="0" column="2"> +     <item row="0" column="3">        <widget class="QPushButton" name="btnEipStartStop">         <property name="text">          <string>Turn On</string> @@ -86,7 +86,7 @@         </property>        </widget>       </item> -     <item row="0" column="3"> +     <item row="0" column="4">        <widget class="QLabel" name="lblVPNStatusIcon">         <property name="maximumSize">          <size> @@ -118,7 +118,7 @@         </property>        </spacer>       </item> -     <item row="2" column="1" colspan="3"> +     <item row="2" column="1" colspan="4">        <widget class="QWidget" name="eip_bandwidth" native="true">         <layout class="QHBoxLayout" name="horizontalLayout">          <property name="spacing"> @@ -161,12 +161,13 @@              <property name="text">               <string>0.0 KB/s</string>              </property> +            <property name="icon"> +             <iconset resource="../../../../../data/resources/mainwindow.qrc"> +              <normaloff>:/images/black/32/arrow-down.png</normaloff>:/images/black/32/arrow-down.png</iconset> +            </property>              <property name="flat">               <bool>true</bool>              </property> -            <property name="icon"> -             <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/down-arrow.png</pixmap> -            </property>             </widget>            </item>            <item> @@ -211,12 +212,13 @@              <property name="text">               <string>0.0 KB/s</string>              </property> +            <property name="icon"> +             <iconset resource="../../../../../data/resources/mainwindow.qrc"> +              <normaloff>:/images/black/32/arrow-up.png</normaloff>:/images/black/32/arrow-up.png</iconset> +            </property>              <property name="flat">               <bool>true</bool>              </property> -            <property name="icon"> -             <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/up-arrow.png</pixmap> -            </property>             </widget>            </item>            <item> @@ -237,6 +239,13 @@         </layout>        </widget>       </item> +     <item row="0" column="2"> +      <widget class="QPushButton" name="btnFwDown"> +       <property name="text"> +        <string>Allow unencrypted traffic</string> +       </property> +      </widget> +     </item>      </layout>     </item>    </layout> diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index 53b1fde9..cde53631 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -114,13 +114,19 @@ class EIPConductor(object):          """          self.qtsigs.do_connect_signal.emit() +    def tear_fw_down(self): +        """ +        Tear the firewall down. +        """ +        self._backend.tear_fw_down() +      @QtCore.Slot()      def _start_eip(self):          """          Starts EIP.          """ -        # FIXME --- pass is_restart parameter to here ??? -        is_restart = self._eip_status and self._eip_status.is_restart +        st = self._eip_status +        is_restart = st and st.is_restart          def reconnect():              self.qtsigs.disconnecting_signal.connect(self._stop_eip) @@ -130,6 +136,7 @@ class EIPConductor(object):          else:              self._eip_status.eip_pre_up()          self.user_stopped_eip = False +        self._eip_status.hide_fw_down_button()          # Until we set an option in the preferences window, we'll assume that          # by default we try to autostart. If we switch it off manually, it @@ -138,7 +145,19 @@ class EIPConductor(object):          self._eip_status.is_restart = False          # DO the backend call! -        self._backend.eip_start() +        self._backend.eip_start(restart=is_restart) + +    def reconnect_stop_signal(self): +        """ +        Restore the original behaviour associated with the disconnecting +        signal, this is, trigger a normal stop, and not a restart one. +        """ + +        def do_stop(*args): +            self._stop_eip(restart=False) + +        self.qtsigs.disconnecting_signal.disconnect() +        self.qtsigs.disconnecting_signal.connect(do_stop)      @QtCore.Slot()      def _stop_eip(self, restart=False, failed=False): @@ -178,10 +197,6 @@ class EIPConductor(object):          def do_stop(*args):              self._stop_eip(restart=False) -        def reconnect_stop_signal(): -            self.qtsigs.disconnecting_signal.disconnect() -            self.qtsigs.disconnecting_signal.connect(do_stop) -          if restart:              # we bypass the on_eip_disconnected here              plug_restart_on_disconnected() @@ -209,7 +224,7 @@ class EIPConductor(object):          # XXX needed?          if restart: -            QtDelayedCall(3000, reconnect_stop_signal) +            QtDelayedCall(2000, self.reconnect_stop_signal)      @QtCore.Slot()      def _do_eip_restart(self): @@ -282,8 +297,11 @@ class EIPConductor(object):              signal = self.qtsigs.connection_aborted_signal              self._backend.eip_terminate() -        # XXX FIXME --- check exitcode is != 0 really -        if exitCode != 0 and not self.user_stopped_eip: +        # XXX FIXME --- check exitcode is != 0 really. +        # bitmask-root is masking the exitcode, so we might need +        # to fix it on that side. +        #if exitCode != 0 and not self.user_stopped_eip: +        if not self.user_stopped_eip:              eip_status_label = self._eip_status.tr(                  "{0} finished in an unexpected manner!")              eip_status_label = eip_status_label.format(self.eip_name) @@ -292,6 +310,9 @@ class EIPConductor(object):              self._eip_status.set_eip_status(eip_status_label,                                              error=True)              signal = self.qtsigs.connection_died_signal +            self._eip_status.show_fw_down_button() +            msg = self._eip_status.tr("Outgoing traffic is blocked") +            self._eip_status.set_eip_message(msg)          if exitCode == 0 and IS_MAC:              # XXX remove this warning after I fix cocoasudo. diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index 1de4a851..f56d464e 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -183,6 +183,8 @@ class VPN(object):          kwargs['openvpn_verb'] = self._openvpn_verb          kwargs['signaler'] = self._signaler +        restart = kwargs.pop('restart', False) +          # start the main vpn subprocess          vpnproc = VPNProcess(*args, **kwargs) @@ -193,8 +195,9 @@ class VPN(object):          # we try to bring the firewall up          if IS_LINUX:              gateways = vpnproc.getGateways() -            firewall_up = self._launch_firewall(gateways) -            if not firewall_up: +            firewall_up = self._launch_firewall(gateways, +                                                restart=restart) +            if not restart and not firewall_up:                  logger.error("Could not bring firewall up, "                               "aborting openvpn launch.")                  return @@ -216,7 +219,7 @@ class VPN(object):          self._pollers.extend(poll_list)          self._start_pollers() -    def _launch_firewall(self, gateways): +    def _launch_firewall(self, gateways, restart=False):          """          Launch the firewall using the privileged wrapper. @@ -231,8 +234,10 @@ class VPN(object):          # XXX could check that the iptables rules are in place.          BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT -        exitCode = subprocess.call(["pkexec", -                                    BM_ROOT, "firewall", "start"] + gateways) +        cmd = ["pkexec", BM_ROOT, "firewall", "start"] +        if restart: +            cmd.append("restart") +        exitCode = subprocess.call(cmd + gateways)          return True if exitCode is 0 else False      def is_fw_down(self): @@ -246,7 +251,7 @@ class VPN(object):          fw_is_down = lambda: commands.getstatusoutput(fw_up_cmd)[0] == 256          return fw_is_down() -    def _tear_down_firewall(self): +    def tear_down_firewall(self):          """          Tear the firewall down using the privileged wrapper.          """ @@ -270,7 +275,7 @@ class VPN(object):                  # we try to tear the firewall down                  if IS_LINUX and self._user_stopped: -                    firewall_down = self._tear_down_firewall() +                    firewall_down = self.tear_down_firewall()                      if firewall_down:                          logger.debug("Firewall down")                      else: @@ -333,7 +338,7 @@ class VPN(object):                  self.TERMINATE_WAIT, self._kill_if_left_alive)              if IS_LINUX and self._user_stopped: -                firewall_down = self._tear_down_firewall() +                firewall_down = self.tear_down_firewall()                  if firewall_down:                      logger.debug("Firewall down")                  else: | 
