Getting the `ntSecurityDescriptor` with an LDAP query

TL;DR

I found out how to retrieve the ntSecurityDescriptor in Active Directory, via LDAP.

Active Directory is a… beast and it’s not always easy to get what’s needed. This took me… some time.

One configuration that is supported for an account is the possibility to set a flag for preventing a user to change password. I’m not sure why there’s such a flag; I can only imagine that’s an easy way to lock an account (you just set a very random password, then prevent anyone from changing it).

There’s a promising flag PASSWD_CANT_CHANGE in userAccountControl, but alas:

You can’t assign this permission by directly modifying the UserAccountControl attribute. For information about how to set the permission programmatically, see the [Property flag descriptions][] section.

The link eventually lands us on page Modifying User Cannot Change Password (LDAP Provider), which is somehow helpful but not too much, because it includes code examples that assume the use of some Microsoft library for C++. I guess 🙄

Anyway, the page includes some interesting info:

  • the information that we’re after is in an Discretionary Access Control List (DACL)
  • it is included in property (/attribute) ntSecurityDescriptor in the LDAP record.

Only fact is that this attribute is nowhere to be found.

Wait a minute. What?!?

It turns out that depending on how you set the query parameters, this field will contain more or less information. By default it is supposed to contain everything, except that normal users are not supposed to see everything, so Active Directory does not include this attribute in the answer.

The trick consists in asking for less. This answer got me on the right track for a solution in Perl: we have to set the extension control LDAP_SERVER_SD_FLAGS_OID to a value that excludes the System Access Control List part from the answer, while keeping everything else. The answer also includes a quick and dirty way of building up the right value to pass for this control:

$sdFlags = 7;
...
   "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, $sdFlags)

This even works in Perl, but it’s a bit too hackish and I’d like to know more.

This page contains the information I was after:

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.

…

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
}

This makes it clearer the trick in the PHP code; it’s just a way to produce the BER encoding of the required data structure. In particular:

0x30 (48 decimal)    tag for a SEQUENCE
0x03 (3 decimal)     length of SEQUENCE (# of following octets)
    0X02 (2 decimal)     tag for an INTEGER
    0x01 (1 decimal)     length of INTEGER (# of following octets)
    0x07 ($sdFlags)      value of INTEGER

When using Perl, especially when using Net::LDAP, there’s a cleaner and more readable way of producing the same:

use Convert::ASN1;
use constant OWNER_SECURITY_INFORMATION => 0x01;
use constant GROUP_SECURITY_INFORMATION => 0x02;
use constant DACL_SECURITY_INFORMATION  => 0x04;
use constant SACL_SECURITY_INFORMATION  => 0x08;
my $asn = Convert::ASN1->new;
$asn->prepare(<<'END');
   SDFlagsRequestValue ::= SEQUENCE {
      Flags    INTEGER
   }
END
my $ldap_control_sd_flags = $asn->encode(Flags =>
     OWNER_SECURITY_INFORMATION
   | GROUP_SECURITY_INFORMATION
   | DACL_SECURITY_INFORMATION
);

Setting the control for the query is quite straightforward, as it’s part of the interface for the search method:

use Net::LDAP::Constant qw< LDAP_CONTROL_SD_FLAGS >;
use Net::LDAP::Control;
my $control = Net::LDAP::Control->new(
    critical => 1,
    type     => LDAP_CONTROL_SD_FLAGS,
    value    => $ldap_control_sd_flags,
);
...
my $res = $ldap->search(..., control => $control);
...

This should eventually give us the ntSecurityDescriptor we’re after (including the DACL), even when we’re not super-administrators.

Stay safe!


Comments? Octodon, , GitHub, Reddit, or drop me a line!