alekstorm / backports.ssl Goto Github PK
View Code? Open in Web Editor NEWUNMAINTAINED - The Python 3.4 standard `ssl` module API implemented on top of pyOpenSSL
License: Other
UNMAINTAINED - The Python 3.4 standard `ssl` module API implemented on top of pyOpenSSL
License: Other
Under Python 3, extension names come back as bytes but get_subject_alt_name
compares the name to a unicode string so the comparison fails.
I noticed the problem when connecting to imap.mail.yahoo.com. The commonName
has *.imap.mail.yahoo.com
which doesn't match the hostname and one of the subjectAltName
entries is imap.mail.yahoo.com
so that is the entry that's required. Under Python 3, the hostname check fails but it works under Python 2.
This should be easy enough to fix but I don't have time right now so I'm just filing the bug for now.
This:
import backports.ssl as ssl
import socket
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_REQUIRED
conn = context.wrap_socket(socket.socket(socket.AF_INET))
conn.connect(('google.com', 443))
print conn.getpeercert()
conn.close()
Results in:
Traceback (most recent call last):
File "test.py", line 8, in <module>
conn.connect(('google.com', 443))
File "C:\Python26\lib\site-packages\backports\ssl\core.py", line 258, in connect
self.do_handshake()
File "C:\Python26\lib\site-packages\backports\ssl\core.py", line 261, in do_handshake
_safe_ssl_call(False, self._conn, 'do_handshake')
File "C:\Python26\lib\site-packages\backports\ssl\core.py", line 222, in _safe_ssl_call
raise SSLError(*e.args)
backports.ssl.core.SSLError: [('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')]
add ssl.create_default_context
backports
A good reason to use backports.ssl is that it makes correct SNI and match_hostname support available to python programs running on RHEL 7 or CentOS 7. These OSes ship with python 2.7.5, and this support was only added into mainstream python in 2.7.9.
However, it turns out that match_hostname silently fails to decode subject_alt_name extension records if the subject_alt_name import fails:
try:
from .subject_alt_name import get_subject_alt_name
except ImportError:
get_subject_alt_name = None
This can happen, for example, if the pyasn1 library (python-pyasn1 RPM) isn't installed.
The net effect is that match_hostname erroneously fails, e.g. against domains such as pypi.python.org, which is listed as a SAN.
My RFE is that if match_hostname is being used, it should fail as early as possible if subject_alt_name isn't available.
Here's a test case:
import sys
import socket
if sys.version_info >= (2, 7, 9):
import ssl
else:
import backports.ssl as ssl
(OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE,
OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE) = (16777216, 33554432, 131072, 4194304,
1048576, 524288)
def mk_clientctx():
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.options |= OP_NO_SSLv2 | OP_NO_SSLv3 | OP_NO_COMPRESSION
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.check_hostname = True
ctx.load_verify_locations("/etc/pki/tls/certs/ca-bundle.crt")
return ctx
d = "pypi.python.org"
s = socket.socket()
s.connect((d, 443))
clientctx = mk_clientctx()
s = clientctx.wrap_socket(s, server_hostname=d)
On CentOS 7 / RHEL 7, if python-pyasn1 is available, this test case works fine. If not, you get the error:
Traceback (most recent call last):
File "test.py", line 25, in <module>
s = clientctx.wrap_socket(s, server_hostname=d)
File "/usr/lib/python2.7/site-packages/backports/ssl/core.py", line 669, in wrap_socket
self.check_hostname)
File "/usr/lib/python2.7/site-packages/backports/ssl/core.py", line 241, in __init__
self.do_handshake()
File "/usr/lib/python2.7/site-packages/backports/ssl/core.py", line 263, in do_handshake
match_hostname(self.getpeercert(), self._conn.get_servername().decode('utf-8'))
File "/usr/lib/python2.7/site-packages/backports/ssl/core.py", line 184, in match_hostname
% (hostname, dnsnames[0]))
backports.ssl.core.CertificateError: hostname u'pypi.python.org' doesn't match u'www.python.org'
PEP466 does include NPN and SNI and anything else that the 3.4 ssl module has. In it's words it proposes:
- in the ssl module:
- this module is almost entirely synchronised with its Python 3 counterpart, bringing TLSv1.2, SSLContext manipulation, Server Name Identification, access to platform certificate stores, standard library support for peer hostname validation and more to the Python 2 series.
- the only ssl module features not backported under this policy are the ssl.RAND_* functions that provide access to OpenSSL's random number generation capabilities - use os.urandom() instead.
pyopenssl raises "from time to time" SysCallError(-1, "Unexpected EOF"):
Traceback (most recent call last):
File "/usr/local/lib/python3.5/site-packages/backports/ssl/core.py", line 201, in _safe_ssl_call
return getattr(sock, call)(*args, **kwargs)
File "/usr/local/lib/python3.5/site-packages/OpenSSL/SSL.py", line 1320, in recv
self._raise_ssl_error(self._ssl, result)
File "/usr/local/lib/python3.5/site-packages/OpenSSL/SSL.py", line 1180, in _raise_ssl_error
raise SysCallError(-1, "Unexpected EOF")
OpenSSL.SSL.SysCallError: (-1, 'Unexpected EOF')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tabellarius/tabellarius.py", line 180, in <module>
exit(main())
File "/tabellarius/tabellarius.py", line 142, in main
mail_uids = imap_pool[acc].search_mails(pre_inbox, pre_inbox_search)
File "/tabellarius/imap.py", line 61, in search_mails
result = self.select_mailbox(mailbox)
File "/tabellarius/imap.py", line 53, in select_mailbox
return self.conn.select_folder(mailbox)
File "/usr/local/lib/python3.5/site-packages/imapclient/imapclient.py", line 474, in select_folder
self._command_and_check('select', self._normalise_folder(folder), readonly)
File "/usr/local/lib/python3.5/site-packages/imapclient/imapclient.py", line 1173, in _command_and_check
typ, data = meth(*args)
File "/usr/local/lib/python3.5/imaplib.py", line 729, in select
typ, dat = self._simple_command(name, mailbox)
File "/usr/local/lib/python3.5/imaplib.py", line 1180, in _simple_command
return self._command_complete(name, self._command(name, *args))
File "/usr/local/lib/python3.5/imaplib.py", line 1003, in _command_complete
typ, data = self._get_tagged_response(tag)
File "/usr/local/lib/python3.5/imaplib.py", line 1123, in _get_tagged_response
self._get_response()
File "/usr/local/lib/python3.5/imaplib.py", line 1031, in _get_response
resp = self._get_line()
File "/usr/local/lib/python3.5/imaplib.py", line 1133, in _get_line
line = self.readline()
File "/usr/local/lib/python3.5/site-packages/imapclient/tls.py", line 166, in readline
return self.file.readline()
File "/usr/local/lib/python3.5/site-packages/backports/ssl/core.py", line 540, in readline
data = _safe_ssl_call(False, self._sock, 'recv', self._rbufsize)
File "/usr/local/lib/python3.5/site-packages/backports/ssl/core.py", line 216, in _safe_ssl_call
raise SSLSysCallError(*e.args)
backports.ssl.core.SSLSysCallError: [Errno -1] Unexpected EOF
This appears on Python 3.2, 3.5 and others maybe too with the latest available version of backports.ssl and pyopenssl on pypi.
$ pip freeze
backports.ssl==0.0.7
PyYAML==3.11
six==1.9.0
wheel==0.24.0
$ python
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import backports.ssl as ssl
>>> import socket
>>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'SSLContext'
>>>
Any ideas?
It would be great if this project would include changelogs or releases. To keep track of changes when upgrading.
This:
import backports.ssl as ssl
import socket
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_REQUIRED
conn = context.wrap_socket(socket.socket(socket.AF_INET))
conn.connect(('google.com', 443))
print(conn.getpeercert()) # modified for Python3
conn.close()
Results in:
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\backports\ssl\core.py", line 201, in _safe_ssl_call
return getattr(sock, call)(*args, **kwargs)
File "C:\Python34\lib\site-packages\OpenSSL\SSL.py", line 1442, in do_handshake
self._raise_ssl_error(self._ssl, result)
File "C:\Python34\lib\site-packages\OpenSSL\SSL.py", line 1187, in _raise_ssl_error
_raise_current_error()
File "C:\Python34\lib\site-packages\OpenSSL\_util.py", line 48, in exception_from_error_queue
raise exception_type(errors)
OpenSSL.SSL.Error: [('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')]
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test_outlook_imap.py", line 8, in <module>
conn.connect(('google.com', 443))
File "C:\Python34\lib\site-packages\backports\ssl\core.py", line 258, in connect
self.do_handshake()
File "C:\Python34\lib\site-packages\backports\ssl\core.py", li�ne 261, in do_handshake
_safe_ssl_call(False, self._conn, 'do_handshake')
File "C:\Python34\lib\site-packages\backports\ssl\core.py", line 222, in _safe_ssl_call
raise SSLError(*e.args)
backports.ssl.core.SSLError: [('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')]
�
There are several significant issues here:
Depending on timing, when read() is called and the remote end terminates its SSL connection cleanly, SSLZeroReturnError will be raised to the user and the final bytes of the stream are lost forever.
data = _safe_ssl_call(False, self._sock, 'recv', maxbufsize)
if not data:
break
In the above snippet, the second line will not execute when SSLZeroReturnError is raised. data
is a local variable and its contents are lost.
There are identical code patterns which need fixing in _fileobject.readline() as well (maybe other places too, I'm not sure).
In the case size < 0, read() does not keep the contract of reading until EOF.
if size < 0:
# Read until EOF
self._rbuf = BytesIO() # reset _rbuf. we consume it via buf.
data = _safe_ssl_call(False, self._sock, 'recv', rbufsize)
buf.write(data)
return buf.getvalue()
In the above snippet, the comment is mistaken. There would have to be a loop for it to be true. No more than rbufsize additional bytes are read from the channel.
This:
import backports.ssl as ssl
import socket
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_REQUIRED
conn = context.wrap_socket(socket.socket(socket.AF_INET))
conn.connect(('google.com', 443))
print conn.getpeercert()
conn.close()
Results in:
Traceback (most recent call last):
File "ffo.py", line 4, in <module>
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
AttributeError: 'module' object has no attribute 'SSLContext'
backports.ssl.HAS_SNI
is missing:
Python 2.7.8 (default, Oct 17 2014, 10:22:45)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.54)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from backports.ssl import HAS_SNI
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name HAS_SNI
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.