EldoS | Feel safer!

Software components for data protection, secure storage and transfer

CbFsGetFileSecurity and C# FileSecurity object

Also by EldoS: CallbackProcess
A component to control process creation and termination in Windows and .NET applications.
#37410
Posted: 08/09/2016 13:07:18
by James Irish (Premium support level)
Joined: 08/09/2016
Posts: 2

Hi,

We are attempting to test out building custom security ACLs when requested. In our crude example below we are attempting the set the Owner to the current user and provide "Everyone" full control over the file.

I believe we are correctly specifying the Length in the first call to the handler and then filling the requested memory block as specified by SecurityDescriptor using the bytes from the ACL generated - this should already be self-relative (and the C# object includes a flag that specifies this). None of the below appears to error however the Explorer 'Security' tab reports "The requested security information is either unavailable or can't be displayed."

Any suggestions as to what we have missed?

Code
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
    string StringSecurityDescriptor,
    uint StringSDRevision,
    out IntPtr SecurityDescriptor,
    out UIntPtr SecurityDescriptorSize
);

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

void CbFsGetFileSecurity(
    object Sender,
    CbFsFileInfo FileInfo,
    CbFsHandleInfo HandleInfo,
    uint SecurityInformation,
    IntPtr SecurityDescriptor,
    uint Length,
    ref uint LengthNeeded
    )
{
    //
    // Getting SACL_SECURITY_INFORMATION requires the program to
    // execute elevated as well as the SE_SECURITY_NAME privilege
    // to be set. So just remove it from the request.
    //
    SecurityInformation &= ~(uint)SECURITY_INFORMATION.SACL_SECURITY_INFORMATION;
    if (SecurityInformation == 0)
        return;

    var acsFlags = AccessControlSections.None;
    if ((SecurityInformation  & (uint)SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Owner;
    if ((SecurityInformation & (uint)SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Group;
    if ((SecurityInformation & (uint)SECURITY_INFORMATION.DACL_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Access;

    var sddl = MyACL.GetSecurityDescriptorSddlForm(acsFlags);

    IntPtr sd;
    UIntPtr sdSize;
    var result = ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, out sd, out sdSize);
            
    if (0 == Length)
        // First call - it needs length of object
        LengthNeeded = sdSize.ToUInt32();
    else
    {
        // Second call supply the actual object
        CopyMemory(SecurityDescriptor, sd, sdSize.ToUInt32());
    }

    LocalFree(sd);
}

enum SECURITY_INFORMATION
{
    OWNER_SECURITY_INFORMATION = 1,
    GROUP_SECURITY_INFORMATION = 2,
    DACL_SECURITY_INFORMATION = 4,
    SACL_SECURITY_INFORMATION = 8,
}

private FileSecurity _myACL;
public FileSecurity MyACL
{
    get
    {
        if (_myACL != null) return _myACL;
        _myACL = new FileSecurity();
        _myACL.SetOwner(WindowsIdentity.GetCurrent().User);
        var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
        var accessRule = new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Allow);
        _myACL.AddAccessRule(accessRule);
        return _myACL;
    }
}
#37420
Posted: 08/10/2016 05:45:16
by James Irish (Premium support level)
Joined: 08/09/2016
Posts: 2

Apologies, we've managed to get this working (simple error!) so for anyone else looking for a C# solution here it is in full (note the example below is puling a valid ACL from an existing file but you can build one yourself.)

Code
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
    string stringSecurityDescriptor,
    uint stringSdRevision,
    out IntPtr securityDescriptor,
    out uint securityDescriptorSize
);

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LocalFree(IntPtr hMem);

private void CbFsGetFileSecurity(
    object sender,
    CbFsFileInfo fileInfo,
    CbFsHandleInfo handleInfo,
    uint securityInformation,
    IntPtr securityDescriptor,
    uint length,
    ref uint lengthNeeded)
{
    //
    // Getting SACL_SECURITY_INFORMATION requires the program to
    // execute elevated as well as the SE_SECURITY_NAME privilege
    // to be set. So just remove it from the request.
    //
    securityInformation &= ~(uint)SECURITY_INFORMATION.SACL_SECURITY_INFORMATION;
    if (securityInformation == 0)
        return;

    var acsFlags = AccessControlSections.None;
    if ((securityInformation  & (uint)SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Owner;
    if ((securityInformation & (uint)SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Group;
    if ((securityInformation & (uint)SECURITY_INFORMATION.DACL_SECURITY_INFORMATION) != 0)
        acsFlags = acsFlags | AccessControlSections.Access;

    var sddl = MyACL.GetSecurityDescriptorSddlForm(acsFlags);

    IntPtr sd;
    uint sdSize;
            
    var b = ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, out sd, out sdSize);
    if (!b) throw new ECBFSError((uint)Marshal.GetLastWin32Error());
            
    lengthNeeded = sdSize;

    if (length >= sdSize)
        // Second call supply the actual data
        CopyMemory(securityDescriptor, sd, sdSize);
            
    LocalFree(sd);
}

enum SECURITY_INFORMATION
{
    OWNER_SECURITY_INFORMATION = 1,
    GROUP_SECURITY_INFORMATION = 2,
    DACL_SECURITY_INFORMATION = 4,
    SACL_SECURITY_INFORMATION = 8,
}

private FileSecurity _myACL;
public FileSecurity MyACL
{
    get
    {
        if (_myACL != null) return _myACL;
        _myACL = System.IO.File.GetAccessControl(@"C:\Downloads\Covering letter (3).docx");
        return _myACL;
    }
}
#37429
Posted: 08/10/2016 09:39:59
by Eugene Mayevski (EldoS Corp.)

Thank you very much for posting the result! It will surely help other users when dealing with file security.


Sincerely yours
Eugene Mayevski

Reply

Statistics

Topic viewed 665 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!