summaryrefslogtreecommitdiff
path: root/glanceclient/common/https.py
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-03-04 08:38:19 +0000
committerGerrit Code Review <review@openstack.org>2015-03-04 08:38:19 +0000
commit5ce1de9da47d0874ee2bf920fdb421c750bc1e89 (patch)
treee0e9db0c9c06e874d02366fa282eab62deffad17 /glanceclient/common/https.py
parentd8d6cd5dda55d3a745f0db5f3f3740f0cd7a1507 (diff)
parentef9fd9fca05f8da8325ccaa6632e34d1321130bf (diff)
downloadpython-glanceclient-5ce1de9da47d0874ee2bf920fdb421c750bc1e89.tar.gz
Merge "https: Prevent leaking sockets for some operations"
Diffstat (limited to 'glanceclient/common/https.py')
-rw-r--r--glanceclient/common/https.py133
1 files changed, 77 insertions, 56 deletions
diff --git a/glanceclient/common/https.py b/glanceclient/common/https.py
index af962b8..f7f5f68 100644
--- a/glanceclient/common/https.py
+++ b/glanceclient/common/https.py
@@ -52,6 +52,82 @@ except ImportError:
from glanceclient import exc
+def verify_callback(host=None):
+ """
+ We use a partial around the 'real' verify_callback function
+ so that we can stash the host value without holding a
+ reference on the VerifiedHTTPSConnection.
+ """
+ def wrapper(connection, x509, errnum,
+ depth, preverify_ok, host=host):
+ return do_verify_callback(connection, x509, errnum,
+ depth, preverify_ok, host=host)
+ return wrapper
+
+
+def do_verify_callback(connection, x509, errnum,
+ depth, preverify_ok, host=None):
+ """
+ Verify the server's SSL certificate.
+
+ This is a standalone function rather than a method to avoid
+ issues around closing sockets if a reference is held on
+ a VerifiedHTTPSConnection by the callback function.
+ """
+ if x509.has_expired():
+ msg = "SSL Certificate expired on '%s'" % x509.get_notAfter()
+ raise exc.SSLCertificateError(msg)
+
+ if depth == 0 and preverify_ok:
+ # We verify that the host matches against the last
+ # certificate in the chain
+ return host_matches_cert(host, x509)
+ else:
+ # Pass through OpenSSL's default result
+ return preverify_ok
+
+
+def host_matches_cert(host, x509):
+ """
+ Verify that 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'.
+ """
+ def check_match(name):
+ # Directly match the name
+ if name == host:
+ return True
+
+ # Support single wildcard matching
+ if name.startswith('*.') and host.find('.') > 0:
+ if name[2:] == host.split('.', 1)[1]:
+ return True
+
+ common_name = x509.get_subject().commonName
+
+ # First see if we can match the CN
+ if check_match(common_name):
+ return True
+ # Also try Subject Alternative Names for a match
+ san_list = None
+ for i in range(x509.get_extension_count()):
+ ext = x509.get_extension(i)
+ if ext.get_short_name() == b'subjectAltName':
+ san_list = str(ext)
+ for san in ''.join(san_list.split()).split(','):
+ if san.startswith('DNS:'):
+ if check_match(san.split(':', 1)[1]):
+ return True
+
+ # Server certificate does not match host
+ msg = ('Host "%s" does not match x509 certificate contents: '
+ 'CommonName "%s"' % (host, common_name))
+ if san_list is not None:
+ msg = msg + ', subjectAltName "%s"' % san_list
+ raise exc.SSLCertificateError(msg)
+
+
def to_bytes(s):
if isinstance(s, six.string_types):
return six.b(s)
@@ -197,61 +273,6 @@ class VerifiedHTTPSConnection(HTTPSConnection):
except excp_lst as e:
raise exc.SSLConfigurationError(str(e))
- @staticmethod
- def host_matches_cert(host, x509):
- """
- Verify that 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'.
- """
- def check_match(name):
- # Directly match the name
- if name == host:
- return True
-
- # Support single wildcard matching
- if name.startswith('*.') and host.find('.') > 0:
- if name[2:] == host.split('.', 1)[1]:
- return True
-
- common_name = x509.get_subject().commonName
-
- # First see if we can match the CN
- if check_match(common_name):
- return True
- # Also try Subject Alternative Names for a match
- san_list = None
- for i in range(x509.get_extension_count()):
- ext = x509.get_extension(i)
- if ext.get_short_name() == b'subjectAltName':
- san_list = str(ext)
- for san in ''.join(san_list.split()).split(','):
- if san.startswith('DNS:'):
- if check_match(san.split(':', 1)[1]):
- return True
-
- # Server certificate does not match host
- msg = ('Host "%s" does not match x509 certificate contents: '
- 'CommonName "%s"' % (host, common_name))
- 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:
- # 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 set_context(self):
"""
Set up the OpenSSL context.
@@ -264,7 +285,7 @@ class VerifiedHTTPSConnection(HTTPSConnection):
if self.insecure is not True:
self.context.set_verify(OpenSSL.SSL.VERIFY_PEER,
- self.verify_callback)
+ verify_callback(host=self.host))
else:
self.context.set_verify(OpenSSL.SSL.VERIFY_NONE,
lambda *args: True)