classification
Title: SSLObject.version returns incorrect value before handshake.
Type: behavior Stage: resolved
Components: SSL Versions: Python 3.6, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: Lukasa, christian.heimes
Priority: normal Keywords:

Created on 2017-03-10 10:03 by Lukasa, last changed 2017-09-06 23:39 by christian.heimes. This issue is now closed.

Files
File name Uploaded Description Edit
test.c Lukasa, 2017-03-10 10:28
Pull Requests
URL Status Linked Edit
PR 3364 merged christian.heimes, 2017-09-05 22:29
PR 3381 merged christian.heimes, 2017-09-06 05:04
Messages (7)
msg289346 - (view) Author: Cory Benfield (Lukasa) * Date: 2017-03-10 10:03
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.
msg289348 - (view) Author: Cory Benfield (Lukasa) * Date: 2017-03-10 10:07
A quick test reveals that Python 3.5 is also affected.
msg289350 - (view) Author: Cory Benfield (Lukasa) * Date: 2017-03-10 10:28
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.
msg289352 - (view) Author: Cory Benfield (Lukasa) * Date: 2017-03-10 10:43
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.
msg301384 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2017-09-05 22:12
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.
msg301439 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2017-09-06 04:55
New changeset 6877111648ac3e042ee5d0458cbeb65dd1a84b2d by Christian Heimes in branch 'master':
bpo-29781: Fix SSLObject.version before handshake (#3364)
https://github.com/python/cpython/commit/6877111648ac3e042ee5d0458cbeb65dd1a84b2d
msg301468 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2017-09-06 13:42
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
History
Date User Action Args
2017-09-06 23:39:28christian.heimessetstatus: open -> closed
type: behavior
resolution: fixed
stage: resolved
2017-09-06 13:47:01christian.heimeslinkissue22559 dependencies
2017-09-06 13:42:33christian.heimessetmessages: + msg301468
2017-09-06 05:04:46christian.heimessetpull_requests: + pull_request3389
2017-09-06 04:55:43christian.heimessetmessages: + msg301439
2017-09-05 22:29:23christian.heimessetpull_requests: + pull_request3375
2017-09-05 22:12:30christian.heimessetmessages: + msg301384
2017-03-10 10:43:45Lukasasetmessages: + msg289352
2017-03-10 10:29:00Lukasasetfiles: + test.c

messages: + msg289350
2017-03-10 10:07:44Lukasasetmessages: + msg289348
versions: + Python 3.5
2017-03-10 10:03:04Lukasacreate