summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2013-12-02 01:56:02 (GMT)
committerChristian Heimes <christian@cheimes.de>2013-12-02 01:56:02 (GMT)
commite5b5edfa2ccf2c031be156a03267dc5629feda77 (patch)
treeaadbba4b4cfadf99b963a7db9e18d8fa2bb6c08d
parent1aa9a75fbff2333fd07574e3de8710c629483258 (diff)
downloadcpython-e5b5edfa2ccf2c031be156a03267dc5629feda77.zip
cpython-e5b5edfa2ccf2c031be156a03267dc5629feda77.tar.gz
cpython-e5b5edfa2ccf2c031be156a03267dc5629feda77.tar.bz2
Issue #19781: ftplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
-rw-r--r--Doc/library/ftplib.rst10
-rw-r--r--Lib/ftplib.py8
-rw-r--r--Lib/test/test_ftplib.py33
-rw-r--r--Misc/NEWS3
4 files changed, 51 insertions, 3 deletions
diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst
index dcb2ac4..7f98d0b 100644
--- a/Doc/library/ftplib.rst
+++ b/Doc/library/ftplib.rst
@@ -94,6 +94,11 @@ The module defines the following items:
.. versionchanged:: 3.3
*source_address* parameter was added.
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
+ :data:`~ssl.HAS_SNI`).
+
Here's a sample session using the :class:`FTP_TLS` class:
>>> from ftplib import FTP_TLS
@@ -427,6 +432,11 @@ FTP_TLS Objects
Set up secure control connection by using TLS or SSL, depending on what
specified in :meth:`ssl_version` attribute.
+ .. versionchanged:: 3.4
+ The method now supports hostname check with
+ :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
+ :data:`~ssl.HAS_SNI`).
+
.. method:: FTP_TLS.ccc()
Revert control channel back to plaintext. This can be useful to take
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index 1b16e0a..2cc4702 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -748,7 +748,9 @@ else:
resp = self.voidcmd('AUTH TLS')
else:
resp = self.voidcmd('AUTH SSL')
- self.sock = self.context.wrap_socket(self.sock)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ self.sock = self.context.wrap_socket(self.sock,
+ server_hostname=server_hostname)
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
return resp
@@ -787,7 +789,9 @@ else:
def ntransfercmd(self, cmd, rest=None):
conn, size = FTP.ntransfercmd(self, cmd, rest)
if self._prot_p:
- conn = self.context.wrap_socket(conn)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ conn = self.context.wrap_socket(conn,
+ server_hostname=server_hostname)
return conn, size
def abort(self):
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 41463e2..15458a8 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -301,7 +301,8 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
if ssl is not None:
- CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
+ CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
+ CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
class SSLConnection(asyncore.dispatcher):
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
@@ -923,6 +924,36 @@ class TestTLS_FTPClass(TestCase):
self.client.ccc()
self.assertRaises(ValueError, self.client.sock.unwrap)
+ def test_check_hostname(self):
+ self.client.quit()
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+ ctx.verify_mode = ssl.CERT_REQUIRED
+ ctx.check_hostname = True
+ ctx.load_verify_locations(CAFILE)
+ self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
+
+ # 127.0.0.1 doesn't match SAN
+ self.client.connect(self.server.host, self.server.port)
+ with self.assertRaises(ssl.CertificateError):
+ self.client.auth()
+ # exception quits connection
+
+ self.client.connect(self.server.host, self.server.port)
+ self.client.prot_p()
+ with self.assertRaises(ssl.CertificateError):
+ with self.client.transfercmd("list") as sock:
+ pass
+ self.client.quit()
+
+ self.client.connect("localhost", self.server.port)
+ self.client.auth()
+ self.client.quit()
+
+ self.client.connect("localhost", self.server.port)
+ self.client.prot_p()
+ with self.client.transfercmd("list") as sock:
+ pass
+
class TestTimeouts(TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index 07f8668..39785a7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Core and Builtins
Library
-------
+- Issue #19781: ftplib now supports SSLContext.check_hostname and server name
+ indication for TLS/SSL connections.
+
- Issue #19509: Add SSLContext.check_hostname to match the peer's certificate
with server_hostname on handshake.