Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / security / accesscontrol / acl.cs
bloba32e35b6cc514f161570626d18e43a800437b2ce
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Classes: Access Control List (ACL) family of classes
9 **
11 ===========================================================*/
13 namespace System.Security.AccessControl
15 using System;
16 using System.Collections;
17 using System.Security.Principal;
18 using System.Diagnostics.Contracts;
20 public sealed class AceEnumerator : IEnumerator
22 #region Private Members
25 // Current enumeration index
28 private int _current;
31 // Parent collection
34 private readonly GenericAcl _acl;
36 #endregion
38 #region Constructors
40 internal AceEnumerator( GenericAcl collection )
42 if ( collection == null )
44 throw new ArgumentNullException( "collection" );
46 Contract.EndContractBlock();
48 _acl = collection;
49 Reset();
52 #endregion
54 #region IEnumerator Interface
56 object IEnumerator.Current
58 get
60 if ( _current == -1 ||
61 _current >= _acl.Count )
63 throw new InvalidOperationException( Environment.GetResourceString( "Arg_InvalidOperationException" ));
66 return _acl[_current];
70 public GenericAce Current
72 get { return (( IEnumerator )this ).Current as GenericAce; }
75 public bool MoveNext()
77 _current++;
79 return ( _current < _acl.Count );
82 public void Reset()
84 _current = -1;
87 #endregion
91 public abstract class GenericAcl : ICollection
93 #region Constructors
95 protected GenericAcl()
96 { }
98 #endregion
100 #region Public Constants
103 // ACL revisions
106 public static readonly byte AclRevision = 2;
107 public static readonly byte AclRevisionDS = 4;
110 // Maximum length of a binary representation of the ACL
113 public static readonly int MaxBinaryLength = ushort.MaxValue;
115 #endregion
117 #region Protected Members
120 // Define an ACL and the ACE format. The structure of an ACL header
121 // followed by one or more ACEs. Pictorally the structure of an ACL header
122 // is as follows:
124 // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
125 // 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
126 // +-------------------------------+---------------+---------------+
127 // | AclSize | Sbz1 | AclRevision |
128 // +-------------------------------+---------------+---------------+
129 // | Sbz2 | AceCount |
130 // +-------------------------------+-------------------------------+
133 internal const int HeaderLength = 8;
135 #endregion
137 #region Public Properties
140 // Returns the revision of the ACL
143 public abstract byte Revision { get; }
146 // Returns the length of the binary representation of the ACL
149 public abstract int BinaryLength { get; }
152 // Retrieves the ACE at a specified index
155 public abstract GenericAce this[int index] { get; set; }
157 #endregion
159 #region Public Methods
162 // Returns the binary representation of the ACL
165 public abstract void GetBinaryForm( byte[] binaryForm, int offset );
167 #endregion
169 #region ICollection Implementation
171 void ICollection.CopyTo( Array array, int index )
173 if ( array == null )
175 throw new ArgumentNullException( "array" );
178 if ( array.Rank != 1 )
180 throw new RankException( Environment.GetResourceString( "Rank_MultiDimNotSupported" ));
182 Contract.EndContractBlock();
184 if ( index < 0 )
186 throw new ArgumentOutOfRangeException(
187 "index",
188 Environment.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
190 else if ( array.Length - index < Count )
192 throw new ArgumentOutOfRangeException(
193 "array",
194 Environment.GetResourceString( "ArgumentOutOfRange_ArrayTooSmall" ));
197 for ( int i = 0; i < Count; i++ )
199 array.SetValue( this[i], index + i );
203 public void CopyTo( GenericAce[] array, int index )
205 (( ICollection )this ).CopyTo( array, index );
208 public abstract int Count { get; }
210 public bool IsSynchronized
212 get { return false; }
215 public virtual object SyncRoot
217 get { return this; }
220 #endregion
222 #region IEnumerable Implementation
224 IEnumerator IEnumerable.GetEnumerator()
226 return new AceEnumerator( this );
229 public AceEnumerator GetEnumerator()
231 return (( IEnumerable )this ).GetEnumerator() as AceEnumerator;
234 #endregion
238 public sealed class RawAcl : GenericAcl
240 #region Private Members
242 private byte _revision;
243 private ArrayList _aces;
245 #endregion
247 #region Private Methods
249 private static void VerifyHeader( byte[] binaryForm, int offset, out byte revision, out int count, out int length )
251 if ( binaryForm == null )
253 throw new ArgumentNullException( "binaryForm" );
255 Contract.EndContractBlock();
257 if ( offset < 0 )
260 // Offset must not be negative
263 throw new ArgumentOutOfRangeException(
264 "offset",
265 Environment.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
268 if ( binaryForm.Length - offset < HeaderLength )
271 // We expect at least the ACL header
274 goto InvalidParameter;
277 revision = binaryForm[offset + 0];
278 length = ( binaryForm[offset + 2] << 0 ) + ( binaryForm[offset + 3] << 8 );
279 count = ( binaryForm[offset + 4] << 0 ) + ( binaryForm[offset + 5] << 8 );
281 if ( length > binaryForm.Length - offset )
284 // Reported length of ACL ought to be no longer than the
285 // length of the buffer passed in
288 goto InvalidParameter;
291 return;
293 InvalidParameter:
295 throw new ArgumentOutOfRangeException(
296 "binaryForm",
297 Environment.GetResourceString( "ArgumentOutOfRange_ArrayTooSmall" ));
300 private void MarshalHeader( byte[] binaryForm, int offset )
302 if ( binaryForm == null )
304 throw new ArgumentNullException( "binaryForm" );
306 else if ( offset < 0 )
308 throw new ArgumentOutOfRangeException(
309 "offset",
310 Environment.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
312 else if ( BinaryLength > MaxBinaryLength )
314 throw new InvalidOperationException( Environment.GetResourceString( "AccessControl_AclTooLong" ));
316 else if ( binaryForm.Length - offset < BinaryLength )
318 throw new ArgumentOutOfRangeException(
319 "binaryForm",
320 Environment.GetResourceString( "ArgumentOutOfRange_ArrayTooSmall" ));
323 binaryForm[offset + 0] = Revision;
324 binaryForm[offset + 1] = 0;
325 binaryForm[offset + 2] = ( byte )( BinaryLength >> 0 );
326 binaryForm[offset + 3] = ( byte )( BinaryLength >> 8 );
327 binaryForm[offset + 4] = ( byte )( Count >> 0 );
328 binaryForm[offset + 5] = ( byte )( Count >> 8 );
329 binaryForm[offset + 6] = 0;
330 binaryForm[offset + 7] = 0;
333 internal void SetBinaryForm( byte[] binaryForm, int offset )
335 int count, length;
338 // Verify the header and extract interesting header info
341 VerifyHeader( binaryForm, offset, out _revision, out count, out length );
344 // Remember how far ahead the binary form should end (for later verification)
347 length += offset;
349 offset += HeaderLength;
351 _aces = new ArrayList( count );
352 int binaryLength = HeaderLength;
354 for ( int i = 0; i < count; i++ )
356 GenericAce ace = GenericAce.CreateFromBinaryForm( binaryForm, offset );
358 int aceLength = ace.BinaryLength;
360 if ( binaryLength + aceLength > MaxBinaryLength )
363 // The ACE was too long - it would overflow the ACL maximum length
366 throw new ArgumentException(
367 Environment.GetResourceString( "ArgumentException_InvalidAclBinaryForm" ),
368 "binaryForm" );
371 _aces.Add( ace );
373 if ( aceLength % 4 != 0 )
376 // This indicates a bug in one of the ACE classes.
377 // Binary length of an ace must ALWAYS be divisible by 4.
380 Contract.Assert( false, "aceLength % 4 != 0" );
381 throw new SystemException();
384 binaryLength += aceLength;
386 if ( _revision == AclRevisionDS )
389 // Increment the offset by the advertised length rather than the
390 // actual binary length. (Ideally these two should match, but for
391 // object aces created through ADSI, the actual length is 32 bytes
392 // less than the allocated size of the ACE. This is a bug in ADSI.)
394 offset += (binaryForm[offset + 2] << 0) + (binaryForm[offset + 3] << 8);
396 else
398 offset += aceLength;
402 // Verify that no more than the advertised length of the ACL was consumed
405 if ( offset > length )
407 goto InvalidParameter;
411 return;
413 InvalidParameter:
415 throw new ArgumentException(
416 Environment.GetResourceString( "ArgumentException_InvalidAclBinaryForm" ),
417 "binaryForm" );
420 #endregion
422 #region Constructors
425 // Creates an empty ACL
428 public RawAcl( byte revision, int capacity )
429 : base()
431 _revision = revision;
432 _aces = new ArrayList( capacity );
436 // Creates an ACL from its binary representation
439 public RawAcl( byte[] binaryForm, int offset )
440 : base()
442 SetBinaryForm( binaryForm, offset );
445 #endregion
447 #region Public Properties
450 // Returns the revision of the ACL
453 public override byte Revision
455 get { return _revision; }
459 // Returns the number of ACEs in the ACL
462 public override int Count
464 get { return _aces.Count; }
468 // Returns the length of the binary representation of the ACL
471 public override int BinaryLength
475 int binaryLength = HeaderLength;
477 for (int i = 0; i < Count; i++)
479 GenericAce ace = _aces[i] as GenericAce;
480 binaryLength += ace.BinaryLength;
483 return binaryLength;
487 #endregion
489 #region Public Methods
492 // Returns the binary representation of the ACL
495 public override void GetBinaryForm( byte[] binaryForm, int offset )
498 // Populate the header
501 MarshalHeader( binaryForm, offset );
502 offset += HeaderLength;
504 for ( int i = 0; i < Count; i++ )
506 GenericAce ace = _aces[i] as GenericAce;
508 ace.GetBinaryForm( binaryForm, offset );
510 int aceLength = ace.BinaryLength;
512 if ( aceLength % 4 != 0 )
515 // This indicates a bug in one of the ACE classes.
516 // Binary length of an ace must ALWAYS be divisible by 4.
519 Contract.Assert( false, "aceLength % 4 != 0" );
520 throw new SystemException();
523 offset += aceLength;
528 // Return an ACE at a particular index
529 // The ACE is not cloned prior to returning, enabling the caller
530 // to modify the ACE in place (a potentially dangerous operation)
533 public override GenericAce this[int index]
537 return _aces[index] as GenericAce;
542 if ( value == null )
544 throw new ArgumentNullException( "value" );
546 Contract.EndContractBlock();
548 if ( value.BinaryLength % 4 != 0 )
551 // This indicates a bug in one of the ACE classes.
552 // Binary length of an ace must ALWAYS be divisible by 4.
555 Contract.Assert( false, "aceLength % 4 != 0" );
556 throw new SystemException();
559 int newBinaryLength = BinaryLength - ( index < _aces.Count ? ( _aces[index] as GenericAce ).BinaryLength : 0 ) + value.BinaryLength;
561 if ( newBinaryLength > MaxBinaryLength )
563 throw new OverflowException( Environment.GetResourceString( "AccessControl_AclTooLong" ));
566 _aces[index] = value;
571 // Adds an ACE at the specified index
574 public void InsertAce( int index, GenericAce ace )
576 if ( ace == null )
578 throw new ArgumentNullException( "ace" );
580 Contract.EndContractBlock();
582 if ( BinaryLength + ace.BinaryLength > MaxBinaryLength )
584 throw new OverflowException( Environment.GetResourceString( "AccessControl_AclTooLong" ));
587 _aces.Insert( index, ace );
591 // Removes an ACE at the specified index
594 public void RemoveAce( int index )
596 GenericAce ace = _aces[index] as GenericAce;
597 _aces.RemoveAt( index );
600 #endregion
604 public abstract class CommonAcl : GenericAcl
606 #region Add/Remove Logic Support
608 [Flags]
609 private enum AF // ACE flags
611 CI = 0x8, // container inherit
612 OI = 0x4, // object inherit
613 IO = 0x2, // inherit only
614 NP = 0x1, // no propagate inherit
615 Invalid = NP, // not a valid combination of flags
618 [Flags]
619 private enum PM // Propagation matrix
621 F = 0x10, // folder
622 CF = 0x08, // child folder
623 CO = 0x04, // child object
624 GF = 0x02, // grandchild folder
625 GO = 0x01, // grandchild object
626 Invalid = GO, // not a valid combination of flags
629 private static PM[] AFtoPM; // AceFlags-to-Propagation conversion matrix
630 private static AF[] PMtoAF; // Propagation-to-AceFlags conversion matrix
632 static CommonAcl()
634 AFtoPM = new PM[16];
636 for ( int i = 0; i < AFtoPM.Length; i++ )
638 AFtoPM[i] = PM.Invalid;
642 // This table specifies what effect various combinations of inheritance bits
643 // have on how ACEs are inherited onto child objects
644 // Important: Not all combinations of inheritance bits are valid
647 AFtoPM[( int )( 0 | 0 | 0 | 0 )] = PM.F | 0 | 0 | 0 | 0 ;
648 AFtoPM[( int )( 0 | AF.OI | 0 | 0 )] = PM.F | 0 | PM.CO | 0 | PM.GO ;
649 AFtoPM[( int )( 0 | AF.OI | 0 | AF.NP )] = PM.F | 0 | PM.CO | 0 | 0 ;
650 AFtoPM[( int )( 0 | AF.OI | AF.IO | 0 )] = 0 | 0 | PM.CO | 0 | PM.GO ;
651 AFtoPM[( int )( 0 | AF.OI | AF.IO | AF.NP )] = 0 | 0 | PM.CO | 0 | 0 ;
652 AFtoPM[( int )( AF.CI | 0 | 0 | 0 )] = PM.F | PM.CF | 0 | PM.GF | 0 ;
653 AFtoPM[( int )( AF.CI | 0 | 0 | AF.NP )] = PM.F | PM.CF | 0 | 0 | 0 ;
654 AFtoPM[( int )( AF.CI | 0 | AF.IO | 0 )] = 0 | PM.CF | 0 | PM.GF | 0 ;
655 AFtoPM[( int )( AF.CI | 0 | AF.IO | AF.NP )] = 0 | PM.CF | 0 | 0 | 0 ;
656 AFtoPM[( int )( AF.CI | AF.OI | 0 | 0 )] = PM.F | PM.CF | PM.CO | PM.GF | PM.GO ;
657 AFtoPM[( int )( AF.CI | AF.OI | 0 | AF.NP )] = PM.F | PM.CF | PM.CO | 0 | 0 ;
658 AFtoPM[( int )( AF.CI | AF.OI | AF.IO | 0 )] = 0 | PM.CF | PM.CO | PM.GF | PM.GO ;
659 AFtoPM[( int )( AF.CI | AF.OI | AF.IO | AF.NP )] = 0 | PM.CF | PM.CO | 0 | 0 ;
661 PMtoAF = new AF[32];
663 for ( int i = 0; i < PMtoAF.Length; i++ )
665 PMtoAF[i] = AF.Invalid;
669 // This table is a reverse lookup table of the AFtoPM table
670 // Given how inheritance is applied to child objects and containers,
671 // it helps figure out whether that pattern is expressible using
672 // the four ACE inheritance bits
675 PMtoAF[( int )( PM.F | 0 | 0 | 0 | 0 )] = 0 | 0 | 0 | 0 ;
676 PMtoAF[( int )( PM.F | 0 | PM.CO | 0 | PM.GO )] = 0 | AF.OI | 0 | 0 ;
677 PMtoAF[( int )( PM.F | 0 | PM.CO | 0 | 0 )] = 0 | AF.OI | 0 | AF.NP ;
678 PMtoAF[( int )( 0 | 0 | PM.CO | 0 | PM.GO )] = 0 | AF.OI | AF.IO | 0 ;
679 PMtoAF[( int )( 0 | 0 | PM.CO | 0 | 0 )] = 0 | AF.OI | AF.IO | AF.NP ;
680 PMtoAF[( int )( PM.F | PM.CF | 0 | PM.GF | 0 )] = AF.CI | 0 | 0 | 0 ;
681 PMtoAF[( int )( PM.F | PM.CF | 0 | 0 | 0 )] = AF.CI | 0 | 0 | AF.NP ;
682 PMtoAF[( int )( 0 | PM.CF | 0 | PM.GF | 0 )] = AF.CI | 0 | AF.IO | 0 ;
683 PMtoAF[( int )( 0 | PM.CF | 0 | 0 | 0 )] = AF.CI | 0 | AF.IO | AF.NP ;
684 PMtoAF[( int )( PM.F | PM.CF | PM.CO | PM.GF | PM.GO )] = AF.CI | AF.OI | 0 | 0 ;
685 PMtoAF[( int )( PM.F | PM.CF | PM.CO | 0 | 0 )] = AF.CI | AF.OI | 0 | AF.NP ;
686 PMtoAF[( int )( 0 | PM.CF | PM.CO | PM.GF | PM.GO )] = AF.CI | AF.OI | AF.IO | 0 ;
687 PMtoAF[( int )( 0 | PM.CF | PM.CO | 0 | 0 )] = AF.CI | AF.OI | AF.IO | AF.NP ;
691 // Canonicalizes AceFlags into a form that the mapping tables understand
694 private static AF AFFromAceFlags( AceFlags aceFlags, bool isDS )
696 AF af = 0;
698 if (( aceFlags & AceFlags.ContainerInherit ) != 0)
700 af |= AF.CI;
704 // ObjectInherit applies only to regular aces not object aces
705 // so it can be ignored in the object aces case
707 if (( !isDS ) && (( aceFlags & AceFlags.ObjectInherit ) != 0 ))
709 af |= AF.OI;
712 if (( aceFlags & AceFlags.InheritOnly ) != 0 )
714 af |= AF.IO;
717 if (( aceFlags & AceFlags.NoPropagateInherit ) != 0 )
719 af |= AF.NP;
722 return af;
726 // Converts lookup table representation of AceFlags into the "public" form
729 private static AceFlags AceFlagsFromAF( AF af, bool isDS )
731 AceFlags aceFlags = 0;
733 if (( af & AF.CI ) != 0 )
735 aceFlags |= AceFlags.ContainerInherit;
739 // ObjectInherit applies only to regular aces not object aces
740 // so it can be ignored in the object aces case
742 if (( !isDS ) && (( af & AF.OI ) != 0 ))
744 aceFlags |= AceFlags.ObjectInherit;
747 if (( af & AF.IO ) != 0 )
749 aceFlags |= AceFlags.InheritOnly;
752 if (( af & AF.NP ) != 0 )
754 aceFlags |= AceFlags.NoPropagateInherit;
757 return aceFlags;
761 // Implements the merge of inheritance bits during the 'ADD' operation
764 private static bool MergeInheritanceBits( AceFlags left, AceFlags right, bool isDS, out AceFlags result )
766 result = 0;
768 AF leftAF = AFFromAceFlags( left, isDS );
769 AF rightAF = AFFromAceFlags( right, isDS );
771 PM leftPM = AFtoPM[(int)leftAF];
772 PM rightPM = AFtoPM[(int)rightAF];
774 if ( leftPM == PM.Invalid || rightPM == PM.Invalid )
776 return false; // incorrect ACE flags?
779 PM resultPM = leftPM | rightPM;
780 AF resultAF = PMtoAF[( int )resultPM];
782 if ( resultAF == AF.Invalid )
784 return false;
786 else
788 result = AceFlagsFromAF( resultAF, isDS );
789 return true;
793 private static bool RemoveInheritanceBits( AceFlags existing, AceFlags remove, bool isDS, out AceFlags result, out bool total )
795 result = 0;
796 total = false;
798 AF leftAF = AFFromAceFlags( existing, isDS );
799 AF rightAF = AFFromAceFlags( remove, isDS );
801 PM leftPM = AFtoPM[( int )leftAF];
802 PM rightPM = AFtoPM[( int )rightAF];
804 if ( leftPM == PM.Invalid || rightPM == PM.Invalid )
806 return false; // incorrect ACE flags?
809 PM resultPM;
810 unchecked { resultPM = leftPM & ~rightPM; }
813 // If the resulting propagation matrix is zero,
814 // communicate back the fact that removal is "total"
817 if ( resultPM == 0 )
819 total = true;
820 return true;
823 AF resultAF = PMtoAF[( int )resultPM];
825 if ( resultAF == AF.Invalid )
827 return false;
829 else
831 result = AceFlagsFromAF( resultAF, isDS );
832 return true;
836 #endregion
838 #region Private Members
840 private RawAcl _acl;
841 private bool _isDirty = false;
842 private readonly bool _isCanonical;
843 private readonly bool _isContainer;
846 // To distinguish between a directory object acl and other common acls.
849 private readonly bool _isDS;
851 #endregion
853 #region Private Methods
855 private void CanonicalizeIfNecessary()
857 if ( _isDirty )
859 Canonicalize( false, this is DiscretionaryAcl );
860 _isDirty = false;
864 private enum ComparisonResult
866 LessThan,
867 EqualTo,
868 GreaterThan,
872 // Compares two discretionary ACEs and returns
873 // LessThan if ace1 < ace2
874 // EqualTo if ace1 == ace2
875 // GreaterThan if ace1 > ace2
877 // The order is:
878 // - explicit Access Denied ACEs
879 // [regular aces first, then object aces]
880 // - explicit Access Allowed ACEs
881 // [regular aces first, then object aces]
882 // - inherited ACEs (in the original order )
883 // - user-defined ACEs (in the original order )
886 private static int DaclAcePriority( GenericAce ace)
888 int result;
889 AceType type = ace.AceType;
891 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
894 // inherited aces are at the end as a group
897 result = 2 * ushort.MaxValue + ace._indexInAcl;
899 else if ( type == AceType.AccessDenied ||
900 type == AceType.AccessDeniedCallback )
902 result = 0;
904 else if ( type == AceType.AccessDeniedObject ||
905 type == AceType.AccessDeniedCallbackObject )
907 result = 1;
909 else if ( type == AceType.AccessAllowed ||
910 type == AceType.AccessAllowedCallback )
912 result = 2;
914 else if ( type == AceType.AccessAllowedObject ||
915 type == AceType.AccessAllowedCallbackObject )
917 result = 3;
919 else
922 // custom aces are at the second group
924 result = ushort.MaxValue + ace._indexInAcl;
927 return result;
931 // Compares two system ACEs and returns
932 // LessThan if ace1 < ace2
933 // EqualTo if ace1 == ace2
934 // GreaterThan if ace1 > ace2
936 // The order is:
937 // - explicit audit or alarm ACEs
938 // - explicit audit or alarm object ACEs
939 // - inherited ACEs (in the original order )
940 // - user-defined ACEs (in the original order )
943 private static int SaclAcePriority( GenericAce ace )
945 int result;
946 AceType type = ace.AceType;
948 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
950 result = 2 * ushort.MaxValue + ace._indexInAcl;
952 else if ( type == AceType.SystemAudit ||
953 type == AceType.SystemAlarm ||
954 type == AceType.SystemAuditCallback ||
955 type == AceType.SystemAlarmCallback )
957 result = 0;
959 else if ( type == AceType.SystemAuditObject ||
960 type == AceType.SystemAlarmObject ||
961 type == AceType.SystemAuditCallbackObject ||
962 type == AceType.SystemAlarmCallbackObject )
964 result = 1;
966 else
968 result = ushort.MaxValue + ace._indexInAcl;
971 return result;
974 private static ComparisonResult CompareAces( GenericAce ace1, GenericAce ace2, bool isDacl )
976 int ace1Priority = isDacl ? DaclAcePriority( ace1 ) : SaclAcePriority( ace1 );
977 int ace2Priority = isDacl ? DaclAcePriority( ace2 ) : SaclAcePriority( ace2 );
979 if ( ace1Priority < ace2Priority )
981 return ComparisonResult.LessThan;
983 else if ( ace1Priority > ace2Priority )
985 return ComparisonResult.GreaterThan;
987 else
989 KnownAce k_ace1 = ace1 as KnownAce;
990 KnownAce k_ace2 = ace2 as KnownAce;
992 if ( k_ace1 != null && k_ace2 != null )
994 int result = k_ace1.SecurityIdentifier.CompareTo( k_ace2.SecurityIdentifier );
996 if ( result < 0 )
998 return ComparisonResult.LessThan;
1000 else if ( result > 0 )
1002 return ComparisonResult.GreaterThan;
1006 return ComparisonResult.EqualTo;
1010 private void QuickSort( int left, int right, bool isDacl )
1012 GenericAce pivot;
1013 int leftHold, rightHold;
1014 int pivotIndex;
1016 if ( left >= right )
1018 return;
1021 leftHold = left;
1022 rightHold = right;
1024 pivot = _acl[left];
1025 pivotIndex = left;
1027 while ( left < right )
1029 // while (( _acl[right] >= pivot ) && ( left < right ))
1030 while (( ComparisonResult.LessThan != CompareAces( _acl[right], pivot, isDacl ) ) && ( left < right ))
1032 right--;
1035 if ( left != right )
1037 _acl[left] = _acl[right];
1038 left++;
1041 // while (( _acl[left] <= pivot ) && ( left < right ))
1042 while (( ComparisonResult.GreaterThan != CompareAces( _acl[left], pivot, isDacl ) ) && ( left < right ))
1044 left++;
1047 if ( left != right )
1049 _acl[right] = _acl[left];
1050 right--;
1054 _acl[left] = pivot;
1055 pivotIndex = left;
1056 left = leftHold;
1057 right = rightHold;
1059 if ( left < pivotIndex )
1061 QuickSort( left, pivotIndex - 1, isDacl );
1064 if ( right > pivotIndex )
1066 QuickSort( pivotIndex + 1, right, isDacl );
1071 // Inspects the ACE, modifies it by stripping away unnecessary or
1072 // meaningless flags.
1073 // Returns 'true' if the ACE should remain in the ACL, 'false' otherwise
1076 private bool InspectAce( ref GenericAce ace, bool isDacl )
1078 const AceFlags AuditFlags =
1079 AceFlags.SuccessfulAccess |
1080 AceFlags.FailedAccess;
1082 const AceFlags InheritFlags =
1083 AceFlags.ObjectInherit |
1084 AceFlags.ContainerInherit |
1085 AceFlags.NoPropagateInherit |
1086 AceFlags.InheritOnly;
1089 // Any ACE without at least one bit set in the access mask can be removed
1092 KnownAce knownAce = ace as KnownAce;
1094 if ( knownAce != null )
1096 if ( knownAce.AccessMask == 0 )
1098 return false;
1102 if ( !IsContainer )
1105 // On a leaf object ACL, inheritance bits are meaningless.
1106 // Specifically, an ACE marked "inherit-only" will never participate
1107 // in access control and can be removed.
1108 // Similarly, an ACE marked "container-inherit", "no-propagate-inherit"
1109 // or "object-inherit" can have those bits cleared since they carry
1110 // no meaning.
1113 if (( ace.AceFlags & AceFlags.InheritOnly ) != 0 )
1115 return false;
1118 if (( ace.AceFlags & InheritFlags ) != 0 )
1120 unchecked { ace.AceFlags &= ~InheritFlags; }
1123 else
1126 // Without either "container inherit" or "object inherit" to go with it,
1127 // the InheritOnly bit is meaningless and the entire ACE can be removed.
1130 if ((( ace.AceFlags & AceFlags.InheritOnly ) != 0 ) &&
1131 (( ace.AceFlags & AceFlags.ContainerInherit ) == 0 ) &&
1132 (( ace.AceFlags & AceFlags.ObjectInherit ) == 0 ))
1134 return false;
1138 // Without either "container inherit" or "object inherit" to go with it,
1139 // the NoPropagateInherit bit is meaningless and can be turned off.
1141 if ((( ace.AceFlags & AceFlags.NoPropagateInherit ) != 0 ) &&
1142 (( ace.AceFlags & AceFlags.ContainerInherit ) == 0 ) &&
1143 (( ace.AceFlags & AceFlags.ObjectInherit ) == 0 ))
1145 unchecked { ace.AceFlags &= ~AceFlags.NoPropagateInherit; }
1149 QualifiedAce qualifiedAce = knownAce as QualifiedAce;
1151 if ( isDacl )
1154 // There is no place for audit flags on a DACL
1157 unchecked { ace.AceFlags &= ~AuditFlags; }
1159 if ( qualifiedAce != null )
1162 // Qualified ACEs in a DACL must be allow or deny ACEs
1165 if ( qualifiedAce.AceQualifier != AceQualifier.AccessAllowed &&
1166 qualifiedAce.AceQualifier != AceQualifier.AccessDenied )
1168 return false;
1172 else
1175 // On a SACL, any ACE that does not specify Success or Failure
1176 // flags can be removed
1179 if (( ace.AceFlags & AuditFlags ) == 0 )
1181 return false;
1185 // Qualified ACEs in a SACL must be audit ACEs
1188 if ( qualifiedAce != null )
1190 if ( qualifiedAce.AceQualifier != AceQualifier.SystemAudit )
1192 return false;
1196 return true;
1200 // Strips meaningless flags from ACEs, removes meaningless ACEs
1203 private void RemoveMeaninglessAcesAndFlags( bool isDacl )
1206 // Be warned: do NOT use the Count property because it has
1207 // side-effect of calling canonicalization.
1210 for ( int i = _acl.Count - 1; i >= 0; i-- )
1212 GenericAce ace = _acl[i];
1214 if ( false == InspectAce( ref ace, isDacl ))
1216 _acl.RemoveAce( i );
1222 // Converts the ACL to its canonical form
1225 private void Canonicalize( bool compact, bool isDacl )
1228 // for quick sort to work, we must not allow the ace's indexes - which are constantly
1229 // changing during sorting - to influence our element's sorting order value. For
1230 // that purpose, we fix the ace's _indexInAcl here and use it for creating the
1231 // ace's sorting order value.
1234 for (ushort aclIndex = 0; aclIndex < _acl.Count; aclIndex++)
1236 _acl[aclIndex]._indexInAcl = aclIndex;
1239 QuickSort( 0, _acl.Count - 1, isDacl );
1241 if ( compact )
1243 for ( int i = 0; i < Count - 1; i++ )
1245 QualifiedAce thisAce = _acl[i] as QualifiedAce;
1247 if ( thisAce == null )
1249 continue;
1252 QualifiedAce nextAce = _acl[i + 1] as QualifiedAce;
1254 if ( nextAce == null )
1256 continue;
1259 if ( true == MergeAces( ref thisAce, nextAce ))
1261 _acl.RemoveAce(i + 1);
1268 // This method determines whether the object type and inherited object type from the original ace
1269 // should be retained or not based on access mask and aceflags for a given split
1271 private void GetObjectTypesForSplit( ObjectAce originalAce, int accessMask, AceFlags aceFlags, out ObjectAceFlags objectFlags, out Guid objectType, out Guid inheritedObjectType )
1274 objectFlags = 0;
1275 objectType = Guid.Empty;
1276 inheritedObjectType = Guid.Empty;
1279 // We should retain the object type if the access mask for this split contains any bits that refer to object type
1281 if (( accessMask & ObjectAce.AccessMaskWithObjectType ) != 0 )
1283 // keep the original ace's object flags and object type
1284 objectType = originalAce.ObjectAceType;
1285 objectFlags |= originalAce.ObjectAceFlags & ObjectAceFlags.ObjectAceTypePresent;
1289 // We should retain the inherited object type if the aceflags for this contains inheritance (ContainerInherit)
1291 if (( aceFlags & AceFlags.ContainerInherit ) != 0 )
1293 // keep the original ace's object flags and object type
1294 inheritedObjectType = originalAce.InheritedObjectAceType;
1295 objectFlags |= originalAce.ObjectAceFlags & ObjectAceFlags.InheritedObjectAceTypePresent;
1299 private bool ObjectTypesMatch( QualifiedAce ace, QualifiedAce newAce )
1301 Guid objectType = ( ace is ObjectAce ) ? (( ObjectAce ) ace ).ObjectAceType : Guid.Empty;
1302 Guid newObjectType = ( newAce is ObjectAce ) ? (( ObjectAce ) newAce ).ObjectAceType : Guid.Empty;
1304 return objectType.Equals( newObjectType );
1307 private bool InheritedObjectTypesMatch( QualifiedAce ace, QualifiedAce newAce )
1309 Guid inheritedObjectType = ( ace is ObjectAce ) ? (( ObjectAce ) ace ).InheritedObjectAceType : Guid.Empty;
1310 Guid newInheritedObjectType = ( newAce is ObjectAce ) ? (( ObjectAce ) newAce ).InheritedObjectAceType : Guid.Empty;
1312 return inheritedObjectType.Equals( newInheritedObjectType );
1315 private bool AccessMasksAreMergeable( QualifiedAce ace, QualifiedAce newAce )
1318 // The access masks are mergeable in any of the following conditions
1319 // 1. Object types match
1320 // 2. (Object types do not match) The existing ace does not have an object type and
1321 // already contains all the bits of the new ace which refer to the object type
1324 if ( ObjectTypesMatch( ace, newAce ))
1326 // case 1
1327 return true;
1330 ObjectAceFlags objectFlags = ( ace is ObjectAce ) ? (( ObjectAce ) ace ).ObjectAceFlags : ObjectAceFlags.None;
1331 if ((( ace.AccessMask & newAce.AccessMask & ObjectAce.AccessMaskWithObjectType ) == ( newAce.AccessMask & ObjectAce.AccessMaskWithObjectType )) &&
1332 (( objectFlags & ObjectAceFlags.ObjectAceTypePresent ) == 0 ))
1334 // case 2
1335 return true;
1338 return false;
1341 private bool AceFlagsAreMergeable( QualifiedAce ace, QualifiedAce newAce )
1344 // The ace flags can be considered for merge in any of the following conditions
1345 // 1. Inherited object types match
1346 // 2. (Inherited object types do not match) The existing ace does not have an inherited object type and
1347 // already contains all the bits of the new ace
1350 if ( InheritedObjectTypesMatch( ace, newAce ))
1352 // case 1
1353 return true;
1356 ObjectAceFlags objectFlags = ( ace is ObjectAce ) ? (( ObjectAce ) ace ).ObjectAceFlags : ObjectAceFlags.None;
1357 if (( objectFlags & ObjectAceFlags.InheritedObjectAceTypePresent ) == 0 )
1359 // case 2
1362 // This method is called only when the access masks of the two aces are already confirmed to be exact matches
1363 // therefore the second condition of case 2 is already verified
1365 Contract.Assert(( ace.AccessMask & newAce.AccessMask) == newAce.AccessMask, "AceFlagsAreMergeable:: AccessMask of existing ace does not contain all access bits of new ace.");
1366 return true;
1369 return false;
1372 private bool GetAccessMaskForRemoval( QualifiedAce ace, ObjectAceFlags objectFlags, Guid objectType, ref int accessMask )
1374 if (( ace.AccessMask & accessMask & ObjectAce.AccessMaskWithObjectType ) != 0 )
1378 // If the aces have access bits in common which refer to object types
1379 // then we follow these rules:
1381 // Remove No OT OT = A OT = B
1382 // Existing
1384 // No OT Remove Invalid Invalid
1386 // OT = A Remove Remove Nothing Common
1390 if ( ace is ObjectAce )
1392 bool commonAccessBitsWithObjectTypeExist = true;
1393 ObjectAce objectAce = ace as ObjectAce;
1396 // if what we are trying to remove has an object type
1397 // but the existing ace does not then this is an invalid case
1399 if ((( objectFlags & ObjectAceFlags.ObjectAceTypePresent ) != 0 ) &&
1400 (( objectAce.ObjectAceFlags & ObjectAceFlags.ObjectAceTypePresent ) == 0 ))
1402 return false;
1406 // if what we are trying to remove has no object type or
1407 // if object types match (since at this point we have ensured that both have object types present)
1408 // then we have common access bits with object type
1410 commonAccessBitsWithObjectTypeExist = (( objectFlags & ObjectAceFlags.ObjectAceTypePresent ) == 0 ) ||
1411 objectAce.ObjectTypesMatch( objectFlags, objectType );
1412 if ( !commonAccessBitsWithObjectTypeExist )
1414 accessMask &= ~ObjectAce.AccessMaskWithObjectType;
1417 else if (( objectFlags & ObjectAceFlags.ObjectAceTypePresent ) != 0 )
1419 // the existing ace is a common ace and the one we're removing
1420 // refers to a specific object type so this is invalid
1421 return false;
1425 return true;
1429 private bool GetInheritanceFlagsForRemoval( QualifiedAce ace, ObjectAceFlags objectFlags, Guid inheritedObjectType, ref AceFlags aceFlags )
1431 if ((( ace.AceFlags & AceFlags.ContainerInherit ) != 0 ) && (( aceFlags & AceFlags.ContainerInherit ) != 0 ))
1435 // If the aces have inheritance bits in common
1436 // then we follow these rules:
1438 // Remove No IOT IOT = A IOT = B
1439 // Existing
1441 // No IOT Remove Invalid Invalid
1443 // IOT = A Remove Remove Nothing Common
1447 if ( ace is ObjectAce )
1449 bool commonInheritanceFlagsExist = true;
1450 ObjectAce objectAce = ace as ObjectAce;
1453 // if what we are trying to remove has an inherited object type
1454 // but the existing ace does not then this is an invalid case
1456 if ((( objectFlags & ObjectAceFlags.InheritedObjectAceTypePresent ) != 0 ) &&
1457 (( objectAce.ObjectAceFlags & ObjectAceFlags.InheritedObjectAceTypePresent ) == 0 ))
1459 return false;
1463 // if what we are trying to remove has no inherited object type or
1464 // if inherited object types match then we have common inheritance flags
1466 commonInheritanceFlagsExist = (( objectFlags & ObjectAceFlags.InheritedObjectAceTypePresent ) == 0 ) ||
1467 objectAce.InheritedObjectTypesMatch( objectFlags, inheritedObjectType );
1468 if ( !commonInheritanceFlagsExist )
1470 aceFlags &= ~AceFlags.InheritanceFlags;
1473 else if (( objectFlags & ObjectAceFlags.InheritedObjectAceTypePresent ) != 0 )
1475 // the existing ace is a common ace and the one we're removing
1476 // refers to a specific child type so this is invalid
1477 return false;
1481 return true;
1485 static private bool AceOpaquesMatch( QualifiedAce ace, QualifiedAce newAce )
1487 byte[] aceOpaque = ace.GetOpaque();
1488 byte[] newAceOpaque = newAce.GetOpaque();
1490 if ( aceOpaque == null || newAceOpaque == null )
1492 return aceOpaque == newAceOpaque;
1495 if ( aceOpaque.Length != newAceOpaque.Length )
1497 return false;
1500 for ( int i = 0; i < aceOpaque.Length; ++i )
1502 if ( aceOpaque[i] != newAceOpaque[i] )
1504 return false;
1508 return true;
1511 static private bool AcesAreMergeable( QualifiedAce ace, QualifiedAce newAce )
1514 // Only interested in ACEs with the specified type
1517 if ( ace.AceType != newAce.AceType )
1519 return false;
1523 // Only interested in explicit (non-inherited) ACEs
1526 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
1528 return false;
1531 if (( newAce.AceFlags & AceFlags.Inherited ) != 0 )
1533 return false;
1537 // Only interested in ACEs with the specified qualifier
1540 if ( ace.AceQualifier != newAce.AceQualifier )
1542 return false;
1546 // Only interested in ACEs with the specified SID
1549 if ( ace.SecurityIdentifier != newAce.SecurityIdentifier )
1551 return false;
1555 // Only interested in ACEs with the specified callback data
1558 if ( !AceOpaquesMatch( ace, newAce ))
1560 return false;
1563 return true;
1567 // Merge routine for qualified ACEs
1570 private bool MergeAces( ref QualifiedAce ace, QualifiedAce newAce )
1573 // Check whether the ACEs are potentially mergeable
1576 if ( !AcesAreMergeable( ace, newAce ))
1578 return false;
1582 // The modification algorithm proceeds in stages
1584 // Stage 1: if flags match, add to the access mask
1587 if ( ace.AceFlags == newAce.AceFlags )
1589 if ( ace is ObjectAce || newAce is ObjectAce )
1591 // for object aces we need to match the inherited object types (for ace flags equality)
1592 if ( InheritedObjectTypesMatch( ace, newAce ))
1594 // also since access mask bits are further qualified by object type, they cannot always be added on
1595 if ( AccessMasksAreMergeable( ace, newAce ))
1597 ace.AccessMask |= newAce.AccessMask;
1598 return true;
1602 else
1604 ace.AccessMask |= newAce.AccessMask;
1605 return true;
1612 // Stage 2: Audit flags can be combined if the rest of the
1613 // flags (both access mask and inheritance) match
1616 if ((( ace.AceFlags & AceFlags.InheritanceFlags ) == ( newAce.AceFlags & AceFlags.InheritanceFlags )) &&
1617 ( ace.AccessMask == newAce.AccessMask ))
1619 if (( ace is ObjectAce ) || ( newAce is ObjectAce ))
1621 // for object aces we need to match the inherited object types (for inheritance flags equality) and object type (for access mask equality) as well
1622 if ( InheritedObjectTypesMatch( ace, newAce ) &&
1623 ( ObjectTypesMatch( ace, newAce )))
1625 ace.AceFlags |= ( newAce.AceFlags & AceFlags.AuditFlags );
1626 return true;
1629 else
1631 ace.AceFlags |= ( newAce.AceFlags & AceFlags.AuditFlags );
1632 return true;
1638 // Stage 3: Inheritance flags can be combined in some cases
1639 // provided access mask and audit bits are the same
1642 if ((( ace.AceFlags & AceFlags.AuditFlags ) == ( newAce.AceFlags & AceFlags.AuditFlags )) &&
1643 ( ace.AccessMask == newAce.AccessMask ))
1645 AceFlags merged;
1648 // See whether the inheritance bits can be merged
1651 if (( ace is ObjectAce ) || ( newAce is ObjectAce ))
1653 // object types need to match (for access mask equality) and inheritance flags need additional DS specific logic
1654 // to check whether they can be merged
1655 if (( ObjectTypesMatch( ace, newAce )) &&
1656 ( AceFlagsAreMergeable( ace, newAce )))
1658 if ( true == MergeInheritanceBits( ace.AceFlags, newAce.AceFlags, IsDS, out merged ))
1660 ace.AceFlags = ( merged | ( ace.AceFlags & AceFlags.AuditFlags ));
1661 return true;
1665 else
1667 if ( true == MergeInheritanceBits( ace.AceFlags, newAce.AceFlags, IsDS, out merged ))
1669 ace.AceFlags = ( merged | ( ace.AceFlags & AceFlags.AuditFlags ));
1670 return true;
1676 return false;
1680 // Returns 'true' if the ACL is in canonical order; 'false' otherwise
1683 private bool CanonicalCheck( bool isDacl )
1685 if ( isDacl )
1688 // DACL canonical order:
1689 // Explicit Deny - Explicit Allow - Inherited
1692 const int AccessDenied = 0;
1693 const int AccessAllowed = 1;
1694 const int Inherited = 2;
1695 const int Unknown = 3;
1697 int currentStage = AccessDenied;
1700 // In this loop, do NOT use 'Count' as upper bound of the loop,
1701 // since doing so will canonicalize the ACL invalidating the result
1702 // of this check!
1705 for ( int i = 0; i < _acl.Count; i++ )
1707 int aceStage = Unknown;
1709 GenericAce ace = _acl[i];
1711 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
1713 aceStage = Inherited;
1715 else
1717 QualifiedAce qualifiedAce = ace as QualifiedAce;
1719 if ( qualifiedAce == null )
1722 // Explicit ACE is not recognized - this is not a canonical ACL
1725 return false;
1728 if ( qualifiedAce.AceQualifier == AceQualifier.AccessAllowed )
1730 aceStage = AccessAllowed;
1732 else if ( qualifiedAce.AceQualifier == AceQualifier.AccessDenied )
1734 aceStage = AccessDenied;
1736 else
1739 // Only allow and deny ACEs are allowed here
1742 Contract.Assert( false, "Audit and alarm ACEs must have been stripped by remove-meaningless logic" );
1743 return false;
1747 if ( aceStage == Unknown )
1749 continue;
1752 if ( aceStage > currentStage )
1754 currentStage = aceStage;
1756 else if ( aceStage < currentStage )
1758 return false;
1762 else
1765 // SACL canonical order:
1766 // Explicit - Inherited
1769 const int Explicit = 0;
1770 const int Inherited = 1;
1771 const int Unknown = 2;
1773 int currentStage = Explicit;
1776 // In this loop, do NOT use 'Count' as upper bound of the loop,
1777 // since doing so will canonicalize the ACL invalidating the result
1778 // of this check!
1781 for ( int i = 0; i < _acl.Count; i++ )
1783 int aceStage = Unknown;
1785 GenericAce ace = _acl[i];
1787 if ( ace == null )
1790 // <Microsoft-9/19/2004> Afraid to yank this statement now
1791 // for fear of destabilization, so adding an assert instead
1794 Contract.Assert( ace != null, "How did a null ACE end up in a SACL?" );
1795 continue;
1798 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
1800 aceStage = Inherited;
1802 else
1804 QualifiedAce qualifiedAce = ace as QualifiedAce;
1806 if ( qualifiedAce == null )
1809 // Explicit ACE is not recognized - this is not a canonical ACL
1812 return false;
1815 if ( qualifiedAce.AceQualifier == AceQualifier.SystemAudit ||
1816 qualifiedAce.AceQualifier == AceQualifier.SystemAlarm )
1818 aceStage = Explicit;
1820 else
1823 // Only audit and alarm ACEs are allowed here
1826 Contract.Assert( false, "Allow and deny ACEs must have been stripped by remove-meaningless logic" );
1827 return false;
1831 if ( aceStage > currentStage )
1833 currentStage = aceStage;
1835 else if ( aceStage < currentStage )
1837 return false;
1842 return true;
1845 [Pure]
1846 private void ThrowIfNotCanonical()
1848 if ( !_isCanonical )
1850 throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_ModificationOfNonCanonicalAcl" ));
1854 #endregion
1856 #region Constructors
1859 // Creates an empty ACL
1862 internal CommonAcl( bool isContainer, bool isDS, byte revision, int capacity )
1863 : base()
1865 _isContainer = isContainer;
1866 _isDS = isDS;
1867 _acl = new RawAcl( revision, capacity );
1868 _isCanonical = true; // since it is empty
1872 // Creates an ACL from a raw ACL
1873 // - 'trusted' (internal) callers get to pass the raw ACL
1874 // that this object will take ownership of
1875 // - 'untrusted' callers are handled by creating a local
1876 // copy of the ACL passed in
1879 internal CommonAcl( bool isContainer, bool isDS, RawAcl rawAcl, bool trusted, bool isDacl )
1880 : base()
1882 if ( rawAcl == null )
1884 throw new ArgumentNullException( "rawAcl" );
1886 Contract.EndContractBlock();
1888 _isContainer = isContainer;
1889 _isDS = isDS;
1891 if (trusted)
1894 // In the trusted case, we take over ownership of the ACL passed in
1897 _acl = rawAcl;
1899 RemoveMeaninglessAcesAndFlags( isDacl );
1901 else
1904 // In the untrusted case, we create our own raw ACL to keep the ACEs in
1907 _acl = new RawAcl( rawAcl.Revision, rawAcl.Count );
1909 for ( int i = 0; i < rawAcl.Count; i++ )
1912 // Clone each ACE prior to putting it in
1915 GenericAce ace = rawAcl[i].Copy();
1918 // Avoid inserting meaningless ACEs
1921 if ( true == InspectAce( ref ace, isDacl ))
1923 _acl.InsertAce( _acl.Count, ace );
1929 // See whether the ACL is canonical to begin with
1932 if ( true == CanonicalCheck( isDacl ))
1935 // Sort and compact the array
1938 Canonicalize( true, isDacl );
1940 _isCanonical = true;
1942 else
1944 _isCanonical = false;
1948 #endregion
1950 #region Internal Properties
1952 internal RawAcl RawAcl
1954 get { return _acl; }
1957 #endregion
1959 #region Protected Methods
1961 internal void CheckAccessType( AccessControlType accessType )
1963 if ( accessType != AccessControlType.Allow &&
1964 accessType != AccessControlType.Deny )
1966 throw new ArgumentOutOfRangeException(
1967 "accessType",
1968 Environment.GetResourceString( "ArgumentOutOfRange_Enum" ));
1972 internal void CheckFlags( InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
1974 if ( IsContainer )
1977 // Supplying propagation flags without inheritance flags is illegal
1980 if ( inheritanceFlags == InheritanceFlags.None &&
1981 propagationFlags != PropagationFlags.None )
1983 throw new ArgumentException(
1984 Environment.GetResourceString( "Argument_InvalidAnyFlag" ),
1985 "propagationFlags" );
1988 else if ( inheritanceFlags != InheritanceFlags.None )
1990 throw new ArgumentException(
1991 Environment.GetResourceString("Argument_InvalidAnyFlag"),
1992 "inheritanceFlags" );
1994 else if ( propagationFlags != PropagationFlags.None )
1996 throw new ArgumentException(
1997 Environment.GetResourceString( "Argument_InvalidAnyFlag" ),
1998 "propagationFlags" );
2001 return;
2005 // Helper function behind all the AddXXX methods for qualified aces
2008 internal void AddQualifiedAce( SecurityIdentifier sid, AceQualifier qualifier, int accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
2010 if ( sid == null )
2012 throw new ArgumentNullException( "sid" );
2014 Contract.EndContractBlock();
2016 ThrowIfNotCanonical();
2018 bool aceMerged = false; // if still false after all attempts to merge, create new entry
2020 if ( qualifier == AceQualifier.SystemAudit &&
2021 (( flags & AceFlags.AuditFlags ) == 0 ))
2023 throw new ArgumentException(
2024 Environment.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
2025 "flags" );
2028 if ( accessMask == 0 )
2030 throw new ArgumentException(
2031 Environment.GetResourceString( "Argument_ArgumentZero" ),
2032 "accessMask" );
2035 GenericAce newAce;
2037 if (( !IsDS ) || ( objectFlags == ObjectAceFlags.None ))
2039 newAce = new CommonAce( flags, qualifier, accessMask, sid, false, null );
2041 else
2043 newAce = new ObjectAce( flags, qualifier, accessMask, sid, objectFlags, objectType, inheritedObjectType, false, null );
2047 // Make sure the new ACE wouldn't be meaningless before proceeding
2050 if ( false == InspectAce( ref newAce, ( this is DiscretionaryAcl )))
2052 return;
2056 // See if the new ACE can be merged with any of the existing ones
2059 for ( int i = 0; i < Count; i++ )
2061 QualifiedAce ace = _acl[i] as QualifiedAce;
2063 if ( ace == null )
2065 continue;
2068 if ( true == MergeAces( ref ace, newAce as QualifiedAce ))
2070 aceMerged = true;
2071 break;
2076 // Couldn't modify any existing entry, so add a new one
2079 if ( !aceMerged )
2081 _acl.InsertAce( _acl.Count, newAce );
2083 _isDirty = true;
2085 OnAclModificationTried();
2089 // Helper function behind all the SetXXX methods
2092 internal void SetQualifiedAce( SecurityIdentifier sid, AceQualifier qualifier, int accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
2094 if ( sid == null )
2096 throw new ArgumentNullException( "sid" );
2099 if ( qualifier == AceQualifier.SystemAudit &&
2100 (( flags & AceFlags.AuditFlags ) == 0 ))
2102 throw new ArgumentException(
2103 Environment.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
2104 "flags" );
2107 if ( accessMask == 0 )
2109 throw new ArgumentException(
2110 Environment.GetResourceString( "Argument_ArgumentZero" ),
2111 "accessMask" );
2113 Contract.EndContractBlock();
2114 ThrowIfNotCanonical();
2116 GenericAce newAce;
2118 if (( !IsDS ) || ( objectFlags == ObjectAceFlags.None ))
2120 newAce = new CommonAce( flags, qualifier, accessMask, sid, false, null );
2122 else
2124 newAce = new ObjectAce( flags, qualifier, accessMask, sid, objectFlags, objectType, inheritedObjectType, false, null );
2128 // Make sure the new ACE wouldn't be meaningless before proceeding
2131 if ( false == InspectAce( ref newAce, ( this is DiscretionaryAcl )))
2133 return;
2136 for ( int i = 0; i < Count; i++ )
2138 QualifiedAce ace = _acl[i] as QualifiedAce;
2141 // Not a qualified ACE - keep going
2144 if ( ace == null )
2146 continue;
2150 // Only interested in explicit (non-inherited) ACEs
2153 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
2155 continue;
2159 // Only interested in ACEs with the specified qualifier
2162 if ( ace.AceQualifier != qualifier )
2164 continue;
2168 // Only interested in ACEs with the specified SID
2171 if ( ace.SecurityIdentifier != sid )
2173 continue;
2177 // This ACE corresponds to the SID and qualifier in question - remove it
2180 _acl.RemoveAce( i );
2181 i--;
2185 // As a final step, add the ACE we want.
2186 // Add it at the end - we'll re-canonicalize later.
2189 _acl.InsertAce( _acl.Count, newAce );
2192 // To aid the efficiency of batch operations, recanonicalize this later
2195 _isDirty = true;
2196 OnAclModificationTried();
2200 // Helper function behind all the RemoveXXX methods
2203 internal bool RemoveQualifiedAces( SecurityIdentifier sid, AceQualifier qualifier, int accessMask, AceFlags flags, bool saclSemantics, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
2205 if ( accessMask == 0 )
2207 throw new ArgumentException(
2208 Environment.GetResourceString( "Argument_ArgumentZero" ),
2209 "accessMask" );
2212 if ( qualifier == AceQualifier.SystemAudit &&
2213 (( flags & AceFlags.AuditFlags ) == 0 ))
2215 throw new ArgumentException(
2216 Environment.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
2217 "flags" );
2220 if ( sid == null )
2222 throw new ArgumentNullException( "sid" );
2224 Contract.EndContractBlock();
2225 ThrowIfNotCanonical();
2229 // Two passes are made.
2230 // During the first pass, no changes are made to the ACL,
2231 // the ACEs are simply evaluated to ascertain that the operation
2232 // can succeed.
2233 // If everything is kosher, the second pass is the one that makes changes.
2236 bool evaluationPass = true;
2237 bool removePossible = true; // unless proven otherwise
2240 // Needed for DS acls to keep track of the original access mask specified for removal
2242 int originalAccessMask = accessMask;
2243 AceFlags originalFlags = flags;
2246 // It is possible that the removal will result in an overflow exception
2247 // because more ACEs get inserted.
2248 // Save the current state of the object and revert to it later if
2249 // and exception is thrown.
2252 byte[] recovery = new byte[BinaryLength];
2253 GetBinaryForm( recovery, 0 );
2255 MakeAnotherPass:
2259 for ( int i = 0; i < Count; i++ )
2261 QualifiedAce ace = _acl[i] as QualifiedAce;
2264 // Not a qualified ACE - keep going
2267 if ( ace == null )
2269 continue;
2273 // Only interested in explicit (non-inherited) ACEs
2276 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
2278 continue;
2282 // Only interested in ACEs with the specified qualifier
2285 if ( ace.AceQualifier != qualifier )
2287 continue;
2291 // Only interested in ACEs with the specified SID
2294 if ( ace.SecurityIdentifier != sid )
2296 continue;
2300 // If access masks have nothing in common, skip the whole exercise
2303 if ( IsDS )
2306 // incase of directory aces, if the access mask of the
2307 // existing and new ace have any bits in common that need
2308 // an object type, then we need to perform some checks on the
2309 // object types in the two aces. Since certain bits are further qualified
2310 // by the object type they cannot be determined to be common without
2311 // inspecting the object type. It is possible that the same bits may be set but
2312 // the object types are different in which case they are really not common bits.
2314 accessMask = originalAccessMask;
2315 bool objectTypesConflict = !GetAccessMaskForRemoval( ace, objectFlags, objectType, ref accessMask );
2317 // if the access masks have nothing in common, skip
2318 if (( ace.AccessMask & accessMask ) == 0 )
2320 continue;
2324 // incase of directory aces, if the existing and new ace are being inherited,
2325 // then we need to perform some checks on the
2326 // inherited object types in the two aces. Since inheritance is further qualified
2327 // by the inherited object type the inheritance flags cannot be determined to be common without
2328 // inspecting the inherited object type. It is possible that both aces may be further inherited but if
2329 // the inherited object types are different the inheritance may not be common.
2331 flags = originalFlags;
2332 bool inheritedObjectTypesConflict = !GetInheritanceFlagsForRemoval( ace, objectFlags, inheritedObjectType, ref flags );
2334 if (((( ace.AceFlags & AceFlags.ContainerInherit ) == 0 ) && (( flags & AceFlags.ContainerInherit ) != 0 ) && (( flags & AceFlags.InheritOnly ) != 0 )) ||
2335 ((( flags & AceFlags.ContainerInherit ) == 0 ) && (( ace.AceFlags & AceFlags.ContainerInherit ) != 0 ) && (( ace.AceFlags & AceFlags.InheritOnly ) != 0)))
2337 // if one ace applies only to self and the other only to children/descendents we have nothing in common
2338 continue;
2342 // if the ace being removed referred only to child types and child types among existing ace and
2343 // ace being removed are not common then there is nothing in common between these aces (skip)
2345 if ((( originalFlags & AceFlags.ContainerInherit ) != 0 ) && (( originalFlags & AceFlags.InheritOnly ) != 0 ) && (( flags & AceFlags.ContainerInherit ) == 0 ))
2347 continue;
2350 if ( objectTypesConflict || inheritedObjectTypesConflict )
2353 // if we reached this stage, then we've found something common between the two aces.
2354 // But since there is a conflict between the object types (or inherited object types), the remove is not possible
2356 removePossible = false;
2357 break;
2360 else
2362 if (( ace.AccessMask & accessMask ) == 0 )
2364 continue;
2369 // If audit flags on a SACL have nothing in common,
2370 // skip the whole exercise
2373 if ( saclSemantics &&
2374 (( ace.AceFlags & flags & AceFlags.AuditFlags ) == 0 ))
2376 continue;
2380 // See if the ACE needs to be split into several
2381 // To illustrate with an example, consider this equation:
2382 // From: CI OI NP SA FA R W
2383 // Remove: OI IO NP SA R
2385 // PermissionSplit: CI OI NP SA FA W // remove R
2386 // AuditingSplit: CI OI NP FA R // remove SA
2387 // MergeSplit: CI OI NP SA R // ready for merge
2388 // Remove: OI IO NP SA R // same audit and perm flags as merge split
2390 // Result: CI OI NP SA FA W // PermissionSplit
2391 // CI OI NP FA R // AuditingSplit
2392 // CI NP SA R // Result of perm removal
2396 // Example for DS acls (when removal is possible)
2398 // From: CI(Guid) LC CC(Guid)
2399 // Remove: CI IO LC
2401 // PermissionSplit: CI(Guid) CC(Guid) // Remove GR
2402 // MergeSplit: CI(Guid) LC // Ready for merge
2403 // Remove: CI IO LC // Removal is possible since we are trying to remove inheritance for
2404 // all child types when it exists for one specific child type
2406 // Result: CI(Guid) CC(Guid) // PermissionSplit
2407 // LC // Result of perm removal
2410 // Example for DS acls (when removal is NOT possible)
2412 // From: CI GR CC(Guid)
2413 // Remove: CI(Guid) IO LC
2415 // PermissionSplit: CI CC(Guid) // Remove GR
2416 // MergeSplit: CI LC // Ready for merge
2417 // Remove: CI(Guid) IO CC // Removal is not possible since we are trying to remove
2418 // inheritance for a specific child type when it exists for all child types
2421 // Permission split settings
2422 AceFlags ps_AceFlags = 0;
2423 int ps_AccessMask = 0;
2424 ObjectAceFlags ps_ObjectAceFlags = 0;
2425 Guid ps_ObjectAceType = Guid.Empty;
2426 Guid ps_InheritedObjectAceType = Guid.Empty;
2428 // Auditing split makes sense if this is a SACL
2429 AceFlags as_AceFlags = 0;
2430 int as_AccessMask = 0;
2431 ObjectAceFlags as_ObjectAceFlags = 0;
2432 Guid as_ObjectAceType = Guid.Empty;
2433 Guid as_InheritedObjectAceType = Guid.Empty;
2435 // Merge split settings
2436 AceFlags ms_AceFlags = 0;
2437 int ms_AccessMask = 0;
2438 ObjectAceFlags ms_ObjectAceFlags = 0;
2439 Guid ms_ObjectAceType = Guid.Empty;
2440 Guid ms_InheritedObjectAceType = Guid.Empty;
2442 // Merge result settings
2443 AceFlags mergeResultFlags = 0;
2444 bool mergeRemoveTotal = false;
2447 // First compute the permission split
2450 ps_AceFlags = ace.AceFlags;
2451 unchecked { ps_AccessMask = ace.AccessMask & ~accessMask; }
2453 if ( ace is ObjectAce )
2456 // determine what should be the object/inherited object types on the permission split
2458 GetObjectTypesForSplit( ace as ObjectAce, ps_AccessMask /* access mask for this split */, ps_AceFlags /* flags remain the same */, out ps_ObjectAceFlags, out ps_ObjectAceType, out ps_InheritedObjectAceType );
2462 // Next, for SACLs only, compute the auditing split
2465 if ( saclSemantics )
2468 // This operation can set the audit bits region
2469 // of ACE flags to zero;
2470 // This case will be handled later
2473 unchecked { as_AceFlags = ace.AceFlags & ~( flags & AceFlags.AuditFlags ); }
2476 // The result of this evaluation is guaranteed
2477 // not to be zero by now
2480 as_AccessMask = ( ace.AccessMask & accessMask );
2482 if ( ace is ObjectAce )
2485 // determine what should be the object/inherited object types on the audit split
2487 GetObjectTypesForSplit( ace as ObjectAce, as_AccessMask /* access mask for this split */, as_AceFlags /* flags remain the same for inheritance */, out as_ObjectAceFlags, out as_ObjectAceType, out as_InheritedObjectAceType );
2492 // Finally, compute the merge split
2495 ms_AceFlags = ( ace.AceFlags & AceFlags.InheritanceFlags ) | ( flags & ace.AceFlags & AceFlags.AuditFlags );
2496 ms_AccessMask = ( ace.AccessMask & accessMask );
2500 // Now is the time to obtain the result of applying the remove
2501 // operation to the merge split
2502 // Skipping this step for SACLs where the merge split step
2503 // produced no auditing flags
2506 if ( !saclSemantics ||
2507 (( ms_AceFlags & AceFlags.AuditFlags ) != 0 ))
2509 if ( false == RemoveInheritanceBits( ms_AceFlags, flags, IsDS, out mergeResultFlags, out mergeRemoveTotal ))
2511 removePossible = false;
2512 break;
2515 if ( !mergeRemoveTotal )
2517 mergeResultFlags |= ( ms_AceFlags & AceFlags.AuditFlags );
2519 if ( ace is ObjectAce )
2522 // determine what should be the object/inherited object types on the merge split
2524 GetObjectTypesForSplit( ace as ObjectAce, ms_AccessMask /* access mask for this split */, mergeResultFlags /* flags for this split */, out ms_ObjectAceFlags, out ms_ObjectAceType, out ms_InheritedObjectAceType );
2530 // If this is no longer an evaluation, go ahead and make the changes
2533 if ( !evaluationPass )
2535 QualifiedAce newAce;
2538 // Modify the existing ACE in-place if it has any access
2539 // mask bits left, otherwise simply remove it
2540 // However, if for an object ace we are removing the object type
2541 // then we should really remove this ace and add a new one since
2542 // we would be changing the size of this ace
2545 if ( ps_AccessMask != 0 )
2547 if (( ace is ObjectAce ) &&
2548 (((( ObjectAce) ace ).ObjectAceFlags & ObjectAceFlags.ObjectAceTypePresent ) != 0 ) &&
2549 (( ps_ObjectAceFlags & ObjectAceFlags.ObjectAceTypePresent ) == 0 ))
2551 ObjectAce newObjectAce;
2553 _acl.RemoveAce(i);
2554 newObjectAce = new ObjectAce( ps_AceFlags, qualifier, ps_AccessMask, ace.SecurityIdentifier, ps_ObjectAceFlags, ps_ObjectAceType, ps_InheritedObjectAceType, false, null );
2555 _acl.InsertAce( i, newObjectAce );
2557 else
2559 ace.AceFlags = ps_AceFlags;
2560 ace.AccessMask = ps_AccessMask;
2562 if ( ace is ObjectAce )
2564 ObjectAce objectAce = ace as ObjectAce;
2566 objectAce.ObjectAceFlags = ps_ObjectAceFlags;
2567 objectAce.ObjectAceType = ps_ObjectAceType;
2568 objectAce.InheritedObjectAceType = ps_InheritedObjectAceType;
2572 else
2574 _acl.RemoveAce(i);
2575 i--; // keep the array index honest
2579 // On a SACL, the result of the auditing split must be recorded
2582 if ( saclSemantics && (( as_AceFlags & AceFlags.AuditFlags ) != 0 ))
2584 if ( ace is CommonAce )
2586 newAce = new CommonAce( as_AceFlags, qualifier, as_AccessMask, ace.SecurityIdentifier, false, null );
2588 else
2590 // object ace
2591 newAce = new ObjectAce( as_AceFlags, qualifier, as_AccessMask, ace.SecurityIdentifier, as_ObjectAceFlags, as_ObjectAceType, as_InheritedObjectAceType, false, null );
2594 i++; // so it's not considered again
2595 _acl.InsertAce( i, newAce );
2599 // If there are interesting bits left over from a remove, store them
2600 // as a separate ACE
2603 if ( !mergeRemoveTotal )
2605 if ( ace is CommonAce )
2607 newAce = new CommonAce( mergeResultFlags, qualifier, ms_AccessMask, ace.SecurityIdentifier, false, null );
2609 else
2611 // object ace
2612 newAce = new ObjectAce( mergeResultFlags, qualifier, ms_AccessMask, ace.SecurityIdentifier, ms_ObjectAceFlags, ms_ObjectAceType, ms_InheritedObjectAceType, false, null );
2615 i++; // so it's not considered again
2616 _acl.InsertAce( i, newAce );
2621 catch( OverflowException )
2624 // Oops, overflow means that the ACL became too big.
2625 // Inform the caller that the remove was not possible.
2628 _acl.SetBinaryForm( recovery, 0 );
2629 return false;
2633 // Finished evaluating the possibility of a remove.
2634 // If it looks like it's doable, go ahead and do it.
2637 if ( evaluationPass && removePossible )
2639 evaluationPass = false;
2640 goto MakeAnotherPass;
2643 OnAclModificationTried();
2645 return removePossible;
2648 internal void RemoveQualifiedAcesSpecific( SecurityIdentifier sid, AceQualifier qualifier, int accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
2650 if ( accessMask == 0 )
2652 throw new ArgumentException(
2653 Environment.GetResourceString( "Argument_ArgumentZero" ),
2654 "accessMask" );
2657 if ( qualifier == AceQualifier.SystemAudit &&
2658 (( flags & AceFlags.AuditFlags ) == 0 ))
2660 throw new ArgumentException(
2661 Environment.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
2662 "flags" );
2665 if ( sid == null )
2667 throw new ArgumentNullException( "sid" );
2669 Contract.EndContractBlock();
2670 ThrowIfNotCanonical();
2672 for ( int i = 0; i < Count; i++ )
2674 QualifiedAce ace = _acl[i] as QualifiedAce;
2677 // Not a qualified ACE - keep going
2680 if ( ace == null )
2682 continue;
2686 // Only interested in explicit (non-inherited) ACEs
2689 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
2691 continue;
2695 // Only interested in ACEs with the specified qualifier
2698 if ( ace.AceQualifier != qualifier )
2700 continue;
2704 // Only interested in ACEs with the specified SID
2707 if ( ace.SecurityIdentifier != sid )
2709 continue;
2713 // Only interested in exact ACE flag matches
2716 if ( ace.AceFlags != flags )
2718 continue;
2722 // Only interested in exact access mask matches
2725 if ( ace.AccessMask != accessMask )
2727 continue;
2730 if ( IsDS )
2733 // Incase of object aces, only interested in ACEs which match in their
2734 // objectType and inheritedObjectType
2737 if (( ace is ObjectAce ) && ( objectFlags != ObjectAceFlags.None ))
2740 // both are object aces, so must match in object type and inherited object type
2742 ObjectAce objectAce = ace as ObjectAce;
2744 if (( !objectAce.ObjectTypesMatch( objectFlags, objectType ))
2745 || ( !objectAce.InheritedObjectTypesMatch( objectFlags, inheritedObjectType )))
2747 continue;
2750 else if (( ace is ObjectAce ) || ( objectFlags != ObjectAceFlags.None ))
2752 // one is object ace and the other is not, so no match
2753 continue;
2758 // Got our exact match; now remove it
2761 _acl.RemoveAce(i);
2762 i--; // keep the array index honest
2764 OnAclModificationTried();
2767 internal virtual void OnAclModificationTried()
2770 #endregion
2772 #region Public Properties
2775 // Returns the revision of the ACL
2778 public sealed override byte Revision
2780 get { return _acl.Revision; }
2784 // Returns the number of ACEs in the ACL
2787 public sealed override int Count
2791 CanonicalizeIfNecessary();
2792 return _acl.Count;
2797 // Returns the length of the binary representation of the ACL
2800 public sealed override int BinaryLength
2804 CanonicalizeIfNecessary();
2805 return _acl.BinaryLength;
2810 // Returns 'true' if the ACL was canonical at creation time
2813 public bool IsCanonical
2815 get { return _isCanonical; }
2818 public bool IsContainer
2820 get { return _isContainer; }
2823 public bool IsDS
2825 get { return _isDS; }
2828 #endregion
2830 #region Public Methods
2833 // Returns the binary representation of the ACL
2836 public sealed override void GetBinaryForm( byte[] binaryForm, int offset )
2838 CanonicalizeIfNecessary();
2839 _acl.GetBinaryForm( binaryForm, offset );
2843 // Retrieves the ACE at a given index inside the ACL
2844 // Since the caller can modify the ACE it receives,
2845 // clone the ACE prior to returning it to the caller
2848 public sealed override GenericAce this[int index]
2852 CanonicalizeIfNecessary();
2853 return _acl[index].Copy();
2858 throw new NotSupportedException( Environment.GetResourceString( "NotSupported_SetMethod" ));
2862 public void RemoveInheritedAces()
2864 ThrowIfNotCanonical();
2867 // Iterating backwards as an optimization - all inherited ACEs
2868 // are usually in the back of the ACL
2871 for ( int i = _acl.Count - 1; i >= 0; i-- )
2873 GenericAce ace = _acl[i];
2875 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
2877 _acl.RemoveAce( i );
2880 OnAclModificationTried();
2883 public void Purge( SecurityIdentifier sid )
2885 if ( sid == null )
2887 throw new ArgumentNullException( "sid" );
2889 Contract.EndContractBlock();
2890 ThrowIfNotCanonical();
2892 for ( int i = Count - 1; i >= 0; i-- )
2894 KnownAce ace = _acl[i] as KnownAce;
2897 // Skip over unknown ACEs
2900 if ( ace == null )
2902 continue;
2906 // Skip over inherited ACEs
2909 if (( ace.AceFlags & AceFlags.Inherited ) != 0 )
2911 continue;
2915 // SID matches - ACE is out
2918 if ( ace.SecurityIdentifier == sid )
2920 _acl.RemoveAce( i );
2923 OnAclModificationTried();
2926 #endregion
2930 public sealed class SystemAcl : CommonAcl
2932 #region Constructors
2935 // Creates an emtpy ACL
2938 public SystemAcl( bool isContainer, bool isDS, int capacity )
2939 : this( isContainer, isDS, isDS ? AclRevisionDS : AclRevision, capacity )
2943 public SystemAcl( bool isContainer, bool isDS, byte revision, int capacity )
2944 : base( isContainer, isDS, revision, capacity )
2949 // Creates an ACL from a given raw ACL
2950 // after canonicalizing it
2953 public SystemAcl( bool isContainer, bool isDS, RawAcl rawAcl )
2954 : this( isContainer, isDS, rawAcl, false )
2959 // Internal version - if 'trusted' is true,
2960 // takes ownership of the given raw ACL
2963 internal SystemAcl( bool isContainer, bool isDS, RawAcl rawAcl, bool trusted )
2964 : base( isContainer, isDS, rawAcl, trusted, false )
2968 #endregion
2970 #region Public Methods
2972 public void AddAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
2974 CheckFlags( inheritanceFlags, propagationFlags );
2975 AddQualifiedAce( sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags( auditFlags ) | GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
2978 public void SetAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
2980 CheckFlags( inheritanceFlags, propagationFlags );
2981 SetQualifiedAce( sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags( auditFlags ) | GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
2984 public bool RemoveAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
2986 return RemoveQualifiedAces(sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags( auditFlags ) | GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), true, ObjectAceFlags.None, Guid.Empty, Guid.Empty );
2989 public void RemoveAuditSpecific( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
2991 RemoveQualifiedAcesSpecific( sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags( auditFlags ) | GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
2994 public void AddAudit(SecurityIdentifier sid, ObjectAuditRule rule)
2996 AddAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
2999 public void AddAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3002 // This is valid only for DS Acls
3004 if ( !IsDS )
3006 throw new InvalidOperationException(
3007 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3009 Contract.EndContractBlock();
3011 CheckFlags( inheritanceFlags, propagationFlags );
3012 AddQualifiedAce(sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags(auditFlags) | GenericAce.AceFlagsFromInheritanceFlags(inheritanceFlags, propagationFlags), objectFlags, objectType, inheritedObjectType);
3015 public void SetAudit(SecurityIdentifier sid, ObjectAuditRule rule)
3017 SetAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3020 public void SetAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3023 // This is valid only for DS Acls
3025 if ( !IsDS )
3027 throw new InvalidOperationException(
3028 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3030 Contract.EndContractBlock();
3032 CheckFlags( inheritanceFlags, propagationFlags );
3033 SetQualifiedAce(sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags(auditFlags) | GenericAce.AceFlagsFromInheritanceFlags(inheritanceFlags, propagationFlags), objectFlags, objectType, inheritedObjectType);
3036 public bool RemoveAudit(SecurityIdentifier sid, ObjectAuditRule rule)
3038 return RemoveAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3041 public bool RemoveAudit( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3044 // This is valid only for DS Acls
3046 if ( !IsDS )
3048 throw new InvalidOperationException(
3049 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3051 Contract.EndContractBlock();
3053 return RemoveQualifiedAces(sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags(auditFlags) | GenericAce.AceFlagsFromInheritanceFlags(inheritanceFlags, propagationFlags), true, objectFlags, objectType, inheritedObjectType);
3056 public void RemoveAuditSpecific(SecurityIdentifier sid, ObjectAuditRule rule)
3058 RemoveAuditSpecific(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3061 public void RemoveAuditSpecific( AuditFlags auditFlags, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3064 // This is valid only for DS Acls
3066 if ( !IsDS )
3068 throw new InvalidOperationException(
3069 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3071 Contract.EndContractBlock();
3073 RemoveQualifiedAcesSpecific(sid, AceQualifier.SystemAudit, accessMask, GenericAce.AceFlagsFromAuditFlags(auditFlags) | GenericAce.AceFlagsFromInheritanceFlags(inheritanceFlags, propagationFlags), objectFlags, objectType, inheritedObjectType);
3076 #endregion
3080 public sealed class DiscretionaryAcl : CommonAcl
3082 #region
3083 static private SecurityIdentifier _sidEveryone = new SecurityIdentifier( WellKnownSidType.WorldSid, null );
3084 private bool everyOneFullAccessForNullDacl = false;
3085 #endregion
3087 #region Constructors
3090 // Creates an emtpy ACL
3093 public DiscretionaryAcl( bool isContainer, bool isDS, int capacity )
3094 : this( isContainer, isDS, isDS ? AclRevisionDS : AclRevision, capacity )
3098 public DiscretionaryAcl( bool isContainer, bool isDS, byte revision, int capacity )
3099 : base( isContainer, isDS, revision, capacity )
3104 // Creates an ACL from a given raw ACL
3105 // after canonicalizing it
3108 public DiscretionaryAcl( bool isContainer, bool isDS, RawAcl rawAcl )
3109 : this( isContainer, isDS, rawAcl, false )
3114 // Internal version - if 'trusted' is true,
3115 // takes ownership of the given raw ACL
3118 internal DiscretionaryAcl( bool isContainer, bool isDS, RawAcl rawAcl, bool trusted )
3119 : base( isContainer, isDS, rawAcl == null ? new RawAcl( isDS ? AclRevisionDS : AclRevision, 0 ) : rawAcl, trusted, true )
3123 #endregion
3125 #region Public Methods
3127 public void AddAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
3129 CheckAccessType( accessType );
3130 CheckFlags( inheritanceFlags, propagationFlags );
3131 everyOneFullAccessForNullDacl = false;
3132 AddQualifiedAce( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
3135 public void SetAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
3137 CheckAccessType( accessType );
3138 CheckFlags( inheritanceFlags, propagationFlags );
3139 everyOneFullAccessForNullDacl = false;
3140 SetQualifiedAce( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
3143 public bool RemoveAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
3145 CheckAccessType( accessType );
3146 everyOneFullAccessForNullDacl = false;
3147 return RemoveQualifiedAces( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), false, ObjectAceFlags.None, Guid.Empty, Guid.Empty );
3150 public void RemoveAccessSpecific( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags )
3152 CheckAccessType( accessType );
3153 everyOneFullAccessForNullDacl = false;
3154 RemoveQualifiedAcesSpecific(sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), ObjectAceFlags.None, Guid.Empty, Guid.Empty );
3157 public void AddAccess(AccessControlType accessType, SecurityIdentifier sid, ObjectAccessRule rule)
3159 AddAccess(accessType, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3162 public void AddAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3165 // This is valid only for DS Acls
3167 if ( !IsDS )
3169 throw new InvalidOperationException(
3170 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3172 Contract.EndContractBlock();
3174 CheckAccessType( accessType );
3175 CheckFlags( inheritanceFlags, propagationFlags );
3176 everyOneFullAccessForNullDacl = false;
3177 AddQualifiedAce( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), objectFlags, objectType, inheritedObjectType );
3180 public void SetAccess(AccessControlType accessType, SecurityIdentifier sid, ObjectAccessRule rule)
3182 SetAccess(accessType, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3185 public void SetAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3188 // This is valid only for DS Acls
3190 if ( !IsDS )
3192 throw new InvalidOperationException(
3193 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3195 Contract.EndContractBlock();
3197 CheckAccessType( accessType );
3198 CheckFlags( inheritanceFlags, propagationFlags );
3199 everyOneFullAccessForNullDacl = false;
3200 SetQualifiedAce( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), objectFlags, objectType, inheritedObjectType);
3203 public bool RemoveAccess(AccessControlType accessType, SecurityIdentifier sid, ObjectAccessRule rule)
3205 return RemoveAccess(accessType, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3208 public bool RemoveAccess( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3211 // This is valid only for DS Acls
3213 if ( !IsDS )
3215 throw new InvalidOperationException(
3216 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3218 Contract.EndContractBlock();
3220 CheckAccessType( accessType );
3221 everyOneFullAccessForNullDacl = false;
3222 return RemoveQualifiedAces(sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), false, objectFlags, objectType, inheritedObjectType );
3225 public void RemoveAccessSpecific(AccessControlType accessType, SecurityIdentifier sid, ObjectAccessRule rule)
3227 RemoveAccessSpecific(accessType, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
3230 public void RemoveAccessSpecific( AccessControlType accessType, SecurityIdentifier sid, int accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType )
3233 // This is valid only for DS Acls
3235 if ( !IsDS )
3237 throw new InvalidOperationException(
3238 Environment.GetResourceString( "InvalidOperation_OnlyValidForDS" ));
3240 Contract.EndContractBlock();
3242 CheckAccessType( accessType );
3243 everyOneFullAccessForNullDacl = false;
3244 RemoveQualifiedAcesSpecific( sid, accessType == AccessControlType.Allow ? AceQualifier.AccessAllowed : AceQualifier.AccessDenied, accessMask, GenericAce.AceFlagsFromInheritanceFlags( inheritanceFlags, propagationFlags ), objectFlags, objectType, inheritedObjectType );
3247 #endregion
3249 #region internals and privates
3252 // DACL's "allow everyone full access may be created to replace a null DACL because managed
3253 // access control does not want to leave null DACLs around. But we need to remember this MACL
3254 // created ACE when the DACL is modified, we can remove it to match the same native semantics of
3255 // a null DACL.
3257 internal bool EveryOneFullAccessForNullDacl
3259 get { return everyOneFullAccessForNullDacl; }
3260 set { everyOneFullAccessForNullDacl = value; }
3264 // As soon as you tried successfully to modified the ACL, the internally created allow every one full access ACL is materialized
3265 // because in native world, a NULL dacl can't be operated on.
3267 internal override void OnAclModificationTried()
3269 everyOneFullAccessForNullDacl = false;
3272 /// <summary>
3273 /// This static method will create an "allow everyone full control" single ACE DACL.
3274 /// </summary>
3275 /// <param name="isDS">whether it is a DS DACL</param>
3276 /// <param name="isContainer">whether it is a container</param>
3277 /// <returns>The single ACE DACL</returns>
3278 /// Note: This method is created to get the best behavior for using "allow everyone full access"
3279 /// single ACE DACL to replace null DACL from CommonSecurityObject.
3280 static internal DiscretionaryAcl CreateAllowEveryoneFullAccess(bool isDS, bool isContainer)
3282 DiscretionaryAcl dcl = new DiscretionaryAcl( isContainer, isDS, 1 );
3283 dcl.AddAccess(
3284 AccessControlType.Allow,
3285 _sidEveryone,
3287 isContainer ? ( InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit ) : InheritanceFlags.None,
3288 PropagationFlags.None );
3290 dcl.everyOneFullAccessForNullDacl = true;
3291 return dcl;
3293 #endregion