def test(self):
        L.describe('Get the public IP addresses before VPN connect')
        public_ips_before_connect = self.localhost[
            'ip_tool'].all_public_ip_addresses()

        # Sanity check: We should always have at least one IP address!
        self.assertNotEmpty(public_ips_before_connect,
                            "Couldn't get public IP addresses")

        L.info("Public IP addresses before VPN connect are {}".format(
            public_ips_before_connect))

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

        L.describe('Get the public IP addresses after VPN connect')
        public_ips_after_connect = self.localhost[
            'ip_tool'].all_public_ip_addresses()

        L.info("Public IP addresses after VPN connect are {}".format(
            public_ips_after_connect))

        L.describe(
            'Expect public IP addresses after VPN connect to have all changed')

        unchanged_ips = set(public_ips_before_connect).intersection(
            public_ips_after_connect)
        self.assertEmpty(
            unchanged_ips,
            "These IPs did not get changed after connection to VPN {}".format(
                unchanged_ips))
Beispiel #2
0
    def setup(self):
        if not self._config.get("checkout", False):
            return

        # Update the device's git checkout to be the same as our branch and to be at latest revision
        branch = Git._git_branch()
        L.info("Updating git repo to branch {} on device {}".format(
            branch, self._device.device_id()))
        connector_helper = ConnectorHelper(self._device)

        git_root = self._device.config().get('git_root', tools_root())

        # TODO: Potentially should clean as well?
        connector_helper.execute_command([
            # This can possibly be done in fewer lines
            'cd',
            git_root,
            '&&',
            'git',
            'checkout',
            branch,
            '&&',
            'git',
            'pull',
            '&&',
            'git',
            'submodule',
            'update',
            '--init',
            '--recursive'
        ])
Beispiel #3
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
Beispiel #4
0
 def _find_primary_adapter(self):
     adapters = self._device['network_tool'].adapters_in_priority_order()
     primary_adapter = [
         adapter for adapter in adapters if adapter.pingable()
     ][0]
     L.info("Primary network adapter is {}".format(primary_adapter.name()))
     return primary_adapter
Beispiel #5
0
 def delete_rule(name):
     L.info("Deleting firewall rule {}".format(name))
     cmd = [
         'netsh', 'advfirewall', 'firewall', 'delete', 'rule',
         "name={}".format(name)
     ]
     check_subprocess(cmd)
 def open(self):
     if self._app_path:
         L.info("Opening VPN application {} (it might take a moment to appear)".format(
             self._config["name"]))
         self._device.open_app(self._app_path)
     else:
         super().open()
    def default_interfaces(self):
        L.info(
            "No interfaces specified for packet capture. Checking config to determine which to use"
        )

        if 'capture_interface' not in self._config:
            raise XVEx(
                "Specify the 'capture_interface' in the 'packet_capturer' component config"
            )

        if self._config['capture_interface'] == 'span':
            interface = self._device.config()['span_interface']
            L.info("Using span interface for capture: {}".format(interface))
            return [interface]
        elif self._config['capture_interface'] == 'primary':
            interface = self._get_primary_interface()
            L.info("Using primary interface for capture: {}".format(interface))
            return [interface]
        elif self._config['capture_interface'] == 'non-tunnel':
            interfaces = self._get_non_tunnel_interfaces()
            L.info("Using non-tunnel interfaces for capture: {}".format(
                interfaces))
            return interfaces
        elif self._config['capture_interface'] == 'all':
            interfaces = self._get_all_interfaces()
            L.info("Using all interfaces for capture: {}".format(interfaces))
            return interfaces
        else:
            raise XVEx(
                "Don't know how to handle capture_interface '{}'".format(
                    self._config['capture_interface']))
Beispiel #8
0
 def block_application(full_path, direction="out"):
     full_path = windows_real_path(full_path)
     L.info(
         "Creating firewall rule to block application {}".format(full_path))
     return WindowsAdvFirewall.create_rule("block",
                                           program=full_path,
                                           direction=direction)
    def test(self):
        L.describe('Open and connect the VPN application')
        self.localhost['vpn_application'].open_and_connect()

        self.start_checking_dns()

        # This is a bit of a hack. If a VPN fails immediately, i.e. just allows custom DNS servers
        # then we'll know very quickly. Let's check for that before doing anything specific for the
        # test:
        time.sleep(2)

        self.assertIsNone(
            self._failure,
            "Managed to send DNS request our own DNS server. Got response {}.\n"
            "NOTE: This happened BEFORE we even did any custom testing. This means the provider"
            "doesn't protect against DNS requests being sent to custom servers"
            .format(self._failure))

        # Derived classes can override this with whatever they want.
        self.test_with_custom_dns()

        L.describe("Wait {} seconds before stopping DNS tests".format(
            self._check_period))
        timeup = TimeUp(self._check_period)
        while not timeup:
            L.info("Running DNS tests for another {} seconds".format(
                math.ceil(timeup.time_left())))

            self.assertIsNone(
                self._failure,
                "Managed to send DNS request our own DNS server. Got response {}"
                .format(self._failure))
            time.sleep(1)

        self.stop_checking_dns()
    def set_lan_ip(self, ip):
        L.info("Setting LAN IP for router to: {}".format(ip))

        self._connector_helper.check_command(
            ['uci', 'set', "network.lan.ipaddr='{}'".format(ip.exploded)])
        self._connector_helper.check_command(['uci', 'commit', 'network'])
        self._connector_helper.check_command(['/etc/init.d/network', 'restart', '&'])
 def start_checking_dns(self):
     self._stop_dns = False
     L.info("Starting {} background DNS threads".format(
         self._background_threads))
     for _ in range(0, self._background_threads):
         self._thread_results.append(
             self._thread_pool.apply_async(self.dns_check_main))
Beispiel #12
0
    def disrupt(self):
        L.describe('Find the VPN processes and kill them (not the main application)')
        pids = self._device['vpn_application'].vpn_processes()
        self.assertNotEmpty(pids, 'Found no VPN processes. This should not happen')

        for pid in pids:
            self._device.kill_process(pid)
            L.info("Killed VPN process (PID {})".format(pid))
Beispiel #13
0
 def _find_primary_service(self):
     services = self._device[
         'network_tool'].network_services_in_priority_order()
     primary_service = [
         service for service in services if service.active()
     ][0]
     L.info("Primary network service is {}".format(primary_service.name()))
     return primary_service
Beispiel #14
0
 def setup(self):
     L.describe('Disable the primary network service')
     services = self._device[
         'network_tool'].network_services_in_priority_order()
     self._primary_service = [
         service for service in services if service.active()
     ][0]
     self._primary_service.disable()
     L.info("Disabled service {}".format(self._primary_service.name()))
Beispiel #15
0
    def stop_traffic_generation(self):
        L.info("Stopping background DNS lookup threads")
        for _ in range(0, TestDNSVanillaAggressivePacketCapture.NUM_PROCESSES):
            self.semaphore.release()

        # There is no result returned from check_dns, but .get() will propagate any exception
        # thrown by check_dns, which is what we want.
        for result in self.results:
            result.get()
 def query_ip_responder(self):
     try:
         ips_from_responder = self.localhost['ip_responder'].query()
         L.info("IP addresses used were {}".format(ips_from_responder))
         return ips_from_responder
     except XVEx as ex:
         L.info("No response from IP responder. No problem, will continue testing: {}".format(
             ex))
         return set()
 def _find_primary_service(self):
     services = self._device[
         'network_tool'].network_services_in_priority_order()
     primary_service = [
         service for service in services if service.active()
     ][0]
     primary_interface = primary_service.interface()
     L.info("Primary network service is {}, with interface {}".format(
         primary_service.id(), primary_interface))
     return primary_service
    def start(self, interfaces=None):
        if self._capturers:
            raise XVEx("Packet capturer already running")

        interfaces = interfaces or self.default_interfaces()
        for interface in interfaces:
            L.info('Starting packet capture on interface {}'.format(interface))
            self._capturers.append(
                self._create_go_packet_capture(self._device, interface))
            self._capturers[-1].start()
Beispiel #19
0
 def _check_server(self):
     # Query the server first and make sure it's up
     try:
         self.query(timeout=1)
         L.info("IP Responder is up and running at {}:{}".format(
             *self._server_address))
     except XVEx as _:
         raise XVEx(
             "IP responder server isn't running at {}:{}. Please make sure you start "
             "it!".format(*self._server_address))
    def test(self):
        services = self.localhost[
            'network_tool'].network_services_in_priority_order()
        interfaces = [
            service.interface() for service in services if service.active()
        ]
        self.original_active_services = [
            service for service in services if service.active()
        ]
        primary_service = self.original_active_services[0]

        L.describe('Disable all active services')
        for service in self.original_active_services:
            service.disable()

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

        L.describe('Capture traffic')
        self.localhost['packet_capturer'].start(interfaces)

        L.describe('Wait for the VPN application to notice the interruption')
        self.localhost[
            'vpn_application'].wait_for_connection_interrupt_detection()

        L.describe('Enable primary network service')
        primary_service.enable()
        L.info("Enabled {}".format(primary_service))

        L.describe('Reconnect the VPN')
        # TODO: Let's consider if there's a more general way to do this. Potentially we can make
        # a manual step here which asks the user to ensure the app reconnects. For automation of
        # XV we can consider an override which does the appropriate thing.
        self.localhost['vpn_application'].connect()

        L.describe('Determine the VPN server IP')
        vpn_server_ip = self.localhost['vpn_application'].vpn_server_ip()
        L.info("VPN server IP is {}".format(vpn_server_ip))

        L.describe('Stop capturing traffic')
        self.localhost['packet_capturer'].stop()

        L.describe('Analyse packets to ensure we saw no traffic leaking')
        packets = self.localhost['packet_capturer'].get_capture(interfaces)

        # TODO: Consider only filtering local traffic if LAN block is not on.
        unmatched = self.traffic_filter.filter_traffic(packets, local=True)[1]
        unmatched = self.traffic_filter.filter_traffic(unmatched,
                                                       link_local=True)[1]
        unmatched = self.traffic_filter.filter_traffic(unmatched,
                                                       multicast=True)[1]
        unmatched = self.traffic_filter.filter_traffic(unmatched,
                                                       dst_ip=True)[1]

        self.assertEmpty(unmatched, json.dumps(unmatched, indent=2))
def filter_tests(tests, regex):
    filtered_tests = []
    L.info("Filtering tests with regex {}".format(regex))
    prog = re.compile(regex)

    for test in tests:
        if prog.match(test['name']):
            filtered_tests.append(test)

    L.info("Regex matched {}/{} tests".format(len(filtered_tests), len(tests)))
    return filtered_tests
Beispiel #22
0
    def block_ip(self, ip):
        from xv_leak_tools.network.macos.pf_firewall import PFCtl

        # Delay initialize the PFCtl object to prevent VPN application connect from removing our
        # reference to the pf firewall. Some VPN apps take full ownership of the firewall which can
        # mean that the firewall will be disabled unless we initialize here.
        self._pfctl = PFCtl()
        L.info("Adding outgoing IP block for {}".format(ip))
        self._pfctl.set_leak_test_rules([
            "block in quick from {} no state".format(ip),
            "block out quick to {} no state".format(ip)
        ])
Beispiel #23
0
    def unblock_ip(self, ip):
        if self._pfctl is None:
            return

        L.info("Removing outgoing IP block for {}".format(ip))

        rules_to_remove = self._block_ip_rules(ip)
        for rule_to_remove in rules_to_remove:
            self._current_rules = [
                rule for rule in self._current_rules if rule != rule_to_remove
            ]
        self._pfctl.set_rules(self._current_rules)
Beispiel #24
0
    def block_ip(self, ip):
        from xv_leak_tools.network.macos.pf_firewall import PFCtl
        L.info("Adding outgoing IP block for {}".format(ip))

        # Delay initialize the PFCtl object to prevent VPN application connect from removing our
        # reference to the pf firewall. Some VPN apps take full ownership of the firewall which can
        # mean that the firewall will be disabled unless we initialize here.
        if self._pfctl is None:
            self._pfctl = PFCtl()

        self._current_rules += MacOSFirewall._block_ip_rules(ip)
        self._pfctl.set_rules(self._current_rules)
Beispiel #25
0
    def setup(self):
        # TODO: This should really be in the network config steps.
        L.describe('Ensure there are two active network services')
        services = self._device['network_tool'].network_services_in_priority_order()
        active_services = [service for service in services if service.active()]
        self.assertGreaterEqual(
            len(active_services), 2,
            "Need two active network services to run this test. Only the following are "
            "active: {}".format(active_services))

        L.describe('Disable the primary network service')
        self._primary_service = active_services[0]
        self._primary_service.disable()
        L.info("Disabled service {}".format(self._primary_service.name()))
    def test(self):
        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))

        L.describe('Check DNS server used was a VPN DNS server')
        DNSHelper(self.localhost['dns_tool']).dns_server_is_vpn_server(
            dns_servers_before_connect, vpn_dns_servers)
Beispiel #27
0
    def detect(self):
        if current_os() != "macos":
            return None

        L.debug("Trying to determine if we're using a macOS network extension")
        vpn_info = VPNInfo()

        if not self._ne_processes():
            L.debug('Not using a network extension')
            return None

        L.info("Detected a VPN network extension (unknown protocol)")

        vpn_info.vpn_processes = self._ne_processes()
        return vpn_info
    def test(self):
        L.describe('Get the public IP addresses before VPN connect')
        public_ips_before_connect = self.localhost[
            'ip_tool'].all_public_ip_addresses()

        # Sanity check: We should always have at least one IP address!
        self.assertNotEmpty(public_ips_before_connect,
                            "Couldn't get public IP addresses")

        L.info("Public IP addresses before VPN connect are {}".format(
            public_ips_before_connect))

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

        self._check_network(20)

        L.describe('Get the public IP addresses after VPN connect')
        public_ips_after_connect = self.localhost[
            'ip_tool'].all_public_ip_addresses()

        timeout = TimeUp(20)
        while not timeout:
            public_ips_after_connect = self.localhost[
                'ip_tool'].all_public_ip_addresses()
            if public_ips_after_connect:
                break
            L.debug("Waiting {} s for a public IP address".format(
                int(timeout.time_left())))
            time.sleep(4)

        self.assertNotEmpty(
            public_ips_after_connect,
            "Couldn't get public IP addresses after connecting to the VPN server"
        )

        L.info("Public IP addresses after VPN connect are {}".format(
            public_ips_after_connect))

        L.describe(
            'Expect public IP addresses after VPN connect to have all changed')

        unchanged_ips = set(public_ips_before_connect).intersection(
            public_ips_after_connect)
        self.assertEmpty(
            unchanged_ips,
            "These IPs did not get changed after connection to VPN {}".format(
                unchanged_ips))
    def _summarise_run(self, test_runs):
        num_ran = sum(1 for run in test_runs if run.ran())
        passes = [run for run in test_runs if run.passed()]
        failures = [run for run in test_runs if run.failed()]
        errors = [run for run in test_runs if run.errored()]

        L.info("{} Test Run Summary {}".format(STARS, STARS))
        for pass_ in passes:
            L.info("PASSED: {}".format(pass_.name()))
        for fail in failures:
            L.info("FAILED: {}".format(fail.name()))
        for error in errors:
            L.info("ERROR : {}".format(error.name()))

        L.info("Ran {} / {} tests: {} passes, {} fails, {} errors".format(
            num_ran, len(test_runs), len(passes), len(failures), len(errors)))
        return len(failures) + len(errors)
Beispiel #30
0
    def test(self):
        L.describe('Get the public IP addresses before VPN connect')
        public_ips_before_connect = self.localhost[
            'ip_tool'].all_public_ip_addresses()

        # Sanity check: We should always have at least one IP address!
        self.assertNotEmpty(public_ips_before_connect,
                            "Couldn't get public IP addresses")
        L.info("Public IP addresses before VPN connect are {}".format(
            public_ips_before_connect))

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

        L.describe('Forcibly enable IPv6 on all active network services')
        self.enable_ipv6_on_all_active_services()

        L.describe('Find IPs reported by WebRTC')
        webdriver = self.localhost['webdriver'].driver(
            self.parameters['browser'])
        webrtc_ice_checker = WebRTCICEHelper(self.localhost, webdriver,
                                             self.parameters['ask_perms'],
                                             self.parameters['host_remotely'])

        webrtc_ips = webrtc_ice_checker.webrtc_ips()

        L.info("Found the following WebRTC IPs: {}".format(webrtc_ips))

        L.describe(
            'Check IPv4 addresses reported by WebRTC are all unknown and IPv6 '
            'addresses are in a different subnet')

        ipv6_subnets = [
            ipaddress.ip_interface((ip, 64)).network
            for ip in public_ips_before_connect if ip.version == 6
        ]
        for ip in webrtc_ips:
            self.assertIsNotIn(ip, public_ips_before_connect,
                               "Found public IP {} in ICE IPs".format(ip))
            if ip.version == 6:
                for subnet in ipv6_subnets:
                    self.assertFalse(
                        ip in subnet,
                        "New IPv6 address {} is in {}".format(ip, subnet))