user2956813
user2956813

Reputation: 21

Perl LDAP search - over 1500 member in a group

I want to search with an Perl script and ldap connection all members of a group with over 10.000 member.

I can only find results, if i set $first=0 and $last=1499 and than i get only the first 1500 member of the group. If i use other parameter for $first and $last, then i got no results.

"$ldapsearchresult = $ldapconnect->search (
Sizelimit => 0,
base   => 'any_base',
filter => '(objectClass=*)',
attr => ['member;range=$first-$last'],
);"

Thanks for your help!

Upvotes: 2

Views: 5109

Answers (3)

user2956813
user2956813

Reputation: 21

I found an easier way to search all member of a AD group:

http://permalink.gmane.org/gmane.comp.lang.perl.modules.ldap/246

use Net::LDAP; use Net::LDAP::Util;

# Connect to AD make sure to specify version 3

$ldap = new Net::LDAP("myGC.yy.xx.com",
                      port => 3268,
                      debug => 0,
                      version => 3
    ) or die "New failed:$ <at> ";

# Do an anonymous bind. You MAY have to do an authenticated bind in your configuration

$result=$ldap->ldapbind() || die "Bind Failed:$ <at> ";

# Some error trapping

$err=$result->code;

if ($err){
    $errname=Net::LDAP::Util::ldap_error_name($err);
    $errtxt=Net::LDAP::Util::ldap_error_text($err);
    if ($errtxt){
        print "($err) $errtxt\n";
    }
    else
    {
        if ($errname){
            print "($err) $errname\n";
        }
        else
        {
            print "ERR: $err\n";
        }
    }
    exit;
}

# The combination of the search base and filter determine which object that you
# retrieve

# set search filter to groups of objects. This is what you want to enumerate NT groups.

$filter="(objectClass=group)";

# Set the search base to the DN of the object that you want to retrieve. BTW, using this method on
# groups with less than 1000 members works as well.

$base='CN=mygroup,DCyyy,DC=xxx,DC=com';

# Set the initial attribute indexes and name

$found=1; $startr=0; $endr=-1; $startattr="member";

while($found){

    # Create the attribute range specification

    $startr=$endr+1;
    $endr=$startr+999;
    $attr="$startattr;range=$startr-$endr";
    $saveattr=$attr;
    <at> attr=("$attr");

    # Perform the search

    $result=$mesg = $ldap->search(base   => "$base",filter => $filter,
                                  attrs => [ <at> attr],
                                  scope => "sub") or die "search died";

    # Some error trapping

    $err=$result->code;
    if ($err){
        if (!($err == 1)){
            $errname=Net::LDAP::Util::ldap_error_name($err);
            $errtxt=Net::LDAP::Util::ldap_error_text($err);
            if ($errtxt){
                print "($err) $errtxt\n";
            }
            else
            {
                if ($errname){
                    print "($err) $errname\n";
                }
                else
                {
                    print "ERR: $err\n";
                }
            }
        }
        else
        {
            print "COUNT=$cnt\n";
        }
        exit;
    }

    $found=0;

    # OK, get the attribute range...so we can update the value of the attribute
    # on the next pass

    foreach $entry ($mesg->all_entries) {
        <at> attr=$entry->attributes;
        foreach( <at> attr){
            $curattr=$_;
        }
    }

    # Print out the current chunk of members

    foreach $entry ($mesg->all_entries) {
        $ar=$entry->get("$curattr");
        foreach( <at> $ar){
            $cnt++;
            print "$_\n";
        }
        $found=1;
        if (! <at> $ar[0]){
            $found=0;
        }
    }

    # Check to see if we got the last chunk. If we did print toe total and set
    # the found flag so we don't search for anymore members

    if ($curattr=~/\;range=/){
        if ($curattr=~/\-\*/){
            print "LASTCOUNT:$cnt\n";
            $found=0;
        }
    }

}

Upvotes: 0

jwilleke
jwilleke

Reputation: 11026

You maybe able to simplify the program by searching for: memberOf=CN=GroupOne,OU=Security Groups,OU=Groups,DC=YOURDOMAIN,DC=NET

You will still need to use the paged results control but will not need the range control.

Microsoft Active Directory uses the MaxValRange to control the number of values that are returned in the retrieval of multi-valued attributes of an entry.

By using the filter above, you can avoid the MaxValRange settings.

BY THE WAY: if you want to obtain nested members also, try:

(memberOf:1.2.840.113556.1.4.1941:=CN=GroupOne,OU=Security Groups,OU=Groups,DC=YOURDOMAIN,DC=NET)

This filter uses the LDAP_MATCHING_RULE_IN_CHAIN extensible match.

-jim

Upvotes: 0

user1126070
user1126070

Reputation: 5069

You need to search the attribute range as a subtype again and again until the last return '*'.

Here is the code I am using, it is also use paged search in AD.

use Net::LDAP;
use Net::LDAP qw(LDAP_CONTROL_PAGED);
use Net::LDAP::Util qw(ldap_error_name canonical_dn ldap_explode_dn ldap_error_text);
use Net::LDAP::Control::Paged;

my $page_page = Net::LDAP::Control::Paged->new( 'size' => $input{'page'} );
my $finished_search = 0;
my $page_cookie;
my $result;
my @page_search_args = (
    'base'     => $input{"base"},               
    'scope'    => $input{'scope'},                             
    'filter'   => $input{'filter'},  
    'attrs'    => $input{'attrs'},  
    'control'  => [ $page_page ],
    'deref'    => 'never',
    'raw'      => qr!^DO_NOT_MATCH!,
);

while (!$finished_search) {
        my $msg = $ldap->search(@page_search_args);
        if ($msg->is_error()) {
            die "ERROR: ",$msg->error,"\n";
            last;
        } else {
            my ($response) = $msg->control(LDAP_CONTROL_PAGED);
            $page_cookie = $response->cookie();
            $finished_search = 1 if !$page_cookie;
            $page_page->cookie($page_cookie);

            while (my $entry = $msg->pop_entry()){
                $ldap_searches++;
                print_all_attributes($entry);
            }
        }
}

if ($page_cookie) {
        $page_page->cookie($page_cookie);
        $page_page->size(0);
        $ldap->search(@page_search_args);
}   

sub add_result {
    my $dn     = shift;
    my $attr   = shift;
    my $data   = shift;
    my $res    = shift; 

    $attr =~ s!(;range\=\d+\-\d+)!!i;
    #print "removed $1 from $attr" if $1;
    foreach my $subtype (keys %{$data}){
        $attr = $attr.$subtype if $subtype ne '';
        $attr =~ s!(;range\=\d+\-\d+)!!i;
        if (defined $$res->{$dn}->{$attr}){
            push(@{$$res->{$dn}->{$attr}},@{$data->{$subtype}});
        } else {
            push(@{$$res->{$dn}->{$attr}},@{$data->{$subtype}});
        }
    }

    return $res;    
}
sub print_all_attributes {
    my $entry = shift;
    foreach my $attr ($entry->attributes()) {
        if ($attr =~ /;range=/) {   
            my $last = 0;my $first = 0;
            ### $var will look like this --> "member;range=0-1499"
            (my $pure_attr,my $range) = split /;/, $attr,2;
            (my $junk,$range)         = split /=/, $range,2;
            ($first,$last)            = split /-/, $range,2;
            $i++;
            add_result($entry->dn(),$pure_attr,$entry->get_value($attr,alloptions => 1, asref => 1),\$result) if $last eq '*' or $last >= $parms{'attribute_page'}; 
            ### if $last eq "*", indicates this is the last range increment, and
            ### we do not need to perform another supplemental search
            if ($last ne "*") {                                     
                my $range_diff = ($last - $first) + 1;
                my $increment =  $last + $range_diff;
                $last = $last + 1;
                $attr = "$pure_attr;range=$last-$increment";
                $parms{'attrs'} = [$attr];
                search_nonpaged(%parms);            
            }       
        } else {### if $attr matches range pattern                  
            add_result($entry->dn(),$attr,$entry->get_value($attr,alloptions => 1, asref => 1),\$result);
        }
    }
    return 1;
}
sub search_nonpaged{
    my %input = @_;
    my @page_search_args = (
        'base'     => $input{"base"},               
        'scope'    => $input{'scope'},                             
        'filter'   => $input{'filter'},  
        'attrs'    => $input{'attrs'},  
        'deref'    => 'never',
        'raw'      => qr!^DO_NOT_MATCH!,
    );  
    my $msg = $ldap->search(@page_search_args);
    if ($msg->is_error()) {
        die "ERROR: ",$msg->error,"\n"; 
    }
    while (my $entry = $msg->pop_entry()){
        $ldap_searches++;
        print_all_attributes($entry);
    }
}

Upvotes: 2

Related Questions