So, I am trying to connect to an ftp server to get directory listings and download files. But the first command after the prot_p() function is raising an exception - Producing these errors from the log:
*get* '150 Here comes the directory listing.\r\n'
*resp* '150 Here comes the directory listing.'
*get* '522 SSL connection failed; session reuse required: see require_ssl_reuse
option in vsftpd.conf man page\r\n'
*resp* '522 SSL connection failed; session reuse required: see require_ssl_reuse
option in vsftpd.conf man page'
Traceback (most recent call last):
File "C:\temp\download.py", line 29, in <module>
files = ftps.dir()
File "C:\Python27\lib\ftplib.py", line 522, in dir
self.retrlines(cmd, func)
File "C:\Python27\lib\ftplib.py", line 725, in retrlines
return self.voidresp()
File "C:\Python27\lib\ftplib.py", line 224, in voidresp
resp = self.getresp()
File "C:\Python27\lib\ftplib.py", line 219, in getresp
raise error_perm, resp
ftplib.error_perm: 522 SSL connection failed; session reuse required: see requir
e_ssl_reuse option in vsftpd.conf man page
Here is the code:
from ftplib import FTP_TLS
import os
import socket
host = 'example.com'
port = 34567
user = 'user1'
passwd = 'pass123'
acct = 'Normal'
ftps = FTP_TLS()
ftps.set_debuglevel(2)
ftps.connect(host, port)
print(ftps.getwelcome())
print(ftps.sock)
ftps.auth()
ftps.login(user, passwd, acct)
ftps.set_pasv(True)
ftps.prot_p()
print('Current directory:')
print(ftps.pwd())
files = ftps.dir()
ftps.quit()
I want to do this securely, hence using FTP over TLS Explicit. I have the idea that I may need to manipulate some settings in the Socket class referenced by FTPLib. Changing the settings on the server is not a possibility. I have tested the server successfully with FileZilla client, an older version of WinSCP was raising the same error - although an upgrade to the newest version fixed it.
Any ideas?
It can be now easily fixed for Python 3.6+ by this class (descendant of FTP_TLS):
class MyFTP_TLS(ftplib.FTP_TLS):
"""Explicit FTPS, with shared TLS session"""
def ntransfercmd(self, cmd, rest=None):
conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
if self._prot_p:
conn = self.context.wrap_socket(conn,
server_hostname=self.host,
session=self.sock.session) # this is the fix
return conn, size
It looks more likely a vsftpd issue than ftplib as you mention an upgrade to the newest version fixed the problem.
Provided that you cannot touch server's settings, sub-classing the FTP_TLS
may help resolve your issue, although it is quite a HACK in my opinion, referenced to this SO question & answers Python FTP TLS connection issue. You can also take a look from this python bug issue 19500:
" It is reasonable for the server to insist that the data connection
uses a TLS cached session. This might be a cache of a previous data
connection or of a cleared control connection. If this is the reason
for the refusal to allow the data transfer, then the '522' reply
should indicate this.
Note: This has an important impact on client design, but allows
servers to minimize the cycles used during TLS negotiation by
refusing to perform a full negotiation with a previously
authenticated client."
It appears that vsftpd server implemented exactly that by enforcing
the "SSL session reuse between the control and data connection".
http://scarybeastsecurity.blogspot.com/2009/02/vsftpd-210-released.html
Looking at the source of Python core library ftplib.py, there isn't
any regard to the idea of SSL session reuse between data connection
vs. control connection (correct me if I am wrong here. I've tried
FTP_TLS.transfercmd(cmd[, rest])¶, didn't work).
This issue is well documented on other FTP clients that supports FTPS,
I.E. WinSCP: https://winscp.net/tracker/668
See test log file attached. A vsftpd server with "require_ssl_reuse"
set to true in vsftpd.conf would do the trick and can be reproduced.
Hope this helps.