mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Fix inconsistencies in event validation (#13088)
This commit is contained in:
parent
e16ea87d0f
commit
d4b1c0d800
1
changelog.d/13088.bugfix
Normal file
1
changelog.d/13088.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix some inconsistencies in the event authentication code.
|
@ -150,7 +150,7 @@ async def check_state_independent_auth_rules(
|
|||||||
# 1.5 Otherwise, allow
|
# 1.5 Otherwise, allow
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check the auth events.
|
# 2. Reject if event has auth_events that: ...
|
||||||
auth_events = await store.get_events(
|
auth_events = await store.get_events(
|
||||||
event.auth_event_ids(),
|
event.auth_event_ids(),
|
||||||
redact_behaviour=EventRedactBehaviour.as_is,
|
redact_behaviour=EventRedactBehaviour.as_is,
|
||||||
@ -158,6 +158,7 @@ async def check_state_independent_auth_rules(
|
|||||||
)
|
)
|
||||||
room_id = event.room_id
|
room_id = event.room_id
|
||||||
auth_dict: MutableStateMap[str] = {}
|
auth_dict: MutableStateMap[str] = {}
|
||||||
|
expected_auth_types = auth_types_for_event(event.room_version, event)
|
||||||
for auth_event_id in event.auth_event_ids():
|
for auth_event_id in event.auth_event_ids():
|
||||||
auth_event = auth_events.get(auth_event_id)
|
auth_event = auth_events.get(auth_event_id)
|
||||||
|
|
||||||
@ -179,6 +180,24 @@ async def check_state_independent_auth_rules(
|
|||||||
% (event.event_id, room_id, auth_event_id, auth_event.room_id),
|
% (event.event_id, room_id, auth_event_id, auth_event.room_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
k = (auth_event.type, auth_event.state_key)
|
||||||
|
|
||||||
|
# 2.1 ... have duplicate entries for a given type and state_key pair
|
||||||
|
if k in auth_dict:
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
f"Event {event.event_id} has duplicate auth_events for {k}: {auth_dict[k]} and {auth_event_id}",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2.2 ... have entries whose type and state_key don’t match those specified by
|
||||||
|
# the auth events selection algorithm described in the server
|
||||||
|
# specification.
|
||||||
|
if k not in expected_auth_types:
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
f"Event {event.event_id} has unexpected auth_event for {k}: {auth_event_id}",
|
||||||
|
)
|
||||||
|
|
||||||
# We also need to check that the auth event itself is not rejected.
|
# We also need to check that the auth event itself is not rejected.
|
||||||
if auth_event.rejected_reason:
|
if auth_event.rejected_reason:
|
||||||
raise AuthError(
|
raise AuthError(
|
||||||
@ -187,7 +206,7 @@ async def check_state_independent_auth_rules(
|
|||||||
% (event.event_id, auth_event.event_id),
|
% (event.event_id, auth_event.event_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
auth_dict[(auth_event.type, auth_event.state_key)] = auth_event_id
|
auth_dict[k] = auth_event_id
|
||||||
|
|
||||||
# 3. If event does not have a m.room.create in its auth_events, reject.
|
# 3. If event does not have a m.room.create in its auth_events, reject.
|
||||||
creation_event = auth_dict.get((EventTypes.Create, ""), None)
|
creation_event = auth_dict.get((EventTypes.Create, ""), None)
|
||||||
|
@ -225,9 +225,10 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
|
|||||||
|
|
||||||
# we need a user on the remote server to be a member, so that we can send
|
# we need a user on the remote server to be a member, so that we can send
|
||||||
# extremity-causing events.
|
# extremity-causing events.
|
||||||
|
remote_server_user_id = f"@user:{self.OTHER_SERVER_NAME}"
|
||||||
self.get_success(
|
self.get_success(
|
||||||
event_injection.inject_member_event(
|
event_injection.inject_member_event(
|
||||||
self.hs, room_id, f"@user:{self.OTHER_SERVER_NAME}", "join"
|
self.hs, room_id, remote_server_user_id, "join"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -247,6 +248,12 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
|
|||||||
# create more than is 5 which corresponds to the number of backward
|
# create more than is 5 which corresponds to the number of backward
|
||||||
# extremities we slice off in `_maybe_backfill_inner`
|
# extremities we slice off in `_maybe_backfill_inner`
|
||||||
federation_event_handler = self.hs.get_federation_event_handler()
|
federation_event_handler = self.hs.get_federation_event_handler()
|
||||||
|
auth_events = [
|
||||||
|
ev
|
||||||
|
for ev in current_state
|
||||||
|
if (ev.type, ev.state_key)
|
||||||
|
in {("m.room.create", ""), ("m.room.member", remote_server_user_id)}
|
||||||
|
]
|
||||||
for _ in range(0, 8):
|
for _ in range(0, 8):
|
||||||
event = make_event_from_dict(
|
event = make_event_from_dict(
|
||||||
self.add_hashes_and_signatures(
|
self.add_hashes_and_signatures(
|
||||||
@ -258,15 +265,14 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
|
|||||||
"body": "message connected to fake event",
|
"body": "message connected to fake event",
|
||||||
},
|
},
|
||||||
"room_id": room_id,
|
"room_id": room_id,
|
||||||
"sender": f"@user:{self.OTHER_SERVER_NAME}",
|
"sender": remote_server_user_id,
|
||||||
"prev_events": [
|
"prev_events": [
|
||||||
ev1.event_id,
|
ev1.event_id,
|
||||||
# We're creating an backward extremity each time thanks
|
# We're creating an backward extremity each time thanks
|
||||||
# to this fake event
|
# to this fake event
|
||||||
generate_fake_event_id(),
|
generate_fake_event_id(),
|
||||||
],
|
],
|
||||||
# lazy: *everything* is an auth event
|
"auth_events": [ev.event_id for ev in auth_events],
|
||||||
"auth_events": [ev.event_id for ev in current_state],
|
|
||||||
"depth": ev1.depth + 1,
|
"depth": ev1.depth + 1,
|
||||||
},
|
},
|
||||||
room_version,
|
room_version,
|
||||||
|
@ -98,7 +98,6 @@ class FederationEventHandlerTests(unittest.FederatingHomeserverTestCase):
|
|||||||
auth_event_ids = [
|
auth_event_ids = [
|
||||||
initial_state_map[("m.room.create", "")],
|
initial_state_map[("m.room.create", "")],
|
||||||
initial_state_map[("m.room.power_levels", "")],
|
initial_state_map[("m.room.power_levels", "")],
|
||||||
initial_state_map[("m.room.join_rules", "")],
|
|
||||||
member_event.event_id,
|
member_event.event_id,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -150,6 +150,92 @@ class EventAuthTestCase(unittest.TestCase):
|
|||||||
event_auth.check_state_independent_auth_rules(event_store, bad_event)
|
event_auth.check_state_independent_auth_rules(event_store, bad_event)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_duplicate_auth_events(self):
|
||||||
|
"""Events with duplicate auth_events should be rejected
|
||||||
|
|
||||||
|
https://spec.matrix.org/v1.3/rooms/v9/#authorization-rules
|
||||||
|
2. Reject if event has auth_events that:
|
||||||
|
1. have duplicate entries for a given type and state_key pair
|
||||||
|
"""
|
||||||
|
creator = "@creator:example.com"
|
||||||
|
|
||||||
|
create_event = _create_event(RoomVersions.V9, creator)
|
||||||
|
join_event1 = _join_event(RoomVersions.V9, creator)
|
||||||
|
pl_event = _power_levels_event(
|
||||||
|
RoomVersions.V9,
|
||||||
|
creator,
|
||||||
|
{"state_default": 30, "users": {"creator": 100}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# create a second join event, so that we can make a duplicate
|
||||||
|
join_event2 = _join_event(RoomVersions.V9, creator)
|
||||||
|
|
||||||
|
event_store = _StubEventSourceStore()
|
||||||
|
event_store.add_events([create_event, join_event1, join_event2, pl_event])
|
||||||
|
|
||||||
|
good_event = _random_state_event(
|
||||||
|
RoomVersions.V9, creator, [create_event, join_event2, pl_event]
|
||||||
|
)
|
||||||
|
bad_event = _random_state_event(
|
||||||
|
RoomVersions.V9, creator, [create_event, join_event1, join_event2, pl_event]
|
||||||
|
)
|
||||||
|
# a variation: two instances of the *same* event
|
||||||
|
bad_event2 = _random_state_event(
|
||||||
|
RoomVersions.V9, creator, [create_event, join_event2, join_event2, pl_event]
|
||||||
|
)
|
||||||
|
|
||||||
|
get_awaitable_result(
|
||||||
|
event_auth.check_state_independent_auth_rules(event_store, good_event)
|
||||||
|
)
|
||||||
|
with self.assertRaises(AuthError):
|
||||||
|
get_awaitable_result(
|
||||||
|
event_auth.check_state_independent_auth_rules(event_store, bad_event)
|
||||||
|
)
|
||||||
|
with self.assertRaises(AuthError):
|
||||||
|
get_awaitable_result(
|
||||||
|
event_auth.check_state_independent_auth_rules(event_store, bad_event2)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unexpected_auth_events(self):
|
||||||
|
"""Events with excess auth_events should be rejected
|
||||||
|
|
||||||
|
https://spec.matrix.org/v1.3/rooms/v9/#authorization-rules
|
||||||
|
2. Reject if event has auth_events that:
|
||||||
|
2. have entries whose type and state_key don’t match those specified by the
|
||||||
|
auth events selection algorithm described in the server specification.
|
||||||
|
"""
|
||||||
|
creator = "@creator:example.com"
|
||||||
|
|
||||||
|
create_event = _create_event(RoomVersions.V9, creator)
|
||||||
|
join_event = _join_event(RoomVersions.V9, creator)
|
||||||
|
pl_event = _power_levels_event(
|
||||||
|
RoomVersions.V9,
|
||||||
|
creator,
|
||||||
|
{"state_default": 30, "users": {"creator": 100}},
|
||||||
|
)
|
||||||
|
join_rules_event = _join_rules_event(RoomVersions.V9, creator, "public")
|
||||||
|
|
||||||
|
event_store = _StubEventSourceStore()
|
||||||
|
event_store.add_events([create_event, join_event, pl_event, join_rules_event])
|
||||||
|
|
||||||
|
good_event = _random_state_event(
|
||||||
|
RoomVersions.V9, creator, [create_event, join_event, pl_event]
|
||||||
|
)
|
||||||
|
# join rules should *not* be included in the auth events.
|
||||||
|
bad_event = _random_state_event(
|
||||||
|
RoomVersions.V9,
|
||||||
|
creator,
|
||||||
|
[create_event, join_event, pl_event, join_rules_event],
|
||||||
|
)
|
||||||
|
|
||||||
|
get_awaitable_result(
|
||||||
|
event_auth.check_state_independent_auth_rules(event_store, good_event)
|
||||||
|
)
|
||||||
|
with self.assertRaises(AuthError):
|
||||||
|
get_awaitable_result(
|
||||||
|
event_auth.check_state_independent_auth_rules(event_store, bad_event)
|
||||||
|
)
|
||||||
|
|
||||||
def test_random_users_cannot_send_state_before_first_pl(self):
|
def test_random_users_cannot_send_state_before_first_pl(self):
|
||||||
"""
|
"""
|
||||||
Check that, before the first PL lands, the creator is the only user
|
Check that, before the first PL lands, the creator is the only user
|
||||||
|
Loading…
Reference in New Issue
Block a user