geertj/gruvi

Type checking in _sslcompat broken under debian jessie

mschmitzer opened this issue · 7 comments

Using gruvi's ssl support under debian jessie produces errors from _sslcompat.c (python is 2.7.8).

Traceback:
File "/usr/lib/pymodules/python2.7/gruvi/http.py", line 1033, in connect
return super(HttpClient, self).connect(address, *_kwargs)
File "/usr/lib/pymodules/python2.7/gruvi/endpoints.py", line 259, in connect
@SwitchPoint
File "/usr/lib/pymodules/python2.7/gruvi/endpoints.py", line 269, in connect
self._connection = create_connection(self._protocol_factory, address, *_kwargs)
File "/usr/lib/pymodules/python2.7/gruvi/endpoints.py", line 69, in create_connection
family=0, flags=0, local_address=None, timeout=None, mode='rw'):
File "/usr/lib/pymodules/python2.7/gruvi/endpoints.py", line 173, in create_connection
events = transport.start(protocol)
File "/usr/lib/pymodules/python2.7/gruvi/ssl.py", line 272, in start
self.do_handshake()
File "/usr/lib/pymodules/python2.7/gruvi/ssl.py", line 441, in do_handshake
self._process_write_backlog()
File "/usr/lib/pymodules/python2.7/gruvi/ssl.py", line 326, in _process_write_backlog
ssldata, offset = self._sslpipe.do_handshake(self._ssl_active.set), 1
File "/usr/lib/pymodules/python2.7/gruvi/ssl.py", line 102, in do_handshake
self._sslobj = wrap_bio(self._context, *wrapargs)
File "/usr/lib/pymodules/python2.7/gruvi/sslcompat.py", line 227, in wrap_bio
_sslcompat.set_bio(sslobj, incoming, outgoing)
Error: expecting a ssl.SSLContext instance

Tracing what happens:

  • SSLContext is from the standard library's ssl module (sslcompat.py:42).
  • SSLContext has a '_wrap_socket' method (sslcompat.py:208, marked as 2.7.9 there)
  • SSLContext._wrap_socket returns a '_ssl._SSLSocket' instance.
  • The _SSLSocket instance is passed to sslcompat_set_bio in _sslcompat.c (sslcompat.py:227)
  • sslcompat_set_bio uses the CHECK_SSL_OBJ macro
  • CHECK_SSL_OBJ expects the type name of sslobj to be "ssl.SSLContext" (as visible in the traceback) for all python 2.7 versions and thus fails.

I'm not sure whether this is a genuine bug in gruvi or whether debian does funky stuff to the python version they're shipping. Either way, maybe the type checks in _sslcompat.c should just be removed.

@MarcSchmitzer thanks for the report.

I assume you're compiling and running this on the same machine, right?

What is sys.version_info for your Python?

Also could you try out the following snippet:

import socket, ssl
s = socket.socket()
s.connect(('www.google.com', 443))
w = ssl.wrap_socket(s)
print(w._sslobj)

Also I'm wondering if this is related to 274a798. Could you try backing out that change?

Hi,
I won't be back at work until next week, but I'll try to respond from memory.

I assume you're compiling and running this on the same machine, right?

Not the same machine, no. But both Debian Jessie.

What is sys.version_info for your Python?

2.7.8

I can't try the code snippet right now because I don't have a debian machine here.

I don't think 274a798 is to blame for this. I "fixed" the problem for myself by changing the the check in _sslcompat.c from

#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7 && PY_MICRO_VERSION < 9

to

#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7 && PY_MICRO_VERSION < 8

That is, SSL_OBJ_NAME has to be _ssl._SSLSocket for me. That wasn't the case without 274a798 either. Anyway, best as I could try the current check seems to be correct for vanilla python 2.7.8. I couldn't find the offending debian patches, though, so I'm kinda stumped.

Hmm.. There's definitely some kind of version issue going on here. In the upstream Python, we have:

  • On Python 2.7.8, SSLSocket._sslobj is an instance of "ssl.SSLContext"
  • On Python 2.7.9+, a whole bunch of SSL features got backported from 3.x, and SSLSocket._sslobj is now an instance of "_ssl._SSLSocket"

It appears that your case, the Python version is 2.7.8 (it expects ssl.SSLContext), but has some of the SSL stuff from 2.7.9 backported to it (as _sslobj is of type "_ssl._SSLSocket"). The way to confirm that would be to run the code snippet that I sent.

I could see if there's a feature based way to detect the correct type vs just checking the version. But how to do that is not immediately obvious to me as the SSL module doesn't install any headers that I can check.

Another way to do it is to accept either string in all versions. The risk of accepting the wrong object should be very low. This is probably what I'll end up doing.

I could see if there's a feature based way to detect the correct type vs just checking the version. But how to do that is not immediately obvious to me as the SSL module doesn't install any headers that I can check.

Another way to do it is to accept either string in all versions. The risk of accepting the wrong object should be very low. This is probably what I'll end up doing.

Or, do not check at all. After all, python tends to rely on duck-typing a lot, so checking for a specific type (much less a type name) may just not be such a good idea.

I'm still completely clueless as to where the difference between upstream python and the debian version comes from...

Regarding the funky check on type name: see this comment in _sslcompat:

/* Define a structure that has the same layout as PySSLObject in the _ssl
 * module. This allows us to compile this module separately from the Python
 * source tree.
 */

Unfortunately _ssl does not provide a public C level API.

Yes, this is pretty much lobotomy :) Python SSL support has greatly improved in 3.3+ and this modules does a lot of hackery to get some of those features in 2.7.

Hi, back at work now.

What is sys.version_info for your Python?

sys.version_info(major=2, minor=7, micro=8, releaselevel='final', serial=0)

Also could you try out the following snippet:

import socket, ssl
s = socket.socket()
s.connect(('www.google.com', 443))
w = ssl.wrap_socket(s)
print(w._sslobj)

<_ssl._SSLSocket object at 0x7f7c012888b0>

It seems the problem has solved itself by now. Debian has upgraded python in jessie to 2.7.9 and everything should be fine now.

Sorry for the noise.