Fix hostname validation in the SSL verification code (CVE-2012-3446). Reported by researchers
from the University of Texas at Austin (Martin Georgiev, Suman Jana and Vitaly Shmatikov). 
For more info, see http://libcloud.apache.org/security.html.


git-svn-id: https://svn.apache.org/repos/asf/libcloud/branches/0.11.x@1368323 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/CHANGES b/CHANGES
index 9e9b4c5..ad39981 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,14 @@
                                    -*- coding: utf-8 -*-
 
+Changes with Apache Libcloud 0.11.1:
+
+  *) General
+
+    - Fix hostname validation in the SSL verification code (CVE-2012-3446).
+
+      Reported by researchers from the University of Texas at Austin (Martin
+      Georgiev, Suman Jana and Vitaly Shmatikov).
+
 Changes with Apache Libcloud 0.11.0:
 
   *) Compute
@@ -139,7 +148,7 @@
       CloudFiles driver.
       [Tomaz Muraus]
 
-    - Fix a bug with content_type and and encoding of object and path names in 
+    - Fix a bug with content_type and and encoding of object and path names in
       the Atmos driver.
       [Russell Keith-Magee]
 
@@ -234,7 +243,7 @@
 
     - Add ex_rescue and ex_unrescue method to OpenStack 1.1 driver. ;
       LIBCLOUD-193
-      [Shawn Smith] 
+      [Shawn Smith]
 
     - Include 'password' in the node extra dictionary when calling deploy_node
       if the password auth is used.
@@ -419,7 +428,7 @@
      - Enable ex_delete_image method in the OpenStack 1.1 driver.
        [Shawn Smith]
 
-     - Return NodeImage instance in OpenStack 1.1 driver ex_save_image method 
+     - Return NodeImage instance in OpenStack 1.1 driver ex_save_image method
        ; LIBCLOUD-138
        [Shawn Smith]
 
diff --git a/libcloud/__init__.py b/libcloud/__init__.py
index ed68165..a3421fd 100644
--- a/libcloud/__init__.py
+++ b/libcloud/__init__.py
@@ -20,7 +20,7 @@
 """
 
 __all__ = ['__version__', 'enable_debug']
-__version__ = '0.11.0'
+__version__ = '0.11.1'
 
 try:
     import paramiko
diff --git a/libcloud/httplib_ssl.py b/libcloud/httplib_ssl.py
index 012e191..8e6cf56 100644
--- a/libcloud/httplib_ssl.py
+++ b/libcloud/httplib_ssl.py
@@ -122,13 +122,8 @@
         # replace * with alphanumeric and dash
         # replace . with literal .
         valid_patterns = [
-            re.compile(
-                pattern.replace(
-                    r".", r"\."
-                ).replace(
-                    r"*", r"[0-9A-Za-z]+"
-                )
-            )
+            re.compile('^' + pattern.replace(r".", r"\.") \
+                                    .replace(r"*", r"[0-9A-Za-z]+") + '$')
             for pattern in (set(common_name) | set(alt_names))]
 
         return any(
diff --git a/libcloud/test/test_httplib_ssl.py b/libcloud/test/test_httplib_ssl.py
index 6c8d48e..dbbe866 100644
--- a/libcloud/test/test_httplib_ssl.py
+++ b/libcloud/test/test_httplib_ssl.py
@@ -45,16 +45,49 @@
          'subjectAltName': ((('DNS', 'foo.alt.name')),
                            (('DNS', 'foo.alt.name.1')))}
 
+        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'),))}
+
         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))
+
     def test_get_subject_alt_names(self):
         cert1 = {'notAfter': 'Feb 16 16:54:50 2013 GMT',
          'subject': ((('countryName', 'US'),),