Issue 12139: add CCC command support to FTP_TLS class to revert the SSL connection back to clear-text.
This commit is contained in:
parent
504783975b
commit
096dcb1eff
|
@ -426,6 +426,14 @@ FTP_TLS Objects
|
||||||
|
|
||||||
Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute.
|
Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute.
|
||||||
|
|
||||||
|
.. method:: FTP_TLS.ccc()
|
||||||
|
|
||||||
|
Revert control channel back to plaintex. This can be useful to take
|
||||||
|
advantage of firewalls that know how to handle NAT with non-secure FTP
|
||||||
|
without opening fixed ports.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
.. method:: FTP_TLS.prot_p()
|
.. method:: FTP_TLS.prot_p()
|
||||||
|
|
||||||
Set up secure data connection.
|
Set up secure data connection.
|
||||||
|
|
|
@ -192,6 +192,17 @@ The :mod:`ssl` module has new functions:
|
||||||
* :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes.
|
* :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes.
|
||||||
|
|
||||||
|
|
||||||
|
ftplib
|
||||||
|
------
|
||||||
|
|
||||||
|
The :class:`~ftplib.FTP_TLS` class now provides a new
|
||||||
|
:func:`~ftplib.FTP_TLS.ccc` function to revert control channel back to
|
||||||
|
plaintex. This can be useful to take advantage of firewalls that know how to
|
||||||
|
handle NAT with non-secure FTP without opening fixed ports.
|
||||||
|
|
||||||
|
(Patch submitted by Giampaolo Rodolà in :issue:`12139`.)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -708,6 +708,14 @@ else:
|
||||||
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
|
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
def ccc(self):
|
||||||
|
'''Switch back to a clear-text control connection.'''
|
||||||
|
if not isinstance(self.sock, ssl.SSLSocket):
|
||||||
|
raise ValueError("not using TLS")
|
||||||
|
resp = self.voidcmd('CCC')
|
||||||
|
self.sock = self.sock.unwrap()
|
||||||
|
return resp
|
||||||
|
|
||||||
def prot_p(self):
|
def prot_p(self):
|
||||||
'''Set up secure data connection.'''
|
'''Set up secure data connection.'''
|
||||||
# PROT defines whether or not the data channel is to be protected.
|
# PROT defines whether or not the data channel is to be protected.
|
||||||
|
|
|
@ -303,11 +303,11 @@ if ssl is not None:
|
||||||
_ssl_closing = False
|
_ssl_closing = False
|
||||||
|
|
||||||
def secure_connection(self):
|
def secure_connection(self):
|
||||||
self.del_channel()
|
|
||||||
socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
|
socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
|
||||||
certfile=CERTFILE, server_side=True,
|
certfile=CERTFILE, server_side=True,
|
||||||
do_handshake_on_connect=False,
|
do_handshake_on_connect=False,
|
||||||
ssl_version=ssl.PROTOCOL_SSLv23)
|
ssl_version=ssl.PROTOCOL_SSLv23)
|
||||||
|
self.del_channel()
|
||||||
self.set_socket(socket)
|
self.set_socket(socket)
|
||||||
self._ssl_accepting = True
|
self._ssl_accepting = True
|
||||||
|
|
||||||
|
@ -342,7 +342,10 @@ if ssl is not None:
|
||||||
# http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
|
# http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
|
||||||
pass
|
pass
|
||||||
self._ssl_closing = False
|
self._ssl_closing = False
|
||||||
super(SSLConnection, self).close()
|
if getattr(self, '_ccc', False) == False:
|
||||||
|
super(SSLConnection, self).close()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_read_event(self):
|
def handle_read_event(self):
|
||||||
if self._ssl_accepting:
|
if self._ssl_accepting:
|
||||||
|
@ -410,12 +413,18 @@ if ssl is not None:
|
||||||
def __init__(self, conn):
|
def __init__(self, conn):
|
||||||
DummyFTPHandler.__init__(self, conn)
|
DummyFTPHandler.__init__(self, conn)
|
||||||
self.secure_data_channel = False
|
self.secure_data_channel = False
|
||||||
|
self._ccc = False
|
||||||
|
|
||||||
def cmd_auth(self, line):
|
def cmd_auth(self, line):
|
||||||
"""Set up secure control channel."""
|
"""Set up secure control channel."""
|
||||||
self.push('234 AUTH TLS successful')
|
self.push('234 AUTH TLS successful')
|
||||||
self.secure_connection()
|
self.secure_connection()
|
||||||
|
|
||||||
|
def cmd_ccc(self, line):
|
||||||
|
self.push('220 Reverting back to clear-text')
|
||||||
|
self._ccc = True
|
||||||
|
self._do_ssl_shutdown()
|
||||||
|
|
||||||
def cmd_pbsz(self, line):
|
def cmd_pbsz(self, line):
|
||||||
"""Negotiate size of buffer for secure data transfer.
|
"""Negotiate size of buffer for secure data transfer.
|
||||||
For TLS/SSL the only valid value for the parameter is '0'.
|
For TLS/SSL the only valid value for the parameter is '0'.
|
||||||
|
@ -872,6 +881,15 @@ class TestTLS_FTPClass(TestCase):
|
||||||
self.assertIs(sock.context, ctx)
|
self.assertIs(sock.context, ctx)
|
||||||
self.assertIsInstance(sock, ssl.SSLSocket)
|
self.assertIsInstance(sock, ssl.SSLSocket)
|
||||||
|
|
||||||
|
def test_ccc(self):
|
||||||
|
self.assertRaises(ValueError, self.client.ccc)
|
||||||
|
self.client.login(secure=True)
|
||||||
|
self.assertIsInstance(self.client.sock, ssl.SSLSocket)
|
||||||
|
self.client.ccc()
|
||||||
|
self.assertRaises(ValueError, self.client.sock.unwrap)
|
||||||
|
self.client.sendcmd('noop')
|
||||||
|
self.client.quit()
|
||||||
|
|
||||||
|
|
||||||
class TestTimeouts(TestCase):
|
class TestTimeouts(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue