Printout Header
RSS Feed

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 acces 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:

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.


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:

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 DACL_PROTECTED bit in the control flags of the entire security descriptor.

  2. 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, whether INHERITED_ACE the flag is set there in the ACEFlags.

  3. 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 the flags in the ACE generated by you. You can use this matrix:

    Vererbungsreglen für Active Directory Access Control Entries

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


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 = 1 Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 5 Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = 6 Const ADS_ACEFLAG_INHERITED_ACE = &H10 Const ADS_ACEFLAG_INHERIT_ACE = &H2 Const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE = &H4 Const ADS_ACEFLAG_INHERIT_ONLY_ACE = &H8 Const ADS_RIGHT_DS_CREATE_CHILD = &H1 Const ADS_RIGHT_DS_DELETE_CHILD = &H2 Const ADS_RIGHT_ACTRL_DS_LIST = &H4 Const ADS_RIGHT_DS_SELF = &H8 Const ADS_RIGHT_DS_READ_PROP = &H10 Const ADS_RIGHT_DS_WRITE_PROP = &H20 Const ADS_RIGHT_DS_DELETE_TREE = &H40 Const ADS_RIGHT_DS_LIST_OBJECT = &H80 Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Const ADS_RIGHT_DELETE = &H10000 Const ADS_RIGHT_READ_CONTROL = &H20000 Const ADS_RIGHT_WRITE_DAC = &H40000 Const ADS_RIGHT_WRITE_OWNER = &H80000 Const ADS_RIGHT_GENERIC_ALL = &H10000000 Const ADS_RIGHT_GENERIC_WRITE = &H40000000 Const 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

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:

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 = &H20 Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Const 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 = trustee newAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED newAce.AceFlags = 0 newAce.AccessMask = ADS_RIGHT_DELETE newACE.Flags = 0 objDACL.AddAce newAce 'create a new ACE entry: read/write the 'description' field for all contained obejcts Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = trustee newAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT newAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE Or ADS_ACEFLAG_INHERIT_ONLY_ACE newAce.AccessMask = ADS_RIGHT_DS_READ_PROP Or ADS_RIGHT_DS_WRITE_PROP newACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT newACE.ObjectType = GUID_ATTR_DESCRIPTION objDACL.AddAce newAce 'create a new ACE entry: reset password for user accounts Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = trustee newAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT newAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE newAce.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS newACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT Or ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT newACE.ObjectType = GUID_RIGHT_RESETPASSWORD newACE.InheritedObjectType = GUID_CLASS_USER objDACL.AddAce newAce 'write new DACL objACL.DiscretionaryAcl = objDACL obj.Put "ntSecurityDescriptor", objACL obj.SetInfo

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_DS newDACL.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 = newDACL obj.Put "ntSecurityDescriptor", objACL obj.SetInfo