I ran into a problem performing memberOf LDAP queries against an Active Directory controller. Turns out that while almost everyone is able to read most attributes from user objects, by default memberOf is not visible. The result is that queries using memberOf do not return a result. I was frustrated that the queries worked when tested on a domain controller using the LDP.exe utility, when tested as a domain admin. I was focused on getting the LDAP query correct and had not stopped to consider that the LDAP bind account I was using could not read the memberOf attribute.
Anyway, turns out that “Read Member Of” is a property you can assign via the Active Directory Users and Computers MMC. Right click on the top OU from where you want the permission to be granted (this might be the root of the AD tree or a sub-OU) and select “Properties”. Select the “Security” tab and then click “Advanced”. You can now add a permission for the LDAP bind user (or group as needed) using the “Add” button. Select a principal (i.e. user / group as needed), choose “Allow” for the “Type” and then you’ll probably want “Applies to” to be “This object and all descendant objects”. Scroll the bottom of the screen and choose “Clear all” and then find and select the property “Read Member Of”. Save and close the windows and your LDAP bind account should be able to filter based on group membership.
The below is a simple Perl snippet (based on code from http://search.cpan.org/dist/perl-ldap-0.55/lib/Net/LDAP/Examples.pod) to test the LDAP queries.
use Net::LDAP;
$ldap = Net::LDAP->new ( "dc01.example.com" ) or die "$@";
#if you has LDAPS enabled you can use the following instead
#$ldap = Net::LDAP->new ( "ldaps://dc01.example.com" ) or die "$@";
$mesg = $ldap->bind ( "CN=ldapbind,OU=Services,DC=ad,DC=example,DC=com",
password => "LongSecretPassword",
version => 3 );
#@Attrs = (); # to request all attributes
my @Attrs = ( 'cn','mail','givenName','sn','sAMAccountName');
my $base = "DC=ad,DC=example,DC=com";
#find members of the group
#my $filter = "(memberOf=CN=Some-group,OU=Groups,DC=ad,DC=example,DC=com)";
#find all objects with a sn attribute
#my $filter = "sn=*";
#find active users within the specified group
my $filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf=CN=Other-group,OU=Groups,DC=ad,DC=example,DC=com))";
#my $result = LDAPsearch ( $ldap, $query, \@Attrs );
my $result = $ldap->search ( base => "$base",
scope => "sub",
filter => "$filter",
attrs => \@Attrs
);
my @entries = $result->entries;
my $entr;
foreach $entr ( @entries ) {
print "DN: ", $entr->dn, "\n";
my $attr;
foreach $attr ( sort $entr->attributes ) {
# skip binary we can't handle
next if ( $attr =~ /;binary$/ );
print " $attr : ", $entr->get_value ( $attr ) ,"\n";
}
print "#-------------------------------\n";
}
A useful summary of LDAP search options supported by AD servers (incl for disabled accounts and nested-group membership) is in an article titled “Active Directory: LDAP Syntax Filters“.