Search code examples
c#windowswindows-securitydaclsystem.security

How can I edit a DACL in C#?


Is there an analog of the SetFileSecurity function?

I need to re-write some tests from Python and I'm stuck on this part. In Python I can freely edit a DACL with pywin32 (modules with a C++ implementation to work with Windows API). I can edit any ACE with win32security.

Change owner to Everyone? Okay.

win32security.SetNamedSecurityInfo("somefile.txt", win32security.SE_FILE_OBJECT,
                                       win32security.OWNER_SECURITY_INFORMATION,
                                       win32security.ConvertStringSidToSid("S-1-1-0"))
sd.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)

Remove an inherited ACE? Easy.

sd = win32security.GetFileSecurity("", win32security.DACL_SECURITY_INFORMATION)
dacl = SECURITY_DESCRIPTOR.GetSecurityDescriptorDacl()
dacl.DeleteAce(0)
sd.SetSecurityDescriptorDacl(1, dacl, 0)  # may not be necessary
win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)

And all of those without some special permissions.

But if I want to do something like this in C#. One way I found is changing a security descriptor with pure SDDL, but using System.Security.File.SetAccessControl() with FileSecurity doesn't work if SetSecurityDescriptorSddlForm was called without the SeSecurityPrivilege privilege. Also, even using an administrator token with nearly all privileges, if I want to change something in a "wrong" way (delete some inherited ACEs), the security descriptor doesn't apply. If I try to do something "very wrong", like set the owner to Everyone, an exception will be thrown.

var sddl_everyone_owner = @"O:S-1-1-0G:DUD:P";
var path = @"C:\Users\someuser\test.txt";
FileSecurity fs_edit = new FileSecurity();
fs_edit.SetSecurityDescriptorSddlForm(sddl_everyone_owner);
File.SetAccessControl(path, fs_edit);

Run with administrator token:

Unhandled Exception: System.InvalidOperationException: The security identifier is not 
allowed to be the owner of this object.
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle 
   handle, 
   AccessControlSections includeSections, Object exceptionContext)
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, 
   AccessControlSections includeSections, Object exceptionContext)
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, 
   AccessControlSections includeSections)
at System.Security.AccessControl.FileSystemSecurity.Persist(String fullPath)
at System.IO.File.SetAccessControl(String path, FileSecurity fileSecurity)
at rtest.Program.Main(String[] args) in C:\somepath\Program.cs:line 52

Solution

  • After 11 hours of googling, then trying to write some woking code i have this:

    // changes SDDL of file:
    using System;
    using System.Runtime.InteropServices; // DllImport
    
    public class SomeClass
    {
        [DllImport("Advapi32.dll", SetLastError = true)]
        static extern void SetFileSecurity(string path, int type_of_sd, IntPtr sd);
        [DllImport("Advapi32.dll", SetLastError = true)]
        static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, out UIntPtr SecurityDescriptorSize);
        private static void Main()
        {
            string path = @"C:\Some\path\to\file";
            string sddl = "D:AI(A;ID;FA;;;S-1-1-0)"; // set only one ACE: inherited full access to Everyone
            uint sd_revision = 1;  // the only revision of SECURITY_DESCRIPTOR
            int DACL_SECURITY_INFORMATION = 4; // can be changed to change other properties, not DACL, relying on SECURITY_DESCRIPTOR_CONTROL parameters https://msdn.microsoft.com/ru-ru/library/windows/desktop/aa379566%28v=vs.85%29.aspx
            IntPtr sd_ptr = new IntPtr();
            UIntPtr sd_size_ptr = new UIntPtr();
            ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, sd_revision, out sd_ptr, out sd_size_ptr);
            SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd_ptr);
        }
    }
    

    This code import functions from Advapi32.dll right to C# code. Special thanks to PInvoke.net! Added this code to code samples.