FTP Proxy authentication

0 votes
asked Nov 5, 2013 by joe edwards (120 points)
edited Nov 6, 2013

I'm trying to connect to a proxy similar to [but apparently not exactly like] the FtpProxyType.FtpDoubleLogin.

Here's what needs to happen:

<< 220-This is the Initech FTP proxy service.
<< 220-Access by unauthorized users is prohibited.
<< 220-All transfers through this proxy are logged.
<< 220-When prompted for username, enter ``RemoteUsername@RemoteHost''.
<< 220 For example: anonymous@ftp.uu.net
>> USER remoteuser@initech-origin.cdnetworks.net
<< 331-(----GATEWAY CONNECTED TO initech-origin.cdnetworks.net----)
<< 331-(220 ProFTPD 1.2.10 Server (ProFTPD Default Installation-WOW) [1.2.3.4])
<< 331 Password required for remoteuser.
>> PASS hunter2
<< 230 User remoteuser logged in.
>> AUTH proxyuser
<< 331 Enter authentication password for proxyuser
>> RESP hunter2
<< 230 User authenticated to proxy

However, none of the available proxy options seem to function this way. I can't seem to figure out how to send the proxy authentication ("AUTH" and "RESP") commands without using raw SendCommand. Also, the NetworkCredentials I pass for the proxy authentication are instead being passed to the remote server.

I have hacked it into sort-of working by using the remote credentials as my proxy credentials, and then sending raw commands to authenticate to the proxy (with AUTH/RESP).

Am I missing something?

Example code:

var ftp = new Ftp();
ftp.Passive = false;
ftp.Settings.DoNotDetectFeatures = true;

ftp.CommandSent += (sender, args) => { Console.WriteLine( ">> {0}", args.Command ); };
ftp.ResponseRead += (sender, args) => { Console.WriteLine( "<< {0}", args.Response ); };

// these are the credentials for initech-origin.cdnetworks.net, not gate.initech.com
ftp.Proxy = new FtpProxy( FtpProxyType.FtpDoubleLogin, "gate.initech.com", 21, false, new NetworkCredential( "remoteuser", "hunter2" ) );
ftp.Connect( "initech-origin.cdnetworks.net" );

try {
    // ftp.Login( "proxyuser", "hunter2" ); // sends wrong command
    ftp.SendCommand( "AUTH proxyuser" ); // proxy username
    ftp.ReadResponse();
    ftp.SendCommand( "RESP hunter2" ); // proxy password
    ftp.ReadResponse();
} finally {
    ftp.Disconnect();
}
Applies to: Rebex FTP/SSL

1 Answer

+1 vote
answered Nov 6, 2013 by Jan Sotola (16,540 points)
edited Nov 6, 2013

Thanks for a nicely elaborated question.

Just to summarize:
The DoubleLogin proxy expects the following sequence of commands:

USER ProxyUser@FtpHost
PASS ProxyPassword
USER FtpUser
PASS FtpPassword
While your gateway seems to expect the following sequence of commands:
USER FtpUser@FtpHost
PASS FtpPassword
AUTH ProxyUser
RESP ProxyPassword
The another difference is that the DoubleLogin proxy uses common FTP authentication commands only (USER, PASS) while your proxy requires specific commands - the AUTH command is used for the SSL/TLS authentication and the RESP is not used in FTP protocol at all.

I'm afraid Rebex FTP/SSL really does not provide an instant proxy type suitable for your gateway, but the SendCommand would be a reasonable solution here. If it succeeds, we would consider adding an support for this proxy type into our component, but for the 10 years of the Rebex FTP component we've never met a proxy gateway with this behavior.

I think your code is almost right, it needs only a one trick: do not use any pre-defined proxy and do the proxy authentication just using the Login and SendCommand method calls. Additionally, I'd recommend to examine results of the FTP protocol responses:

var ftp = new Ftp();
ftp.Passive = false;
ftp.Settings.DoNotDetectFeatures = true;

//ftp.CommandSent += (sender, args) => { Console.WriteLine( ">> {0}", args.Command ); };
//ftp.ResponseRead += (sender, args) => { Console.WriteLine( "<< {0}", args.Response ); };
// Wouldn't be easier to use the instant component logging?
ftp.LogWriter = new FileLogWriter(@"c:\temp\log.txt", LogLevel.Debug);

ftp.Connect("gate.initech.com" );  // connect to the proxy gateway first

try {
    // let the proxy to connect to the remote server
    ftp.Login("remoteuser@initech-origin.cdnetworks.net", "remotepassword" ); 
    ftp.SendCommand( "AUTH proxyuser" ); // proxy username
    var response = ftp.ReadResponse();
    // in common FTP authentication, the 3xx response means "password required"
    // but you may have to modify the following condition, 
    // since your gateway may return another response even when password is required
    if (response.Group == 3)
    {
        ftp.SendCommand( "RESP hunter2" ); // proxy password
        ftp.ReadResponse();
    }
    // if the last command didn't succeed
    if (response.Group != 2)
        throw new FtpException(response);
} finally {
    ftp.Disconnect();
}
...