选择 AD ntSecurityDescriptor 属性作为非管理员

2022-01-07 00:00:00 ldap active-directory php

我正在为 Active Directory ACL/ACE 开发 SDDL/安全描述符解析器.我几乎完成了,当我使用管理帐户连接到 LDAP 时,一切正常.

I'm working on a SDDL/Security Descriptor parser for Active Directory ACLs/ACEs. I'm nearly complete, everything works fine when I connect to LDAP using an administrative account.

但是,当我尝试将 ntSecurityDescriptor 作为非管理帐户查询时,它不返回任何值.用户帐户本身有权读取该属性.当我开始调查这个时,我遇到了以下 LDAP 服务器控件:

However, when I try to query the ntSecurityDescriptor as a non-administrative account it returns no values. The user account itself has rights to read the attribute. When I started to investigate this I ran across the following LDAP server control:

https://msdn.microsoft.com/en-us/library/cc223323.aspx

LDAP_SERVER_SD_FLAGS_OID 控件与 LDAP 搜索一起使用请求控制 Windows 安全描述符的一部分取回.DC 仅返回证券的指定部分描述符.它还与 LDAP 添加和修改请求一起使用控制要修改的 Windows 安全描述符部分.直流电仅修改安全描述符的指定部分.

The LDAP_SERVER_SD_FLAGS_OID control is used with an LDAP Search request to control the portion of a Windows security descriptor to retrieve. The DC returns only the specified portion of the security descriptors. It is also used with LDAP Add and Modify requests to control the portion of a Windows security descriptor to modify. The DC modifies only the specified portion of the security descriptor.

当将此控件发送到 DC 时,controlValue 字段设置为以下ASN.1结构的BER编码.

When sending this control to the DC, the controlValue field is set to the BER encoding of the following ASN.1 structure.

 SDFlagsRequestValue ::= SEQUENCE {
     Flags    INTEGER
 }

Flags 值具有以下格式,以 big-endian 字节表示命令.X 表示客户端应该设置为 0 的未使用位,并且必须被服务器忽略.

The Flags value has the following format presented in big-endian byte order. X denotes unused bits that SHOULD be set to 0 by the client and that MUST be ignored by the server.

指定未设置位或不使用 LDAP_SERVER_SD_FLAGS_OID 控件的 Flags 等效于将 Flags 设置为 (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION).将此控件发送到 DC 不会导致服务器在其响应中包含任何控件.

Specifying Flags with no bits set, or not using the LDAP_SERVER_SD_FLAGS_OID control, is equivalent to setting Flags to (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION). Sending this control to the DC does not cause the server to include any controls in its response.

文档中该声明的最后一部分似乎不正确,至少在非管理用户的上下文中不正确.

The last part of that statement from the docs does not appear to be correct, at least not when under the context of a non-administrative user.

我的问题:我应该如何使用标准的 PHP LDAP 库函数将此控件发送到 LDAP?我知道我必须设置服务器控件,但我不确定如何对值进行编码.我已将其缩小为最简单的示例:

My question: how am I supposed to send this control to LDAP using the standard PHP LDAP library functions? I know I have to set the server controls, but I'm not sure how to encode the value. I have narrowed this down to the simplest possible example:

$user = 'user@example.local';
$pass = 'secret';
$server = 'dc1.example.local';

$ldap = ldap_connect($server);
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

ldap_bind($ldap, $user, $pass);
$ctrl1 = array(
    "oid" => "1.2.840.113556.1.4.801",
    "iscritical" => true,
    // How should this value be set???
    "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 15)
);
if (!ldap_set_option($ldap, LDAP_OPT_SERVER_CONTROLS, array($ctrl1))) {
    echo "Failed to set server controls";
}

$searchUser = "user";
$dn = "dc=example,dc=local";
$filter="(sAMAccountName=$searchUser)";
$attr = array("ntsecuritydescriptor");

$sr = ldap_search($ldap, $dn, $filter, $attr);
$info = ldap_get_entries($ldap, $sr);

// Should contain ntSecurityDescriptor...but it does not.
var_dump($info);

我知道控件的值需要进行 BER 编码,但我不确定如何实现文档中定义的值.我能够找到以下 Java 示例:

I know the value for the control needs to be BER encoded, but I'm not sure how to achieve that for the value as it is defined in the docs. I was able to find the following Java example:

https://github.com/Tirasa/ADSDDL/blob/master/src/main/java/net/tirasa/adsddl/ntsd/controls/SDFlagsControl.java

但我一直无法将那里发生的事情翻译成 PHP.有什么想法吗?

But I have been unable to translate what's going on there to PHP. Any ideas?

推荐答案

问题似乎是非特权 AD 用户帐户将无法访问安全描述符的 SACL.为了解决这个问题并仍然检索 ntSecurityDescriptor(减去 SACL),发送带有所有其他标志设置的值(该值为 7)的控件:

The issue appears to be that non-privileged AD user accounts will not have access to the SACL of the security descriptor. To get around this and still retrieve the ntSecurityDescriptor (minus the SACL), send the control with a value of all other flags set (which would be a value of 7):

// OWNER_SECURITY_INFORMATION + GROUP_SECURITY_INFORMATION + DACL_SECURITY_INFORMATION
$sdFlags = 7;

$ctrl1 = array(
    "oid" => "1.2.840.113556.1.4.801",
    "iscritical" => true,
    "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, $sdFlags)
);
if (!ldap_set_option($ldap, LDAP_OPT_SERVER_CONTROLS, array($ctrl1))) {
    echo "Failed to set server controls";
}

我的猜测是 MS 文档没有错,LDAP_SERVER_SD_FLAGS_OID 的默认值是设置所有标志(包括 SACL).由于大多数普通帐户无权访问该 SACL,因此 AD 可能决定不返回安全描述符的任何部分,因此即使您选择了查询,也不会从查询中返回 ntSecurityDescriptor 值.

My guess is that the MS docs are not wrong, the default value of the LDAP_SERVER_SD_FLAGS_OID is for all flags to be set (which includes the SACL). Since most normal accounts do not have access to that SACL, AD probably decides to return no portion of the security descriptor, and thus no ntSecurityDescriptor value is returned from a query even though you select it.

另一个重要的注意事项,如果您使用 LDAP 分页,它似乎会干扰此控制.您不能同时使用分页和此控件.我不确定这是否是此控件的一般副作用,或者是在 PHP 的 LDAP 模块中如何完成服务器控件的问题.

Another important note, if you are using LDAP paging it seems to interfere with this control. You cannot use paging and this control at the same time. I'm not sure if this is a side-effect of this control in general, or an issue with how server controls are done in PHP's LDAP module.

相关文章