Exemplo n.º 1
0
    def pingable(self):
        if not self.enabled():
            L.verbose("{} not enabled".format(self.name()))
            return False
        ips = self.ip_addresses()
        if not ips:
            L.verbose("{} has no IP address".format(self.name()))
            return False
        cmd = ['ping', '-n', '1', '-w', '2', '-S', ips[0].exploded, '8.8.8.8']
        try:
            output = check_subprocess(cmd)[0]
            if 'Received = 1' in output:
                return True
            else:
                # Consider this a real error and propagate. It's likely a code issue.
                raise XVEx(
                    "Don't know how to parse ping output: {}".format(output))
        except XVProcessException as ex:
            if 'Lost = 1' in ex.stderr:
                L.debug("Couldn't ping on adapter {}".format(self))
                return False
            L.warning("Ping failed unexpectedly with error '{}'. "
                      "Assuming adapter {} un-pingable.".format(
                          ex.stderr, self))

            return False
    def protocol(self):
        info = self._vpn_info()
        if info is not None and info.protocol:
            return info.protocol

        L.warning("Couldn't determine the VPN protocol. Will fallback to default method")
        return super().protocol()
Exemplo n.º 3
0
    def dns_server_ips(self):
        info = self._vpn_info()
        if info is not None and info.dns_server_ips:
            return info.dns_server_ips

        if not self._config.get('strict', False):
            dns_servers_after_connect = set(
                self._device['dns_tool'].known_servers())
            L.debug(
                "Inferring VPN DNS servers. DNS before connect: {}, DNS after connect: {}"
                .format(self._dns_servers_before_connect,
                        dns_servers_after_connect))

            for server in self._dns_servers_before_connect:
                dns_servers_after_connect.discard(server)

            if dns_servers_after_connect:
                L.warning(
                    "Inferring VPN DNS server IPs from System Configuration. "
                    "This is likely correct, but can be prevented by specifying "
                    "the 'strict' keyword in the VPN configuration.")
                return list(dns_servers_after_connect)
            L.warning("Couldn't find DNS servers by inspecting system.")

        return super().dns_server_ips()
Exemplo n.º 4
0
    def detect(self):
        L.debug("Trying to determine if we're using the l2tp protocol")
        vpn_info = VPNInfo()

        # Assume that no-process definitively means no openvpn
        if not self._l2tp_processes():
            L.debug('Not using l2tp')
            return None

        L.info("Detected l2tp as the current VPN protocol")

        vpn_info.vpn_processes = self._l2tp_processes()
        vpn_info.protocol = 'l2tp'
        # TODO: Do tunnel_interface
        vpn_info.vpn_server_ip = self._get_vpn_server_ip()
        vpn_info.dns_server_ips = []
        vpn_info.tunnel_interface = None

        if vpn_info.vpn_server_ip == ipaddress.ip_address('127.0.0.1'):
            L.warning(
                "Got vpn server IP 127.0.0.1. The app is likely using a proxy. Removing this IP"
            )
            vpn_info.vpn_server_ip = None

        # Prevent anyone who's holding on to this class from using stale data.
        self._reset()
        L.debug(
            "Detected the following info about openvpn: {}".format(vpn_info))
        return vpn_info
Exemplo n.º 5
0
    def configure(self):
        skip_settings = self._config.get('skip_settings', True)
        settings = self._config.get('settings', {})
        human_readable = settings.get('human_readable', None)

        if human_readable is not None:
            L.describe("VPN application should be configured to {}".format(
                human_readable))
        elif settings:
            L.describe(
                "VPN application should be configured to {}".format(settings))

        if not settings:
            L.warning(
                "No settings specified for VPN application. Using current settings."
            )
            return
        elif skip_settings:
            L.warning(
                "Configuring VPN application skipped as skip_settings=True")
            return

        msg = ""
        for key, value in list(settings.items()):
            msg += "  {} => {}\n".format(key, value)

        message_and_await_enter(
            "Configure the VPN application with the following settings:\n{}".
            format(msg))
 def known_servers(self):
     L.warning(
         'TODO: Implement DNS server discovery for Linux. Currently we just return the DNS '
         'server used for a DNSserver')
     dns_servers = [self.lookup()[0]]
     self._check_current_dns_server_is_known(dns_servers)
     return dns_servers
Exemplo n.º 7
0
    def _vpn_server_ip_from_route(self):
        L.debug("Attempting to get VPN server IP from routing table")
        routes = self._device['route'].get_v4_routes()
        if not routes or not self._routes_before_connect:
            return None

        potential_routes = []
        for route in routes:
            if route in self._routes_before_connect:
                continue
            dest_ip = route.destination_ip()
            if dest_ip and route.gateway_ip() and not dest_ip.is_private:
                potential_routes.append(route)

        if len(potential_routes) == 1:
            ip = potential_routes[0].destination_ip()
            L.debug("Got VPN server IP {} from routing table".format(ip))
            return ip
        elif len(potential_routes) >= 1:
            L.warning(
                "Tried to get VPN server from route table but found more than one "
                "candidate:\n{}".format(potential_routes))
        else:
            L.warning(
                "Couldn't find any routes which look like they're for the VPN server"
            )

        return None
    def dns_server_ips(self):
        info = self._vpn_info()
        if info is not None and info.dns_server_ips:
            return info.dns_server_ips

        L.warning("Couldn't find VPN DNS server IPs. Will fallback to default method")
        return super().dns_server_ips()
    def _webrtc_ips(self, url_base):
        url = self._url(url_base)
        L.describe("Open ICE detection webpage {}".format(url))
        self._webdriver.get(url)

        # TODO: This isn't working properly. Also, try to automate
        if self._ask_perms:
            message_and_await_enter(
                "Please grant the browser permissions to use audio/video")

        # Not in love with this but not sure we have a lot of choice. There's no way to know whether
        # the javascript code has done searching for IPs TODO: Verify that
        L.describe("Wait for {} seconds for IPs to report".format(
            WebRTCICEHelper.TIME_TO_WAIT_FOR_IPS))
        time.sleep(WebRTCICEHelper.TIME_TO_WAIT_FOR_IPS)

        if 'xv_leak_tools local ICE IP detection' not in self._webdriver.title:
            raise XVEx("Got unexpected webpage title: {}".format(
                self._webdriver.title))

        L.describe(
            "Check ICE detection webpage doesn't show our public IP addresses")
        inner_html = self._webdriver.find_element_by_id(
            'ice_ips').get_attribute('innerHTML')
        if not inner_html:
            raise XVEx('ice_ips div in webrtc page contained no inner_html')
        json_reponse = json.loads(inner_html)

        if not json_reponse['webrtc_supported']:
            L.warning(
                'WebRTC not supported for this browser. Reporting no ips.')
            return []

        return [ipaddress.ip_address(ip) for ip in json_reponse['ips']]
    def vpn_processes(self):
        info = self._vpn_info()
        if info is not None and info.vpn_processes:
            return info.vpn_processes

        L.warning("Couldn't find VPN processes. Will fallback to default method")
        return super().vpn_processes()
    def report_info(self):
        info = super().report_info()
        try:
            info += self._connector_helper.check_command(['systeminfo'])[0]
        except XVProcessException as ex:
            L.warning("Couldn't get OS info from systeminfo:\n{}".format(ex))

        return info
Exemplo n.º 12
0
 def _start_server(self):
     L.warning("The server must be started manually :(")
     if self._device.connector().execute(['lsof', '-t', '-i', ':4444'])[0]:
         message_and_await_enter("Start the Selenium server")
         while self._device.connector().execute(
             ['lsof', '-t', '-i', ':4444'])[0]:
             L.debug("Waiting for Selenium")
             time.sleep(0.5)
     self._server_running = True
 def close_app(self, app_path, root=False):  # pylint: disable=unused-argument
     pname = windows_path_split(app_path)[1]
     pids = [str(pid) for pid in self.pgrep(pname)]
     if len(pids) > 1:
         L.warning(
             "Closing all pids {} associated to application {}".format(
                 ', '.join(pids), pname))
     for pid in pids:
         self.kill_process(pid)
Exemplo n.º 14
0
 def __del__(self):
     if self._no_disable:
         return
     L.debug("Releasing pfctl reference {}".format(self._token))
     try:
         self._pfctl(['-X', self._token])
     except XVProcessException as ex:
         # This can happen as some providers will aggressively tear down the firewall when they
         # disconnect (using pfctl -d) and thus remove our reference (token) to the firewall.
         L.warning("Failed to terminate pf firewall: {}".format(ex))
    def vpn_server_ip(self):
        info = self._vpn_info()
        if info is not None and info.vpn_server_ip:
            return info.vpn_server_ip

        ip = self._vpn_server_ip_from_route()
        if ip:
            return ip

        L.warning("Couldn't find VPN server IP. Will fallback to default method")
        return super().vpn_server_ip()
Exemplo n.º 16
0
    def _get_public_ip_addresses(self, url, prog):
        stdout = self._url_getter(url)[0]

        matches = prog.match(stdout)
        if not matches:
            L.warning(
                "Couldn't determine public IP address using {}. Got response\n{}"
                .format(url, stdout))
            return []

        return [ipaddress.ip_address(matches.group(1))]
    def _close_vpn_applications(self):
        for application in self._vpn_applications:
            for pid in self._device.pgrep(application):
                # Warn the user because ideally tests should start on a clean machine
                L.warning("Cleanup killing processes for application {}".format(application))
                self._device.kill_process(pid)

        for application in self._unkillable_applications:
            if self._device.pgrep(application):
                L.warning("Application {} is open but I don't know how to kill it".format(
                    application))
Exemplo n.º 18
0
 def _windump_index(guid, windump_lines):
     # Try to parse something
     # 1.\Device\NPF_{0F888A67-F8CF-4B5E-8812-5473E4A1D365} (ExpressVPN Tap Adapter)
     for line in windump_lines:
         match = _PROG_WINDUMP.match(line)
         if not match:
             continue
         if match.group(2) == guid:
             return match.group(1)
     L.warning(
         "Couldn't find windump interface index for adapter with GUID {}".
         format(guid))
     return "0"
    def _get_packets(self):
        dst = os.path.join(self._device.temp_directory(), self._capture_file)
        timeup = TimeUp(5)
        while not timeup:
            if os.path.exists(dst):
                break
            L.warning("Waiting for capture file to be written: {}".format(dst))
            time.sleep(1)

        if not os.path.exists(dst):
            raise XVEx("Couldn't get capture file from capture device")

        return object_from_json_file(dst, 'attribute')['data']
    def build(self, device, config):
        if not isinstance(device, DesktopDevice):
            raise ComponentNotSupported(
                "Can't create firewall tool for : {}".format(device.os_name()))

        if device.os_name() == 'macos':
            return MacOSFirewall(device, config)
        elif device.os_name() == 'windows':
            return WindowsFirewall(device, config)
        elif device.os_name() == 'linux':
            L.warning("Firewall not implemented for linux yet")
            raise ComponentNotSupported(
                "Firewall not implemented for linux yet")
Exemplo n.º 21
0
 def _kill_server(self):
     if self._server_running:
         L.debug("Killing Selenium server")
         selenium_process = self._device.connector().execute(
             ['lsof', '-t', '-i', ':4444'])
         if not selenium_process[0]:
             selenium_pid = selenium_process[1].strip()
             self._device.connector().execute(['kill', selenium_pid])
             self._server_running = False
         else:
             L.warning(
                 "Couldn't find the Selenium server process; try killing Java"
             )
    def _sort_services(services, service_order):
        sorted_services = [None] * len(service_order)
        for service in services:
            try:
                sorted_services[service_order.index(service.id())] = service
            except ValueError as _:
                L.warning(
                    "Couldn't determine service order. Couldn't find order for service {}"
                    .format(service.name()))

        # It's possible that we had more service order items than services, in which case we strip
        # them. Let's hope it can't be the other way around (presumably we'll have already got an
        # exception in that case!)
        return [service for service in sorted_services if service is not None]
    def discover_device(self, discovery_keys):
        if 'device_id' not in discovery_keys or discovery_keys['device_id'] != 'localhost':
            return None

        if len(discovery_keys) != 1:
            L.warning(
                "Only the 'device_id' discovery key is valid for discovering localhost. The others "
                "will be ignored: {}".format(discovery_keys))

        if current_os() == 'windows':
            connector = WindowsLocalShellConnector()
        else:
            connector = LocalShellConnector()
        return create_device(current_os(), self._localhost_config, connector)
Exemplo n.º 24
0
def punch_hole_in_firewall(ips):
    if current_os() == 'macos':
        pf = PFCtl()
        ip_list = '{ ' + ', '.join(ips) + ' }'
        pf.set_rules([
            "pass in quick from {} no state".format(ip_list),
            "pass out quick to {} no state".format(ip_list)
        ])
    elif current_os() == 'windows':
        L.warning(
            "Ignoring option to open up firewall for {} on Windows".format(
                ', '.join(ips)))
    else:
        raise XVEx('Editing the firewall is only supported for PF/macOS')
Exemplo n.º 25
0
    def dns_server_ips(self):
        if not self._tap_adapter_name:
            L.debug(
                "Don't know TAP adapter name for VPN application {}. Falling back to default "
                "method to get DNS server IPs".format(self._config["name"]))
            return super().dns_server_ips()

        adapter = self._device['network_tool'].adapter_by_name(self._tap_adapter_name)
        if adapter:
            return adapter.dns_servers()

        L.warning(
            "Couldn't find TAP adapter '{}' for VPN application '{}'. Falling back to default "
            "method to get DNS server IPs".format(self._tap_adapter_name, self._config["name"]))
        return super().dns_server_ips()
    def _protocol_from_config_file(self):
        L.debug(
            "Trying to determine if openvpn is using UDP or TCP by looking at the config file")
        if not self._config_file():
            L.debug("Couldn't get config file so can't establish which protocol is being used")
            return None

        with open(self._config_file()) as file_:
            for line in file_.readlines():
                for protocol in OpenVPNDetector.UDP_TCP:
                    if protocol in line:
                        L.debug("Detected protocol {} from the config file".format(protocol))
                        return protocol
        L.warning("Couldn't determine openvpn protocol from config file {}".format(
            self._config_file()))
        return None
    def report_info(self):
        info = super().report_info()
        commands = [
            ['uname', '-a'],
            ['lsb_release', '-a'],
            ['lscpu'],
        ]
        for command in commands:
            try:
                info += self._connector_helper.check_command(command)[0]
            except XVProcessException as ex:
                L.warning(
                    "Couldn't get system info using command {}:\n{}".format(
                        command, ex))

        return info
    def test(self):
        L.info("Using disrupter: {}".format(self.disrupter))

        L.describe('Find all known DNS servers before connecting to VPN')
        dns_servers_before_connect = self.localhost['dns_tool'].known_servers()

        L.info(
            "All known DNS servers are: {}".format(dns_servers_before_connect))

        L.describe('Open and connect the VPN application')
        self.localhost['vpn_application'].open_and_connect()

        vpn_dns_servers = self.localhost['vpn_application'].dns_server_ips()
        L.info("VPN DNS servers are: {}".format(vpn_dns_servers))

        self._check_network(time_limit=20)

        L.describe('Check DNS server used was a VPN DNS server')
        self.dns_helper.dns_server_is_vpn_server(dns_servers_before_connect,
                                                 vpn_dns_servers)

        L.describe("Create disruption...")
        self.disrupter.create_disruption()

        L.describe(
            "Do some DNS lookups for the next {} seconds and check the DNS server used is a VPN "
            "DNS Server".format(self._check_period))

        timeup = TimeUp(self._check_period)
        message_time = self._check_period
        while not timeup:
            if timeup.time_left() < message_time:
                L.info("Doing DNS lookup tests for another {} seconds".format(
                    math.ceil(timeup.time_left())))
                message_time = message_time - 5

            try:
                # Max timeout for each DNS lookup is 2 seconds.
                timeout = min(timeup.time_left(), 2)
                self.dns_helper.dns_server_is_vpn_server(
                    dns_servers_before_connect,
                    vpn_dns_servers,
                    timeout=timeout)

            except XVEx as ex:
                L.warning("DNS lookup failed, assuming no leak: {}".format(ex))
Exemplo n.º 29
0
    def _get_ips(self, msg, max_ips=0):
        msg = msg + " Press enter after each one. When finished, just press enter."
        ips = []
        while 1:
            ip_string = message_and_await_string(msg)
            if not ip_string:
                break
            try:
                ips.append(ipaddress.ip_address(ip_string))
                if max_ips != 0 and len(ips) == max_ips:
                    break
            except (ipaddress.AddressValueError, ValueError) as ex:
                L.warning(
                    "{}: invalid IP address. Please re-enter.".format(ex))

        if not ips:
            L.warning('User did not provide any valid IP addresses')
        return ips
    def _clean_parameters(self, parameters):
        supported_parameters = self.supported_parameters()
        for name, _ in list(parameters.items()):
            if name not in supported_parameters:
                L.warning(
                    "Parameter {} not supported for test {}. It will be ignored"
                    .format(name, self.__class__.__name__))
                continue

        for name, supported_parameter in list(supported_parameters.items()):
            if name not in parameters:
                if supported_parameter.required:
                    raise XVEx("Parameter {} is mandatory for test {}".format(
                        name, self.__class__.__name__))
                else:
                    L.debug("Defaulting test parameter '{}' to {}".format(
                        name, copy.deepcopy(supported_parameter.default)))
                    parameters[name] = supported_parameter.default
        return parameters