Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Setting an ACE on a Directory Object

[C++]

The following C++ code has a SetRight subroutine that adds an ACE to the DACL of the security descriptor of a specified Active Directory object. The subroutine is multipurpose, allowing you to set any of the following types of ACEs.

Here's the code for the SetRight subroutine. Following that code are several samples that show how to call SetRight to set different types of ACEs.

HRESULT SetRight(
          IADs *pObject,
          long lAccessMask,
          long lAccessType,
          long lAccessInheritFlags,
          LPOLESTR szObjectGUID,
          LPOLESTR szInheritedObjectGUID,
          LPOLESTR szTrustee)
{
VARIANT varSD;
HRESULT hr = E_FAIL;
IADsAccessControlList *pACL = NULL;
IADsSecurityDescriptor *pSD = NULL;
IDispatch *pDispDACL = NULL;
IADsAccessControlEntry *pACE = NULL;
IDispatch *pDispACE = NULL;
long lFlags = 0L;
 
// This sample takes the szTrustee in an expected naming format 
// and assumes it is the name for the correct trustee.
// Your application should check the validity of the specified trustee.
if (!szTrustee || !pObject)
    return E_INVALIDARG;
 
VariantClear(&varSD);
 
// Get the nTSecurityDescriptor.
// Type should be VT_DISPATCH--an IDispatch ptr to the security descriptor object.
hr = pObject->Get(L"nTSecurityDescriptor", &varSD);
if ( FAILED(hr) || varSD.vt != VT_DISPATCH ) {
    wprintf(L"get nTSecurityDescriptor failed: 0x%x\n", hr);
    return hr;
}
 
hr = V_DISPATCH( &varSD )->QueryInterface(IID_IADsSecurityDescriptor,(void**)&pSD);
if ( FAILED(hr) ) {
    wprintf(L"QI for IADsSecurityDescriptor failed: 0x%x\n", hr);
    goto cleanup;
}
 
// Get the DACL.
hr = pSD->get_DiscretionaryAcl(&pDispDACL);
if (SUCCEEDED(hr)) 
    hr = pDispDACL->QueryInterface(IID_IADsAccessControlList,(void**)&pACL);
if ( FAILED(hr) ) {
    wprintf(L"Could not get DACL: 0x%x\n", hr);
    goto cleanup;
}
 
// Create the COM object for the new ACE.
hr  = CoCreateInstance( 
               CLSID_AccessControlEntry,
               NULL,
               CLSCTX_INPROC_SERVER,
               IID_IADsAccessControlEntry,
               (void **)&pACE
               );
if ( FAILED(hr) ) {
    wprintf(L"Could not create ACE object: 0x%x\n", hr);
    goto cleanup;
}
 
// Set the properties of the new ACE.
 
// Set the mask that specifies the access right.
hr = pACE->put_AccessMask( lAccessMask );
 
// Set the trustee.
hr = pACE->put_Trustee( szTrustee );
 
// Set AceType.
hr = pACE->put_AceType( lAccessType );
 
// Set AceFlags to specify whether other objects can inherit the ACE from the specified object.
hr = pACE->put_AceFlags( lAccessInheritFlags );
 
// If an szObjectGUID is specified, add ADS_FLAG_OBJECT_TYPE_PRESENT 
// to the lFlags mask and set the ObjectType.
if (szObjectGUID)
{
    lFlags |= ADS_FLAG_OBJECT_TYPE_PRESENT;
    hr = pACE->put_ObjectType( szObjectGUID );
}
 
// If an szInheritedObjectGUID is specified, add ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT 
// to the lFlags mask and set the InheritedObjectType.
if (szInheritedObjectGUID)
{
    lFlags |= ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT;
    hr = pACE->put_InheritedObjectType( szInheritedObjectGUID );
}
 
// Set flags if ObjectType or InheritedObjectType were set.
if (lFlags)
    hr = pACE->put_Flags(lFlags);
 
// Add the ACE to the ACL to the SD to the cache to the object.
// Need to QI for the IDispatch pointer to pass to the AddAce method.
hr = pACE->QueryInterface(IID_IDispatch, (void**)&pDispACE);
if (SUCCEEDED(hr))
{
    hr = pACL->AddAce(pDispACE);
    if (SUCCEEDED(hr))
    {
        // Write the DACL
        hr = pSD->put_DiscretionaryAcl(pDispDACL);
        if (SUCCEEDED(hr))
        {
            // Write the ntSecurityDescriptor property to the property cache.
            hr = pObject->Put(L"nTSecurityDescriptor", varSD);
            if (SUCCEEDED(hr))
            {
                // Call SetInfo to update the property on the object in the directory.
                hr = pObject->SetInfo();
            }
        }
    }
}
 
cleanup:
if (pDispACE)
    pDispACE->Release();
if (pACE)
    pACE->Release();
if (pACL)
    pACL->Release();
if (pDispDACL)
    pDispDACL->Release();
if (pSD)
    pSD->Release();
 
VariantClear(&varSD);
return hr;
}

Allow or deny access to the entire object

The following C++ code fragment calls the SetRight subroutine to set an ACE that allows the trustee to read or write any property on the object. The code fragment assumes that pObject and szTrustee are set to valid values. For a discussion of the possible formats for the trustee string, see Setting Access Rights on an Object.

HRESULT hr;
IADs *pObject;
LPWSTR szTrustee;
 
hr = SetRight(
          pObject,  // IADs pointer to the object
          ADS_RIGHT_READ_PROP | ADS_RIGHT_WRITE_PROP,
          ADS_ACETYPE_ACCESS_ALLOWED,
          0,        // not inheritable
          NULL,     // no object type GUID
          NULL,     // no inherited object type GUID
          szTrustee
          );

Allow or deny access to a specific property on the object

This C++ code fragment calls the SetRight subroutine to allow the trustee to read or write a specific property on the object. Note that you must specify the schemIDGUID of the property and you must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE. This sample also specifies the ADS_ACEFLAG_INHERIT_ACE flag which means the ACE can be inherited by child objects.

// Grant trustee the right to read the Telephone-Number property
// of all child objects in the Users container. 
// {bf967a49-0de6-11d0-a285-00aa003049e2} is the schemaIDGUID of 
// the Telephone-Number property.
hr = SetRight(
          pObject,  // IADs pointer to the object
          ADS_RIGHT_READ_PROP | ADS_RIGHT_WRITE_PROP,
          ADS_ACETYPE_ACCESS_ALLOWED_OBJECT,
          ADS_ACEFLAG_INHERIT_ACE,
          L"{bf967a49-0de6-11d0-a285-00aa003049e2}",
          NULL,     // no inherited object type GUID
          szTrustee
          );

Allow or deny access to a set of properties on the object

This C++ code fragment calls the SetRight subroutine to allow the trustee to read or write a specific set of properties on the object. You must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE.

A property set is defined by a controlAccessRight object in the Extended Rights container of the Configuration partition. To identify the property set in the ACE, you must specify the rightsGUID property of a controlAccessRight object. Note that this property set GUID is also set in the attributeSecurityGUID property of every attributeSchema object included in the property set. For more information, see Extended Rights.

This sample also specifies inheritance flags that make the ACE inheritable by child objects but ineffective on the immediate object. In addition, the sample specifies the GUID of the User class, which means that the ACE can be inherited only by objects of that class.

// Grant trustee the right to read or write a set of properties.
// {77B5B886-944A-11d1-AEBD-0000F80367C1} is a GUID that identifies 
// a property set (rightsGUID of a controlAccessRight object).
// {bf967aba-0de6-11d0-a285-00aa003049e2} is the schemaIDGUID of the
// User class, so this ACE is inherited only by objects of that class.
hr = SetRight(
          pObject,  // IADs pointer to the object
          ADS_RIGHT_READ_PROP | ADS_RIGHT_WRITE_PROP,
          ADS_ACETYPE_ACCESS_ALLOWED_OBJECT,
          ADS_ACEFLAG_INHERIT_ACE | ADS_ACEFLAG_INHERIT_ONLY_ACE,
          L"{77B5B886-944A-11d1-AEBD-0000F80367C1}",
          L"{bf967aba-0de6-11d0-a285-00aa003049e2}",
          szTrustee
          );

Allow or deny the right to create a specific type of child object

This C++ code fragment calls the SetRight subroutine to allow a specified trustee to create and delete User objects in the subtree under the specified object. Note that the sample specifies the GUID of the User class, which means the ACE only allows the trustee to create User objects, not objects of other classes. You must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE.

// Grant trustee the right to create or delete User objects 
// in the specified object. 
// {bf967aba-0de6-11d0-a285-00aa003049e2} is the schemaIDGUID of the
// User class.
hr = SetRight(
          pObject,  // IADs pointer to the object
          ADS_RIGHT_DS_CREATE_CHILD | ADS_RIGHT_DS_DELETE_CHILD,
          ADS_ACETYPE_ACCESS_ALLOWED_OBJECT,
          0,        // not inheritable
          L"{bf967aba-0de6-11d0-a285-00aa003049e2}",
          NULL,     // no inherited object type GUID
          szTrustee
          );

For the schemaIDGUID of a predefined attribute or class, see the attribute or class reference page in the Active Directory Schema Reference in the Active Directory Reference. For sample code to retrieve a schemaIDGUID programmatically, see Reading attributeSchema and classSchema Objects.

[Visual Basic]

The following Visual Basic code has a SetRight subroutine that adds an ACE to the DACL of the security descriptor of a specified Active Directory object. The subroutine is multipurpose, allowing you to set any of the following types of ACEs.

Here's the code for the SetRight subroutine. Following that code are several samples that show how to call SetRight to set different types of ACEs.

Sub SetRight(objectDN As String, _
             accessrights As Long, _
             accesstype As Long, _
             aceinheritflags As Long, _
             objectGUID As String, _
             inheritedObjectGUID As String, _
             trustee As String)
Dim dsobject As IADs
Dim sd As IADsSecurityDescriptor
Dim dacl As IADsAccessControlList
Dim newace As New AccessControlEntry
Dim lflags As Long
 
'Bind to the specified object
Set dsobject = GetObject(objectDN)
 
'Read the security descriptor on the object
Set sd = dsobject.Get("ntSecurityDescriptor")
 
'Get the DACL from the security descriptor.
Set dacl = sd.DiscretionaryAcl
 
'Set the properties of the new ACE.
newace.accessmask = accessrights
newace.AceType = accesstype
newace.aceflags = aceinheritflags
newace.trustee = trustee
 
'Set the GUID for the object type or inherited object type
lflags = 0
If Not objectGUID = vbNullString Then
newace.ObjectType = objectGUID
lflags = lflags Or &H1 'ADS_FLAG_OBJECT_TYPE_PRESENT
End If
If Not inheritedObjectGUID = vbNullString Then
newace.inheritedObjectType = inheritedObjectGUID
lflags = lflags Or &H2 'ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT
End If
If Not (lflags = 0) Then newace.Flags = lflags
 
'Now add the ACE to the DACL and to the security descriptor.
dacl.AddAce newace
sd.DiscretionaryAcl = dacl
 
'And apply it to the object.
dsobject.Put "ntSecurityDescriptor", sd
dsobject.setinfo
End Sub

Allow or deny access to the entire object

The following code fragment builds a binding string for the Users container and then calls the SetRight subroutine to set an ACE on the Users container. The first example simply sets an ACE that allows the trustee to read or write any property on the object.

Private Sub Form_Load()
Dim rootDSE As IADs
Dim objectDN As String
ADS_RIGHT_READ_PROP = &H10
ADS_RIGHT_WRITE_PROP = &H20
 
'Bind to the Users container in the local domain
Set rootDSE = GetObject("LDAP://rootDSE")
objectDN = "LDAP://cn=users," & rootDSE.Get("defaultNamingContext")
 
'Grant trustee the right to read/write any property.
SetRight objectDN, _
         ADS_RIGHT_READ_PROP Or ADS_RIGHT_WRITE_PROP, _
         ADS_ACETYPE_ACCESS_ALLOWED, _
         0, _
         vbNullString, _
         vbNullString, _
         "bfoot@Microsoft.com" 'trustee
End Sub

Allow or deny access to a specific property on the object

This code fragment calls the SetRight subroutine to allow the trustee to read or write a specific property on the object. Note that you must specify the schemIDGUID of the property and you must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE. This sample also specifies the ADS_ACEFLAG_INHERIT_ACE flag which means the ACE can be inherited by child objects.

'Grant trustee the right to read the Telephone-Number property
'of all child objects in the Users container. 
'{bf967a49-0de6-11d0-a285-00aa003049e2} is the schemaIDGUID of 
'the Telephone-Number property.
SetRight objectDN, _
         ADS_RIGHT_WRITE_PROP Or ADS_RIGHT_READ_PROP, _
         ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, _
         ADS_ACEFLAG_INHERIT_ACE, _
         "{bf967a49-0de6-11d0-a285-00aa003049e2}", _
         vbNullString, _
         "bfoot@twokay.local" 'trustee

Allow or deny access to a set of properties on the object

This code fragment calls the SetRight subroutine to allow the trustee to read or write a specific set of properties on the object. You must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE.

A property set is defined by a controlAccessRight object in the Extended Rights container of the Configuration partition. To identify the property set in the ACE, you must specify the rightsGUID property of a controlAccessRight object. Note that this property set GUID is also set in the attributeSecurityGUID property of every attributeSchema object included in the property set. For more information, see Extended Rights.

This sample also specifies inheritance flags that make the ACE inheritable by child objects but ineffective on the immediate object. In addition, the sample specifies the GUID of the User class, which means that the ACE can be inherited only by objects of that class.

'Grant trustee the right to read or write a set of properties.
'{77B5B886-944A-11d1-AEBD-0000F80367C1} is a GUID that identifies 
'a property set.
'{bf967aba-0de6-11d0-a285-00aa003049e2} is a GUID that identifies the
'User class, so this ACE is inherited only by objects of that class.
SetRight objectDN, _
         ADS_RIGHT_READ_PROP Or ADS_RIGHT_WRITE_PROP, _
         ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, _
         ADS_ACEFLAG_INHERIT_ACE Or ADS_ACEFLAG_INHERIT_ONLY_ACE, _
         "{77B5B886-944A-11d1-AEBD-0000F80367C1}", _
         "{bf967aba-0de6-11d0-a285-00aa003049e2}", _
         "bfoot@Microsoft.com" 'trustee
End Sub

Allow or deny the right to create a specific type of child object

This code fragment calls the SetRight subroutine to allow a specified trustee to create and delete User objects in the subtree under the specified object. Note that the sample specifies the GUID of the User class, which means the ACE only allows the trustee to create User objects, not objects of other classes. You must specify ADS_ACETYPE_ACCESS_ALLOWED_OBJECT to indicate that this is an object-specific ACE.

'Grant trustee the right to create or delete User objects 
'in the specified object. 
'{bf967aba-0de6-11d0-a285-00aa003049e2} is a GUID that identifies the
'User class.
SetRight objectDN, _
         ADS_RIGHT_DS_CREATE_CHILD Or ADS_RIGHT_DS_DELETE_CHILD, _
         ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, _
         0, _
         "{bf967aba-0de6-11d0-a285-00aa003049e2}", _
         vbNullString, _
         "bfoot@twokay.local" 'trustee

For the schemaIDGUID of a predefined attribute or class, see the attribute or class reference page in the Active Directory Schema Reference in the Active Directory Reference. For sample code to retrieve a schemaIDGUID programmatically, see Reading attributeSchema and classSchema Objects.