File: System\Security\AccessControl\Win32.cs
Web Access
Project: src\src\runtime\src\libraries\System.Security.AccessControl\src\System.Security.AccessControl.csproj (System.Security.AccessControl)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Principal;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;

namespace System.Security.AccessControl
{
    internal static class Win32
    {
        //
        // Wrapper around advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
        //

        internal static int ConvertSdToSddl(
            byte[] binaryForm,
            int requestedRevision,
            SecurityInfos si,
            out string? resultSddl)
        {
            int errorCode;
            IntPtr ByteArray;
            uint ByteArraySize = 0;

            if (!Interop.Advapi32.ConvertSdToStringSd(binaryForm, (uint)requestedRevision, (uint)si, out ByteArray, ref ByteArraySize))
            {
                errorCode = Marshal.GetLastPInvokeError();
                goto Error;
            }

            //
            // Extract data from the returned pointer
            //

            resultSddl = Marshal.PtrToStringUni(ByteArray)!;

            //
            // Now is a good time to get rid of the returned pointer
            //

            Marshal.FreeHGlobal(ByteArray);

            return 0;

        Error:

            resultSddl = null;

            if (errorCode == Interop.Errors.ERROR_NOT_ENOUGH_MEMORY)
            {
                throw new OutOfMemoryException();
            }

            return errorCode;
        }

        //
        // Wrapper around advapi32.GetSecurityInfo
        //

        internal static unsafe int GetSecurityInfo(
            ResourceType resourceType,
            string? name,
            SafeHandle? handle,
            AccessControlSections accessControlSections,
            out RawSecurityDescriptor? resultSd
            )
        {
            resultSd = null;

            int errorCode;
            IntPtr SidOwner, SidGroup, Dacl, Sacl, ByteArray;
            SecurityInfos SecurityInfos = 0;
            Privilege? privilege = null;

            if ((accessControlSections & AccessControlSections.Owner) != 0)
            {
                SecurityInfos |= SecurityInfos.Owner;
            }

            if ((accessControlSections & AccessControlSections.Group) != 0)
            {
                SecurityInfos |= SecurityInfos.Group;
            }

            if ((accessControlSections & AccessControlSections.Access) != 0)
            {
                SecurityInfos |= SecurityInfos.DiscretionaryAcl;
            }

            if ((accessControlSections & AccessControlSections.Audit) != 0)
            {
                SecurityInfos |= SecurityInfos.SystemAcl;
                privilege = new Privilege(Privilege.Security);
            }

            try
            {
                if (privilege != null)
                {
                    try
                    {
                        privilege.Enable();
                    }
                    catch (PrivilegeNotHeldException)
                    {
                        // we will ignore this exception and press on just in case this is a remote resource
                    }
                }

                if (name != null)
                {
                    errorCode = (int)Interop.Advapi32.GetSecurityInfoByName(name, (uint)resourceType, (uint)SecurityInfos, out SidOwner, out SidGroup, out Dacl, out Sacl, out ByteArray);
                }
                else if (handle != null)
                {
                    if (handle.IsInvalid)
                    {
                        throw new ArgumentException(
                            SR.Argument_InvalidSafeHandle,
                            nameof(handle));
                    }
                    else
                    {
                        errorCode = (int)Interop.Advapi32.GetSecurityInfoByHandle(handle, (uint)resourceType, (uint)SecurityInfos, &SidOwner, &SidGroup, &Dacl, &Sacl, &ByteArray);
                    }
                }
                else
                {
                    // both are null, shouldn't happen
                    // Changing from SystemException to ArgumentException as this code path is indicative of a null name argument
                    // as well as an accessControlSections argument with an audit flag
                    throw new ArgumentException();
                }

                if (errorCode == Interop.Errors.ERROR_SUCCESS && IntPtr.Zero.Equals(ByteArray))
                {
                    //
                    // This means that the object doesn't have a security descriptor. And thus we throw
                    // a specific exception for the caller to catch and handle properly.
                    //
                    throw new InvalidOperationException(SR.InvalidOperation_NoSecurityDescriptor);
                }
                else if (errorCode == Interop.Errors.ERROR_NOT_ALL_ASSIGNED ||
                         errorCode == Interop.Errors.ERROR_PRIVILEGE_NOT_HELD)
                {
                    throw new PrivilegeNotHeldException(Privilege.Security);
                }
                else if (errorCode == Interop.Errors.ERROR_ACCESS_DENIED ||
                    errorCode == Interop.Errors.ERROR_CANT_OPEN_ANONYMOUS)
                {
                    throw new UnauthorizedAccessException();
                }

                if (errorCode != Interop.Errors.ERROR_SUCCESS)
                {
                    goto Error;
                }
            }
            catch
            {
                // protection against exception filter-based luring attacks
                privilege?.Revert();
                throw;
            }
            finally
            {
                privilege?.Revert();
            }

            //
            // Extract data from the returned pointer
            //

            uint Length = Interop.Advapi32.GetSecurityDescriptorLength(ByteArray);

            byte[] BinaryForm = new byte[Length];

            Marshal.Copy(ByteArray, BinaryForm, 0, (int)Length);

            Marshal.FreeHGlobal(ByteArray);

            resultSd = new RawSecurityDescriptor(BinaryForm, 0);

            return Interop.Errors.ERROR_SUCCESS;

        Error:

            if (errorCode == Interop.Errors.ERROR_NOT_ENOUGH_MEMORY)
            {
                throw new OutOfMemoryException();
            }

            return errorCode;
        }

        //
        // Wrapper around advapi32.SetNamedSecurityInfoW and advapi32.SetSecurityInfo
        //

        internal static int SetSecurityInfo(
            ResourceType type,
            string? name,
            SafeHandle? handle,
            SecurityInfos securityInformation,
            SecurityIdentifier? owner,
            SecurityIdentifier? group,
            GenericAcl? sacl,
            GenericAcl? dacl)
        {
            int errorCode;
            int Length;
            byte[]? OwnerBinary = null, GroupBinary = null, SaclBinary = null, DaclBinary = null;
            Privilege? securityPrivilege = null;

            if (owner != null)
            {
                Length = owner.BinaryLength;
                OwnerBinary = new byte[Length];
                owner.GetBinaryForm(OwnerBinary, 0);
            }

            if (group != null)
            {
                Length = group.BinaryLength;
                GroupBinary = new byte[Length];
                group.GetBinaryForm(GroupBinary, 0);
            }

            if (dacl != null)
            {
                Length = dacl.BinaryLength;
                DaclBinary = new byte[Length];
                dacl.GetBinaryForm(DaclBinary, 0);
            }

            if (sacl != null)
            {
                Length = sacl.BinaryLength;
                SaclBinary = new byte[Length];
                sacl.GetBinaryForm(SaclBinary, 0);
            }

            if ((securityInformation & SecurityInfos.SystemAcl) != 0)
            {
                //
                // Enable security privilege if trying to set a SACL.
                // Note: even setting it by handle needs this privilege enabled!
                //

                securityPrivilege = new Privilege(Privilege.Security);
            }

            try
            {
                if (securityPrivilege != null)
                {
                    try
                    {
                        securityPrivilege.Enable();
                    }
                    catch (PrivilegeNotHeldException)
                    {
                        // we will ignore this exception and press on just in case this is a remote resource
                    }
                }

                if (name != null)
                {
                    errorCode = (int)Interop.Advapi32.SetSecurityInfoByName(name, (uint)type, unchecked((uint)securityInformation), OwnerBinary, GroupBinary, DaclBinary, SaclBinary);
                }
                else if (handle != null)
                {
                    if (handle.IsInvalid)
                    {
                        throw new ArgumentException(
                            SR.Argument_InvalidSafeHandle,
                            nameof(handle));
                    }
                    else
                    {
                        errorCode = (int)Interop.Advapi32.SetSecurityInfoByHandle(handle, (uint)type, (uint)securityInformation, OwnerBinary, GroupBinary, DaclBinary, SaclBinary);
                    }
                }
                else
                {
                    // both are null, shouldn't happen
                    Debug.Fail("Internal error: both name and handle are null");
                    throw new ArgumentException();
                }

                if (errorCode == Interop.Errors.ERROR_NOT_ALL_ASSIGNED ||
                    errorCode == Interop.Errors.ERROR_PRIVILEGE_NOT_HELD)
                {
                    throw new PrivilegeNotHeldException(Privilege.Security);
                }
                else if (errorCode == Interop.Errors.ERROR_ACCESS_DENIED ||
                    errorCode == Interop.Errors.ERROR_CANT_OPEN_ANONYMOUS)
                {
                    throw new UnauthorizedAccessException();
                }
                else if (errorCode != Interop.Errors.ERROR_SUCCESS)
                {
                    goto Error;
                }
            }
            catch
            {
                // protection against exception filter-based luring attacks
                securityPrivilege?.Revert();
                throw;
            }
            finally
            {
                securityPrivilege?.Revert();
            }

            return 0;

        Error:

            if (errorCode == Interop.Errors.ERROR_NOT_ENOUGH_MEMORY)
            {
                throw new OutOfMemoryException();
            }

            return errorCode;
        }
    }
}