Verify that host matches certificate

When using https verify that the Common Name (CN) or
the Subject Alternative Name listed in the server's
certificate match the host we are connected to.

Addresses LP bug 1079692.

Change-Id: I24ea1511a2cbdb7c34ce72ac704d7b5e7d57cec2
This commit is contained in:
Stuart McLaren
2012-11-16 13:46:59 +00:00
parent c6b9712482
commit 7a5946fd87
7 changed files with 301 additions and 113 deletions
+43 -3
View File
@@ -243,6 +243,8 @@ class VerifiedHTTPSConnection(httplib.HTTPSConnection):
"""
Extended HTTPSConnection which uses the OpenSSL library
for enhanced SSL support.
Note: Much of this functionality can eventually be replaced
with native Python 3.3 code.
"""
def __init__(self, host, port, key_file=None, cert_file=None,
ca_file=None, timeout=None, insecure=False,
@@ -259,9 +261,47 @@ class VerifiedHTTPSConnection(httplib.HTTPSConnection):
self.setcontext()
@staticmethod
def verify_callback(connection, x509, errnum, errdepth, preverify_ok):
# Pass through OpenSSL's default result
return preverify_ok
def host_matches_cert(host, x509):
"""
Verify that the the x509 certificate we have received
from 'host' correctly identifies the server we are
connecting to, ie that the certificate's Common Name
or a Subject Alternative Name matches 'host'.
"""
# First see if we can match the CN
if x509.get_subject().commonName == host:
return True
# Also try Subject Alternative Names for a match
san_list = None
for i in xrange(x509.get_extension_count()):
ext = x509.get_extension(i)
if ext.get_short_name() == 'subjectAltName':
san_list = str(ext)
for san in ''.join(san_list.split()).split(','):
if san == "DNS:%s" % host:
return True
# Server certificate does not match host
msg = ('Host "%s" does not match x509 certificate contents: '
'CommonName "%s"' % (host, x509.get_subject().commonName))
if san_list is not None:
msg = msg + ', subjectAltName "%s"' % san_list
raise exc.SSLCertificateError(msg)
def verify_callback(self, connection, x509, errnum,
depth, preverify_ok):
if x509.has_expired():
msg = "SSL Certificate expired on '%s'" % x509.get_notAfter()
raise exc.SSLCertificateError(msg)
if depth == 0 and preverify_ok is True:
# We verify that the host matches against the last
# certificate in the chain
return self.host_matches_cert(self.host, x509)
else:
# Pass through OpenSSL's default result
return preverify_ok
def setcontext(self):
"""