Block clients from sending server ACLs that lock the local server out. (#8708)

Fixes #4042
This commit is contained in:
Erik Johnston 2020-11-03 12:13:48 +00:00 committed by GitHub
parent 4b09b7438e
commit 243d427fbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 9 deletions

1
changelog.d/8708.misc Normal file
View file

@ -0,0 +1 @@
Block attempts by clients to send server ACLs, or redactions of server ACLs, that would result in the local server being blocked from the room.

View file

@ -13,6 +13,7 @@ files =
synapse/config, synapse/config,
synapse/event_auth.py, synapse/event_auth.py,
synapse/events/builder.py, synapse/events/builder.py,
synapse/events/validator.py,
synapse/events/spamcheck.py, synapse/events/spamcheck.py,
synapse/federation, synapse/federation,
synapse/handlers/_base.py, synapse/handlers/_base.py,

View file

@ -13,20 +13,26 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from typing import Union
from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership
from synapse.api.errors import Codes, SynapseError from synapse.api.errors import Codes, SynapseError
from synapse.api.room_versions import EventFormatVersions from synapse.api.room_versions import EventFormatVersions
from synapse.config.homeserver import HomeServerConfig
from synapse.events import EventBase
from synapse.events.builder import EventBuilder
from synapse.events.utils import validate_canonicaljson from synapse.events.utils import validate_canonicaljson
from synapse.federation.federation_server import server_matches_acl_event
from synapse.types import EventID, RoomID, UserID from synapse.types import EventID, RoomID, UserID
class EventValidator: class EventValidator:
def validate_new(self, event, config): def validate_new(self, event: EventBase, config: HomeServerConfig):
"""Validates the event has roughly the right format """Validates the event has roughly the right format
Args: Args:
event (FrozenEvent): The event to validate. event: The event to validate.
config (Config): The homeserver's configuration. config: The homeserver's configuration.
""" """
self.validate_builder(event) self.validate_builder(event)
@ -76,12 +82,18 @@ class EventValidator:
if event.type == EventTypes.Retention: if event.type == EventTypes.Retention:
self._validate_retention(event) self._validate_retention(event)
def _validate_retention(self, event): if event.type == EventTypes.ServerACL:
if not server_matches_acl_event(config.server_name, event):
raise SynapseError(
400, "Can't create an ACL event that denies the local server"
)
def _validate_retention(self, event: EventBase):
"""Checks that an event that defines the retention policy for a room respects the """Checks that an event that defines the retention policy for a room respects the
format enforced by the spec. format enforced by the spec.
Args: Args:
event (FrozenEvent): The event to validate. event: The event to validate.
""" """
if not event.is_state(): if not event.is_state():
raise SynapseError(code=400, msg="must be a state event") raise SynapseError(code=400, msg="must be a state event")
@ -116,13 +128,10 @@ class EventValidator:
errcode=Codes.BAD_JSON, errcode=Codes.BAD_JSON,
) )
def validate_builder(self, event): def validate_builder(self, event: Union[EventBase, EventBuilder]):
"""Validates that the builder/event has roughly the right format. Only """Validates that the builder/event has roughly the right format. Only
checks values that we expect a proto event to have, rather than all the checks values that we expect a proto event to have, rather than all the
fields an event would have fields an event would have
Args:
event (EventBuilder|FrozenEvent)
""" """
strings = ["room_id", "sender", "type"] strings = ["room_id", "sender", "type"]

View file

@ -1138,6 +1138,9 @@ class EventCreationHandler:
if original_event.room_id != event.room_id: if original_event.room_id != event.room_id:
raise SynapseError(400, "Cannot redact event from a different room") raise SynapseError(400, "Cannot redact event from a different room")
if original_event.type == EventTypes.ServerACL:
raise AuthError(403, "Redacting server ACL events is not permitted")
prev_state_ids = await context.get_prev_state_ids() prev_state_ids = await context.get_prev_state_ids()
auth_events_ids = self.auth.compute_auth_events( auth_events_ids = self.auth.compute_auth_events(
event, prev_state_ids, for_verification=True event, prev_state_ids, for_verification=True

View file

@ -154,3 +154,60 @@ class EventCreationTestCase(unittest.HomeserverTestCase):
# Check that we've deduplicated the events. # Check that we've deduplicated the events.
self.assertEqual(len(events), 2) self.assertEqual(len(events), 2)
self.assertEqual(events[0].event_id, events[1].event_id) self.assertEqual(events[0].event_id, events[1].event_id)
class ServerAclValidationTestCase(unittest.HomeserverTestCase):
servlets = [
admin.register_servlets,
login.register_servlets,
room.register_servlets,
]
def prepare(self, reactor, clock, hs):
self.user_id = self.register_user("tester", "foobar")
self.access_token = self.login("tester", "foobar")
self.room_id = self.helper.create_room_as(self.user_id, tok=self.access_token)
def test_allow_server_acl(self):
"""Test that sending an ACL that blocks everyone but ourselves works.
"""
self.helper.send_state(
self.room_id,
EventTypes.ServerACL,
body={"allow": [self.hs.hostname]},
tok=self.access_token,
expect_code=200,
)
def test_deny_server_acl_block_outselves(self):
"""Test that sending an ACL that blocks ourselves does not work.
"""
self.helper.send_state(
self.room_id,
EventTypes.ServerACL,
body={},
tok=self.access_token,
expect_code=400,
)
def test_deny_redact_server_acl(self):
"""Test that attempting to redact an ACL is blocked.
"""
body = self.helper.send_state(
self.room_id,
EventTypes.ServerACL,
body={"allow": [self.hs.hostname]},
tok=self.access_token,
expect_code=200,
)
event_id = body["event_id"]
# Redaction of event should fail.
path = "/_matrix/client/r0/rooms/%s/redact/%s" % (self.room_id, event_id)
request, channel = self.make_request(
"POST", path, content={}, access_token=self.access_token
)
self.render(request)
self.assertEqual(int(channel.result["code"]), 403)