]> granicus.if.org Git - python/commitdiff
don't require OpenSSL SNI to pass hostname to ssl functions (#22921)
authorBenjamin Peterson <benjamin@python.org>
Sun, 23 Nov 2014 23:04:34 +0000 (17:04 -0600)
committerBenjamin Peterson <benjamin@python.org>
Sun, 23 Nov 2014 23:04:34 +0000 (17:04 -0600)
Patch by Donald Stufft.

16 files changed:
Doc/library/ssl.rst
Lib/asyncio/selector_events.py
Lib/ftplib.py
Lib/http/client.py
Lib/imaplib.py
Lib/nntplib.py
Lib/poplib.py
Lib/smtplib.py
Lib/ssl.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_selector_events.py
Lib/test/test_ftplib.py
Lib/test/test_imaplib.py
Lib/test/test_poplib.py
Lib/test/test_ssl.py
Modules/_ssl.c

index fb20793d358672bb881011d2f696712adffe1ce0..1a0b1dad5cb6a6e2ea6b13ca1c508510a56884fc 100644 (file)
@@ -664,8 +664,7 @@ Constants
 .. data:: HAS_SNI
 
    Whether the OpenSSL library has built-in support for the *Server Name
-   Indication* extension (as defined in :rfc:`4366`).  When true, you can
-   use the *server_hostname* argument to :meth:`SSLContext.wrap_socket`.
+   Indication* extension (as defined in :rfc:`4366`).
 
    .. versionadded:: 3.2
 
@@ -1227,11 +1226,12 @@ to speed up repeated connections from the same clients.
    On client connections, the optional parameter *server_hostname* specifies
    the hostname of the service which we are connecting to.  This allows a
    single server to host multiple SSL-based services with distinct certificates,
-   quite similarly to HTTP virtual hosts.  Specifying *server_hostname*
-   will raise a :exc:`ValueError` if the OpenSSL library doesn't have support
-   for it (that is, if :data:`HAS_SNI` is :const:`False`).  Specifying
-   *server_hostname* will also raise a :exc:`ValueError` if *server_side*
-   is true.
+   quite similarly to HTTP virtual hosts. Specifying *server_hostname* will
+   raise a :exc:`ValueError` if *server_side* is true.
+
+   .. versionchanged:: 3.5
+      Always allow a server_hostname to be passed, even if OpenSSL does not
+      have SNI.
 
 .. method:: SSLContext.session_stats()
 
index f0c94c4557f2084a3a06de78ab0e838dba4bef56..7df8b866502f4829f9da74a7e5fc06ebd62397cb 100644 (file)
@@ -708,7 +708,7 @@ class _SelectorSslTransport(_SelectorTransport):
             'server_side': server_side,
             'do_handshake_on_connect': False,
         }
-        if server_hostname and not server_side and ssl.HAS_SNI:
+        if server_hostname and not server_side:
             wrap_kwargs['server_hostname'] = server_hostname
         sslsock = sslcontext.wrap_socket(rawsock, **wrap_kwargs)
 
index c83be2b3195c4e670af4bc2ed03981b55370efc2..9c73f5f019d14c3d65efab17f7f8f2e4c39e204d 100644 (file)
@@ -747,9 +747,8 @@ else:
                 resp = self.voidcmd('AUTH TLS')
             else:
                 resp = self.voidcmd('AUTH SSL')
-            server_hostname = self.host if ssl.HAS_SNI else None
             self.sock = self.context.wrap_socket(self.sock,
-                                                 server_hostname=server_hostname)
+                                                 server_hostname=self.host)
             self.file = self.sock.makefile(mode='r', encoding=self.encoding)
             return resp
 
@@ -788,9 +787,8 @@ else:
         def ntransfercmd(self, cmd, rest=None):
             conn, size = FTP.ntransfercmd(self, cmd, rest)
             if self._prot_p:
-                server_hostname = self.host if ssl.HAS_SNI else None
                 conn = self.context.wrap_socket(conn,
-                                                server_hostname=server_hostname)
+                                                server_hostname=self.host)
             return conn, size
 
         def abort(self):
index b4cdec4508a5e3282aa55b4338b487cafd73aa3d..cb1a53579c5f90b7c038ca439e91fa03a4634ec4 100644 (file)
@@ -1224,10 +1224,9 @@ else:
                 server_hostname = self._tunnel_host
             else:
                 server_hostname = self.host
-            sni_hostname = server_hostname if ssl.HAS_SNI else None
 
             self.sock = self._context.wrap_socket(self.sock,
-                                                  server_hostname=sni_hostname)
+                                                  server_hostname=server_hostname)
             if not self._context.check_hostname and self._check_hostname:
                 try:
                     ssl.match_hostname(self.sock.getpeercert(), server_hostname)
index ad104fe76a71d8ee3259ddd76f6be0c7d3779979..72ab5d79e5f66283662b71fd88fda9d1136119ed 100644 (file)
@@ -745,9 +745,8 @@ class IMAP4:
             ssl_context = ssl._create_stdlib_context()
         typ, dat = self._simple_command(name)
         if typ == 'OK':
-            server_hostname = self.host if ssl.HAS_SNI else None
             self.sock = ssl_context.wrap_socket(self.sock,
-                                                server_hostname=server_hostname)
+                                                server_hostname=self.host)
             self.file = self.sock.makefile('rb')
             self._tls_established = True
             self._get_capabilities()
@@ -1223,9 +1222,8 @@ if HAVE_SSL:
 
         def _create_socket(self):
             sock = IMAP4._create_socket(self)
-            server_hostname = self.host if ssl.HAS_SNI else None
             return self.ssl_context.wrap_socket(sock,
-                                                server_hostname=server_hostname)
+                                                server_hostname=self.host)
 
         def open(self, host='', port=IMAP4_SSL_PORT):
             """Setup connection to remote server on "host:port".
index d33faf85b9e5a7aa887b7437c273ab06f35630bb..bcf7d1bd3363bd9c06507828e0e298b4ff8ec833 100644 (file)
@@ -289,8 +289,7 @@ if _have_ssl:
         # Generate a default SSL context if none was passed.
         if context is None:
             context = ssl._create_stdlib_context()
-        server_hostname = hostname if ssl.HAS_SNI else None
-        return context.wrap_socket(sock, server_hostname=server_hostname)
+        return context.wrap_socket(sock, server_hostname=hostname)
 
 
 # The classes themselves
index 2b0e3d54156999dc76e3d96897090a2cc4a1fe25..8ad9cb77db756e0b37a0bc83e3e609b81493206e 100644 (file)
@@ -387,9 +387,8 @@ class POP3:
         if context is None:
             context = ssl._create_stdlib_context()
         resp = self._shortcmd('STLS')
-        server_hostname = self.host if ssl.HAS_SNI else None
         self.sock = context.wrap_socket(self.sock,
-                                        server_hostname=server_hostname)
+                                        server_hostname=self.host)
         self.file = self.sock.makefile('rb')
         self._tls_established = True
         return resp
@@ -430,9 +429,8 @@ if HAVE_SSL:
 
         def _create_socket(self, timeout):
             sock = POP3._create_socket(self, timeout)
-            server_hostname = self.host if ssl.HAS_SNI else None
             sock = self.context.wrap_socket(sock,
-                                            server_hostname=server_hostname)
+                                            server_hostname=self.host)
             return sock
 
         def stls(self, keyfile=None, certfile=None, context=None):
index 09b4ea647962a7797a4ffd0097bf3f1079d9f286..fa7f9e61abcd94286923860634aa0ac095a59605 100755 (executable)
@@ -684,9 +684,8 @@ class SMTP:
             if context is None:
                 context = ssl._create_stdlib_context(certfile=certfile,
                                                      keyfile=keyfile)
-            server_hostname = self._host if ssl.HAS_SNI else None
             self.sock = context.wrap_socket(self.sock,
-                                            server_hostname=server_hostname)
+                                            server_hostname=self._host)
             self.file = None
             # RFC 3207:
             # The client MUST discard any knowledge obtained from
@@ -915,9 +914,8 @@ if _have_ssl:
                 print('connect:', (host, port), file=stderr)
             new_socket = socket.create_connection((host, port), timeout,
                     self.source_address)
-            server_hostname = self._host if ssl.HAS_SNI else None
             new_socket = self.context.wrap_socket(new_socket,
-                                                  server_hostname=server_hostname)
+                                                  server_hostname=self._host)
             return new_socket
 
     __all__.append("SMTP_SSL")
index 2b81b790d8b54c18159d56ca1808fc6b8954387e..3d4997caf05b4b491f4b8ff6672a1e686f7edd72 100644 (file)
@@ -538,12 +538,7 @@ class SSLSocket(socket):
             raise ValueError("server_hostname can only be specified "
                              "in client mode")
         if self._context.check_hostname and not server_hostname:
-            if HAS_SNI:
-                raise ValueError("check_hostname requires server_hostname")
-            else:
-                raise ValueError("check_hostname requires server_hostname, "
-                                 "but it's not supported by your OpenSSL "
-                                 "library")
+            raise ValueError("check_hostname requires server_hostname")
         self.server_side = server_side
         self.server_hostname = server_hostname
         self.do_handshake_on_connect = do_handshake_on_connect
index fab3259ffcab39d0a685f6947367822becfcf7cc..ea657fd44d45c9e57042c2e7d938ff2afcde5115 100644 (file)
@@ -12,9 +12,6 @@ try:
     import ssl
 except ImportError:
     ssl = None
-    HAS_SNI = False
-else:
-    from ssl import HAS_SNI
 import subprocess
 import sys
 import threading
@@ -857,7 +854,6 @@ class EventLoopTestsMixin:
         server.close()
 
     @unittest.skipIf(ssl is None, 'No ssl module')
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_create_server_ssl_verify_failed(self):
         proto = MyProto(loop=self.loop)
         server, host, port = self._make_ssl_server(
@@ -882,7 +878,6 @@ class EventLoopTestsMixin:
         server.close()
 
     @unittest.skipIf(ssl is None, 'No ssl module')
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
     def test_create_unix_server_ssl_verify_failed(self):
         proto = MyProto(loop=self.loop)
@@ -909,7 +904,6 @@ class EventLoopTestsMixin:
         server.close()
 
     @unittest.skipIf(ssl is None, 'No ssl module')
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_create_server_ssl_match_failed(self):
         proto = MyProto(loop=self.loop)
         server, host, port = self._make_ssl_server(
@@ -937,7 +931,6 @@ class EventLoopTestsMixin:
         server.close()
 
     @unittest.skipIf(ssl is None, 'No ssl module')
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
     def test_create_unix_server_ssl_verified(self):
         proto = MyProto(loop=self.loop)
@@ -963,7 +956,6 @@ class EventLoopTestsMixin:
         server.close()
 
     @unittest.skipIf(ssl is None, 'No ssl module')
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_create_server_ssl_verified(self):
         proto = MyProto(loop=self.loop)
         server, host, port = self._make_ssl_server(
index 528da39d67b5b5d6e495f39c99f2f0a04c8d5cd2..8eba56c443b2665417bc2d8d3922694451c63063 100644 (file)
@@ -1408,7 +1408,7 @@ class SelectorSslTransportTests(test_utils.TestCase):
         self.assertEqual(tr._conn_lost, 1)
         self.assertEqual(1, self.loop.remove_reader_count[1])
 
-    @unittest.skipIf(ssl is None or not ssl.HAS_SNI, 'No SNI support')
+    @unittest.skipIf(ssl is None, 'No SSL support')
     def test_server_hostname(self):
         _SelectorSslTransport(
             self.loop, self.sock, self.protocol, self.sslcontext,
index a9bf30b3d5da9e1f712fb4189058d2ecc87236c1..fa9c6f440c9cd624ff1b8f313d658a81791277a4 100644 (file)
@@ -15,9 +15,6 @@ try:
     import ssl
 except ImportError:
     ssl = None
-    HAS_SNI = False
-else:
-    from ssl import HAS_SNI
 
 from unittest import TestCase, skipUnless
 from test import support
@@ -927,7 +924,6 @@ class TestTLS_FTPClass(TestCase):
         self.client.ccc()
         self.assertRaises(ValueError, self.client.sock.unwrap)
 
-    @skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_check_hostname(self):
         self.client.quit()
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
index a6d83d42f32fd439c2078d1e116c6bd750b2ab7f..b34e652347fcad871dc5eea97014b3921b4bdf3d 100644 (file)
@@ -18,9 +18,6 @@ try:
     import ssl
 except ImportError:
     ssl = None
-    HAS_SNI = False
-else:
-    from ssl import HAS_SNI
 
 CERTFILE = None
 CAFILE = None
@@ -352,7 +349,6 @@ class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests):
     imap_class = IMAP4_SSL
 
     @reap_threads
-    @unittest.skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_ssl_verified(self):
         ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
         ssl_context.verify_mode = ssl.CERT_REQUIRED
index d076fc1b9c3fbb77cda654909a3d507ad8e1ab31..8a3c9f42faa66692d57f7a0d4b3eaeacce5b95e0 100644 (file)
@@ -21,13 +21,10 @@ PORT = 0
 SUPPORTS_SSL = False
 if hasattr(poplib, 'POP3_SSL'):
     import ssl
-    from ssl import HAS_SNI
 
     SUPPORTS_SSL = True
     CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
     CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")
-else:
-    HAS_SNI = False
 
 requires_ssl = skipUnless(SUPPORTS_SSL, 'SSL not supported')
 
@@ -334,7 +331,6 @@ class TestPOP3Class(TestCase):
         self.assertEqual(resp, expected)
 
     @requires_ssl
-    @skipUnless(HAS_SNI, 'No SNI support in ssl module')
     def test_stls_context(self):
         expected = b'+OK Begin TLS negotiation'
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
index 7f1f40504471325fadf5b5554c33161087b6fdf7..928f5e6a8fae4f394fb9d5f33c443b24111ac83c 100644 (file)
@@ -1281,11 +1281,8 @@ class NetworkedTests(unittest.TestCase):
             # Same with a server hostname
             s = ctx.wrap_socket(socket.socket(socket.AF_INET),
                                 server_hostname="svn.python.org")
-            if ssl.HAS_SNI:
-                s.connect(("svn.python.org", 443))
-                s.close()
-            else:
-                self.assertRaises(ValueError, s.connect, ("svn.python.org", 443))
+            s.connect(("svn.python.org", 443))
+            s.close()
             # This should fail because we have no verification certs
             ctx.verify_mode = ssl.CERT_REQUIRED
             s = ctx.wrap_socket(socket.socket(socket.AF_INET))
@@ -2038,7 +2035,6 @@ else:
                     cert = s.getpeercert()
                     self.assertTrue(cert, "Can't get peer certificate.")
 
-        @needs_sni
         def test_check_hostname(self):
             if support.verbose:
                 sys.stdout.write("\n")
index 5a9a13b82178c615ce7e06cfa5ea4057fbbe87ae..90bca98aebed4ead87fdfb2bc1e92ba2f597b44a 100644 (file)
@@ -2806,12 +2806,6 @@ context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
             &sock, &server_side,
             "idna", &hostname))
             return NULL;
-#if !HAVE_SNI
-        PyMem_Free(hostname);
-        PyErr_SetString(PyExc_ValueError, "server_hostname is not supported "
-                        "by your OpenSSL library");
-        return NULL;
-#endif
     }
 
     res = (PyObject *) newPySSLSocket(self, sock, server_side,