Пример #1
0
    def test_notify_without_Dendrite(self):
        Dendrite.publish_single = MagicMock()

        event = Event(event_type='test_event', source='test')
        event.notify()

        self.assertEqual(Dendrite.publish_single.call_count, 1)
Пример #2
0
    def test_notify_with_Dendrite(self):
        Dendrite = MagicMock()

        event = Event(event_type='test_event',
                      source='test',
                      dendrite=Dendrite())
        event.notify()

        self.assertEqual(event.dendrite.publish.call_count, 1)
Пример #3
0
    def test_Event_timestamp(self):
        event = Event(event_type='test_event', source='test')
        self.assertAlmostEqual(datetime.utcfromtimestamp(event.timestamp),
                               datetime.utcnow(),
                               delta=timedelta(seconds=2),
                               msg='timestamp set to current epoch')

        event = Event(event_type='test_event', source='test', timestamp=1234)
        self.assertEqual(event.timestamp, 1234)
Пример #4
0
 def checkAuthzOnVlan(self, mac, vlan):
     authz = nac.checkAuthz(mac)
     if not authz or vlan not in authz.allow_on:
         event = Event('device-not-authorized',
                       source='network',
                       level='danger')
         event.add_data('mac', mac, data_type='mac')
         event.add_data('vlan', vlan)
         event.notify()
Пример #5
0
def AuthenticationGroupFailed(req):
    try:
        request = request_as_hash_of_values(req)
        
        # Todo: set source depending on requester: radius-dot1x, captive-portal-web or captive-portal-guest-access
        Event('runtime-failure-authentication', source='radius', level='danger')\
            .add_data('authentication_group', request.get('ELAN-Auth-Provider'), data_type='authentication')\
            .notify()
    except:
        ExceptionEvent(source='radius').notify()
        raise
Пример #6
0
    def test_EventDataType(self):
        event = Event(event_type='test_event', source='test')

        event.add_data(key='test', value='test 1', data_type='str')
        event.add_data(key='test', value='test 2')

        self.assertEqual(event.data[0]['type'], 'str')
        self.assertNotIn('type', event.data[1])
Пример #7
0
    async def poll(self, ip, timeout=10):
        ''' poll and cache result'''
        try:
            device_snmp = await asyncio.wait_for(self._poll(ip), timeout)
        except asyncio.TimeoutError:
            device_snmp = None

        if device_snmp is None:
            event = Event('runtime-failure', source='snmp', level='warning')
            event.add_data('ip', ip)
            event.notify()
            return

        # Try to find a cached device
        device_id = self.get_id_by_ip(ip)

        if not device_id and 'ports' in device_snmp:
            # Try to find cached device by mac
            for port in device_snmp['ports']:
                device_id = self.get_id_by_mac(port['mac'])
                if device_id:
                    break

        if device_id:
            cached_device = self.get_device_by_id(device_id)
        else:
            device_id = self.get_new_device_id()
            cached_device = None

        device_snmp['local_id'] = device_id

        # Update cached device if needed
        if cached_device != device_snmp:
            if cached_device is None or self.switch_has_changed(cached_device, device_snmp):
                # notify if has changed. Only send relevant keys
                Dendrite.publish_single('snmp', { k:v for k, v in device_snmp.items() if k not in IGNORE_SWITCH_KEYS })
            # cache the device, including dynamic fields like fw_mac
            with self.synapse.pipeline() as pipe:
                pipe.hset(self.DEVICE_SNMP_CACHE_PATH, device_id, device_snmp)
                pipe.hset(self.DEVICE_IP_SNMP_CACHE_PATH, ip, device_id)
                cached_macs = set()
                if cached_device:
                    for port in cached_device['ports']:
                        if port['mac']:
                            cached_macs.add(port['mac'])
                device_macs = set()
                for port in device_snmp['ports']:
                    if port['mac']:
                        device_macs.add(port['mac'])
                for mac in cached_macs - device_macs:  # macs not longer valid
                    pipe.hdel(self.DEVICE_MAC_SNMP_CACHE_PATH, mac)
                for mac in device_macs - cached_macs:  # new macs
                    pipe.hset(self.DEVICE_MAC_SNMP_CACHE_PATH, mac, device_id)
                pipe.execute()
        return device_snmp
Пример #8
0
async def post_auth(req):
    try:
        request = request_as_hash_of_values(req)

        if request.get('ELAN-Auth-Type', None) == 'Reject':
            return await seen(request)
        else:
            return await get_assignments(request)
    except neuron.RequestTimeout:
        Event('runtime-failure-authorization', source='radius', level='danger')\
            .add_data('error', 'timeout')\
            .notify()
        raise
    except:
        ExceptionEvent(source='radius').notify()
        raise
Пример #9
0
def AuthenticationProviderFailed(req, level='danger'):
    # Treat error from auth
    try:
        request = request_as_hash_of_values(req)
        
        # Todo: set source depending on requester: radius-dot1x, captive-portal-web or captive-portal-guest-access
        event = Event('runtime-failure-authentication', source='radius', level=level)\
            .add_data('authentication_provider', request.get('ELAN-Auth-Failed'), data_type='authentication')
        
        if request.get('Module-Failure-Message'):
            event.add_data('details', request.get('Module-Failure-Message'))

        event.notify()
        
    except:
        ExceptionEvent(source='radius').notify()
        raise
Пример #10
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()
Пример #11
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)}
Пример #12
0
def login(request, context=None):
    if context is None:
        context = {}

    clientIP = request.META['REMOTE_ADDR']
    clientMAC = ip4_to_mac(clientIP)

    if is_authenticated(clientMAC):
        # VlanID not present, means it has been not been redirected, so MAC is allowed on VLAN (maybe not from web or captive portal)
        return redirect('status')

    default_context = {}
    if 'web_authentication' in request.META:
        default_context['web_authentication'] = request.META[
            'web_authentication']

    if 'guest_access' in request.META:
        default_context['guest_access'] = GuestAccess.get(
            id=int(request.META['guest_access']))
        if default_context['guest_access']:
            default_context.update(
                guest_registration_fields=default_context['guest_access']
                ['fields'],
                guest_access_pending=is_authz_pending(clientMAC))

    for key in default_context:
        if key not in context:
            context[key] = default_context[key]

    if request.method != 'POST' or context.get('post_processed', False):
        return render(request, 'captive-portal/login.html', context)

    # POST
    try:
        username = request.POST['username']
        password = request.POST['password']
    except (KeyError):
        # Redisplay the login form.
        context['error_message'] = _("'username' or 'password' missing.")
        return render(request, 'captive-portal/login.html', context)

    context['username'] = username
    if 'web_authentication' not in request.META:
        context['error_message'] = _("Invalid username or password.")
        return render(request, 'captive-portal/login.html', context)
    try:
        authenticator_id = pwd_authenticate(request.META['web_authentication'],
                                            username,
                                            password,
                                            source='captive-portal-web')

    except AuthenticationFailed as e:
        if not e.args:
            context['error_message'] = _("Invalid username or password.")
        elif len(e.args) == 1:
            context['error_message'] = e.args[0]
        else:
            context['error_message'] = e.args

        return render(request, 'captive-portal/login.html', context)

    except RequestError:
        context['error_message'] = _("An error occurred, please retry.")
        return render(request, 'captive-portal/login.html', context)

    except RequestTimeout:
        context['error_message'] = _("request timed out, please retry.")
        return render(request, 'captive-portal/login.html', context)

    vlan = '{interface}.{id}'.format(interface=request.META['interface'],
                                     id=request.META['vlan_id'])
    # start session
    authz = nac.newAuthz(clientMAC,
                         source='captive-portal-web',
                         till_disconnect=True,
                         login=username,
                         authentication_provider=authenticator_id,
                         vlan=vlan)
    # TODO: if vlan incorrect, try to change it
    if not authz or vlan not in authz.allow_on:
        # log no assignment rule matched....
        event = Event('device-not-authorized',
                      source='captive-portal-web',
                      level='danger')
        event.add_data('mac', clientMAC, 'mac')
        event.add_data('vlan', vlan)
        event.add_data('authentication_provider', authenticator_id,
                       'authentication')
        event.add_data('login', username)
        event.notify()

    return redirect('status')
Пример #13
0
    async def process_alert(self, alert):
        try:
            #timestamp = time.strftime('%Y-%m-%dT%H:%M:%S.'+ str(event['event-microsecond']) +'Z', time.gmtime(event['event-second']))
            event = Event(event_type='device-alert',
                          source='ids',
                          level='danger',
                          timestamp=alert['event-second'],
                          dendrite=self.dendrite)
            event.add_data('priority', alert['priority'])
            event.add_data('signature',
                           alert['signature-id'],
                           data_type='signature')
            event.add_data('revision', alert['signature-revision'])

            # decode Ethernet packet to get mac addresses and protocol
            # TODO: This may be nflog wrapped now it is sent via nflog? to be checked...
            packet = b''
            for p in alert['packets']:
                packet += p
            packet = Ether(packet)

            src_mac = packet.src
            if src_mac != '00:00:00:00:00:00':
                event.add_data('src_mac', src_mac, 'mac')

            dst_mac = packet.dst
            if dst_mac != '00:00:00:00:00:00':
                event.add_data('dst_mac', dst_mac, 'mac')

            src_vlan = get_bridge_port(src_mac)
            if src_vlan:
                event.add_data('src_vlan', src_vlan)

            dst_vlan = get_bridge_port(dst_mac)
            if dst_vlan:
                event.add_data('dst_vlan', dst_vlan)

            protocol = None
            while packet:
                if packet.name not in ('Raw', 'Padding'):
                    protocol = packet.name
                packet = packet.payload

            if protocol:
                event.add_data('protocol', protocol)

            event.add_data('src_ip', alert['source-ip'])
            event.add_data('dst_ip', alert['destination-ip'])
            event.add_data('src_port', alert['sport-itype'])
            event.add_data('dst_port', alert['dport-icode'])

            event.notify()
        except:
            ExceptionEvent(source='ids').notify()