veeso / suppaftp

a super FTP/FTPS client library for Rust with support for both passive and active mode
Apache License 2.0
121 stars 31 forks source link

[QUESTION] - Handling IP address mismatch for server certificates #87

Closed Lukrecjaaa closed 1 month ago

Lukrecjaaa commented 2 months ago

When connecting to a FTPS server with its normal domain name (eg. ftp.*****.com), everything works as expected. However, when connecting via a VPN (with server's IP address like 10.*.*.*), I receive an error due to an IP address mismatch in the server certificate. Here's the code I'm using to handle the connection:

    pub fn connect(&self) -> Result<(), FtpError> {
        let cert_pem = include_bytes!("../ftp_server.pem");
        let cert = Certificate::from_pem(cert_pem).unwrap();

        let tls_connector = TlsConnector::builder()
                                .add_root_certificate(cert)
                                .build()
                                .unwrap();

        let ftp_stream = NativeTlsFtpStream::connect(format!("{}:21", &self.cfg.ftp_address))?;
        let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(tls_connector), &self.cfg.ftp_address)?;
        ftp_stream.login(&self.cfg.ftp_username, &self.cfg.ftp_password)?;
        ftp_stream.mkdir(&self.cfg.ftp_directory).ok();
        *self.ftp_stream.lock().unwrap() = Some(ftp_stream);
        info!("Connected to the FTPS server successfully");
        Ok(())
    }

Where ftp_server.pem is the server's certificate. Here's the output I'm getting:

2024-09-06T11:13:40.369Z DEBUG [suppaftp::sync_ftp] Connecting to server
2024-09-06T11:13:40.392Z DEBUG [suppaftp::sync_ftp] Established connection with server
2024-09-06T11:13:40.393Z DEBUG [suppaftp::sync_ftp] Reading server response...
2024-09-06T11:13:40.414Z DEBUG [suppaftp::sync_ftp] Server READY; response: Some("220 Please visit https://filezilla-project.org/")
2024-09-06T11:13:40.414Z DEBUG [suppaftp::sync_ftp] Initializing TLS auth
2024-09-06T11:13:40.686Z DEBUG [suppaftp::sync_ftp] TLS OK; initializing ssl stream
2024-09-06T11:13:40.765Z ERROR [axis_camera_converter::ftp_handler] Failed to connect to FTPS server: Secure error: Secure error: error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2091: (IP address mismatch)

When I'm trying to connect to the server with Filezilla via VPN, I'm getting a "The server's certificate is unknown. Please carefully examine the certificate to make sure the server can be trusted." notification, which allows me to accept or reject the certificate. After accepting it, everything works as expected.

I cannot use the normal domain name to connect to the server in my program, I have to connect to the server via VPN. Another requirement is that I'm using native-tls-vendored version of the crate. Is there a way to achieve something similar to what Filezilla does? I don't really want to skip certificate verification entirely, but I also need to somehow bypass the IP mismatch check.

Lukrecjaaa commented 1 month ago

I've modified the connect function to use danger_accept_invalid_hostnames, now it looks like this:

    pub fn connect(&self) -> Result<(), FtpError> {
        let cert_pem = include_bytes!("../ftp_server.pem");
        let cert = Certificate::from_pem(cert_pem).unwrap();

        let tls_connector = TlsConnector::builder()
            .add_root_certificate(cert)
            .danger_accept_invalid_hostnames(true)
            .build()
            .unwrap();

        let ftp_stream = NativeTlsFtpStream::connect(format!("{}:21", &self.cfg.ftp_address))
            .map_err(|e| {
                error!("Failed to establish a connection: {:?}", e);
                e
        })?;

        let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(tls_connector), &self.cfg.ftp_address)
            .map_err(|e| {
                error!("Failed during SSL stream initialization: {:?}", e);
                e
        })?;

        ftp_stream.login(&self.cfg.ftp_username, &self.cfg.ftp_password)?;
        ftp_stream.transfer_type(FileType::Binary)?;
        ftp_stream.set_mode(suppaftp::Mode::Passive);
        ftp_stream.set_passive_nat_workaround(true);
        ftp_stream.mkdir(&self.cfg.ftp_directory).ok();
        *self.ftp_stream.lock().unwrap() = Some(ftp_stream);
        info!("Connected to the FTPS server successfully");
        Ok(())
    }

After this change the program connects to the server successfully, but when trying to send a file I'm getting the following error:

2024-09-23T08:21:34.803Z DEBUG [suppaftp::sync_ftp] Connecting to server
2024-09-23T08:21:34.830Z DEBUG [suppaftp::sync_ftp] Established connection with server
2024-09-23T08:21:34.830Z DEBUG [suppaftp::sync_ftp] Reading server response...
2024-09-23T08:21:34.849Z DEBUG [suppaftp::sync_ftp] Server READY; response: Some("220 Please visit https://filezilla-project.org/")
2024-09-23T08:21:34.849Z DEBUG [suppaftp::sync_ftp] Initializing TLS auth
2024-09-23T08:21:35.123Z DEBUG [suppaftp::sync_ftp] TLS OK; initializing ssl stream
2024-09-23T08:21:35.205Z DEBUG [suppaftp::sync_ftp] TLS Steam OK
2024-09-23T08:21:35.765Z DEBUG [suppaftp::sync_ftp] Signin in with user 'arhue'
2024-09-23T08:21:35.930Z DEBUG [suppaftp::sync_ftp] Password is required
2024-09-23T08:21:36.147Z DEBUG [suppaftp::sync_ftp] Login OK
2024-09-23T08:21:36.147Z DEBUG [suppaftp::sync_ftp] Setting transfer type I
2024-09-23T08:21:36.164Z DEBUG [suppaftp::sync_ftp] Changed mode to Passive
2024-09-23T08:21:36.164Z DEBUG [suppaftp::sync_ftp] Creating directory at /IN
2024-09-23T08:21:36.185Z INFO  [axis_camera_converter::ftp_handler] Connected to the FTPS server successfully
2024-09-23T08:21:36.185Z INFO  [axis_camera_converter] Listening on 0.0.0.0:3000
2024-09-23T08:21:41.408Z DEBUG [axis_camera_converter::utils::tcp_utils] Handling incoming TCP request
2024-09-23T08:21:41.587Z DEBUG [axis_camera_converter::utils::data_processing] Decoding image
2024-09-23T08:21:41.587Z DEBUG [axis_camera_converter::utils::data_processing] Generating XML
2024-09-23T08:21:41.588Z DEBUG [axis_camera_converter::utils::file_utils] Saving image and XML files 20240923082654227-eventmessage-MSG01hzn8yynv31
2024-09-23T08:21:41.588Z DEBUG [suppaftp::sync_ftp] Reading /IN directory content
2024-09-23T08:21:41.589Z DEBUG [suppaftp::sync_ftp] PASV command
2024-09-23T08:21:42.598Z DEBUG [suppaftp::sync_ftp] Finalizing retr stream
2024-09-23T08:21:42.598Z DEBUG [suppaftp::sync_ftp::tls::native_tls] TLS Stream shut down
2024-09-23T08:21:42.609Z WARN  [axis_camera_converter::ftp_handler] Failed to transfer files: Invalid response: [425] 425 Unable to build data connection: TLS session of data connection not resumed.. Attempt 1/5
2024-09-23T08:21:42.610Z WARN  [axis_camera_converter::ftp_handler] Reconnecting to FTPS server...
Lukrecjaaa commented 1 month ago

It turns out there was something wrong with the server I was testing my program with, when I used my own vsftpd server everything works correctly with the .danger_accept_invalid_hostnames(true) option.