alekstorm / backports.ssl

UNMAINTAINED - The Python 3.4 standard `ssl` module API implemented on top of pyOpenSSL

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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'