Repository: libcloud Updated Branches: refs/heads/trunk 5fb73ba18 -> 34b7e02cf
Use backports.ssl_match_hostname, instead of having our own logic Closes #374 Signed-off-by: Tomaz Muraus <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/6362ce99 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/6362ce99 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/6362ce99 Branch: refs/heads/trunk Commit: 6362ce99d371cf0b66211eeff43a084449fd6098 Parents: 5fb73ba Author: Alex Gaynor <[email protected]> Authored: Thu Oct 16 10:02:20 2014 -0700 Committer: Tomaz Muraus <[email protected]> Committed: Wed Nov 19 23:31:12 2014 +0800 ---------------------------------------------------------------------- CHANGES.rst | 9 +++ libcloud/httplib_ssl.py | 57 ++------------- libcloud/test/test_httplib_ssl.py | 126 --------------------------------- setup.py | 7 +- 4 files changed, 19 insertions(+), 180 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/6362ce99/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index 5a6f1be..3a41e83 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,15 @@ Changelog Changes with Apache Libcloud in development ------------------------------------------- +General +~~~~~~~ + +- Use ``match_hostname`` function from ``backports.ssl_match_hostname`` + package to verify the SSL certificate hostname instead of relying on + our own logic. + (GITHUB-374) + [Alex Gaynor] + Compute ~~~~~~~ http://git-wip-us.apache.org/repos/asf/libcloud/blob/6362ce99/libcloud/httplib_ssl.py ---------------------------------------------------------------------- diff --git a/libcloud/httplib_ssl.py b/libcloud/httplib_ssl.py index 7787e7b..4e4188f 100644 --- a/libcloud/httplib_ssl.py +++ b/libcloud/httplib_ssl.py @@ -17,12 +17,13 @@ Subclass for httplib.HTTPSConnection with optional certificate name verification, depending on libcloud.security settings. """ import os -import re import socket import ssl import base64 import warnings +from backports.ssl_match_hostname import match_hostname, CertificateError + import libcloud.security from libcloud.utils.py3 import b from libcloud.utils.py3 import httplib @@ -277,55 +278,7 @@ class LibcloudHTTPSConnection(httplib.HTTPSConnection, LibcloudBaseConnection): ca_certs=self.ca_cert, ssl_version=ssl.PROTOCOL_TLSv1) cert = self.sock.getpeercert() - if not self._verify_hostname(self.host, cert): + try: + match_hostname(cert, self.host) + except CertificateError: raise ssl.SSLError('Failed to verify hostname') - - def _verify_hostname(self, hostname, cert): - """ - Verify hostname against peer cert - - Check both commonName and entries in subjectAltName, using a - rudimentary glob to dns regex check to find matches - """ - common_name = self._get_common_name(cert) - alt_names = self._get_subject_alt_names(cert) - - # replace * with alphanumeric and dash - # replace . with literal . - # http://www.dns.net/dnsrd/trick.html#legal-hostnames - valid_patterns = [ - re.compile('^' + pattern.replace(r".", r"\.") - .replace(r"*", r"[0-9A-Za-z\-]+") + '$') - for pattern in (set(common_name) | set(alt_names))] - - return any( - pattern.search(hostname) - for pattern in valid_patterns - ) - - def _get_subject_alt_names(self, cert): - """ - Get SubjectAltNames - - Retrieve 'subjectAltName' attributes from cert data structure - """ - if 'subjectAltName' not in cert: - values = [] - else: - values = [value - for field, value in cert['subjectAltName'] - if field == 'DNS'] - return values - - def _get_common_name(self, cert): - """ - Get Common Name - - Retrieve 'commonName' attribute from cert data structure - """ - if 'subject' not in cert: - return None - values = [value[0][1] - for value in cert['subject'] - if value[0][0] == 'commonName'] - return values http://git-wip-us.apache.org/repos/asf/libcloud/blob/6362ce99/libcloud/test/test_httplib_ssl.py ---------------------------------------------------------------------- diff --git a/libcloud/test/test_httplib_ssl.py b/libcloud/test/test_httplib_ssl.py index 5308eee..65e676c 100644 --- a/libcloud/test/test_httplib_ssl.py +++ b/libcloud/test/test_httplib_ssl.py @@ -67,132 +67,6 @@ class TestHttpLibSSLTests(unittest.TestCase): self.assertEqual(libcloud.security.CA_CERTS_PATH, [file_path]) - def test_verify_hostname(self): - # commonName - cert1 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'somemachine.python.org'),))} - - # commonName - cert2 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'somemachine.python.org'),)), - 'subjectAltName': ((('DNS', 'foo.alt.name')), - (('DNS', 'foo.alt.name.1')))} - - # commonName - cert3 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'python.org'),))} - - # wildcard commonName - cert4 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', '*.api.joyentcloud.com'),))} - - self.assertFalse(self.httplib_object._verify_hostname( - hostname='invalid', cert=cert1)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='machine.python.org', cert=cert1)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='foomachine.python.org', cert=cert1)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='somesomemachine.python.org', cert=cert1)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='somemachine.python.orga', cert=cert1)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='somemachine.python.org.org', cert=cert1)) - self.assertTrue(self.httplib_object._verify_hostname( - hostname='somemachine.python.org', cert=cert1)) - - self.assertFalse(self.httplib_object._verify_hostname( - hostname='invalid', cert=cert2)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='afoo.alt.name.1', cert=cert2)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='a.foo.alt.name.1', cert=cert2)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='foo.alt.name.1.2', cert=cert2)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='afoo.alt.name.1.2', cert=cert2)) - self.assertTrue(self.httplib_object._verify_hostname( - hostname='foo.alt.name.1', cert=cert2)) - - self.assertTrue(self.httplib_object._verify_hostname( - hostname='python.org', cert=cert3)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='opython.org', cert=cert3)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='ython.org', cert=cert3)) - - self.assertTrue(self.httplib_object._verify_hostname( - hostname='us-east-1.api.joyentcloud.com', cert=cert4)) - self.assertTrue(self.httplib_object._verify_hostname( - hostname='useast-1.api.joyentcloud.com', cert=cert4)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='t1.useast-1.api.joyentcloud.com', cert=cert4)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='ponies.useast-1.api.joyentcloud.com', cert=cert4)) - self.assertFalse(self.httplib_object._verify_hostname( - hostname='api.useast-1.api.joyentcloud.com', cert=cert4)) - - def test_get_subject_alt_names(self): - cert1 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'somemachine.python.org'),))} - - cert2 = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'somemachine.python.org'),)), - 'subjectAltName': ((('DNS', 'foo.alt.name')), - (('DNS', 'foo.alt.name.1')))} - - self.assertEqual(self.httplib_object._get_subject_alt_names(cert=cert1), - []) - - alt_names = self.httplib_object._get_subject_alt_names(cert=cert2) - self.assertEqual(len(alt_names), 2) - self.assertTrue('foo.alt.name' in alt_names) - self.assertTrue('foo.alt.name.1' in alt_names) - - def test_get_common_name(self): - cert = {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', 'US'),), - (('stateOrProvinceName', 'Delaware'),), - (('localityName', 'Wilmington'),), - (('organizationName', 'Python Software Foundation'),), - (('organizationalUnitName', 'SSL'),), - (('commonName', 'somemachine.python.org'),))} - - self.assertEqual(self.httplib_object._get_common_name(cert)[0], - 'somemachine.python.org') - self.assertEqual(self.httplib_object._get_common_name({}), - None) - @patch('warnings.warn') def test_setup_verify(self, _): libcloud.security.CA_CERTS_PATH = [] http://git-wip-us.apache.org/repos/asf/libcloud/blob/6362ce99/setup.py ---------------------------------------------------------------------- diff --git a/setup.py b/setup.py index cbc2e74..b8daaa4 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ from setuptools import setup from distutils.core import Command from unittest import TextTestRunner, TestLoader from glob import glob -from subprocess import call from os.path import splitext, basename, join as pjoin try: @@ -217,6 +216,10 @@ class CoverageCommand(Command): forbid_publish() +install_requires = ['backports.ssl_match_hostname'] +if pre_python26: + install_requires.extend(['ssl', 'simplejson']) + setup( name='apache-libcloud', version=read_version_string(), @@ -225,7 +228,7 @@ setup( ' and documentation, please see http://libcloud.apache.org', author='Apache Software Foundation', author_email='[email protected]', - requires=([], ['ssl', 'simplejson'],)[pre_python26], + install_requires=install_requires, packages=get_packages('libcloud'), package_dir={ 'libcloud': 'libcloud',
