超出 PHP ldap_search 大小限制

Env*_*rus 7 php size search ldap limit

我对查询 Microsoft 的 Active Directory 并遇到一些困难很陌生:

AD 的大小限制为每个请求 1000 个元素。我无法更改大小限制。PHP 似乎不支持分页(我使用的是 5.2 版,并且无法更新生产服务器。)

到目前为止,我遇到了两种可能的解决方案:

  1. 按 objectSid 对条目进行排序并使用过滤器来获取所有对象。示例代码
    我不喜欢这样的原因有几个:
    • 弄乱 objectSid 似乎是不可预测的,因为您必须将其拆开,将其转换为十进制,然后将其转换回......
    • 我不明白你如何比较这些 ID。
      (我试过:'&((objectClass=user)(objectSid>=0))')

  2. 对象名称的第一个字母(如建议后过滤器在这里):
    这不是尽可能多的用户的最佳解决方案/在我们的系统组前缀相同的几个字母。

所以我的问题:

这里最好使用什么方法?
如果是第一个,我如何确保正确处理 objectSid?

还有其他可能吗?我错过了一些明显的东西吗?

更新:
-相关问题提供了有关“简单分页结果”扩展为何不起作用的信息。
- Web 服务器在 Linux 服务器上运行,因此 COM 对象/adoDB 不是一个选项。

Fok*_*est 5

我能够使用ldap_control_paged_result绕过大小限制

ldap_control_paged_result用于通过发送分页控制来启用 LDAP 分页。下面的函数在我的例子中运行得很好。这适用于(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;
    }
Run Code Online (Sandbox Code Playgroud)

如果您现在已经成功更新服务器,那么上面的函数可以获取所有条目。我正在使用此功能来获取我们 AD 中的所有用户。


Env*_*rus 2

由于我没有找到任何干净的解决方案,因此我决定采用第一种方法:按 Object-Sids 过滤。

此解决方法有其局限性:

  1. 它仅适用于具有objectsid 的对象,即用户和组。
  2. 它假设所有用户/组都是由相同的权限创建的。
  3. 它假定缺失的相对 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'); //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);
Run Code Online (Sandbox Code Playgroud)

$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;
}
Run Code Online (Sandbox Code Playgroud)