Search code examples
c#directoryservices

Find directory rights (weird rights entries)


DirectorySecurity fs;
string FolderPath = "C:/Program Files";

fs = Directory.GetAccessControl(FolderPath, AccessControlSections.All);

foreach (FileSystemAccessRule fileSystemAccessRule in fs.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount))) 
{
    userNameDomain = fileSystemAccessRule.IdentityReference.Value;
    userRights = fileSystemAccessRule.FileSystemRights.ToString();
        
    string[] row = { userNameDomain, userRights };
    var listViewItem = new ListViewItem(row);
    lv_perm.Items.Add(listViewItem);
}

When doing this on "regular" files and folders (ex: C:/Users/Julien/Desktop/New folder), everything seems good:

ListView:

enter image description here

But, when I'm doing this on a folder with "special" rights (ex: C:/Program Files), I got duplicate IdentityReference.Value associated to strange numbers for rights:

Listview not good:

enter image description here

I don't have as many rights entries with strange numbers when I open the Permissions tab in C:/Program Files properties.

Maybe I'm doing something bad?

EDIT: From that page Here:

Using .NET you may think that determining which permissions are assigned to a directory/file should be quite easy, as there is a FileSystemRights Enum defined that seems to contain every possible permission that a file/directory can have and calling AccessRule.FileSystemRights returns a combination of these values. However, you will soon come across some permissions where the value in this property does not match any of the values in the FileSystemRights Enum (I do wish they wouldn’t name some properties with the same name as a Type but hey).

The end result of this is that for some files/directories you simply cannot determine which permissions are assigned to them. If you do AccessRule.FileSystemRights.ToString then for these values all you see is a number rather than a description (e.g Modify, Delete, FullControl etc). Common numbers you might see are:

-1610612736, –536805376, and 268435456

To work out what these permissions actually are, you need to look at which bits are set when you treat that number as 32 separate bits rather than as an Integer (as Integers are 32 bits long), and compare them to this diagram: msdn.microsoft.com/en-us/library/aa374896(v=vs.85).aspx

So for example, -1610612736 has the first bit and the third bit set, which means it is GENERIC_READ combined with GENERIC_EXECUTE. So now you can convert these generic permissions into the specific file system permissions that they correspond to.

You can see which permissions each generic permission maps to here: msdn.microsoft.com/en-us/library/aa364399.aspx. Just be aware that STANDARD_RIGHTS_READ, STANDARD_RIGHTS_EXECUTE and STANDARD_RIGHTS_WRITE are all the same thing (no idea why, seems strange to me) and actually all equal the FileSystemRights.ReadPermissions value.

So I think, because GetAccessRules are unable to group for ex:

NT SERVICE\TrustedInstaller FullControl

and

NT SERVICE\TrustedInstaller 268435456

He create 2 distinct entry.

I have to correct the FileSystemRights so they fit to the enumeration.

268435456 - FullControl

-536805376 - Modify, Synchronize

-1610612736 - ReadAndExecute, Synchronize

This issue exist since 2014. And still exist today.


Solution

  • You're not doing anything bad. The access mask is actually represented as an int internally (and on the file system), but the enum FileSystemRights is incomplete.

    Therefore the visualizers and those functions converting a FileSystemRights value to a string will be at a loss and simply give you the numeric value (but as a string).

    This means in order to make sense of them you'd have to inspect WinNT.h (from the Windows SDK) and look up all the possible access mask values - including generic ones - and provide a manual way of converting the numeric representation to a more human-readable string.

    What you encountered are two distinct ACEs with different (but semantically equivalent) access masks, it seems. This is perfectly fine and happens in the wild (as your screenshot proves!). The .NET framework, however, chooses to ignore the issue.

    If you look at the ACL with Powershell's Get-Acl or with icacls (a tool which has been on board since Windows 2000), you can see that there are potentially differences in how the individual ACEs are inherited or propagate down the filesystem hierarchy.

    Another alternative would be to call the MapGenericMask() and have it perform the mapping. So when you would give it GENERIC_ALL (== 0x10000000) it would return 0x001f01ff, which corresponds to FileSystemRights.FullControl. This way you could "bend" the generic access mask into a form understood by the .NET framework.