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