You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "j-verse (Jira)" <ji...@apache.org> on 2020/09/11 09:53:00 UTC

[jira] [Comment Edited] (NET-687) FTP data connection error Unsupported or unrecognized SSL message. Probably another ssl_reuse session error ?

    [ https://issues.apache.org/jira/browse/NET-687?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17194169#comment-17194169 ] 

j-verse edited comment on NET-687 at 9/11/20, 9:52 AM:
-------------------------------------------------------

The issue is caused by the following commit:
 [https://github.com/apache/commons-net/commit/c660afa96016f6cd2de356439b58fa9c4443d467]

This commit moves the SSL socket creation (from a normal socket) to a separate function in order to also use it when opening the data connection:
{code:java}
    @Override
    protected Socket _openDataConnection_(String command, String arg)
            throws IOException {
        Socket socket = super._openDataConnection_(command, arg);
        socket = createSSLSocket(socket);
        _prepareDataSocket_(socket);
        if (socket instanceof SSLSocket) {
            SSLSocket sslSocket = (SSLSocket)socket;            sslSocket.setUseClientMode(isClientMode);
            sslSocket.setEnableSessionCreation(isCreation);            // server mode
            if (!isClientMode) {
                sslSocket.setNeedClientAuth(isNeedClientAuth);
                sslSocket.setWantClientAuth(isWantClientAuth);
            }
            if (suites != null) {
                sslSocket.setEnabledCipherSuites(suites);
            }
            if (protocols != null) {
                sslSocket.setEnabledProtocols(protocols);
            }
            sslSocket.startHandshake();
        }        return socket;
    }
{code}
This might seem logical, but it is incorrect. The super call to _openDataConnection_ internally uses the socket factory to obtain a new socket. When we want the data connection to also use SSL, we execute a PROT P command to the server. This is done use the execPROT function:
{code:java}
    public void execPROT(String prot) throws SSLException, IOException {
        if (prot == null) {
            prot = DEFAULT_PROT;
        }
        if (!checkPROTValue(prot)) {
            throw new IllegalArgumentException();
        }
        if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
            throw new SSLException(getReplyString());
        }
        if (DEFAULT_PROT.equals(prot)) {
            setSocketFactory(null);
            setServerSocketFactory(null);
        } else {
            setSocketFactory(new FTPSSocketFactory(context));
            setServerSocketFactory(new FTPSServerSocketFactory(context));
            initSslContext();
        }
    }
{code}
This function sets the socket factory to be a FTPSSocketFactory when prot is P (P indicates we want an encrypted data channel, DEFAULT_PROT is C). The FTPSSocketFactory already returns SSL sockets when asked to build a new socket. The line added in the mentioned commit when opening the data connection thus builds a SSL socket over what is already a SSL socket. This causes the first SSL handshake to trigger another SSL handshake on the stacked SSL sockets and this eventually leads to the "Unsupported or unrecognized SSL message" exception because one of them tries to interpret application data as a handshake message.

Removing the line added in the mentioned commit fixes the issue. I've opened a pull request:
 [https://github.com/apache/commons-net/pull/59]

 


was (Author: jelle.evers@cofano.nl):
The issue is caused by the following commit:
[https://github.com/apache/commons-net/commit/c660afa96016f6cd2de356439b58fa9c4443d467]

This commit moves the SSL socket creation (from a normal socket) to a separate function in order to also use it when opening the data connection:
{code:java}
    @Override
    protected Socket _openDataConnection_(String command, String arg)
            throws IOException {
        Socket socket = super._openDataConnection_(command, arg);
        socket = createSSLSocket(socket);
        _prepareDataSocket_(socket);
        if (socket instanceof SSLSocket) {
            SSLSocket sslSocket = (SSLSocket)socket;            sslSocket.setUseClientMode(isClientMode);
            sslSocket.setEnableSessionCreation(isCreation);            // server mode
            if (!isClientMode) {
                sslSocket.setNeedClientAuth(isNeedClientAuth);
                sslSocket.setWantClientAuth(isWantClientAuth);
            }
            if (suites != null) {
                sslSocket.setEnabledCipherSuites(suites);
            }
            if (protocols != null) {
                sslSocket.setEnabledProtocols(protocols);
            }
            sslSocket.startHandshake();
        }        return socket;
    }
{code}
This might seem logical, but it is incorrect. The super call to _openDataConnection_ internally uses the socket factory to obtain a new socket. When we want the data connection to also use SSL, we execute a PROT P command to the server. This is done use the execPROT function:
{code:java}
    public void execPROT(String prot) throws SSLException, IOException {
        if (prot == null) {
            prot = DEFAULT_PROT;
        }
        if (!checkPROTValue(prot)) {
            throw new IllegalArgumentException();
        }
        if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
            throw new SSLException(getReplyString());
        }
        if (DEFAULT_PROT.equals(prot)) {
            setSocketFactory(null);
            setServerSocketFactory(null);
        } else {
            setSocketFactory(new FTPSSocketFactory(context));
            setServerSocketFactory(new FTPSServerSocketFactory(context));
            initSslContext();
        }
    }
{code}
This function sets the socket factory to be a FTPSSocketFactory when prot is P (DEFAULT_PROT is C). The FTPSSocketFactory already returns SSL sockets when asked to build a new socket. The line added in the mentioned commit when opening the data connection thus builds a SSL socket over what is already a SSL socket. This causes the first SSL handshake to trigger another SSL handshake on the stacked SSL sockets and this eventually leads to the "Unsupported or unrecognized SSL message" exception because one of them tries to interpret application data as a handshake message.

Removing the line added in the mentioned commit fixes the issue. I've opened a pull request:
[https://github.com/apache/commons-net/pull/59]

 

> FTP data connection error Unsupported or unrecognized SSL message. Probably another ssl_reuse session error ?
> -------------------------------------------------------------------------------------------------------------
>
>                 Key: NET-687
>                 URL: https://issues.apache.org/jira/browse/NET-687
>             Project: Commons Net
>          Issue Type: Bug
>          Components: FTP
>    Affects Versions: 3.7
>         Environment: Tested with JDK 8, 11, 13, 14
>            Reporter: Mikael
>            Priority: Major
>
> After adding the self signed polynesie.cer certificate to JVM security (_jdk-x.x.x/lib/security_) :
> {code:java}
> keytool.exe -import -storepass "changeit" -keystore "./cacerts" -alias polynesie.cer -file ./polynesie.cer -noprompt{code}
>  polynesie.cer obtained by copying certificate part from this command line result :
> {code:java}
> openssl s_client -connect ftp0.gov.pf:21 -starttls ftp{code}
> Trying to retrieve a file with ftpes :
> {code:java}
> java -cp commons-net-examples-3.5.jar;commons-net-3.5.jar examples/ftp/FTPClientExample -A -p TLS,false -e -b ftp0.gov.pf DataVRS/fiche_Station_VRS_VAI1.pdf fiche_Station_VRS_VAI1.pdf{code}
> Produce this exception :
> {code:java}
> javax.net.ssl.SSLException: Unsupported or unrecognized SSL message
>         at java.base/sun.security.ssl.SSLSocketInputRecord.handleUnknownRecord(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketInputRecord.decode(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
>         at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
>         at org.apache.commons.net.ftp.FTPSClient._openDataConnection_(FTPSClient.java:642)
>         at org.apache.commons.net.ftp.FTPClient._retrieveFile(FTPClient.java:1907)
>         at org.apache.commons.net.ftp.FTPClient.retrieveFile(FTPClient.java:1893)
>         at testFTP2.FTPClientExample.main(FTPClientExample.java:513)
> {code}
> It is probably the same error of ssl_reuse session as NET-408.
> Same try with ftp4j library reports this error :
> {code:java}
> code=522, message= SSL connection failed; session reuse required: see require_ssl_reuse option in vsftpd.conf man page
> {code}
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)