Skip to content

FtpWebRequest not reusing ssl session on linux (FTPS) #27916

@cluetjen

Description

@cluetjen

It seems that FtpWebRequest on Linux does not reuse ssl sessions for FTPS passive data connections and for this reason does not work with the currently recommended ftp server setups.

Expected
An ftp client must reuse the same ssl session that he used for the control connection for the data connection too. That's the current default configuration for most ftps servers.

Problem
On Linux FtpWebClient creates a new ssl session for the data connection and the connection fails.

Test code

        [Fact]
        public async Task We_should_be_able_to_list_files_with_ftpwebrequest()
        {
            ServicePointManager
                    .ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;

            // Get the object used to communicate with the server.
            var request = (FtpWebRequest)WebRequest.Create("ftp://localhost/");
            request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            request.EnableSsl = true;
            request.UsePassive = true;

            // This example assumes the FTP site uses anonymous logon.
            request.Credentials = new NetworkCredential("test", "test");

            FtpWebResponse response = (FtpWebResponse)request.GetResponse();

            var responseStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(responseStream);

            var text = reader.ReadToEnd();

            Console.WriteLine($"Directory List Complete, status {response.StatusDescription}");

            reader.Close();
            response.Close();

            Assert.NotEmpty(text);
        }

Result:

[xUnit.net 00:00:03.4876467]     FileTransfer.Tests.FileZillaTests.We_should_be_able_to_list_files_with_ftpwebrequest [FAIL]
Fehler FileTransfer.Tests.FileZillaTests.We_should_be_able_to_list_files_with_ftpwebrequest
Fehlermeldung:
 System.Net.WebException : The remote server returned an error: 150 Opening data channel for directory listing of "/"
.
---- System.IO.IOException : Authentication failed because the remote party has closed the transport stream.
Stapelverfolgung:
   at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
   at System.Net.CommandStream.InvokeRequestCallback(Object obj)
   at System.Net.CommandStream.Abort(Exception e)
   at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
   at System.Net.FtpWebRequest.GetResponse()
   at FileTransfer.Tests.FileZillaTests.We_should_be_able_to_list_files_with_ftpwebrequest() in /mnt/c/workspace/lib-cs-filetransfer/FileTransfer.Tests/FileZillaTests.cs:line 45
--- End of stack trace from previous location where exception was thrown ---
----- Inner Stack Trace -----
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at System.Net.TlsStream.AuthenticateAsClient()
   at System.Net.FtpControlStream.QueueOrCreateFtpDataStream(Stream& stream)
   at System.Net.FtpControlStream.PipelineCallback(PipelineEntry entry, ResponseDescription response, Boolean timeout, Stream& stream)
   at System.Net.CommandStream.PostReadCommandProcessing(Stream& stream)
   at System.Net.CommandStream.PostSendCommandProcessing(Stream& stream)
   at System.Net.CommandStream.ContinueCommandPipeline()
   at System.Net.FtpWebRequest.TimedSubmitRequestHelper(Boolean isAsync)
   at System.Net.FtpWebRequest.SubmitRequest(Boolean isAsync)

FileZilla FTP server logs:

(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> Connected on port 21, sending welcome message...
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> 220-FileZilla Server 0.9.60 beta
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> 220-written by Tim Kosse (tim.kosse@filezilla-project.org)
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> 220 Please visit https://filezilla-project.org/
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> AUTH TLS
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> 234 Using authentication type TLS
(000015)14.11.2018 23:26:12 - (not logged in) (127.0.0.1)> TLS connection established
(000015)14.11.2018 23:26:13 - (not logged in) (127.0.0.1)> USER test
(000015)14.11.2018 23:26:13 - (not logged in) (127.0.0.1)> 331 Password required for test
(000015)14.11.2018 23:26:13 - (not logged in) (127.0.0.1)> PASS ****
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 230 Logged on
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> PBSZ 0
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 200 PBSZ=0
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> PROT P
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 200 Protection level set to P
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> OPTS utf8 on
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 202 UTF8 mode is always enabled. No need to send this command.
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> PWD
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 257 "/" is current directory.
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> TYPE I
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 200 Type set to I
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> PASV
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,212,245)
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> LIST
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 150 Opening data channel for directory listing of "/"
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> 450 TLS session of data connection has not resumed or the session does not match the control connection
(000015)14.11.2018 23:26:13 - test (127.0.0.1)> disconnected.
  • Only Linux (tested with Ubuntu 16.04, 18.04 on WSL and native)
  • Tested with FileZilla FTP server current version
  • Tested with dotnet core 2.1 and 2.2 preview 3

Steps to reproduce

  • Install FileZilla server (https://filezilla-project.org/)
  • Add user "test", PW "test" and any shared folder in FileZilla
  • Create xunit project in VS mit FluentFTP nuget package
  • Add test from this issue
  • open bash (or install ubuntu from windows store)
  • install dotnet sdk in bash (https://www.microsoft.com/net/download)
  • cd /mnt/c/... wherevery your vs project is
  • dotnet test

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.NetenhancementProduct code improvement that does NOT require public API changes/additionsos-linuxLinux OS (any supported distro)

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions