summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/leap/bitmask/services/eip/darwinvpnlauncher.py190
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py232
-rw-r--r--src/leap/bitmask/services/eip/vpnlauncher.py290
-rw-r--r--src/leap/bitmask/services/eip/windowsvpnlauncher.py69
4 files changed, 781 insertions, 0 deletions
diff --git a/src/leap/bitmask/services/eip/darwinvpnlauncher.py b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
new file mode 100644
index 00000000..f3b6bfc8
--- /dev/null
+++ b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# darwinvpnlauncher.py
+# Copyright (C) 2013 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 logging
+import os
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.util import get_path_prefix
+
+logger = logging.getLogger(__name__)
+
+
+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.\"")
+
+ INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../")
+ 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,)
+
+ 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, "leap-client.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
+ """
+ return {
+ "DYLD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
new file mode 100644
index 00000000..c2c28627
--- /dev/null
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -0,0 +1,232 @@
+# -*- coding: utf-8 -*-
+# linuxvpnlauncher.py
+# Copyright (C) 2013 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 logging
+import os
+import subprocess
+import time
+
+from leap.bitmask.config import flags
+from leap.bitmask.util import privilege_policies
+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.common.check import leap_assert
+from leap.bitmask.util import first
+
+logger = logging.getLogger(__name__)
+
+
+class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
+ pass
+
+
+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
+ """
+ ps = 'ps aux | grep polkit-%s-authentication-agent-1'
+ opts = (ps % case for case in ['[g]nome', '[k]de'])
+ is_running = map(lambda l: commands.getoutput(l), opts)
+ return any(is_running)
+
+
+def _try_to_launch_agent():
+ """
+ Tries to launch a polkit daemon.
+ """
+ env = None
+ if flags.STANDALONE is True:
+ 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.
+ subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
+ shell=True, env=env)
+ except Exception as exc:
+ logger.exception(exc)
+
+
+class LinuxVPNLauncher(VPNLauncher):
+ PKEXEC_BIN = 'pkexec'
+ OPENVPN_BIN = 'openvpn'
+ OPENVPN_BIN_PATH = os.path.join(
+ get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
+
+ SYSTEM_CONFIG = "/etc/leap"
+ UP_DOWN_FILE = "resolv-update"
+ UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
+
+ # We assume this is there by our openvpn dependency, and
+ # we will put it there on the bundle too.
+ # TODO adapt to the bundle path.
+ OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
+ OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
+ OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
+ OPENVPN_DOWN_ROOT_BASE,
+ OPENVPN_DOWN_ROOT_FILE)
+
+ UP_SCRIPT = DOWN_SCRIPT = UP_DOWN_PATH
+ UPDOWN_FILES = (UP_DOWN_PATH,)
+ POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
+ OTHER_FILES = (POLKIT_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(0.5)
+ 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 missing_other_files(kls):
+ """
+ 'Extend' the VPNLauncher's missing_other_files to check if the polkit
+ files is outdated. If the polkit file that is in OTHER_FILES exists but
+ is not up to date, it is added to the missing list.
+
+ :returns: a list of missing files
+ :rtype: list of str
+ """
+ # we use `super` in order to send the class to use
+ missing = super(LinuxVPNLauncher, kls).missing_other_files()
+ polkit_file = LinuxPolicyChecker.get_polkit_path()
+ if polkit_file not in missing:
+ if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH):
+ missing.append(polkit_file)
+
+ return missing
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ 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, openvpn_verb)
+
+ pkexec = kls.maybe_pkexec()
+ if pkexec:
+ command.insert(0, first(pkexec))
+
+ return command
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath, pol_file):
+ """
+ Returns a sh script that can copy the missing files.
+
+ :param frompath: The path where the up/down scripts live
+ :type frompath: str
+ :param pol_file: The path where the dynamically generated
+ policy file lives
+ :type pol_file: str
+
+ :rtype: str
+ """
+ to = kls.SYSTEM_CONFIG
+
+ cmd = '#!/bin/sh\n'
+ cmd += 'mkdir -p "%s"\n' % (to, )
+ cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
+ cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
+ cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_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
+ """
+ return {
+ "LD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py
new file mode 100644
index 00000000..935d75f1
--- /dev/null
+++ b/src/leap/bitmask/services/eip/vpnlauncher.py
@@ -0,0 +1,290 @@
+# -*- coding: utf-8 -*-
+# vpnlauncher.py
+# Copyright (C) 2013 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/>.
+"""
+Platform independant VPN launcher interface.
+"""
+import getpass
+import logging
+import os
+import stat
+
+from abc import ABCMeta, abstractmethod
+from functools import partial
+
+from leap.bitmask.config import flags
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.util import first
+from leap.bitmask.util import get_path_prefix
+from leap.common.check import leap_assert, leap_assert_type
+from leap.common.files import which
+
+logger = logging.getLogger(__name__)
+
+
+class VPNLauncherException(Exception):
+ pass
+
+
+class OpenVPNNotFoundException(VPNLauncherException):
+ pass
+
+
+def _has_updown_scripts(path, warn=True):
+ """
+ Checks the existence of the up/down scripts and its
+ exec bit if applicable.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.error("Could not find up/down script %s. "
+ "Might produce DNS leaks." % (path,))
+
+ # XXX check if applies in win
+ is_exe = False
+ try:
+ is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0)
+ except OSError as e:
+ logger.warn("%s" % (e,))
+ if warn and not is_exe:
+ logger.error("Up/down script %s is not executable. "
+ "Might produce DNS leaks." % (path,))
+ return is_file and is_exe
+
+
+def _has_other_files(path, warn=True):
+ """
+ Checks the existence of other important files.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.warning("Could not find file during checks: %s. " % (
+ path,))
+ return is_file
+
+
+class VPNLauncher(object):
+ """
+ Abstract launcher class
+ """
+ __metaclass__ = ABCMeta
+
+ UPDOWN_FILES = None
+ OTHER_FILES = None
+
+ @classmethod
+ @abstractmethod
+ def get_vpn_command(kls, eipconfig, providerconfig,
+ socket_host, socket_port, openvpn_verb=1):
+ """
+ Returns the platform dependant 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
+ """
+ leap_assert_type(eipconfig, EIPConfig)
+ leap_assert_type(providerconfig, ProviderConfig)
+
+ kwargs = {}
+ if flags.STANDALONE:
+ kwargs['path_extension'] = os.path.join(
+ get_path_prefix(), "..", "apps", "eip")
+
+ openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs)
+ if len(openvpn_possibilities) == 0:
+ raise OpenVPNNotFoundException()
+
+ openvpn = first(openvpn_possibilities)
+ args = []
+
+ args += [
+ '--setenv', "LEAPOPENVPN", "1"
+ ]
+
+ if openvpn_verb is not None:
+ args += ['--verb', '%d' % (openvpn_verb,)]
+
+ gateways = []
+ leap_settings = LeapSettings()
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException(kls.tr('No gateway was found!'))
+
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
+
+ for gw in gateways:
+ args += ['--remote', gw, '1194', 'udp']
+
+ args += [
+ '--client',
+ '--dev', 'tun',
+ ##############################################################
+ # persist-tun makes ping-restart fail because it leaves a
+ # broken routing table
+ ##############################################################
+ # '--persist-tun',
+ '--persist-key',
+ '--tls-client',
+ '--remote-cert-tls',
+ 'server'
+ ]
+
+ openvpn_configuration = eipconfig.get_openvpn_configuration()
+ for key, value in openvpn_configuration.items():
+ args += ['--%s' % (key,), value]
+
+ user = getpass.getuser()
+
+ ##############################################################
+ # The down-root plugin fails in some situations, so we don't
+ # drop privs for the time being
+ ##############################################################
+ # args += [
+ # '--user', user,
+ # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
+ # ]
+
+ if socket_port == "unix": # that's always the case for linux
+ args += [
+ '--management-client-user', user
+ ]
+
+ args += [
+ '--management-signal',
+ '--management', socket_host, socket_port,
+ '--script-security', '2'
+ ]
+
+ if _has_updown_scripts(kls.UP_SCRIPT):
+ args += [
+ '--up', '\"%s\"' % (kls.UP_SCRIPT,),
+ ]
+
+ if _has_updown_scripts(kls.DOWN_SCRIPT):
+ args += [
+ '--down', '\"%s\"' % (kls.DOWN_SCRIPT,)
+ ]
+
+ ###########################################################
+ # For the time being we are disabling the usage of the
+ # down-root plugin, because it doesn't quite work as
+ # expected (i.e. it doesn't run route -del as root
+ # when finishing, so it fails to properly
+ # restart/quit)
+ ###########################################################
+ # if _has_updown_scripts(kls.OPENVPN_DOWN_PLUGIN):
+ # args += [
+ # '--plugin', kls.OPENVPN_DOWN_ROOT,
+ # '\'%s\'' % kls.DOWN_SCRIPT # for OSX
+ # '\'script_type=down %s\'' % kls.DOWN_SCRIPT # for Linux
+ # ]
+
+ args += [
+ '--cert', eipconfig.get_client_cert_path(providerconfig),
+ '--key', eipconfig.get_client_cert_path(providerconfig),
+ '--ca', providerconfig.get_ca_cert_path()
+ ]
+
+ command_and_args = [openvpn] + args
+ logger.debug("Running VPN with command:")
+ logger.debug(" ".join(command_and_args))
+
+ return command_and_args
+
+ @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
+ """
+ return {}
+
+ @classmethod
+ def missing_updown_scripts(kls):
+ """
+ Returns what updown scripts are missing.
+
+ :rtype: list
+ """
+ leap_assert(kls.UPDOWN_FILES is not None,
+ "Need to define UPDOWN_FILES for this particular "
+ "launcher before calling this method")
+ file_exist = partial(_has_updown_scripts, warn=False)
+ zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
+
+ @classmethod
+ def missing_other_files(kls):
+ """
+ Returns what other important files are missing during startup.
+ Same as missing_updown_scripts but does not check for exec bit.
+
+ :rtype: list
+ """
+ leap_assert(kls.OTHER_FILES is not None,
+ "Need to define OTHER_FILES for this particular "
+ "auncher before calling this method")
+ file_exist = partial(_has_other_files, warn=False)
+ zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
diff --git a/src/leap/bitmask/services/eip/windowsvpnlauncher.py b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
new file mode 100644
index 00000000..3f1ed43b
--- /dev/null
+++ b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# windowsvpnlauncher.py
+# Copyright (C) 2013 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.
+"""
+import logging
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.common.check import leap_assert
+
+logger = logging.getLogger(__name__)
+
+
+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
+ """
+ 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