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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
        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
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
 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
Exemplo n.º 7
0
    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
Exemplo n.º 9
0
        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
Exemplo n.º 10
0
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)))
Exemplo n.º 11
0
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)))
Exemplo n.º 12
0
        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
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
    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
                )