Only check event ID domain for signatures for V1 events

In future version events won't have an event ID, so we won't be able to
do this check.
This commit is contained in:
Erik Johnston 2019-01-29 17:21:48 +00:00
parent 8e3d34e3c5
commit 840068bd78
3 changed files with 44 additions and 31 deletions

View File

@ -20,7 +20,7 @@ import six
from twisted.internet import defer from twisted.internet import defer
from twisted.internet.defer import DeferredList from twisted.internet.defer import DeferredList
from synapse.api.constants import MAX_DEPTH, EventTypes, Membership from synapse.api.constants import KNOWN_ROOM_VERSIONS, MAX_DEPTH, EventTypes, Membership
from synapse.api.errors import Codes, SynapseError from synapse.api.errors import Codes, SynapseError
from synapse.crypto.event_signing import check_event_content_hash from synapse.crypto.event_signing import check_event_content_hash
from synapse.events import event_type_from_format_version from synapse.events import event_type_from_format_version
@ -66,7 +66,7 @@ class FederationBase(object):
Returns: Returns:
Deferred : A list of PDUs that have valid signatures and hashes. Deferred : A list of PDUs that have valid signatures and hashes.
""" """
deferreds = self._check_sigs_and_hashes(pdus) deferreds = self._check_sigs_and_hashes(room_version, pdus)
@defer.inlineCallbacks @defer.inlineCallbacks
def handle_check_result(pdu, deferred): def handle_check_result(pdu, deferred):
@ -121,16 +121,17 @@ class FederationBase(object):
else: else:
defer.returnValue([p for p in valid_pdus if p]) defer.returnValue([p for p in valid_pdus if p])
def _check_sigs_and_hash(self, pdu): def _check_sigs_and_hash(self, room_version, pdu):
return logcontext.make_deferred_yieldable( return logcontext.make_deferred_yieldable(
self._check_sigs_and_hashes([pdu])[0], self._check_sigs_and_hashes(room_version, [pdu])[0],
) )
def _check_sigs_and_hashes(self, pdus): def _check_sigs_and_hashes(self, room_version, pdus):
"""Checks that each of the received events is correctly signed by the """Checks that each of the received events is correctly signed by the
sending server. sending server.
Args: Args:
room_version (str): The room version of the PDUs
pdus (list[FrozenEvent]): the events to be checked pdus (list[FrozenEvent]): the events to be checked
Returns: Returns:
@ -141,7 +142,7 @@ class FederationBase(object):
* throws a SynapseError if the signature check failed. * throws a SynapseError if the signature check failed.
The deferreds run their callbacks in the sentinel logcontext. The deferreds run their callbacks in the sentinel logcontext.
""" """
deferreds = _check_sigs_on_pdus(self.keyring, pdus) deferreds = _check_sigs_on_pdus(self.keyring, room_version, pdus)
ctx = logcontext.LoggingContext.current_context() ctx = logcontext.LoggingContext.current_context()
@ -203,16 +204,17 @@ class FederationBase(object):
class PduToCheckSig(namedtuple("PduToCheckSig", [ class PduToCheckSig(namedtuple("PduToCheckSig", [
"pdu", "redacted_pdu_json", "event_id_domain", "sender_domain", "deferreds", "pdu", "redacted_pdu_json", "sender_domain", "deferreds",
])): ])):
pass pass
def _check_sigs_on_pdus(keyring, pdus): def _check_sigs_on_pdus(keyring, room_version, pdus):
"""Check that the given events are correctly signed """Check that the given events are correctly signed
Args: Args:
keyring (synapse.crypto.Keyring): keyring object to do the checks keyring (synapse.crypto.Keyring): keyring object to do the checks
room_version (str): the room version of the PDUs
pdus (Collection[EventBase]): the events to be checked pdus (Collection[EventBase]): the events to be checked
Returns: Returns:
@ -243,32 +245,22 @@ def _check_sigs_on_pdus(keyring, pdus):
# #
# let's start by getting the domain for each pdu, and flattening the event back # let's start by getting the domain for each pdu, and flattening the event back
# to JSON. # to JSON.
pdus_to_check = [ pdus_to_check = [
PduToCheckSig( PduToCheckSig(
pdu=p, pdu=p,
redacted_pdu_json=prune_event(p).get_pdu_json(), redacted_pdu_json=prune_event(p).get_pdu_json(),
event_id_domain=get_domain_from_id(p.event_id),
sender_domain=get_domain_from_id(p.sender), sender_domain=get_domain_from_id(p.sender),
deferreds=[], deferreds=[],
) )
for p in pdus for p in pdus
] ]
# first make sure that the event is signed by the event_id's domain # First we check that the sender event is signed by the sender's domain
deferreds = keyring.verify_json_objects_for_server([ # (except if its a 3pid invite, in which case it may be sent by any server)
(p.event_id_domain, p.redacted_pdu_json)
for p in pdus_to_check
])
for p, d in zip(pdus_to_check, deferreds):
p.deferreds.append(d)
# now let's look for events where the sender's domain is different to the
# event id's domain (normally only the case for joins/leaves), and add additional
# checks.
pdus_to_check_sender = [ pdus_to_check_sender = [
p for p in pdus_to_check p for p in pdus_to_check
if p.sender_domain != p.event_id_domain and not _is_invite_via_3pid(p.pdu) if not _is_invite_via_3pid(p.pdu)
] ]
more_deferreds = keyring.verify_json_objects_for_server([ more_deferreds = keyring.verify_json_objects_for_server([
@ -279,19 +271,37 @@ def _check_sigs_on_pdus(keyring, pdus):
for p, d in zip(pdus_to_check_sender, more_deferreds): for p, d in zip(pdus_to_check_sender, more_deferreds):
p.deferreds.append(d) p.deferreds.append(d)
# now let's look for events where the sender's domain is different to the
# event id's domain (normally only the case for joins/leaves), and add additional
# checks. Only do this if the room version has a concept of event ID domain
if room_version in KNOWN_ROOM_VERSIONS:
pdus_to_check_event_id = [
p for p in pdus_to_check
if p.sender_domain != get_domain_from_id(p.pdu.event_id)
]
more_deferreds = keyring.verify_json_objects_for_server([
(get_domain_from_id(p.pdu.event_id), p.redacted_pdu_json)
for p in pdus_to_check_event_id
])
for p, d in zip(pdus_to_check_event_id, more_deferreds):
p.deferreds.append(d)
# replace lists of deferreds with single Deferreds # replace lists of deferreds with single Deferreds
return [_flatten_deferred_list(p.deferreds) for p in pdus_to_check] return [_flatten_deferred_list(p.deferreds) for p in pdus_to_check]
def _flatten_deferred_list(deferreds): def _flatten_deferred_list(deferreds):
"""Given a list of one or more deferreds, either return the single deferred, or """Given a list of deferreds, either return the single deferred,
combine into a DeferredList. combine into a DeferredList, or return an already resolved deferred.
""" """
if len(deferreds) > 1: if len(deferreds) > 1:
return DeferredList(deferreds, fireOnOneErrback=True, consumeErrors=True) return DeferredList(deferreds, fireOnOneErrback=True, consumeErrors=True)
else: elif len(deferreds) == 1:
assert len(deferreds) == 1
return deferreds[0] return deferreds[0]
else:
return defer.succeed(None)
def _is_invite_via_3pid(event): def _is_invite_via_3pid(event):
@ -319,7 +329,7 @@ def event_from_pdu_json(pdu_json, event_format_version, outlier=False):
""" """
# we could probably enforce a bunch of other fields here (room_id, sender, # we could probably enforce a bunch of other fields here (room_id, sender,
# origin, etc etc) # origin, etc etc)
assert_params_in_dict(pdu_json, ('event_id', 'type', 'depth')) assert_params_in_dict(pdu_json, ('type', 'depth'))
depth = pdu_json['depth'] depth = pdu_json['depth']
if not isinstance(depth, six.integer_types): if not isinstance(depth, six.integer_types):

View File

@ -205,7 +205,7 @@ class FederationClient(FederationBase):
# FIXME: We should handle signature failures more gracefully. # FIXME: We should handle signature failures more gracefully.
pdus[:] = yield logcontext.make_deferred_yieldable(defer.gatherResults( pdus[:] = yield logcontext.make_deferred_yieldable(defer.gatherResults(
self._check_sigs_and_hashes(pdus), self._check_sigs_and_hashes(room_version, pdus),
consumeErrors=True, consumeErrors=True,
).addErrback(unwrapFirstError)) ).addErrback(unwrapFirstError))
@ -268,7 +268,7 @@ class FederationClient(FederationBase):
pdu = pdu_list[0] pdu = pdu_list[0]
# Check signatures are correct. # Check signatures are correct.
signed_pdu = yield self._check_sigs_and_hash(pdu) signed_pdu = yield self._check_sigs_and_hash(room_version, pdu)
break break
@ -757,7 +757,7 @@ class FederationClient(FederationBase):
pdu = event_from_pdu_json(pdu_dict, format_ver) pdu = event_from_pdu_json(pdu_dict, format_ver)
# Check signatures are correct. # Check signatures are correct.
pdu = yield self._check_sigs_and_hash(pdu) pdu = yield self._check_sigs_and_hash(room_version, pdu)
# FIXME: We should handle signature failures more gracefully. # FIXME: We should handle signature failures more gracefully.

View File

@ -645,9 +645,12 @@ class FederationServer(FederationBase):
pdu.event_id, origin pdu.event_id, origin
) )
# We've already checked that we know the room version by this point
room_version = yield self.store.get_room_version(pdu.room_id)
# Check signature. # Check signature.
try: try:
pdu = yield self._check_sigs_and_hash(pdu) pdu = yield self._check_sigs_and_hash(room_version, pdu)
except SynapseError as e: except SynapseError as e:
raise FederationError( raise FederationError(
"ERROR", "ERROR",