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)
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)
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)
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))
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
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