Selecting the AD ntSecurityDescriptor Attribute as a Non-Admin

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:


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 = '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);

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?


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 <strong>all</strong> 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.


  • FIWARE - Orion Context Broker offset parameter behaviour
  • SQL query results into php table
  • CSR failing: Error Parsing Request ASN1 bad tag value met (ASN: 267 CRYPT_E_ASN1_BADTAG)
  • How to encode an RSA key using PKCS12 in Python?
  • Removing a Watermark from an SWF file
  • Convert Byte[] to int
  • Deinterleaving of a 2-channel WAV file into two text files containing raw data [closed]
  • Dereferenceing on casting the void pointer to float*/int*
  • Get last insert id of Postgresql
  • Is it possible to generate a unique numeric value for each row in an iSeries table without looping?
  • Unable to use dot layout (graphviz as a library)
  • C++ - Is destructor called when a vector holds objects?
  • WebApp in AppServices vs CloudService
  • Cannot connect to native local socket on android 5.1
  • How do i find all references to a user control
  • Action Pack components in Rails
  • Is it possible to run clang with llc flags
  • Getting NullPointer exception with File.listfiles()
  • Sensibility of combined Maven/Ant+Ivy build management for dual platform Desktop/Android deployment?
  • Bootstrap (v3.3.4) glyphicons not displayed in IE when refresh page (F5)
  • Clear activity stack before launching another activity
  • Pycharm: Marking a folder as 'sources root' is not recursive for subfolders
  • Convert Type Decimal to Hex (string) in .NET 3.5
  • Z3: Convert between FP and BitVector?
  • Date Conversion from yyyy-mm-dd to dd-mm-yyyy
  • Textfile Structure (tables)
  • copying resource to sdcard gives a damaged file in android
  • Atlas images wrong size on iPad iOS 9
  • Email format validation in mvc3 view
  • Cannot connect to cassandra from Spark
  • Counter field in MS Access, how to generate?
  • Fill an image in a square container while keeping aspect ratio
  • Convert array of 8 bytes to signed long in C++
  • Font Awesome Showing Box instead of Icons
  • How do you troubleshoot character encoding problems?
  • Properly structure and highlight a GtkPopoverMenu using PyGObject
  • Why joiner is not used after Sequence generator or Update statergy
  • Is it possible to post an object from jquery to bottle.py?
  • Unable to use reactive element in my shiny app
  • Python/Django TangoWithDjango Models and Databases