|
|
|
@ -20,7 +20,7 @@ import six |
|
|
|
|
from twisted.internet import defer |
|
|
|
|
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.crypto.event_signing import check_event_content_hash |
|
|
|
|
from synapse.events import event_type_from_format_version |
|
|
|
@ -66,7 +66,7 @@ class FederationBase(object): |
|
|
|
|
Returns: |
|
|
|
|
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 |
|
|
|
|
def handle_check_result(pdu, deferred): |
|
|
|
@ -121,16 +121,17 @@ class FederationBase(object): |
|
|
|
|
else: |
|
|
|
|
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( |
|
|
|
|
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 |
|
|
|
|
sending server. |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
room_version (str): The room version of the PDUs |
|
|
|
|
pdus (list[FrozenEvent]): the events to be checked |
|
|
|
|
|
|
|
|
|
Returns: |
|
|
|
@ -141,7 +142,7 @@ class FederationBase(object): |
|
|
|
|
* throws a SynapseError if the signature check failed. |
|
|
|
|
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() |
|
|
|
|
|
|
|
|
@ -203,16 +204,17 @@ class FederationBase(object): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PduToCheckSig(namedtuple("PduToCheckSig", [ |
|
|
|
|
"pdu", "redacted_pdu_json", "event_id_domain", "sender_domain", "deferreds", |
|
|
|
|
"pdu", "redacted_pdu_json", "sender_domain", "deferreds", |
|
|
|
|
])): |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
# to JSON. |
|
|
|
|
|
|
|
|
|
pdus_to_check = [ |
|
|
|
|
PduToCheckSig( |
|
|
|
|
pdu=p, |
|
|
|
|
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), |
|
|
|
|
deferreds=[], |
|
|
|
|
) |
|
|
|
|
for p in pdus |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
# first make sure that the event is signed by the event_id's domain |
|
|
|
|
deferreds = keyring.verify_json_objects_for_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. |
|
|
|
|
# First we check that the sender event is signed by the sender's domain |
|
|
|
|
# (except if its a 3pid invite, in which case it may be sent by any server) |
|
|
|
|
pdus_to_check_sender = [ |
|
|
|
|
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([ |
|
|
|
@ -279,19 +271,37 @@ def _check_sigs_on_pdus(keyring, pdus): |
|
|
|
|
for p, d in zip(pdus_to_check_sender, more_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. 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 |
|
|
|
|
return [_flatten_deferred_list(p.deferreds) for p in pdus_to_check] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _flatten_deferred_list(deferreds): |
|
|
|
|
"""Given a list of one or more deferreds, either return the single deferred, or |
|
|
|
|
combine into a DeferredList. |
|
|
|
|
"""Given a list of deferreds, either return the single deferred, |
|
|
|
|
combine into a DeferredList, or return an already resolved deferred. |
|
|
|
|
""" |
|
|
|
|
if len(deferreds) > 1: |
|
|
|
|
return DeferredList(deferreds, fireOnOneErrback=True, consumeErrors=True) |
|
|
|
|
else: |
|
|
|
|
assert len(deferreds) == 1 |
|
|
|
|
elif len(deferreds) == 1: |
|
|
|
|
return deferreds[0] |
|
|
|
|
else: |
|
|
|
|
return defer.succeed(None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, |
|
|
|
|
# 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'] |
|
|
|
|
if not isinstance(depth, six.integer_types): |
|
|
|
|