summaryrefslogtreecommitdiff
path: root/src/leap/bitmask
diff options
context:
space:
mode:
authorIvan Alejandro <ivanalejandro0@gmail.com>2014-09-11 16:59:03 -0300
committerIvan Alejandro <ivanalejandro0@gmail.com>2014-09-11 16:59:03 -0300
commitaacc94aac0f824bb1c292a1cddbbe1a6a0cd4d39 (patch)
tree19f99bf8a5a24eb555680ad767e6da999a0e55f3 /src/leap/bitmask
parentbdfac84e20389e2653939fa9f2dd9752eaabcfed (diff)
parent3d0708ad3e20aa8dddf6894b7536be3cd59cfbca (diff)
Merge remote-tracking branch 'meskio/feature/6040_email_firewall' into develop
Diffstat (limited to 'src/leap/bitmask')
-rw-r--r--src/leap/bitmask/gui/mainwindow.py40
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py99
-rw-r--r--src/leap/bitmask/services/mail/conductor.py19
-rw-r--r--src/leap/bitmask/services/mail/emailfirewall.py115
-rw-r--r--src/leap/bitmask/util/privilege_policies.py98
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