def _SendMessages(self, grr_msgs, background=False):
    """Sends a block of messages through Fleetspeak."""
    message_list = rdf_flows.PackedMessageList()
    communicator.Communicator.EncodeMessageList(
        rdf_flows.MessageList(job=grr_msgs), message_list)
    fs_msg = fs_common_pb2.Message(
        message_type="MessageList",
        destination=fs_common_pb2.Address(service_name="GRR"),
        background=background)
    fs_msg.data.Pack(message_list.AsPrimitiveProto())

    for grr_msg in grr_msgs:
      if (grr_msg.session_id is None or grr_msg.request_id is None or
          grr_msg.response_id is None):
        continue
      # Place all ids in a single annotation, instead of having separate
      # annotations for the flow-id, request-id and response-id. This reduces
      # overall size of the annotations by half (~60 bytes to ~30 bytes).
      annotation = fs_msg.annotations.entries.add()
      annotation.key = _DATA_IDS_ANNOTATION_KEY
      annotation.value = "%s:%d:%d" % (grr_msg.session_id.Basename(),
                                       grr_msg.request_id, grr_msg.response_id)
      if fs_msg.annotations.ByteSize() >= _MAX_ANNOTATIONS_BYTES:
        break

    try:
      sent_bytes = self._fs.Send(fs_msg)
    except (IOError, struct.error):
      logging.critical("Broken local Fleetspeak connection (write end).")
      raise

    communicator.GRR_CLIENT_SENT_BYTES.Increment(sent_bytes)
Example #2
0
    def testReceiveMessageListFleetspeak(self):
        fsd = fs_frontend_tool.GRRFSServer()

        grr_client_nr = 0xab
        grr_client_id_urn = self.SetupClient(grr_client_nr)

        flow_obj = self.FlowSetup(flow_test_lib.FlowOrderTest.__name__,
                                  grr_client_id_urn)

        num_msgs = 9

        session_id = flow_obj.session_id
        messages = [
            rdf_flows.GrrMessage(request_id=1,
                                 response_id=i,
                                 session_id=session_id,
                                 payload=rdfvalue.RDFInteger(i))
            for i in range(1, num_msgs + 1)
        ]

        fs_client_id = b"\x10\x00\x00\x00\x00\x00\x00\xab"
        # fs_client_id should be equivalent to grr_client_id_urn
        self.assertEqual(
            fs_client_id,
            fleetspeak_utils.GRRIDToFleetspeakID(grr_client_id_urn.Basename()))

        message_list = rdf_flows.PackedMessageList()
        communicator.Communicator.EncodeMessageList(
            rdf_flows.MessageList(job=messages), message_list)

        fs_message = fs_common_pb2.Message(message_type="MessageList",
                                           source=fs_common_pb2.Address(
                                               client_id=fs_client_id,
                                               service_name=FS_SERVICE_NAME))
        fs_message.data.Pack(message_list.AsPrimitiveProto())
        fsd.Process(fs_message, None)

        # Make sure the task is still on the client queue
        manager = queue_manager.QueueManager(token=self.token)
        tasks_on_client_queue = manager.Query(grr_client_id_urn, 100)
        self.assertEqual(len(tasks_on_client_queue), 1)

        want_messages = [message.Copy() for message in messages]
        for want_message in want_messages:
            # This is filled in by the frontend as soon as it gets the message.
            want_message.auth_state = (
                rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED)
            want_message.source = grr_client_id_urn

        stored_messages = data_store.DB.ReadResponsesForRequestId(
            session_id, 1)

        self.assertEqual(len(stored_messages), len(want_messages))

        stored_messages.sort(key=lambda m: m.response_id)
        # Check that messages were stored correctly
        for stored_message, want_message in itertools.izip(
                stored_messages, want_messages):
            stored_message.timestamp = None
            self.assertRDFValuesEqual(stored_message, want_message)
    def testReceiveMessageList(self):
        fs_server = fleetspeak_frontend_server.GRRFSServer()
        client_id = "C.1234567890123456"
        flow_id = "12345678"
        data_store.REL_DB.WriteClientMetadata(client_id,
                                              fleetspeak_enabled=True)

        rdf_flow = rdf_flow_objects.Flow(
            client_id=client_id,
            flow_id=flow_id,
            create_time=rdfvalue.RDFDatetime.Now())
        data_store.REL_DB.WriteFlowObject(rdf_flow)

        flow_request = rdf_flow_objects.FlowRequest(client_id=client_id,
                                                    flow_id=flow_id,
                                                    request_id=1)

        data_store.REL_DB.WriteFlowRequests([flow_request])
        session_id = "%s/%s" % (client_id, flow_id)
        fs_client_id = fleetspeak_utils.GRRIDToFleetspeakID(client_id)
        grr_messages = []
        for i in range(1, 10):
            grr_message = rdf_flows.GrrMessage(request_id=1,
                                               response_id=i + 1,
                                               session_id=session_id,
                                               payload=rdfvalue.RDFInteger(i))
            grr_messages.append(grr_message)
        packed_messages = rdf_flows.PackedMessageList()
        communicator.Communicator.EncodeMessageList(
            rdf_flows.MessageList(job=grr_messages), packed_messages)
        fs_message = fs_common_pb2.Message(message_type="MessageList",
                                           source=fs_common_pb2.Address(
                                               client_id=fs_client_id,
                                               service_name=FS_SERVICE_NAME))
        fs_message.data.Pack(packed_messages.AsPrimitiveProto())
        fs_message.validation_info.tags["foo"] = "bar"

        with test_lib.FakeTime(
                rdfvalue.RDFDatetime.FromSecondsSinceEpoch(123)):
            fs_server.Process(fs_message, None)

        # Ensure the last-ping timestamp gets updated.
        client_data = data_store.REL_DB.MultiReadClientMetadata([client_id])
        self.assertEqual(client_data[client_id].ping,
                         rdfvalue.RDFDatetime.FromSecondsSinceEpoch(123))
        self.assertEqual(
            client_data[client_id].last_fleetspeak_validation_info.
            ToStringDict(), {"foo": "bar"})

        flow_data = data_store.REL_DB.ReadAllFlowRequestsAndResponses(
            client_id, flow_id)
        self.assertLen(flow_data, 1)
        stored_flow_request, flow_responses = flow_data[0]
        self.assertEqual(stored_flow_request, flow_request)
        self.assertLen(flow_responses, 9)
Example #4
0
  def _SendMessages(self, grr_msgs, background=False):
    """Sends a block of messages through Fleetspeak."""
    message_list = rdf_flows.PackedMessageList()
    communicator.Communicator.EncodeMessageList(
        rdf_flows.MessageList(job=grr_msgs), message_list)
    fs_msg = fs_common_pb2.Message(
        message_type="MessageList",
        destination=fs_common_pb2.Address(service_name="GRR"),
        background=background)
    fs_msg.data.Pack(message_list.AsPrimitiveProto())

    try:
      sent_bytes = self._fs.Send(fs_msg)
    except (IOError, struct.error):
      logging.critical("Broken local Fleetspeak connection (write end).")
      raise

    stats.STATS.IncrementCounter("grr_client_sent_bytes", sent_bytes)
Example #5
0
    def testWireFormatAccess(self):

        m = rdf_flows.PackedMessageList()

        now = 1369308998000000

        # An unset RDFDatetime with no defaults will be None.
        self.assertEqual(m.timestamp, None)

        # Set the wireformat to the integer equivalent.
        m.SetPrimitive("timestamp", now)

        self.assertTrue(isinstance(m.timestamp, rdfvalue.RDFDatetime))
        self.assertEqual(m.timestamp, now)

        rdf_now = rdfvalue.RDFDatetime.Now()

        m.timestamp = rdf_now
        self.assertEqual(m.GetPrimitive("timestamp"), int(rdf_now))
Example #6
0
    def EncodeMessages(self,
                       message_list,
                       result,
                       destination=None,
                       timestamp=None,
                       api_version=3):
        """Accepts a list of messages and encodes for transmission.

    This function signs and then encrypts the payload.

    Args:
       message_list: A MessageList rdfvalue containing a list of GrrMessages.
       result: A ClientCommunication rdfvalue which will be filled in.
       destination: The CN of the remote system this should go to.
       timestamp: A timestamp to use for the signed messages. If None - use the
         current time.
       api_version: The api version which this should be encoded in.

    Returns:
       A nonce (based on time) which is inserted to the encrypted payload. The
       client can verify that the server is able to decrypt the message and
       return the nonce.

    Raises:
       RuntimeError: If we do not support this api version.
    """
        if api_version not in [3]:
            raise RuntimeError("Unsupported api version: %s, expected 3." %
                               api_version)
        # TODO(amoser): This is actually not great, we have two
        # communicator classes already, one for the client, one for the
        # server. This should be different methods, not a single one that
        # gets passed a destination (server side) or not (client side).
        if destination is None:
            destination = self.server_name
            # For the client it makes sense to cache the server cipher since
            # it's the only cipher it ever uses.
            cipher = self._GetServerCipher()
        else:
            remote_public_key = self._GetRemotePublicKey(destination)
            cipher = Cipher(self.common_name, self.private_key,
                            remote_public_key)

        # Make a nonce for this transaction
        if timestamp is None:
            self.timestamp = timestamp = int(time.time() * 1000000)

        packed_message_list = rdf_flows.PackedMessageList(timestamp=timestamp)
        self.EncodeMessageList(message_list, packed_message_list)

        result.encrypted_cipher_metadata = cipher.encrypted_cipher_metadata

        # Include the encrypted cipher.
        result.encrypted_cipher = cipher.encrypted_cipher

        serialized_message_list = packed_message_list.SerializeToString()

        # Encrypt the message symmetrically.
        # New scheme cipher is signed plus hmac over message list.
        result.packet_iv, result.encrypted = cipher.Encrypt(
            serialized_message_list)

        # This is to support older endpoints.
        result.hmac = cipher.HMAC(result.encrypted)

        # Newer endpoints only look at this HMAC. It is recalculated for each packet
        # in the session. Note that encrypted_cipher and encrypted_cipher_metadata
        # do not change between all packets in this session.
        result.full_hmac = cipher.HMAC(result.encrypted,
                                       result.encrypted_cipher,
                                       result.encrypted_cipher_metadata,
                                       result.packet_iv.SerializeToString(),
                                       struct.pack("<I", api_version))

        result.api_version = api_version

        if isinstance(result, rdfvalue.RDFValue):
            # Store the number of messages contained.
            result.num_messages = len(message_list)

        return timestamp
Example #7
0
    def EncodeMessages(self,
                       message_list,
                       result,
                       timestamp=None,
                       api_version=3):
        """Accepts a list of messages and encodes for transmission.

    This function signs and then encrypts the payload.

    Args:
       message_list: A MessageList rdfvalue containing a list of GrrMessages.
       result: A ClientCommunication rdfvalue which will be filled in.
       timestamp: A timestamp to use for the signed messages. If None - use the
         current time.
       api_version: The api version which this should be encoded in.

    Returns:
       A nonce (based on time) which is inserted to the encrypted payload. The
       client can verify that the server is able to decrypt the message and
       return the nonce.

    Raises:
       RuntimeError: If we do not support this api version.
    """
        if api_version not in [3]:
            raise RuntimeError("Unsupported api version: %s, expected 3." %
                               api_version)
        cipher = self._GetServerCipher()

        # Make a nonce for this transaction
        if timestamp is None:
            self.timestamp = timestamp = int(time.time() * 1000000)

        packed_message_list = rdf_flows.PackedMessageList(timestamp=timestamp)
        self.EncodeMessageList(message_list, packed_message_list)

        result.encrypted_cipher_metadata = cipher.encrypted_cipher_metadata

        # Include the encrypted cipher.
        result.encrypted_cipher = cipher.encrypted_cipher

        serialized_message_list = packed_message_list.SerializeToBytes()

        # Encrypt the message symmetrically.
        # New scheme cipher is signed plus hmac over message list.
        result.packet_iv, result.encrypted = cipher.Encrypt(
            serialized_message_list)

        # This is to support older endpoints.
        result.hmac = cipher.HMAC(result.encrypted)

        # Newer endpoints only look at this HMAC. It is recalculated for each packet
        # in the session. Note that encrypted_cipher and encrypted_cipher_metadata
        # do not change between all packets in this session.
        result.full_hmac = cipher.HMAC(result.encrypted,
                                       result.encrypted_cipher,
                                       result.encrypted_cipher_metadata,
                                       result.packet_iv.SerializeToBytes(),
                                       struct.pack("<I", api_version))

        result.api_version = api_version

        if isinstance(result, rdfvalue.RDFValue):
            # Store the number of messages contained.
            result.num_messages = len(message_list)

        return timestamp