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

Commit f0aec0a

Browse files
authored
Improve logging when signature checks fail (#12925)
* Raise a dedicated `InvalidEventSignatureError` from `_check_sigs_on_pdu` * Downgrade logging about redactions to DEBUG this can be very spammy during a room join, and it's not very useful. * Raise `InvalidEventSignatureError` from `_check_sigs_and_hash` ... and, more importantly, move the logging out to the callers. * changelog
1 parent cf05258 commit f0aec0a

File tree

4 files changed

+95
-65
lines changed

4 files changed

+95
-65
lines changed

changelog.d/12925.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve the logging when signature checks on events fail.

synapse/federation/federation_base.py

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
logger = logging.getLogger(__name__)
3333

3434

35+
class InvalidEventSignatureError(RuntimeError):
36+
"""Raised when the signature on an event is invalid.
37+
38+
The stringification of this exception is just the error message without reference
39+
to the event id. The event id is available as a property.
40+
"""
41+
42+
def __init__(self, message: str, event_id: str):
43+
super().__init__(message)
44+
self.event_id = event_id
45+
46+
3547
class FederationBase:
3648
def __init__(self, hs: "HomeServer"):
3749
self.hs = hs
@@ -59,20 +71,13 @@ async def _check_sigs_and_hash(
5971
Returns:
6072
* the original event if the checks pass
6173
* a redacted version of the event (if the signature
62-
matched but the hash did not)
74+
matched but the hash did not). In this case a warning will be logged.
6375
6476
Raises:
65-
SynapseError if the signature check failed.
77+
InvalidEventSignatureError if the signature check failed. Nothing
78+
will be logged in this case.
6679
"""
67-
try:
68-
await _check_sigs_on_pdu(self.keyring, room_version, pdu)
69-
except SynapseError as e:
70-
logger.warning(
71-
"Signature check failed for %s: %s",
72-
pdu.event_id,
73-
e,
74-
)
75-
raise
80+
await _check_sigs_on_pdu(self.keyring, room_version, pdu)
7681

7782
if not check_event_content_hash(pdu):
7883
# let's try to distinguish between failures because the event was
@@ -87,7 +92,7 @@ async def _check_sigs_and_hash(
8792
if set(redacted_event.keys()) == set(pdu.keys()) and set(
8893
redacted_event.content.keys()
8994
) == set(pdu.content.keys()):
90-
logger.info(
95+
logger.debug(
9196
"Event %s seems to have been redacted; using our redacted copy",
9297
pdu.event_id,
9398
)
@@ -116,12 +121,13 @@ async def _check_sigs_on_pdu(
116121
) -> None:
117122
"""Check that the given events are correctly signed
118123
119-
Raise a SynapseError if the event wasn't correctly signed.
120-
121124
Args:
122125
keyring: keyring object to do the checks
123126
room_version: the room version of the PDUs
124127
pdus: the events to be checked
128+
129+
Raises:
130+
InvalidEventSignatureError if the event wasn't correctly signed.
125131
"""
126132

127133
# we want to check that the event is signed by:
@@ -147,44 +153,38 @@ async def _check_sigs_on_pdu(
147153

148154
# First we check that the sender event is signed by the sender's domain
149155
# (except if its a 3pid invite, in which case it may be sent by any server)
156+
sender_domain = get_domain_from_id(pdu.sender)
150157
if not _is_invite_via_3pid(pdu):
151158
try:
152159
await keyring.verify_event_for_server(
153-
get_domain_from_id(pdu.sender),
160+
sender_domain,
154161
pdu,
155162
pdu.origin_server_ts if room_version.enforce_key_validity else 0,
156163
)
157164
except Exception as e:
158-
errmsg = "event id %s: unable to verify signature for sender %s: %s" % (
165+
raise InvalidEventSignatureError(
166+
f"unable to verify signature for sender domain {sender_domain}: {e}",
159167
pdu.event_id,
160-
get_domain_from_id(pdu.sender),
161-
e,
162-
)
163-
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
168+
) from None
164169

165170
# now let's look for events where the sender's domain is different to the
166171
# event id's domain (normally only the case for joins/leaves), and add additional
167172
# checks. Only do this if the room version has a concept of event ID domain
168173
# (ie, the room version uses old-style non-hash event IDs).
169-
if room_version.event_format == EventFormatVersions.V1 and get_domain_from_id(
170-
pdu.event_id
171-
) != get_domain_from_id(pdu.sender):
172-
try:
173-
await keyring.verify_event_for_server(
174-
get_domain_from_id(pdu.event_id),
175-
pdu,
176-
pdu.origin_server_ts if room_version.enforce_key_validity else 0,
177-
)
178-
except Exception as e:
179-
errmsg = (
180-
"event id %s: unable to verify signature for event id domain %s: %s"
181-
% (
182-
pdu.event_id,
183-
get_domain_from_id(pdu.event_id),
184-
e,
174+
if room_version.event_format == EventFormatVersions.V1:
175+
event_domain = get_domain_from_id(pdu.event_id)
176+
if event_domain != sender_domain:
177+
try:
178+
await keyring.verify_event_for_server(
179+
event_domain,
180+
pdu,
181+
pdu.origin_server_ts if room_version.enforce_key_validity else 0,
185182
)
186-
)
187-
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
183+
except Exception as e:
184+
raise InvalidEventSignatureError(
185+
f"unable to verify signature for event domain {event_domain}: {e}",
186+
pdu.event_id,
187+
) from None
188188

189189
# If this is a join event for a restricted room it may have been authorised
190190
# via a different server from the sending server. Check those signatures.
@@ -204,15 +204,10 @@ async def _check_sigs_on_pdu(
204204
pdu.origin_server_ts if room_version.enforce_key_validity else 0,
205205
)
206206
except Exception as e:
207-
errmsg = (
208-
"event id %s: unable to verify signature for authorising server %s: %s"
209-
% (
210-
pdu.event_id,
211-
authorising_server,
212-
e,
213-
)
214-
)
215-
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
207+
raise InvalidEventSignatureError(
208+
f"unable to verify signature for authorising serve {authorising_server}: {e}",
209+
pdu.event_id,
210+
) from None
216211

217212

218213
def _is_invite_via_3pid(event: EventBase) -> bool:

synapse/federation/federation_client.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@
5454
RoomVersions,
5555
)
5656
from synapse.events import EventBase, builder
57-
from synapse.federation.federation_base import FederationBase, event_from_pdu_json
57+
from synapse.federation.federation_base import (
58+
FederationBase,
59+
InvalidEventSignatureError,
60+
event_from_pdu_json,
61+
)
5862
from synapse.federation.transport.client import SendJoinResponse
5963
from synapse.http.types import QueryParams
6064
from synapse.types import JsonDict, UserID, get_domain_from_id
@@ -319,7 +323,13 @@ async def get_pdu_from_destination_raw(
319323
pdu = pdu_list[0]
320324

321325
# Check signatures are correct.
322-
signed_pdu = await self._check_sigs_and_hash(room_version, pdu)
326+
try:
327+
signed_pdu = await self._check_sigs_and_hash(room_version, pdu)
328+
except InvalidEventSignatureError as e:
329+
errmsg = f"event id {pdu.event_id}: {e}"
330+
logger.warning("%s", errmsg)
331+
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
332+
323333
return signed_pdu
324334

325335
return None
@@ -555,20 +565,24 @@ async def _check_sigs_and_hash_and_fetch_one(
555565
556566
Returns:
557567
The PDU (possibly redacted) if it has valid signatures and hashes.
568+
None if no valid copy could be found.
558569
"""
559570

560-
res = None
561571
try:
562-
res = await self._check_sigs_and_hash(room_version, pdu)
563-
except SynapseError:
564-
pass
565-
566-
if not res:
567-
# Check local db.
568-
res = await self.store.get_event(
569-
pdu.event_id, allow_rejected=True, allow_none=True
572+
return await self._check_sigs_and_hash(room_version, pdu)
573+
except InvalidEventSignatureError as e:
574+
logger.warning(
575+
"Signature on retrieved event %s was invalid (%s). "
576+
"Checking local store/orgin server",
577+
pdu.event_id,
578+
e,
570579
)
571580

581+
# Check local db.
582+
res = await self.store.get_event(
583+
pdu.event_id, allow_rejected=True, allow_none=True
584+
)
585+
572586
pdu_origin = get_domain_from_id(pdu.sender)
573587
if not res and pdu_origin != origin:
574588
try:
@@ -1043,9 +1057,14 @@ async def send_invite(
10431057
pdu = event_from_pdu_json(pdu_dict, room_version)
10441058

10451059
# Check signatures are correct.
1046-
pdu = await self._check_sigs_and_hash(room_version, pdu)
1060+
try:
1061+
pdu = await self._check_sigs_and_hash(room_version, pdu)
1062+
except InvalidEventSignatureError as e:
1063+
errmsg = f"event id {pdu.event_id}: {e}"
1064+
logger.warning("%s", errmsg)
1065+
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
10471066

1048-
# FIXME: We should handle signature failures more gracefully.
1067+
# FIXME: We should handle signature failures more gracefully.
10491068

10501069
return pdu
10511070

synapse/federation/federation_server.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@
4848
from synapse.crypto.event_signing import compute_event_signature
4949
from synapse.events import EventBase
5050
from synapse.events.snapshot import EventContext
51-
from synapse.federation.federation_base import FederationBase, event_from_pdu_json
51+
from synapse.federation.federation_base import (
52+
FederationBase,
53+
InvalidEventSignatureError,
54+
event_from_pdu_json,
55+
)
5256
from synapse.federation.persistence import TransactionActions
5357
from synapse.federation.units import Edu, Transaction
5458
from synapse.http.servlet import assert_params_in_dict
@@ -631,7 +635,12 @@ async def on_invite_request(
631635
pdu = event_from_pdu_json(content, room_version)
632636
origin_host, _ = parse_server_name(origin)
633637
await self.check_server_matches_acl(origin_host, pdu.room_id)
634-
pdu = await self._check_sigs_and_hash(room_version, pdu)
638+
try:
639+
pdu = await self._check_sigs_and_hash(room_version, pdu)
640+
except InvalidEventSignatureError as e:
641+
errmsg = f"event id {pdu.event_id}: {e}"
642+
logger.warning("%s", errmsg)
643+
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
635644
ret_pdu = await self.handler.on_invite_request(origin, pdu, room_version)
636645
time_now = self._clock.time_msec()
637646
return {"event": ret_pdu.get_pdu_json(time_now)}
@@ -864,7 +873,12 @@ async def _on_send_membership_event(
864873
)
865874
)
866875

867-
event = await self._check_sigs_and_hash(room_version, event)
876+
try:
877+
event = await self._check_sigs_and_hash(room_version, event)
878+
except InvalidEventSignatureError as e:
879+
errmsg = f"event id {event.event_id}: {e}"
880+
logger.warning("%s", errmsg)
881+
raise SynapseError(403, errmsg, Codes.FORBIDDEN)
868882

869883
return await self._federation_event_handler.on_send_membership_event(
870884
origin, event
@@ -1016,8 +1030,9 @@ async def _handle_received_pdu(self, origin: str, pdu: EventBase) -> None:
10161030
# Check signature.
10171031
try:
10181032
pdu = await self._check_sigs_and_hash(room_version, pdu)
1019-
except SynapseError as e:
1020-
raise FederationError("ERROR", e.code, e.msg, affected=pdu.event_id)
1033+
except InvalidEventSignatureError as e:
1034+
logger.warning("event id %s: %s", pdu.event_id, e)
1035+
raise FederationError("ERROR", 403, str(e), affected=pdu.event_id)
10211036

10221037
if await self._spam_checker.should_drop_federated_event(pdu):
10231038
logger.warning(

0 commit comments

Comments
 (0)