summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--changes/feature-install-local-path1
-rw-r--r--pkg/linux/README.rst31
-rw-r--r--pkg/linux/polkit/se.leap.bitmask.bundle.policy23
-rwxr-xr-xsetup.py61
-rw-r--r--src/leap/bitmask/__init__.py2
-rw-r--r--src/leap/bitmask/backend.py4
-rw-r--r--src/leap/bitmask/platform_init/initializers.py18
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py74
-rw-r--r--src/leap/bitmask/services/eip/vpnlauncher.py13
-rw-r--r--src/leap/bitmask/services/eip/vpnprocess.py8
-rw-r--r--src/leap/bitmask/util/__init__.py19
-rw-r--r--src/leap/bitmask/util/privilege_policies.py10
13 files changed, 209 insertions, 56 deletions
diff --git a/.gitignore b/.gitignore
index 0e5bb658..23bfbfe8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@ pkg/osx/build
src/*.egg-info
src/pysqlcipher
+src/leap/bitmask/_binaries.py
src/leap/bitmask/util/reqs.txt
MANIFEST
_trial_temp*
diff --git a/changes/feature-install-local-path b/changes/feature-install-local-path
new file mode 100644
index 00000000..17e1d2d7
--- /dev/null
+++ b/changes/feature-install-local-path
@@ -0,0 +1 @@
+- Install helpers to /usr/local for bundle. Closes: #5741
diff --git a/pkg/linux/README.rst b/pkg/linux/README.rst
index 220565ff..249c721f 100644
--- a/pkg/linux/README.rst
+++ b/pkg/linux/README.rst
@@ -3,8 +3,33 @@ Files
In GNU/Linux, we expect these files to be in place::
- update-resolv-conf -> /etc/leap/update-resolv-conf
- resolv-update -> /etc/leap/resolv-update
-
bitmask-root -> /usr/sbin/bitmask-root
polkit/se.leap.bitmask.policy -> /usr/share/polkit-1/actions/se.leap.bitmask.policy
+
+Bundle
+======
+
+The bundle will ask for permission to install to a different path. This search
+path will be used if the flag ``--standalone`` is set::
+
+ bitmask-root -> /usr/local/sbin/bitmask-root
+ polkit/se.leap.bitmask.bundle.policy -> /usr/share/polkit-1/actions/se.leap.bitmask.bundle.policy
+
+You will also have to place an openvpn binary in the following path::
+
+ leap-openvpn -> /usr/local/sbin/leap-openvpn
+
+
+Binary hashing
+==============
+
+To be able to update the binaries when needed, the bundles distribute with the
+sha256 hash of the packaged binaries for each release. This info can be found
+in::
+
+ src/leap/bitmask/_binaries.py
+
+That file is generated during the bundling process, by issuing the following
+command from the root folder::
+
+ python setup.py hash_binaries
diff --git a/pkg/linux/polkit/se.leap.bitmask.bundle.policy b/pkg/linux/polkit/se.leap.bitmask.bundle.policy
new file mode 100644
index 00000000..58fcaaa8
--- /dev/null
+++ b/pkg/linux/polkit/se.leap.bitmask.bundle.policy
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+<policyconfig>
+
+ <vendor>LEAP Project</vendor>
+ <vendor_url>http://leap.se/</vendor_url>
+
+ <action id="se.leap.bitmask.bundle.policy">
+ <description>Runs bitmask helper to launch firewall and openvpn (bundle version)</description>
+ <description xml:lang="es">Ejecuta el asistente de bitmask para lanzar el firewall y openvpn (version bundle)</description>
+ <message>Bitmask needs that you authenticate to start</message>
+ <message xml:lang="es">Bitmask necesita autorizacion para comenzar</message>
+ <icon_name>package-x-generic</icon_name>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">/usr/local/sbin/bitmask-root</annotate>
+ </action>
+</policyconfig>
diff --git a/setup.py b/setup.py
index 3d12db64..bb1937cc 100755
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,9 @@ Setup file for bitmask.
"""
from __future__ import print_function
+import hashlib
import sys
+import os
import re
if not sys.version_info[0] == 2:
@@ -34,7 +36,6 @@ except ImportError:
from pkg import distribute_setup
distribute_setup.use_setuptools()
from setuptools import setup, find_packages
-import os
from pkg import utils
@@ -168,6 +169,64 @@ class cmd_develop(_develop):
cmdclass["develop"] = cmd_develop
+
+class cmd_binary_hash(Command):
+ """
+ Update the _binaries.py file with hashes for the different helpers.
+ This is used from within the bundle.
+ """
+
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self, *args):
+
+ OPENVPN_BIN = os.environ.get('OPENVPN_BIN', None)
+ BITMASK_ROOT = os.environ.get('BITMASK_ROOT', None)
+
+ def exit():
+ print("Please set environment variables "
+ "OPENVPN_BIN and BITMASK_ROOT pointing to the right path "
+ "to use this command")
+ sys.exit(1)
+
+ bin_paths = OPENVPN_BIN, BITMASK_ROOT
+ if not all(bin_paths):
+ exit()
+
+ if not all(map(os.path.isfile, bin_paths)):
+ exit()
+
+ openvpn_bin_hash, bitmask_root_hash = map(
+ lambda path: hashlib.sha256(open(path).read()).hexdigest(),
+ bin_paths)
+
+ template = r"""
+# Hashes for binaries used in Bitmask Bundle.
+# This file has been automatically generated by `setup.py hash_binaries`
+# DO NOT modify it manually.
+
+OPENVPN_BIN = "{openvpn}"
+BITMASK_ROOT = "{bitmask}"
+"""
+ subst_template = template.format(
+ openvpn=openvpn_bin_hash,
+ bitmask=bitmask_root_hash)
+
+ bin_hash_path = os.path.join('src', 'leap', 'bitmask', '_binaries.py')
+ with open(bin_hash_path, 'w') as f:
+ f.write(subst_template)
+ print("Binaries hash file %s has been updated!" % (bin_hash_path,))
+
+
+cmdclass["hash_binaries"] = cmd_binary_hash
+
+
# next two classes need to augment the versioneer modified ones
versioneer_build = cmdclass['build']
diff --git a/src/leap/bitmask/__init__.py b/src/leap/bitmask/__init__.py
index c844beb1..0f733f26 100644
--- a/src/leap/bitmask/__init__.py
+++ b/src/leap/bitmask/__init__.py
@@ -66,7 +66,7 @@ except ImportError:
__appname__ = "unknown"
try:
- from leap._appname import __appname__
+ from leap.bitmask._appname import __appname__
except ImportError:
#running on a tree that has not run
#the setup.py setver
diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py
index 7ebe7f97..f7200dd7 100644
--- a/src/leap/bitmask/backend.py
+++ b/src/leap/bitmask/backend.py
@@ -54,6 +54,7 @@ from leap.bitmask.services.mail.smtpconfig import SMTPConfig
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
+from leap.bitmask.util import force_eval
from leap.common import certs as leap_certs
@@ -609,7 +610,8 @@ class EIP(object):
eip_loaded = eip_config.load(eipconfig.get_eipconfig_path(domain))
launcher = get_vpn_launcher()
- if not os.path.isfile(launcher.OPENVPN_BIN_PATH):
+ ovpn_path = force_eval(launcher.OPENVPN_BIN_PATH)
+ if not os.path.isfile(ovpn_path):
logger.error("Cannot start OpenVPN, binary not found")
return False
diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py
index b282a229..79fdd554 100644
--- a/src/leap/bitmask/platform_init/initializers.py
+++ b/src/leap/bitmask/platform_init/initializers.py
@@ -70,10 +70,10 @@ NOTFOUND_MSG = ("Tried to install %s, but %s "
BADEXEC_MSG = ("Tried to install %s, but %s "
"failed to %s.")
-UPDOWN_NOTFOUND_MSG = NOTFOUND_MSG % (
- "updown scripts", "those were")
-UPDOWN_BADEXEC_MSG = BADEXEC_MSG % (
- "updown scripts", "they", "be copied")
+HELPERS_NOTFOUND_MSG = NOTFOUND_MSG % (
+ "helper files", "those were")
+HELPERS_BADEXEC_MSG = BADEXEC_MSG % (
+ "helper files", "they", "be copied")
def get_missing_updown_dialog():
@@ -88,7 +88,7 @@ def get_missing_updown_dialog():
"to install helper files. "
"Do you want to proceed?")
msg = QtGui.QMessageBox()
- msg.setWindowTitle(msg.tr("Missing up/down scripts"))
+ msg.setWindowTitle(msg.tr("Missing helper files"))
msg.setText(msg.tr(WE_NEED_POWERS))
# but maybe the user really deserve to know more
#msg.setInformativeText(msg.tr(BECAUSE))
@@ -123,8 +123,10 @@ def check_missing():
"Installer not found for platform %s." % (_system,))
return
+ print "INSTALL FUN", install_missing_fun
+
# XXX maybe move constants to fun
- ok = install_missing_fun(UPDOWN_BADEXEC_MSG, UPDOWN_NOTFOUND_MSG)
+ ok = install_missing_fun(HELPERS_BADEXEC_MSG, HELPERS_NOTFOUND_MSG)
if not ok:
msg = QtGui.QMessageBox()
msg.setWindowTitle(msg.tr("Problem installing files"))
@@ -385,7 +387,7 @@ def _linux_check_resolvconf():
def _linux_install_missing_scripts(badexec, notfound):
"""
- Try to install the missing up/down scripts.
+ Try to install the missing helper files.
:param badexec: error for notifying execution error during command.
:type badexec: str
@@ -405,6 +407,7 @@ def _linux_install_missing_scripts(badexec, notfound):
polfd, pol_tempfile = tempfile.mkstemp(prefix="leap_installer-")
try:
pkexec = first(launcher.maybe_pkexec())
+
scriptlines = launcher.cmd_for_missing_scripts(installer_path)
with os.fdopen(fd, 'w') as f:
f.write(scriptlines)
@@ -413,6 +416,7 @@ def _linux_install_missing_scripts(badexec, notfound):
os.chmod(tempscript, st.st_mode | stat.S_IEXEC | stat.S_IXUSR |
stat.S_IXGRP | stat.S_IXOTH)
cmdline = ["%s %s" % (pkexec, tempscript)]
+
ret = subprocess.call(
cmdline, stdout=subprocess.PIPE,
shell=True)
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
index 955768d1..8ec0c050 100644
--- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -29,7 +29,7 @@ from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
from leap.common.files import which
from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
-from leap.bitmask.util import get_path_prefix
+from leap.bitmask.util import get_path_prefix, force_eval
from leap.common.check import leap_assert
from leap.bitmask.util import first
@@ -105,26 +105,34 @@ leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f)
class LinuxVPNLauncher(VPNLauncher):
PKEXEC_BIN = 'pkexec'
- BITMASK_ROOT = "/usr/sbin/bitmask-root"
- # We assume this is there by our openvpn dependency, and
- # we will put it there on the bundle too.
- if flags.STANDALONE:
- OPENVPN_BIN_PATH = "/usr/sbin/leap-openvpn"
- else:
- OPENVPN_BIN_PATH = "/usr/sbin/openvpn"
-
- POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
-
- if flags.STANDALONE:
- RESOLVCONF_BIN_PATH = "/usr/local/sbin/leap-resolvconf"
- else:
+ # The following classes depend on force_eval to be called against
+ # the classes, to get the evaluation of the standalone flag on runtine.
+ # If we keep extending this kind of classes, we should abstract the
+ # handling of the STANDALONE flag in a base class
+
+ class BITMASK_ROOT(object):
+ def __call__(self):
+ return ("/usr/local/sbin/bitmask-root" if flags.STANDALONE else
+ "/usr/sbin/bitmask-root")
+
+ class OPENVPN_BIN_PATH(object):
+ def __call__(self):
+ return ("/usr/local/sbin/leap-openvpn" if flags.STANDALONE else
+ "/usr/sbin/openvpn")
+
+ class POLKIT_PATH(object):
+ def __call__(self):
+ # LinuxPolicyChecker will give us the right path if standalone.
+ return LinuxPolicyChecker.get_polkit_path()
+
+ class RESOLVCONF_BIN_PATH(object):
+ def __call__(self):
+ return ("/usr/local/sbin/leap-resolvconf" if flags.STANDALONE else
+ "/sbin/resolvconf")
# this only will work with debian/ubuntu distros.
- RESOLVCONF_BIN_PATH = "/sbin/resolvconf"
- # XXX openvpn binary TOO
- OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH,
- RESOLVCONF_BIN_PATH)
+ OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH)
@classmethod
def maybe_pkexec(kls):
@@ -187,7 +195,7 @@ class LinuxVPNLauncher(VPNLauncher):
command = super(LinuxVPNLauncher, kls).get_vpn_command(
eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
- command.insert(0, kls.BITMASK_ROOT)
+ command.insert(0, force_eval(kls.BITMASK_ROOT))
command.insert(1, "openvpn")
command.insert(2, "start")
@@ -207,35 +215,37 @@ class LinuxVPNLauncher(VPNLauncher):
:rtype: str
"""
+ bin_paths = force_eval(
+ (LinuxVPNLauncher.POLKIT_PATH,
+ LinuxVPNLauncher.OPENVPN_BIN_PATH,
+ LinuxVPNLauncher.BITMASK_ROOT))
+
+ polkit_path, openvpn_bin_path, bitmask_root = bin_paths
+
# no system config for now
# sys_config = kls.SYSTEM_CONFIG
(polkit_file, openvpn_bin_file,
- bitmask_root_file, resolvconf_bin_file) = map(
+ bitmask_root_file) = map(
lambda p: os.path.split(p)[-1],
- (kls.POLKIT_PATH, kls.OPENVPN_BIN_PATH,
- kls.BITMASK_ROOT, kls.RESOLVCONF_BIN_PATH))
+ bin_paths)
cmd = '#!/bin/sh\n'
cmd += 'mkdir -p /usr/local/sbin\n'
cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, polkit_file),
- kls.POLKIT_PATH)
- cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
+ polkit_path)
+ cmd += 'chmod 644 "%s"\n' % (polkit_path, )
cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, bitmask_root_file),
- kls.BITMASK_ROOT)
- cmd += 'chmod 744 "%s"\n' % (kls.BITMASK_ROOT, )
+ bitmask_root)
+ cmd += 'chmod 744 "%s"\n' % (bitmask_root, )
if flags.STANDALONE:
cmd += 'cp "%s" "%s"\n' % (
os.path.join(frompath, openvpn_bin_file),
- kls.OPENVPN_BIN_PATH)
- cmd += 'chmod 744 "%s"\n' % (kls.POLKIT_PATH, )
+ openvpn_bin_path)
+ cmd += 'chmod 744 "%s"\n' % (openvpn_bin_path, )
- cmd += 'cp "%s" "%s"\n' % (
- os.path.join(frompath, resolvconf_bin_file),
- kls.RESOLVCONF_BIN_PATH)
- cmd += 'chmod 744 "%s"\n' % (kls.POLKIT_PATH, )
return cmd
@classmethod
diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py
index 9629afae..58ca0c33 100644
--- a/src/leap/bitmask/services/eip/vpnlauncher.py
+++ b/src/leap/bitmask/services/eip/vpnlauncher.py
@@ -30,6 +30,7 @@ from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.platform_init import IS_LINUX
from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.util import force_eval
from leap.common.check import leap_assert, leap_assert_type
@@ -179,12 +180,13 @@ class VPNLauncher(object):
#raise OpenVPNNotFoundException()
#openvpn = first(openvpn_possibilities)
# -----------------------------------------
- if not os.path.isfile(kls.OPENVPN_BIN_PATH):
+ openvpn_path = force_eval(kls.OPENVPN_BIN_PATH)
+
+ if not os.path.isfile(openvpn_path):
logger.warning("Could not find openvpn bin in path %s" % (
- kls.OPENVPN_BIN_PATH))
+ openvpn_path))
raise OpenVPNNotFoundException()
- openvpn = kls.OPENVPN_BIN_PATH
args = []
args += [
@@ -248,7 +250,7 @@ class VPNLauncher(object):
'--ping', '10',
'--ping-restart', '30']
- command_and_args = [openvpn] + args
+ command_and_args = [openvpn_path] + args
return command_and_args
@classmethod
@@ -293,7 +295,8 @@ class VPNLauncher(object):
leap_assert(kls.OTHER_FILES is not None,
"Need to define OTHER_FILES for this particular "
"auncher before calling this method")
+ other = force_eval(kls.OTHER_FILES)
file_exist = partial(_has_other_files, warn=False)
- zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
+ zipped = zip(other, map(file_exist, other))
missing = filter(lambda (path, exists): exists is False, zipped)
return [path for path, exists in missing]
diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py
index f56d464e..b54f2925 100644
--- a/src/leap/bitmask/services/eip/vpnprocess.py
+++ b/src/leap/bitmask/services/eip/vpnprocess.py
@@ -43,7 +43,7 @@ from leap.bitmask.services.eip import get_vpn_launcher
from leap.bitmask.services.eip import linuxvpnlauncher
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.bitmask.services.eip.udstelnet import UDSTelnet
-from leap.bitmask.util import first
+from leap.bitmask.util import first, force_eval
from leap.bitmask.platform_init import IS_MAC, IS_LINUX
from leap.common.check import leap_assert, leap_assert_type
@@ -233,7 +233,7 @@ class VPN(object):
# XXX could check for wrapper existence, check it's root owned etc.
# XXX could check that the iptables rules are in place.
- BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT
+ BM_ROOT = force_eval(linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT)
cmd = ["pkexec", BM_ROOT, "firewall", "start"]
if restart:
cmd.append("restart")
@@ -246,7 +246,7 @@ class VPN(object):
:rtype: bool
"""
- BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT
+ BM_ROOT = force_eval(linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT)
fw_up_cmd = "pkexec {0} firewall isup".format(BM_ROOT)
fw_is_down = lambda: commands.getstatusoutput(fw_up_cmd)[0] == 256
return fw_is_down()
@@ -255,7 +255,7 @@ class VPN(object):
"""
Tear the firewall down using the privileged wrapper.
"""
- BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT
+ BM_ROOT = force_eval(linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT)
exitCode = subprocess.call(["pkexec",
BM_ROOT, "firewall", "stop"])
return True if exitCode is 0 else False
diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py
index c35be99e..25b86874 100644
--- a/src/leap/bitmask/util/__init__.py
+++ b/src/leap/bitmask/util/__init__.py
@@ -110,3 +110,22 @@ def make_address(user, provider):
:type provider: basestring
"""
return "%s@%s" % (user, provider)
+
+
+def force_eval(items):
+ """
+ Return a sequence that evaluates any callable in the sequence,
+ instantiating it beforehand if the item is a class, and
+ leaves the non-callable items without change.
+ """
+ def do_eval(thing):
+ if isinstance(thing, type):
+ return thing()()
+ if callable(thing):
+ return thing()
+ return thing
+
+ if isinstance(items, (list, tuple)):
+ return map(do_eval, items)
+ else:
+ return do_eval(items)
diff --git a/src/leap/bitmask/util/privilege_policies.py b/src/leap/bitmask/util/privilege_policies.py
index 9d1e2c9a..adc3503f 100644
--- a/src/leap/bitmask/util/privilege_policies.py
+++ b/src/leap/bitmask/util/privilege_policies.py
@@ -24,6 +24,8 @@ import platform
from abc import ABCMeta, abstractmethod
+from leap.bitmask.config import flags
+
logger = logging.getLogger(__name__)
@@ -71,6 +73,8 @@ class LinuxPolicyChecker(PolicyChecker):
"""
LINUX_POLKIT_FILE = ("/usr/share/polkit-1/actions/"
"se.leap.bitmask.policy")
+ LINUX_POLKIT_FILE_BUNDLE = ("/usr/share/polkit-1/actions/"
+ "se.leap.bitmask.bundle.policy")
@classmethod
def get_polkit_path(self):
@@ -79,7 +83,8 @@ class LinuxPolicyChecker(PolicyChecker):
:rtype: str
"""
- return self.LINUX_POLKIT_FILE
+ return (self.LINUX_POLKIT_FILE_BUNDLE if flags.STANDALONE
+ else self.LINUX_POLKIT_FILE)
def is_missing_policy_permissions(self):
# FIXME this name is quite confusing, it does not have anything to do with
@@ -90,4 +95,5 @@ class LinuxPolicyChecker(PolicyChecker):
:rtype: bool
"""
- return not os.path.isfile(self.LINUX_POLKIT_FILE)
+ path = self.get_polkit_path()
+ return not os.path.isfile(path)