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',

Reply via email to