Exemple #1
0
    def test_authentication_sessions_till_disconnect(self):
        mac = 'aa:bb:cc:dd:ee:01'
        session.seen(mac=mac, vlan='eth0.1', ip='1.2.3.1')
        session.add_authentication_session(mac, source='test')

        self.assertTrue(session.source_in_authentication_sessions(mac, 'test'))
        self.assertEqual(session.get_authentication_sessions(mac),
                         [{
                             'till_disconnect': True,
                             'source': 'test'
                         }])
        self.assertEqual(
            session.get_authentication_sessions(mac, source='test'),
            [{
                'till_disconnect': True,
                'source': 'test'
            }])
        self.assertEqual(
            session.get_authentication_sessions(mac, till_disconnect=False),
            [])

        session.end(mac)

        self.assertFalse(session.source_in_authentication_sessions(
            mac, 'test'))
        self.assertEqual(session.get_authentication_sessions(mac), [])
Exemple #2
0
    def test_seen_new_mac_new_vlan_new_ip(self, notify_new_MAC_session,
                                          notify_new_VLAN_session,
                                          notify_new_IP_session):
        response = session.seen(mac='aa:bb:cc:dd:ee:01',
                                vlan='eth0.1',
                                ip='1.2.3.4')

        self.assertEqual(response, (True, True, True))
        self.assertEqual(notify_new_MAC_session.call_count, 0)
        self.assertEqual(notify_new_VLAN_session.call_count, 0)
        self.assertEqual(notify_new_IP_session.call_count, 1)
        self.assertTrue(session.is_online(mac='aa:bb:cc:dd:ee:01'))
        self.assertTrue(
            session.is_online(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1'))
        self.assertTrue(
            session.is_online(mac='aa:bb:cc:dd:ee:01',
                              vlan='eth0.1',
                              ip='1.2.3.4'))

        response = session.seen(mac='aa:bb:cc:dd:ee:01',
                                vlan='eth0.1',
                                ip='1.2.3.4')

        self.assertEqual(response, (False, False, False))
        self.assertEqual(notify_new_MAC_session.call_count, 0)
        self.assertEqual(notify_new_VLAN_session.call_count, 0)
        self.assertEqual(notify_new_IP_session.call_count, 1)  # Not changed
Exemple #3
0
    def test_seen_known_mac(self, notify_new_MAC_session,
                            notify_new_VLAN_session, notify_new_IP_session):
        session.seen(mac='aa:bb:cc:dd:ee:01')
        notify_new_MAC_session.reset_mock()

        response = session.seen(mac='aa:bb:cc:dd:ee:01')

        self.assertEqual(response, (False, False, False))
        self.assertEqual(notify_new_MAC_session.call_count, 0)
        self.assertEqual(notify_new_VLAN_session.call_count, 0)
        self.assertEqual(notify_new_IP_session.call_count, 0)
        self.assertTrue(session.is_online(mac='aa:bb:cc:dd:ee:01'))
Exemple #4
0
    def set_port_of_mac(self, mac):
        '''
        Will try to fond port of given
        '''
        ports_with_new_macs = self.get_ports_with_new_macs()

        if ports_with_new_macs:
            device_ips = { port.pop('device_ip') for port in ports_with_new_macs }
            for device_ip in device_ips:
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
                mac_ports = loop.run_until_complete(self.get_macs_on_device(device_ip))
                loop.close()
                port = mac_ports.get(mac, None)
                if port in ports_with_new_macs:
                    session.seen(mac, port=port)
                    port['device_ip'] = device_ip
                    self.port_has_no_new_macs(port)
                    break
Exemple #5
0
async def get_assignments(request):
    ''' Will create new session for Mac and allow it on VLAN and on the net if authorized'''
    mac = extract_mac(request.get('Calling-Station-Id', None))

    port = await find_port(request)

    session.seen(mac, port=port)

    auth_type = request.get('ELAN-Auth-Type', None)

    if auth_type == 'radius-mac':
        authz = await asyncio.get_event_loop().run_in_executor(
            None, nac.checkAuthz, mac)
    elif auth_type == 'radius-dot1x':
        authentication_provider = request.get('ELAN-Auth-Provider')
        login = request.get('ELAN-Login', request.get('User-Name'))
        authz = await asyncio.get_event_loop().run_in_executor(
            None,
            functools.partial(nac.newAuthz,
                              mac,
                              no_duplicate_source=True,
                              source='radius-dot1x',
                              till_disconnect=True,
                              authentication_provider=authentication_provider,
                              login=login))

    if not authz:
        # log no assignment rule matched....
        event = Event(event_type='device-not-authorized',
                      source=auth_type,
                      level='warning')
        event.add_data('mac', mac, 'mac')
        event.add_data('port', port, 'port')
        if auth_type == 'radius-dot1x':
            event.add_data('authentication_provider', authentication_provider,
                           'authentication')
            event.add_data('login', login)
        event.notify()

        raise NotAuthorized

    return {'ELAN-Vlan-Id': str(authz.assign_vlan)}
Exemple #6
0
    def test_get_current_session_ids(self):
        session.seen(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1', ip='1.2.3.4')
        session.seen(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1', ip='1.2.3.5')
        session.seen(mac='aa:bb:cc:dd:ee:02', vlan='eth0.1')
        session.seen(mac='aa:bb:cc:dd:ee:02', vlan='eth0.2')
        session.seen(mac='aa:bb:cc:dd:ee:03')

        session_ids = session.get_current_session_ids()

        self.assertEqual(
            set(session_ids.keys()), {
                ('aa:bb:cc:dd:ee:01', 'eth0.1', '1.2.3.4'),
                ('aa:bb:cc:dd:ee:01', 'eth0.1', '1.2.3.5'),
                ('aa:bb:cc:dd:ee:01', 'eth0.1', None),
                ('aa:bb:cc:dd:ee:02', 'eth0.1', None),
                ('aa:bb:cc:dd:ee:02', 'eth0.2', None),
                ('aa:bb:cc:dd:ee:01', None, None),
                ('aa:bb:cc:dd:ee:02', None, None),
                ('aa:bb:cc:dd:ee:03', None, None),
            })
Exemple #7
0
    def test_seen_new_mac(self, notify_new_MAC_session,
                          notify_new_VLAN_session, notify_new_IP_session,
                          notify_MAC_port):
        response = session.seen(mac='aa:bb:cc:dd:ee:01')

        self.assertEqual(response, (True, False, False))
        self.assertEqual(notify_new_MAC_session.call_count, 1)
        self.assertEqual(notify_new_VLAN_session.call_count, 0)
        self.assertEqual(notify_new_IP_session.call_count, 0)
        self.assertEqual(notify_MAC_port.call_count, 0)
        self.assertTrue(session.is_online(mac='aa:bb:cc:dd:ee:01'))
Exemple #8
0
    def test_end(self, notify_end_IP_session, notify_end_VLAN_session,
                 notify_end_MAC_session):
        session.seen(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1', ip='1.2.3.1')
        session.seen(mac='aa:bb:cc:dd:ee:02', vlan='eth0.2', ip='1.2.3.2')
        session.seen(mac='aa:bb:cc:dd:ee:03', vlan='eth0.3', ip='1.2.3.3')

        session.end(mac='aa:bb:cc:dd:ee:01')

        self.assertFalse(session.is_online('aa:bb:cc:dd:ee:01'))
        self.assertFalse(session.is_online('aa:bb:cc:dd:ee:01', vlan='eth0.1'))
        self.assertFalse(
            session.is_online('aa:bb:cc:dd:ee:01', vlan='eth0.1',
                              ip='1.2.3.1'))
        self.assertEqual(notify_end_MAC_session.call_count, 1)

        session.end(mac='aa:bb:cc:dd:ee:02', vlan='eth0.2')

        self.assertTrue(session.is_online('aa:bb:cc:dd:ee:02'))
        self.assertFalse(session.is_online('aa:bb:cc:dd:ee:02', vlan='eth0.2'))
        self.assertFalse(
            session.is_online('aa:bb:cc:dd:ee:02', vlan='eth0.2',
                              ip='1.2.3.2'))
        self.assertEqual(notify_end_VLAN_session.call_count, 1)

        session.end(mac='aa:bb:cc:dd:ee:03', vlan='eth0.3', ip='1.2.3.3')

        self.assertTrue(session.is_online('aa:bb:cc:dd:ee:03'))
        self.assertTrue(session.is_online('aa:bb:cc:dd:ee:03', vlan='eth0.3'))
        self.assertFalse(
            session.is_online('aa:bb:cc:dd:ee:03', vlan='eth0.3',
                              ip='1.2.3.3'))
        self.assertEqual(notify_end_IP_session.call_count, 1)
Exemple #9
0
    async def parse_trap_str(self, trap_str, timeout=5):
        '''
        parse the trap and return
        - trap type
        - port (ip???/interface)
        - mac if present

        '''
        # if creds not known, poll switch
        # If creds ,  parse trap,-> smart way, trying each one... and saving the matches...,
        # -> parse trap... (create a thread just to process trap ?)
        # -> check if swicth/ switch port known and if not poll
        splitted_trap_str = trap_str.split('|', 3)
        trap_time = (datetime.datetime.strptime('{} {}'.format(*splitted_trap_str[0:2]), '%Y-%m-%d %H:%M:%S') - datetime.datetime(1970, 1, 1)).total_seconds()  # Epoch

        snmp_connection_str = splitted_trap_str[2]
        # example of snmp_connection_str: UDP: [10.30.0.2]:56550->[10.30.0.5] -> grab what is enclosed in first brackets
        device_ip = snmp_connection_str.split(']', 1)[0].split('[', 1)[1]

        # Grab SNMP read credentials of device
        read_params = await self.get_read_params(device_ip)
        if not read_params:
            return

        try:
            trap = await asyncio.wait_for(self._parse_trap_str(device_ip, trap_str, read_params), timeout)
        except asyncio.TimeoutError:
            trap = None

        if trap and trap['trapType'] != 'unknown':
            '''
            Trap types:
              - up:
                  - trapIfIndex
              - down
                  - trapIfIndex
              - mac:
                  - trapOperation:
                      - learnt
                      - removed
                      - unknown -> what do we do ? -> macsuck ?
                  - trapIfIndex
                  - trapMac
                  - trapVlan
              - secureMacAddrViolation:
                  - trapIfIndex
                  - trapMac
                  - trapVlan
              - dot11Deauthentication:
                  - trapMac
              - wirelessIPS
                  - trapMac
              - roaming
                  - trapSSID
                  - trapIfIndex
                  - trapVlan
                  - trapMac
                  - trapClientUserName
                  - trapConnectionType
            '''
            if 'trapMac' in trap:
                if trap['trapType'] in ['secureMacAddrViolation', 'wirelessIPS', 'roaming'] or \
                  (trap['trapType'] == ['mac'] and trap['trapOperation'] == 'learnt'):
                    port = await self.getPortFromIndex(device_ip, trap.get('trapIfIndex', None))
                    vlan = trap.get('vlan', None)
                    session.seen(trap['trapMac'], vlan=vlan, port=port, time=trap_time)
                    if port is None:
                        DebugEvent(source='snmp-notification')\
                            .add_data('details', 'Port not found')\
                            .add_data('trap', trap)\
                            .add_data('trap_str', trap_str)\
                            .notify()
                elif trap['trapType'] == 'dot11Deauthentication'or \
                    (trap['trapType'] == ['mac'] and trap['trapOperation'] == 'removed'):
                    session.end(mac=trap['trapMac'], time=trap_time)
            elif trap['trapType'] in ['up', 'down']:
                port = await self.getPortFromIndex(device_ip, trap['trapIfIndex'])
                if port:
                    port['device_ip'] = device_ip
                    if trap['trapType'] == 'up':
                        # remember this port could have new ip.
                        self.port_has_new_macs(port)
                    else:
                        self.port_has_no_new_macs(port)
                        # remove macs that are no longer on port
                        mac_ports = await self.get_macs_on_device(device_ip)
                        for mac in session.port_macs(port):
                            if mac not in mac_ports:
                                session.end(mac)

            # TODO: Mark Port as potentially containing a new  mac -> macksuck when new mac?
        else:
            event = Event(event_type='runtime-failure', source='snmp-notification', level='warning')
            event.add_data('ip', device_ip)
            event.notify()
Exemple #10
0
    def test_notify_current_sessions(self):
        session.seen(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1', ip='1.2.3.4')
        session.seen(mac='aa:bb:cc:dd:ee:01', vlan='eth0.1', ip='1.2.3.5')
        session.seen(mac='aa:bb:cc:dd:ee:02', vlan='eth0.1')
        session.seen(mac='aa:bb:cc:dd:ee:02', vlan='eth0.2')
        session.seen(mac='aa:bb:cc:dd:ee:02')
        session.seen(mac='aa:bb:cc:dd:ee:03')

        self.assertEqual(session.notify_current_sessions(), 5)
Exemple #11
0
    def test_seen_with_port(self, notify_MAC_port, notify_end_MAC_session):
        self.assertIsNone(session.mac_port('aa:bb:cc:dd:ee:01'))

        session.seen(mac='aa:bb:cc:dd:ee:01',
                     port={
                         'local_id': 1,
                         'interface': 'i1'
                     })

        self.assertTrue(session.is_online(mac='aa:bb:cc:dd:ee:01'))
        self.assertEqual(session.mac_port('aa:bb:cc:dd:ee:01'), {
            'local_id': 1,
            'interface': 'i1'
        })
        self.assertEqual(notify_MAC_port.call_count, 0)
        self.assertEqual(notify_end_MAC_session.call_count, 0,
                         'No session end on same port')

        session.seen(mac='aa:bb:cc:dd:ee:01',
                     port={
                         'local_id': 1,
                         'interface': 'i2'
                     })

        self.assertEqual(session.mac_port('aa:bb:cc:dd:ee:01'), {
            'local_id': 1,
            'interface': 'i2'
        })
        self.assertEqual(notify_MAC_port.call_count, 0)
        self.assertEqual(notify_end_MAC_session.call_count, 1,
                         'session ended on change of port')

        session.seen(mac='aa:bb:cc:dd:ee:01',
                     port={
                         'local_id': 1,
                         'interface': None
                     })

        self.assertEqual(session.mac_port('aa:bb:cc:dd:ee:01'), {
            'local_id': 1,
            'interface': 'i2'
        })
        self.assertEqual(notify_MAC_port.call_count, 0)
        self.assertEqual(notify_end_MAC_session.call_count, 1,
                         'Session end on port change')

        session.seen(mac='aa:bb:cc:dd:ee:01',
                     port={
                         'local_id': 2,
                         'interface': None,
                         'ssid': 'SSID'
                     })

        self.assertEqual(session.mac_port('aa:bb:cc:dd:ee:01'), {
            'local_id': 2,
            'interface': None,
            'ssid': 'SSID'
        })
        self.assertEqual(notify_MAC_port.call_count, 0)
        self.assertEqual(notify_end_MAC_session.call_count, 2,
                         'No session end on same port')

        session.seen(mac='aa:bb:cc:dd:ee:01',
                     port={
                         'local_id': 2,
                         'interface': 'i3'
                     })

        self.assertEqual(session.mac_port('aa:bb:cc:dd:ee:01'), {
            'local_id': 2,
            'interface': 'i3',
            'ssid': 'SSID'
        })
        self.assertEqual(notify_MAC_port.call_count, 1)
        self.assertEqual(notify_end_MAC_session.call_count, 2,
                         'No session end on same port')
    def process_packet(self, packet):
        try:
            # device sessions
            mac = packet.eth.src
            if session.ignore_MAC(mac):
                return

            nic = self.interfaces[int(packet.frame_info.interface_id)]
            vlan_id = 0
            packet_vlan = getattr(packet, 'vlan', None)
            if packet_vlan:
                vlan_id = packet_vlan.id
            vlan = '{nic}.{vlan_id}'.format(nic=nic, vlan_id=vlan_id)
            epoch = int(float(packet.frame_info.time_epoch))

            if packet.highest_layer == 'ARP' \
                or \
               packet.highest_layer == 'ICMPV6' and str(packet.icmpv6.type) in ('136', '135'):  # ('Neighbor Advertisement', 'Neighbor Solicitation')

                if packet.highest_layer == 'ARP':
                    ip = packet.arp.src_proto_ipv4
                else:
                    ip = packet.ipv6.src

                if session.ignore_IP(ip):
                    mac_added, vlan_added, _ip_added = session.seen(mac,
                                                                    vlan=vlan,
                                                                    time=epoch)
                else:
                    mac_added, vlan_added, _ip_added = session.seen(mac,
                                                                    vlan=vlan,
                                                                    ip=ip,
                                                                    time=epoch)
            else:
                mac_added, vlan_added, _ip_added = session.seen(mac,
                                                                vlan=vlan,
                                                                time=epoch)

            tasks = []  # tasks to be launched in thread

            if mac_added:
                tasks.append(
                    functools.partial(DeviceSnmpManager().set_port_of_mac,
                                      mac))

            if vlan_added and nac.vlan_has_access_control(vlan):
                tasks.append(
                    functools.partial(self.checkAuthzOnVlan, mac, vlan))

            if tasks:
                task = threading.Thread(
                    target=lambda: [task() for task in tasks])
                task.start()

            source = packet.highest_layer
            # more meaningful name
            if source == 'BROWSER':
                source = 'NetBIOS'
            elif source == 'BOOTP':
                source = 'DHCPV4'

            # Hostname: grab it from netbios or dhcpv4 or dhcpv6 or mdns
            hostname = None
            try:
                hostname = str(packet.nbdgm.source_name)  # ends with <??>
                p = re.compile('<..>$')
                hostname = p.sub('', hostname)
            except AttributeError:
                pass

            try:
                hostname = str(packet.bootp.option_hostname)
            except AttributeError:
                pass

            try:
                hostname = str(packet.dhcpv6.client_fqdn)
            except AttributeError:
                pass

            try:
                if int(packet.mdns.dns_flags_response) and int(
                        packet.mdns.dns_flags_authoritative):
                    mdns = packet.mdns
                    try:
                        a_list = mdns.dns_a.fields.copy()
                        a_list.reverse()
                    except AttributeError:
                        a_list = []
                    try:
                        aaaa_list = mdns.dns_aaaa.fields.copy()
                        aaaa_list.reverse()
                    except AttributeError:
                        aaaa_list = []
                    resp_types = mdns.dns_resp_type.fields.copy()
                    resp_types.reverse()
                    for field in mdns.dns_resp_name.fields:
                        name, *domain = field.showname_value.split('.')
                        if domain == ['local']:
                            resp_type = resp_types.pop().showname_value.split(
                                ' ')[0]
                            target = None
                            if resp_type == 'A':
                                target = a_list.pop().showname_value
                            elif resp_type == 'AAAA':
                                target = aaaa_list.pop().showname_value
                            if target:
                                if session.mac_has_ip_on_vlan(
                                        mac, target, vlan):
                                    hostname = name
                                    break  # Only first one found...
            except AttributeError:
                pass

            if hostname:
                device.seen_hostname(mac, hostname, source)

            # DHCP fingerprint
            if source == 'DHCPV4':
                try:
                    fingerprint = {
                        'request_list':
                        ','.join(
                            str(option.hex_value) for option in
                            packet.bootp.option_request_list_item.fields),
                        'vendor':
                        str(getattr(packet.bootp, 'option_vendor_class_id',
                                    ''))
                    }
                except AttributeError:
                    pass
                else:
                    device.seen_fingerprint(mac, fingerprint, source, hostname)

            elif source == 'DHCPV6':
                try:
                    fingerprint = {
                        'request_list':
                        ','.join(
                            str(option.hex_value) for option in
                            packet.dhcpv6.requested_option_code.fields),
                        'vendor':
                        str(getattr(packet.dhcpv6, 'vendorclass_data', '')),
                        'enterprise':
                        str(
                            getattr(packet.dhcpv6, 'vendorclass_enterprise',
                                    ''))
                    }
                except AttributeError:
                    pass
                else:
                    device.seen_fingerprint(mac, fingerprint, source, hostname)

        except Exception:
            ExceptionEvent(source='device-tracker')\
                 .add_data('packet', str(packet))\
                 .notify()