vADC Docs

libLDAP.rts: a TrafficScript LDAP Library

by markbod on ‎12-18-2012 09:24 AM - edited on ‎07-14-2015 02:35 PM by PaulWallace (1,075 Views)

The libLDAP.rts library and supporting library files (written by Mark Boddington) allow you to interrogate and modify LDAP traffic from a TrafficScript rule, and to respond directly to an LDAP request when desired.

 

You can use the library to meet a range of use cases, as described in the document Managing LDAP traffic with libLDAP.rts.

 

Note: This library allows you to inspect and modify LDAP traffic as it is balanced by Stingray.  If you want to issue LDAP requests from Stingray, check out the auth.query() TrafficScript function for this purpose, or the equivalent Authenticating users with Active Directory and Stingray Java Extensions Java Extension.

 

Overview

 

A long, long time ago on a Traffic Manager far, far away, I (Mark Boddington) wrote some libraries for processing LDAP traffic in TrafficScript:

 

  • libBER.rts – This is a TrafficScript library which implements all of the required Basic Encoding Rules (BER) functionality for LDAP. It does not completely implement BER though, LDAP doesn't use all of the available types, and this library hasn't implemented those not required by LDAP.
  • libLDAP.rts – This is a TrafficScript library of functions which can be used to inspect and manipulate LDAP requests and responses. It requires libBER.rts to encode the LDAP packets.
  • libLDAPauth.rts – This is a small library which uses libLdap to provide simple LDAP authentication to other services.

 

That library (version 1.0) mostly focused on inspecting LDAP requests. It was not particularly well suited to processing LDAP responses. Now, thanks to a Stingray PoC being run in partnership with the guys over at Clever Consulting, I've had cause to revist this library and improve upon the original. I'm pleased to announce libLDAP.rts version 1.1 has arrived.

 

 

What's new in libLdap Version 1.1?

 

  • Lazy Decoding. The library now only decodes the envelope  when getPacket() or getNextPacket() is called. This gets you the MessageID and the Operation. If you want to process further, the other functions handle decoding additional data as needed.
  • New support for processing streams of LDAP Responses. Unlike Requests LDAP Responses are typically made up of multiple LDAP messages. The library can now be used to process multiple packets in a response.
  • New SearchResult processing functions: getSearchResultDetails(), getSearchResultAttributes() and updateSearchResultDetails()

 

Lazy Decoding

 

Now that the decoding is lazier it means you can almost entirely bypass decoding for packets which you have no interest in. So if you only want to check BindRequests and/or BindResponses then those are the only packets you need to fully decode. The rest are sent through un-inspected (well except for the envelope).

 

Support for LDAP Response streams

 

We now have several functions to allow you to process responses which are made up of multiple LDAP messages, such  as those for Search Requests. You can use a loop with the "getNextPacket($packet["lastByte"])" function to process each LDAP message as it is returned from the LDAP server. The LDAP packet hash  now has a "lastByte" entry to help you keep track of the messages in the stream. There is also a new skipPacket() function to allow you to skip the encoder for packets which ou aren't modifying.

 

Search Result Processing

 

With the ability to process response streams I have added a  number of functions specifically for processing SearchResults. The getSearchDetails() function will return a SearchResult hash which contains the ObjectName decoded. If you are then interested in the object you can  call getSearchResultAttributes() to decode the Attributes which have been returned. If you make any changes to the Search Result you can then call updateSearchResultDetails() to update the packet, and then encodePacket() to re-encode it. Of course if at any point you determine that no changes are needed then you can call skipPacket() instead.

 

Example - Search Result Processing

 

import libDLAP.rts as ldap;

$packet = ldap.getNextPacket(0);

while ( $packet ) {

   # Get the Operation

   $op = ldap.getOp($packet);


   # Are we a Search Request Entry?

   if ( $op == "SearchRequestEntry" ) {

      $searchResult = ldap.getSearchResultDetails($packet);

      # Is the LDAPDN within example.com?

      if ( string.endsWith($searchResult["objectName"], "dc=example,dc=com") ) {


         # We have a search result in the tree we're interested in. Get the Attributes

         ldap.getSearchResultAttributes($searchResult);

         # Process all User Objects

         if ( array.contains($searchResult["attributes"]["objectClass"], "inetOrgPerson") ) {


            # Log the DN and all of the attributes

            log.info("DN: " . $searchResult["objectName"] );

            foreach ( $att in hash.keys($searchResult["attributes"]) ) {

               log.info($att . " = " . lang.dump($searchResult["attributes"][$att]) );

            }


            # Add the users favourite colour

            $searchResult["attributes"]["Favourite_Colour"] = [ "Riverbed Orange" ];


            # If the password attribute is included.... remove it

            hash.delete($searchResult["attributes"], "userPassword");


            # Update the search result

            ldap.updateSearchResultDetails($packet, $searchResult);


            # Commit the changes

            $stream .= ldap.encodePacket( $packet );

            $packet = ldap.getNextPacket($packet["lastByte"]);

            continue;

         }

      }

   }

   # Not an interesting packet. Skip and move on.

   $stream .= ldap.skipPacket( $packet );

   $packet = ldap.getNextPacket($packet["lastByte"]);

}

response.set($stream);

response.flush();

 

This example reads each packet in turn by calling getNextPacket() and passing the lastByte attribute from the previously processed packet as the argument. We're looking for SearchResultEntry operations, If we find one we pass the packet to getSearchResultDetails() to decode the object which the search was for in order to determine the DN. If it's in example.com then we decide to process further and decode the attributes with getSearchResultAttributes(). If the object has an objectClass of inetOrgPerson we then print the attributes to the event log, remove the userPassword if it exists and set a favourite colour for the user. Finally we encode the packet and move on to the next one. Packets which we aren't interested in modifying are skipped.

 

Of course, rather than do all this checking in the response, we could have checked the SearchRequest in a request rule and then used connection.data.set() to flag the message ID for further processing.

 

We should also have a request rule which ensures that the objectClass is in the list of attributes requested by the end-user. But I'll leave that as an exercise for the reader ;-)

 

If you want more examples of how this library can be used, then please check out the additional use cases here: Managing LDAP traffic with libLDAP.rts

Contributors