summaryrefslogtreecommitdiffstats
path: root/Lib/ssl.py
diff options
context:
space:
mode:
authorŁukasz Langa <lukasz@langa.pl>2023-08-22 17:53:15 (GMT)
committerGitHub <noreply@github.com>2023-08-22 17:53:15 (GMT)
commit0cb0c238d520a8718e313b52cffc356a5a7561bf (patch)
tree4f250f175ccf9ee94b9c39fb4df67dc72feeef60 /Lib/ssl.py
parentd2879f2095abd5c8186c7f69c964a341c2053572 (diff)
downloadcpython-0cb0c238d520a8718e313b52cffc356a5a7561bf.zip
cpython-0cb0c238d520a8718e313b52cffc356a5a7561bf.tar.gz
cpython-0cb0c238d520a8718e313b52cffc356a5a7561bf.tar.bz2
gh-108310: Fix CVE-2023-40217: Check for & avoid the ssl pre-close flaw (#108315)
Instances of `ssl.SSLSocket` were vulnerable to a bypass of the TLS handshake and included protections (like certificate verification) and treating sent unencrypted data as if it were post-handshake TLS encrypted data. The vulnerability is caused when a socket is connected, data is sent by the malicious peer and stored in a buffer, and then the malicious peer closes the socket within a small timing window before the other peers’ TLS handshake can begin. After this sequence of events the closed socket will not immediately attempt a TLS handshake due to not being connected but will also allow the buffered data to be read as if a successful TLS handshake had occurred. Co-authored-by: Gregory P. Smith [Google LLC] <greg@krypto.org>
Diffstat (limited to 'Lib/ssl.py')
-rw-r--r--Lib/ssl.py31
1 files changed, 30 insertions, 1 deletions
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 1d58737..ff363c7 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -975,7 +975,7 @@ class SSLSocket(socket):
)
self = cls.__new__(cls, **kwargs)
super(SSLSocket, self).__init__(**kwargs)
- self.settimeout(sock.gettimeout())
+ sock_timeout = sock.gettimeout()
sock.detach()
self._context = context
@@ -994,9 +994,38 @@ class SSLSocket(socket):
if e.errno != errno.ENOTCONN:
raise
connected = False
+ blocking = self.getblocking()
+ self.setblocking(False)
+ try:
+ # We are not connected so this is not supposed to block, but
+ # testing revealed otherwise on macOS and Windows so we do
+ # the non-blocking dance regardless. Our raise when any data
+ # is found means consuming the data is harmless.
+ notconn_pre_handshake_data = self.recv(1)
+ except OSError as e:
+ # EINVAL occurs for recv(1) on non-connected on unix sockets.
+ if e.errno not in (errno.ENOTCONN, errno.EINVAL):
+ raise
+ notconn_pre_handshake_data = b''
+ self.setblocking(blocking)
+ if notconn_pre_handshake_data:
+ # This prevents pending data sent to the socket before it was
+ # closed from escaping to the caller who could otherwise
+ # presume it came through a successful TLS connection.
+ reason = "Closed before TLS handshake with data in recv buffer."
+ notconn_pre_handshake_data_error = SSLError(e.errno, reason)
+ # Add the SSLError attributes that _ssl.c always adds.
+ notconn_pre_handshake_data_error.reason = reason
+ notconn_pre_handshake_data_error.library = None
+ try:
+ self.close()
+ except OSError:
+ pass
+ raise notconn_pre_handshake_data_error
else:
connected = True
+ self.settimeout(sock_timeout) # Must come after setblocking() calls.
self._connected = connected
if connected:
# create the SSL object