diff options
Diffstat (limited to 'src/leap/services/eip/vpnlaunchers.py')
-rw-r--r-- | src/leap/services/eip/vpnlaunchers.py | 188 |
1 files changed, 137 insertions, 51 deletions
diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py index fa2989bc..af77c146 100644 --- a/src/leap/services/eip/vpnlaunchers.py +++ b/src/leap/services/eip/vpnlaunchers.py @@ -35,6 +35,7 @@ from leap.common.check import leap_assert, leap_assert_type from leap.common.files import which from leap.config.providerconfig import ProviderConfig from leap.services.eip.eipconfig import EIPConfig, VPNGatewaySelector +from leap.util import first logger = logging.getLogger(__name__) @@ -59,9 +60,11 @@ class VPNLauncher: """ Abstract launcher class """ - __metaclass__ = ABCMeta + UPDOWN_FILES = None + OTHER_FILES = None + @abstractmethod def get_vpn_command(self, eipconfig=None, providerconfig=None, socket_host=None, socket_port=None): @@ -97,6 +100,35 @@ class VPNLauncher: """ 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 " + "auncher 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.UPDOWN_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] + def get_platform_launcher(): launcher = globals()[platform.system() + "VPNLauncher"] @@ -117,7 +149,8 @@ def _is_pkexec_in_system(): def _has_updown_scripts(path, warn=True): """ - Checks the existence of the up/down scripts. + Checks the existence of the up/down scripts and its + exec bit if applicable. :param path: the path to be checked :type path: str @@ -132,6 +165,7 @@ def _has_updown_scripts(path, warn=True): logger.error("Could not find up/down script %s. " "Might produce DNS leaks." % (path,)) + # XXX check if applies in win is_exe = os.access(path, os.X_OK) if warn and not is_exe: logger.error("Up/down script %s is not executable. " @@ -139,6 +173,25 @@ def _has_updown_scripts(path, warn=True): 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 + + def _is_auth_agent_running(): """ Checks if a polkit daemon is running. @@ -160,8 +213,59 @@ class LinuxVPNLauncher(VPNLauncher): PKEXEC_BIN = 'pkexec' OPENVPN_BIN = 'openvpn' - UP_DOWN_SCRIPT = "/etc/leap/resolv-update" - OPENVPN_DOWN_ROOT = "/usr/lib/openvpn/openvpn-down-root.so" + 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 = "/usr/lib/openvpn/openvpn-plugin-down-root.so" + + POLKIT_BASE = "/usr/share/polkit-1/actions" + POLKIT_FILE = "net.openvpn.gui.leap.policy" + POLKIT_PATH = "%s/%s" % (POLKIT_BASE, POLKIT_FILE) + + UPDOWN_FILES = (UP_DOWN_PATH,) + OTHER_FILES = (POLKIT_PATH,) + + @classmethod + def cmd_for_missing_scripts(kls, frompath): + """ + Returns a command that can copy the missing scripts. + :rtype: str + """ + to = kls.SYSTEM_CONFIG + cmd = "#!/bin/sh\nset -e\nmkdir -p %s\ncp %s/%s %s\ncp %s/%s %s" % ( + to, + frompath, kls.UP_DOWN_FILE, to, + frompath, kls.POLKIT_FILE, kls.POLKIT_PATH) + return cmd + + @classmethod + def maybe_pkexec(kls): + """ + Checks whether pkexec is available in the system, and + returns the path if found. + + Might raise EIPNoPkexecAvailable or EIPNoPolkitAuthAgentAvailable + + :returns: a list of the paths where pkexec is to be found + :rtype: list + """ + if _is_pkexec_in_system(): + 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() def get_vpn_command(self, eipconfig=None, providerconfig=None, socket_host=None, socket_port="unix"): @@ -201,44 +305,34 @@ class LinuxVPNLauncher(VPNLauncher): providerconfig.get_path_prefix(), "..", "apps", "eip") - openvpn_possibilities = which( - self.OPENVPN_BIN, - **kwargs) + openvpn_possibilities = which(self.OPENVPN_BIN, **kwargs) if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() - openvpn = openvpn_possibilities[0] + openvpn = first(openvpn_possibilities) args = [] - if _is_pkexec_in_system(): - if _is_auth_agent_running(): - pkexec_possibilities = which(self.PKEXEC_BIN) - leap_assert(len(pkexec_possibilities) > 0, - "We couldn't find pkexec") - args.append(openvpn) - openvpn = pkexec_possibilities[0] - 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() + pkexec = self.maybe_pkexec() + if pkexec: + args.append(openvpn) + openvpn = first(pkexec) # TODO: handle verbosity gateway_selector = VPNGatewaySelector(eipconfig) - gateway_ip = gateway_selector.get_best_gateway_ip() + gateways = gateway_selector.get_gateways() - logger.debug("Using gateway ip %s" % (gateway_ip,)) + logger.debug("Using gateways ips: {}".format(', '.join(gateways))) + + for gw in gateways: + args += ['--remote', gw, '1194', 'udp'] args += [ '--client', '--dev', 'tun', '--persist-tun', '--persist-key', - '--remote', gateway_ip, '1194', 'udp', '--tls-client', '--remote-cert-tls', 'server' @@ -265,12 +359,12 @@ class LinuxVPNLauncher(VPNLauncher): '--script-security', '2' ] - if _has_updown_scripts(self.UP_DOWN_SCRIPT): + if _has_updown_scripts(self.UP_DOWN_PATH): args += [ - '--up', self.UP_DOWN_SCRIPT, - '--down', self.UP_DOWN_SCRIPT, + '--up', self.UP_DOWN_PATH, + '--down', self.UP_DOWN_PATH, '--plugin', self.OPENVPN_DOWN_ROOT, - '\'script_type=down %s\'' % self.UP_DOWN_SCRIPT + '\'script_type=down %s\'' % self.UP_DOWN_PATH ] args += [ @@ -324,17 +418,6 @@ class DarwinVPNLauncher(VPNLauncher): UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN) @classmethod - def missing_updown_scripts(kls): - """ - Returns what updown scripts are missing. - :rtype: list - """ - 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 cmd_for_missing_scripts(kls, frompath): """ Returns a command that can copy the missing scripts. @@ -387,22 +470,24 @@ class DarwinVPNLauncher(VPNLauncher): if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() - openvpn = openvpn_possibilities[0] + openvpn = first(openvpn_possibilities) args = [openvpn] # TODO: handle verbosity gateway_selector = VPNGatewaySelector(eipconfig) - gateway_ip = gateway_selector.get_best_gateway_ip() + gateways = gateway_selector.get_gateways() - logger.debug("Using gateway ip %s" % (gateway_ip,)) + logger.debug("Using gateways ips: {}".format(', '.join(gateways))) + + for gw in gateways: + args += ['--remote', gw, '1194', 'udp'] args += [ '--client', '--dev', 'tun', '--persist-tun', '--persist-key', - '--remote', gateway_ip, '1194', 'udp', '--tls-client', '--remote-cert-tls', 'server' @@ -489,6 +574,8 @@ class WindowsVPNLauncher(VPNLauncher): OPENVPN_BIN = 'openvpn_leap.exe' + # XXX UPDOWN_FILES ... we do not have updown files defined yet! + def get_vpn_command(self, eipconfig=None, providerconfig=None, socket_host=None, socket_port="9876"): """ @@ -528,29 +615,30 @@ class WindowsVPNLauncher(VPNLauncher): if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() - openvpn = openvpn_possibilities[0] + openvpn = first(openvpn_possibilities) args = [] # TODO: handle verbosity gateway_selector = VPNGatewaySelector(eipconfig) - gateway_ip = gateway_selector.get_best_gateway_ip() + gateways = gateway_selector.get_gateways() + + logger.debug("Using gateways ips: {}".format(', '.join(gateways))) - logger.debug("Using gateway ip %s" % (gateway_ip,)) + for gw in gateways: + args += ['--remote', gw, '1194', 'udp'] args += [ '--client', '--dev', 'tun', '--persist-tun', '--persist-key', - '--remote', gateway_ip, '1194', 'udp', '--tls-client', '--remote-cert-tls', 'server' ] openvpn_configuration = eipconfig.get_openvpn_configuration() - # XXX sanitize this for key, value in openvpn_configuration.items(): args += ['--%s' % (key,), value] @@ -558,13 +646,11 @@ class WindowsVPNLauncher(VPNLauncher): '--user', getpass.getuser(), #'--group', grp.getgrgid(os.getgroups()[-1]).gr_name ] - args += [ '--management-signal', '--management', socket_host, socket_port, '--script-security', '2' ] - args += [ '--cert', eipconfig.get_client_cert_path(providerconfig), '--key', eipconfig.get_client_cert_path(providerconfig), |