Reputation: 2869
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.
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
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.
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 }
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.
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.
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 = '[email protected]';
$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);
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:
But I have been unable to translate what's going on there to PHP. Any ideas?
Upvotes: 4
Views: 5081
Reputation: 2869
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";
}
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.
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.
Upvotes: 5