Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] - TITLE - FTPS - TLS session of data connection not resumed error while using FileZilla server #93

Open
fervand1 opened this issue Nov 2, 2024 · 2 comments
Assignees
Labels
question Further information is requested

Comments

@fervand1
Copy link

fervand1 commented Nov 2, 2024

Is this bug solved by Suppaftp mattnenterprise/rust-ftp#96 ? The library is not maintained so I thought to use Suppaftp

I wrote a simple program with suppaftp to connect to filezilla ftps server version 1.9.2 and list directory but I get below error

<Date/Time> Info [Type] Message
<02-11-2024 12:10:21> FTP Session 106 127.0.0.1 testuser [Error] TLS session of data connection not resumed error.

Following is the script I tried to run

use suppaftp::{NativeTlsFtpStream, NativeTlsConnector};
use suppaftp::native_tls::TlsConnector;

fn main() {
    // Connect to the FTP server in passive mode
    let ftp_stream = NativeTlsFtpStream::connect("127.0.0.1:21").unwrap();

    let tls_connector = TlsConnector::builder()
        .danger_accept_invalid_certs(true) // Only for testing; not recommended in production
        .build()
        .unwrap();

    // Convert the connector into the type expected by suppaftp
    let native_tls_connector = NativeTlsConnector::from(tls_connector);

    // Switch to secure mode using the connector with modified validation settings
    let mut ftp_stream = ftp_stream.into_secure(native_tls_connector, "127.0.0.1").unwrap();

    // Log in to the FTP server
    ftp_stream.login("testuser", "test$123456").unwrap();
    
    // List files in the current directory
    match ftp_stream.list(None) {
        Ok(files) => {
            for file in files {
                println!("{}", file);
            }
        }
        Err(e) => {
            eprintln!("Failed to list files: {}", e);
        }
    }

    // Quit the FTP session
    assert!(ftp_stream.quit().is_ok());
}

My cargo.toml looks like this

[package]
name = "ftp_client"
version = "0.1.0"
edition = "2021"

[dependencies]
suppaftp = { version = "^6", features = ["native-tls"] }
native-tls = "0.2.12"
openssl = { version = "0.10.68", features = ["vendored"] }

The login to the server happens fine. The error occurs when you call commands like to list directory or any other file actions.
Before raising a bug , I wanted ask a question, if this is solved or not yet.

Note: The server is working fine because the same when I tried with node js code am able to do the directory listing, so it looks like to me the library lacks support for this feature. Can you confirm or am I doing something wrong? Thanks

@fervand1 fervand1 added the question Further information is requested label Nov 2, 2024
@leontoeides
Copy link

leontoeides commented Nov 3, 2024

I'm not sure if this is related to fervand1's issue or not, but I am also having issues with FileZilla.

When retrieving files, sometimes errors such as: Invalid response: [226] 226 Operation successful and Connection error: No connection could be made because the target machine actively refused it. (os error 10061) are returned.

This seems to happen when FileZilla responds with 150 About to start data transfer.

I think suppaftp is supposed to chill out on this response.

However, immediately afterward suppaftp sends PASV which seemingly messes things up?

FileZilla complains about this and says A command is already being processed. Queuing the new one until the current one is finished.

I believe that at this point suppaftp returns Connection error: No connection could be made to my program

@fervand1
Copy link
Author

fervand1 commented Nov 3, 2024

@leontoeides I spent few days on this trying to understand why it is happening. And seems like the issue is with session resumption support. Am not an expert on this topic but here is what I found and hopefully might help you as well
Suppaftp supports FTP via TLS through native-tls and rustls. After researching a bit I found out that the native-tls crate doesn't have support for session resumption and hence we are seeing the behaviour where upon successful login it disconnects when we try to list or do any other file/dir related actions.

Rustls on the other hand has the support for this function which filezilla server is applying by default refer https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.resumption . Following is the script that worked for me. FYI ignore the implementation of bypass with NoVerifier (will implement it proper to accept a proper certificate or rootstore)

use std::sync::Arc;
use suppaftp::{RustlsFtpStream, RustlsConnector};
use suppaftp::rustls::{ClientConfig};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};

#[derive(Debug)]
struct NoVerifier;

fn main(){
    let config = ClientConfig::builder()
        .dangerous()
        .with_custom_certificate_verifier(Arc::new(NoVerifier))
        .with_no_client_auth();

    // Create a connection to an FTP server and authenticate to it.
    let rc_config = Arc::new(config);

    let mut ftp_stream = RustlsFtpStream::connect("127.0.0.1:21")
        .unwrap()
        .into_secure(RustlsConnector::from(rc_config), "127.0.0.1")
        .unwrap();

    ftp_stream.login("testuser", "test$123456")
        .expect("Failed to log in to FTP server");

    println!("Successfully connected and logged in!");

    match ftp_stream.pwd() {
        Ok(current_dir) => println!("Current directory: {}", current_dir),
        Err(e) => eprintln!("Error retrieving current directory: {}", e),
    }

    match ftp_stream.list(Some("/")) {
        Ok(dir_list) => {
            println!("Directory listing:");
            for entry in dir_list {
                println!("{}", entry); // Print each entry
            }
        }
        Err(e) => eprintln!("Error listing directory: {}", e), // Handle any errors
    }

    // Terminate the connection to the server.
    let _ = ftp_stream.quit();
}

/// DANGER: This custom implementation of the SeverCertVerifier
/// is really dangerous and return success for all and everything.
impl ServerCertVerifier for NoVerifier {
    fn verify_server_cert(
        &self,
        _end_entity: &rustls::pki_types::CertificateDer<'_>,
        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
        _server_name: &rustls::pki_types::ServerName<'_>,
        _ocsp_response: &[u8],
        _now: rustls::pki_types::UnixTime,
    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }
    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
        vec![
            rustls::SignatureScheme::RSA_PKCS1_SHA1,
            rustls::SignatureScheme::ECDSA_SHA1_Legacy,
            rustls::SignatureScheme::RSA_PKCS1_SHA256,
            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
            rustls::SignatureScheme::RSA_PKCS1_SHA384,
            rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
            rustls::SignatureScheme::RSA_PKCS1_SHA512,
            rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
            rustls::SignatureScheme::RSA_PSS_SHA256,
            rustls::SignatureScheme::RSA_PSS_SHA384,
            rustls::SignatureScheme::RSA_PSS_SHA512,
            rustls::SignatureScheme::ED25519,
            rustls::SignatureScheme::ED448,
        ]
    }
}

In cargo.toml, enable the rustls feature

suppaftp = { version = "^6", features = ["rustls"] }

See if this helps. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants