mirror of
https://github.com/python/cpython.git
synced 2024-12-04 15:25:13 +08:00
[bpo-28414] Make all hostnames in SSL module IDN A-labels (GH-5128)
Previously, the ssl module stored international domain names (IDNs) as U-labels. This is problematic for a number of reasons -- for example, it made it impossible for users to use a different version of IDNA than the one built into Python. After this change, we always convert to A-labels as soon as possible, and use them for all internal processing. In particular, server_hostname attribute is now an A-label, and on the server side there's a new sni_callback that receives the SNI servername as an A-label rather than a U-label.
This commit is contained in:
parent
82ab13d756
commit
11a1493bc4
@ -1268,6 +1268,12 @@ SSL sockets also have the following additional methods and attributes:
|
|||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. versionchanged:: 3.7
|
||||||
|
The attribute is now always ASCII text. When ``server_hostname`` is
|
||||||
|
an internationalized domain name (IDN), this attribute now stores the
|
||||||
|
A-label form (``"xn--pythn-mua.org"``), rather than the U-label form
|
||||||
|
(``"pythön.org"``).
|
||||||
|
|
||||||
.. attribute:: SSLSocket.session
|
.. attribute:: SSLSocket.session
|
||||||
|
|
||||||
The :class:`SSLSession` for this SSL connection. The session is available
|
The :class:`SSLSession` for this SSL connection. The session is available
|
||||||
@ -1532,23 +1538,24 @@ to speed up repeated connections from the same clients.
|
|||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
.. method:: SSLContext.set_servername_callback(server_name_callback)
|
.. attribute:: SSLContext.sni_callback
|
||||||
|
|
||||||
Register a callback function that will be called after the TLS Client Hello
|
Register a callback function that will be called after the TLS Client Hello
|
||||||
handshake message has been received by the SSL/TLS server when the TLS client
|
handshake message has been received by the SSL/TLS server when the TLS client
|
||||||
specifies a server name indication. The server name indication mechanism
|
specifies a server name indication. The server name indication mechanism
|
||||||
is specified in :rfc:`6066` section 3 - Server Name Indication.
|
is specified in :rfc:`6066` section 3 - Server Name Indication.
|
||||||
|
|
||||||
Only one callback can be set per ``SSLContext``. If *server_name_callback*
|
Only one callback can be set per ``SSLContext``. If *sni_callback*
|
||||||
is ``None`` then the callback is disabled. Calling this function a
|
is set to ``None`` then the callback is disabled. Calling this function a
|
||||||
subsequent time will disable the previously registered callback.
|
subsequent time will disable the previously registered callback.
|
||||||
|
|
||||||
The callback function, *server_name_callback*, will be called with three
|
The callback function will be called with three
|
||||||
arguments; the first being the :class:`ssl.SSLSocket`, the second is a string
|
arguments; the first being the :class:`ssl.SSLSocket`, the second is a string
|
||||||
that represents the server name that the client is intending to communicate
|
that represents the server name that the client is intending to communicate
|
||||||
(or :const:`None` if the TLS Client Hello does not contain a server name)
|
(or :const:`None` if the TLS Client Hello does not contain a server name)
|
||||||
and the third argument is the original :class:`SSLContext`. The server name
|
and the third argument is the original :class:`SSLContext`. The server name
|
||||||
argument is the IDNA decoded server name.
|
argument is text. For internationalized domain name, the server
|
||||||
|
name is an IDN A-label (``"xn--pythn-mua.org"``).
|
||||||
|
|
||||||
A typical use of this callback is to change the :class:`ssl.SSLSocket`'s
|
A typical use of this callback is to change the :class:`ssl.SSLSocket`'s
|
||||||
:attr:`SSLSocket.context` attribute to a new object of type
|
:attr:`SSLSocket.context` attribute to a new object of type
|
||||||
@ -1563,23 +1570,33 @@ to speed up repeated connections from the same clients.
|
|||||||
the TLS connection has progressed beyond the TLS Client Hello and therefore
|
the TLS connection has progressed beyond the TLS Client Hello and therefore
|
||||||
will not contain return meaningful values nor can they be called safely.
|
will not contain return meaningful values nor can they be called safely.
|
||||||
|
|
||||||
The *server_name_callback* function must return ``None`` to allow the
|
The *sni_callback* function must return ``None`` to allow the
|
||||||
TLS negotiation to continue. If a TLS failure is required, a constant
|
TLS negotiation to continue. If a TLS failure is required, a constant
|
||||||
:const:`ALERT_DESCRIPTION_* <ALERT_DESCRIPTION_INTERNAL_ERROR>` can be
|
:const:`ALERT_DESCRIPTION_* <ALERT_DESCRIPTION_INTERNAL_ERROR>` can be
|
||||||
returned. Other return values will result in a TLS fatal error with
|
returned. Other return values will result in a TLS fatal error with
|
||||||
:const:`ALERT_DESCRIPTION_INTERNAL_ERROR`.
|
:const:`ALERT_DESCRIPTION_INTERNAL_ERROR`.
|
||||||
|
|
||||||
If there is an IDNA decoding error on the server name, the TLS connection
|
If an exception is raised from the *sni_callback* function the TLS
|
||||||
will terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS
|
|
||||||
alert message to the client.
|
|
||||||
|
|
||||||
If an exception is raised from the *server_name_callback* function the TLS
|
|
||||||
connection will terminate with a fatal TLS alert message
|
connection will terminate with a fatal TLS alert message
|
||||||
:const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`.
|
:const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`.
|
||||||
|
|
||||||
This method will raise :exc:`NotImplementedError` if the OpenSSL library
|
This method will raise :exc:`NotImplementedError` if the OpenSSL library
|
||||||
had OPENSSL_NO_TLSEXT defined when it was built.
|
had OPENSSL_NO_TLSEXT defined when it was built.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. attribute:: SSLContext.set_servername_callback(server_name_callback)
|
||||||
|
|
||||||
|
This is a legacy API retained for backwards compatibility. When possible,
|
||||||
|
you should use :attr:`sni_callback` instead. The given *server_name_callback*
|
||||||
|
is similar to *sni_callback*, except that when the server hostname is an
|
||||||
|
IDN-encoded internationalized domain name, the *server_name_callback*
|
||||||
|
receives a decoded U-label (``"pythön.org"``).
|
||||||
|
|
||||||
|
If there is an decoding error on the server name, the TLS connection will
|
||||||
|
terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS
|
||||||
|
alert message to the client.
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. method:: SSLContext.load_dh_params(dhfile)
|
.. method:: SSLContext.load_dh_params(dhfile)
|
||||||
|
@ -662,6 +662,14 @@ ciphers that have been blocked by OpenSSL security update. Default cipher
|
|||||||
suite selection can be configured on compile time.
|
suite selection can be configured on compile time.
|
||||||
(Contributed by Christian Heimes in :issue:`31429`.)
|
(Contributed by Christian Heimes in :issue:`31429`.)
|
||||||
|
|
||||||
|
Added support for validating server certificates containing
|
||||||
|
internationalized domain names (IDNs). As part of this change, the
|
||||||
|
:attr:`ssl.SSLSocket.server_hostname` attribute now stores the
|
||||||
|
expected hostname in A-label form (``"xn--pythn-mua.org"``), rather
|
||||||
|
than the U-label form (``"pythön.org"``). (Contributed by
|
||||||
|
Nathaniel J. Smith and Christian Heimes in :issue:`28414`.)
|
||||||
|
|
||||||
|
|
||||||
string
|
string
|
||||||
------
|
------
|
||||||
|
|
||||||
|
40
Lib/ssl.py
40
Lib/ssl.py
@ -355,13 +355,20 @@ class SSLContext(_SSLContext):
|
|||||||
self = _SSLContext.__new__(cls, protocol)
|
self = _SSLContext.__new__(cls, protocol)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __init__(self, protocol=PROTOCOL_TLS):
|
def _encode_hostname(self, hostname):
|
||||||
self.protocol = protocol
|
if hostname is None:
|
||||||
|
return None
|
||||||
|
elif isinstance(hostname, str):
|
||||||
|
return hostname.encode('idna').decode('ascii')
|
||||||
|
else:
|
||||||
|
return hostname.decode('ascii')
|
||||||
|
|
||||||
def wrap_socket(self, sock, server_side=False,
|
def wrap_socket(self, sock, server_side=False,
|
||||||
do_handshake_on_connect=True,
|
do_handshake_on_connect=True,
|
||||||
suppress_ragged_eofs=True,
|
suppress_ragged_eofs=True,
|
||||||
server_hostname=None, session=None):
|
server_hostname=None, session=None):
|
||||||
|
# SSLSocket class handles server_hostname encoding before it calls
|
||||||
|
# ctx._wrap_socket()
|
||||||
return self.sslsocket_class(
|
return self.sslsocket_class(
|
||||||
sock=sock,
|
sock=sock,
|
||||||
server_side=server_side,
|
server_side=server_side,
|
||||||
@ -374,8 +381,12 @@ class SSLContext(_SSLContext):
|
|||||||
|
|
||||||
def wrap_bio(self, incoming, outgoing, server_side=False,
|
def wrap_bio(self, incoming, outgoing, server_side=False,
|
||||||
server_hostname=None, session=None):
|
server_hostname=None, session=None):
|
||||||
sslobj = self._wrap_bio(incoming, outgoing, server_side=server_side,
|
# Need to encode server_hostname here because _wrap_bio() can only
|
||||||
server_hostname=server_hostname)
|
# handle ASCII str.
|
||||||
|
sslobj = self._wrap_bio(
|
||||||
|
incoming, outgoing, server_side=server_side,
|
||||||
|
server_hostname=self._encode_hostname(server_hostname)
|
||||||
|
)
|
||||||
return self.sslobject_class(sslobj, session=session)
|
return self.sslobject_class(sslobj, session=session)
|
||||||
|
|
||||||
def set_npn_protocols(self, npn_protocols):
|
def set_npn_protocols(self, npn_protocols):
|
||||||
@ -389,6 +400,19 @@ class SSLContext(_SSLContext):
|
|||||||
|
|
||||||
self._set_npn_protocols(protos)
|
self._set_npn_protocols(protos)
|
||||||
|
|
||||||
|
def set_servername_callback(self, server_name_callback):
|
||||||
|
if server_name_callback is None:
|
||||||
|
self.sni_callback = None
|
||||||
|
else:
|
||||||
|
if not callable(server_name_callback):
|
||||||
|
raise TypeError("not a callable object")
|
||||||
|
|
||||||
|
def shim_cb(sslobj, servername, sslctx):
|
||||||
|
servername = self._encode_hostname(servername)
|
||||||
|
return server_name_callback(sslobj, servername, sslctx)
|
||||||
|
|
||||||
|
self.sni_callback = shim_cb
|
||||||
|
|
||||||
def set_alpn_protocols(self, alpn_protocols):
|
def set_alpn_protocols(self, alpn_protocols):
|
||||||
protos = bytearray()
|
protos = bytearray()
|
||||||
for protocol in alpn_protocols:
|
for protocol in alpn_protocols:
|
||||||
@ -447,6 +471,10 @@ class SSLContext(_SSLContext):
|
|||||||
def hostname_checks_common_name(self):
|
def hostname_checks_common_name(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def protocol(self):
|
||||||
|
return _SSLMethod(super().protocol)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def verify_flags(self):
|
def verify_flags(self):
|
||||||
return VerifyFlags(super().verify_flags)
|
return VerifyFlags(super().verify_flags)
|
||||||
@ -749,7 +777,7 @@ class SSLSocket(socket):
|
|||||||
raise ValueError("check_hostname requires server_hostname")
|
raise ValueError("check_hostname requires server_hostname")
|
||||||
self._session = _session
|
self._session = _session
|
||||||
self.server_side = server_side
|
self.server_side = server_side
|
||||||
self.server_hostname = server_hostname
|
self.server_hostname = self._context._encode_hostname(server_hostname)
|
||||||
self.do_handshake_on_connect = do_handshake_on_connect
|
self.do_handshake_on_connect = do_handshake_on_connect
|
||||||
self.suppress_ragged_eofs = suppress_ragged_eofs
|
self.suppress_ragged_eofs = suppress_ragged_eofs
|
||||||
if sock is not None:
|
if sock is not None:
|
||||||
@ -781,7 +809,7 @@ class SSLSocket(socket):
|
|||||||
# create the SSL object
|
# create the SSL object
|
||||||
try:
|
try:
|
||||||
sslobj = self._context._wrap_socket(self, server_side,
|
sslobj = self._context._wrap_socket(self, server_side,
|
||||||
server_hostname)
|
self.server_hostname)
|
||||||
self._sslobj = SSLObject(sslobj, owner=self,
|
self._sslobj = SSLObject(sslobj, owner=self,
|
||||||
session=self._session)
|
session=self._session)
|
||||||
if do_handshake_on_connect:
|
if do_handshake_on_connect:
|
||||||
|
@ -1528,16 +1528,6 @@ class SSLErrorTests(unittest.TestCase):
|
|||||||
# For compatibility
|
# For compatibility
|
||||||
self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ)
|
self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ)
|
||||||
|
|
||||||
def test_bad_idna_in_server_hostname(self):
|
|
||||||
# Note: this test is testing some code that probably shouldn't exist
|
|
||||||
# in the first place, so if it starts failing at some point because
|
|
||||||
# you made the ssl module stop doing IDNA decoding then please feel
|
|
||||||
# free to remove it. The test was mainly added because this case used
|
|
||||||
# to cause memory corruption (see bpo-30594).
|
|
||||||
ctx = ssl.create_default_context()
|
|
||||||
with self.assertRaises(UnicodeError):
|
|
||||||
ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(),
|
|
||||||
server_hostname="xn--.com")
|
|
||||||
|
|
||||||
def test_bad_server_hostname(self):
|
def test_bad_server_hostname(self):
|
||||||
ctx = ssl.create_default_context()
|
ctx = ssl.create_default_context()
|
||||||
@ -2634,10 +2624,10 @@ class ThreadedTests(unittest.TestCase):
|
|||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
|
|
||||||
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
server_context.load_cert_chain(IDNSANSFILE)
|
server_context.load_cert_chain(IDNSANSFILE)
|
||||||
|
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||||
context.verify_mode = ssl.CERT_REQUIRED
|
context.verify_mode = ssl.CERT_REQUIRED
|
||||||
context.check_hostname = True
|
context.check_hostname = True
|
||||||
context.load_verify_locations(SIGNING_CA)
|
context.load_verify_locations(SIGNING_CA)
|
||||||
@ -2646,18 +2636,26 @@ class ThreadedTests(unittest.TestCase):
|
|||||||
# different ways
|
# different ways
|
||||||
idn_hostnames = [
|
idn_hostnames = [
|
||||||
('könig.idn.pythontest.net',
|
('könig.idn.pythontest.net',
|
||||||
'könig.idn.pythontest.net',),
|
'xn--knig-5qa.idn.pythontest.net'),
|
||||||
('xn--knig-5qa.idn.pythontest.net',
|
('xn--knig-5qa.idn.pythontest.net',
|
||||||
'xn--knig-5qa.idn.pythontest.net'),
|
'xn--knig-5qa.idn.pythontest.net'),
|
||||||
(b'xn--knig-5qa.idn.pythontest.net',
|
(b'xn--knig-5qa.idn.pythontest.net',
|
||||||
b'xn--knig-5qa.idn.pythontest.net'),
|
'xn--knig-5qa.idn.pythontest.net'),
|
||||||
|
|
||||||
('königsgäßchen.idna2003.pythontest.net',
|
('königsgäßchen.idna2003.pythontest.net',
|
||||||
'königsgäßchen.idna2003.pythontest.net'),
|
'xn--knigsgsschen-lcb0w.idna2003.pythontest.net'),
|
||||||
('xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
|
('xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
|
||||||
'xn--knigsgsschen-lcb0w.idna2003.pythontest.net'),
|
'xn--knigsgsschen-lcb0w.idna2003.pythontest.net'),
|
||||||
(b'xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
|
(b'xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
|
||||||
b'xn--knigsgsschen-lcb0w.idna2003.pythontest.net'),
|
'xn--knigsgsschen-lcb0w.idna2003.pythontest.net'),
|
||||||
|
|
||||||
|
# ('königsgäßchen.idna2008.pythontest.net',
|
||||||
|
# 'xn--knigsgchen-b4a3dun.idna2008.pythontest.net'),
|
||||||
|
('xn--knigsgchen-b4a3dun.idna2008.pythontest.net',
|
||||||
|
'xn--knigsgchen-b4a3dun.idna2008.pythontest.net'),
|
||||||
|
(b'xn--knigsgchen-b4a3dun.idna2008.pythontest.net',
|
||||||
|
'xn--knigsgchen-b4a3dun.idna2008.pythontest.net'),
|
||||||
|
|
||||||
]
|
]
|
||||||
for server_hostname, expected_hostname in idn_hostnames:
|
for server_hostname, expected_hostname in idn_hostnames:
|
||||||
server = ThreadedEchoServer(context=server_context, chatty=True)
|
server = ThreadedEchoServer(context=server_context, chatty=True)
|
||||||
@ -2676,16 +2674,6 @@ class ThreadedTests(unittest.TestCase):
|
|||||||
s.getpeercert()
|
s.getpeercert()
|
||||||
self.assertEqual(s.server_hostname, expected_hostname)
|
self.assertEqual(s.server_hostname, expected_hostname)
|
||||||
|
|
||||||
# bug https://bugs.python.org/issue28414
|
|
||||||
# IDNA 2008 deviations are broken
|
|
||||||
idna2008 = 'xn--knigsgchen-b4a3dun.idna2008.pythontest.net'
|
|
||||||
server = ThreadedEchoServer(context=server_context, chatty=True)
|
|
||||||
with server:
|
|
||||||
with self.assertRaises(UnicodeError):
|
|
||||||
with context.wrap_socket(socket.socket(),
|
|
||||||
server_hostname=idna2008) as s:
|
|
||||||
s.connect((HOST, server.port))
|
|
||||||
|
|
||||||
# incorrect hostname should raise an exception
|
# incorrect hostname should raise an exception
|
||||||
server = ThreadedEchoServer(context=server_context, chatty=True)
|
server = ThreadedEchoServer(context=server_context, chatty=True)
|
||||||
with server:
|
with server:
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
The ssl module now allows users to perform their own IDN en/decoding when using SNI.
|
131
Modules/_ssl.c
131
Modules/_ssl.c
@ -337,13 +337,14 @@ typedef struct {
|
|||||||
unsigned int alpn_protocols_len;
|
unsigned int alpn_protocols_len;
|
||||||
#endif
|
#endif
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
PyObject *set_hostname;
|
PyObject *set_sni_cb;
|
||||||
#endif
|
#endif
|
||||||
int check_hostname;
|
int check_hostname;
|
||||||
/* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct.
|
/* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct.
|
||||||
* We have to maintain our own copy. OpenSSL's hostflags default to 0.
|
* We have to maintain our own copy. OpenSSL's hostflags default to 0.
|
||||||
*/
|
*/
|
||||||
unsigned int hostflags;
|
unsigned int hostflags;
|
||||||
|
int protocol;
|
||||||
} PySSLContext;
|
} PySSLContext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -407,8 +408,6 @@ class _ssl.SSLSession "PySSLSession *" "&PySSLSession_Type"
|
|||||||
|
|
||||||
static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout);
|
static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout);
|
||||||
|
|
||||||
|
|
||||||
#define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type)
|
|
||||||
#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type)
|
#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type)
|
||||||
#define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type)
|
#define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type)
|
||||||
#define PySSLSession_Check(v) (Py_TYPE(v) == &PySSLSession_Type)
|
#define PySSLSession_Check(v) (Py_TYPE(v) == &PySSLSession_Type)
|
||||||
@ -761,7 +760,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname)
|
|||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname = PyUnicode_Decode(server_hostname, len, "idna", "strict");
|
hostname = PyUnicode_Decode(server_hostname, len, "ascii", "strict");
|
||||||
if (hostname == NULL) {
|
if (hostname == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -1992,7 +1991,7 @@ PyDoc_STRVAR(PySSL_set_context_doc,
|
|||||||
"_setter_context(ctx)\n\
|
"_setter_context(ctx)\n\
|
||||||
\
|
\
|
||||||
This changes the context associated with the SSLSocket. This is typically\n\
|
This changes the context associated with the SSLSocket. This is typically\n\
|
||||||
used from within a callback function set by the set_servername_callback\n\
|
used from within a callback function set by the sni_callback\n\
|
||||||
on the SSLContext to change the certificate information associated with the\n\
|
on the SSLContext to change the certificate information associated with the\n\
|
||||||
SSLSocket before the cryptographic exchange handshake messages\n");
|
SSLSocket before the cryptographic exchange handshake messages\n");
|
||||||
|
|
||||||
@ -2850,6 +2849,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
|
|||||||
}
|
}
|
||||||
self->ctx = ctx;
|
self->ctx = ctx;
|
||||||
self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
|
self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
|
||||||
|
self->protocol = proto_version;
|
||||||
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
self->npn_protocols = NULL;
|
self->npn_protocols = NULL;
|
||||||
#endif
|
#endif
|
||||||
@ -2857,7 +2857,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
|
|||||||
self->alpn_protocols = NULL;
|
self->alpn_protocols = NULL;
|
||||||
#endif
|
#endif
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
self->set_hostname = NULL;
|
self->set_sni_cb = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Don't check host name by default */
|
/* Don't check host name by default */
|
||||||
if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
|
if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
|
||||||
@ -2968,7 +2968,7 @@ static int
|
|||||||
context_traverse(PySSLContext *self, visitproc visit, void *arg)
|
context_traverse(PySSLContext *self, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
Py_VISIT(self->set_hostname);
|
Py_VISIT(self->set_sni_cb);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2977,7 +2977,7 @@ static int
|
|||||||
context_clear(PySSLContext *self)
|
context_clear(PySSLContext *self)
|
||||||
{
|
{
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
Py_CLEAR(self->set_hostname);
|
Py_CLEAR(self->set_sni_cb);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3354,6 +3354,10 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_protocol(PySSLContext *self, void *c) {
|
||||||
|
return PyLong_FromLong(self->protocol);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyThreadState *thread_state;
|
PyThreadState *thread_state;
|
||||||
@ -3818,9 +3822,9 @@ _ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock,
|
|||||||
PyObject *res;
|
PyObject *res;
|
||||||
|
|
||||||
/* server_hostname is either None (or absent), or to be encoded
|
/* server_hostname is either None (or absent), or to be encoded
|
||||||
using the idna encoding. */
|
as IDN A-label (ASCII str). */
|
||||||
if (hostname_obj != Py_None) {
|
if (hostname_obj != Py_None) {
|
||||||
if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname))
|
if (!PyArg_Parse(hostname_obj, "es", "ascii", &hostname))
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3851,9 +3855,9 @@ _ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming,
|
|||||||
PyObject *res;
|
PyObject *res;
|
||||||
|
|
||||||
/* server_hostname is either None (or absent), or to be encoded
|
/* server_hostname is either None (or absent), or to be encoded
|
||||||
using the idna encoding. */
|
as IDN A-label (ASCII str). */
|
||||||
if (hostname_obj != Py_None) {
|
if (hostname_obj != Py_None) {
|
||||||
if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname))
|
if (!PyArg_Parse(hostname_obj, "es", "ascii", &hostname))
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3967,15 +3971,13 @@ _servername_callback(SSL *s, int *al, void *args)
|
|||||||
int ret;
|
int ret;
|
||||||
PySSLContext *ssl_ctx = (PySSLContext *) args;
|
PySSLContext *ssl_ctx = (PySSLContext *) args;
|
||||||
PySSLSocket *ssl;
|
PySSLSocket *ssl;
|
||||||
PyObject *servername_o;
|
|
||||||
PyObject *servername_idna;
|
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
/* The high-level ssl.SSLSocket object */
|
/* The high-level ssl.SSLSocket object */
|
||||||
PyObject *ssl_socket;
|
PyObject *ssl_socket;
|
||||||
const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
|
const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
|
||||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
if (ssl_ctx->set_hostname == NULL) {
|
if (ssl_ctx->set_sni_cb == NULL) {
|
||||||
/* remove race condition in this the call back while if removing the
|
/* remove race condition in this the call back while if removing the
|
||||||
* callback is in progress */
|
* callback is in progress */
|
||||||
PyGILState_Release(gstate);
|
PyGILState_Release(gstate);
|
||||||
@ -4005,35 +4007,46 @@ _servername_callback(SSL *s, int *al, void *args)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (servername == NULL) {
|
if (servername == NULL) {
|
||||||
result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket,
|
result = PyObject_CallFunctionObjArgs(ssl_ctx->set_sni_cb, ssl_socket,
|
||||||
Py_None, ssl_ctx, NULL);
|
Py_None, ssl_ctx, NULL);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
servername_o = PyBytes_FromString(servername);
|
PyObject *servername_bytes;
|
||||||
if (servername_o == NULL) {
|
PyObject *servername_str;
|
||||||
|
|
||||||
|
servername_bytes = PyBytes_FromString(servername);
|
||||||
|
if (servername_bytes == NULL) {
|
||||||
PyErr_WriteUnraisable((PyObject *) ssl_ctx);
|
PyErr_WriteUnraisable((PyObject *) ssl_ctx);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
servername_idna = PyUnicode_FromEncodedObject(servername_o, "idna", NULL);
|
/* server_hostname was encoded to an A-label by our caller; put it
|
||||||
if (servername_idna == NULL) {
|
* back into a str object, but still as an A-label (bpo-28414)
|
||||||
PyErr_WriteUnraisable(servername_o);
|
*/
|
||||||
Py_DECREF(servername_o);
|
servername_str = PyUnicode_FromEncodedObject(servername_bytes, "ascii", NULL);
|
||||||
|
Py_DECREF(servername_bytes);
|
||||||
|
if (servername_str == NULL) {
|
||||||
|
PyErr_WriteUnraisable(servername_bytes);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Py_DECREF(servername_o);
|
result = PyObject_CallFunctionObjArgs(
|
||||||
result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket,
|
ssl_ctx->set_sni_cb, ssl_socket, servername_str,
|
||||||
servername_idna, ssl_ctx, NULL);
|
ssl_ctx, NULL);
|
||||||
Py_DECREF(servername_idna);
|
Py_DECREF(servername_str);
|
||||||
}
|
}
|
||||||
Py_DECREF(ssl_socket);
|
Py_DECREF(ssl_socket);
|
||||||
|
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
PyErr_WriteUnraisable(ssl_ctx->set_hostname);
|
PyErr_WriteUnraisable(ssl_ctx->set_sni_cb);
|
||||||
*al = SSL_AD_HANDSHAKE_FAILURE;
|
*al = SSL_AD_HANDSHAKE_FAILURE;
|
||||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (result != Py_None) {
|
/* Result may be None, a SSLContext or an integer
|
||||||
|
* None and SSLContext are OK, integer or other values are an error.
|
||||||
|
*/
|
||||||
|
if (result == Py_None) {
|
||||||
|
ret = SSL_TLSEXT_ERR_OK;
|
||||||
|
} else {
|
||||||
*al = (int) PyLong_AsLong(result);
|
*al = (int) PyLong_AsLong(result);
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
PyErr_WriteUnraisable(result);
|
PyErr_WriteUnraisable(result);
|
||||||
@ -4041,9 +4054,6 @@ _servername_callback(SSL *s, int *al, void *args)
|
|||||||
}
|
}
|
||||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
ret = SSL_TLSEXT_ERR_OK;
|
|
||||||
}
|
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4059,49 +4069,59 @@ error:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*[clinic input]
|
|
||||||
_ssl._SSLContext.set_servername_callback
|
|
||||||
method as cb: object
|
|
||||||
/
|
|
||||||
|
|
||||||
Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.
|
|
||||||
|
|
||||||
If the argument is None then the callback is disabled. The method is called
|
|
||||||
with the SSLSocket, the server name as a string, and the SSLContext object.
|
|
||||||
See RFC 6066 for details of the SNI extension.
|
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_ssl__SSLContext_set_servername_callback(PySSLContext *self, PyObject *cb)
|
get_sni_callback(PySSLContext *self, void *c)
|
||||||
/*[clinic end generated code: output=3439a1b2d5d3b7ea input=a2a83620197d602b]*/
|
|
||||||
{
|
{
|
||||||
|
PyObject *cb = self->set_sni_cb;
|
||||||
|
if (cb == NULL) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
Py_INCREF(cb);
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_sni_callback(PySSLContext *self, PyObject *arg, void *c)
|
||||||
|
{
|
||||||
|
if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"sni_callback cannot be set on TLS_CLIENT context");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT)
|
#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT)
|
||||||
Py_CLEAR(self->set_hostname);
|
Py_CLEAR(self->set_sni_cb);
|
||||||
if (cb == Py_None) {
|
if (arg == Py_None) {
|
||||||
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!PyCallable_Check(cb)) {
|
if (!PyCallable_Check(arg)) {
|
||||||
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"not a callable object");
|
"not a callable object");
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_INCREF(cb);
|
Py_INCREF(arg);
|
||||||
self->set_hostname = cb;
|
self->set_sni_cb = arg;
|
||||||
SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback);
|
SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback);
|
||||||
SSL_CTX_set_tlsext_servername_arg(self->ctx, self);
|
SSL_CTX_set_tlsext_servername_arg(self->ctx, self);
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
return 0;
|
||||||
#else
|
#else
|
||||||
PyErr_SetString(PyExc_NotImplementedError,
|
PyErr_SetString(PyExc_NotImplementedError,
|
||||||
"The TLS extension servername callback, "
|
"The TLS extension servername callback, "
|
||||||
"SSL_CTX_set_tlsext_servername_callback, "
|
"SSL_CTX_set_tlsext_servername_callback, "
|
||||||
"is not in the current OpenSSL library.");
|
"is not in the current OpenSSL library.");
|
||||||
return NULL;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
|
||||||
|
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
|
||||||
|
\n\
|
||||||
|
If the argument is None then the callback is disabled. The method is called\n\
|
||||||
|
with the SSLSocket, the server name as a string, and the SSLContext object.\n\
|
||||||
|
See RFC 6066 for details of the SNI extension.");
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
_ssl._SSLContext.cert_store_stats
|
_ssl._SSLContext.cert_store_stats
|
||||||
|
|
||||||
@ -4217,8 +4237,12 @@ static PyGetSetDef context_getsetlist[] = {
|
|||||||
(setter) set_check_hostname, NULL},
|
(setter) set_check_hostname, NULL},
|
||||||
{"_host_flags", (getter) get_host_flags,
|
{"_host_flags", (getter) get_host_flags,
|
||||||
(setter) set_host_flags, NULL},
|
(setter) set_host_flags, NULL},
|
||||||
|
{"sni_callback", (getter) get_sni_callback,
|
||||||
|
(setter) set_sni_callback, PySSLContext_sni_callback_doc},
|
||||||
{"options", (getter) get_options,
|
{"options", (getter) get_options,
|
||||||
(setter) set_options, NULL},
|
(setter) set_options, NULL},
|
||||||
|
{"protocol", (getter) get_protocol,
|
||||||
|
NULL, NULL},
|
||||||
{"verify_flags", (getter) get_verify_flags,
|
{"verify_flags", (getter) get_verify_flags,
|
||||||
(setter) set_verify_flags, NULL},
|
(setter) set_verify_flags, NULL},
|
||||||
{"verify_mode", (getter) get_verify_mode,
|
{"verify_mode", (getter) get_verify_mode,
|
||||||
@ -4238,7 +4262,6 @@ static struct PyMethodDef context_methods[] = {
|
|||||||
_SSL__SSLCONTEXT_SESSION_STATS_METHODDEF
|
_SSL__SSLCONTEXT_SESSION_STATS_METHODDEF
|
||||||
_SSL__SSLCONTEXT_SET_DEFAULT_VERIFY_PATHS_METHODDEF
|
_SSL__SSLCONTEXT_SET_DEFAULT_VERIFY_PATHS_METHODDEF
|
||||||
_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
||||||
_SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF
|
|
||||||
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
|
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
|
||||||
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
|
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
|
||||||
_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
|
_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
|
||||||
|
@ -650,19 +650,6 @@ PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_curve__doc__,
|
|||||||
|
|
||||||
#endif /* !defined(OPENSSL_NO_ECDH) */
|
#endif /* !defined(OPENSSL_NO_ECDH) */
|
||||||
|
|
||||||
PyDoc_STRVAR(_ssl__SSLContext_set_servername_callback__doc__,
|
|
||||||
"set_servername_callback($self, method, /)\n"
|
|
||||||
"--\n"
|
|
||||||
"\n"
|
|
||||||
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n"
|
|
||||||
"\n"
|
|
||||||
"If the argument is None then the callback is disabled. The method is called\n"
|
|
||||||
"with the SSLSocket, the server name as a string, and the SSLContext object.\n"
|
|
||||||
"See RFC 6066 for details of the SNI extension.");
|
|
||||||
|
|
||||||
#define _SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF \
|
|
||||||
{"set_servername_callback", (PyCFunction)_ssl__SSLContext_set_servername_callback, METH_O, _ssl__SSLContext_set_servername_callback__doc__},
|
|
||||||
|
|
||||||
PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__,
|
PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__,
|
||||||
"cert_store_stats($self, /)\n"
|
"cert_store_stats($self, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
@ -1168,4 +1155,4 @@ exit:
|
|||||||
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
||||||
#define _SSL_ENUM_CRLS_METHODDEF
|
#define _SSL_ENUM_CRLS_METHODDEF
|
||||||
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
||||||
/*[clinic end generated code: output=3d42305ed0ad162a input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=84e1fd89aff9b0f7 input=a9049054013a1b77]*/
|
||||||
|
Loading…
Reference in New Issue
Block a user