diff options
Diffstat (limited to 'src/leap/bitmask/services')
-rw-r--r-- | src/leap/bitmask/services/eip/conductor.py | 6 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/eipconfig.py | 43 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/linuxvpnlauncher.py | 99 | ||||
-rw-r--r-- | src/leap/bitmask/services/eip/vpnprocess.py | 20 | ||||
-rw-r--r-- | src/leap/bitmask/services/mail/conductor.py | 19 | ||||
-rw-r--r-- | src/leap/bitmask/services/mail/emailfirewall.py | 115 | ||||
-rw-r--r-- | src/leap/bitmask/services/mail/smtpbootstrapper.py | 2 |
7 files changed, 180 insertions, 124 deletions
diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index 0ee56628..01dd7449 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -124,6 +124,12 @@ class EIPConductor(object): """ self._backend.tear_fw_down() + def terminate(self): + """ + Turn off VPN + """ + self.qtsigs.do_disconnect_signal.emit() + @QtCore.Slot() def _start_eip(self): """ diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py index 37c0c8ae..5b51d12e 100644 --- a/src/leap/bitmask/services/eip/eipconfig.py +++ b/src/leap/bitmask/services/eip/eipconfig.py @@ -113,39 +113,38 @@ class VPNGatewaySelector(object): """ Return the existing gateways, sorted by timezone proximity. - :rtype: list of tuples (location, ip) - (str, IPv4Address or IPv6Address object) + :rtype: list of tuples (label, ip, country_code) + (str, IPv4Address or IPv6Address object, str) """ gateways_timezones = [] locations = self._eipconfig.get_locations() gateways = self._eipconfig.get_gateways() for idx, gateway in enumerate(gateways): - gateway_location = gateway.get('location') - gateway_distance = 99 # if hasn't location -> should go last - - if gateway_location is not None: - timezone = locations[gateway['location']]['timezone'] - gateway_name = locations[gateway['location']].get('name', None) - if gateway_name is not None: - gateway_location = gateway_name - - gw_offset = int(timezone) - if gw_offset in self.equivalent_timezones: - gw_offset = self.equivalent_timezones[gw_offset] - - gateway_distance = self._get_timezone_distance(gw_offset) + distance = 99 # if hasn't location -> should go last + location = locations.get(gateway.get('location')) + label = gateway.get('location', 'Unknown') + country = 'XX' + if location is not None: + country = location.get('country_code', 'XX') + label = location.get('name', label) + timezone = location.get('timezone') + if timezone is not None: + offset = int(timezone) + if offset in self.equivalent_timezones: + offset = self.equivalent_timezones[offset] + distance = self._get_timezone_distance(offset) ip = self._eipconfig.get_gateway_ip(idx) - gateways_timezones.append((ip, gateway_distance, gateway_location)) + gateways_timezones.append((ip, distance, label, country)) gateways_timezones = sorted(gateways_timezones, key=lambda gw: gw[1]) - gateways = [] - for ip, distance, location in gateways_timezones: - gateways.append((location, ip)) + result = [] + for ip, distance, label, country in gateways_timezones: + result.append((label, ip, country)) - return gateways + return result def get_gateways(self): """ @@ -153,7 +152,7 @@ class VPNGatewaySelector(object): :rtype: list of IPv4Address or IPv6Address object. """ - gateways = [ip for location, ip in self.get_gateways_list()][:4] + gateways = [gateway[1] for gateway in self.get_gateways_list()][:4] return gateways def get_gateways_country_code(self): diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index b6e47f25..a3ab408b 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -20,17 +20,15 @@ Linux VPN launcher implementation. import commands import logging import os -import subprocess import sys -import time from leap.bitmask.config import flags from leap.bitmask.util.privilege_policies import LinuxPolicyChecker -from leap.common.files import which +from leap.bitmask.util.privilege_policies import NoPolkitAuthAgentAvailable +from leap.bitmask.util.privilege_policies import NoPkexecAvailable from leap.bitmask.services.eip.vpnlauncher import VPNLauncher from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException from leap.bitmask.util import get_path_prefix, force_eval -from leap.common.check import leap_assert from leap.bitmask.util import first logger = logging.getLogger(__name__) @@ -46,66 +44,11 @@ class EIPNoPkexecAvailable(VPNLauncherException): pass -def _is_pkexec_in_system(): - """ - Checks the existence of the pkexec binary in system. - """ - pkexec_path = which('pkexec') - if len(pkexec_path) == 0: - return False - return True - - -def _is_auth_agent_running(): - """ - Checks if a polkit daemon is running. - - :return: True if it's running, False if it's not. - :rtype: boolean - """ - # Note that gnome-shell does not uses a separate process for the - # polkit-agent, it uses a polkit-agent within its own process so we can't - # ps-grep a polkit process, we can ps-grep gnome-shell itself. - - # the [x] thing is to avoid grep match itself - polkit_options = [ - 'ps aux | grep "polkit-[g]nome-authentication-agent-1"', - 'ps aux | grep "polkit-[k]de-authentication-agent-1"', - 'ps aux | grep "polkit-[m]ate-authentication-agent-1"', - 'ps aux | grep "[l]xpolkit"', - 'ps aux | grep "[g]nome-shell"', - 'ps aux | grep "[f]ingerprint-polkit-agent"', - ] - is_running = [commands.getoutput(cmd) for cmd in polkit_options] - - return any(is_running) - - -def _try_to_launch_agent(): - """ - Tries to launch a polkit daemon. - """ - env = None - if flags.STANDALONE: - env = {"PYTHONPATH": os.path.abspath('../../../../lib/')} - try: - # We need to quote the command because subprocess call - # will do "sh -c 'foo'", so if we do not quoute it we'll end - # up with a invocation to the python interpreter. And that - # is bad. - logger.debug("Trying to launch polkit agent") - subprocess.call(["python -m leap.bitmask.util.polkit_agent"], - shell=True, env=env) - except Exception as exc: - logger.exception(exc) - - SYSTEM_CONFIG = "/etc/leap" leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f) class LinuxVPNLauncher(VPNLauncher): - PKEXEC_BIN = 'pkexec' # The following classes depend on force_eval to be called against # the classes, to get the evaluation of the standalone flag on runtine. @@ -130,36 +73,6 @@ class LinuxVPNLauncher(VPNLauncher): OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH) @classmethod - def maybe_pkexec(kls): - """ - Checks whether pkexec is available in the system, and - returns the path if found. - - Might raise: - EIPNoPkexecAvailable, - EIPNoPolkitAuthAgentAvailable. - - :returns: a list of the paths where pkexec is to be found - :rtype: list - """ - if _is_pkexec_in_system(): - if not _is_auth_agent_running(): - _try_to_launch_agent() - time.sleep(2) - if _is_auth_agent_running(): - pkexec_possibilities = which(kls.PKEXEC_BIN) - leap_assert(len(pkexec_possibilities) > 0, - "We couldn't find pkexec") - return pkexec_possibilities - else: - logger.warning("No polkit auth agent found. pkexec " + - "will use its own auth agent.") - raise EIPNoPolkitAuthAgentAvailable() - else: - logger.warning("System has no pkexec") - raise EIPNoPkexecAvailable() - - @classmethod def get_vpn_command(kls, eipconfig, providerconfig, socket_host, socket_port="unix", openvpn_verb=1): """ @@ -194,7 +107,13 @@ class LinuxVPNLauncher(VPNLauncher): command.insert(1, "openvpn") command.insert(2, "start") - pkexec = kls.maybe_pkexec() + policyChecker = LinuxPolicyChecker() + try: + pkexec = policyChecker.maybe_pkexec() + except NoPolkitAuthAgentAvailable: + raise EIPNoPolkitAuthAgentAvailable() + except NoPkexecAvailable: + raise EIPNoPkexecAvailable() if pkexec: command.insert(0, first(pkexec)) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index c7159a93..8dc6021f 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -50,8 +50,7 @@ from leap.common.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) vpnlog = logging.getLogger('leap.openvpn') -from twisted.internet import protocol -from twisted.internet import defer +from twisted.internet import defer, protocol, reactor from twisted.internet import error as internet_error from twisted.internet.task import LoopingCall @@ -157,10 +156,8 @@ class VPN(object): of a QObject containing the QSignals that we will pass along to the VPNManager. """ - from twisted.internet import reactor self._vpnproc = None self._pollers = [] - self._reactor = reactor self._signaler = kwargs['signaler'] self._openvpn_verb = flags.OPENVPN_VERBOSITY @@ -217,14 +214,15 @@ class VPN(object): # and abstract us away from anything else. try: cmd = vpnproc.getCommand() - except Exception: - logger.error("Error while getting vpn command...") + except Exception as e: + logger.error("Error while getting vpn command... {0!r}".format(e)) raise + env = os.environ for key, val in vpnproc.vpn_env.items(): env[key] = val - self._reactor.spawnProcess(vpnproc, cmd[0], cmd, env) + reactor.spawnProcess(vpnproc, cmd[0], cmd, env) self._vpnproc = vpnproc # add pollers for status and state @@ -300,7 +298,6 @@ class VPN(object): :param tries: counter of tries, used in recursion :type tries: int """ - from twisted.internet import reactor while tries < self.TERMINATE_MAXTRIES: if self._vpnproc.transport.pid is None: logger.debug("Process has been happily terminated.") @@ -351,7 +348,6 @@ class VPN(object): :param restart: whether this stop is part of a hard restart. :type restart: bool """ - from twisted.internet import reactor self._stop_pollers() # First we try to be polite and send a SIGTERM... @@ -375,6 +371,8 @@ class VPN(object): logger.debug("Firewall down") else: logger.warning("Could not tear firewall down") + else: + logger.debug("VPN is not running.") def _start_pollers(self): """ @@ -424,8 +422,6 @@ class VPNManager(object): backend :type signaler: backend.Signaler """ - from twisted.internet import reactor - self._reactor = reactor self._tn = None self._signaler = signaler self._aborted = False @@ -602,7 +598,7 @@ class VPNManager(object): logger.debug('trying to connect to management') if not self.aborted and not self.is_connected(): self.connect_to_management(self._socket_host, self._socket_port) - self._reactor.callLater( + reactor.callLater( self.CONNECTION_RETRY_TIME, self.try_to_connect_to_management, retry + 1) diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py index 5e85368f..416aff34 100644 --- a/src/leap/bitmask/services/mail/conductor.py +++ b/src/leap/bitmask/services/mail/conductor.py @@ -22,6 +22,7 @@ import logging from leap.bitmask.config import flags from leap.bitmask.gui import statemachines from leap.bitmask.services.mail import connection as mail_connection +from leap.bitmask.services.mail.emailfirewall import get_email_firewall from leap.common.events import events_pb2 as leap_events from leap.common.events import register as leap_register @@ -211,6 +212,11 @@ class MailConductor(IMAPControl, SMTPControl): self._mail_connection = mail_connection.MailConnection() self._userid = None + try: + self._firewall = get_email_firewall() + except NotImplementedError: + self._firewall = None + logger.info("Email firewall is not implemented in this platform") @property def userid(self): @@ -247,12 +253,25 @@ class MailConductor(IMAPControl, SMTPControl): self._smtp_machine = smtp self._smtp_machine.start() + def start_mail_service(self, download_if_needed=False, offline=False): + """ + Start the IMAP and SMTP servcies. + """ + if self._firewall is not None: + self._firewall.start() + if not offline: + logger.debug("not starting smtp in offline mode") + self.start_smtp_service(download_if_needed=download_if_needed) + self.start_imap_service() + def stop_mail_services(self): """ Stop the IMAP and SMTP services. """ self.stop_imap_service() self.stop_smtp_service() + if self._firewall is not None: + self._firewall.stop() def connect_mail_signals(self, widget): """ diff --git a/src/leap/bitmask/services/mail/emailfirewall.py b/src/leap/bitmask/services/mail/emailfirewall.py new file mode 100644 index 00000000..2cd2ec31 --- /dev/null +++ b/src/leap/bitmask/services/mail/emailfirewall.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# emailfirewall.py +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +""" +Email firewall implementation. +""" + +import os +import subprocess + +from abc import ABCMeta, abstractmethod + +from leap.bitmask.config import flags +from leap.bitmask.platform_init import IS_LINUX +from leap.bitmask.util import first, force_eval +from leap.bitmask.util.privilege_policies import LinuxPolicyChecker +from leap.common.check import leap_assert + + +def get_email_firewall(): + """ + Return the email firewall handler for the current platform. + """ + if not (IS_LINUX): + error_msg = "Email firewall not implemented for this platform." + raise NotImplementedError(error_msg) + + firewall = None + if IS_LINUX: + firewall = LinuxEmailFirewall + + leap_assert(firewall is not None) + + return firewall() + + +class EmailFirewall(object): + """ + Abstract email firwall class + """ + __metaclass__ = ABCMeta + + @abstractmethod + def start(self): + """ + Start email firewall + """ + return False + + @abstractmethod + def stop(self): + """ + Stop email firewall + """ + return False + + +class EmailFirewallException(Exception): + pass + + +class LinuxEmailFirewall(EmailFirewall): + + class BITMASK_ROOT(object): + def __call__(self): + return ("/usr/local/sbin/bitmask-root" if flags.STANDALONE else + "/usr/sbin/bitmask-root") + + def start(self): + uid = str(os.getuid()) + return True if self._run(["start", uid]) is 0 else False + + def stop(self): + return True if self._run(["stop"]) is 0 else False + + def _run(self, cmd): + """ + Run an email firewall command with bitmask-root + + Might raise: + NoPkexecAvailable, + NoPolkitAuthAgentAvailable, + + :param cmd: command to send to bitmask-root fw-email + :type cmd: [str] + :returns: exit code of bitmask-root + :rtype: int + """ + command = [] + + policyChecker = LinuxPolicyChecker() + pkexec = policyChecker.maybe_pkexec() + if pkexec: + command.append(first(pkexec)) + + command.append(force_eval(self.BITMASK_ROOT)) + command.append("fw-email") + command += cmd + + # XXX: will be nice to use twisted ProcessProtocol instead of + # subprocess to avoid blocking until it finish + return subprocess.call(command) diff --git a/src/leap/bitmask/services/mail/smtpbootstrapper.py b/src/leap/bitmask/services/mail/smtpbootstrapper.py index 3ef755e8..9dd61488 100644 --- a/src/leap/bitmask/services/mail/smtpbootstrapper.py +++ b/src/leap/bitmask/services/mail/smtpbootstrapper.py @@ -173,3 +173,5 @@ class SMTPBootstrapper(AbstractBootstrapper): logger.debug('Stopping SMTP service.') self._smtp_port.stopListening() self._smtp_service.doStop() + else: + logger.debug('SMTP service not running.') |