My solution is based on the .NET framework 4.8 reference source an may not necessarily work for other versions. But since the solution involves a function also used when internally reading ACLs from the filesystem, I am optimistic that the interface won't simply be scrapped.
TL;DR
var ace = dirsec.AccessRuleFactory(ident.User, 0x10000000,
false, InheritanceFlags.None, PropagationFlags.None,
AccessControlType.Allow)
as FileSystemAccessRule; // <<<< IMPORTANT PART!
Why does it fail?
The issue is that FileSystemAccessRule
only has public constructors which will access the enum FileSystemRights
and which will "validate" that by calling a private method named AccessMaskFromRights()
. This means even though we can manage to build after forcibly passing (FileSystemRights)0x10000000
to the constructors, we will always get the following exception:
Unhandled Exception: System.ArgumentOutOfRangeException: The value '268435456' is not valid for this usage of the type FileSystemRights.
However, there is an internal
constructor which takes int accessMask
as second argument instead. This is how the constructor is declared:
internal FileSystemAccessRule(
IdentityReference identity,
int accessMask,
bool isInherited,
InheritanceFlags inheritanceFlags,
PropagationFlags propagationFlags,
AccessControlType type);
That's the one we need to access. But how?
NB: Unfortunately, even though we could create an AccessRule
(from which FileSystemAccessRule
is derived) the AddAccessRule()
method of FileSystemSecurity
and DirectorySecurity
will not accept it, it only accepts FileSystemAccessRule
as input.
How to work around it?
After digging a bit further it turns out FileSystemSecurity
does have a method named AccessRuleFactory()
which internally also appears to be what's used whenever the FileSystemAccessRule
instances get created from actual ACLs read from the file system.
Said method takes an int accessMask
as well and - unsurprisingly - has access to the internal
constructor mentioned above, which allows us to create a FileSystemAccessRule
from an int
as opposed to a FileSystemRights
(which is simply missing our desired values).
public sealed override AccessRule AccessRuleFactory(
IdentityReference identityReference,
int accessMask,
bool isInherited,
InheritanceFlags inheritanceFlags,
PropagationFlags propagationFlags,
AccessControlType type);
So in essence what we need to do is to have an instance of FileSystemSecurity
or DirectorySecurity
which we can use to call AccessRuleFactory()
.
DirectorySecurity dirsec = System.IO.Directory.GetAccessControl(
System.IO.Path.GetTempPath());
using (WindowsIdentity ident = WindowsIdentity.GetCurrent())
{
// 0x10000000 == GENERIC_ALL
var ace = dirsec.AccessRuleFactory(ident.User, 0x10000000,
false, InheritanceFlags.None, PropagationFlags.None,
AccessControlType.Allow)
as FileSystemAccessRule; // <<<< IMPORTANT PART!
dirsec.AddAccessRule(ace);
// ... set dirsec on some directory ...
}
This creates a FileSystemAccessRule
via the DirectorySecurity.AccessRuleFactory()
, giving GENERIC_ALL
access to the the user in whose context the current process is running.