vADC Docs

Managing FTP traffic with Stingray

by on ‎05-16-2013 07:39 AM - edited on ‎07-06-2015 04:06 PM by PaulWallace (1,121 Views)

An interesting use case cropped up recently - one of our users wanted to do some smarts with the login credentials of an FTP session.

 

This article steps through a few sample FTP rules and explains how to manage this sort of traffic.

 

Before you begin

 

  • Make sure you have a suitable FTP client.  The command-line ftp tool shipped with most Unix-like systems supports a -d flag that reports the underlying FTP messages, so it's great for this exercise.

 

  • Pick a target FTP server.  I tested against ftp.riverbed.com and ftp.debian.org, but other ftp servers may differ for subtle reasons.

 

  • Review the FTP protocol specification - it's sufficient to know that it's a single TCP control channel, requests are of the form 'VERB[ parameter]\r\n" and responses are of the form 'CODE message\n'.  Multi-line responses are accepted; all but the last line of the reponse include an additional hyphen ('CODE-message\n').

 

Create your FTP virtual server

 

Use the 'Add a new service' wizard to create your FTP virtual server.  Just for fun, add a server banner (Virtual Server > Connection Management > FTP-Specific Settings):

 

Screen Shot 2013-05-16 at 15.05.05.png

 

Verify that you can log in to your FTP server through Stingray, and that the banner is rewritten:

Screen Shot 2013-05-16 at 15.08.22.png

Now we're good to go!

 

Intercepting login credentials

 

We want to intercept FTP login attempts, and change all logins to 'anonymous'.  If a user logs in with 'usernameSmiley Tongueassword', we're going to convert that to 'anonymous:username' and discard the password.

 

Create the following Request Rule, and assign it to the FTP virtual server:

 

log.info( "Recieved connection: state is '" . connection.data.get( "state" ) . "'" ); 

if( connection.data.get( "state" ) == "" ) {
   # This is server-first, so we have no data on the first connect
   connection.data.set( "state", "connected" );
   break;
}

if( connection.data.get( "state" ) == "connected" ) {
   # Get the request line
   $req = string.trim( request.endswith( "\n" ) );
   log.info( " ... got request '" . $req . "'" );
   
   if( string.regexmatch( $req, "USER (.*)" ) ) { 
      connection.data.set( "user", $1 );  

      # Translate this to an anonymous login
      log.info( " ... rewriting request to 'USER anonymous'" );
      request.set( "USER anonymous\r\n" );
   }


   if( string.regexmatch( $req, "PASS (.*)" ) ) { 
      $pass = $1;
      connection.data.set( "pass", $pass );
      $user = connection.data.get( "user" );

      # Set the appropriate password
      log.info( " ... rewriting request to 'PASS ".$user."'" ); 
      request.set( "PASS ".$user."\r\n" );
   }
}

 

Now, if you log in with your email address (for example) and a password, the rule will switch your login to an anonymous one and will log the result:

 

99A04475-6B85-4AF6-8D7D-E34499F8017D.png

Authenticating the user's credentials

 

You can extend this rule to authenticate the credentials that the user provided.  At the point in the rule where you have the username and password, you can call a Stingray authenticator, a Java Extension, or reference a libTable.rts: Interrogating tables of data in TrafficScript in your TrafficScript rule:

 

#AD authentication

   $ldap = auth.query( "AD Auth", $user, $pass );


   if( $ldap['Error'] ) {
      log.error( "Error with authenticator 'AD Auth': " . $auth['Error'] );
      connection.discard();
   }

   else if( !$ldap['OK'] ) {
      log.info("User not authenticated.  Username and/or password incorrect");
      connection.discard();
   }

 

Contributors