6

I try to connect to a FTP Server which only supports TLS 1.2 Using Python 3.4.1

My Code:

import ftplib
import ssl
ftps = ftplib.FTP_TLS()

ftps.ssl_version = ssl.PROTOCOL_TLSv1_2
print (ftps.connect('108.61.166.122',31000))
print(ftps.login('test','test123'))
ftps.prot_p()
print (ftps.retrlines('LIST'))

Error on client side:

ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:598)

Error on server side:

  Failed TLS negotiation on control channel, disconnected. (SSL_accept(): 
  error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol)

The credentials in the example are working for testing.

W0bble
  • 757
  • 1
  • 8
  • 24

2 Answers2

3

See the end of this post for the final solution. The rest are the steps needed to debug the problem.

I try to connect to a FTP Server which only supports TLS 1.2 Using Python 3.4.1

How do you know?

ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:598)

I would suggest one of the many SSL problems between client and server, like the server not supporting TLS 1.2, no common ciphers etc. These problems are hard to debug because you either get only some SSL alert or the server will simply close the connection without any obvious reason. If you have access to the server look for error messages on the server side.

You may also try to not to enforce an SSL version but use the default instead, so that client and server will agree to the best SSL version both support. If this will still not work try with a client which is known to work with this server and make a packet capture of the good and bad connections and compare. If you need help with that post the packet captures to cloudshark.org.

Edit#1: just tried it with python 3.4.0 and 3.4.2 against a test server:

  • python 3.4.0 does a TLS 1.0 handshake, i.e. ignores the setting
  • python 3.4.2 does a successful TLS 1.2 handshake

In both versions ftplib has the minor bug, that it sends AUTH SSL instead of AUTH TLS if ftps.ssl_version is something else then TLS 1.0, i.e. SSLv3 or TLS1.1.+. While I doubt that this is the origin of the problem it might actually be if the FTP server handles AUTH TLS and AUTH SSL differently.

Edit#2 and Solution:

A packet capture shows that setting ftps.ssl_version has no effect and the SSL handshake will still be done with TLS 1.0 only. Looking at the source code of ftplib in 3.4.0 gives:

    ssl_version = ssl.PROTOCOL_TLSv1

    def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
                 certfile=None, context=None,
                 timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
        ....
        if context is None:
            context = ssl._create_stdlib_context(self.ssl_version,
                                                 certfile=certfile,
                                                 keyfile=keyfile)
        self.context = context

Since __init__ is called when ftplib.FTP_TLS() is called the SSL context will be created with the default ssl_version used by ftplib (ssl.PROTOCOL_TLSv1) and not with your own version. To enforce another SSL version you must to provide your own context with the needed SSL version. The following works for me:

import ftplib
import ssl

ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1_2)
ftps = ftplib.FTP_TLS(context=ctx)

print (ftps.connect('108.61.166.122',31000))
print(ftps.login('test','test123'))
ftps.prot_p()
print (ftps.retrlines('LIST'))

Alternatively you could set the protocol version globally instead of only for this FTP_TLS object:

ftplib.FTP_TLS.ssl_version = ssl.PROTOCOL_TLSv1_2
ftps = ftplib.FTP_TLS()

And just a small but important observation: it looks like that ftplib does not do any kind of certificate validation, since it accepts this self-signed certificate which does not match the name without complaining. This makes a active man-in-the-middle attack possible. Hopefully they will fix this insecure behavior in the future, in which case the code here will fail because of an invalid certificate.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • I know it because I set it up. When I remove the line: ftps.ssl_version = ssl.PROTOCOL_TLSv1_2 I get ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:598 – W0bble May 16 '15 at 14:04
  • A properly setup server will negotiate the used protocol. What kind of server is this? How is it setup? Can you see warnings or errors on the server side? Do any other clients work with this server? – Steffen Ullrich May 16 '15 at 14:10
  • It is a glftpd server and every other client like filezilla ftprush etc work. (All clients which use the standard openssl libraries) It is defenitly a python problem. Error on server side: Failed TLS negotiation on control channel, disconnected. (SSL_accept(): (1) error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol) – W0bble May 16 '15 at 14:19
  • Could you provide a packet capture? This "unknown protocol" on the server side should only happen if the client sends bogus data, according to the OpenSSL source. – Steffen Ullrich May 16 '15 at 14:58
  • Thank you very much. The thing with accepting any certificate is only because its a test setup for stackoverflow with no productive use – W0bble May 26 '15 at 16:28
  • this is a huge help @SteffenUllrich – root May 11 '18 at 20:13
0

Firstly AFAIK no ftp supports SSL directly, for which ftps is introduced. Also sftp and ftps are two different concepts: http://en.wikipedia.org/wiki/FTPS .Now, your problem is regarding the programming and not related to SSL or FTPs or any such client-server communication

import ftplib
import ssl
ftps = ftplib.FTP_TLS()

#ftps.ssl_version = ssl.PROTOCOL_TLSv1_2
print (ftps.connect('108.61.166.122',31000))
print(ftps.login('test','test123'))
ftps.prot_p()
print (ftps.retrlines('LIST'))

as ftplib has no attribute PROTOCOL_TLSv1_2 besides which it works fine. and well, your host is not responding !

Hopefully it helps !

kedarkhetia
  • 101
  • 9