pyftpdlib icon indicating copy to clipboard operation
pyftpdlib copied to clipboard

Add Implicit SSL support.

Open giampaolo opened this issue 11 years ago • 13 comments

From [email protected] on March 19, 2011 02:18:11

Currently pyftpdlib provides SSL/TLS support in the form of Explicit SSL. 
http://en.wikipedia.org/wiki/FTPS#Explicit This mode requires the client to 
secure the channel after connecting. It is generally more flexible and useful 
that Implicit SSL. However, many users desire Implicit SSL. 
http://en.wikipedia.org/wiki/FTPS#Implicit Supporting this feature makes it 
easier for the user to configure their FTP client (choose any SSL option and it 
just works).

That said, I wonder how hard it would be to implement this feature. I see a 
couple of hurdles.

1. The asyncore.dispatcher only allows a bind() to a single address/port.
2. If a second FTPServer (FTPImplicitSSLServer) instance is used, how does it 
participate in the asyncore.loop()?

The rest is simply modifying the Handlers (FTPHandler, DTPHandler) to 
immediately secure the channel.

Original issue: http://code.google.com/p/pyftpdlib/issues/detail?id=160

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 21, 2011 12:38:07

This is actually trivial to implement. I have only two small issues.

Example:
--
class SSLImplicitFTPHandler(FTPHandler):
    def handle(self):
        self.secure_connection(self.ssl_context)

    def handle_ssl_established(self):
        FTPHandler.handle(self)

    def ftp_AUTH(self, arg):
        self.respond("550 not supposed to be used with implicit SSL.")

    ftpes = ftpserver.FTPServer(('0.0.0.0', 21))
    ftpis = ftpserver.FTPServer(('0.0.0.0', 990))
    ftpes.serve_forever()
-- Issue 1 : Upon timeout, Filezilla reports the following.

15:17:37    Status: Directory listing successful
15:22:37    Response:   421 Control connection timed out.
15:22:37    Error:  GnuTLS error -9: A TLS packet with unexpected length was received.
15:22:37    Status: Server did not properly shut down TLS connection
15:22:37    Error:  Could not read from socket: ECONNABORTED - Connection aborted
15:22:37    Error:  Disconnected from server

You can see this occurs 5 minutes after the last successful command. Something 
is not being shut down properly server-side in response to a timeout. Issue 2 : 
This is trivial, however, calling serve_forever() on one of the FTPServer 
instances seems a bit hacky, perhaps this function could be a module-level 
function (ftpserver.serve_forever()). The FTPServer class could then call this 
and issue a deprecation warning. I think this might make more sense in the case 
of running multiple FTP servers in the same process, there are actually a 
couple scenarios for doing this: multiple ports, multiple IPs, implicit SSL 
(actually a variation of multiple ports).

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 21, 2011 12:41:05

Sorry, my example had indentation issues due to me copying/pasting from 
different locations in a .py script.

Example:
--
class SSLImplicitFTPHandler(ftpserver.FTPHandler):
    def handle(self):
        self.secure_connection(self.ssl_context)

    def handle_ssl_established(self):
        FTPHandler.handle(self)

    def ftp_AUTH(self, arg):
        self.respond("550 not supposed to be used with implicit SSL.")

ftpes = ftpserver.FTPHandler(('0.0.0.0', 21))
ftpis = SSLImplicitFTPHandler(('0.0.0.0', 990))
ftpes.serve_forever()
--

giampaolo avatar May 28 '14 15:05 giampaolo

From g.rodola on March 22, 2011 05:36:57

> Upon timeout, Filezilla reports the following.

It seems we should invoke SSL shutdown() also for the control connection.
I opened issue 162 for this and fixed it in r840 .

+1 on adding a new ImplicitTLS_FTPHandler class.
+0, for now, about a module level serve_forever() method because it occurred to 
me there are implications with max_cons and max_cons_per_ip options and also 
with close_all().

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 22, 2011 07:37:19

Brilliant! That change resolves the SSL timeout issue.

The other issue is so low priority it is not worth worrying about.

Thanks.

giampaolo avatar May 28 '14 15:05 giampaolo

From g.rodola on March 22, 2011 07:42:23

;)

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 22, 2011 07:48:14

Here is a simple patch to provide an ImplicitTLS_Handler class.

Attachment: contrib-handlers-implicit_tls_handler.patch

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 22, 2011 08:37:44

A simple patch adding ImplicitTLS_FTPHandler support to demo/tls_ftpd.py.

Attachment: demo-tls_ftpd-implicit_tls_handler.patch

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 23, 2011 13:10:11

Here is a patch that includes the above two patches (with some fixes) and a 
modified unit test suite.

3 tests are failing, but this is likely due to the FTP_TLS client and not the 
server itself.

I will fix these tests when I have some time.

Attachment: implicit-ssl.patch

giampaolo avatar May 28 '14 15:05 giampaolo

From [email protected] on March 28, 2011 11:14:31

I am going to need some help with these failing tests. I have been working on 
it for a few hours. I tried to extrude the FTPd class and the FTPISClient class 
into separate test files and they work together. They just don't apparently 
work under the test suite. It always hangs on the client at the 
ssl.wrapsocket() call.

giampaolo avatar May 28 '14 15:05 giampaolo

Hello, Any update on this issue? Can we raise the priority? So far we have used this as a workaround: http://stackoverflow.com/questions/12164470/python-ftp-tls-connection-issue but that is giving us problems after upgrade to Debian Jessie and python 2.7.9:

 File "/usr/lib/python2.7/ftplib.py", line 752, in storbinary
    conn.unwrap()
  File "/usr/lib/python2.7/ssl.py", line 771, in unwrap
    s = self._sslobj.shutdown()
socket.error: [Errno 0] Error

python3.4.2

  File "/usr/lib/python3.4/ftplib.py", line 740, in login
    return FTP.login(self, user, passwd, acct)
  File "/usr/lib/python3.4/ftplib.py", line 417, in login
    resp = self.sendcmd('USER ' + user)
  File "/usr/lib/python3.4/ftplib.py", line 272, in sendcmd
    return self.getresp()
  File "/usr/lib/python3.4/ftplib.py", line 235, in getresp
    resp = self.getmultiline()
  File "/usr/lib/python3.4/ftplib.py", line 221, in getmultiline
    line = self.getline()
  File "/usr/lib/python3.4/ftplib.py", line 212, in getline
    elif line[-1:] in CRLF:

mrinterestfull avatar Jul 08 '15 04:07 mrinterestfull

I also have a need for this feature. Any progress or recipe where we can make it work?

davidkhess avatar Oct 22 '20 20:10 davidkhess

If this is still available on a branch somewhere, I'm willing to work on it as a PR.

davidkhess avatar Oct 22 '20 21:10 davidkhess