diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-11-05 15:30:16 +0100 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-11-05 15:30:16 +0100 |
commit | ac10ccd53e4bc6a20f72c5dd8afb3ea7c1a98179 (patch) | |
tree | 0e1d3aec75a0dd44911fa1ff4d0e76cef1f8e101 | |
parent | f803275de42ee06357fbba334592c0153b1ea25d (diff) | |
download | trollius-ac10ccd53e4bc6a20f72c5dd8afb3ea7c1a98179.tar.gz |
Python issue #22641: On Python 3.4 and newer, the default SSL context for
client connections is now created using ssl.create_default_context(), for
stronger security. Patch written by Antoine Pitrou.
-rw-r--r-- | asyncio/selector_events.py | 13 | ||||
-rw-r--r-- | asyncio/test_utils.py | 13 | ||||
-rw-r--r-- | tests/test_base_events.py | 2 | ||||
-rw-r--r-- | tests/test_events.py | 48 |
4 files changed, 58 insertions, 18 deletions
diff --git a/asyncio/selector_events.py b/asyncio/selector_events.py index da64a60..116d380 100644 --- a/asyncio/selector_events.py +++ b/asyncio/selector_events.py @@ -688,16 +688,17 @@ class _SelectorSslTransport(_SelectorTransport): if not sslcontext: # Client side may pass ssl=True to use a default # context; in that case the sslcontext passed is None. - # The default is the same as used by urllib with - # cadefault=True. - if hasattr(ssl, '_create_stdlib_context'): - sslcontext = ssl._create_stdlib_context( - cert_reqs=ssl.CERT_REQUIRED, - check_hostname=bool(server_hostname)) + # The default is secure for client connections. + if hasattr(ssl, 'create_default_context'): + # Python 3.4+: use up-to-date strong settings. + sslcontext = ssl.create_default_context() + if not server_hostname: + sslcontext.check_hostname = False else: # Fallback for Python 3.3. sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 sslcontext.set_default_verify_paths() sslcontext.verify_mode = ssl.CERT_REQUIRED diff --git a/asyncio/test_utils.py b/asyncio/test_utils.py index ac7680d..3e5eee5 100644 --- a/asyncio/test_utils.py +++ b/asyncio/test_utils.py @@ -91,6 +91,13 @@ class SilentWSGIRequestHandler(WSGIRequestHandler): class SilentWSGIServer(WSGIServer): + request_timeout = 2 + + def get_request(self): + request, client_addr = super().get_request() + request.settimeout(self.request_timeout) + return request, client_addr + def handle_error(self, request, client_address): pass @@ -138,7 +145,8 @@ def _run_test_server(*, address, use_ssl=False, server_cls, server_ssl_cls): httpd = server_class(address, SilentWSGIRequestHandler) httpd.set_app(app) httpd.address = httpd.server_address - server_thread = threading.Thread(target=httpd.serve_forever) + server_thread = threading.Thread( + target=lambda: httpd.serve_forever(poll_interval=0.05)) server_thread.start() try: yield httpd @@ -160,12 +168,15 @@ if hasattr(socket, 'AF_UNIX'): class UnixWSGIServer(UnixHTTPServer, WSGIServer): + request_timeout = 2 + def server_bind(self): UnixHTTPServer.server_bind(self) self.setup_environ() def get_request(self): request, client_addr = super().get_request() + request.settimeout(self.request_timeout) # Code in the stdlib expects that get_request # will return a socket and a tuple (host, port). # However, this isn't true for UNIX sockets, diff --git a/tests/test_base_events.py b/tests/test_base_events.py index afc448c..d61a64c 100644 --- a/tests/test_base_events.py +++ b/tests/test_base_events.py @@ -371,7 +371,7 @@ class BaseEventLoopTests(test_utils.TestCase): self.loop.run_until_complete, self.loop.subprocess_exec, asyncio.SubprocessProtocol) - # exepected multiple arguments, not a list + # expected multiple arguments, not a list self.assertRaises(TypeError, self.loop.run_until_complete, self.loop.subprocess_exec, asyncio.SubprocessProtocol, args) diff --git a/tests/test_events.py b/tests/test_events.py index a305e66..fe1e3ad 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -606,15 +606,43 @@ class EventLoopTestsMixin: self.assertGreater(pr.nbytes, 0) tr.close() + def _dummy_ssl_create_context(self, purpose=ssl.Purpose.SERVER_AUTH, *, + cafile=None, capath=None, cadata=None): + """ + A ssl.create_default_context() replacement that doesn't enable + cert validation. + """ + self.assertEqual(purpose, ssl.Purpose.SERVER_AUTH) + return test_utils.dummy_ssl_context() + + def _test_create_ssl_connection(self, httpd, create_connection, + check_sockname=True): + conn_fut = create_connection(ssl=test_utils.dummy_ssl_context()) + self._basetest_create_ssl_connection(conn_fut, check_sockname) + + # With ssl=True, ssl.create_default_context() should be called + with mock.patch('ssl.create_default_context', + side_effect=self._dummy_ssl_create_context) as m: + conn_fut = create_connection(ssl=True) + self._basetest_create_ssl_connection(conn_fut, check_sockname) + self.assertEqual(m.call_count, 1) + + # With the real ssl.create_default_context(), certificate + # validation will fail + with self.assertRaises(ssl.SSLError) as cm: + conn_fut = create_connection(ssl=True) + self._basetest_create_ssl_connection(conn_fut, check_sockname) + + self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') + @unittest.skipIf(ssl is None, 'No ssl module') def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: - conn_fut = self.loop.create_connection( + create_connection = functools.partial( + self.loop.create_connection, lambda: MyProto(loop=self.loop), - *httpd.address, - ssl=test_utils.dummy_ssl_context()) - - self._basetest_create_ssl_connection(conn_fut) + *httpd.address) + self._test_create_ssl_connection(httpd, create_connection) @unittest.skipIf(ssl is None, 'No ssl module') @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') @@ -624,13 +652,13 @@ class EventLoopTestsMixin: check_sockname = not osx_tiger() with test_utils.run_test_unix_server(use_ssl=True) as httpd: - conn_fut = self.loop.create_unix_connection( - lambda: MyProto(loop=self.loop), - httpd.address, - ssl=test_utils.dummy_ssl_context(), + create_connection = functools.partial( + self.loop.create_unix_connection, + lambda: MyProto(loop=self.loop), httpd.address, server_hostname='127.0.0.1') - self._basetest_create_ssl_connection(conn_fut, check_sockname) + self._test_create_ssl_connection(httpd, create_connection, + check_sockname) def test_create_connection_local_addr(self): with test_utils.run_test_server() as httpd: |