]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #19782: imaplib now supports SSLContext.check_hostname and server name
authorChristian Heimes <christian@cheimes.de>
Mon, 2 Dec 2013 19:01:29 +0000 (20:01 +0100)
committerChristian Heimes <christian@cheimes.de>
Mon, 2 Dec 2013 19:01:29 +0000 (20:01 +0100)
indication for TLS/SSL connections.

Doc/library/imaplib.rst
Lib/imaplib.py
Lib/test/test_imaplib.py
Misc/NEWS

index 01236fbb8ea5ae7f676b184d27da78664bde90fd..be2f599b0c4ce3197a18ce401379f07fd2c477a8 100644 (file)
@@ -80,6 +80,10 @@ There's also a subclass for secure connections:
    .. versionchanged:: 3.3
       *ssl_context* parameter added.
 
+   .. versionchanged:: 3.4
+      The class now supports hostname check with
+      :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
+      :data:`~ssl.HAS_SNI`).
 
 The second subclass allows for connections created by a child process:
 
@@ -437,6 +441,10 @@ An :class:`IMAP4` instance has the following methods:
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.4
+      The method now supports hostname check with
+      :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
+      :data:`~ssl.HAS_SNI`).
 
 .. method:: IMAP4.status(mailbox, names)
 
index dabf161b87aabce1546cf9f663c9b8baa5075201..ade2f9c2aa0573e0de09993d65d838e9d5210790 100644 (file)
@@ -745,7 +745,9 @@ class IMAP4:
             ssl_context = ssl._create_stdlib_context()
         typ, dat = self._simple_command(name)
         if typ == 'OK':
-            self.sock = ssl_context.wrap_socket(self.sock)
+            server_hostname = self.host if ssl.HAS_SNI else None
+            self.sock = ssl_context.wrap_socket(self.sock,
+                                                server_hostname=server_hostname)
             self.file = self.sock.makefile('rb')
             self._tls_established = True
             self._get_capabilities()
@@ -1216,7 +1218,9 @@ if HAVE_SSL:
 
         def _create_socket(self):
             sock = IMAP4._create_socket(self)
-            return self.ssl_context.wrap_socket(sock)
+            server_hostname = self.host if ssl.HAS_SNI else None
+            return self.ssl_context.wrap_socket(sock,
+                                                server_hostname=server_hostname)
 
         def open(self, host='', port=IMAP4_SSL_PORT):
             """Setup connection to remote server on "host:port".
index 81bfd1fbc2d7a9064b92940e18e45e758b6e5f1d..bafd62b63d2f7a5bb86be53adde9ba8b2355d9c1 100644 (file)
@@ -20,6 +20,7 @@ except ImportError:
     ssl = None
 
 CERTFILE = None
+CAFILE = None
 
 
 class TestImaplib(unittest.TestCase):
@@ -348,6 +349,25 @@ class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests):
     server_class = SecureTCPServer
     imap_class = IMAP4_SSL
 
+    @reap_threads
+    def test_ssl_verified(self):
+        ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        ssl_context.verify_mode = ssl.CERT_REQUIRED
+        ssl_context.check_hostname = True
+        ssl_context.load_verify_locations(CAFILE)
+
+        with self.assertRaisesRegex(ssl.CertificateError,
+                                    "hostname '127.0.0.1' doesn't match 'localhost'"):
+            with self.reaped_server(SimpleIMAPHandler) as server:
+                client = self.imap_class(*server.server_address,
+                                         ssl_context=ssl_context)
+                client.shutdown()
+
+        with self.reaped_server(SimpleIMAPHandler) as server:
+            client = self.imap_class("localhost", server.server_address[1],
+                                     ssl_context=ssl_context)
+            client.shutdown()
+
 
 class RemoteIMAPTest(unittest.TestCase):
     host = 'cyrus.andrew.cmu.edu'
@@ -460,11 +480,15 @@ def load_tests(*args):
 
     if support.is_resource_enabled('network'):
         if ssl:
-            global CERTFILE
+            global CERTFILE, CAFILE
             CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
-                                    "keycert.pem")
+                                    "keycert3.pem")
             if not os.path.exists(CERTFILE):
                 raise support.TestFailed("Can't read certificate files!")
+            CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
+                                 "pycacert.pem")
+            if not os.path.exists(CAFILE):
+                raise support.TestFailed("Can't read CA file!")
         tests.extend([
             ThreadedNetworkedTests, ThreadedNetworkedTestsSSL,
             RemoteIMAPTest, RemoteIMAP_SSLTest, RemoteIMAP_STARTTLSTest,
index 9b71e13d8bfbb8638c39b6f4b542fc98cd10a9e4..3785840afcaa436d77188ce8f29b2659ad22cd96 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #19782: imaplib now supports SSLContext.check_hostname and server name
+  indication for TLS/SSL connections.
+
 - Issue #19834: Support unpickling of exceptions pickled by Python 2.
 
 - Issue #19781: ftplib now supports SSLContext.check_hostname and server name