Ldap_search size limit exceeded

I accessed Microsoft Active Directory quite recently and ran into some difficulties:

AD has a size limit of 1000 items per request. I can not change the size limit. PHP does not seem to support paging (I am using version 5.2 and there is no way to upgrade the production server.)

So far I have come across two possible solutions:

  • Sort records by Sid object and use filters to get all objects. Code example
    I do not like this for several reasons:
    • It seems unpredictable to mess with objectSid since you need to parse it, convert to decimal, convert back ...
    • I do not see how you can compare these identifiers.
      (I tried: '& ((objectclass = user) (objectSid> = 0)))

  • Filter after the first letters of the object names (as suggested here ):
    This is not an optimal solution, since many users / groups in our system have a prefix with the same letters.

So my question is:

Which approach is best used here?
If this is the first, how can I handle objectSid correctly?

Any other features? Am I missing something?

Update:
- This related question provides information on why the Simple Paged Results extension does not work.
- The web server runs on a Linux server, so the COM objects / adoDB are not parameters.

+4
source share
4 answers

Since I did not find any clean solutions, I decided to go with the first approach: Filtering By Object-Sids.

There are limitations in this workaround:

  • It works only for objects with objects, that is, users and groups.
  • It is assumed that all users / groups are created by the same authority.
  • He suggests that there is not enough missing relative security identifiers than the size limit.

The idea is to first read all possible objects and select the one with the lowest relative SID. Relative SID - last fragment in SID:

S-1-5-21-3188256696-111411151-3922474875-1158

Suppose this is the lowest relative SID in the search, which returns only "Partial search results". Suppose further that the size limit is 1000.

Then the program performs the following actions: It searches for all objects with SIDs between

S-1-5-21-3188256696-111411151-3922474875-1158
and
S-1-5-21-3188256696-111411151-3922474875-0159

then everything in between

S-1-5-21-3188256696-111411151-3922474875-1158
and
S-1-5-21-3188256696-111411151-3922474875-2157

and so on, until one of the queries returns null objects.

There are several problems with this approach, but it is enough for my purposes.
Code:

$filter = '(objectClass=Group)'; $attributes = array('objectsid','cn'); //objectsid needs to be set $result = array(); $maxPageSize = 1000; $searchStep = $maxPageSize-1; $adResult = @$adConn->search($filter,$attributes); //Supress warning for first query (because it exceeds the size limit) //Read smallest RID from the resultset $minGroupRID = ''; for($i=0;$i<$adResult['count'];$i++){ $groupRID = unpack('V',substr($adResult[$i]['objectsid'][0],24)); if($minGroupRID == '' || $minGroupRID>$groupRID[1]){ $minGroupRID = $groupRID[1]; } } $sidPrefix = substr($adResult[$i-1]['objectsid'][0],0,24); //Read last objectsid and cut off the prefix $nextStepGroupRID = $minGroupRID; do{ //Search for all objects with a lower objectsid than minGroupRID $adResult = $adConn->search('(&'.$filter.'(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID-$searchStep))).'))', $attributes); for($i=0;$i<$adResult['count'];$i++){ $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24)); //Extract the relative SID from the SID $RIDs[] = $RID[1]; $resultSet = array(); foreach($attributes as $attribute){ $resultSet[$attribute] = $adResult[$i][$attribute][0]; } $result[$RID[1]] = $resultSet; } $nextStepGroupRID = $nextStepGroupRID-$searchStep; }while($adResult['count']>1); $nextStepGroupRID = $minGroupRID; do{ //Search for all object with a higher objectsid than minGroupRID $adResult = $adConn->search('(&'.$filter.'(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID+$searchStep))).'))', $attributes); for($i=0;$i<$adResult['count'];$i++){ $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24)); //Extract the relative SID from the SID $RIDs[] = $RID[1]; $resultSet = array(); foreach($attributes as $attribute){ $resultSet[$attribute] = $adResult[$i][$attribute][0]; } $result[$RID[1]] = $resultSet; } $nextStepGroupRID = $nextStepGroupRID+$searchStep; }while($adResult['count']>1); var_dump($result); 

The $ adConn-> method looks like this:

 function search($filter, $attributes = false, $base_dn = null) { if(!isset($base_dn)){ $base_dn = $this->baseDN; } $entries = false; if (is_string($filter) && $this->bind) { if (is_array($attributes)) { $search = ldap_search($this->resource, $base_dn, $filter, $attributes); } else { $search = ldap_search($this->resource, $base_dn, $filter); } if ($search !== false) { $entries = ldap_get_entries($this->resource, $search); } } return $entries; } 
+1
source

Never make assumptions about server or server settings, this leads to fragile code and unexpected, sometimes spectacular crashes. Just because this AD today does not mean that it will be tomorrow, or that Microsoft will not change the default limit on the server. I recently came across a situation where the client code was written with tribal knowledge that the size limit was 2000 years, and when administrators, at their discretion, changed the size limit, the client code failed.

Are you sure that PHP does not support query controls (a simple query-derived extension is a query control)? I wrote an article on “LDAP: Simple Downloadable Results,” and although the sample code for the article is Java, concepts are important, not language. See Also LDAP: Programming Practices .

+1
source

A previous script error may occur when the distance between the nearest SID 999 is greater. Example:

S-1-5-21-3188256696-111411151-3922474875-1158

S-1-5-21-3188256696-111411151-3922474875- 3359

3359-1158> 999

to avoid this you need to use hard structures

Example:

 $tt = '1'; do { ... $nextStepGroupRID = $nextStepGroupRID - $searchStep; $tt++; } while ($tt < '30'); 

In this example, we are forced to check the values ​​999 * 30 * 2 = 59940.

+1
source

I managed to get around the size limit using ldap_control_paged_result

ldap_control_paged_result is used to enable LDAP pagination by sending the pagination control. The function below worked perfectly in my case. This will work (PHP 5> = 5.4.0, PHP 7)

 function retrieves_users($conn) { $dn = 'ou=,dc=,dc='; $filter = "(&(objectClass=user)(objectCategory=person)(sn=*))"; $justthese = array(); // enable pagination with a page size of 100. $pageSize = 100; $cookie = ''; do { ldap_control_paged_result($conn, $pageSize, true, $cookie); $result = ldap_search($conn, $dn, $filter, $justthese); $entries = ldap_get_entries($conn, $result); if(!empty($entries)){ for ($i = 0; $i < $entries["count"]; $i++) { $data['usersLdap'][] = array( 'name' => $entries[$i]["cn"][0], 'username' => $entries[$i]["userprincipalname"][0] ); } } ldap_control_paged_result_response($conn, $result, $cookie); } while($cookie !== null && $cookie != ''); return $data; } 

If you have successfully upgraded your server so far, the function above can get all the records. I use this function to get all users in our AD.

0
source

Source: https://habr.com/ru/post/1388019/


All Articles