Validate signatures on incoming events

This commit is contained in:
Mark Haines 2014-11-14 16:45:39 +00:00
parent 44a24605ad
commit de1ec90133
3 changed files with 52 additions and 7 deletions

View file

@ -19,6 +19,7 @@ from synapse.api.events.utils import prune_event
from syutil.jsonutil import encode_canonical_json from syutil.jsonutil import encode_canonical_json
from syutil.base64util import encode_base64, decode_base64 from syutil.base64util import encode_base64, decode_base64
from syutil.crypto.jsonsign import sign_json from syutil.crypto.jsonsign import sign_json
from synapse.api.errors import SynapseError, Codes
import hashlib import hashlib
import logging import logging
@ -29,15 +30,24 @@ logger = logging.getLogger(__name__)
def check_event_content_hash(event, hash_algorithm=hashlib.sha256): def check_event_content_hash(event, hash_algorithm=hashlib.sha256):
"""Check whether the hash for this PDU matches the contents""" """Check whether the hash for this PDU matches the contents"""
computed_hash = _compute_content_hash(event, hash_algorithm) computed_hash = _compute_content_hash(event, hash_algorithm)
logging.debug("Expecting hash: %s", encode_base64(computed_hash.digest()))
if computed_hash.name not in event.hashes: if computed_hash.name not in event.hashes:
raise Exception("Algorithm %s not in hashes %s" % ( raise SynapseError(
computed_hash.name, list(event.hashes) 400,
)) "Algorithm %s not in hashes %s" % (
computed_hash.name, list(event.hashes),
),
Codes.UNAUTHORIZED,
)
message_hash_base64 = event.hashes[computed_hash.name] message_hash_base64 = event.hashes[computed_hash.name]
try: try:
message_hash_bytes = decode_base64(message_hash_base64) message_hash_bytes = decode_base64(message_hash_base64)
except: except:
raise Exception("Invalid base64: %s" % (message_hash_base64,)) raise SynapseError(
400,
"Invalid base64: %s" % (message_hash_base64,),
Codes.UNAUTHORIZED,
)
return message_hash_bytes == computed_hash.digest() return message_hash_bytes == computed_hash.digest()

View file

@ -17,13 +17,17 @@
from ._base import BaseHandler from ._base import BaseHandler
from synapse.api.errors import AuthError, FederationError from synapse.api.events.utils import prune_event
from synapse.api.errors import AuthError, FederationError, SynapseError
from synapse.api.events.room import RoomMemberEvent from synapse.api.events.room import RoomMemberEvent
from synapse.api.constants import Membership from synapse.api.constants import Membership
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
from synapse.federation.pdu_codec import PduCodec from synapse.federation.pdu_codec import PduCodec
from synapse.util.async import run_on_reactor from synapse.util.async import run_on_reactor
from synapse.crypto.event_signing import compute_event_signature from synapse.crypto.event_signing import (
compute_event_signature, check_event_content_hash
)
from syutil.jsonutil import encode_canonical_json
from twisted.internet import defer from twisted.internet import defer
@ -59,6 +63,7 @@ class FederationHandler(BaseHandler):
self.state_handler = hs.get_state_handler() self.state_handler = hs.get_state_handler()
# self.auth_handler = gs.get_auth_handler() # self.auth_handler = gs.get_auth_handler()
self.server_name = hs.hostname self.server_name = hs.hostname
self.keyring = hs.get_keyring()
self.lock_manager = hs.get_room_lock_manager() self.lock_manager = hs.get_room_lock_manager()
@ -112,6 +117,34 @@ class FederationHandler(BaseHandler):
logger.debug("Processing event: %s", event.event_id) logger.debug("Processing event: %s", event.event_id)
redacted_event = prune_event(event)
redacted_event.origin = pdu.origin
redacted_event.origin_server_ts = pdu.origin_server_ts
redacted_pdu = self.pdu_codec.pdu_from_event(redacted_event)
redacted_pdu_json = redacted_pdu.get_dict()
try:
yield self.keyring.verify_json_for_server(
event.origin, redacted_pdu_json
)
except SynapseError as e:
logger.warn("Signature check failed for %s redacted to %s",
encode_canonical_json(pdu.get_dict()),
encode_canonical_json(redacted_pdu_json),
)
raise FederationError(
"ERROR",
e.code,
e.msg,
affected=event.event_id,
)
if not check_event_content_hash(pdu):
logger.warn(
"Event content has been tampered, redacting %s", event.event_id
)
event = redacted_event
if state: if state:
state = [self.pdu_codec.event_from_pdu(p) for p in state] state = [self.pdu_codec.event_from_pdu(p) for p in state]

View file

@ -23,7 +23,7 @@ from synapse.handlers.federation import FederationHandler
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.federation.units import Pdu from synapse.federation.units import Pdu
from mock import NonCallableMock, ANY from mock import NonCallableMock, ANY, Mock
from ..utils import MockKey from ..utils import MockKey
@ -62,6 +62,7 @@ class FederationTestCase(unittest.TestCase):
config=self.mock_config, config=self.mock_config,
auth=self.auth, auth=self.auth,
state_handler=self.state_handler, state_handler=self.state_handler,
keyring=Mock(),
) )
self.datastore = hs.get_datastore() self.datastore = hs.get_datastore()
@ -80,6 +81,7 @@ class FederationTestCase(unittest.TestCase):
origin_server_ts=0, origin_server_ts=0,
event_id="$a:b", event_id="$a:b",
origin="b", origin="b",
hashes={"sha256":"PvbCLWrTBxnBsSO7/cJ76072ySTCgI/XGadESRAe02M"},
) )
self.datastore.persist_event.return_value = defer.succeed(None) self.datastore.persist_event.return_value = defer.succeed(None)