-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-31711: Fix for calling SSLSocket.send with empty input. #7559
Conversation
It should behave as ordinary socket as promised in the docs.
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. When your account is ready, please add a comment in this pull request Thanks again to your contribution and we look forward to looking at it! |
Signed CLA |
Thanks for taking this on! Two suggestions:
|
@njsmith I adopted your suggestions. Please take a look. |
Lib/test/test_ssl.py
Outdated
@@ -2578,6 +2578,14 @@ def test_echo(self): | |||
chatty=True, connectionchatty=True, | |||
sni_name=hostname) | |||
|
|||
## Testing that SSLSopcet can handle empty input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this meant to be SSLSocket?
Any onther suggestions? Are you ready to merge? |
The PR looks good to me, and I think the change is a good idea. @tiran, are you OK with this change? (Conversation in [bpo-31711](https://bugs.python.org/issue31711) was inconclusive.) |
I see a potential issue with this patch. Simply speaking, TLS/SSL has two different kinds of messages. There is application data and protocol data. SSL_write and SSL_read not only transmits application data, but they also flush and check for protocol data. That's why a read operation may also send data and a write operation may also read data. This patch breaks the assumption that send() also takes care of protocol data. With TLS 1.2, it's not a big issue. Most critical protocol data like session and client cert auth are handled in the TLS handshake. However in TLS 1.3, the server side sends a TLS client auth request much later. Usually it occurs after the first write. Speaking of handshake, with this patch, send() may no longer establish a handshake with I'm -1 on this patch. Let's figure out how to flush / check protocol data and handle implicit handshake correctly. |
Modules/_ssl.c
Outdated
@@ -2247,6 +2247,13 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) | |||
"string longer than %d bytes", INT_MAX); | |||
goto error; | |||
} | |||
if (b->len == 0) { | |||
/*Sending 0 bytes to SSL_write is undefined behaviour per OpenSSL documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after /*
.
* SSLSocket imitates normal socket in Python. | ||
* We have to guard against empty input here. */ | ||
Py_XDECREF(sock); | ||
return PyLong_FromLong(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This breaks implicit handshake and possible TLS 1.3 edge cases, see comment.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
But in the current module, |
I did a quick check of OpenSSL docs. There seems to be no easy way to process protocol messages without any other side-effects. Best candidate seems to be SSL_peek[_ex] but is has standard warning:
This means that we would have to save a flag in SSLSocket to call SSL_peek again with the same parameters on next calls. This would probably violate even more implicit assumtions. Also, to send data before the end of the handshake in TLS 1.3 one has to explicitly call SSL_write_early_data. Current module doesn't do this anyway. |
I recently encountered this issue and was very surprised by it. I very much agree that this patch improves on the current behavior. Can anything be done to move forward with this? |
Just encountered this problem. What's the status of this issue ? |
@tiran This still seems like an improvement to me, and I don't think your concerns are applicable to this case, but I don't want to just ignore them either... |
Thanks to PEP 644 the issue will be fixed in 3.10 by using SSL_read_ex and SSL_write_ex() functions. I couldn't use the functions earlier because Python had to support older OpenSSL versions and LibreSSL. #25468 |
SSLSocket in Python wraps ordinary sockets and provides SSLSocket.send with same behaviour (except for nonblocking differences mentiond in the documentation). Internally cPython uses OpenSSL as SSL library.
SSLSocket.send() calls to SSL_write() with data to send. However calling SSL_write() with zero-length buffer is undefined behaviour. To emulate ordinary socket behaviour in this case and not to trigger
undefined behaviour from OpenSSL we explicitly check for zero-length buffer before calling SSL_write().
https://bugs.python.org/issue31711