pyopenssl icon indicating copy to clipboard operation
pyopenssl copied to clipboard

How to set timeout for do_handshake()?

Open vvoody opened this issue 3 years ago • 3 comments

Hello, I met a weird connection problem.

TCP connection is ok to establish with my target IP. However, it hangs for a long time at do_handshake().

Here is my code. conn.settimeout(3) and conn.setblocking(True) is used for TCP connection timeout.

import socket
from OpenSSL import crypto, SSL

ssl_context = SSL.Context(method=SSL.SSLv23_METHOD)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn = SSL.Connection(ssl_context, sock)
conn.settimeout(3)
conn.connect(("some_ip", 443))
print('> connected')
conn.setblocking(True)
print('> set to blocking')
conn.do_handshake()
print('> done handshake')

Sometimes it just resets when calls do_handshake(). This is good, which I can ignore it and continue to proceed other targets.

$ python xxx.py
> connected
> set to blocking
Traceback (most recent call last):
  File "xxx.py", line 15, in <module>
    conn.do_handshake()
  File "/usr/local/lib/python3.7/site-packages/OpenSSL/SSL.py", line 1934, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "/usr/local/lib/python3.7/site-packages/OpenSSL/SSL.py", line 1663, in _raise_ssl_error
    raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (104, 'ECONNRESET')

It hangs! This is the worst situation. I have to interrupt it.

$ python xxx.py
> connected
> set to blocking
^CTraceback (most recent call last):
  File "xxx.py", line 15, in <module>
    conn.do_handshake()
  File "/usr/local/lib/python3.7/site-packages/OpenSSL/SSL.py", line 1933, in do_handshake
    result = _lib.SSL_do_handshake(self._ssl)
KeyboardInterrupt

I did a test. do_handshake() raises exception after 300 seconds. It surprises me that it's not due to timeout.

$ time python xxx.py
> connected
> set to blocking
Traceback (most recent call last):
  File "xxx.py", line 15, in <module>
    conn.do_handshake()
  File "/usr/local/lib/python3.7/site-packages/OpenSSL/SSL.py", line 1934, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "/usr/local/lib/python3.7/site-packages/OpenSSL/SSL.py", line 1663, in _raise_ssl_error
    raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (104, 'ECONNRESET')

real	5m3.233s
user	0m0.355s
sys	0m0.028s

300 seconds is not acceptable. There are thousands of targets to scan. I cannot wait for N*300 seconds, although it's a small portion.

python:3.7.9-slim
PyOpenSSL==19.1.0

How can it make do_handshake() succeed or fail within a small amount of time?

Thanks.

vvoody avatar Dec 31 '20 15:12 vvoody

I found 300 seconds as well in openssl command.

$ time openssl s_client -connect some_ip:443
CONNECTED(00000003)
write:errno=104
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 305 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1609729737
    Timeout   : 300 (sec)   # <=== 300 seconds
    Verify return code: 0 (ok)
---

real    5m3.172s
user    0m0.004s
sys     0m0.000s

vvoody avatar Jan 04 '21 05:01 vvoody

Hey @vvoody,

I would also like to see the pyOpenSSL Connection object have a settimeout(). There is a settimeout() function within the <class 'ssl.SSLSocket'>. You can see it fire below, after running c_rehash:

#!/usr/bin/env python3
from socket import socket
import ssl

hostname = 'google.com'
port = 443
with socket() as sock:
    sock.setblocking(True)
    sock.connect((hostname, port))
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.load_verify_locations(cafile=None,
                                  cadata=None,
                                  capath="/path/to/ca_files/")
    with context.wrap_socket(sock=sock,
                             server_hostname=hostname,
                             do_handshake_on_connect=False) as ssl_sock:
        ssl_sock.settimeout(2.0)
        ssl_sock.do_handshake()
        print(hostname, ssl_sock.getpeername(), ssl_sock.version())
        print(ssl_sock)

rustymagnet3000 avatar Feb 04 '21 13:02 rustymagnet3000

Unfortunately, it is an old issue, see #168

bortzmeyer avatar Mar 02 '21 17:03 bortzmeyer