Python FTP implicit TLS connection issue Python FTP implicit TLS connection issue python python

Python FTP implicit TLS connection issue


Extending the solutions that have been proposed so far, the issue is that implicit FTPS connections need the socket to be ssl wrapped automatically, before we get a chance to call login(). A lot of the subclasses that people are proposing do this in the context of the connect method, we can more generally manage this by modifying the get/set of self.sock with a property to auto-wrap on set:

import ftplibimport sslclass ImplicitFTP_TLS(ftplib.FTP_TLS):    """FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS."""    def __init__(self, *args, **kwargs):        super().__init__(*args, **kwargs)        self._sock = None    @property    def sock(self):        """Return the socket."""        return self._sock    @sock.setter    def sock(self, value):        """When modifying the socket, ensure that it is ssl wrapped."""        if value is not None and not isinstance(value, ssl.SSLSocket):            value = self.context.wrap_socket(value)        self._sock = value

Usage is essentially the same as with the standard FTP_TLS class:

ftp_client = ImplicitFTP_TLS()ftp_client.connect(host='ftp.example.com', port=990)ftp_client.login(user='USERNAME', passwd='PASSWORD')ftp_client.prot_p()


I've worked on the same problem for half a day and finally figured it out.

For the implicit FTP TLS/SSL(defualt port 990), our client program must build a TLS/SSL connection right after the socket is created. But python's class FTP_TLS doesn't reload the connect function from class FTP. We need to fix it:

class tyFTP(ftplib.FTP_TLS):  def __init__(self,               host='',               user='',               passwd='',               acct='',               keyfile=None,               certfile=None,               timeout=60):    ftplib.FTP_TLS.__init__(self,                            host=host,                            user=user,                            passwd=passwd,                            acct=acct,                            keyfile=keyfile,                            certfile=certfile,                            timeout=timeout)  def connect(self, host='', port=0, timeout=-999):    """Connect to host.  Arguments are:    - host: hostname to connect to (string, default previous host)    - port: port to connect to (integer, default previous port)    """    if host != '':        self.host = host    if port > 0:        self.port = port    if timeout != -999:        self.timeout = timeout    try:        self.sock = socket.create_connection((self.host, self.port), self.timeout)        self.af = self.sock.family        # add this line!!!        self.sock = ssl.wrap_socket(self.sock,                                    self.keyfile,                                    self.certfile,                                    ssl_version=ssl.PROTOCOL_TLSv1)        # add end        self.file = self.sock.makefile('rb')        self.welcome = self.getresp()    except Exception as e:        print(e)    return self.welcome

This derived class reloads the connect function and builds a wrapper around the socket to TLS. After you successfully connect and login to FTP server, you need to call: FTP_TLS.prot_p() before executing any FTP command!

Hope this will help ^_^


Implicit FTP over TLS with Passive transfer mode

Here's an implementation a little bit more "industrial".

Let's notice that in the previous examples the property named 'context' was missing in the init.

The code bellow works perfectly both with Python 2.7 and Python 3

The code

import ftplib, socket, sslFTPTLS_OBJ = ftplib.FTP_TLS# Class to manage implicit FTP over TLS connections, with passive transfer mode# - Important note:#   If you connect to a VSFTPD server, check that the vsftpd.conf file contains#   the property require_ssl_reuse=NOclass FTPTLS(FTPTLS_OBJ):    host = "127.0.0.1"    port = 990    user = "anonymous"    timeout = 60    logLevel = 0    # Init both this and super    def __init__(self, host=None, user=None, passwd=None, acct=None, keyfile=None, certfile=None, context=None, timeout=60):                FTPTLS_OBJ.__init__(self, host, user, passwd, acct, keyfile, certfile, context, timeout)    # Custom function: Open a new FTPS session (both connection & login)    def openSession(self, host="127.0.0.1", port=990, user="anonymous", password=None, timeout=60):        self.user = user        # connect()        ret = self.connect(host, port, timeout)        # prot_p(): Set up secure data connection.        try:            ret = self.prot_p()            if (self.logLevel > 1): self._log("INFO - FTPS prot_p() done: " + ret)        except Exception as e:            if (self.logLevel > 0): self._log("ERROR - FTPS prot_p() failed - " + str(e))            raise e        # login()        try:            ret = self.login(user=user, passwd=password)            if (self.logLevel > 1): self._log("INFO - FTPS login() done: " + ret)        except Exception as e:            if (self.logLevel > 0): self._log("ERROR - FTPS login() failed - " + str(e))            raise e        if (self.logLevel > 1): self._log("INFO - FTPS session successfully opened")    # Override function    def connect(self, host="127.0.0.1", port=990, timeout=60):        self.host = host        self.port = port        self.timeout = timeout        try:            self.sock = socket.create_connection((self.host, self.port), self.timeout)            self.af = self.sock.family            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)            self.file = self.sock.makefile('r')            self.welcome = self.getresp()            if (self.logLevel > 1): self._log("INFO - FTPS connect() done: " + self.welcome)        except Exception as e:            if (self.logLevel > 0): self._log("ERROR - FTPS connect() failed - " + str(e))            raise e        return self.welcome    # Override function    def makepasv(self):        host, port = FTPTLS_OBJ.makepasv(self)        # Change the host back to the original IP that was used for the connection        host = socket.gethostbyname(self.host)        return host, port    # Custom function: Close the session    def closeSession(self):        try:            self.close()            if (self.logLevel > 1): self._log("INFO - FTPS close() done")        except Exception as e:            if (self.logLevel > 0): self._log("ERROR - FTPS close() failed - " + str(e))            raise e        if (self.logLevel > 1): self._log("INFO - FTPS session successfully closed")    # Private method for logs    def _log(self, msg):        # Be free here on how to implement your own way to redirect logs (e.g: to a console, to a file, etc.)        print(msg)

Usage example

host = "www.myserver.com"port = 990user = "myUserId"password = "myPassword"myFtps = FTPTLS()myFtps.logLevel = 2myFtps.openSession(host, port, user, password)print(myFtps.retrlines("LIST"))myFtps.closeSession()

Output example

INFO - FTPS connect() done: 220 (vsFTPd 3.0.2)INFO - FTPS prot_p() done: 200 PROT now Private.INFO - FTPS login() done: 230 Login successful.INFO - FTPS session successfully opened-rw-------    1 ftp      ftp         86735 Mar 22 16:55 MyModel.yaml-rw-------    1 ftp      ftp          9298 Mar 22 16:55 MyData.csv226 Directory send OK.INFO - FTPS close() doneINFO - FTPS session successfully closed