Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python FTP implicit TLS connection issue

I have a need to connect to FTPS server to which I am able to connect successfully using lftp. However, when I try with Python ftplib.FTP_TLS, it times out, the stack trace shows that it is waiting for the server to send welcome message or like. Does anyone know what the issue is and how to overcome? I wonder if there is something needs to be done on server side, but how come lftp client is working fine. Any help is greatly appreciated.

Here is the stack trace:

    ftp = ftplib.FTP_TLS()  
    ftp.connect(cfg.HOST, cfg.PORT, timeout=60)
  File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 135, in connect  
    self.welcome = self.getresp()  
  File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 210, in getresp  
    resp = self.getmultiline()  
  File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 196, in getmultiline  
    line = self.getline()  
  File "C:\Users\username\Softwares\Python27\lib\ftplib.py", line 183, in getline  
    line = self.file.readline()  
  File "C:\Users\username\Softwares\Python27\lib\socket.py", line 447, in readline  
    data = self._sock.recv(self._rbufsize)  
socket.timeout: timed out  

A successful login using lftp to the same ftps server:

$ lftp
lftp :~> open ftps://ip_address:990
lftp ip_address:~> set ftps:initial-prot P
lftp ip_address:~> login ftps_user_id  ftps_user_passwd
lftp sftp_user_id@ip_address:~> ls
ls: Fatal error: SSL_connect: self signed certificate
lftp ftps_user_id@ip_address:~> set ssl:verif-certificate off
lftp ftps_user_id@ip_address:~> ls
lftp ftps_user_id@ip_address:/>

BTW, I am using Python 2.7.3. I did quite a bit of search using Google but have not found anything helpful.

I am still having this issue, appreciate if someone can help. On looking closely the FTP.connect() the connection to server is not a problem but getting acknowledgement (or the welcome message) from server is an issue. lftp does not have this issue and FileZilla does not have any issue either as in the log here -

Status: Connecting to xx.xx.xx.xxx:990...  
Status: Connection established, initializing TLS...  
Status: Verifying certificate...  
Status: TLS/SSL connection established, waiting for welcome message...  
Response:   220-      Vous allez vous connecter sur un serveur prive  
Response:   220-     Seules les personnes habilitees y sont autorisees  
Response:   220 Les contrevenants s'exposent aux poursuites prevues par la loi.  
Command:    USER xxxxxxxxxxxxx  
Response:   331 Password required for xxxxxxxxxxxxx.  
Command:    PASS **********  
Response:   230 Login OK. Proceed.  
Command:    PBSZ 0  
Response:   200 PBSZ Command OK. Protection buffer size set to 0.  
Command:    PROT P  
Response:   200 PROT Command OK. Using Private data connection  
Status: Connected  
Status: Retrieving directory listing...  
Command:    PWD  
Response:   257 "/" is current folder.  
Command:    TYPE I  
Response:   200 Type set to I.  
Command:    PASV  
Response:   227 Entering Passive Mode (81,93,20,199,4,206).  
Command:    MLSD  
Response:   150 Opening BINARY mode data connection for MLSD /.  
Response:   226 Transfer complete. 0 bytes transferred. 0 bps.  
Status: Directory listing successful  
like image 755
Rg Glpj Avatar asked Aug 28 '12 17:08

Rg Glpj


People also ask

Does FTP support TLS?

The FTP client can implement TLS security by itself, or the FTP client can be configured to use Application Transparent Transport Layer Security (AT-TLS) as a controlling application. For more information on AT-TLS, see Application Transparent Transport Layer Security data protection.

What does Ftplib do Python?

This module defines the class FTP and a few related items. The FTP class implements the client side of the FTP protocol. You can use this to write Python programs that perform a variety of automated FTP jobs, such as mirroring other FTP servers.

How do I connect to an FTP server using Python?

The ftplib module included in Python allows you to use Python scripts to quickly attach to an FTP server, locate files, and then download them to be processed locally. To open a connection to the FTP server, create an FTP server object using the ftplib. FTP([host [, user [, passwd]]]) method.


2 Answers

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 ftplib import ssl  class 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() 
like image 152
George L-W Avatar answered Sep 20 '22 09:09

George L-W


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 ^_^

like image 37
tandztc Avatar answered Sep 19 '22 09:09

tandztc