diff --git a/synapse/crypto/context_factory.py b/synapse/crypto/context_factory.py
index 02b76dfcfb..49cbc7098f 100644
--- a/synapse/crypto/context_factory.py
+++ b/synapse/crypto/context_factory.py
@@ -1,4 +1,5 @@
# Copyright 2014-2016 OpenMarket Ltd
+# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -11,12 +12,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+
import logging
from zope.interface import implementer
from OpenSSL import SSL, crypto
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
from twisted.python.failure import Failure
@@ -42,12 +45,12 @@ class ServerContextFactory(ContextFactory):
logger.exception("Failed to enable elliptic curve for TLS")
context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
context.use_certificate_chain_file(config.tls_certificate_file)
+ context.use_privatekey(config.tls_private_key)
- if not config.no_tls:
- context.use_privatekey(config.tls_private_key)
-
- context.load_tmp_dh(config.tls_dh_params_path)
- context.set_cipher_list("!ADH:HIGH+kEDH:!AECDH:HIGH+kEECDH")
+ # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
+ context.set_cipher_list(
+ "ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1"
+ )
def getContext(self):
return self._context
@@ -96,11 +99,15 @@ class ClientTLSOptions(object):
def __init__(self, hostname, ctx):
self._ctx = ctx
- self._hostname = hostname
- self._hostnameBytes = _idnaBytes(hostname)
- ctx.set_info_callback(
- _tolerateErrors(self._identityVerifyingInfoCallback)
- )
+
+ 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
@@ -109,7 +116,9 @@ class ClientTLSOptions(object):
return connection
def _identityVerifyingInfoCallback(self, connection, where, ret):
- if where & SSL.SSL_CB_HANDSHAKE_START:
+ # 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)
@@ -119,10 +128,8 @@ class ClientTLSOptionsFactory(object):
def __init__(self, config):
# We don't use config options yet
- pass
+ self._options = CertificateOptions(verify=False)
def get_options(self, host):
- return ClientTLSOptions(
- host,
- CertificateOptions(verify=False).getContext()
- )
+ # Use _makeContext so that we get a fresh OpenSSL CTX each time.
+ return ClientTLSOptions(host, self._options._makeContext())
|