Merge branch 'develop' of github.com:matrix-org/synapse into erikj/redactions_eiah
This commit is contained in:
commit
67b82f1336
|
@ -0,0 +1 @@
|
||||||
|
Add infrastructure to support different event formats
|
|
@ -22,6 +22,7 @@ from unpaddedbase64 import decode_base64
|
||||||
|
|
||||||
from synapse.api.constants import (
|
from synapse.api.constants import (
|
||||||
KNOWN_ROOM_VERSIONS,
|
KNOWN_ROOM_VERSIONS,
|
||||||
|
EventFormatVersions,
|
||||||
EventTypes,
|
EventTypes,
|
||||||
JoinRules,
|
JoinRules,
|
||||||
Membership,
|
Membership,
|
||||||
|
@ -55,7 +56,6 @@ def check(room_version, event, auth_events, do_sig_check=True, do_size_check=Tru
|
||||||
|
|
||||||
if do_sig_check:
|
if do_sig_check:
|
||||||
sender_domain = get_domain_from_id(event.sender)
|
sender_domain = get_domain_from_id(event.sender)
|
||||||
event_id_domain = get_domain_from_id(event.event_id)
|
|
||||||
|
|
||||||
is_invite_via_3pid = (
|
is_invite_via_3pid = (
|
||||||
event.type == EventTypes.Member
|
event.type == EventTypes.Member
|
||||||
|
@ -72,9 +72,13 @@ def check(room_version, event, auth_events, do_sig_check=True, do_size_check=Tru
|
||||||
if not is_invite_via_3pid:
|
if not is_invite_via_3pid:
|
||||||
raise AuthError(403, "Event not signed by sender's server")
|
raise AuthError(403, "Event not signed by sender's server")
|
||||||
|
|
||||||
# Check the event_id's domain has signed the event
|
if event.format_version in (EventFormatVersions.V1,):
|
||||||
if not event.signatures.get(event_id_domain):
|
# Only older room versions have event IDs to check.
|
||||||
raise AuthError(403, "Event not signed by sending server")
|
event_id_domain = get_domain_from_id(event.event_id)
|
||||||
|
|
||||||
|
# Check the origin domain has signed the event
|
||||||
|
if not event.signatures.get(event_id_domain):
|
||||||
|
raise AuthError(403, "Event not signed by sending server")
|
||||||
|
|
||||||
if auth_events is None:
|
if auth_events is None:
|
||||||
# Oh, we don't know what the state of the room was, so we
|
# Oh, we don't know what the state of the room was, so we
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, Membership
|
from synapse.api.constants import EventFormatVersions, EventTypes, Membership
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.types import EventID, RoomID, UserID
|
from synapse.types import EventID, RoomID, UserID
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@ class EventValidator(object):
|
||||||
"""
|
"""
|
||||||
self.validate_builder(event)
|
self.validate_builder(event)
|
||||||
|
|
||||||
EventID.from_string(event.event_id)
|
if event.format_version == EventFormatVersions.V1:
|
||||||
|
EventID.from_string(event.event_id)
|
||||||
|
|
||||||
required = [
|
required = [
|
||||||
"auth_events",
|
"auth_events",
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -225,9 +227,7 @@ def _check_sigs_on_pdus(keyring, pdus):
|
||||||
|
|
||||||
# we want to check that the event is signed by:
|
# we want to check that the event is signed by:
|
||||||
#
|
#
|
||||||
# (a) the server which created the event_id
|
# (a) the sender's server
|
||||||
#
|
|
||||||
# (b) the sender's server.
|
|
||||||
#
|
#
|
||||||
# - except in the case of invites created from a 3pid invite, which are exempt
|
# - except in the case of invites created from a 3pid invite, which are exempt
|
||||||
# from this check, because the sender has to match that of the original 3pid
|
# from this check, because the sender has to match that of the original 3pid
|
||||||
|
@ -241,34 +241,26 @@ def _check_sigs_on_pdus(keyring, pdus):
|
||||||
# and signatures are *supposed* to be valid whether or not an event has been
|
# and signatures are *supposed* to be valid whether or not an event has been
|
||||||
# redacted. But this isn't the worst of the ways that 3pid invites are broken.
|
# redacted. But this isn't the worst of the ways that 3pid invites are broken.
|
||||||
#
|
#
|
||||||
|
# (b) for V1 and V2 rooms, the server which created the event_id
|
||||||
|
#
|
||||||
# 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):
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ from twisted.internet import defer
|
||||||
from twisted.internet.abstract import isIPAddress
|
from twisted.internet.abstract import isIPAddress
|
||||||
from twisted.python import failure
|
from twisted.python import failure
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes, Membership
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
FederationError,
|
FederationError,
|
||||||
|
@ -620,16 +620,19 @@ class FederationServer(FederationBase):
|
||||||
"""
|
"""
|
||||||
# check that it's actually being sent from a valid destination to
|
# check that it's actually being sent from a valid destination to
|
||||||
# workaround bug #1753 in 0.18.5 and 0.18.6
|
# workaround bug #1753 in 0.18.5 and 0.18.6
|
||||||
if origin != get_domain_from_id(pdu.event_id):
|
if origin != get_domain_from_id(pdu.sender):
|
||||||
# We continue to accept join events from any server; this is
|
# We continue to accept join events from any server; this is
|
||||||
# necessary for the federation join dance to work correctly.
|
# necessary for the federation join dance to work correctly.
|
||||||
# (When we join over federation, the "helper" server is
|
# (When we join over federation, the "helper" server is
|
||||||
# responsible for sending out the join event, rather than the
|
# responsible for sending out the join event, rather than the
|
||||||
# origin. See bug #1893).
|
# origin. See bug #1893. This is also true for some third party
|
||||||
|
# invites).
|
||||||
if not (
|
if not (
|
||||||
pdu.type == 'm.room.member' and
|
pdu.type == 'm.room.member' and
|
||||||
pdu.content and
|
pdu.content and
|
||||||
pdu.content.get("membership", None) == 'join'
|
pdu.content.get("membership", None) in (
|
||||||
|
Membership.JOIN, Membership.INVITE,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
logger.info(
|
logger.info(
|
||||||
"Discarding PDU %s from invalid origin %s",
|
"Discarding PDU %s from invalid origin %s",
|
||||||
|
@ -642,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",
|
||||||
|
|
|
@ -175,7 +175,7 @@ class TransactionQueue(object):
|
||||||
def handle_event(event):
|
def handle_event(event):
|
||||||
# Only send events for this server.
|
# Only send events for this server.
|
||||||
send_on_behalf_of = event.internal_metadata.get_send_on_behalf_of()
|
send_on_behalf_of = event.internal_metadata.get_send_on_behalf_of()
|
||||||
is_mine = self.is_mine_id(event.event_id)
|
is_mine = self.is_mine_id(event.sender)
|
||||||
if not is_mine and send_on_behalf_of is None:
|
if not is_mine and send_on_behalf_of is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -2293,6 +2293,10 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
EventValidator().validate_new(event)
|
EventValidator().validate_new(event)
|
||||||
|
|
||||||
|
# We need to tell the transaction queue to send this out, even
|
||||||
|
# though the sender isn't a local user.
|
||||||
|
event.internal_metadata.send_on_behalf_of = self.hs.hostname
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield self.auth.check_from_context(room_version, event, context)
|
yield self.auth.check_from_context(room_version, event, context)
|
||||||
except AuthError as e:
|
except AuthError as e:
|
||||||
|
@ -2342,6 +2346,10 @@ class FederationHandler(BaseHandler):
|
||||||
raise e
|
raise e
|
||||||
yield self._check_signature(event, context)
|
yield self._check_signature(event, context)
|
||||||
|
|
||||||
|
# We need to tell the transaction queue to send this out, even
|
||||||
|
# though the sender isn't a local user.
|
||||||
|
event.internal_metadata.send_on_behalf_of = get_domain_from_id(event.sender)
|
||||||
|
|
||||||
# XXX we send the invite here, but send_membership_event also sends it,
|
# XXX we send the invite here, but send_membership_event also sends it,
|
||||||
# so we end up making two requests. I think this is redundant.
|
# so we end up making two requests. I think this is redundant.
|
||||||
returned_invite = yield self.send_invite(origin, event)
|
returned_invite = yield self.send_invite(origin, event)
|
||||||
|
|
|
@ -940,7 +940,8 @@ class RoomMemberHandler(object):
|
||||||
# first member event?
|
# first member event?
|
||||||
create_event_id = current_state_ids.get(("m.room.create", ""))
|
create_event_id = current_state_ids.get(("m.room.create", ""))
|
||||||
if len(current_state_ids) == 1 and create_event_id:
|
if len(current_state_ids) == 1 and create_event_id:
|
||||||
defer.returnValue(self.hs.is_mine_id(create_event_id))
|
# We can only get here if we're in the process of creating the room
|
||||||
|
defer.returnValue(True)
|
||||||
|
|
||||||
for etype, state_key in current_state_ids:
|
for etype, state_key in current_state_ids:
|
||||||
if etype != EventTypes.Member or not self.hs.is_mine_id(state_key):
|
if etype != EventTypes.Member or not self.hs.is_mine_id(state_key):
|
||||||
|
|
Loading…
Reference in New Issue