Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP curl FTPes w/ explicit TLS/SSL

Tags:

php

curl

ssl

ftp

ftps

I've been trying to push to a remote FTP with an explicit TLS/SSL server and the connection keeps timing out. It's connecting but I don't know where it's stopping but I'm assuming it's something do to with the FTP/TLS/SSL control channel. Any help would be greatly appreciated.

Through FileZilla I'm able to connect without a problem but not through curl. I'm probably missing something and hoping someone on here would know the best way to recreate the session using curl. Here's the debug output from FileZilla and the sample code I've been working on in PHP/curl. Here's the PHP code and below is a copy of a successful FileZilla session.

Here are the versions but I've tried it on different systems with the same results.

Curl: v7.43.0

└─(08:04:00 on master ✹)──> php -v                                          ──(Thu,Sep10)─┘
PHP 5.5.27 (cli) (built: Jul 14 2015 17:04:01)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies
    with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans

Here is the actual code.

<?php

$server_data = array(
   'transfer_id' => 123456789,
   'post_url' => "ftps://ftps.widgetsltd.com",
   'port' => 21,
   'username' => 'widgetsftp',
   'password' => 'password',
);

$filename = sprintf("%s-%s-%s.csv",
                    $server_data['transfer_id'],
                    microtime(TRUE),
                    rand(1000, 9999));

$temp_filename = sprintf("/tmp/%s", $filename);
$ftp_data = "This is a test";

$fp = fopen($temp_filename, 'w');
fprintf($fp, "%s", $ftp_data);
fclose($fp);

$fp = fopen($temp_filename, 'r');

$ch = curl_init();

curl_setopt($ch, CURLOPT_VERBOSE, TRUE);    
curl_setopt($ch, CURLOPT_URL, sprintf("%s/%s", $server_data['post_url'], $filename));
curl_setopt($ch, CURLOPT_PORT, 21);    
curl_setopt($ch, CURLOPT_USERPWD, sprintf("%s:%s", $server_data['username'], $server_data['password']));

curl_setopt($ch, CURLOPT_UPLOAD, TRUE);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($temp_filename));

curl_setopt($ch, CURLOPT_USE_SSL, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'AES-128-CBC');

curl_setopt($ch, CURLOPT_FTP_SSL, CURLOPT_FTPSSLAUTH);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);

print_r(array('curl_exec' => curl_exec($ch)));
print_r(array(
           'curl_errno' => curl_errno($ch),
           'curl_error' => curl_error($ch),
        ));

This is the FileZilla session.

Status:        Resolving address of ftps.widgetsltd.com
Status:        Connecting to 123.123.123.123:21...
Status:        Connection established, waiting for welcome message...
Trace:         CFtpControlSocket::OnReceive()
Response:  220-Microsoft FTP Service
Response:  220 Widgets, LTD FTP server
Trace:         CFtpControlSocket::SendNextCommand()
Command:   AUTH TLS
Trace:         CFtpControlSocket::OnReceive()
Response:  234 AUTH command ok. Expecting TLS Negotiation.
Status:        Initializing TLS...
Trace:         CTlsSocket::Handshake()
Trace:         CTlsSocket::ContinueHandshake()
Trace:         CTlsSocket::OnSend()
Trace:         CTlsSocket::OnRead()
Trace:         CTlsSocket::ContinueHandshake()
Trace:         CTlsSocket::OnRead()
Trace:         CTlsSocket::ContinueHandshake()
Trace:         TLS Handshake successful
Trace:         Protocol: TLS1.0, Key exchange: RSA, Cipher: AES-128-CBC, MAC: SHA1
Status:        Verifying certificate...
Status:        TLS connection established.
Trace:         CFtpControlSocket::SendNextCommand()
Command:   USER s-rokfri
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  331 Password required for s-rokfri.
Trace:         CFtpControlSocket::SendNextCommand()
Command:   PASS ********
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  230-This service and information contained therein belong to Widgets, LTD.
Response:  230 User logged in.
Trace:         CFtpControlSocket::SendNextCommand()
Command:   OPTS UTF8 ON
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  200 OPTS UTF8 command successful - UTF8 encoding now ON.
Trace:         CFtpControlSocket::SendNextCommand()
Command:   PBSZ 0
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  200 PBSZ command successful.
Trace:         CFtpControlSocket::SendNextCommand()
Command:   PROT P
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  200 PROT command successful.
Status:        Connected
Trace:         CFtpControlSocket::ResetOperation(0)
Trace:         CControlSocket::ResetOperation(0)
Trace:         CFileZillaEnginePrivate::ResetOperation(0)
Trace:         Measured latency of 141 ms
Status:        Retrieving directory listing...
Trace:         CFtpControlSocket::SendNextCommand()
Trace:         CFtpControlSocket::ChangeDirSend()
Command:   PWD
Trace:         CTlsSocket::OnRead()
Trace:         CFtpControlSocket::OnReceive()
Response:  257 "/" is current directory.
Trace:         CFtpControlSocket::ResetOperation(0)
Trace:         CControlSocket::ResetOperation(0)
Trace:         CFtpControlSocket::ParseSubcommandResult(0)
Trace:         CFtpControlSocket::ListSubcommandResult()
Trace:           state = 1
Trace:         CFtpControlSocket::ResetOperation(0)
Trace:         CControlSocket::ResetOperation(0)
Status:        Directory listing of "/" successful
Trace:         CFileZillaEnginePrivate::ResetOperation(0)

Here is the curl output.

*   Trying 123.123.123.123...
* Connected to ftps.widgetsltd.com (123.123.123.123) port 21 (#0)
* SSL connection timeout
* Closing connection 0
Array
(
    [curl_exec] =>
)
Array
(
    [curl_errno] => 28
    [curl_error] => SSL connection timeout
)
like image 261
jbrahy Avatar asked Sep 10 '15 01:09

jbrahy


1 Answers

The curl obviously tries to use an implicit FTP (as it initializes TLS/SSL session even before any FTP commands are exchanged with the server).

It's because you have specified the ftps:// prefix, which is used for the implicit TLS. It has the special prefix, because the implicit TLS uses a special port (990). But you overrode the default with CURLOPT_PORT.

While the explicit TLS uses the standard FTP port (21), so it uses the standard ftp:// prefix. To enable the explicit TLS, use the CURLOPT_USE_SSL (what you are doing already, just via a wrong value, the option type is enumeration, not boolean).

So the code should be:

$server_data = array(
   ...
   'post_url' => "ftp://ftps.widgetsltd.com", // ftp:// URL
   ...
);

$url = sprintf("%s/%s", $server_data['post_url'], $filename);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USE_SSL, CURLUSESSL_ALL); // Enable TLS/SSL

Note that the CURLOPT_FTP_SSL is obsoleted and is an alias to the CURLOPT_USE_SSL. So, it does not make sense to set both.

like image 199
Martin Prikryl Avatar answered Sep 25 '22 14:09

Martin Prikryl