由于我没有找到任何干净的解决方案,所以我决定采用第一种方法:通过对象SID进行过滤。
这种解决方法有其局限性:
- 它仅适用于具有对象SID的对象,即用户和组。
- 它假设所有用户/组都是由同一权限创建的。
- 它假设不存在更多丢失的相关SID,超出了大小限制。
思路是首先读取所有可能的对象,并挑选出相对SID最低的对象。相对SID是SID中的最后一个块:
S-1-5-21-3188256696-111411151-3922474875-1158
假设这是在仅返回“部分搜索结果”的搜索中最低的相对SID。
再假设大小限制为1000。
然后程序执行以下操作:
搜索所有SID位于以下范围内的对象
S-1-5-21-3188256696-111411151-3922474875-1158
和
S-1-5-21-3188256696-111411151-3922474875-0159
然后搜索所有位于以下范围内的对象
S-1-5-21-3188256696-111411151-3922474875-1158
和
S-1-5-21-3188256696-111411151-3922474875-2157
以此类推,直到其中一个搜索返回零个对象。
这种方法存在几个问题,但对于我的目的而言已足够。
代码如下:
$filter = '(objectClass=Group)';
$attributes = array('objectsid','cn');
$result = array();
$maxPageSize = 1000;
$searchStep = $maxPageSize-1;
$adResult = @$adConn->search($filter,$attributes);
$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);
$nextStepGroupRID = $minGroupRID;
do{
$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));
$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{
$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));
$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);
$adConn->search方法的样式如下:
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;
}
ldap_control_paged_result()
已被弃用。现在我们可以在ldap_search()
中传递 cookie。请参见 https://www.php.net/manual/fr/ldap.examples-controls.php 中的示例#5。 - Worst