// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*============================================================
**
** Classes:  Security Descriptor family of classes
**
**
===========================================================*/

using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Globalization;
using System.Diagnostics.Contracts;

namespace System.Security.AccessControl
{
    [Flags]

    public enum ControlFlags
    {
        None                                = 0x0000,
        OwnerDefaulted                      = 0x0001, // set by RM only
        GroupDefaulted                      = 0x0002, // set by RM only
        DiscretionaryAclPresent             = 0x0004, // set by RM or user, 'off' means DACL is null
        DiscretionaryAclDefaulted           = 0x0008, // set by RM only
        SystemAclPresent                    = 0x0010, // same as DiscretionaryAclPresent
        SystemAclDefaulted                  = 0x0020, // sams as DiscretionaryAclDefaulted
        DiscretionaryAclUntrusted           = 0x0040, // ignore this one
        ServerSecurity                      = 0x0080, // ignore this one
        DiscretionaryAclAutoInheritRequired = 0x0100, // ignore this one
        SystemAclAutoInheritRequired        = 0x0200, // ignore this one
        DiscretionaryAclAutoInherited       = 0x0400, // set by RM only
        SystemAclAutoInherited              = 0x0800, // set by RM only
        DiscretionaryAclProtected           = 0x1000, // when set, RM will stop inheriting
        SystemAclProtected                  = 0x2000, // when set, RM will stop inheriting
        RMControlValid                      = 0x4000, // the reserved 8 bits have some meaning
        SelfRelative                        = 0x8000, // must always be on
    }


    public abstract class GenericSecurityDescriptor
    {
        #region Protected Members

        //
        // Pictorially the structure of a security descriptor is as follows:
        //
        //       3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
        //       1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
        //      +---------------------------------------------------------------+
        //      |            Control            |Reserved1 (SBZ)|   Revision    |
        //      +---------------------------------------------------------------+
        //      |                            Owner                              |
        //      +---------------------------------------------------------------+
        //      |                            Group                              |
        //      +---------------------------------------------------------------+
        //      |                            Sacl                               |
        //      +---------------------------------------------------------------+
        //      |                            Dacl                               |
        //      +---------------------------------------------------------------+
        //

        internal const int HeaderLength = 20;
        internal const int OwnerFoundAt = 4;
        internal const int GroupFoundAt = 8;
        internal const int SaclFoundAt = 12;
        internal const int DaclFoundAt = 16;
        
        #endregion

        #region Private Methods

        //
        // Stores an integer in big-endian format into an array at a given offset
        //

        private static void MarshalInt( byte[] binaryForm, int offset, int number )
        {
            binaryForm[offset + 0] = ( byte )( number >> 0 );
            binaryForm[offset + 1] = ( byte )( number >> 8 );
            binaryForm[offset + 2] = ( byte )( number >> 16 );
            binaryForm[offset + 3] = ( byte )( number >> 24 );
        }

        //
        // Retrieves an integer stored in big-endian format at a given offset in an array
        //

        internal static int UnmarshalInt( byte[] binaryForm, int offset )
        {
            return (int)(
                ( binaryForm[offset + 0] <<  0 ) +
                ( binaryForm[offset + 1] <<  8 ) +
                ( binaryForm[offset + 2] << 16 ) +
                ( binaryForm[offset + 3] << 24 ));
        }

        #endregion

        #region Constructors

        protected GenericSecurityDescriptor()
        { }

        #endregion

        #region Protected Properties

        //
        // Marshaling logic requires calling into the derived
        // class to obtain pointers to SACL and DACL
        //

        internal abstract GenericAcl GenericSacl { get; }
        internal abstract GenericAcl GenericDacl { get; }
        private bool IsCraftedAefaDacl
        {
            get
            {
                return  (GenericDacl is DiscretionaryAcl) && (GenericDacl as DiscretionaryAcl).EveryOneFullAccessForNullDacl;
            }
        }

        #endregion

        #region Public Properties

        public static bool IsSddlConversionSupported()
        {
            return true; // SDDL to binary conversions are supported on Windows 2000 and higher
        }

        public static byte Revision
        {
            get { return 1; }
        }

        //
        // Allows retrieving and setting the control bits for this security descriptor
        //

        public abstract ControlFlags ControlFlags { get; }

        //
        // Allows retrieving and setting the owner SID for this security descriptor
        //

        public abstract SecurityIdentifier Owner { get; set; }

        //
        // Allows retrieving and setting the group SID for this security descriptor
        //

        public abstract SecurityIdentifier Group { get; set; }

        //
        // Retrieves the length of the binary representation
        // of the security descriptor
        //

        public int BinaryLength
        {
            get
            {
                int result = HeaderLength;

                if ( Owner != null )
                {
                    result += Owner.BinaryLength;
                }

                if ( Group != null )
                {
                    result += Group.BinaryLength;
                }

                if (( ControlFlags & ControlFlags.SystemAclPresent ) != 0 &&
                    GenericSacl != null )
                {
                    result += GenericSacl.BinaryLength;
                }

                if (( ControlFlags & ControlFlags.DiscretionaryAclPresent ) != 0 &&
                    GenericDacl != null && !IsCraftedAefaDacl)
                {
                    result += GenericDacl.BinaryLength;
                }

                return result;
            }
        }

        #endregion

        #region Public Methods

        //
        // Converts the security descriptor to its SDDL form
        //

        [System.Security.SecuritySafeCritical]  // auto-generated
        public string GetSddlForm( AccessControlSections includeSections )
        {
            byte[] binaryForm = new byte[BinaryLength];
            string resultSddl;
            int error;

            GetBinaryForm( binaryForm, 0 );

            SecurityInfos flags = 0;

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

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

            if (( includeSections & AccessControlSections.Audit ) != 0 )
            {
                flags |= SecurityInfos.SystemAcl;
            }

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

            error = Win32.ConvertSdToSddl( binaryForm, 1, flags, out resultSddl );

            if ( error == Win32Native.ERROR_INVALID_PARAMETER ||
                error == Win32Native.ERROR_UNKNOWN_REVISION )
            {
                //
                // Indicates that the marshaling logic in GetBinaryForm is busted
                //

                Contract.Assert( false, "binaryForm produced invalid output" );
                throw new InvalidOperationException();
            }
            else if ( error != Win32Native.ERROR_SUCCESS )
            {
                Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "Win32.ConvertSdToSddl returned {0}", error ));
                throw new InvalidOperationException();
            }

            return resultSddl;
        }

        //
        // Converts the security descriptor to its binary form
        //

        public void GetBinaryForm( byte[] binaryForm, int offset )
        {
            if ( binaryForm == null )
            {
                throw new ArgumentNullException( "binaryForm" );
            }

            if ( offset < 0 )
            {
                throw new ArgumentOutOfRangeException("offset",
                    Environment.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
            }

            if ( binaryForm.Length - offset < BinaryLength )
            {
                throw new ArgumentOutOfRangeException(
                    "binaryForm",
                    Environment.GetResourceString( "ArgumentOutOfRange_ArrayTooSmall" ));
            }
            Contract.EndContractBlock();

            //
            // the offset will grow as we go for each additional field (owner, group,
            // acl, etc) being written. But for each of such fields, we must use the
            // original offset as passed in, not the growing offset
            //

            int originalOffset = offset;

            //
            // Populate the header
            //

            int length = BinaryLength;

            byte rmControl =
                (( this is RawSecurityDescriptor ) &&
                 (( ControlFlags & ControlFlags.RMControlValid ) != 0 )) ? (( this as RawSecurityDescriptor ).ResourceManagerControl ) : ( byte )0;

            // if the DACL is our internally crafted NULL replacement, then let us turn off this control
            int materializedControlFlags = ( int )ControlFlags;
            if (IsCraftedAefaDacl)
            {
                unchecked {materializedControlFlags &= ~((int)ControlFlags.DiscretionaryAclPresent);}
            }

            binaryForm[offset + 0] = Revision;
            binaryForm[offset + 1] = rmControl;
            binaryForm[offset + 2] = ( byte )(( int )materializedControlFlags >> 0 );
            binaryForm[offset + 3] = ( byte )(( int )materializedControlFlags >> 8 );

            //
            // Compute offsets at which owner, group, SACL and DACL are stored
            //

            int ownerOffset, groupOffset, saclOffset, daclOffset;

            ownerOffset = offset + OwnerFoundAt;
            groupOffset = offset + GroupFoundAt;
            saclOffset = offset + SaclFoundAt;
            daclOffset = offset + DaclFoundAt;

            offset += HeaderLength;

            //
            // Marhsal the Owner SID into place
            //

            if ( Owner != null )
            {
                MarshalInt( binaryForm, ownerOffset, offset - originalOffset );
                Owner.GetBinaryForm( binaryForm, offset );
                offset += Owner.BinaryLength;
            }
            else
            {
                //
                // If Owner SID is null, store 0 in the offset field
                //

                MarshalInt( binaryForm, ownerOffset, 0 );
            }

            //
            // Marshal the Group SID into place
            //

            if ( Group != null )
            {
                MarshalInt( binaryForm, groupOffset, offset - originalOffset );
                Group.GetBinaryForm( binaryForm, offset );
                offset += Group.BinaryLength;
            }
            else
            {
                //
                // If Group SID is null, store 0 in the offset field
                //

                MarshalInt( binaryForm, groupOffset, 0 );
            }

            //
            // Marshal the SACL into place, if present
            //

            if (( ControlFlags & ControlFlags.SystemAclPresent ) != 0 &&
                GenericSacl != null )
            {
                MarshalInt( binaryForm, saclOffset, offset - originalOffset );
                GenericSacl.GetBinaryForm( binaryForm, offset );
                offset += GenericSacl.BinaryLength;
            }
            else
            {
                //
                // If SACL is null or not present, store 0 in the offset field
                //

                MarshalInt( binaryForm, saclOffset, 0 );
            }

            //
            // Marshal the DACL into place, if present
            //

            if (( ControlFlags & ControlFlags.DiscretionaryAclPresent ) != 0 &&
                GenericDacl != null && !IsCraftedAefaDacl ) 
            {
                MarshalInt( binaryForm, daclOffset, offset - originalOffset );
                GenericDacl.GetBinaryForm( binaryForm, offset );
                offset += GenericDacl.BinaryLength;
            }
            else
            {
                //
                // If DACL is null or not present, store 0 in the offset field
                //

                MarshalInt( binaryForm, daclOffset, 0 );
            }
        }

        #endregion
    }


    public sealed class RawSecurityDescriptor : GenericSecurityDescriptor
    {
        #region Private Members

        private SecurityIdentifier _owner;
        private SecurityIdentifier _group;
        private ControlFlags _flags;
        private RawAcl _sacl;
        private RawAcl _dacl;
        private byte _rmControl; // the not-so-reserved SBZ1 field

        #endregion

        #region Protected Properties

        internal override GenericAcl GenericSacl
        {
            get { return _sacl; }
        }

        internal override GenericAcl GenericDacl
        {
            get { return _dacl; }
        }

        #endregion

        #region Private methods

        private void CreateFromParts( ControlFlags flags, SecurityIdentifier owner, SecurityIdentifier group, RawAcl systemAcl, RawAcl discretionaryAcl )
        {
            SetFlags( flags );
            Owner = owner;
            Group = group;
            SystemAcl = systemAcl;
            DiscretionaryAcl = discretionaryAcl;
            ResourceManagerControl = 0;
        }

        #endregion

        #region Constructors

        //
        // Creates a security descriptor explicitly
        //

        public RawSecurityDescriptor( ControlFlags flags, SecurityIdentifier owner, SecurityIdentifier group, RawAcl systemAcl, RawAcl discretionaryAcl )
            : base()
        {
            CreateFromParts( flags, owner, group, systemAcl, discretionaryAcl );
        }

        //
        // Creates a security descriptor from an SDDL string
        //

        [System.Security.SecuritySafeCritical]  // auto-generated
        public RawSecurityDescriptor( string sddlForm )
            : this( BinaryFormFromSddlForm( sddlForm ), 0 )
        {
        }

        //
        // Creates a security descriptor from its binary representation
        // Important: the representation must be in self-relative format
        //

        public RawSecurityDescriptor( byte[] binaryForm, int offset )
            : base()
        {
            //
            // The array passed in must be valid
            //

            if ( binaryForm == null )
            {
                throw new ArgumentNullException( "binaryForm" );
            }
            
            if ( offset < 0 )
            {
                //
                // Offset must not be negative
                //

                throw new ArgumentOutOfRangeException("offset",
                    Environment.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
            }

            //
            // At least make sure the header is in place
            //

            if ( binaryForm.Length - offset < HeaderLength )
            {
                throw new ArgumentOutOfRangeException(
                    "binaryForm",
                    Environment.GetResourceString( "ArgumentOutOfRange_ArrayTooSmall" ));
            }

            //
            // We only understand revision-1 security descriptors
            //

            if ( binaryForm[offset + 0] != Revision )
            {
                throw new ArgumentOutOfRangeException("binaryForm",
                    Environment.GetResourceString( "AccessControl_InvalidSecurityDescriptorRevision" ));
            }
            Contract.EndContractBlock();


            ControlFlags flags;
            SecurityIdentifier owner, group;
            RawAcl sacl, dacl;
            byte rmControl;

            //
            // Extract the ResourceManagerControl field
            //

            rmControl = binaryForm[offset + 1];

            //
            // Extract the control flags
            //

            flags = ( ControlFlags )(( binaryForm[offset + 2] << 0 ) + ( binaryForm[offset + 3] << 8 ));

            //
            // Make sure that the input is in self-relative format
            //

            if (( flags & ControlFlags.SelfRelative ) == 0 )
            {
                throw new ArgumentException(
                    Environment.GetResourceString( "AccessControl_InvalidSecurityDescriptorSelfRelativeForm" ),
                    "binaryForm" );
            }

            //
            // Extract the owner SID
            //

            int ownerOffset = UnmarshalInt( binaryForm, offset + OwnerFoundAt );

            if ( ownerOffset != 0 )
            {
                owner = new SecurityIdentifier( binaryForm, offset + ownerOffset );
            }
            else
            {
                owner = null;
            }

            //
            // Extract the group SID
            //

            int groupOffset = UnmarshalInt( binaryForm, offset + GroupFoundAt );
            
            if ( groupOffset != 0 )
            {
                group = new SecurityIdentifier( binaryForm, offset + groupOffset );
            }
            else
            {
                group = null;
            }

            //
            // Extract the SACL
            //

            int saclOffset = UnmarshalInt( binaryForm, offset + SaclFoundAt );

            if ((( flags & ControlFlags.SystemAclPresent ) != 0 ) &&
                saclOffset != 0 )
            {
                sacl = new RawAcl( binaryForm, offset + saclOffset );
            }
            else
            {
                sacl = null;
            }

            //
            // Extract the DACL
            //

            int daclOffset = UnmarshalInt( binaryForm, offset + DaclFoundAt );

            if ((( flags & ControlFlags.DiscretionaryAclPresent ) != 0 ) &&
                daclOffset != 0 )
            {
                dacl = new RawAcl( binaryForm, offset + daclOffset );
            }
            else
            {
                dacl = null;
            }

            //
            // Create the resulting security descriptor
            //

            CreateFromParts( flags, owner, group, sacl, dacl );

            //
            // In the offchance that the flags indicate that the rmControl
            // field is meaningful, remember what was there.
            //

            if (( flags & ControlFlags.RMControlValid ) != 0 )
            {
                ResourceManagerControl = rmControl;
            }
        }

        #endregion

        #region Static Methods

        [System.Security.SecurityCritical]  // auto-generated
        private static byte[] BinaryFormFromSddlForm( string sddlForm )
        {
            if ( sddlForm == null )
            {
                throw new ArgumentNullException( "sddlForm" );
            }
            Contract.EndContractBlock();

            int error;
            IntPtr byteArray = IntPtr.Zero;
            uint byteArraySize = 0;
            const System.Int32 TRUE = 1;
            byte[] binaryForm = null;
            
            try
            {
                if ( TRUE != Win32Native.ConvertStringSdToSd(
                        sddlForm,
                        GenericSecurityDescriptor.Revision,
                        out byteArray,
                        ref byteArraySize ))
                {
                    error = Marshal.GetLastWin32Error();

                    if ( error == Win32Native.ERROR_INVALID_PARAMETER ||
                        error == Win32Native.ERROR_INVALID_ACL ||
                        error == Win32Native.ERROR_INVALID_SECURITY_DESCR ||
                        error == Win32Native.ERROR_UNKNOWN_REVISION )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString( "ArgumentException_InvalidSDSddlForm" ),
                            "sddlForm" );
                    }
                    else if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
                    {
                        throw new OutOfMemoryException();
                    }
                    else if ( error == Win32Native.ERROR_INVALID_SID )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString( "AccessControl_InvalidSidInSDDLString" ),
                            "sddlForm" );
                    }
                    else if ( error != Win32Native.ERROR_SUCCESS )
                    {
                        Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "Unexpected error out of Win32.ConvertStringSdToSd: {0}", error ));
                        throw new SystemException();
                    }
                }

                binaryForm = new byte[byteArraySize];

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

                Marshal.Copy( byteArray, binaryForm, 0, ( int )byteArraySize );
            }
            finally
            {
                //
                // Now is a good time to get rid of the returned pointer
                //
                if (byteArray != IntPtr.Zero)
                {
                    Win32Native.LocalFree( byteArray );
                }
            }

            return binaryForm;
        }

        #endregion

        #region Public Properties

        //
        // Allows retrieving the control bits for this security descriptor
        // Important: Special checks must be applied when setting flags and not
        // all flags can be set (for instance, we only deal with self-relative
        // security descriptors), thus flags can be set through other methods.
        //

        public override ControlFlags ControlFlags
        {
            get
            {
                return _flags;
            }
        }

        //
        // Allows retrieving and setting the owner SID for this security descriptor
        //

        public override SecurityIdentifier Owner
        {
            get
            {
                return _owner;
            }

            set
            {
                _owner = value;
            }
        }

        //
        // Allows retrieving and setting the group SID for this security descriptor
        //

        public override SecurityIdentifier Group
        {
            get
            {
                return _group;
            }

            set
            {
                _group = value;
            }
        }

        //
        // Allows retrieving and setting the SACL for this security descriptor
        //

        public RawAcl SystemAcl
        {
            get
            {
                return _sacl;
            }

            set
            {
                _sacl = value;
            }
        }

        //
        // Allows retrieving and setting the DACL for this security descriptor
        //

        public RawAcl DiscretionaryAcl
        {
            get
            {
                return _dacl;
            }

            set
            {
                _dacl = value;
            }
        }

        //
        // CORNER CASE (LEGACY)
        // The ostensibly "reserved" field in the Security Descriptor header
        // can in fact be used by obscure resource managers which in this
        // case must set the RMControlValid flag.
        //

        public byte ResourceManagerControl
        {
            get
            {
                return _rmControl;
            }

            set
            {
                _rmControl = value;
            }
        }


        #endregion

        #region Public Methods

        public void SetFlags( ControlFlags flags )
        {
            //
            // We can not deal with non-self-relative descriptors
            // so just forget about it
            //

            _flags = ( flags | ControlFlags.SelfRelative );
        }

        #endregion
    }


    public sealed class CommonSecurityDescriptor : GenericSecurityDescriptor
    {
        #region Private Members

        bool _isContainer;
        bool _isDS;
        private RawSecurityDescriptor _rawSd;
        private SystemAcl _sacl;
        private DiscretionaryAcl _dacl;


        #endregion

        #region Private Methods

        private void CreateFromParts( bool isContainer, bool isDS, ControlFlags flags, SecurityIdentifier owner, SecurityIdentifier group, SystemAcl systemAcl, DiscretionaryAcl discretionaryAcl )
        {
            if ( systemAcl != null &&
                systemAcl.IsContainer != isContainer )
            {
                throw new ArgumentException(
                    Environment.GetResourceString( isContainer ?
                        "AccessControl_MustSpecifyContainerAcl" :
                        "AccessControl_MustSpecifyLeafObjectAcl" ),
                    "systemAcl" );
            }

            if ( discretionaryAcl != null &&
                discretionaryAcl.IsContainer != isContainer )
            {
                throw new ArgumentException(
                    Environment.GetResourceString( isContainer ?
                        "AccessControl_MustSpecifyContainerAcl" :
                        "AccessControl_MustSpecifyLeafObjectAcl" ),
                    "discretionaryAcl" );
            }

            _isContainer = isContainer;

            if ( systemAcl != null && 
                systemAcl.IsDS != isDS )
            {
                throw new ArgumentException(
                    Environment.GetResourceString( isDS ? 
                        "AccessControl_MustSpecifyDirectoryObjectAcl" : 
                        "AccessControl_MustSpecifyNonDirectoryObjectAcl"), 
                    "systemAcl");
            }

            if ( discretionaryAcl != null && 
                discretionaryAcl.IsDS != isDS )
            {
                throw new ArgumentException(
                    Environment.GetResourceString( isDS ? 
                        "AccessControl_MustSpecifyDirectoryObjectAcl" : 
                        "AccessControl_MustSpecifyNonDirectoryObjectAcl"), 
                    "discretionaryAcl");
            }

            _isDS = isDS;

            _sacl = systemAcl;

            //
            // Replace null DACL with an allow-all for everyone DACL
            //
        
            if ( discretionaryAcl == null )
            {
                //
                // to conform to native behavior, we will add allow everyone ace for DACL
                //

                discretionaryAcl = DiscretionaryAcl.CreateAllowEveryoneFullAccess(_isDS, _isContainer);
            }

            _dacl = discretionaryAcl;

            //
            // DACL is never null. So always set the flag bit on
            //

            ControlFlags actualFlags = flags | ControlFlags.DiscretionaryAclPresent;
            
            //
            // Keep SACL and the flag bit in [....].
            //

            if (systemAcl == null)
            {
                unchecked { actualFlags &= ~(ControlFlags.SystemAclPresent); }
            }
            else
            {
                actualFlags |= (ControlFlags.SystemAclPresent);
            }

            _rawSd = new RawSecurityDescriptor( actualFlags, owner, group, systemAcl == null ? null : systemAcl.RawAcl, discretionaryAcl.RawAcl );
        }

        #endregion

        #region Constructors

        //
        // Creates a security descriptor explicitly
        //

        public CommonSecurityDescriptor( bool isContainer, bool isDS, ControlFlags flags, SecurityIdentifier owner, SecurityIdentifier group, SystemAcl systemAcl, DiscretionaryAcl discretionaryAcl )
        {
            CreateFromParts( isContainer, isDS, flags, owner, group, systemAcl, discretionaryAcl );
        }

        private CommonSecurityDescriptor( bool isContainer, bool isDS, ControlFlags flags, SecurityIdentifier owner, SecurityIdentifier group, RawAcl systemAcl, RawAcl discretionaryAcl )
            : this( isContainer, isDS, flags, owner, group, systemAcl == null ? null : new SystemAcl( isContainer, isDS, systemAcl ), discretionaryAcl == null ? null : new DiscretionaryAcl( isContainer, isDS, discretionaryAcl ))
        {
        }

        public CommonSecurityDescriptor( bool isContainer, bool isDS, RawSecurityDescriptor rawSecurityDescriptor )
            : this( isContainer, isDS, rawSecurityDescriptor, false )
        {
        }

        internal CommonSecurityDescriptor( bool isContainer, bool isDS, RawSecurityDescriptor rawSecurityDescriptor, bool trusted )
        {
            if ( rawSecurityDescriptor == null )
            {
                throw new ArgumentNullException( "rawSecurityDescriptor" );
            }
            Contract.EndContractBlock();

            CreateFromParts(
                isContainer,
                isDS,
                rawSecurityDescriptor.ControlFlags,
                rawSecurityDescriptor.Owner,
                rawSecurityDescriptor.Group,
                rawSecurityDescriptor.SystemAcl == null ? null : new SystemAcl( isContainer, isDS, rawSecurityDescriptor.SystemAcl, trusted ),
                rawSecurityDescriptor.DiscretionaryAcl == null ? null : new DiscretionaryAcl( isContainer, isDS, rawSecurityDescriptor.DiscretionaryAcl, trusted ));
        }

        //
        // Create a security descriptor from an SDDL string
        //

        public CommonSecurityDescriptor( bool isContainer, bool isDS, string sddlForm )
            : this( isContainer, isDS, new RawSecurityDescriptor( sddlForm ), true )
        {
        }

        //
        // Create a security descriptor from its binary representation
        //

        public CommonSecurityDescriptor( bool isContainer, bool isDS, byte[] binaryForm, int offset )
            : this( isContainer, isDS, new RawSecurityDescriptor( binaryForm, offset ), true )
        {
        }

        #endregion

        #region Protected Properties

        internal sealed override GenericAcl GenericSacl
        {
            get { return _sacl; }
        }

        internal sealed override GenericAcl GenericDacl
        {
            get { return _dacl; }
        }

        #endregion

        #region Public Properties

        public bool IsContainer
        {
            get { return _isContainer; }
        }

        public bool IsDS
        {
            get { return _isDS; }
        }


        //
        // Allows retrieving the control bits for this security descriptor
        //

        public override ControlFlags ControlFlags
        {
            get
            {
                return _rawSd.ControlFlags;
            }
        }

        //
        // Allows retrieving and setting the owner SID for this security descriptor
        //

        public override SecurityIdentifier Owner
        {
            get
            {
                return _rawSd.Owner;
            }

            set
            {
                _rawSd.Owner = value;
            }
        }

        //
        // Allows retrieving and setting the group SID for this security descriptor
        //

        public override SecurityIdentifier Group
        {
            get
            {
                return _rawSd.Group;
            }

            set
            {
                _rawSd.Group = value;
            }
        }


        public SystemAcl SystemAcl
        {
            get
            {
                return _sacl;
            }

            set
            {
                if ( value != null )
                {
                    if ( value.IsContainer != this.IsContainer )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString( this.IsContainer ?
                                "AccessControl_MustSpecifyContainerAcl" :
                                "AccessControl_MustSpecifyLeafObjectAcl" ),
                            "value" );
                    }

                    if ( value.IsDS != this.IsDS )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString(this.IsDS ? 
                                "AccessControl_MustSpecifyDirectoryObjectAcl" : 
                                "AccessControl_MustSpecifyNonDirectoryObjectAcl"), 
                            "value");
                    }
                }

                _sacl = value;
                
                if ( _sacl != null )
                {
                    _rawSd.SystemAcl = _sacl.RawAcl;
                    AddControlFlags( ControlFlags.SystemAclPresent );
                }
                else
                {
                    _rawSd.SystemAcl = null;
                    RemoveControlFlags( ControlFlags.SystemAclPresent );
                }
            }
        }

        //
        // Allows retrieving and setting the DACL for this security descriptor
        //

        public DiscretionaryAcl DiscretionaryAcl
        {
            get
            {
                return _dacl;
            }

            set
            {
                if ( value != null )
                {
                    if ( value.IsContainer != this.IsContainer )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString( this.IsContainer ?
                                "AccessControl_MustSpecifyContainerAcl" :
                                "AccessControl_MustSpecifyLeafObjectAcl" ),
                            "value" );
                    }

                    if ( value.IsDS != this.IsDS )
                    {
                        throw new ArgumentException(
                            Environment.GetResourceString( this.IsDS ?
                                "AccessControl_MustSpecifyDirectoryObjectAcl" : 
                                "AccessControl_MustSpecifyNonDirectoryObjectAcl"), 
                            "value");
                    }
                }

                //
                // NULL DACLs are replaced with allow everyone full access DACLs.
                //

                if ( value == null )
                {
                    _dacl = DiscretionaryAcl.CreateAllowEveryoneFullAccess(IsDS, IsContainer);
                }
                else
                {
                    _dacl = value;
                }

                _rawSd.DiscretionaryAcl = _dacl.RawAcl;
                AddControlFlags( ControlFlags.DiscretionaryAclPresent );
            }
        }

        public bool IsSystemAclCanonical
        {
            get { return ( SystemAcl == null || SystemAcl.IsCanonical ); }
        }

        public bool IsDiscretionaryAclCanonical
        {
            get { return ( DiscretionaryAcl == null || DiscretionaryAcl.IsCanonical ); }
        }

        #endregion

        #region Public Methods

        public void SetSystemAclProtection( bool isProtected, bool preserveInheritance )
        {
            if ( !isProtected )
            {
                RemoveControlFlags( ControlFlags.SystemAclProtected );
            }
            else
            {
                if ( !preserveInheritance && SystemAcl != null )
                {
                    SystemAcl.RemoveInheritedAces();
                }

                AddControlFlags( ControlFlags.SystemAclProtected );
            }
        }

        public void SetDiscretionaryAclProtection( bool isProtected, bool preserveInheritance )
        {
            if ( !isProtected )
            {
                RemoveControlFlags( ControlFlags.DiscretionaryAclProtected );
            }
            else
            {
                if ( !preserveInheritance && DiscretionaryAcl != null )
                {
                    DiscretionaryAcl.RemoveInheritedAces();
                }

                AddControlFlags( ControlFlags.DiscretionaryAclProtected );
            }
            if (DiscretionaryAcl != null && DiscretionaryAcl.EveryOneFullAccessForNullDacl)
            {
                DiscretionaryAcl.EveryOneFullAccessForNullDacl = false;
            }
        }

        public void PurgeAccessControl( SecurityIdentifier sid )
        {
            if ( sid == null )
            {
                throw new ArgumentNullException( "sid" );
            }
            Contract.EndContractBlock();

            if ( DiscretionaryAcl != null )
            {
                DiscretionaryAcl.Purge( sid );
            }
        }

        public void PurgeAudit( SecurityIdentifier sid )
        {
            if ( sid == null )
            {
                throw new ArgumentNullException( "sid" );
            }
            Contract.EndContractBlock();

            if ( SystemAcl != null )
            {
                SystemAcl.Purge( sid );
            }
        }

        #endregion

        #region internal Methods
        internal void UpdateControlFlags(ControlFlags flagsToUpdate, ControlFlags newFlags)
        {
            ControlFlags finalFlags = newFlags | (_rawSd.ControlFlags & (~flagsToUpdate));
            _rawSd.SetFlags(finalFlags);
        }

        //
        // These two add/remove method must be called with great care (and thus it is internal)
        // The caller is responsible for keeping the SaclPresent and DaclPresent bits in [....]
        // with the actual SACL and DACL.
        //

        internal void AddControlFlags(ControlFlags flags)
        {
            _rawSd.SetFlags(_rawSd.ControlFlags | flags);
        }

        internal void RemoveControlFlags(ControlFlags flags)
        {
            unchecked
            {
                _rawSd.SetFlags(_rawSd.ControlFlags & ~flags);
            }
        }

        internal bool IsSystemAclPresent
        {
            get
            {
                return (_rawSd.ControlFlags & ControlFlags.SystemAclPresent) != 0;
            }
        }

        internal bool IsDiscretionaryAclPresent
        {
            get
            {
                return (_rawSd.ControlFlags & ControlFlags.DiscretionaryAclPresent) != 0;
            }
        }
        #endregion
    }
}
