Created on 2017-03-10 10:03 by Lukasa, last changed 2022-04-11 14:58 by admin. This issue is now closed.
The SSLObject object from the ssl module has a version() method that is undocumented. A reasonable assumption for the behaviour of that method is that it would follow the behaviour of the same method on SSLSocket(), which has the following documentation: > Return the actual SSL protocol version negotiated by the connection as > a string, or None is no secure connection is established. As of this > writing, possible return values include "SSLv2", "SSLv3", "TLSv1", > "TLSv1.1" and "TLSv1.2". Recent OpenSSL versions may define more return > values. However, SSLObject does not follow that behaviour: Python 3.6.0 (default, Jan 18 2017, 18:08:34) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import ssl >>> ctx = ssl.create_default_context() >>> in_bio = ssl.MemoryBIO() >>> out_bio = ssl.MemoryBIO() >>> buffers = ctx.wrap_bio(in_bio, out_bio) >>> buffers.version() 'TLSv1.2' That is, a SSLObject that does not have a TLS session established will incorrectly report that it is using a TLS version. This method should return None in this case.
A quick test reveals that Python 3.5 is also affected.
This actually appears to be an outcome of OpenSSL's logic. I've attached a smallish C file that, when run against OpenSSL 1.0.2 on my machine, prints "TLSv1.2". This seems like a behaviour we'll have to work around in Python to get the outcome we want here.
I updated the test script to try with a file-descriptor set and OpenSSL returns TLSv1.2 for that one as well. This strongly suggests that OpenSSL's SSL_get_version documentation is somewhat misleading, and that an SSL object will return a version even when it's not connected. If Python wants to consider this a bug, it will need to track connections state for the SSLObject like it does for the SSLSocket. Otherwise, Python can redocument version for SSLObject to say that it will always return a value.
It should be possible to solve the issue w/o tracking the connection state manually. It doesn't work correctly with transparent negotiation -- that is implicit handshake with SSL_write(). SSL_is_init_finished() (https://www.openssl.org/docs/manmaster/man3/SSL_get_state.html) might be the right function.
New changeset 6877111648ac3e042ee5d0458cbeb65dd1a84b2d by Christian Heimes in branch 'master': bpo-29781: Fix SSLObject.version before handshake (#3364) https://github.com/python/cpython/commit/6877111648ac3e042ee5d0458cbeb65dd1a84b2d
New changeset 6da379bde345926e1f7318ead973767f4d791d3e by Christian Heimes in branch '3.6': [3.6] bpo-29781: Fix SSLObject.version before handshake (GH-3364) (#3381) https://github.com/python/cpython/commit/6da379bde345926e1f7318ead973767f4d791d3e
messages: + msg289350