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

Commit

Permalink
Merge branch 'release-v1.0.0' of github.com:matrix-org/synapse into d…
Browse files Browse the repository at this point in the history
…evelop
  • Loading branch information
erikjohnston committed Jun 10, 2019
2 parents 0382b0f + 0167447 commit abce00f
Show file tree
Hide file tree
Showing 14 changed files with 489 additions and 195 deletions.
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
Synapse 1.0.0rc2 (2019-06-10)
=============================

Bugfixes
--------

- Remove redundant warning about key server response validation. ([\#5392](https://github.com/matrix-org/synapse/issues/5392))
- Fix bug where old keys stored in the database with a null valid until timestamp caused all verification requests for that key to fail. ([\#5415](https://github.com/matrix-org/synapse/issues/5415))
- Fix excessive memory using with default `federation_verify_certificates: true` configuration. ([\#5417](https://github.com/matrix-org/synapse/issues/5417))


Synapse 1.0.0rc1 (2019-06-07)
=============================

Expand Down
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ recursive-include docs *
recursive-include scripts *
recursive-include scripts-dev *
recursive-include synapse *.pyi
recursive-include tests *.pem
recursive-include tests *.py
include tests/http/ca.crt
include tests/http/ca.key
include tests/http/server.key

recursive-include synapse/res *
recursive-include synapse/static *.css
Expand Down
1 change: 0 additions & 1 deletion changelog.d/5415.bugfix

This file was deleted.

2 changes: 1 addition & 1 deletion synapse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
except ImportError:
pass

__version__ = "1.0.0rc1"
__version__ = "1.0.0rc2"
27 changes: 23 additions & 4 deletions synapse/config/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@
you are *sure* you want to do this, set 'accept_keys_insecurely' on the
keyserver configuration."""

RELYING_ON_MATRIX_KEY_ERROR = """\
Your server is configured to accept key server responses without TLS certificate
validation, and which are only signed by the old (possibly compromised)
matrix.org signing key 'ed25519:auto'. This likely isn't what you want to do,
and you should enable 'federation_verify_certificates' in your configuration.
If you are *sure* you want to do this, set 'accept_keys_insecurely' on the
trusted_key_server configuration."""


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -340,10 +349,20 @@ def _parse_key_servers(key_servers, federation_verify_certificates):
result.verify_keys[key_id] = verify_key

if (
not verify_keys
and not server.get("accept_keys_insecurely")
and not federation_verify_certificates
not federation_verify_certificates and
not server.get("accept_keys_insecurely")
):
raise ConfigError(INSECURE_NOTARY_ERROR)
_assert_keyserver_has_verify_keys(result)

yield result


def _assert_keyserver_has_verify_keys(trusted_key_server):
if not trusted_key_server.verify_keys:
raise ConfigError(INSECURE_NOTARY_ERROR)

# also check that they are not blindly checking the old matrix.org key
if trusted_key_server.server_name == "matrix.org" and any(
key_id == "ed25519:auto" for key_id in trusted_key_server.verify_keys
):
raise ConfigError(RELYING_ON_MATRIX_KEY_ERROR)
180 changes: 106 additions & 74 deletions synapse/crypto/context_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@

import logging

import idna
from service_identity import VerificationError
from service_identity.pyopenssl import verify_hostname, verify_ip_address
from zope.interface import implementer

from OpenSSL import SSL, crypto
from twisted.internet._sslverify import ClientTLSOptions, _defaultCurveName
from twisted.internet._sslverify import _defaultCurveName
from twisted.internet.abstract import isIPAddress, isIPv6Address
from twisted.internet.interfaces import IOpenSSLClientConnectionCreator
from twisted.internet.ssl import CertificateOptions, ContextFactory, platformTrust
Expand Down Expand Up @@ -56,91 +59,33 @@ def getContext(self):
return self._context


def _idnaBytes(text):
"""
Convert some text typed by a human into some ASCII bytes. This is a
copy of twisted.internet._idna._idnaBytes. For documentation, see the
twisted documentation.
"""
try:
import idna
except ImportError:
return text.encode("idna")
else:
return idna.encode(text)


def _tolerateErrors(wrapped):
"""
Wrap up an info_callback for pyOpenSSL so that if something goes wrong
the error is immediately logged and the connection is dropped if possible.
This is a copy of twisted.internet._sslverify._tolerateErrors. For
documentation, see the twisted documentation.
"""

def infoCallback(connection, where, ret):
try:
return wrapped(connection, where, ret)
except: # noqa: E722, taken from the twisted implementation
f = Failure()
logger.exception("Error during info_callback")
connection.get_app_data().failVerification(f)

return infoCallback
class ClientTLSOptionsFactory(object):
"""Factory for Twisted SSLClientConnectionCreators that are used to make connections
to remote servers for federation.
Uses one of two OpenSSL context objects for all connections, depending on whether
we should do SSL certificate verification.
@implementer(IOpenSSLClientConnectionCreator)
class ClientTLSOptionsNoVerify(object):
"""
Client creator for TLS without certificate identity verification. This is a
copy of twisted.internet._sslverify.ClientTLSOptions with the identity
verification left out. For documentation, see the twisted documentation.
get_options decides whether we should do SSL certificate verification and
constructs an SSLClientConnectionCreator factory accordingly.
"""

def __init__(self, hostname, ctx):
self._ctx = ctx

if isIPAddress(hostname) or isIPv6Address(hostname):
self._hostnameBytes = hostname.encode('ascii')
self._sendSNI = False
else:
self._hostnameBytes = _idnaBytes(hostname)
self._sendSNI = True

ctx.set_info_callback(_tolerateErrors(self._identityVerifyingInfoCallback))

def clientConnectionForTLS(self, tlsProtocol):
context = self._ctx
connection = SSL.Connection(context, None)
connection.set_app_data(tlsProtocol)
return connection

def _identityVerifyingInfoCallback(self, connection, where, ret):
# Literal IPv4 and IPv6 addresses are not permitted
# as host names according to the RFCs
if where & SSL.SSL_CB_HANDSHAKE_START and self._sendSNI:
connection.set_tlsext_host_name(self._hostnameBytes)


class ClientTLSOptionsFactory(object):
"""Factory for Twisted ClientTLSOptions that are used to make connections
to remote servers for federation."""

def __init__(self, config):
self._config = config
self._options_noverify = CertificateOptions()

# Check if we're using a custom list of a CA certificates
trust_root = config.federation_ca_trust_root
if trust_root is None:
# Use CA root certs provided by OpenSSL
trust_root = platformTrust()

self._options_verify = CertificateOptions(trustRoot=trust_root)
self._verify_ssl_context = CertificateOptions(trustRoot=trust_root).getContext()
self._verify_ssl_context.set_info_callback(self._context_info_cb)

def get_options(self, host):
# Use _makeContext so that we get a fresh OpenSSL CTX each time.
self._no_verify_ssl_context = CertificateOptions().getContext()
self._no_verify_ssl_context.set_info_callback(self._context_info_cb)

def get_options(self, host):
# Check if certificate verification has been enabled
should_verify = self._config.federation_verify_certificates

Expand All @@ -151,6 +96,93 @@ def get_options(self, host):
should_verify = False
break

if should_verify:
return ClientTLSOptions(host, self._options_verify._makeContext())
return ClientTLSOptionsNoVerify(host, self._options_noverify._makeContext())
ssl_context = (
self._verify_ssl_context if should_verify else self._no_verify_ssl_context
)

return SSLClientConnectionCreator(host, ssl_context, should_verify)

@staticmethod
def _context_info_cb(ssl_connection, where, ret):
"""The 'information callback' for our openssl context object."""
# we assume that the app_data on the connection object has been set to
# a TLSMemoryBIOProtocol object. (This is done by SSLClientConnectionCreator)
tls_protocol = ssl_connection.get_app_data()
try:
# ... we further assume that SSLClientConnectionCreator has set the
# '_synapse_tls_verifier' attribute to a ConnectionVerifier object.
tls_protocol._synapse_tls_verifier.verify_context_info_cb(
ssl_connection, where
)
except: # noqa: E722, taken from the twisted implementation
logger.exception("Error during info_callback")
f = Failure()
tls_protocol.failVerification(f)


@implementer(IOpenSSLClientConnectionCreator)
class SSLClientConnectionCreator(object):
"""Creates openssl connection objects for client connections.
Replaces twisted.internet.ssl.ClientTLSOptions
"""

def __init__(self, hostname, ctx, verify_certs):
self._ctx = ctx
self._verifier = ConnectionVerifier(hostname, verify_certs)

def clientConnectionForTLS(self, tls_protocol):
context = self._ctx
connection = SSL.Connection(context, None)

# as per twisted.internet.ssl.ClientTLSOptions, we set the application
# data to our TLSMemoryBIOProtocol...
connection.set_app_data(tls_protocol)

# ... and we also gut-wrench a '_synapse_tls_verifier' attribute into the
# tls_protocol so that the SSL context's info callback has something to
# call to do the cert verification.
setattr(tls_protocol, "_synapse_tls_verifier", self._verifier)
return connection


class ConnectionVerifier(object):
"""Set the SNI, and do cert verification
This is a thing which is attached to the TLSMemoryBIOProtocol, and is called by
the ssl context's info callback.
"""

# This code is based on twisted.internet.ssl.ClientTLSOptions.

def __init__(self, hostname, verify_certs):
self._verify_certs = verify_certs

if isIPAddress(hostname) or isIPv6Address(hostname):
self._hostnameBytes = hostname.encode("ascii")
self._is_ip_address = True
else:
# twisted's ClientTLSOptions falls back to the stdlib impl here if
# idna is not installed, but points out that lacks support for
# IDNA2008 (http://bugs.python.org/issue17305).
#
# We can rely on having idna.
self._hostnameBytes = idna.encode(hostname)
self._is_ip_address = False

self._hostnameASCII = self._hostnameBytes.decode("ascii")

def verify_context_info_cb(self, ssl_connection, where):
if where & SSL.SSL_CB_HANDSHAKE_START and not self._is_ip_address:
ssl_connection.set_tlsext_host_name(self._hostnameBytes)

if where & SSL.SSL_CB_HANDSHAKE_DONE and self._verify_certs:
try:
if self._is_ip_address:
verify_ip_address(ssl_connection, self._hostnameASCII)
else:
verify_hostname(ssl_connection, self._hostnameASCII)
except VerificationError:
f = Failure()
tls_protocol = ssl_connection.get_app_data()
tls_protocol.failVerification(f)
7 changes: 0 additions & 7 deletions synapse/crypto/keyring.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,13 +750,6 @@ def _validate_perspectives_response(
verify_signed_json(response, perspective_name, perspective_keys[key_id])
verified = True

if perspective_name == "matrix.org" and key_id == "ed25519:auto":
logger.warning(
"Trusting trusted_key_server responses signed by the "
"compromised matrix.org signing key 'ed25519:auto'. "
"This is a placebo."
)

if not verified:
raise KeyLookupError(
"Response not signed with a known key: signed with: %r, known keys: %r"
Expand Down
5 changes: 4 additions & 1 deletion synapse/python_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@
"canonicaljson>=1.1.3",
"signedjson>=1.0.0",
"pynacl>=1.2.1",
"service_identity>=16.0.0",
"idna>=2",

# validating SSL certs for IP addresses requires service_identity 18.1.
"service_identity>=18.1.0",

# our logcontext handling relies on the ability to cancel inlineCallbacks
# (https://twistedmatrix.com/trac/ticket/4632) which landed in Twisted 18.7.
Expand Down
Loading

0 comments on commit abce00f

Please sign in to comment.