Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit d4b1c0d

Browse files
authored
Fix inconsistencies in event validation (#13088)
1 parent e16ea87 commit d4b1c0d

File tree

5 files changed

+118
-7
lines changed

5 files changed

+118
-7
lines changed

changelog.d/13088.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix some inconsistencies in the event authentication code.

synapse/event_auth.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,15 @@ async def check_state_independent_auth_rules(
150150
# 1.5 Otherwise, allow
151151
return
152152

153-
# Check the auth events.
153+
# 2. Reject if event has auth_events that: ...
154154
auth_events = await store.get_events(
155155
event.auth_event_ids(),
156156
redact_behaviour=EventRedactBehaviour.as_is,
157157
allow_rejected=True,
158158
)
159159
room_id = event.room_id
160160
auth_dict: MutableStateMap[str] = {}
161+
expected_auth_types = auth_types_for_event(event.room_version, event)
161162
for auth_event_id in event.auth_event_ids():
162163
auth_event = auth_events.get(auth_event_id)
163164

@@ -179,6 +180,24 @@ async def check_state_independent_auth_rules(
179180
% (event.event_id, room_id, auth_event_id, auth_event.room_id),
180181
)
181182

183+
k = (auth_event.type, auth_event.state_key)
184+
185+
# 2.1 ... have duplicate entries for a given type and state_key pair
186+
if k in auth_dict:
187+
raise AuthError(
188+
403,
189+
f"Event {event.event_id} has duplicate auth_events for {k}: {auth_dict[k]} and {auth_event_id}",
190+
)
191+
192+
# 2.2 ... have entries whose type and state_key don’t match those specified by
193+
# the auth events selection algorithm described in the server
194+
# specification.
195+
if k not in expected_auth_types:
196+
raise AuthError(
197+
403,
198+
f"Event {event.event_id} has unexpected auth_event for {k}: {auth_event_id}",
199+
)
200+
182201
# We also need to check that the auth event itself is not rejected.
183202
if auth_event.rejected_reason:
184203
raise AuthError(
@@ -187,7 +206,7 @@ async def check_state_independent_auth_rules(
187206
% (event.event_id, auth_event.event_id),
188207
)
189208

190-
auth_dict[(auth_event.type, auth_event.state_key)] = auth_event_id
209+
auth_dict[k] = auth_event_id
191210

192211
# 3. If event does not have a m.room.create in its auth_events, reject.
193212
creation_event = auth_dict.get((EventTypes.Create, ""), None)

tests/handlers/test_federation.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,10 @@ def test_backfill_with_many_backward_extremities(self) -> None:
225225

226226
# we need a user on the remote server to be a member, so that we can send
227227
# extremity-causing events.
228+
remote_server_user_id = f"@user:{self.OTHER_SERVER_NAME}"
228229
self.get_success(
229230
event_injection.inject_member_event(
230-
self.hs, room_id, f"@user:{self.OTHER_SERVER_NAME}", "join"
231+
self.hs, room_id, remote_server_user_id, "join"
231232
)
232233
)
233234

@@ -247,6 +248,12 @@ def test_backfill_with_many_backward_extremities(self) -> None:
247248
# create more than is 5 which corresponds to the number of backward
248249
# extremities we slice off in `_maybe_backfill_inner`
249250
federation_event_handler = self.hs.get_federation_event_handler()
251+
auth_events = [
252+
ev
253+
for ev in current_state
254+
if (ev.type, ev.state_key)
255+
in {("m.room.create", ""), ("m.room.member", remote_server_user_id)}
256+
]
250257
for _ in range(0, 8):
251258
event = make_event_from_dict(
252259
self.add_hashes_and_signatures(
@@ -258,15 +265,14 @@ def test_backfill_with_many_backward_extremities(self) -> None:
258265
"body": "message connected to fake event",
259266
},
260267
"room_id": room_id,
261-
"sender": f"@user:{self.OTHER_SERVER_NAME}",
268+
"sender": remote_server_user_id,
262269
"prev_events": [
263270
ev1.event_id,
264271
# We're creating an backward extremity each time thanks
265272
# to this fake event
266273
generate_fake_event_id(),
267274
],
268-
# lazy: *everything* is an auth event
269-
"auth_events": [ev.event_id for ev in current_state],
275+
"auth_events": [ev.event_id for ev in auth_events],
270276
"depth": ev1.depth + 1,
271277
},
272278
room_version,

tests/handlers/test_federation_event.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ def _test_process_pulled_event_with_missing_state(
9898
auth_event_ids = [
9999
initial_state_map[("m.room.create", "")],
100100
initial_state_map[("m.room.power_levels", "")],
101-
initial_state_map[("m.room.join_rules", "")],
102101
member_event.event_id,
103102
]
104103

tests/test_event_auth.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,92 @@ def test_create_event_with_prev_events(self):
150150
event_auth.check_state_independent_auth_rules(event_store, bad_event)
151151
)
152152

153+
def test_duplicate_auth_events(self):
154+
"""Events with duplicate auth_events should be rejected
155+
156+
https://spec.matrix.org/v1.3/rooms/v9/#authorization-rules
157+
2. Reject if event has auth_events that:
158+
1. have duplicate entries for a given type and state_key pair
159+
"""
160+
creator = "@creator:example.com"
161+
162+
create_event = _create_event(RoomVersions.V9, creator)
163+
join_event1 = _join_event(RoomVersions.V9, creator)
164+
pl_event = _power_levels_event(
165+
RoomVersions.V9,
166+
creator,
167+
{"state_default": 30, "users": {"creator": 100}},
168+
)
169+
170+
# create a second join event, so that we can make a duplicate
171+
join_event2 = _join_event(RoomVersions.V9, creator)
172+
173+
event_store = _StubEventSourceStore()
174+
event_store.add_events([create_event, join_event1, join_event2, pl_event])
175+
176+
good_event = _random_state_event(
177+
RoomVersions.V9, creator, [create_event, join_event2, pl_event]
178+
)
179+
bad_event = _random_state_event(
180+
RoomVersions.V9, creator, [create_event, join_event1, join_event2, pl_event]
181+
)
182+
# a variation: two instances of the *same* event
183+
bad_event2 = _random_state_event(
184+
RoomVersions.V9, creator, [create_event, join_event2, join_event2, pl_event]
185+
)
186+
187+
get_awaitable_result(
188+
event_auth.check_state_independent_auth_rules(event_store, good_event)
189+
)
190+
with self.assertRaises(AuthError):
191+
get_awaitable_result(
192+
event_auth.check_state_independent_auth_rules(event_store, bad_event)
193+
)
194+
with self.assertRaises(AuthError):
195+
get_awaitable_result(
196+
event_auth.check_state_independent_auth_rules(event_store, bad_event2)
197+
)
198+
199+
def test_unexpected_auth_events(self):
200+
"""Events with excess auth_events should be rejected
201+
202+
https://spec.matrix.org/v1.3/rooms/v9/#authorization-rules
203+
2. Reject if event has auth_events that:
204+
2. have entries whose type and state_key don’t match those specified by the
205+
auth events selection algorithm described in the server specification.
206+
"""
207+
creator = "@creator:example.com"
208+
209+
create_event = _create_event(RoomVersions.V9, creator)
210+
join_event = _join_event(RoomVersions.V9, creator)
211+
pl_event = _power_levels_event(
212+
RoomVersions.V9,
213+
creator,
214+
{"state_default": 30, "users": {"creator": 100}},
215+
)
216+
join_rules_event = _join_rules_event(RoomVersions.V9, creator, "public")
217+
218+
event_store = _StubEventSourceStore()
219+
event_store.add_events([create_event, join_event, pl_event, join_rules_event])
220+
221+
good_event = _random_state_event(
222+
RoomVersions.V9, creator, [create_event, join_event, pl_event]
223+
)
224+
# join rules should *not* be included in the auth events.
225+
bad_event = _random_state_event(
226+
RoomVersions.V9,
227+
creator,
228+
[create_event, join_event, pl_event, join_rules_event],
229+
)
230+
231+
get_awaitable_result(
232+
event_auth.check_state_independent_auth_rules(event_store, good_event)
233+
)
234+
with self.assertRaises(AuthError):
235+
get_awaitable_result(
236+
event_auth.check_state_independent_auth_rules(event_store, bad_event)
237+
)
238+
153239
def test_random_users_cannot_send_state_before_first_pl(self):
154240
"""
155241
Check that, before the first PL lands, the creator is the only user

0 commit comments

Comments
 (0)