diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 34a6c87b35..0000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-version: 2
-jobs:
- dockerhubuploadrelease:
- machine: true
- steps:
- - checkout
- - run: docker build -f docker/Dockerfile --label gitsha1=${CIRCLE_SHA1} -t matrixdotorg/synapse:${CIRCLE_TAG} -t matrixdotorg/synapse:${CIRCLE_TAG}-py3 .
- - run: docker login --username $DOCKER_HUB_USERNAME --password $DOCKER_HUB_PASSWORD
- - run: docker push matrixdotorg/synapse:${CIRCLE_TAG}
- - run: docker push matrixdotorg/synapse:${CIRCLE_TAG}-py3
- dockerhubuploadreleaseshhs:
- machine: true
- steps:
- - checkout
- - run: docker build -f docker/Dockerfile --label gitsha1=${CIRCLE_SHA1} -t matrixdotorg/synapse:${CIRCLE_TAG} --build-arg PYTHON_VERSION=3.7 .
- - run: docker login --username $DOCKER_HUB_USERNAME --password $DOCKER_HUB_PASSWORD
- - run: docker push matrixdotorg/synapse:${CIRCLE_TAG}
- dockerhubuploadlatest:
- machine: true
- steps:
- - checkout
- - run: docker build -f docker/Dockerfile --label gitsha1=${CIRCLE_SHA1} -t matrixdotorg/synapse:latest -t matrixdotorg/synapse:latest-py3 .
- - run: docker login --username $DOCKER_HUB_USERNAME --password $DOCKER_HUB_PASSWORD
- - run: docker push matrixdotorg/synapse:latest
- - run: docker push matrixdotorg/synapse:latest-py3
-
-workflows:
- version: 2
- build:
- jobs:
- - dockerhubuploadrelease:
- filters:
- tags:
- only: /v[0-9].[0-9]+.[0-9]+.*/
- branches:
- ignore: /.*/
- - dockerhubuploadreleaseshhs:
- filters:
- tags:
- only: /shhs-v[0-9]+.[0-9]+.*/
- branches:
- ignore: /.*/
- - dockerhubuploadlatest:
- filters:
- branches:
- only: master
diff --git a/CHANGES.md b/CHANGES.md
index d655723326..fa753bed17 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,22 @@
+Synapse 1.2.1 (2019-07-26)
+==========================
+
+Security update
+---------------
+
+This release includes *four* security fixes:
+
+- Prevent an attack where a federated server could send redactions for arbitrary events in v1 and v2 rooms. ([\#5767](https://github.com/matrix-org/synapse/issues/5767))
+- Prevent a denial-of-service attack where cycles of redaction events would make Synapse spin infinitely. Thanks to `@lrizika:matrix.org` for identifying and responsibly disclosing this issue. ([0f2ecb961](https://github.com/matrix-org/synapse/commit/0f2ecb961))
+- Prevent an attack where users could be joined or parted from public rooms without their consent. Thanks to @Dylanger for identifying and responsibly disclosing this issue. ([\#5744](https://github.com/matrix-org/synapse/issues/5744))
+- Fix a vulnerability where a federated server could spoof read-receipts from
+ users on other servers. Thanks to @Dylanger for identifying this issue too. ([\#5743](https://github.com/matrix-org/synapse/issues/5743))
+
+Additionally, the following fix was in Synapse **1.2.0**, but was not correctly
+identified during the original release:
+
+- It was possible for a room moderator to send a redaction for an `m.room.create` event, which would downgrade the room to version 1. Thanks to `/dev/ponies` for identifying and responsibly disclosing this issue! ([\#5701](https://github.com/matrix-org/synapse/issues/5701))
+
Synapse 1.2.0 (2019-07-25)
==========================
@@ -16,6 +35,14 @@ Bugfixes
Synapse 1.2.0rc1 (2019-07-22)
=============================
+Security fixes
+--------------
+
+This update included a security fix which was initially incorrectly flagged as
+a regular bug fix.
+
+- It was possible for a room moderator to send a redaction for an `m.room.create` event, which would downgrade the room to version 1. Thanks to `/dev/ponies` for identifying and responsibly disclosing this issue! ([\#5701](https://github.com/matrix-org/synapse/issues/5701))
+
Features
--------
@@ -41,7 +68,6 @@ Bugfixes
- Fix bug in #5626 that prevented the original_event field from actually having the contents of the original event in a call to `/relations`. ([\#5654](https://github.com/matrix-org/synapse/issues/5654))
- Fix 3PID bind requests being sent to identity servers as `application/x-form-www-urlencoded` data, which is deprecated. ([\#5658](https://github.com/matrix-org/synapse/issues/5658))
- Fix some problems with authenticating redactions in recent room versions. ([\#5699](https://github.com/matrix-org/synapse/issues/5699), [\#5700](https://github.com/matrix-org/synapse/issues/5700), [\#5707](https://github.com/matrix-org/synapse/issues/5707))
-- Ignore redactions of m.room.create events. ([\#5701](https://github.com/matrix-org/synapse/issues/5701))
Updates to the Docker image
diff --git a/debian/changelog b/debian/changelog
index aafdd1cde2..6634c1085a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+matrix-synapse-py3 (1.2.1) stable; urgency=medium
+
+ * New synapse release 1.2.1.
+
+ -- Synapse Packaging team <packages@matrix.org> Fri, 26 Jul 2019 11:32:47 +0100
+
matrix-synapse-py3 (1.2.0) stable; urgency=medium
[ Amber Brown ]
diff --git a/synapse/__init__.py b/synapse/__init__.py
index 3435de4e2f..8301a13d8f 100644
--- a/synapse/__init__.py
+++ b/synapse/__init__.py
@@ -35,4 +35,4 @@ try:
except ImportError:
pass
-__version__ = "1.2.0"
+__version__ = "1.2.1"
diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py
index 8c0a18b120..ed2b6d5eef 100644
--- a/synapse/federation/federation_server.py
+++ b/synapse/federation/federation_server.py
@@ -369,7 +369,7 @@ class FederationServer(FederationBase):
logger.warn("Room version %s not in %s", room_version, supported_versions)
raise IncompatibleRoomVersionError(room_version=room_version)
- pdu = yield self.handler.on_make_join_request(room_id, user_id)
+ pdu = yield self.handler.on_make_join_request(origin, room_id, user_id)
time_now = self._clock.time_msec()
defer.returnValue(
{"event": pdu.get_pdu_json(time_now), "room_version": room_version}
@@ -423,7 +423,7 @@ class FederationServer(FederationBase):
def on_make_leave_request(self, origin, room_id, user_id):
origin_host, _ = parse_server_name(origin)
yield self.check_server_matches_acl(origin_host, room_id)
- pdu = yield self.handler.on_make_leave_request(room_id, user_id)
+ pdu = yield self.handler.on_make_leave_request(origin, room_id, user_id)
room_version = yield self.store.get_room_version(room_id)
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index b32049b064..b944f26f91 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -1204,11 +1204,28 @@ class FederationHandler(BaseHandler):
@defer.inlineCallbacks
@log_function
- def on_make_join_request(self, room_id, user_id):
+ def on_make_join_request(self, origin, room_id, user_id):
""" We've received a /make_join/ request, so we create a partial
join event for the room and return that. We do *not* persist or
process it until the other server has signed it and sent it back.
+
+ Args:
+ origin (str): The (verified) server name of the requesting server.
+ room_id (str): Room to create join event in
+ user_id (str): The user to create the join for
+
+ Returns:
+ Deferred[FrozenEvent]
"""
+
+ if get_domain_from_id(user_id) != origin:
+ logger.info(
+ "Got /make_join request for user %r from different origin %s, ignoring",
+ user_id,
+ origin,
+ )
+ raise SynapseError(403, "User not from origin", Codes.FORBIDDEN)
+
event_content = {"membership": Membership.JOIN}
room_version = yield self.store.get_room_version(room_id)
@@ -1411,11 +1428,27 @@ class FederationHandler(BaseHandler):
@defer.inlineCallbacks
@log_function
- def on_make_leave_request(self, room_id, user_id):
+ def on_make_leave_request(self, origin, room_id, user_id):
""" We've received a /make_leave/ request, so we create a partial
leave event for the room and return that. We do *not* persist or
process it until the other server has signed it and sent it back.
+
+ Args:
+ origin (str): The (verified) server name of the requesting server.
+ room_id (str): Room to create leave event in
+ user_id (str): The user to create the leave for
+
+ Returns:
+ Deferred[FrozenEvent]
"""
+ if get_domain_from_id(user_id) != origin:
+ logger.info(
+ "Got /make_leave request for user %r from different origin %s, ignoring",
+ user_id,
+ origin,
+ )
+ raise SynapseError(403, "User not from origin", Codes.FORBIDDEN)
+
room_version = yield self.store.get_room_version(room_id)
builder = self.event_builder_factory.new(
room_version,
diff --git a/synapse/handlers/receipts.py b/synapse/handlers/receipts.py
index a85dd8cdee..e58bf7e360 100644
--- a/synapse/handlers/receipts.py
+++ b/synapse/handlers/receipts.py
@@ -17,7 +17,7 @@ import logging
from twisted.internet import defer
from synapse.handlers._base import BaseHandler
-from synapse.types import ReadReceipt
+from synapse.types import ReadReceipt, get_domain_from_id
logger = logging.getLogger(__name__)
@@ -40,18 +40,27 @@ class ReceiptsHandler(BaseHandler):
def _received_remote_receipt(self, origin, content):
"""Called when we receive an EDU of type m.receipt from a remote HS.
"""
- receipts = [
- ReadReceipt(
- room_id=room_id,
- receipt_type=receipt_type,
- user_id=user_id,
- event_ids=user_values["event_ids"],
- data=user_values.get("data", {}),
- )
- for room_id, room_values in content.items()
- for receipt_type, users in room_values.items()
- for user_id, user_values in users.items()
- ]
+ receipts = []
+ for room_id, room_values in content.items():
+ for receipt_type, users in room_values.items():
+ for user_id, user_values in users.items():
+ if get_domain_from_id(user_id) != origin:
+ logger.info(
+ "Received receipt for user %r from server %s, ignoring",
+ user_id,
+ origin,
+ )
+ continue
+
+ receipts.append(
+ ReadReceipt(
+ room_id=room_id,
+ receipt_type=receipt_type,
+ user_id=user_id,
+ event_ids=user_values["event_ids"],
+ data=user_values.get("data", {}),
+ )
+ )
yield self._handle_new_receipts(receipts)
diff --git a/synapse/storage/events_worker.py b/synapse/storage/events_worker.py
index c1d8d57dcf..95e58fcf90 100644
--- a/synapse/storage/events_worker.py
+++ b/synapse/storage/events_worker.py
@@ -268,6 +268,14 @@ class EventsWorkerStore(SQLBaseStore):
)
continue
+ if original_event.room_id != entry.event.room_id:
+ logger.info(
+ "Withholding redaction %s of event %s from a different room",
+ event_id,
+ redacted_event_id,
+ )
+ continue
+
if entry.event.internal_metadata.need_to_check_redaction():
original_domain = get_domain_from_id(original_event.sender)
redaction_domain = get_domain_from_id(entry.event.sender)
@@ -629,6 +637,10 @@ class EventsWorkerStore(SQLBaseStore):
# we choose to ignore redactions of m.room.create events.
return None
+ if original_ev.type == "m.room.redaction":
+ # ... and redaction events
+ return None
+
redaction_map = yield self._get_events_from_cache_or_db(redactions)
for redaction_id in redactions:
@@ -636,9 +648,21 @@ class EventsWorkerStore(SQLBaseStore):
if not redaction_entry:
# we don't have the redaction event, or the redaction event was not
# authorized.
+ logger.debug(
+ "%s was redacted by %s but redaction not found/authed",
+ original_ev.event_id,
+ redaction_id,
+ )
continue
redaction_event = redaction_entry.event
+ if redaction_event.room_id != original_ev.room_id:
+ logger.debug(
+ "%s was redacted by %s but redaction was in a different room!",
+ original_ev.event_id,
+ redaction_id,
+ )
+ continue
# Starting in room version v3, some redactions need to be
# rechecked if we didn't have the redacted event at the
@@ -650,8 +674,15 @@ class EventsWorkerStore(SQLBaseStore):
redaction_event.internal_metadata.recheck_redaction = False
else:
# Senders don't match, so the event isn't actually redacted
+ logger.debug(
+ "%s was redacted by %s but the senders don't match",
+ original_ev.event_id,
+ redaction_id,
+ )
continue
+ logger.debug("Redacting %s due to %s", original_ev.event_id, redaction_id)
+
# we found a good redaction event. Redact!
redacted_event = prune_event(original_ev)
redacted_event.unsigned["redacted_by"] = redaction_id
|