classification
Title: Client SSLSocket with select.select() always returns readable with OpenSSL 1.1.1
Type: behavior Stage: resolved
Components: SSL Versions: Python 3.9, Python 3.8, Python 3.7, Python 3.6, Python 3.5, Python 2.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: christian.heimes, ddddaaaa, seahoh
Priority: normal Keywords:

Created on 2019-07-10 03:57 by ddddaaaa, last changed 2019-12-10 08:53 by seahoh. This issue is now closed.

Files
File name Uploaded Description Edit
ssl_select.zip ddddaaaa, 2019-07-10 03:57
Messages (6)
msg347595 - (view) Author: Dean (ddddaaaa) * Date: 2019-07-10 03:57
Hi, I've come across an issue with OpenSSL 1.1.1, when a client socket wrapped using ssl.wrap_socket() is used in select.select() its always returning ready for reading even though there appears to be nothing to read.

To reproduce:
0. Extract files from attached zip
1. Run server.py
2. Run client.py

I expect client.py to print "Nothing to read" and then b'\x00\x01\x02\x03', which it does with Python 2.7.14, 3.6.8 and 3.7.3 and OpenSSL 1.0.1f and 1.1.0g. 

With OpenSSL 1.1.1 it prints 'Pending: 0' and blocks on the sock.recv(1) call.

Thanks!
msg358111 - (view) Author: Wator Sead (seahoh) Date: 2019-12-09 15:51
All new releases which include an OpenSSL version above 1.1.1 has the same issue. Can anybody fix it? Thanks a lot!
msg358115 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-12-09 16:26
The problem is caused by the way how TLS 1.3 works. Select considers a TLS 1.3 socket as readable after the handshake, because there is still data on the line. The server is sending session tickets (usually two) after the handshake has been performed. If you set "context.num_tickets = 0" in server.py or "context.maximum_version = ssl.TLSVersion.TLSv1_2" in either server.py or client.py, your script works. Ticket #37120 has more information on that.

Session tickets are low-level TLS protocol elements. Methods like SSLSocket.pending() and SSLSocket.recv() only act on high-level application protocol data. That's why pending() returns 0 and recv() is blocking. There is no application data available.

You have to take another approach and follow the guidelines in 
https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets . You also have to set the SSLSocket into non-blocking mode and handle SSLWantWriteError or SSLWantReadError.
msg358159 - (view) Author: Wator Sead (seahoh) Date: 2019-12-10 00:49
Thanks for explanation, I understand what the reason is. But why do_handshake() not clear of useless data buffer after it is completed? I think that must be easy to do.
msg358164 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-12-10 07:34
do_handshake() performs just the handshake. TLS protocol can send or receive additional protocol data after the handshake, e.g. alerts, rekeying or renegotiation. The example just happened to not trigger these in TLS 1.2. TLS makes non-blocking IO more complicated.
msg358173 - (view) Author: Wator Sead (seahoh) Date: 2019-12-10 08:53
So, that means there are some additional optional operations after do_handshake(), the data is needed. Thanks for explanation!
History
Date User Action Args
2019-12-10 08:53:47seahohsetmessages: + msg358173
2019-12-10 07:34:39christian.heimessetmessages: + msg358164
2019-12-10 00:49:11seahohsetmessages: + msg358159
2019-12-09 16:26:07christian.heimessetstatus: open -> closed
resolution: wont fix
messages: + msg358115

stage: resolved
2019-12-09 15:51:59seahohsetnosy: + seahoh

messages: + msg358111
versions: + Python 3.5, Python 3.8, Python 3.9
2019-07-10 03:57:44ddddaaaacreate