1
votes

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
2

2 Answers

2
votes

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.

0
votes

You can get the sddl of a file or folder (symlink/junction) using the powershell command: get-acl -path "c:\some\file_or_folder" | fl.

Piping the output to fl translates the acl to both verbose list and ssdl form.

For the folder "C:\Users\someuser\Application Data" the sddl is

O:SYG:SYD:AI(D;;CC;;;WD)(A;OICIID;FA;;;SY)(A;OICIID;FA;;;BA)(A;OICIID;FA;;;S-1-5-21-2614944367-2017529714-1376493066-1XXX)

or

O:SY Owner:NT Authority/System G:SY Group:NT Authority/System D:AI(D;;CC;;;WD)(A;OICIID;FA;;;SY)(A;OICIID;FA;;;BA)(A;OICIID;FA;;;S-1-5-21-2614944367-2017529714-1376493066-1XXX)

"D:" means DACL "AI" means something like Allow Inheritance

Each substring within parentheses is an Access Control Entry (ACE). Each ACE contains six fields delimited by semicolons that indicate the actual permissions. The first ACE, (D;;CC;;;WD), corresponds to the verbose list line: Everyone Deny ReadData. That is, D means Deny, CC means ReadData, and WD means Everyone. Note that the CC code as shown in Microsoft documentation is synonymous with SDDL_CREATE_CHILD in Sddl.h and the access right value ADS_RIGHT_DS_CREATE_CHILD. How CC is interpreted to mean "ReadData" isn't clear. Note also that specifying "Everyone" is done with the code WD (probably derived from "World"), not with a SID.

For those who wish to delve deeper please see https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format

https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-definition-language-for-conditional-aces-

https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings

Good luck!