Editor's Note: This article is the 11th part of a 12-part series about Active Directory Service Interfaces (ADSI). The series started in the January 1999 issue. Refer to previous installments for definitions and background information.
Last month, I introduced you to Security Descriptors (SDs), System Access Control Lists (SACLs), discretionary access control lists (DACLs), and access control entries (ACEs). I also discussed the AccessControlEntry object's seven properties: Trustee, AccessMask, AceType, AceFlags, Flags, ObjectType, and InheritedObjectType. This month, I discuss the possible values for each property and show you how to use those values to create an ACE and an SD.
The Trustee property specifies the group or user receiving the permissions or being audited. The value you assign to the Trustee property can take one of several forms:
Domain accounts. These accounts are the logon names that earlier versions of Windows NT used. They take the form domain\useraccount (e.g., windows\jsmith), where domain (e.g., windows) is the name of the NT domain that contains the user and useraccount (e.g., jsmith) is the specified user's sAMAccountName property. Domain accounts are valid in Windows 2000 (Win2K) domains.
Well-known security principals. These principals represent special identities that the Win2K or NT security system defines. For example, Everyone, Authenticated Users, System, and Creator Owner are well-known security principals. The WellKnown Security Principals container beneath the Configuration container stores the objects representing the well-known security principals. To access these objects, you use the prefix cn= followed by the name in a properly formatted Lightweight Directory Access Protocol (LDAP) string. For example, to access the Everyone group in the mycorp.com domain, you specify LDAP://cn=Everyone,cn=WellKnown SecurityPrincipals,cn=Configuration,dc=mycorp,dc=com.
Built-in groups. These groups represent the built-in user groups that the NT security system defines. They take the form BUILTIN\groupname (e.g., BUILTIN\Administrators), where groupname is the name of the built-in user group (e.g., Administrators). The Builtin container beneath the Domain container stores the objects representing the built-in groups. To access these objects, you use the format: WinNT://your-server-name-here/ built-in-group-name-here.
SIDs. SIDs represent the ObjectSID property of the specified user or group in Win2K's Active Directory (AD). You specify SIDs in string format (e.g., S-1-5-99-427-9).
Distinguished names. DNs represent the DistinguishedName property of the specified user or group in the AD store. A DN might look like cn=Tracy Poodles,ou=Finance,dc=mycorp,dc=com.
The AccessMask property specifies one or more access permissions that you're setting or auditing on a directory object. This property doesn't specify whether you're allowing or denying the permission or whether you're auditing successful or failed access; it specifies only what the permission is.
The AccessMask property has the largest set of values. Examples of these values include
- ADS_RIGHT_GENERIC_READ (permission to read the object)
- ADS_RIGHT_DS_READ_PROP (permission to read a property of the object)
- ADS_RIGHT_DS_CREATE_CHILD (permission to create children of the object)
You can find the complete list of AccessMask values in the ADSI 2.5 documentation on the Web site at http://msdn.microsoft.com/isapi/ msdnlib.idc?theURL=/library/sdkdoc/adsi/ds2_enum_16el.htm.
If you apply create or delete permissions to a child object or read or write permissions to a specific property, you need to specify the relevant globally unique ID (GUID) of the object or property that you're allowing access to. You specify the GUID in the ObjectType or InheritedObjectType properties.
The AceType property dictates whether the ACE allows permissions, denies permissions, or audits the use of permissions. (You define whether you're auditing successes or failures in AceFlags.) The values you set depend on whether the ACE applies to a specific object or property or applies generally. The six possible values are
- ADS_ACETYPE_ACCESS_ALLOWED (allows access permissions generally)
- ADS_ACETYPE_ACCESS_ALLOWED_OBJECT (allows access permissions to an object or property)
- ADS_ACETYPE_ACCESS_DENIED (denies access permissions generally)
- ADS_ACETYPE_ACCESS_DENIED_OBJECT (denies access permissions to an object or property)
- ADS_ACETYPE_SYSTEM_AUDIT (audits the use of permissions generally)
- ADS_ACETYPE_SYSTEM_AUDIT_OBJECT (audits the use of permissions on an object or property)
You can only set one AceType per ACE. If you apply a permission or audit to a specific object (or an object's property), you must specify the GUID of that object in the ObjectType or InheritedObjectType property.
This catchall property stores two sets of information. The first set of AceFlags values stores inheritance information. Some of the values you can use in DACLs are
- ADS_ACEFLAG_VALID_INHERIT_FLAGS (specifies whether the ACE contains valid data—i.e., is a functioning ACE)
- ADS_ACEFLAG_INHERITED_ACE (specifies whether the object inherited the ACE)
- ADS_ACEFLAG_INHERIT_ONLY_ACE (specifies whether the ACE exerts access control on the object it's attached to. If you set this flag, the ACE doesn't exert access control—i.e., is an inherit-only ACE. If you don't set this flag, the ACE exerts access control—i.e., is an effective ACE.)
- ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE (controls the depth of inheritance. If you set this flag, you're setting the depth to one level. If you don't set this flag, you're setting the depth to the entire subtree, providing that you set the ADS_ACEFLAG_INHERIT_ACE flag.)
- ADS_ACEFLAG_INHERIT_ACE (specifies that child objects will inherit this ACE. Inherited ACEs are inheritable, unless you set the ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE flag.)
At first, the ADS_ACEFLAG_INHERIT_ACE and ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE flags might seem mutually exclusive. However, you can set both flags to specify that you want the first child but not the second child to inherit the permissions.
When you attach the ADS_ACEFLAG_INHERIT_ONLY_ACE flag to an object, the object acts only as a carrier for the ACE—the ACE doesn't affect that object. You typically use this flag for a container, which acts as placeholder for the ACE. The container then applies the ACE to whatever objects you target beneath it. If appropriate, the container will propagate the ACE to another container so that the child container can also act as a carrier.
The second set of AceFlags values stores auditing information. You use these values in SACLs:
- ADS_ACEFLAG_FAILED_ACCESS (generates audit events for failures of the AccessMask permission)
- ADS_ACEFLAG_SUCCESSFUL_ACCESS (generates audit events for successes of the AccessMask permission)
At first consideration, you might think that these two SACL flags should be in AceType rather than AceFlags because AceType already specifies the allow or deny permission of a DACL ACE. However, their placement in AceFlags lets you specify both success and failure audits in one ACE.
Flags, ObjectType, and InheritedObjectType
The Flags property specifies whether the AccessControlEntry object has values in its ObjectType or InheritedObjectType properties. The Flags property can have one of four values: 0, 1, 2, or 3. Table 1 shows what those values represent.
The ObjectType and InheritedObjectType properties store GUIDs or Null values that specify what the ACE applies to. Table 2 gives the various options. Note the use of Null and Null (ignored but still set) in Table 2. If Null appears, you need to set the property value to null. If Null (ignored but still set) appears, you don't need to set the property value to null. The system automatically sets the value to null when it creates the ACE. The Flags, ObjectType, and InheritedObjectType properties have the defaults of 0, Null, and Null, respectively.
Here is an example of how you set the Flags, ObjectType, and InheritedObjectType properties. Suppose you want to apply an ACE to an entire object but you don't want its children to inherit the ACE. First, you set the AceFlags property to the value of ADS_ACEFLAG_INHERIT_ONLY_ACE to specify that you want the ACE to apply to the object but not its child objects. Then, you set the Flags property to the value of 0 to specify that you don't want to set the ObjectType or InheritedObjectType fields. You don't set the ObjectType or InheritedObjectType properties at all.
How to Check for and Create an ACE
If you're unsure of the ACEs that have been set on the objects below a container (such as a Domain or OrganizationalUnit object), you can use the script ACEChecker.vbs to find out. I use this script to check my Win2K AD for irregularities. Because the script is long, you can find it on the Win32 Scripting Journal Web site (http://www.winntmag.com/ newsletter/scripting).
If you find that an object needs an ACE, you can create one. Creating an ACE is a three-phase process. In the first phase, you use the IADsAccessControlEntry interface to set the appropriate ACE property values. In the second phase, you use the IADsSecurityDescriptor and IADsAccessControlList interfaces to retrieve the SD and ACL of the object that you want to apply the ACE to. In the third phase, you add the ACE to the existing ACL and return the modified SD and ACL to the AD store.
Listing 1 shows this process in action. This VBScript code creates a simple DACL ACE that allows the ANewGroup object full access to the OrganizationalUnit object myOU and all its children. You begin this code by declaring the constants that represent the property values you want to set and the variables that the script uses. The ADSI 2.5 documentation provides the constants for the AccessControlEntry object's seven properties. The script TwoACEs.vbs on the Win32 Scripting Journal Web site also provides the constants.
After you declare your constants and variables, you call the CreateObject function. This function creates a new empty instance of an ACE object called adsNewACE. You then set the properties you need with the IADsAccessControlEntry interface's property methods. These property methods have the same names as the properties. In Listing 1, for example, you use the adsNewACE.Trustee property method to set the Trustee property value to WINDOWS\ANewGroup (which gives this trustee permission to the myOU object) and the adsNewACE.AccessMask property method to set the AccessMask value to FULL_CONTROL.
After you create the ACE, you bind to the object you want the ACE to apply to. You then use the IADs::Get method to retrieve the object's SD and the IADsSecurityDescriptor::DiscretionaryAcl method to retrieve the object's existing DACL. After you retrieve the DACL, you use the IADsAccessControlList::AddACE method to add the ACE you created to it. You use the DiscretionaryAcl method to write the modified DACL to the property cache. You use the IADs::Put method to replace the SD. Finally, you write the property cache to the AD store with the IADs::SetInfo method.
A Complex ACE Example
TwoACEs.vbs on the Win32 Scripting Journal Web site creates two complex ACEs. This script sets the following permissions in the DACL and audits in the SACL for the myOU object:
- The members of DenyGroup don't have permission to even see the myOU object.
- The members of AllowChildGroup have permission to create, delete, and examine all of myOU's children.
- User Vicky Launders has permission to assume ownership of myOU but not any of myOU's children.
- User Lee Flight has permission to read and write myOU's description.
- The Chris Heaton account has permission to read and write the passwords for all the users in myOU.
- The system generates audit messages for anyone who tries to delete myOU.
- The system generates audit messages for Brian Kerr's successful and failed modifications to any objects below (but not including) myOU in the AD store.
As I mentioned earlier, TwoACEs.vbs includes all the constants for the AccessControlEntry object's property values, even though the script doesn't use them all. In addition, the script uses DNs and domain accounts for the Trustee property values so that you can see how to use both.
How to Create an SD
If you're creating an object but don't want it to get the default DACL and SACL that would usually apply to objects at that location in the tree, you can write a custom DACL and SACL. You manipulate SDs and ACLs with the IADsSecurityDescriptor and IADsAccessControlList interfaces, respectively. As the code in Listing 2 shows, you need to set several properties of these interfaces, including AclRevision and Control Code.
The IADsAccessControlList::AclRevision property is a static version number that every ACE, ACL, and SD in the AD store has. The ADS_SD_REVISION_ENUM value defines the version number. You use the following constant to specify this value:
Const ADS_SD_REVISION_DS = 4
Like the AceFlags property, the IADsSecurityDescriptor::Control Code property uses flags that help define the SD properties. Callout A in Listing 2 shows all the Control Code flags and their constants. You can find descriptions of these flags at http://msdn.microsoft.com/isapi/ msdnlib.idc?theurl=/library/sdkdoc/adsi/ds2_enum_290t.htm.
After you declare your constants and variables, you can create adsObject. To save space, Listing 2 doesn't include the code for object creation. Thus, you need to add this code if you want to run it.
Next, you use the CreateObject function to create an ACE object (adsNewACE) and use the IADsAccessControlEntry interface's property methods to set that object's permissions. This process is similar to the one you used to create the code in Listing 1, except this time, you assign the ACE you created to a new DACL rather than an existing one. To create this DACL object, you use the CreateObject function. To set the new DACL object's properties, you use the IADsAccessControlList interface. You use the IADsAccessControlList::Add ACE method to add the ACE you created to the new DACL.
With your new DACL in hand, you can use the CreateObject function to create an SD object and use the IADsSecurityDescriptor interface to assign the Control Code properties. You also use IADsSecurityDescriptor to specify that you want to use the default SACL (even though this SD doesn't contain a SACL) and that you don't want to use the default DACL. You use the IADsSecurityDescriptor:: DiscretionaryAcl method to assign the DACL you created to the SD. Finally, you assign the SD to adsObject.
This discussion on SDs, ACLs, and ACEs concludes my look at how to control permissions and audit permission modifications in the AD store. Next month, I'll conclude this series by showing you how to extend AD schema.
This article is adapted from Alistair G. Lowe-Norris' forthcoming book Windows 2000 Active Directory (O'Reilly and Associates).