Issue #19781: ftplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
This commit is contained in:
parent
1aa9a75fbf
commit
e5b5edfa2c
|
@ -94,6 +94,11 @@ The module defines the following items:
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
*source_address* parameter was added.
|
*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:
|
Here's a sample session using the :class:`FTP_TLS` class:
|
||||||
|
|
||||||
>>> from ftplib import FTP_TLS
|
>>> 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
|
Set up secure control connection by using TLS or SSL, depending on what
|
||||||
specified in :meth:`ssl_version` attribute.
|
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()
|
.. method:: FTP_TLS.ccc()
|
||||||
|
|
||||||
Revert control channel back to plaintext. This can be useful to take
|
Revert control channel back to plaintext. This can be useful to take
|
||||||
|
|
|
@ -748,7 +748,9 @@ else:
|
||||||
resp = self.voidcmd('AUTH TLS')
|
resp = self.voidcmd('AUTH TLS')
|
||||||
else:
|
else:
|
||||||
resp = self.voidcmd('AUTH SSL')
|
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)
|
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
@ -787,7 +789,9 @@ else:
|
||||||
def ntransfercmd(self, cmd, rest=None):
|
def ntransfercmd(self, cmd, rest=None):
|
||||||
conn, size = FTP.ntransfercmd(self, cmd, rest)
|
conn, size = FTP.ntransfercmd(self, cmd, rest)
|
||||||
if self._prot_p:
|
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
|
return conn, size
|
||||||
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
|
|
|
@ -301,7 +301,8 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
|
||||||
|
|
||||||
if ssl is not None:
|
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):
|
class SSLConnection(asyncore.dispatcher):
|
||||||
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
|
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
|
||||||
|
@ -923,6 +924,36 @@ class TestTLS_FTPClass(TestCase):
|
||||||
self.client.ccc()
|
self.client.ccc()
|
||||||
self.assertRaises(ValueError, self.client.sock.unwrap)
|
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):
|
class TestTimeouts(TestCase):
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #19509: Add SSLContext.check_hostname to match the peer's certificate
|
||||||
with server_hostname on handshake.
|
with server_hostname on handshake.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue