Search code examples
c#powershellactive-directoryldap

In C#, DirectoryEntry is returning empty AuditRules collection, even though audit rules do exist


I am attempting to create a C# console application to manage audit permissions on a domain (not file system). My problem is that I cannot read the existing audit rules in C#, I only get an empty collection, even though I am certain there are audit rules in the domain.

I am needing to manage over 100+ independent domains, so I want to come up with a programmatic solution to help manage them.

I can do this without issue in PowerShell using the following command.

$acl = Get-Acl -Path "DC=mydomain,DC=local" -Audit
$acl.Audit

This returns a collection of 5 audit rules. However, when trying to perform the same operation in C#, the audit rules are not returned.

string path = "LDAP://mydomain.local/DC=mydomain,DC=local";
DirectoryEntry domain = new DirectoryEntry(path);
               
AuthorizationRuleCollection authorizationRuleList1 = domain.ObjectSecurity.GetAuditRules(true, true, typeof(NTAccount));
AuthorizationRuleCollection authorizationRuleList2 = domain.ObjectSecurity.GetAuditRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));

Console.WriteLine("Authorization Rules 1: " + authorizationRuleList1.Count);
Console.WriteLine("Authorization Rules 2: " + authorizationRuleList2.Count);

Running the above shows aa count of 0 audit rules. Oddly, the access rules are returned without issue.

I have tried

  1. Explicitly applying a password for the DirectoryEntry initialization
  2. Running "run as administrator"
  3. Verifying the audit permissions exist by using LDAP explorer

Any pointers would be much appreciated.

Updated code using ActiveDs to get security descriptor.

string path = "LDAP://demo.local/DC=demo,DC=local";
DirectoryEntry domain = new DirectoryEntry(path);
var ntSecurityDescriptor = domain.Properties["ntSecurityDescriptor"];

ActiveDs.ADsSecurityUtility secUtility = new ActiveDs.ADsSecurityUtility();
ActiveDs.IADsSecurityDescriptor sd = (IADsSecurityDescriptor)ntSecurityDescriptor[0];
ActiveDs.IADsAccessControlList aclList =  ActiveDs.IADsAccessControlList)sd.DiscretionaryAcl;

Solution

  • I was able to come up with a solution to capture the audit rules for the domain. It appears that loading and DirectoryEntry using the path alone (as seen in the below code, does not bring back all properties.

    new DirectoryEntry(path)
    

    In order to return custom properties, a DirectorySearcher can be created, and configured to pull back specific properties. Example seen implmented in the below code block. Updating the SecurityMasks to include SecurityMasks.Sacl was also required in my case.

    string path = "DC=mydomain,DC=local";
    DirectorySearcher directorySearcher = new DirectorySearcher($"(distinguishedName={path})");
    
    // Request that the security permissions be returned with the objects.
    directorySearcher.PropertiesToLoad.Add("ntSecurityDescriptor");
    
    // Allow section of all Security Descriptors
    // This is needed for Audit Rules to be returned.
    directorySearcher.SecurityMasks = SecurityMasks.Dacl | SecurityMasks.Owner | SecurityMasks.Sacl;
    
    // Search for Active Directory entries
    SearchResultCollection searchResults = directorySearcher.FindAll();
    
    // Convert ntSecurityDesciptor to a RawSecurityDesciptor object
    RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor((byte[])searchResults[0].Properties["ntSecurityDescriptor"][0], 0);
    

    Now that a raw security object is created, that includes the audit rules stored in the SystemAcl property.

    rawSecurityDescriptor.SystemAcl
    

    This object will allow review of the object's audit rules.

    Hope this helps someone else.