diff options
| author | Ivan Alejandro <ivanalejandro0@gmail.com> | 2014-09-11 16:59:03 -0300 | 
|---|---|---|
| committer | Ivan Alejandro <ivanalejandro0@gmail.com> | 2014-09-11 16:59:03 -0300 | 
| commit | aacc94aac0f824bb1c292a1cddbbe1a6a0cd4d39 (patch) | |
| tree | 19f99bf8a5a24eb555680ad767e6da999a0e55f3 /src | |
| parent | bdfac84e20389e2653939fa9f2dd9752eaabcfed (diff) | |
| parent | 3d0708ad3e20aa8dddf6894b7536be3cd59cfbca (diff) | |
Merge remote-tracking branch 'meskio/feature/6040_email_firewall' into develop
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 40 | ||||
| -rw-r--r-- | src/leap/bitmask/services/eip/linuxvpnlauncher.py | 99 | ||||
| -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/util/privilege_policies.py | 98 | 
5 files changed, 247 insertions, 124 deletions
| diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 8127c1f6..9c5045ec 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -271,9 +271,7 @@ class MainWindow(QtGui.QMainWindow):          # Services signals/slots connection          self.new_updates.connect(self._react_to_new_updates) -        # XXX should connect to mail_conductor.start_mail_service instead -        self.soledad_ready.connect(self._start_smtp_bootstrapping) -        self.soledad_ready.connect(self._start_imap_service) +        self.soledad_ready.connect(self._start_mail_service)          # ################################ end Qt Signals connection ########          init_platform() @@ -1563,37 +1561,12 @@ class MainWindow(QtGui.QMainWindow):          self.soledad_ready.emit()      ################################################################### -    # Service control methods: smtp - -    @QtCore.Slot() -    def _start_smtp_bootstrapping(self): -        """ -        TRIGGERS: -            self.soledad_ready -        """ -        if flags.OFFLINE is True: -            logger.debug("not starting smtp in offline mode") -            return - -        if self._provides_mx_and_enabled(): -            self._mail_conductor.start_smtp_service(download_if_needed=True) - -    ################################################################### -    # Service control methods: imap - +    # Service control methods: mail      @QtCore.Slot() -    def _start_imap_service(self): -        """ -        TRIGGERS: -            self.soledad_ready -        """ -        # TODO in the OFFLINE mode we should also modify the  rules -        # in the mail state machine so it shows that imap is active -        # (but not smtp since it's not yet ready for offline use) +    def _start_mail_service(self):          if self._provides_mx_and_enabled() or flags.OFFLINE: -            self._mail_conductor.start_imap_service() - -    # end service control methods (imap) +            self._mail_conductor.start_mail_service(download_if_needed=True, +                                                    offline=flags.OFFLINE)      ###################################################################      # Service control methods: eip @@ -1902,8 +1875,7 @@ class MainWindow(QtGui.QMainWindow):          self._leap_signaler.eip_stopped.connect(eip_stopped)          logger.debug('Stopping mail services') -        self._backend.imap_stop_service() -        self._backend.smtp_stop_service() +        self._mail_conductor.stop_mail_services()          if self._logged_user is not None:              logger.debug("Doing logout") 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/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/util/privilege_policies.py b/src/leap/bitmask/util/privilege_policies.py index f894d73b..2016e67b 100644 --- a/src/leap/bitmask/util/privilege_policies.py +++ b/src/leap/bitmask/util/privilege_policies.py @@ -18,17 +18,30 @@  Helpers to determine if the needed policies for privilege escalation  are operative under this client run.  """ +import commands  import logging  import os +import subprocess  import platform +import time  from abc import ABCMeta, abstractmethod  from leap.bitmask.config import flags +from leap.common.check import leap_assert +from leap.common.files import which  logger = logging.getLogger(__name__) +class NoPolkitAuthAgentAvailable(Exception): +    pass + + +class NoPkexecAvailable(Exception): +    pass + +  def is_missing_policy_permissions():      """      Returns True if we do not have implemented a policy checker for this @@ -75,6 +88,7 @@ class LinuxPolicyChecker(PolicyChecker):                           "se.leap.bitmask.policy")      LINUX_POLKIT_FILE_BUNDLE = ("/usr/share/polkit-1/actions/"                                  "se.leap.bitmask.bundle.policy") +    PKEXEC_BIN = 'pkexec'      @classmethod      def get_polkit_path(self): @@ -97,3 +111,87 @@ class LinuxPolicyChecker(PolicyChecker):          """          path = self.get_polkit_path()          return not os.path.isfile(path) + +    @classmethod +    def maybe_pkexec(self): +        """ +        Checks whether pkexec is available in the system, and +        returns the path if found. + +        Might raise: +            NoPkexecAvailable, +            NoPolkitAuthAgentAvailable. + +        :returns: a list of the paths where pkexec is to be found +        :rtype: list +        """ +        if self._is_pkexec_in_system(): +            if not self.is_up(): +                self.launch() +                time.sleep(2) +            if self.is_up(): +                pkexec_possibilities = which(self.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 NoPolkitAuthAgentAvailable() +        else: +            logger.warning("System has no pkexec") +            raise NoPkexecAvailable() + +    @classmethod +    def launch(self): +        """ +        Tries to launch policykit +        """ +        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) + +    @classmethod +    def is_up(self): +        """ +        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) + +    @classmethod +    def _is_pkexec_in_system(self): +        """ +        Checks the existence of the pkexec binary in system. +        """ +        pkexec_path = which('pkexec') +        if len(pkexec_path) == 0: +            return False +        return True | 
