from __future__ import absolute_import, division, print_function, with_statement
+import certifi
import collections
import errno
import numbers
if hasattr(errno, "WSAEINPROGRESS"):
_ERRNO_INPROGRESS += (errno.WSAEINPROGRESS,)
-#######################################################
+if hasattr(ssl, 'SSLContext'):
+ if hasattr(ssl, 'create_default_context'):
+ # Python 2.7.9+, 3.4+
+ # Note that the naming of ssl.Purpose is confusing; the purpose
+ # of a context is to authentiate the opposite side of the connection.
+ _client_ssl_defaults = ssl.create_default_context(
+ ssl.Purpose.SERVER_AUTH)
+ _server_ssl_defaults = ssl.create_default_context(
+ ssl.Purpose.CLIENT_AUTH)
+ else:
+ # Python 3.2-3.3
+ _client_ssl_defaults = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ _client_ssl_defaults.verify_mode = ssl.CERT_REQUIRED
+ _client_ssl_defaults.load_verify_locations(certifi.where())
+ _server_ssl_defaults = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+else:
+ # Python 2.6-2.7.8
+ _client_ssl_defaults = dict(cert_reqs=ssl.CERT_REQUIRED,
+ ca_certs=certifi.where())
+ _ssl_server_defaults = {}
class StreamClosedError(IOError):
returns a `.Future` (whose result after a successful
connection will be the stream itself).
- If specified, the ``server_hostname`` parameter will be used
- in SSL connections for certificate validation (if requested in
- the ``ssl_options``) and SNI (if supported; requires
- Python 2.7.9+).
+ In SSL mode, the ``server_hostname`` parameter will be used
+ for certificate validation (unless disabled in the
+ ``ssl_options``) and SNI (if supported; requires Python
+ 2.7.9+).
Note that it is safe to call `IOStream.write
<BaseIOStream.write>` while the connection is pending, in
.. versionchanged:: 4.0
If no callback is given, returns a `.Future`.
+ .. versionchanged:: 4.2
+ SSL certificates are validated by default; pass
+ ``ssl_options=dict(cert_reqs=ssl.CERT_NONE)`` or a
+ suitably-configured `ssl.SSLContext` to the
+ `SSLIOStream` constructor to disable.
"""
self._connecting = True
if callback is not None:
The ``ssl_options`` argument may be either an `ssl.SSLContext`
object or a dictionary of keyword arguments for the
- `ssl.wrap_socket` function. If a ``server_hostname`` is
- given, it will be used for certificate verification (as
- configured in the ``ssl_options``).
+ `ssl.wrap_socket` function. The ``server_hostname`` argument
+ will be used for certificate validation unless disabled
+ in the ``ssl_options``.
This method returns a `.Future` whose result is the new
`SSLIOStream`. After this method has been called,
transferred to the new stream.
.. versionadded:: 4.0
+
+ .. versionchanged:: 4.2
+ SSL certificates are validated by default; pass
+ ``ssl_options=dict(cert_reqs=ssl.CERT_NONE)`` or a
+ suitably-configured `ssl.SSLContext` to disable.
"""
if (self._read_callback or self._read_future or
self._write_callback or self._write_future or
self._read_buffer or self._write_buffer):
raise ValueError("IOStream is not idle; cannot convert to SSL")
if ssl_options is None:
- ssl_options = {}
+ if server_side:
+ ssl_options = _server_ssl_defaults
+ else:
+ ssl_options = _client_ssl_defaults
socket = self.socket
self.io_loop.remove_handler(socket)
`ssl.SSLContext` object or a dictionary of keywords arguments
for `ssl.wrap_socket`
"""
- self._ssl_options = kwargs.pop('ssl_options', {})
+ self._ssl_options = kwargs.pop('ssl_options', _client_ssl_defaults)
super(SSLIOStream, self).__init__(*args, **kwargs)
self._ssl_accepting = True
self._handshake_reading = False
class TestIOStreamWebHTTPS(TestIOStreamWebMixin, AsyncHTTPSTestCase):
def _make_client_iostream(self):
- return SSLIOStream(socket.socket(), io_loop=self.io_loop)
+ return SSLIOStream(socket.socket(), io_loop=self.io_loop,
+ ssl_options=dict(cert_reqs=ssl.CERT_NONE))
class TestIOStream(TestIOStreamMixin, AsyncTestCase):
return SSLIOStream(connection, io_loop=self.io_loop, **kwargs)
def _make_client_iostream(self, connection, **kwargs):
- return SSLIOStream(connection, io_loop=self.io_loop, **kwargs)
+ return SSLIOStream(connection, io_loop=self.io_loop,
+ ssl_options=dict(cert_reqs=ssl.CERT_NONE),
+ **kwargs)
# This will run some tests that are basically redundant but it's the
yield self.server_send_line(b"250 STARTTLS\r\n")
yield self.client_send_line(b"STARTTLS\r\n")
yield self.server_send_line(b"220 Go ahead\r\n")
- client_future = self.client_start_tls()
+ client_future = self.client_start_tls(dict(cert_reqs=ssl.CERT_NONE))
server_future = self.server_start_tls(_server_ssl_options())
self.client_stream = yield client_future
self.server_stream = yield server_future
@gen_test
def test_handshake_fail(self):
server_future = self.server_start_tls(_server_ssl_options())
- client_future = self.client_start_tls(
- dict(cert_reqs=ssl.CERT_REQUIRED, ca_certs=certifi.where()))
+ # Certificates are verified with the default configuration.
+ client_future = self.client_start_tls(server_hostname="localhost")
with ExpectLog(gen_log, "SSL Error"):
with self.assertRaises(ssl.SSLError):
yield client_future