summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/vpn/launchers
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/vpn/launchers')
-rw-r--r--src/leap/bitmask/vpn/launchers/__init__.py0
-rw-r--r--src/leap/bitmask/vpn/launchers/darwin.py199
-rw-r--r--src/leap/bitmask/vpn/launchers/linux.py181
-rw-r--r--src/leap/bitmask/vpn/launchers/windows.py73
4 files changed, 453 insertions, 0 deletions
diff --git a/src/leap/bitmask/vpn/launchers/__init__.py b/src/leap/bitmask/vpn/launchers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/leap/bitmask/vpn/launchers/__init__.py
diff --git a/src/leap/bitmask/vpn/launchers/darwin.py b/src/leap/bitmask/vpn/launchers/darwin.py
new file mode 100644
index 0000000..f19404c
--- /dev/null
+++ b/src/leap/bitmask/vpn/launchers/darwin.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+# darwin.py
+# Copyright (C) 2013-2017 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/>.
+"""
+Darwin VPN launcher implementation.
+"""
+import commands
+import getpass
+import os
+import sys
+
+from twisted.logger import Logger
+
+from leap.bitmask.vpn.launcher import VPNLauncher
+from leap.bitmask.vpn.launcher import VPNLauncherException
+from leap.bitmask.vpn.utils import get_path_prefix
+
+
+logger = Logger()
+
+
+class EIPNoTunKextLoaded(VPNLauncherException):
+ pass
+
+
+class DarwinVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Darwin Platform
+ """
+ COCOASUDO = "cocoasudo"
+ # XXX need the good old magic translate for these strings
+ # (look for magic in 0.2.0 release)
+ SUDO_MSG = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
+ "missing scripts and fix permissions.\"")
+
+ # Hardcode the installation path for OSX for security, openvpn is
+ # run as root
+ INSTALL_PATH = "/Applications/Bitmask.app/"
+ INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
+ OPENVPN_BIN = 'openvpn.leap'
+ OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
+ OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
+ INSTALL_PATH_ESCAPED,)
+ OPENVPN_BIN_PATH = "%s/Contents/Resources/%s" % (INSTALL_PATH,
+ OPENVPN_BIN)
+
+ UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
+ DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
+ OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
+
+ UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
+ OTHER_FILES = []
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath):
+ """
+ Returns a command that can copy the missing scripts.
+ :rtype: str
+ """
+ to = kls.OPENVPN_PATH_ESCAPED
+
+ cmd = "#!/bin/sh\n"
+ cmd += "mkdir -p {0}\n".format(to)
+ cmd += "cp '{0}'/* {1}\n".format(frompath, to)
+ cmd += "chmod 744 {0}/*".format(to)
+
+ return cmd
+
+ @classmethod
+ def is_kext_loaded(kls):
+ """
+ Checks if the needed kext is loaded before launching openvpn.
+
+ :returns: True if kext is loaded, False otherwise.
+ :rtype: bool
+ """
+ return bool(commands.getoutput('kextstat | grep "leap.tun"'))
+
+ @classmethod
+ def _get_icon_path(kls):
+ """
+ Returns the absolute path to the app icon.
+
+ :rtype: str
+ """
+ resources_path = os.path.abspath(
+ os.path.join(os.getcwd(), "../../Contents/Resources"))
+
+ return os.path.join(resources_path, "bitmask.tiff")
+
+ @classmethod
+ def get_cocoasudo_ovpn_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to run openvpn
+ as admin with a nice password prompt. The actual command needs to be
+ appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ sudo_msg = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (sudo_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_cocoasudo_installmissing_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to install missing
+ files as admin with a nice password prompt. The actual command needs to
+ be appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ install_msg = ('"Bitmask needs administrative privileges to install '
+ 'missing scripts and fix permissions."')
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (install_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the OSX implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoTunKextLoaded,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ if not kls.is_kext_loaded():
+ raise EIPNoTunKextLoaded
+
+ # we use `super` in order to send the class to use
+ command = super(DarwinVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ cocoa, cargs = kls.get_cocoasudo_ovpn_cmd()
+ cargs.extend(command)
+ command = cargs
+ command.insert(0, cocoa)
+
+ command.extend(['--setenv', "LEAPUSER", getpass.getuser()])
+
+ return command
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ ld_library_path = os.path.join(get_path_prefix(), "..", "lib")
+ ld_library_path.encode(sys.getfilesystemencoding())
+ return {
+ "DYLD_LIBRARY_PATH": ld_library_path
+ }
diff --git a/src/leap/bitmask/vpn/launchers/linux.py b/src/leap/bitmask/vpn/launchers/linux.py
new file mode 100644
index 0000000..a86dcb4
--- /dev/null
+++ b/src/leap/bitmask/vpn/launchers/linux.py
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+# linux
+# Copyright (C) 2013-2017 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/>.
+
+"""
+Linux VPN launcher implementation.
+"""
+
+import commands
+import os
+import sys
+
+from twisted.logger import Logger
+
+from leap.bitmask.vpn.utils import first
+from leap.bitmask.vpn.utils import get_path_prefix, force_eval
+from leap.bitmask.vpn.privilege import LinuxPolicyChecker
+from leap.bitmask.vpn.privilege import NoPkexecAvailable
+from leap.bitmask.vpn.privilege import NoPolkitAuthAgentAvailable
+from leap.bitmask.vpn.launcher import VPNLauncher
+from leap.bitmask.vpn.launcher import VPNLauncherException
+
+logger = Logger()
+COM = commands
+flags_STANDALONE = False
+
+
+class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
+ pass
+
+
+class EIPNoPkexecAvailable(VPNLauncherException):
+ pass
+
+
+SYSTEM_CONFIG = "/etc/leap"
+leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f)
+
+
+class LinuxVPNLauncher(VPNLauncher):
+
+ # 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()
+
+ OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH)
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ remotes, socket_port="unix", openvpn_verb=1):
+ """
+ Returns the Linux implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoPkexecAvailable,
+ EIPNoPolkitAuthAgentAvailable,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ # we use `super` in order to send the class to use
+ command = super(LinuxVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, remotes,
+ openvpn_verb)
+
+ command.insert(0, force_eval(kls.BITMASK_ROOT))
+ command.insert(1, "openvpn")
+ command.insert(2, "start")
+
+ policyChecker = LinuxPolicyChecker()
+ try:
+ pkexec = policyChecker.maybe_pkexec()
+ except NoPolkitAuthAgentAvailable:
+ raise EIPNoPolkitAuthAgentAvailable()
+ except NoPkexecAvailable:
+ raise EIPNoPkexecAvailable()
+ if pkexec:
+ command.insert(0, first(pkexec))
+
+ return command
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath):
+ """
+ Returns a sh script that can copy the missing files.
+
+ :param frompath: The path where the helper files live
+ :type frompath: str
+
+ :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) = map(
+ lambda p: os.path.split(p)[-1],
+ bin_paths)
+
+ cmd = '#!/bin/sh\n'
+ cmd += 'mkdir -p /usr/local/sbin\n'
+
+ cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, polkit_file),
+ polkit_path)
+ cmd += 'chmod 644 "%s"\n' % (polkit_path, )
+
+ cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, bitmask_root_file),
+ bitmask_root)
+ cmd += 'chmod 744 "%s"\n' % (bitmask_root, )
+
+ if flags_STANDALONE:
+ cmd += 'cp "%s" "%s"\n' % (
+ os.path.join(frompath, openvpn_bin_file),
+ openvpn_bin_path)
+ cmd += 'chmod 744 "%s"\n' % (openvpn_bin_path, )
+
+ return cmd
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ ld_library_path = os.path.join(get_path_prefix(), "..", "lib")
+ ld_library_path.encode(sys.getfilesystemencoding())
+ return {
+ "LD_LIBRARY_PATH": ld_library_path
+ }
diff --git a/src/leap/bitmask/vpn/launchers/windows.py b/src/leap/bitmask/vpn/launchers/windows.py
new file mode 100644
index 0000000..bfaac2f
--- /dev/null
+++ b/src/leap/bitmask/vpn/launchers/windows.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+# windows.py
+# Copyright (C) 2013-2017 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/>.
+
+"""
+Windows VPN launcher implementation.
+"""
+
+from twisted.logger import Logger
+
+from leap.bitmask.vpn.launcher import VPNLauncher
+
+
+logger = get_logger()
+
+
+class WindowsVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Windows platform
+ """
+
+ OPENVPN_BIN = 'openvpn_leap.exe'
+
+ # XXX UPDOWN_FILES ... we do not have updown files defined yet!
+ # (and maybe we won't)
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="9876", openvpn_verb=1):
+ """
+ Returns the Windows implementation for the vpn launching command.
+
+ Might raise:
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ # TODO add check for this
+ # leap_assert(socket_port != "unix",
+ # "We cannot use unix sockets in windows!")
+
+ # we use `super` in order to send the class to use
+ command = super(WindowsVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ return command