def callback(_, pdu, redacted): if not check_event_content_hash(pdu): logger.warn( "Event content has been tampered, redacting %s: %s", pdu.event_id, pdu.get_pdu_json()) return redacted return pdu
def _check_sigs_and_hash(self, pdu): """Throws a SynapseError if the PDU does not have the correct signatures. Returns: FrozenEvent: Either the given event or it redacted if it failed the content hash check. """ # Check signatures are correct. redacted_event = prune_event(pdu) redacted_pdu_json = redacted_event.get_pdu_json() try: yield self.keyring.verify_json_for_server(pdu.origin, redacted_pdu_json) except SynapseError: logger.warn( "Signature check failed for %s redacted to %s", encode_canonical_json(pdu.get_pdu_json()), encode_canonical_json(redacted_pdu_json), ) raise if not check_event_content_hash(pdu): logger.warn("Event content has been tampered, redacting %s, %s", pdu.event_id, encode_canonical_json(pdu.get_dict())) defer.returnValue(redacted_event) defer.returnValue(pdu)
def _check_sigs_and_hash(self, pdu): """Throws a SynapseError if the PDU does not have the correct signatures. Returns: FrozenEvent: Either the given event or it redacted if it failed the content hash check. """ # Check signatures are correct. redacted_event = prune_event(pdu) redacted_pdu_json = redacted_event.get_pdu_json() try: yield self.keyring.verify_json_for_server( pdu.origin, redacted_pdu_json ) except SynapseError: logger.warn( "Signature check failed for %s redacted to %s", encode_canonical_json(pdu.get_pdu_json()), encode_canonical_json(redacted_pdu_json), ) raise if not check_event_content_hash(pdu): logger.warn( "Event content has been tampered, redacting %s, %s", pdu.event_id, encode_canonical_json(pdu.get_dict()) ) defer.returnValue(redacted_event) defer.returnValue(pdu)
def callback(_, pdu): with logcontext.PreserveLoggingContext(ctx): if not check_event_content_hash(pdu): # let's try to distinguish between failures because the event was # redacted (which are somewhat expected) vs actual ball-tampering # incidents. # # This is just a heuristic, so we just assume that if the keys are # about the same between the redacted and received events, then the # received event was probably a redacted copy (but we then use our # *actual* redacted copy to be on the safe side.) redacted_event = prune_event(pdu) if (set(redacted_event.keys()) == set(pdu.keys()) and set(six.iterkeys(redacted_event.content)) == set(six.iterkeys(pdu.content))): logger.info( "Event %s seems to have been redacted; using our redacted " "copy", pdu.event_id, ) else: logger.warning( "Event %s content has been tampered, redacting", pdu.event_id, pdu.get_pdu_json(), ) return redacted_event if self.spam_checker.check_event_for_spam(pdu): logger.warn("Event contains spam, redacting %s: %s", pdu.event_id, pdu.get_pdu_json()) return prune_event(pdu) return pdu
async def _check_sigs_and_hash( self, room_version: RoomVersion, pdu: EventBase ) -> EventBase: """Checks that event is correctly signed by the sending server. Also checks the content hash, and redacts the event if there is a mismatch. Also runs the event through the spam checker; if it fails, redacts the event and flags it as soft-failed. Args: room_version: The room version of the PDU pdu: the event to be checked Returns: * the original event if the checks pass * a redacted version of the event (if the signature matched but the hash did not). In this case a warning will be logged. Raises: InvalidEventSignatureError if the signature check failed. Nothing will be logged in this case. """ await _check_sigs_on_pdu(self.keyring, room_version, pdu) if not check_event_content_hash(pdu): # let's try to distinguish between failures because the event was # redacted (which are somewhat expected) vs actual ball-tampering # incidents. # # This is just a heuristic, so we just assume that if the keys are # about the same between the redacted and received events, then the # received event was probably a redacted copy (but we then use our # *actual* redacted copy to be on the safe side.) redacted_event = prune_event(pdu) if set(redacted_event.keys()) == set(pdu.keys()) and set( redacted_event.content.keys() ) == set(pdu.content.keys()): logger.debug( "Event %s seems to have been redacted; using our redacted copy", pdu.event_id, ) else: logger.warning( "Event %s content has been tampered, redacting", pdu.event_id, ) return redacted_event spam_check = await self.spam_checker.check_event_for_spam(pdu) if spam_check != self.spam_checker.NOT_SPAM: logger.warning("Event contains spam, soft-failing %s", pdu.event_id) # we redact (to save disk space) as well as soft-failing (to stop # using the event in prev_events). redacted_event = prune_event(pdu) redacted_event.internal_metadata.soft_failed = True return redacted_event return pdu
def callback(_, pdu, redacted): if not check_event_content_hash(pdu): logger.warn( "Event content has been tampered, redacting %s: %s", pdu.event_id, pdu.get_pdu_json() ) return redacted return pdu
async def _check_sigs_and_hash(self, room_version: RoomVersion, pdu: EventBase) -> EventBase: """Checks that event is correctly signed by the sending server. Args: room_version: The room version of the PDU pdu: the event to be checked Returns: * the original event if the checks pass * a redacted version of the event (if the signature matched but the hash did not) * throws a SynapseError if the signature check failed.""" try: await _check_sigs_on_pdu(self.keyring, room_version, pdu) except SynapseError as e: logger.warning( "Signature check failed for %s: %s", pdu.event_id, e, ) raise if not check_event_content_hash(pdu): # let's try to distinguish between failures because the event was # redacted (which are somewhat expected) vs actual ball-tampering # incidents. # # This is just a heuristic, so we just assume that if the keys are # about the same between the redacted and received events, then the # received event was probably a redacted copy (but we then use our # *actual* redacted copy to be on the safe side.) redacted_event = prune_event(pdu) if set(redacted_event.keys()) == set(pdu.keys()) and set( redacted_event.content.keys()) == set(pdu.content.keys()): logger.info( "Event %s seems to have been redacted; using our redacted copy", pdu.event_id, ) else: logger.warning( "Event %s content has been tampered, redacting", pdu.event_id, ) return redacted_event result = await self.spam_checker.check_event_for_spam(pdu) if result: logger.warning("Event contains spam, soft-failing %s", pdu.event_id) # we redact (to save disk space) as well as soft-failing (to stop # using the event in prev_events). redacted_event = prune_event(pdu) redacted_event.internal_metadata.soft_failed = True return redacted_event return pdu
def callback(_, pdu): with logcontext.PreserveLoggingContext(ctx): if not check_event_content_hash(pdu): logger.warn( "Event content has been tampered, redacting %s: %s", pdu.event_id, pdu.get_pdu_json()) return prune_event(pdu) if self.spam_checker.check_event_for_spam(pdu): logger.warn("Event contains spam, redacting %s: %s", pdu.event_id, pdu.get_pdu_json()) return prune_event(pdu) return pdu
def callback(_, pdu, redacted): with logcontext.PreserveLoggingContext(ctx): if not check_event_content_hash(pdu): logger.warn( "Event content has been tampered, redacting %s: %s", pdu.event_id, pdu.get_pdu_json() ) return redacted if self.spam_checker.check_event_for_spam(pdu): logger.warn( "Event contains spam, redacting %s: %s", pdu.event_id, pdu.get_pdu_json() ) return redacted return pdu
def main(): parser = argparse.ArgumentParser() parser.add_argument( "input_json", nargs="?", type=argparse.FileType('r'), default=sys.stdin ) args = parser.parse_args() logging.basicConfig() event_json = dictobj(json.load(args.input_json)) algorithms = {"sha256": hashlib.sha256} for alg_name in event_json.hashes: if check_event_content_hash(event_json, algorithms[alg_name]): print("PASS content hash %s" % (alg_name,)) else: print("FAIL content hash %s" % (alg_name,)) for algorithm in algorithms.values(): name, h_bytes = compute_event_reference_hash(event_json, algorithm) print("Reference hash %s: %s" % (name, encode_base64(h_bytes)))
def main(): parser = argparse.ArgumentParser() parser.add_argument("input_json", nargs="?", type=argparse.FileType('r'), default=sys.stdin) args = parser.parse_args() logging.basicConfig() event_json = dictobj(json.load(args.input_json)) algorithms = {"sha256": hashlib.sha256} for alg_name in event_json.hashes: if check_event_content_hash(event_json, algorithms[alg_name]): print("PASS content hash %s" % (alg_name, )) else: print("FAIL content hash %s" % (alg_name, )) for algorithm in algorithms.values(): name, h_bytes = compute_event_reference_hash(event_json, algorithm) print("Reference hash %s: %s" % (name, encode_base64(h_bytes)))
def callback(_, pdu): with logcontext.PreserveLoggingContext(ctx): if not check_event_content_hash(pdu): # let's try to distinguish between failures because the event was # redacted (which are somewhat expected) vs actual ball-tampering # incidents. # # This is just a heuristic, so we just assume that if the keys are # about the same between the redacted and received events, then the # received event was probably a redacted copy (but we then use our # *actual* redacted copy to be on the safe side.) redacted_event = prune_event(pdu) if ( set(redacted_event.keys()) == set(pdu.keys()) and set(six.iterkeys(redacted_event.content)) == set(six.iterkeys(pdu.content)) ): logger.info( "Event %s seems to have been redacted; using our redacted " "copy", pdu.event_id, ) else: logger.warning( "Event %s content has been tampered, redacting", pdu.event_id, pdu.get_pdu_json(), ) return redacted_event if self.spam_checker.check_event_for_spam(pdu): logger.warn( "Event contains spam, redacting %s: %s", pdu.event_id, pdu.get_pdu_json() ) return prune_event(pdu) return pdu
def on_receive_pdu(self, origin, pdu, backfilled, state=None, auth_chain=None): """ Called by the ReplicationLayer when we have a new pdu. We need to do auth checks and put it through the StateHandler. """ event = pdu logger.debug("Got event: %s", event.event_id) # If we are currently in the process of joining this room, then we # queue up events for later processing. if event.room_id in self.room_queues: self.room_queues[event.room_id].append((pdu, origin)) return logger.debug("Processing event: %s", event.event_id) redacted_event = prune_event(event) redacted_pdu_json = redacted_event.get_pdu_json() try: yield self.keyring.verify_json_for_server(event.origin, redacted_pdu_json) except SynapseError as e: logger.warn( "Signature check failed for %s redacted to %s", encode_canonical_json(pdu.get_pdu_json()), encode_canonical_json(redacted_pdu_json), ) raise FederationError( "ERROR", e.code, e.msg, affected=event.event_id, ) if not check_event_content_hash(event): logger.warn("Event content has been tampered, redacting %s, %s", event.event_id, encode_canonical_json(event.get_dict())) event = redacted_event logger.debug("Event: %s", event) # FIXME (erikj): Awful hack to make the case where we are not currently # in the room work current_state = None is_in_room = yield self.auth.check_host_in_room( event.room_id, self.server_name) if not is_in_room and not event.internal_metadata.outlier: logger.debug("Got event for room we're not in.") replication = self.replication_layer if not state: state, auth_chain = yield replication.get_state_for_context( origin, context=event.room_id, event_id=event.event_id, ) if not auth_chain: auth_chain = yield replication.get_event_auth( origin, context=event.room_id, event_id=event.event_id, ) for e in auth_chain: e.internal_metadata.outlier = True try: yield self._handle_new_event(e, fetch_auth_from=origin) except: logger.exception( "Failed to handle auth event %s", e.event_id, ) current_state = state if state: for e in state: logging.info("A :) %r", e) e.internal_metadata.outlier = True try: yield self._handle_new_event(e) except: logger.exception( "Failed to handle state event %s", e.event_id, ) try: yield self._handle_new_event( event, state=state, backfilled=backfilled, current_state=current_state, ) except AuthError as e: raise FederationError( "ERROR", e.code, e.msg, affected=event.event_id, ) # if we're receiving valid events from an origin, # it's probably a good idea to mark it as not in retry-state # for sending (although this is a bit of a leap) retry_timings = yield self.store.get_destination_retry_timings(origin) if (retry_timings and retry_timings.retry_last_ts): self.store.set_destination_retry_timings(origin, 0, 0) room = yield self.store.get_room(event.room_id) if not room: try: yield self.store.store_room( room_id=event.room_id, room_creator_user_id="", is_public=False, ) except StoreError: logger.exception("Failed to store room.") if not backfilled: extra_users = [] if event.type == EventTypes.Member: target_user_id = event.state_key target_user = self.hs.parse_userid(target_user_id) extra_users.append(target_user) yield self.notifier.on_new_room_event(event, extra_users=extra_users) if event.type == EventTypes.Member: if event.membership == Membership.JOIN: user = self.hs.parse_userid(event.state_key) yield self.distributor.fire("user_joined_room", user=user, room_id=event.room_id)
def on_receive_pdu(self, origin, pdu, backfilled, state=None): """ Called by the ReplicationLayer when we have a new pdu. We need to do auth checks and put it through the StateHandler. """ event = pdu logger.debug("Got event: %s", event.event_id) # If we are currently in the process of joining this room, then we # queue up events for later processing. if event.room_id in self.room_queues: self.room_queues[event.room_id].append((pdu, origin)) return logger.debug("Processing event: %s", event.event_id) redacted_event = prune_event(event) redacted_pdu_json = redacted_event.get_pdu_json() try: yield self.keyring.verify_json_for_server( event.origin, redacted_pdu_json ) except SynapseError as e: logger.warn( "Signature check failed for %s redacted to %s", encode_canonical_json(pdu.get_pdu_json()), encode_canonical_json(redacted_pdu_json), ) raise FederationError( "ERROR", e.code, e.msg, affected=event.event_id, ) if not check_event_content_hash(event): logger.warn( "Event content has been tampered, redacting %s, %s", event.event_id, encode_canonical_json(event.get_full_dict()) ) event = redacted_event logger.debug("Event: %s", event) # FIXME (erikj): Awful hack to make the case where we are not currently # in the room work current_state = None is_in_room = yield self.auth.check_host_in_room( event.room_id, self.server_name ) if not is_in_room and not event.outlier: logger.debug("Got event for room we're not in.") replication_layer = self.replication_layer auth_chain = yield replication_layer.get_event_auth( origin, context=event.room_id, event_id=event.event_id, ) for e in auth_chain: e.outlier = True try: yield self._handle_new_event(e, fetch_missing=False) except: logger.exception( "Failed to parse auth event %s", e.event_id, ) if not state: state = yield replication_layer.get_state_for_context( origin, context=event.room_id, event_id=event.event_id, ) current_state = state if state: for e in state: e.outlier = True try: yield self._handle_new_event(e) except: logger.exception( "Failed to parse state event %s", e.event_id, ) try: yield self._handle_new_event( event, state=state, backfilled=backfilled, current_state=current_state, ) except AuthError as e: raise FederationError( "ERROR", e.code, e.msg, affected=event.event_id, ) room = yield self.store.get_room(event.room_id) if not room: try: yield self.store.store_room( room_id=event.room_id, room_creator_user_id="", is_public=False, ) except StoreError: logger.exception("Failed to store room.") if not backfilled: extra_users = [] if event.type == RoomMemberEvent.TYPE: target_user_id = event.state_key target_user = self.hs.parse_userid(target_user_id) extra_users.append(target_user) yield self.notifier.on_new_room_event( event, extra_users=extra_users ) if event.type == RoomMemberEvent.TYPE: if event.membership == Membership.JOIN: user = self.hs.parse_userid(event.state_key) yield self.distributor.fire( "user_joined_room", user=user, room_id=event.room_id )