Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyOpenSSL set timeout on do_handshake()

Tags:

pyopenssl

With PyOpenSSL ( version 20.0.0 ), is it possible to set a timeout on do_handshake() call when socket.setblocking(False)?

I have a hostname that responds to a socket ( so you get an IP address). But when I call do_handshake() the server never answers the Client Hello. I don't control the server.

If I set the sock.setblocking(True) the do_handshake() works great with normal endpoints. But then I hit my bad host ( good socket, no TLS ). It fails after ~ 35 seconds with OpenSSL.SSL.Error. Makes sense. The error is ('SSL routines', 'ssl3_get_record', 'wrong version number').

I tried setting the timeout in the OpenSSL.Context:

context = Context(TLSv1_2_METHOD)
context.set_timeout(5)

That didn't work. I tried setting a timeout on the socket:

sock.setblocking(False)
sock.settimeout(3.0)

I think that could work. But, that always fires OpenSSL.SSL.WantReadError when I call do_handshake():

tls_client = Connection(verifier.context, s.sock)
tls_client.set_tlsext_host_name(bytes(s.host, 'utf-8'))
tls_client.set_connect_state()        # set to work in client mode
try:
   tls_client.do_handshake()
except WantReadError:
    print("[!]WantReadError.  Only generated with sock.setblocking(False)")

So I am stuck waiting for ~35 seconds with the following settings to avoid the WantReadError:

self.sock.setblocking(True)
#self.sock.settimeout(3.0)

openssl s_client shows the debug flow:

openssl s_client -CApath ${CERTS} -state -nbio -connect foo.bar.com:443            
CONNECTED(00000007)
Turned on non blocking io
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:error in SSLv3/TLS write client hello
write R BLOCK
like image 236
rustyMagnet Avatar asked Sep 03 '26 12:09

rustyMagnet


1 Answers

I met the exact problem (no comments yet). Mine is getting reset after 300 seconds.

After struggling for a few days, I use standard ssl lib to handle timeout before PyOpenSSL code:

import ssl
import socket

_ssl_ctx = ssl.SSLContext()
_ssl_ctx.verify_mode = ssl.CERT_NONE
_ssl_ctx.check_hostname = False
_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
_sock.settimeout(1)
_ssl_sock = _ssl_ctx.wrap_socket(_sock, do_handshake_on_connect=False)
try:
    _ssl_sock.connect(("host", 443))
    _ssl_sock.do_handshake()
except Exception as e:
    print(str(e))
finally:
    _ssl_sock.close()

#
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn = SSL.Connection(ssl_context, sock)
conn.settimeout(1)  # socket timeout for establishing connection
conn.connect(("host", 443))
conn.setblocking(True)
conn.do_handshake()
conn.get_peer_cert_chain()
like image 97
vvoody Avatar answered Sep 05 '26 15:09

vvoody



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!