Files
Compose-Files/Backups/Miker/.trash/Information Security Descriptors.md
T
2026-07-20 09:23:17 -04:00

53 KiB
Raw Blame History

Active Directory Permissions : Security Descriptors

This article of the SelfADSI tutorial explains how to read or set permissions on Active Directory objects by script. The following sections are available:

The internal structureof a Security Descriptors (SD) Permission Inheritance
Access Control Lists (ACLs) Read AD Permissions in a Script
Access Control Entries (ACEs) Set AD Permissions in a Script
Control Flags in the SD Header Remove AD Permissions in a Script
Flags in ACEs
List of permissions - Access Mask Bits in the ACEs
Object Type GUIDs in ACEs
Inherited Type GUIDs in ACEs
Trustees in ACEs

Permissions in Active Directory are defined by so-called security descriptors, which are stored as properties directly in the AD objects. Actually, two different
Active Directory attributes are internally structured as a security descriptor:

nTSecurityDescriptor Alle Objekte Hier werden die Berechtigungen f?r das Objekt selbst gespeichert. Jedes Objekt hat einen eigenen Security Desriptor.
msExchMailboxSecurityDescriptor Postfach-Benutzer Diese Eigenschaft existiert nur in Microsoft Exchange-Umgebungen bei Benutzern mit Postfach. Es wird festgelegt, wer auf
das betreffende Postfach zugreifen darf - allerdings nur zum Zeitpunkt der Erstellung des Postfachs. Sp?ter werden Postfachrechte
nur durch dieses Attribut repr?sentiert, k?nnen jedoch nicht dar?ber gesetzt werden.

The basic LDAP attribute data type for these attributes is a is Microsoft's proprietary LDAP attribute syntax called String (NT Sec Desc). Basically, this is a binary format,
but the data contents of a security descriptor can be fortunately read by scripts through the ADSI interface. The relevant objects are described in detail in the ADSI reference:

But before we deal with the reading and setting of permissions in the Active Directory in scripting, we should take a look at the internal structures of a security descriptor.
The exact structure of a general security descriptor value is described in these documents Micrsoft:

Note: Security Descriptor structures are found not only at the permissions on directory objects, but also at
permissions in the NTFS file system, registry permission or in general with all permissions on system objects
(services, printers, etc.). This article focuses more on the specifics of the permissions in Active Directory, this
concerns mainly the construction of ACEs and Access Masks.

The internal structureof a Security Descriptors (SD)
The internal structure of a security descriptor is quite complex because we must consider several elements that are nested within each other:
The Security Descriptor as a whole consists of a header, the owner information, and two different access lists (ACLs). Regarding these, the
actual ACL permissions are included in the Discretionary ACL (DACL). This DACL in turn consists of several Access Control Entries (ACEs),
four of which are relevant to us.

Ein Active Directory Security Descriptor

When creating scripts this means the following: When a script wants to read the permissions of an Active Directory , it must first read the Security
Descirptor and the included DACL to get the list of ACEs. In that ACEs are the actual permissions stored. If you want to set permissions, so you have rarely to
built up a security descriptor from nothing by yourself - there simply are no AD object that is not already configured with permissions. Immediately after the
object creation permissions automatically result from the inheritance of the default settings. Granting or revoking permissions in scripts is thus based always
on the change of the existing list of ACEs.

Let us first consider the security descriptor with its construction of a header, owner information and ACLs. As mentioned above, we find a detailed description of the technical
structure of a security descriptor in the Microsoft Technet:

Struktur des Security Descriptors

Header: A structure that includes a general revision value as well as various control bits. These flags provide information about how and of what elements the security descriptor in
question is composed of - or will put together, if you want to create the structure with just a new Secruity descriptor. In addition, the control flags determine whether the permissions
and audit settings are inherited by parent structures, or whether the inheritance is blocked in this security descriptor. Additionally,
the header technically still contain offset addresses to determine the internal storage addresses of other components of the security descriptor.

Owner: The Object owner - technically in the form of a SID (security identifier). The owner is the user account that created the object. Owners may always change the permissions
on the object. A special feature: Did a member of the pre-defined group of administrators create the object, then this group is registered as owner. When this field is set in a script,
you can directly use a Windows login name, eg 'SELFADSI\pfoeckel'.

Owner Group: The group of the object owners - technically in the form of a SID (security identifier). The group owner is rarely
used in practice, usually only the owner plays a role - the owner group is simply the primary group of the owner.

SACL - System Access Control List: The SACL is the list in which the auditing settings for that object are stored (Which requests by whom are monitored and how?).
Only those who have the policy privilege 'Manage Auditing and Security Log' on the domain controllers can
read or write the SACL. Also keep in mind that there are still global audit flags which must be active for the directory service auditing on a domain controller (done via GPO).

DACL - Discretionary Access Control List: The DACL contains the access permissions on an object (Who can do what?). This is list with the actual permissions.
It can always be changed by the owner of the object and of all those who are authorized with WRITE_DAC (see list of Access Masks Bits) on the object. To read the
Discrentionary Access Control, you need the READ_CONTROL right.

Access Control Lists (ACLs)
An Access List (regardless whether a System ACL for auditing or a Discretionary ACL for permissions) always consists of a header and a number of Acces Control Entries (ACEs).
The exact technical structure of an ACL is described here:

Struktur einer Access Control List

It should be noted that the revision value for an Active Directory ACL must always 4. The ACEs are so to speak the individual permissions that we see in the
normal administrative management tool: Access Control Lists in der GUI
Access Control Entries (ACEs)
An ACE is the config strucutre for a single permision grant (or denial of rights) for a particular user or a particular group. There are many different types of ACEs, but in the permissions for
Active Directory, there are only four different types of meaning, two each for granting and the denying of permissions. The detailed technical structure of ACEs is described here:

There is a simple ACE form, which can be used to grant/revoke permissions for the entire Obekt (including standard inheritance). There are still the more complex forms of the so-called 
Object ACEs. Because in the following cases, the simple form of ACEs are not enough:

  • Permissions do not apply to the entire object, but only for a particular attribute or set of attributes (eg, write permission to the address information of a user object).
  • For permission to create/delete sub-objects, when this shall concerns only certain classes (eg the right to create child groups).
  • Special permission must be granted (some of which are maybe specific for other applications), eg the right to reset user passwords, or the right to send mail on behalf of another
  • user, or the right to perform an AD data replication.
  • Rights must be assigned to produce a self-reference to the object, eg the right to set yourself in a group as a member.

The four ACE-Types which are relevant for us, in detail:

Access Allowed ACE:

Access Allowed ACE

In this ACE the type field is always set to 1. It is designed for easy assignment of rights to the entire object. The ACE flags determine whether this is an inherited
or explicitly given permission. An authorization of the ACE type 0 is always passed down to child objects.

Access Allowed Object ACE:

Access Allowed Object ACE

In this ACE the type field is to always set to 5. This object ACE can set both ACE special rights or limited rights, eg granted only for certain attributes (=> ObjectType GUID)
as well as a special inheritance can be configured so they can inherit only certain classes of objects that right, or that the permission applies only to the object itself and is
not inherited (=> Interhited Type GUID). The flags field indicates whether there is an object type field or a inherited type field, or both.

Access Denied ACE:

Access Denied ACE

In this type of ACE is to always set to 1. It is designed for simple revoking of permissions for the entire object. The ACE flags determine whether this is an
inherited or explicitly assigned object revoking. A denial of the ACE type 5 is always propgated down to child objects.

 
Access Denied Object ACE:

Access Denied Object ACE

In this ACE the type field is to always set to 6. This object ACE can revoke both ACE special rights or limited rights, eg revoked only for certain attributes (=> ObjectType GUID)
as well as a special inheritance can be configured so they can inherit only certain classes of objects that right, or that the permission denial applies only to the object itself
and is not inherited (=> Interhited Type GUID). The flags field indicates whether there is an object type field or a inherited type field, or both.

Control Flags in the SD Header
The header of a security descriptor contains the so-called control flags. Many of the flags do not matter when dealing with normal Active Directory permissions.
The exact structure of the control flag is described here:

If you read a security descriptor, the following flags play an important role:

Control Flag Bit Value Meaning
DACL_AUTO_INHERITED 1024 The security descriptor is created through normal inheritance.
DACL_PROTECTED 4096 The security descriptor does NOT inherit permissions from parent objects. So if this flag is set, the rights inheritance for that AD object is disabled.

 
If you write or newly create a Security Descriptor, the following flags are important:

Control Flag Bit Value Meaning
OWNER_DEFAULTED 1 The Owner information is generated automatically. You can create a new Security Descriptor with this flag and does not need extra specify
the Owner. In this case, the SID of the creating user himself is the owner (Exception: If a member of the default administrators creates an
object, then the group and not the user account is the owner).
GROUP_DEFAULTED 2 The Owner Group information is generated automatically. You can create a new security descriptor with this flag, then an extra group ID
for the owner do not need to be specified. In this case, the primary group of the creating user is used as the owner group.
DACL_DEFAULTED 8 The DACL (the permissions) will be automatically inherited from the parent container/object. You can use this flag to create a new security
descriptor and simply inherit its rights without expicitely specifiying any permissions.
SACL_DEFAULTED 32 The SACL (the aduit settings) will be automatically inherited from the parent container/object. You can use this flag to create a new security
descriptor and simply inherit its audit settings without expicitely specifiying any settings.

Flags in ACEs
The flags which are described here are part of every single Access Control Entry. So it may exist many times within one security descriptor. Caution, risk of confusion:
In ACEs there are two types of flags: The "ACE Flags" and the "Flags". They control the inheritance settings of the regarding ACEs and may activate any existing Object type fields.

In every ACE there are "ACE Flags" that are part of the technical ACE header. These flags determine the mode of inheritance of new ACEs - or indicate the inheritance situation
when you read the relevant ACE.

Flag Abbreviation
in SDDL
Value
(dez)
Value
(hex)
Explanation
ADS_ACEFLAG_INHERIT_ACE
  also called
CONTAINER_INHERIT_ACE
CI 2 2 Child objects inherit the permissions. The flag 'Object Inherit (OI)' which plays an important
role in the permissions for files and directories, is not required for AD rights. In AD permissions
only the "Container Inherit (CI)" flag is used. It refers to all objects, even those who do not have
sub-objects to themselves.
ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE NP 4 4 Only the immediate child objects inherit the permissions. Can be set as an addition to the CI flag.
ADS_ACEFLAG_INHERIT_ONLY_ACE IO 8 8 The permission is valid only for the child objects, but not for the actual object itself. Can be set as
an addition to the CI flag.
ADS_ACEFLAG_INHERITED_ACE ID 16 10 This flag indicates that this is a permission inherited from the top down. This flag can not be set,
but can be checked to find inherited rights.

There are also other flag values they either don't exist in Active Directory ACLs, or aren't important for permissions, but for the audit settings in SACLs. A complete documentation of the
flags can be found here:

The propagation of the permission to child objects can be specified more detailed in the more complex object ACEs (ACE type 5 and 6). There it can be determined for example whether
such a right should only apply to child objects users, or only to groups. These must be placed in the appropriate ACEs the "Inherited Type". Whether this is the case, you can see in the
so-called 'Flags' field:

Flag Bit Value Meaning
OBJECT_TYPE_PRESENT 1 The Object Type field in the ACE exists and contains an Object GUID with the following meaning

- For permission to create and delete child objects, the regarding object must be specified here. In the Object Type field,
- there is the GUID of an object class from the AD schema.
- For permissions to read and write properties, the attribute or the property set are given here to which the right relates. In the
- Object Type field, there is an attribute GUID from the schema or the GUID of a property sets from the AD configuration partition 
- (container "Extended Rights").
- Regarding extended rights, the specific permission can be determined. In the Object Type field, there is an extended rights GUID
- from the AD configuration partition (container "Extended Rights").
INHERITED_OBJECT_TYPE_PRESENT 2 The Inherited Object Type field in the ACE exists and contains an Object GUID of an object class from the AD schema.

A complete documentation of the flags can be found here:

List of permissions - Access Mask Bits in the ACEs
This is a list of permissions on Active Directory directory objects, as contained in the above-described access control entries (ACEs). For some access masks, various bits can
be set simultaneously, eg there can be defined in a single ACE that someone has the permission to create user objects as well as to delete them (CC + DC). However,
there are obvious limitations, because in a single object-ACE is room for exactly one ObjectType GUID, so it's as not possible to set the permission to delete user objects
and for writing the address properties in the same ACE.
 
The technical description of the Access mask bits from Microsoft:

Permission Abbreviation
in SDDL
Value Value
(hex)
Explanation
ADS_RIGHT_DS_CREATE_CHILD CC 1 1 Create child Objects. This right can be restricted to certain classes of objects. In this case the ACE property
ObjectType must be set to the GUID of the appropriate object class (SchemaIDGuid in the class definition of the
schema).
ADS_RIGHT_DS_DELETE_CHILD DC 2 2 Delete child Objects. This right can be restricted to certain classes of objects. In this case the ACE property
ObjectType must be set to the GUID of the appropriate object class (SchemaIDGuid in the class definition of
the schema).
ADS_RIGHT_ACTRL_DS_LIST LC 4 4 List child objects.
ADS_RIGHT_DS_SELF WS



(auch SW)
8 8 Perform so called 'Validated Writes'. This right can be restricted to certain Validated Writes, eg 'Add yourself as
a member', 'Set the own DNS address' etc. In this case the ACE property ObjectType must be the GUID of the
appropriate ExtendedRights included (RightsGuid value from the Configuration Partition \ Extended-Rights).
ADS_RIGHT_DS_READ_PROP RP 16 10 Read properties / attribute values of the regarding object. In this case the ACE property ObjectType must be
set to the GUID of the appropriate attribute (SchemaIDGuid in the attribute definition of the schema).
ADS_RIGHT_DS_WRITE_PROP WP 32 20 Write properties / set attribute values of the regarding object. In this case the ACE property ObjectType must
be set to the GUID of the appropriate attribute (SchemaIDGuid in the attribute definition of the schema).
ADS_RIGHT_DS_DELETE_TREE DT 64 40 Delete all subordinate child objects, no matter which permission is configured in the regarding sub structure.
ADS_RIGHT_DS_LIST_OBJECT LO 128 80 List the object in question, although the general permission "ADS_RIGHT_ACTRL_DS_LIST" was maybe denied
in the regarding container. It's kind of an overwrite for individual objects that are visible, although the rest of the
container is hidden. This permission is effective only when the third dsHeuristics flag is set to 1.
ADS_RIGHT_DS_CONTROL_ACCESS CA



(auch CR)
256 100 Perform extended rights. This can be further restricted to specific extended rights, such as "Reset Password",
"Change Rid Master", "Replication Synchronization", "Migrate SID History", etc. In this case the ACE property
ObjectType must set to the GUID of the appropriate ExtendedRights (RightsGuid value from the Configuration
Partition \ Extended-Rights).
ADS_RIGHT_DELETE DE 65536 10000 Delete the regarding object.
ADS_RIGHT_READ_CONTROL RC 131072 20000 Read the permissions (DACL) and the owner properties of the regarding object.
ADS_RIGHT_WRITE_DAC WD 262144 40000 Change The permissions (DACL) of the regarding object. Note that the owner can always change the DACL, even
if he do not have explicitly this right.
ADS_RIGHT_WRITE_OWNER WO 524288 80000 Change the owner property of the regarding object.
ADS_RIGHT_SYNCHRONIZE SY 1048576 100000 You will not see this permission when you deal with AD object rights - nor set this permission flag by yourself for
any AD object. It's a generic permission and not required for Active Directory objects.
ADS_RIGHT_ACCESS_SYSTEM_SECURITY AS 16777216 1000000 You will not see this permission when you deal with AD object rights - nor set this permission flag by yourself for
any AD object. This bit has a meaning Only in SACLs audit settings: You can specify that the access to the SACL
itsel fis audited .
ADS_RIGHT_GENERIC_ALL GA 268435456 10000000 You will not see this permission when you read AD object rights. However, you can use this bit if you set AD object
permissions, the system is internally translating this bit into the following combination of 'real' AD object rights:
ADS_RIGHT_DS_CREATE_CHILD +
ADS_RIGHT_DS_DELETE_CHILD +
ADS_RIGHT_ACTRL_DS_LIST +
ADS_RIGHT_DS_SELF +
ADS_RIGHT_DS_READ_PROP +
ADS_RIGHT_DS_WRITE_PROP +
ADS_RIGHT_DS_DELETE_TREE +
ADS_RIGHT_DS_LIST_OBJECT + ADS_RIGHT_DS_CONTROL_ACCESS +
ADS_RIGHT_DELETE +
ADS_RIGHT_READ_CONTROL +
ADS_RIGHT_WRITE_DAC +
ADS_RIGHT_WRITE_OWNER
ADS_RIGHT_GENERIC_EXECUTE GX 536870912 20000000 You will not see this permission when you read AD object rights. However, you can use this bit if you set AD object
permissions, the system is internally translating this bit into the ADS_RIGHT_ACTRL_DS_LIST permission (list chlid objects):
ADS_RIGHT_GENERIC_WRITE GW 1073741824 40000000 You will not see this permission when you read AD object rights. However, you can use this bit if you set AD object
permissions, the system is internally translating this bit into the following combination of 'real' AD object rights:
ADS_RIGHT_DS_SELF +
ADS_RIGHT_DS_WRITE_PROP +
ADS_RIGHT_READ_CONTROL
ADS_RIGHT_GENERIC_READ GR 2147483648 80000000 You will not see this permission when you read AD object rights. However, you can use this bit if you set AD object
permissions, the system is internally translating this bit into the following combination of 'real' AD object rights:
ADS_RIGHT_DS_READ_PROP +
ADS_RIGHT_ACTRL_DS_LIST +
ADS_RIGHT_READ_CONTROL

Object Type GUIDs in ACEs

In complex ACEs of type 5 and 6, there are a field named 'ObjectType GUID'. There may be three cases in which this field is used. In these situations, the flag OBJECT_TYPE_PRESENT (1)
in the respective access control entry must be set. By the way, the GUID here can be read and set in script as a simple character string.

  • For CC / DC permissions (Create Child / Delete Child), the child object class can be specified here. In this case there is the GUID of an object class from the AD schema the Object
  • Type field (SchemaIDGuid value in the class definition of the schema). A few important GUID values:====
Object Class GUID
Organizational Units bf967aa5-0de6-11d0-a285-00aa003049e2
Computer bf967a86-0de6-11d0-a285-00aa003049e2
User bf967aba-0de6-11d0-a285-00aa003049e2
Groups bf967a9c-0de6-11d0-a285-00aa003049e2
Contacts 5cb41ed0-0e4c-11d0-a286-00aa003049e2
  • For RP / WP permission (Read Property / Write Property) here, the attribute or the property set can be specified here. In this case the Object Type field must be filled with
  • a GUID of an attribute from the AD schema or the property set GUID from the configuration partition (container "Extended Rights") in . A few important attribute GUID values:====
Attribute GUID
department bf96794f-0de6-11d0-a285-00aa003049e2
description bf967950-0de6-11d0-a285-00aa003049e2
displayName bf967953-0de6-11d0-a285-00aa003049e2
givenName f0f8ff8e-1191-11d0-a060-00aa006c33ed
mail bf967961-0de6-11d0-a285-00aa003049e2
member bf9679c0-0de6-11d0-a285-00aa003049e2
physicalDeliveryOfficeName bf9679f7-0de6-11d0-a285-00aa003049e2
proxyAddresses bf967a06-0de6-11d0-a285-00aa003049e2
sn bf967a41-0de6-11d0-a285-00aa003049e2
telephoneNumber bf967a49-0de6-11d0-a285-00aa003049e2

A few examples for property set GUID values:

Property Set GUID
General Information 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
Personal Information 77b5b886-944a-11d1-aebd-0000f80367c1
Private Information 91e647de-d96f-4b70-9557-d63ff4f3ccd8
Public Information e48d0154-bcf8-11d1-8702-00c04fb96050
  • For extended CA rights (Control Access), the specific permission be specified here. In this case, the Object Type field must be filled with a GUID of an extended right from the
  • config partition (container "Extended Rights"). Some examples of ExtendedRight GUIDs:====
Extended Right GUID
Administer Exchange information store d74a8762-22b9-11d3-aa62-00c04f8eedd8
Create Inbound Forest Trust e2a36dc9-ae17-47c3-b58b-be34c55ba633
Change Password ab721a53-1e2f-11d0-9819-00aa0040529b
Migrate SID History ba33815a-4f93-4c76-87f3-57574bff8109
Reanimate Tombstones 45ec5156-db7e-47bb-b53f-dbeb2d03c40f
Receive As ab721a56-1e2f-11d0-9819-00aa0040529b
Replication Synchronization 1131f6ab-9c07-11d1-f79f-00c04fc2dcd2
Reset Password 00299570-246d-11d0-a768-00aa006e0529
Send As ab721a54-1e2f-11d0-9819-00aa0040529b
Unexpire Password ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501
View Exchange information store status d74a875e-22b9-11d3-aa62-00c04f8eedd8

Inherited Type GUIDs in ACEs
In complex ACEs of type 5 and 6, there are field named 'Inherited Object Type GUID'. It can be used to pass on the privilege only to specific classes of objects
(eg "The display name can be changed, but only in groups"). In this case, the flag INHERITED_OBJECT_TYPE_PRESENT (2) in the respective access control entry
must be set. In th the Inherited Object Type field, there is the GUID of an object class from the AD schema (SchemaIDGuid value in the class definition of the schema).
By the way, the GUID here can be read and set in script as a simple character string. A few important object class GUID values:

Objektklasse GUID
Organizational Units bf967aa5-0de6-11d0-a285-00aa003049e2
Computer bf967a86-0de6-11d0-a285-00aa003049e2
User bf967aba-0de6-11d0-a285-00aa003049e2
Groups bf967a9c-0de6-11d0-a285-00aa003049e2
Contacts 5cb41ed0-0e4c-11d0-a286-00aa003049e2

Trustees in ACEs
An Access Control Entriy (ACEs) always contains always the trustee, that means the account or group for which permissions are granted or revoked. When setting and reading trustee
values, there may be two forms used:

  • If you run a script to read or set permissions in the same Active Directory domain, then trustees will simply be represented in the NetBIOS notation 'DOMAIN\username'
  • or 'DOMAIN\groupname'.
  • If you are not automatically logged in the domain with your script, you have to use explicit credentials. Trustees must be specified then as technical security identifiers (SIDs),
  • eg 'S-1-5-21-1621763826-2590103247-2238570322-1113'. You must of course know how to get the SID of a user or group.

There is a detailed SelfADSI tuorial article about the structure of Microsoft SIDs and how to deal with them: "Microsoft Security Identifier (SID) Attribute".

Permission Inheritance
In the inheritance of permissions for Active Directory objects, there are several interesting aspects:

  1. You may want to examine directory object in a script and check, whether the inheritance of permissions was basically disabled / blocked. Here you only need to check the 
  2. DACL_PROTECTED bit in the control flags of the entire security descriptor.
  3. You may want to find out via script for a single existing permission, whether it was inherited from the top down, or set explicitly. You can check the relevant Access Control Entry,
  4. whether INHERITED_ACE the flag is set there in the ACEFlags.
  5. If you want to set new permissions by script, you want to determine whether and how these rights will be inherited by child objects. A large role is played by
  6. the flags in the ACE generated by you. You can use this matrix: Vererbungsreglen fr Active Directory Access Contro...

Read Active Directory Permissions in a Script
Here is an example how to read the permissions of a single Active Directory object. For this purpose we have to read and evaluate the LDAP attribute nTSecurityDesriptor:
**'you have to use an object of your own environment!******Set obj = GetObject("LDAP://CN=test,OU=users,OU=example,DC=ldapexplorer,DC=com")
Const ADS_FLAG_OBJECT_TYPE_PRESENT = 1 ****Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = 2
Set objACL = obj.get("nTSecurityDescriptor") ****Set objDACL = objACL.DiscretionaryAcl
WScript.Echo obj.distinguishedname & vbCrLf
Set ace = CreateObject("AccessControlEntry") ****For Each ace In objDACL WScript.Echo "__________________________________________________________________________________________________" 'pure ACE data output output = "ACEType: " & ace.AceType & ", ACEFlags: " & ace.AceFlags & ", Mask: " & ace.AccessMask If (ace.AceType = 5) Or (ace.AceType = 6) Then output = output & ", Flags: " & ace.Flags
If ((ace.Flags And ADS_FLAG_OBJECT_TYPE_PRESENT) <> 0) Then    output = output & ", ObjectTypeGUID: " & ace.ObjectType End If
If ((ace.Flags And ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT) <> 0) Then     output = output & ", InheritedObjectTypeGUID: " & ace.InheritedObjectType End If End If output = output & ", Trustee: " & ace.Trustee WScript.Echo output ****Next
Download Script

Download Icon

The story becomes more complicated if you want a detailed analysis of the permissions and propagated inheritance. This is because the script have to seek and decode
all the regarding GUIDs in the schema or the config partition. The two functions decode_permission and decode_propagation are used for this.

This script has to be run as an administrator on a system that is member in the regarding domain.
'you have to use an object of your own environment!****Set obj = GetObject("LDAP://CN=test,OU=users,OU=example,DC=ldapexplorer,DC=com")
Const ADS_ACETYPE_ACCESS_ALLOWED = 0**Const ADS_ACETYPE_ACCESS_DENIED = 1Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 5Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = 6**
Const ADS_ACEFLAG_INHERITED_ACE = &H10**Const ADS_ACEFLAG_INHERIT_ACE = &H2Const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE = &H4Const ADS_ACEFLAG_INHERIT_ONLY_ACE = &H8**
Const ADS_RIGHT_DS_CREATE_CHILD = &H1**Const ADS_RIGHT_DS_DELETE_CHILD = &H2Const ADS_RIGHT_ACTRL_DS_LIST = &H4Const ADS_RIGHT_DS_SELF = &H8Const ADS_RIGHT_DS_READ_PROP = &H10Const ADS_RIGHT_DS_WRITE_PROP = &H20Const ADS_RIGHT_DS_DELETE_TREE = &H40Const ADS_RIGHT_DS_LIST_OBJECT = &H80Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100Const ADS_RIGHT_DELETE = &H10000Const ADS_RIGHT_READ_CONTROL = &H20000Const ADS_RIGHT_WRITE_DAC = &H40000Const ADS_RIGHT_WRITE_OWNER = &H80000Const ADS_RIGHT_GENERIC_ALL = &H10000000Const ADS_RIGHT_GENERIC_WRITE = &H40000000Const ADS_RIGHT_GENERIC_READ = &H80000000**
Const ADS_FLAG_OBJECT_TYPE_PRESENT = 1**Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = 2**
Set objACL = obj.get("nTSecurityDescriptor")****Set objDACL = objACL.DiscretionaryAcl
WScript.Echo obj.distinguishedname & vbCrLf****
Set ace = CreateObject("AccessControlEntry") For Each ace In objDACL WScript.Echo "__________________________________________________________________________________________________"**** '1) -> first: pure ACE data output**** output = "ACEType: " & ace.AceType & ", ACEFlags: " & ace.AceFlags & ", Mask: " & ace.AccessMask If (ace.AceType = 5) Or (ace.AceType = 6) Then**** output = output & ", Flags: " & ace.Flags****
If ((ace.Flags And ADS_FLAG_OBJECT_TYPE_PRESENT) <> 0) Then****    output = output & ", ObjectTypeGUID: " & ace.ObjectType**** End If****
If ((ace.Flags And ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT) <> 0) Then****     output = output & ", InheritedObjectTypeGUID: " & ace.InheritedObjectType**** End If**** End If**** output = output & ", Trustee: " & ace.Trustee**** WScript.Echo output****
'2) -> decode ACE type**** If ((ace.AceFlags And ADS_ACEFLAG_INHERITED_ACE) <> 0) Then WScript.Echo " Inherited"**** Else**** WScript.Echo " Directly"**** End If****
If (ace.AceType = ADS_ACETYPE_ACCESS_ALLOWED) Or (ace.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT) Then**** output = "Grant "**** Else output = "Deny "**** End If**** output = output & "to " & ace.Trustee WScript.Echo " " & output****
'3) -> decode permissions**** WScript.Echo " " & decode_permission(ace.AccessMask, ace.ObjectType)****
'4) -> decode inheritance**** WScript.Echo " " & decode_propagation(ace.AceFlags, ace.InheritedObjectType)****Next
Function decode_permission(mask, guid)**** 'converts an access mask value to a readable string****
classNeeded = (((mask And ADS_RIGHT_DS_CREATE_CHILD) <> 0) Or ((mask And ADS_RIGHT_DS_DELETE_CHILD) <> 0))**** attributeNeeded = (((mask And ADS_RIGHT_DS_READ_PROP) <> 0) Or ((mask And ADS_RIGHT_DS_WRITE_PROP) <> 0))**** extendedNeeded = ((mask And ADS_RIGHT_DS_CONTROL_ACCESS) <> 0)**** validatedNeeded = ((mask And ADS_RIGHT_DS_SELF) <> 0)**** guidExists = (Len(guid) > 0)****
'handle objectTypeGuide... If (guidExists) Then**** If (classNeeded And attributeNeeded) Then**** objectclass = guid_to_class(ace.ObjectType)**** attribute = guid_to_attribute(ace.ObjectType)****
foundNoClass = (Left(objectclass, 1) = "{") foundNoAttribute = (Left(attribute, 1) = "{")
If (foundNoClass And foundNoAttribute) Then**** objectclass = " (" & objectclass & ")"**** attribute = " (" & attribute & ")"**** ElseIf (foundNoClass) Then objectclass = " (All Classes)"**** attribute = "(" & attribute & ")" Else**** objectclass = " (" & objectclass & ")"**** attribute = " (All Properties)"**** End If**** ElseIf (classNeeded) Then**** objectclass = " (" & guid_to_class(ace.ObjectType) & ")"**** ElseIf (attributeNeeded) Then**** attribute = " (" & guid_to_attribute(ace.ObjectType) & ")"**** ElseIf (extendedNeeded) Then**** extended = " (" & guid_to_right(ace.ObjectType) & ")"**** ElseIf (validatedNeeded) Then**** validated = " (" & guid_to_right(ace.ObjectType) & ")"**** End If**** Else**** objectclass = " (All Classes)"**** attribute = " (All Properties)"**** extended = " (All Extended Rights)"**** validated = " (All validated Rights)"**** End If****
decode_permission = ""****
If ((mask And ADS_RIGHT_DS_CREATE_CHILD) <> 0) Then**** decode_permission = decode_permission & vbcrlf & " Create Child" & objectclass**** End If****
If ((mask And ADS_RIGHT_DS_DELETE_CHILD) <> 0) Then decode_permission = decode_permission & vbcrlf & " Delete Child" & objectclass**** End If****
If ((mask And ADS_RIGHT_ACTRL_DS_LIST) <> 0) Then decode_permission = decode_permission & vbcrlf & " List Childs"**** End If****
If ((mask And ADS_RIGHT_DS_SELF) <> 0) Then decode_permission = decode_permission & vbcrlf & " Validated Write" & validated**** End If****
If ((mask And ADS_RIGHT_DS_READ_PROP) <> 0) Then decode_permission = decode_permission & vbcrlf & " Read Property" & attribute**** End If****
If ((mask And ADS_RIGHT_DS_WRITE_PROP) <> 0) Then decode_permission = decode_permission & vbcrlf & " Write Property" & attribute**** End If****
If ((mask And ADS_RIGHT_DS_DELETE_TREE) <> 0) Then decode_permission = decode_permission & vbcrlf & " Delete Tree"**** End If****
If ((mask And ADS_RIGHT_DS_LIST_OBJECT) <> 0) Then decode_permission = decode_permission & vbcrlf & " List Object"**** End If****
If ((mask And ADS_RIGHT_DS_CONTROL_ACCESS) <> 0) Then decode_permission = decode_permission & vbcrlf & " Control Access" & extended**** End If****
If ((mask And ADS_RIGHT_DELETE) <> 0) Then decode_permission = decode_permission & vbcrlf & " Delete"**** End If****
If ((mask And ADS_RIGHT_READ_CONTROL) <> 0) Then decode_permission = decode_permission & vbcrlf & " Read Control"**** End If****
If ((mask And ADS_RIGHT_WRITE_DAC) <> 0) Then decode_permission = decode_permission & vbcrlf & " Write DAC"**** End If****
If ((mask And ADS_RIGHT_WRITE_OWNER) <> 0) Then decode_permission = decode_permission & vbcrlf & "Write Owner"**** End If****
If (Len(decode_permission) > 1) Then decode_permission = Mid(decode_permission, 7)**** End If**End Function**
Function decode_propagation(flags, guid)**** 'converts the ACE inheritance configuration to child objects in the corresponding readable string****
guidExists = (Len(guid) > 0)**** decode_propagation = ""**** If ((flags And ADS_ACEFLAG_INHERIT_ACE) = 0) Then**** decode_propagation = "This Object Only"**** Else**** If ((flags And ADS_ACEFLAG_INHERIT_ONLY_ACE) = 0) Then**** decode_propagation = decode_propagation & "Only "**** Else**** decode_propagation = "This object and "**** End If****
If ((flags And ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE) = 0) Then decode_propagation = decode_propagation & "only direct child objects"**** Else**** decode_propagation = decode_propagation & "child objects"**** End If****
If (guidExists) Then**** decode_propagation = decode_propagation & " (" & guid_to_class(guid) & ")"**** End If**** End If**End Function**
Function guid_to_class(guidString)**** 'converts an objectclass schema-GUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" in the name of a class****
'create search filter**** guidFilter = "(&(objectClass=classSchema)(schemaIDGUID=" & guid_to_filter(guidString) & "))"****
'evaluate the schema path**** Set rootDSE = GetObject("LDAP://rootDSE") schemaDN = rootDSE.Get("schemaNamingContext")****
'search class**** Set ado = CreateObject("ADODB.Connection")**** ado.Provider = "ADSDSOObject"**** ado.Open "ADSearch" On Error Resume Next**** Set classObj = ado.Execute("<LDAP://" & schemaDN & ">;" & guidFilter & ";lDAPDisplayName;onelevel")**** If (Err.Number = 0) And (classObj.RecordCount = 1) Then**** guid_to_class = CStr(classObj.Fields(0).Value)**** Else**** guid_to_class = Mid(guidString, 2, Len(guidString) - 2)**** End If**End Function**
Function guid_to_attribute(guidString)**** 'converts an objectclass schema-GUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" to ...**** '...the name of the corresponding attribute (or property sets) name****
'create search filter guidFilter = "(&(objectClass=attributeSchema)(schemaIDGUID=" & guid_to_filter(guidString) & "))"****
'evaluate the schema path**** Set rootDSE = GetObject("LDAP://rootDSE") schemaDN = rootDSE.Get("schemaNamingContext")****
'search attribute**** Set ado = CreateObject("ADODB.Connection")**** ado.Provider = "ADSDSOObject"**** ado.Open "ADSearch" On Error Resume Next**** Set classObj = ado.Execute("<LDAP://" & schemaDN & ">;" & guidFilter & ";lDAPDisplayName;onelevel")**** If (Err.Number = 0) And (classObj.RecordCount = 1) Then**** guid_to_attribute = CStr(classObj.Fields(0).Value)**** Else**** 'no result => we search for a property set in the config partition**** configDN = rootDSE.Get("configurationNamingContext")**** extendedDN = "CN=Extended-Rights," & configDN**** guidString = Mid(guidString, 2, Len(guidString) - 2)**** guidFilter = "(&(rightsGuid=" & guidString & ")(validAccesses=48))"****
Err.Clear**** Set classObj = ado.Execute("<LDAP://" & extendedDN & ">;" & guidFilter & ";displayName;onelevel")**** If (Err.Number = 0) And (classObj.RecordCount = 1) Then**** guid_to_attribute = CStr(classObj.Fields(0).Value)**** Else**** guid_to_attribute = guidString**** End If**** End If**End Function**
Function guid_to_right(guidString)**** 'converts an objectclass schema-GUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" to the name**** '...of the extended rights / validated write****
'create search filter**** guidString = Mid(guidString, 2, Len(guidString) - 2) guidFilter = "(rightsGuid=" & guidString & ")"****
'evaluate the path to the extended rights container**** Set rootDSE = GetObject("LDAP://rootDSE") configDN = rootDSE.Get("configurationNamingContext")**** extendedDN = "CN=Extended-Rights," & configDN****
'search extended right**** Set ado = CreateObject("ADODB.Connection")**** ado.Provider = "ADSDSOObject"**** ado.Open "ADSearch" On Error Resume Next**** Set classObj = ado.Execute("<LDAP://" & extendedDN & ">;" & guidFilter & ";displayName;onelevel")**** If (Err.Number = 0) And (classObj.RecordCount = 1) Then**** guid_to_right = CStr(classObj.Fields(0).Value)**** Else**** guid_to_right = guidString**** End If**End Function**
Function guid_to_filter(guidString)**** 'converts an objectclass schema-GUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" to the**** '....corresponding LDAP search filter for the binary data****
guidValue = Array()**** ReDim guidValue(16)**** guidValue(0) = Mid(guidString, 8, 2)**** guidValue(1) = Mid(guidString, 6, 2)**** guidValue(2) = Mid(guidString, 4, 2)**** guidValue(3) = Mid(guidString, 2, 2)**** guidValue(4) = Mid(guidString, 13, 2)**** guidValue(5) = Mid(guidString, 11, 2)**** guidValue(6) = Mid(guidString, 18, 2)**** guidValue(7) = Mid(guidString, 16, 2)**** guidValue(8) = Mid(guidString, 21, 2)**** guidValue(9) = Mid(guidString, 23, 2)**** guidValue(10) = Mid(guidString, 26, 2)**** guidValue(11) = Mid(guidString, 28, 2)**** guidValue(12) = Mid(guidString, 30, 2)**** guidValue(13) = Mid(guidString, 32, 2)**** guidValue(14) = Mid(guidString, 34, 2)**** guidValue(15) = Mid(guidString, 36, 2)**** guid_to_filter = ""**** For i = 0 To 15**** guid_to_filter = guid_to_filter & "" & guidValue(i)**** Next****End Function
Download Script

Set Active Directory Permissions in a Script
Here is an example of how to assign permission for a single object in the Active Directory. For this purpose, you have to read the nTSecurityDesriptor attribute first.
Then you can change the DAC within by adding new ACE entries. Later, the entire attribute is written back.

A trustee (for example user or a group from the same AD forest) gets permissions on a OU:

  • Delete this OU (this counts only for the OU itself, so it should be empty then)
  • Write permissions on the attribute "description" for all objects in this OU
  • Reset passwords for user accounts in this OU

Please note the appropriate ACE flags and object GUIDs which are used for this permissions. The script has to be run as an administrator on a system that is member in
the regarding domain:
'you have to use an object of your own environment!****Set obj = GetObject("LDAP://OU=users,OU=example,DC=ldapexplorer,DC=com")
trustee = "LEX\AccessUser"****
Const ADS_REVISION_DS = 4****
Const ADS_ACETYPE_ACCESS_ALLOWED = 0**Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 5**
Const ADS_ACEFLAG_INHERIT_ACE = &H2**Const ADS_ACEFLAG_INHERIT_ONLY_ACE = &H8**
Const ADS_RIGHT_DS_READ_PROP = &H10**Const ADS_RIGHT_DS_WRITE_PROP = &H20Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100Const ADS_RIGHT_DELETE = &H10000**
Const ADS_FLAG_OBJECT_TYPE_PRESENT = 1**Const ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = 2**
Const GUID_ATTR_DESCRIPTION = "{bf967950-0de6-11d0-a285-00aa003049e2}"****Const GUID_CLASS_USER = "{bf967aba-0de6-11d0-a285-00aa003049e2}"Const GUID_RIGHT_RESETPASSWORD = "{ab721a53-1e2f-11d0-9819-00aa0040529b}"
Set objACL = obj.get("nTSecurityDescriptor")****Set objDACL = objACL.DiscretionaryAcl
WScript.Echo obj.distinguishedname & vbCrLf****
'create a new ACE entry: delete th OU, but not the child objects therein**Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = trusteenewAce.AceType = ADS_ACETYPE_ACCESS_ALLOWEDnewAce.AceFlags = 0newAce.AccessMask = ADS_RIGHT_DELETEnewACE.Flags = 0objDACL.AddAce newAce
'create a new ACE entry: read/write the 'description' field for all contained obejcts
Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = trusteenewAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECTnewAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE Or ADS_ACEFLAG_INHERIT_ONLY_ACEnewAce.AccessMask = ADS_RIGHT_DS_READ_PROP Or ADS_RIGHT_DS_WRITE_PROPnewACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENTnewACE.ObjectType = GUID_ATTR_DESCRIPTIONobjDACL.AddAce newAce
'create a new ACE entry: reset password for user accounts
Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = trusteenewAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECTnewAce.AceFlags = ADS_ACEFLAG_INHERIT_ACEnewAce.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESSnewACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT Or ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENTnewACE.ObjectType = GUID_RIGHT_RESETPASSWORDnewACE.InheritedObjectType = GUID_CLASS_USERobjDACL.AddAce newAce
'write new DACL
objACL.DiscretionaryAcl = objDACLobj.Put "ntSecurityDescriptor", objACL**obj.SetInfo
Download Script

Download Icon
Remove Active Directory Permissions in a Script
Here is an example of how to get the permission for a single object in the Active Directory removed. For this purpose the nTSecurityDesriptor attribute has to be read.
We read from the contained Discretionary Access Control List (DACL) all ACEs that are not inherited from above, and which do not grant permissions to the trustee which
you want to remove from the DACL. In rewriting the entire attribute, then the permissions inherited from abovecome back automatically.
'you have to use an object of your own environment!****Set obj = GetObject("LDAP://OU=users,OU=example,DC=ldapexplorer,DC=com")trustee = "LEX\AccessUser"
Const ADS_REVISION_DS = 4****
Set objACL = obj.get("nTSecurityDescriptor")****
Set objDACL = objACL.DiscretionaryAcl**WScript.Echo obj.distinguishedname & vbCrLf**
Set newDACL = CreateObject("AccessControlList")****newDACL.AclRevision = ADS_REVISION_DSnewDACL.AceCount = 0
Set ace = CreateObject("AccessControlEntry") For Each ace In objDACL 'keep all the old ACEs, if they are NOT inherited and if the NOT contain the trustee**** If ((ace.AceFlags And ADS_ACEFLAG_INHERITED_ACE) <> 0) Or (ace.Trustee <> trustee) Then**** newDACL.AddAce ace**** End If**Next**
'write new DACL**objACL.DiscretionaryAcl = newDACLobj.Put "ntSecurityDescriptor", objACL**obj.SetInfo > From <http://www.selfadsi.org/deep-inside/ad-security-descriptors.htm>