Mercurial > cpython-lru_cache-weakref
changeset 87689:373797990f57
Issue #19781: ftplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
| author | Christian Heimes <christian@cheimes.de> |
|---|---|
| date | Mon, 02 Dec 2013 02:56:02 +0100 |
| parents | aa531135bc6b |
| children | 66c1c9f32567 |
| files | Doc/library/ftplib.rst Lib/ftplib.py Lib/test/test_ftplib.py Misc/NEWS |
| diffstat | 4 files changed, 51 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/Doc/library/ftplib.rst Mon Dec 02 02:41:19 2013 +0100 +++ b/Doc/library/ftplib.rst Mon Dec 02 02:56:02 2013 +0100 @@ -94,6 +94,11 @@ .. 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 @@ 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
--- a/Lib/ftplib.py Mon Dec 02 02:41:19 2013 +0100 +++ b/Lib/ftplib.py Mon Dec 02 02:56:02 2013 +0100 @@ -748,7 +748,9 @@ 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 @@ 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):
--- a/Lib/test/test_ftplib.py Mon Dec 02 02:41:19 2013 +0100 +++ b/Lib/test/test_ftplib.py Mon Dec 02 02:56:02 2013 +0100 @@ -301,7 +301,8 @@ 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 @@ 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):
--- a/Misc/NEWS Mon Dec 02 02:41:19 2013 +0100 +++ b/Misc/NEWS Mon Dec 02 02:56:02 2013 +0100 @@ -18,6 +18,9 @@ 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.
