If using match_hostname, subject_alt_name support should be mandatory
jim-minter opened this issue · comments
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'