LDAP Server Side Sort (SSS) Control



By: ab

July 2, 2009 1:37 pm

Reads: 383

License:
GPL v3

Download ldapsssctrl.pl_0

Server Side Sort (SSS) is the ability to have the server return its results in some order. Normally, per the LDAP RFC, there is no need to order or scramble data on the server side and it is entirely up to the client to decide how to handle the data regardless of the data size returned from the server. Sorting on the client side is fairly cheap so that’s not a big deal, especially since the server will often be fairly busy and shouldn’t have its speed impacted by silly things like sorting otherwise-random results. Still, there are cases where sorting on the server side would be nice.

Some limitations exist around this feature. First, in most LDAP-server cases (talking about the world outside eDirectory) the ability to handle these controls is advertised on what is known as the RootDSE which is something commonly-available in LDAP for a client to see what the server does. With eDirectory this control is NOT listed there ever because this functionality is not specifically supported. With that said if the application/client making the request doesn’t care about that and just tries to use it anyway things are often fine.

Second, eDirectory currently requires an index (and I think a ‘Value’ index at that) on any attributes which are being sorted. Not having one will get you the results back (the control is non-critical so LDAP still returns what you expect it to) but the results are not sorted in any way. An LDAP trace also shows you a message about this when it happens:

2634980256 LDAP: [2009/06/26 15:40:29.946] (137.65.121.54:52256)(0x0002:0x63) Search request:
base: ""
scope:2 dereference:2 sizelimit:0 timelimit:0 attrsonly:0
filter: "(objectClass=inetOrgPerson)"
attribute: "dn"
2634980256 LDAP: [2009/06/26 15:40:29.947] (137.65.121.54:52256)(0x0002:0x63) Sort setup with index "description"
2634980256 LDAP: [2009/06/26 15:40:29.948] (137.65.121.54:52256)(0x0002:0x63) SearchWithControls: sort attribute (description) not indexed
2634980256 LDAP: [2009/06/26 15:40:29.948] (137.65.121.54:52256)(0x0002:0x63) nds_back_search: Failure of non-critical control ignored, err = 53 (0x35)

Finally eDirectory needs replicas of objects to be returned. If you have any eDirectory experience this should be a no-brainer but it’s bound to be asked. having sorted results is just too difficult when you have to walk to potentially dozens or hundreds of other servers, and there is no guarantee that the other servers will be indexed…. it’s just a mess so do not expect it to even work if the server queried does not have replicas.

So, how do we do this? I know of two ways. First let’s start with ldapsearch because that’s a tool everybody is likely to have and it’s a simple test. With ldapsearch the ‘-e’ option is used to enable extensions and a few options are specified within the ldapsearch man page. A normal query may be the following for all users:

ldapsearch -h my.server.here -p 389 -D cn=admin,dc=user,dc=system -x -W dn

# extended LDIF
#
# LDAPv3
# base with scope subtree
# filter: (objectclass=*)
# requesting: dn
#

# minion, novell, org
dn: ou=minion,o=novell,dc=org

# test00, minion, novell, org
dn: cn=test00,ou=minion,o=novell,dc=org

# testAA, minion, novell, org
dn: cn=testAA,ou=minion,o=novell,dc=org

# testbb, minion, novell, org
dn: cn=testbb,ou=minion,o=novell,dc=org

# test11, minion, novell, org
dn: cn=test11,ou=minion,o=novell,dc=org

# testCC, minion, novell, org
dn: cn=testCC,ou=minion,o=novell,dc=org

# search result
search: 2
result: 0 Success

# numResponses: 7
# numEntries: 6

After running this I am prompted for credentials and the results all come back with just the DN portions. The results will not be sorted, and between one query and the next they will not even necessarily be the same (there is no guarantee that there should be per the RFC). So now let’s implement sorting by cn:

ldapsearch -h my.server.here -p 389 -D cn=admin,dc=user,dc=system -x -W -E 'sss=cn' dn

# extended LDIF
#
# LDAPv3
# base with scope subtree
# filter: (objectclass=*)
# requesting: dn
# with server side sorting control
#

# test00, minion, novell, org
dn: cn=test00,ou=minion,o=novell,dc=org

# test11, minion, novell, org
dn: cn=test11,ou=minion,o=novell,dc=org

# testAA, minion, novell, org
dn: cn=testAA,ou=minion,o=novell,dc=org

# testbb, minion, novell, org
dn: cn=testbb,ou=minion,o=novell,dc=org

# testCC, minion, novell, org
dn: cn=testCC,ou=minion,o=novell,dc=org

# search result
search: 2
result: 0 Success
control: 1.2.840.113556.1.4.474 false MIQAAAADCgEA
sortResult:: KDApIFN1Y2Nlc3Mg

# numResponses: 6
# numEntries: 5

We have added the -E flag telling it to use server-side sorting based on ‘cn’ if possible. This implies a few things. First, the user with which I bind to eDirectory has rights to view the CN object. It also implies CN is indexed and the server holds replicas. When everything is completed out come the values (just the DNs still for brevity) and now they are sorted by the cn. This is ‘ascii’ sorting so keep that in mind, and it is also case-insensitive sort in this case. Notice that the output changed a bit with regard to the response. It showed the control used as well as the sortResult. At the top of the output it also mentioned server side sorting was used. So ldapsearch which comes with the openldap2-client package on SLED 11 does this automatically.

With Perl the ability to do the same is there though it’s a bit more programming. In the long run there is a lot of control (play on words intended) given to somebody using something like Perl because of the ability to construct the query exactly as desired and then work with the output within Perl (vs. having to work with it in bash. This is probably possible in every language but Perl is one of the most cross-platform languages available so we’ll be showing it off here.

In this case we need to implement the Perl controls by including them first thing. I have added the perl-ldap package to my system so I can use the capabilities included therein. From that point we ‘use’ a few packages:

use Net::LDAP;
use Net::LDAP::Control::Sort;
use Net::LDAP::Constant qw(LDAP_CONTROL_SORTRESULT);

The last line creates a constant LDAP_CONTROL_SORTRESULT which has the SSS control's UID in it for later. Next we'll connect and setup our query:

#Connect to the server and bind as the user specified.
my($ldap) = Net::LDAP->new($bindServer) or die "Can't bind to ldap: $!\n";

my($mesg) = $ldap->bind(
dn => $bindDN,
password => $bindPassword
);

#SSS Setup
my($sort) = Net::LDAP::Control::Sort->new( order => 'cn' );

#Query Arguments
my(@args) = ( base => '',
scope => 'subtree',
attrs => ['dn'],
filter => '(objectClass=inetOrgPerson)',
control => [ $sort ],
);

#Run the search
$mesg = $ldap->search( @args );

#Dump the results.
foreach $entry ($mesg->entries) { $entry->dump; }

I have setup a few variables beforehand which are shown above and will be prompted-for in the script but basically you enter the same fields as you would with ldapsearch and then you also specify a control object which has an array of things on which to sort (in order). At the end we run the search and then loop through the results dumping them to the screen for review but it would also make sense to do further processing within Perl (if this were not simply a demo anyway).

So the end command to the script looks like the following and returns all entries in the tree (the base of ” and scope of ‘subtree’) returning only the ‘dn’ of each object that is of type ‘inetOrgPerson’ (User in eDirectory terms):

./ldapsssctrl.pl ldap.server.goes.here cn=admin,dc=user,dc=system
Please enter a password:
------------------------------------------------------------------------
dn:cn=admin,dc=user,dc=system 

------------------------------------------------------------------------
dn:cn=grouptest00,ou=finance,o=novell,dc=org 

------------------------------------------------------------------------
dn:cn=IUSR_NTS93,ou=testmad01driverset0,o=suse,dc=org 

------------------------------------------------------------------------
dn:cn=IWAM_NTS93,ou=testmad01driverset0,o=suse,dc=org 

------------------------------------------------------------------------
dn:cn=jwardle,ou=testmad01driverset0,o=suse,dc=org 

------------------------------------------------------------------------

We have just successfully (if the requirements were met anyway) sorted on the server side and saved our client a bit of work at the expense of server performance. There’s always a tradeoff, but at least the possibility is there.

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)

Tags: , ,
Categories: Cool Tools, eDirectory

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

1 Comment

  1. By:jwilleke

    There is some additional Information in TID: 7001493
    http://www.novell.com/support/php/search.do?cmd=displayKC&docType=kc&externalId=7001493&sliceId=1&docTypeID=DT_TID_1_1&dialogID=43758424&stateId=1%200%2043756208

    I find it amazing that there is no support for these controls in eDirectory.
    The lack of these controls cause issues as several “off-the-shelf” applications look for the control in the rootDSE and fail is not found.

    These controls are considered “basic” to a LDAP server implementation and should be supported.

    -jim

Comment