From ca0e1c4518749e27bccad817d22ab87afbf8acf7 Mon Sep 17 00:00:00 2001 From: "Kali Kaneko (leap communications)" Date: Tue, 31 Jan 2017 13:31:13 +0100 Subject: [feature] initial port of legacy vpn code non functional at the moment, but started doing some cleanup --- src/leap/bitmask/vpn/launchers/__init__.py | 0 src/leap/bitmask/vpn/launchers/darwin.py | 199 +++++++++++++++++++++++++++++ src/leap/bitmask/vpn/launchers/linux.py | 181 ++++++++++++++++++++++++++ src/leap/bitmask/vpn/launchers/windows.py | 73 +++++++++++ 4 files changed, 453 insertions(+) create mode 100644 src/leap/bitmask/vpn/launchers/__init__.py create mode 100644 src/leap/bitmask/vpn/launchers/darwin.py create mode 100644 src/leap/bitmask/vpn/launchers/linux.py create mode 100644 src/leap/bitmask/vpn/launchers/windows.py (limited to 'src/leap/bitmask/vpn/launchers') 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 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 . +""" +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 . + +""" +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 . + +""" +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 -- cgit v1.2.3