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

Commit a5d2ea3

Browse files
authored
Check *all* auth events for room id and rejection (#11009)
This fixes a bug where we would accept an event whose `auth_events` include rejected events, if the rejected event was shadowed by another `auth_event` with same `(type, state_key)`. The approach is to pass a list of auth events into `check_auth_rules_for_event` instead of a dict, which of course means updating the call sites. This is an extension of #10956.
1 parent 73743b8 commit a5d2ea3

File tree

8 files changed

+122
-85
lines changed

8 files changed

+122
-85
lines changed

changelog.d/11009.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug which meant that events received over federation were sometimes incorrectly accepted into the room state.

synapse/event_auth.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# limitations under the License.
1515

1616
import logging
17-
from typing import Any, Dict, List, Optional, Set, Tuple, Union
17+
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union
1818

1919
from canonicaljson import encode_canonical_json
2020
from signedjson.key import decode_verify_key_bytes
@@ -113,7 +113,7 @@ def validate_event_for_room_version(
113113

114114

115115
def check_auth_rules_for_event(
116-
room_version_obj: RoomVersion, event: EventBase, auth_events: StateMap[EventBase]
116+
room_version_obj: RoomVersion, event: EventBase, auth_events: Iterable[EventBase]
117117
) -> None:
118118
"""Check that an event complies with the auth rules
119119
@@ -137,8 +137,6 @@ def check_auth_rules_for_event(
137137
Raises:
138138
AuthError if the checks fail
139139
"""
140-
assert isinstance(auth_events, dict)
141-
142140
# We need to ensure that the auth events are actually for the same room, to
143141
# stop people from using powers they've been granted in other rooms for
144142
# example.
@@ -147,7 +145,7 @@ def check_auth_rules_for_event(
147145
# the state res algorithm isn't silly enough to give us events from different rooms.
148146
# Still, it's easier to do it anyway.
149147
room_id = event.room_id
150-
for auth_event in auth_events.values():
148+
for auth_event in auth_events:
151149
if auth_event.room_id != room_id:
152150
raise AuthError(
153151
403,
@@ -186,16 +184,18 @@ def check_auth_rules_for_event(
186184
logger.debug("Allowing! %s", event)
187185
return
188186

187+
auth_dict = {(e.type, e.state_key): e for e in auth_events}
188+
189189
# 3. If event does not have a m.room.create in its auth_events, reject.
190-
creation_event = auth_events.get((EventTypes.Create, ""), None)
190+
creation_event = auth_dict.get((EventTypes.Create, ""), None)
191191
if not creation_event:
192192
raise AuthError(403, "No create event in auth events")
193193

194194
# additional check for m.federate
195195
creating_domain = get_domain_from_id(event.room_id)
196196
originating_domain = get_domain_from_id(event.sender)
197197
if creating_domain != originating_domain:
198-
if not _can_federate(event, auth_events):
198+
if not _can_federate(event, auth_dict):
199199
raise AuthError(403, "This room has been marked as unfederatable.")
200200

201201
# 4. If type is m.room.aliases
@@ -217,44 +217,41 @@ def check_auth_rules_for_event(
217217
logger.debug("Allowing! %s", event)
218218
return
219219

220-
if logger.isEnabledFor(logging.DEBUG):
221-
logger.debug("Auth events: %s", [a.event_id for a in auth_events.values()])
222-
223220
# 5. If type is m.room.membership
224221
if event.type == EventTypes.Member:
225-
_is_membership_change_allowed(room_version_obj, event, auth_events)
222+
_is_membership_change_allowed(room_version_obj, event, auth_dict)
226223
logger.debug("Allowing! %s", event)
227224
return
228225

229-
_check_event_sender_in_room(event, auth_events)
226+
_check_event_sender_in_room(event, auth_dict)
230227

231228
# Special case to allow m.room.third_party_invite events wherever
232229
# a user is allowed to issue invites. Fixes
233230
# https://github.com/vector-im/vector-web/issues/1208 hopefully
234231
if event.type == EventTypes.ThirdPartyInvite:
235-
user_level = get_user_power_level(event.user_id, auth_events)
236-
invite_level = get_named_level(auth_events, "invite", 0)
232+
user_level = get_user_power_level(event.user_id, auth_dict)
233+
invite_level = get_named_level(auth_dict, "invite", 0)
237234

238235
if user_level < invite_level:
239236
raise AuthError(403, "You don't have permission to invite users")
240237
else:
241238
logger.debug("Allowing! %s", event)
242239
return
243240

244-
_can_send_event(event, auth_events)
241+
_can_send_event(event, auth_dict)
245242

246243
if event.type == EventTypes.PowerLevels:
247-
_check_power_levels(room_version_obj, event, auth_events)
244+
_check_power_levels(room_version_obj, event, auth_dict)
248245

249246
if event.type == EventTypes.Redaction:
250-
check_redaction(room_version_obj, event, auth_events)
247+
check_redaction(room_version_obj, event, auth_dict)
251248

252249
if (
253250
event.type == EventTypes.MSC2716_INSERTION
254251
or event.type == EventTypes.MSC2716_BATCH
255252
or event.type == EventTypes.MSC2716_MARKER
256253
):
257-
check_historical(room_version_obj, event, auth_events)
254+
check_historical(room_version_obj, event, auth_dict)
258255

259256
logger.debug("Allowing! %s", event)
260257

synapse/handlers/event_auth.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ async def check_auth_rules_from_context(
5555
"""Check an event passes the auth rules at its own auth events"""
5656
auth_event_ids = event.auth_event_ids()
5757
auth_events_by_id = await self._store.get_events(auth_event_ids)
58-
auth_events = {(e.type, e.state_key): e for e in auth_events_by_id.values()}
59-
check_auth_rules_for_event(room_version_obj, event, auth_events)
58+
check_auth_rules_for_event(room_version_obj, event, auth_events_by_id.values())
6059

6160
def compute_auth_events(
6261
self,

synapse/handlers/federation.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,13 +1167,11 @@ async def _persist_auth_tree(
11671167
logger.info("Failed to find auth event %r", e_id)
11681168

11691169
for e in itertools.chain(auth_events, state, [event]):
1170-
auth_for_e = {
1171-
(event_map[e_id].type, event_map[e_id].state_key): event_map[e_id]
1172-
for e_id in e.auth_event_ids()
1173-
if e_id in event_map
1174-
}
1170+
auth_for_e = [
1171+
event_map[e_id] for e_id in e.auth_event_ids() if e_id in event_map
1172+
]
11751173
if create_event:
1176-
auth_for_e[(EventTypes.Create, "")] = create_event
1174+
auth_for_e.append(create_event)
11771175

11781176
try:
11791177
validate_event_for_room_version(room_version, e)

synapse/handlers/federation_event.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,7 @@ async def _auth_and_persist_fetched_events_inner(
12031203

12041204
def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]:
12051205
with nested_logging_context(suffix=event.event_id):
1206-
auth = {}
1206+
auth = []
12071207
for auth_event_id in event.auth_event_ids():
12081208
ae = persisted_events.get(auth_event_id)
12091209
if not ae:
@@ -1216,7 +1216,7 @@ def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]:
12161216
# exist, which means it is premature to reject `event`. Instead we
12171217
# just ignore it for now.
12181218
return None
1219-
auth[(ae.type, ae.state_key)] = ae
1219+
auth.append(ae)
12201220

12211221
context = EventContext.for_outlier()
12221222
try:
@@ -1305,7 +1305,9 @@ async def _check_event_auth(
13051305
auth_events_for_auth = calculated_auth_event_map
13061306

13071307
try:
1308-
check_auth_rules_for_event(room_version_obj, event, auth_events_for_auth)
1308+
check_auth_rules_for_event(
1309+
room_version_obj, event, auth_events_for_auth.values()
1310+
)
13091311
except AuthError as e:
13101312
logger.warning("Failed auth resolution for %r because %s", event, e)
13111313
context.rejected = RejectedReason.AUTH_ERROR
@@ -1403,11 +1405,9 @@ async def _check_for_soft_fail(
14031405
current_state_ids_list = [
14041406
e for k, e in current_state_ids.items() if k in auth_types
14051407
]
1406-
1407-
auth_events_map = await self._store.get_events(current_state_ids_list)
1408-
current_auth_events = {
1409-
(e.type, e.state_key): e for e in auth_events_map.values()
1410-
}
1408+
current_auth_events = await self._store.get_events_as_list(
1409+
current_state_ids_list
1410+
)
14111411

14121412
try:
14131413
check_auth_rules_for_event(room_version_obj, event, current_auth_events)

synapse/state/v1.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def _resolve_auth_events(
332332
event_auth.check_auth_rules_for_event(
333333
RoomVersions.V1,
334334
event,
335-
auth_events,
335+
auth_events.values(),
336336
)
337337
prev_event = event
338338
except AuthError:
@@ -350,7 +350,7 @@ def _resolve_normal_events(
350350
event_auth.check_auth_rules_for_event(
351351
RoomVersions.V1,
352352
event,
353-
auth_events,
353+
auth_events.values(),
354354
)
355355
return event
356356
except AuthError:

synapse/state/v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ async def _iterative_auth_checks(
549549
event_auth.check_auth_rules_for_event(
550550
room_version,
551551
event,
552-
auth_events,
552+
auth_events.values(),
553553
)
554554

555555
resolved_state[(event.type, event.state_key)] = event_id

0 commit comments

Comments
 (0)