Example #1
0
    def CheckNotificationsDisappear(self, session_id):
        worker_obj = worker.GRRWorker(token=self.token)
        manager = queue_manager.QueueManager(token=self.token)
        notification = rdf_flows.GrrNotification(session_id=session_id)
        with data_store.DB.GetMutationPool() as pool:
            manager.NotifyQueue(notification, mutation_pool=pool)

        notifications = manager.GetNotificationsByPriority(queues.FLOWS).get(
            notification.priority, [])

        # Check the notification is there. With multiple worker queue shards we can
        # get other notifications such as for audit event listeners, so we need to
        # filter out ours.
        notifications = [
            x for x in notifications if x.session_id == session_id
        ]
        self.assertEqual(len(notifications), 1)

        # Process all messages
        worker_obj.RunOnce()
        worker_obj.thread_pool.Join()

        notifications = manager.GetNotificationsByPriority(queues.FLOWS).get(
            notification.priority, [])
        notifications = [
            x for x in notifications if x.session_id == session_id
        ]

        # Check the notification is now gone.
        self.assertEqual(len(notifications), 0)
Example #2
0
    def QueueNotification(self, notification=None, timestamp=None, **kw):
        """Queues a notification for a flow."""

        if notification is None:
            notification = rdf_flows.GrrNotification(**kw)

        session_id = notification.session_id
        if session_id:
            if timestamp is None:
                timestamp = self.frozen_timestamp

            # We must not store more than one notification per session id and
            # timestamp or there will be race conditions. We therefore only keep
            # the one with the highest request number (indicated by last_status).
            # Note that timestamp might be None. In that case we also only want
            # to keep the latest.
            if timestamp is None:
                ts_str = "None"
            else:
                ts_str = int(timestamp)
            key = "%s!%s" % (session_id, ts_str)
            existing = self.notifications.get(key)
            if existing is not None:
                if existing[0].last_status < notification.last_status:
                    self.notifications[key] = (notification, timestamp)
            else:
                self.notifications[key] = (notification, timestamp)
Example #3
0
  def testAuthentication2(self):
    """Test that flows refuse to processes unauthenticated messages.

    Here we try to simulate an attacker injecting unauthenticated
    messages midstream.

    The current implementation actually fails to process the entire
    flow since the injected messages displace the real ones if they
    arrive earlier. This can be an effective DoS against legitimate
    clients but would require attackers to guess session ids.
    """
    flow_obj = self.FlowSetup("FlowOrderTest")

    # Simulate processing messages arriving in random order
    message_ids = [1, 2]
    self.SendMessages(message_ids, flow_obj.session_id, authenticated=True)

    # Now suppose some of the messages are spoofed
    message_ids = [3, 4, 5]
    self.SendMessages(message_ids, flow_obj.session_id, authenticated=False)

    # And now our real messages arrive
    message_ids = [5, 6]
    self.SendMessages(message_ids, flow_obj.session_id, authenticated=True)

    # Send the status message
    self.SendOKStatus(7, flow_obj.session_id)

    runner = flow_obj.GetRunner()
    notification = rdf_flows.GrrNotification(
        timestamp=rdfvalue.RDFDatetime.Now())
    runner.ProcessCompletedRequests(notification)

    # Some messages should actually be processed
    self.assertEqual(flow_obj.messages, [1, 2, 5, 6])
Example #4
0
    def ExpireRules(self):
        """Removes any rules with an expiration date in the past."""
        rules = self.Get(self.Schema.RULES)
        new_rules = self.Schema.RULES()
        now = time.time() * 1e6
        expired_session_ids = set()
        for rule in rules:
            if rule.expires > now:
                new_rules.Append(rule)
            else:
                for action in rule.actions:
                    if action.hunt_id:
                        expired_session_ids.add(action.hunt_id)

        if expired_session_ids:
            # Notify the worker to mark this hunt as terminated.
            manager = queue_manager.QueueManager(token=self.token)
            manager.MultiNotifyQueue([
                rdf_flows.GrrNotification(session_id=session_id)
                for session_id in expired_session_ids
            ])

        if len(new_rules) < len(rules):
            self.Set(self.Schema.RULES, new_rules)
            self.Flush()
Example #5
0
    def _GetUnsortedNotifications(self,
                                  queue_shard,
                                  notifications_by_session_id=None):
        """Returns all the available notifications for a queue_shard.

    Args:
      queue_shard: urn of queue shard
      notifications_by_session_id: store notifications in this dict rather than
        creating a new one

    Returns:
      dict of notifications. keys are session ids.
    """
        if not notifications_by_session_id:
            notifications_by_session_id = {}
        end_time = self.frozen_timestamp or rdfvalue.RDFDatetime().Now()
        for predicate, serialized_notification, ts in data_store.DB.ResolveRegex(
                queue_shard,
                self.NOTIFY_PREDICATE_PREFIX % ".*",
                timestamp=(0, end_time),
                token=self.token,
                limit=10000):

            # Parse the notification.
            try:
                notification = rdf_flows.GrrNotification(
                    serialized_notification)
            except Exception:  # pylint: disable=broad-except
                logging.exception(
                    "Can't unserialize notification, deleting it: "
                    "predicate=%s, ts=%d", predicate, ts)
                data_store.DB.DeleteAttributes(
                    queue_shard,
                    [predicate],
                    token=self.token,
                    # Make the time range narrow, but be sure to include the needed
                    # notification.
                    start=ts,
                    end=ts,
                    sync=True)
                continue

            # Strip the prefix from the predicate to get the session_id.
            session_id = predicate[len(self.NOTIFY_PREDICATE_PREFIX % ""):]
            notification.session_id = session_id
            notification.timestamp = ts

            existing = notifications_by_session_id.get(notification.session_id)
            if existing:
                # If we have a notification for this session_id already, we only store
                # the one that was scheduled last.
                if notification.first_queued > existing.first_queued:
                    notifications_by_session_id[
                        notification.session_id] = notification
            else:
                notifications_by_session_id[
                    notification.session_id] = notification

        return notifications_by_session_id
Example #6
0
    def QueueNotification(self, notification=None, timestamp=None, **kw):
        """Queues a notification for a flow."""

        if notification is None:
            notification = rdf_flows.GrrNotification(**kw)

        if notification.session_id:
            if timestamp is None:
                timestamp = self.frozen_timestamp

            self.notifications.append((notification, timestamp))
Example #7
0
    def testNotificationReschedulingTTL(self):
        """Test that notifications are not rescheduled forever."""

        with test_lib.FakeTime(10000):
            worker_obj = worker.GRRWorker(token=self.token)
            flow_obj = self.FlowSetup("RaisingTestFlow")
            session_id = flow_obj.session_id
            flow_obj.Close()

            with queue_manager.QueueManager(token=self.token) as manager:
                notification = rdf_flows.GrrNotification(session_id=session_id,
                                                         timestamp=time.time(),
                                                         last_status=1)
                with data_store.DB.GetMutationPool() as pool:
                    manager.NotifyQueue(notification, mutation_pool=pool)

                notifications = manager.GetNotifications(queues.FLOWS)
                # Check the notification is there.
                notifications = [
                    n for n in notifications if n.session_id == session_id
                ]
                self.assertEqual(len(notifications), 1)

        delay = flow_runner.FlowRunner.notification_retry_interval

        ttl = notification.ttl
        for i in xrange(ttl - 1):
            with test_lib.FakeTime(10000 + 100 + delay * (i + 1)):
                # Process all messages.
                worker_obj.RunOnce()
                worker_obj.thread_pool.Join()

                notifications = manager.GetNotifications(queues.FLOWS)
                # Check the notification is for the correct session_id.
                notifications = [
                    n for n in notifications if n.session_id == session_id
                ]
                self.assertEqual(len(notifications), 1)

        with test_lib.FakeTime(10000 + 100 + delay * ttl):
            # Process all messages.
            worker_obj.RunOnce()
            worker_obj.thread_pool.Join()

            notifications = manager.GetNotifications(queues.FLOWS)
            self.assertEqual(len(notifications), 0)
Example #8
0
    def testReordering(self):
        """Check that out of order client messages are reordered."""
        flow_obj = self.FlowSetup(flow_test_lib.FlowOrderTest.__name__)

        # Simulate processing messages arriving in random order
        message_ids = [2, 1, 4, 3, 5]
        self.SendMessages(message_ids, flow_obj.session_id)

        # Send the status message
        self.SendOKStatus(6, flow_obj.session_id)

        runner = flow_obj.GetRunner()
        notification = rdf_flows.GrrNotification(
            timestamp=rdfvalue.RDFDatetime.Now())
        runner.ProcessCompletedRequests(notification)

        # Check that the messages were processed in order
        self.assertEqual(flow_obj.messages, [1, 2, 3, 4, 5])
Example #9
0
  def testAuthentication1(self):
    """Test that flows refuse to processes unauthenticated messages."""
    flow_obj = self.FlowSetup("FlowOrderTest")

    # Simulate processing messages arriving in random order
    message_ids = [2, 1, 4, 3, 5]
    self.SendMessages(message_ids, flow_obj.session_id, authenticated=False)

    # Send the status message
    self.SendOKStatus(6, flow_obj.session_id)

    runner = flow_obj.GetRunner()
    notification = rdf_flows.GrrNotification(
        timestamp=rdfvalue.RDFDatetime.Now())
    runner.ProcessCompletedRequests(notification)

    # Now messages should actually be processed
    self.assertEqual(flow_obj.messages, [])
Example #10
0
    def testNotificationRacesAreResolved(self):
        # We need a random flow object for this test.
        session_id = flow.GRRFlow.StartFlow(client_id=self.client_id,
                                            flow_name="WorkerSendingTestFlow",
                                            token=self.token)
        worker_obj = worker.GRRWorker(token=self.token)
        manager = queue_manager.QueueManager(token=self.token)
        manager.DeleteNotification(session_id)
        manager.Flush()

        # We simulate a race condition here - the notification for request #1 is
        # there but the actual request #1 is not. The worker should pick up the
        # notification, notice that the request #1 is not there yet and reschedule
        # the notification.
        notification = rdf_flows.GrrNotification(session_id=session_id,
                                                 last_status=1)
        with data_store.DB.GetMutationPool() as pool:
            manager.NotifyQueue(notification, mutation_pool=pool)

        notifications = manager.GetNotifications(queues.FLOWS)
        # Check the notification is there.
        notifications = [
            n for n in notifications if n.session_id == session_id
        ]
        self.assertEqual(len(notifications), 1)

        # Process all messages
        worker_obj.RunOnce()
        worker_obj.thread_pool.Join()

        delay = flow_runner.FlowRunner.notification_retry_interval
        with test_lib.FakeTime(time.time() + 10 + delay):
            requeued_notifications = manager.GetNotifications(queues.FLOWS)
            # Check that there is a new notification.
            notifications = [
                n for n in notifications if n.session_id == session_id
            ]
            self.assertEqual(len(requeued_notifications), 1)

            self.assertEqual(requeued_notifications[0].first_queued,
                             notifications[0].first_queued)
            self.assertNotEqual(requeued_notifications[0].timestamp,
                                notifications[0].timestamp)
Example #11
0
    def PublishMultipleEvents(cls, events, delay=None, token=None):
        """Publish the message into all listeners of the event.

    If event_name is a string, we send the message to all event handlers which
    contain this string in their EVENT static member. This allows the event to
    be sent to multiple interested listeners. Alternatively, the event_name can
    specify a single URN of an event listener to receive the message.

    Args:

      events: A dict with keys being event names and values being lists of
        messages.
      delay: An rdfvalue.Duration object. If given, the event will be published
             after the indicated time.
      token: ACL token.

    Raises:
      ValueError: If the message is invalid. The message must be a Semantic
        Value (instance of RDFValue) or a full GrrMessage.
    """
        with queue_manager.WellKnownQueueManager(token=token) as manager:
            event_name_map = registry.EventRegistry.EVENT_NAME_MAP
            for event_name, messages in events.iteritems():
                handler_urns = []
                if isinstance(event_name, basestring):
                    for event_cls in event_name_map.get(event_name, []):
                        if event_cls.well_known_session_id is None:
                            logging.error(
                                "Well known flow %s has no session_id.",
                                event_cls.__name__)
                        else:
                            handler_urns.append(
                                event_cls.well_known_session_id)

                else:
                    handler_urns.append(event_name)

                for msg in messages:
                    # Allow the event name to be either a string or a URN of an event
                    # listener.
                    if not isinstance(msg, rdfvalue.RDFValue):
                        raise ValueError(
                            "Can only publish RDFValue instances.")

                    # Wrap the message in a GrrMessage if needed.
                    if not isinstance(msg, rdf_flows.GrrMessage):
                        msg = rdf_flows.GrrMessage(payload=msg)

                    # Randomize the response id or events will get overwritten.
                    msg.response_id = msg.task_id = msg.GenerateTaskID()
                    # Well known flows always listen for request id 0.
                    msg.request_id = 0

                    timestamp = None
                    if delay:
                        timestamp = (rdfvalue.RDFDatetime.Now() +
                                     delay).AsMicroSecondsFromEpoch()

                    # Forward the message to the well known flow's queue.
                    for event_urn in handler_urns:
                        manager.QueueResponse(event_urn, msg)
                        manager.QueueNotification(
                            rdf_flows.GrrNotification(session_id=event_urn,
                                                      priority=msg.priority,
                                                      timestamp=timestamp))