3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
8 ** Classes: Access Control List (ACL) family of classes
11 ===========================================================*/
13 namespace System
.Security
.AccessControl
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
34 private readonly GenericAcl _acl
;
40 internal AceEnumerator( GenericAcl collection
)
42 if ( collection
== null )
44 throw new ArgumentNullException( "collection" );
46 Contract
.EndContractBlock();
54 #region IEnumerator Interface
56 object IEnumerator
.Current
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()
79 return ( _current
< _acl
.Count
);
91 public abstract class GenericAcl
: ICollection
95 protected GenericAcl()
100 #region Public Constants
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
;
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
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;
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; }
159 #region Public Methods
162 // Returns the binary representation of the ACL
165 public abstract void GetBinaryForm( byte[] binaryForm
, int offset
);
169 #region ICollection Implementation
171 void ICollection
.CopyTo( Array array
, int index
)
175 throw new ArgumentNullException( "array" );
178 if ( array
.Rank
!= 1 )
180 throw new RankException( Environment
.GetResourceString( "Rank_MultiDimNotSupported" ));
182 Contract
.EndContractBlock();
186 throw new ArgumentOutOfRangeException(
188 Environment
.GetResourceString( "ArgumentOutOfRange_NeedNonNegNum" ));
190 else if ( array
.Length
- index
< Count
)
192 throw new ArgumentOutOfRangeException(
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
222 #region IEnumerable Implementation
224 IEnumerator IEnumerable
.GetEnumerator()
226 return new AceEnumerator( this );
229 public AceEnumerator
GetEnumerator()
231 return (( IEnumerable
)this ).GetEnumerator() as AceEnumerator
;
238 public sealed class RawAcl
: GenericAcl
240 #region Private Members
242 private byte _revision
;
243 private ArrayList _aces
;
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();
260 // Offset must not be negative
263 throw new ArgumentOutOfRangeException(
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
;
295 throw new ArgumentOutOfRangeException(
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(
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(
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
)
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)
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" ),
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);
402 // Verify that no more than the advertised length of the ACL was consumed
405 if ( offset
> length
)
407 goto InvalidParameter
;
415 throw new ArgumentException(
416 Environment
.GetResourceString( "ArgumentException_InvalidAclBinaryForm" ),
425 // Creates an empty ACL
428 public RawAcl( byte revision
, int capacity
)
431 _revision
= revision
;
432 _aces
= new ArrayList( capacity
);
436 // Creates an ACL from its binary representation
439 public RawAcl( byte[] binaryForm
, int offset
)
442 SetBinaryForm( binaryForm
, offset
);
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
;
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();
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
;
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
)
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
);
604 public abstract class CommonAcl
: GenericAcl
606 #region Add/Remove Logic Support
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
619 private enum PM
// Propagation matrix
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
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 ;
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
)
698 if (( aceFlags
& AceFlags
.ContainerInherit
) != 0)
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 ))
712 if (( aceFlags
& AceFlags
.InheritOnly
) != 0 )
717 if (( aceFlags
& AceFlags
.NoPropagateInherit
) != 0 )
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
;
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
)
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
)
788 result
= AceFlagsFromAF( resultAF
, isDS
);
793 private static bool RemoveInheritanceBits( AceFlags existing
, AceFlags
remove, bool isDS
, out AceFlags result
, out bool total
)
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?
810 unchecked { resultPM = leftPM & ~rightPM; }
813 // If the resulting propagation matrix is zero,
814 // communicate back the fact that removal is "total"
823 AF resultAF
= PMtoAF
[( int )resultPM
];
825 if ( resultAF
== AF
.Invalid
)
831 result
= AceFlagsFromAF( resultAF
, isDS
);
838 #region Private Members
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
;
853 #region Private Methods
855 private void CanonicalizeIfNecessary()
859 Canonicalize( false, this is DiscretionaryAcl
);
864 private enum ComparisonResult
872 // Compares two discretionary ACEs and returns
873 // LessThan if ace1 < ace2
874 // EqualTo if ace1 == ace2
875 // GreaterThan if ace1 > ace2
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
)
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
)
904 else if ( type
== AceType
.AccessDeniedObject
||
905 type
== AceType
.AccessDeniedCallbackObject
)
909 else if ( type
== AceType
.AccessAllowed
||
910 type
== AceType
.AccessAllowedCallback
)
914 else if ( type
== AceType
.AccessAllowedObject
||
915 type
== AceType
.AccessAllowedCallbackObject
)
922 // custom aces are at the second group
924 result
= ushort.MaxValue
+ ace
._indexInAcl
;
931 // Compares two system ACEs and returns
932 // LessThan if ace1 < ace2
933 // EqualTo if ace1 == ace2
934 // GreaterThan if ace1 > ace2
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
)
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
)
959 else if ( type
== AceType
.SystemAuditObject
||
960 type
== AceType
.SystemAlarmObject
||
961 type
== AceType
.SystemAuditCallbackObject
||
962 type
== AceType
.SystemAlarmCallbackObject
)
968 result
= ushort.MaxValue
+ ace
._indexInAcl
;
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
;
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
);
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
)
1013 int leftHold
, rightHold
;
1016 if ( left
>= right
)
1027 while ( left
< right
)
1029 // while (( _acl[right] >= pivot ) && ( left < right ))
1030 while (( ComparisonResult
.LessThan
!= CompareAces( _acl
[right
], pivot
, isDacl
) ) && ( left
< right
))
1035 if ( left
!= right
)
1037 _acl
[left
] = _acl
[right
];
1041 // while (( _acl[left] <= pivot ) && ( left < right ))
1042 while (( ComparisonResult
.GreaterThan
!= CompareAces( _acl
[left
], pivot
, isDacl
) ) && ( left
< right
))
1047 if ( left
!= right
)
1049 _acl
[right
] = _acl
[left
];
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 )
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
1113 if (( ace
.AceFlags
& AceFlags
.InheritOnly
) != 0 )
1118 if (( ace
.AceFlags
& InheritFlags
) != 0 )
1120 unchecked { ace.AceFlags &= ~InheritFlags; }
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 ))
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
;
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
)
1175 // On a SACL, any ACE that does not specify Success or Failure
1176 // flags can be removed
1179 if (( ace
.AceFlags
& AuditFlags
) == 0 )
1185 // Qualified ACEs in a SACL must be audit ACEs
1188 if ( qualifiedAce
!= null )
1190 if ( qualifiedAce
.AceQualifier
!= AceQualifier
.SystemAudit
)
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
);
1243 for ( int i
= 0; i
< Count
- 1; i
++ )
1245 QualifiedAce thisAce
= _acl
[i
] as QualifiedAce
;
1247 if ( thisAce
== null )
1252 QualifiedAce nextAce
= _acl
[i
+ 1] as QualifiedAce
;
1254 if ( nextAce
== null )
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
)
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
))
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 ))
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
))
1356 ObjectAceFlags objectFlags
= ( ace
is ObjectAce
) ? (( ObjectAce
) ace
).ObjectAceFlags
: ObjectAceFlags
.None
;
1357 if (( objectFlags
& ObjectAceFlags
.InheritedObjectAceTypePresent
) == 0 )
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.");
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
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 ))
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
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
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 ))
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
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
)
1500 for ( int i
= 0; i
< aceOpaque
.Length
; ++i
)
1502 if ( aceOpaque
[i
] != newAceOpaque
[i
] )
1511 static private bool AcesAreMergeable( QualifiedAce ace
, QualifiedAce newAce
)
1514 // Only interested in ACEs with the specified type
1517 if ( ace
.AceType
!= newAce
.AceType
)
1523 // Only interested in explicit (non-inherited) ACEs
1526 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
1531 if (( newAce
.AceFlags
& AceFlags
.Inherited
) != 0 )
1537 // Only interested in ACEs with the specified qualifier
1540 if ( ace
.AceQualifier
!= newAce
.AceQualifier
)
1546 // Only interested in ACEs with the specified SID
1549 if ( ace
.SecurityIdentifier
!= newAce
.SecurityIdentifier
)
1555 // Only interested in ACEs with the specified callback data
1558 if ( !AceOpaquesMatch( ace
, newAce
))
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
))
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
;
1604 ace
.AccessMask
|= newAce
.AccessMask
;
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
);
1631 ace
.AceFlags
|= ( newAce
.AceFlags
& AceFlags
.AuditFlags
);
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
))
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
));
1667 if ( true == MergeInheritanceBits( ace
.AceFlags
, newAce
.AceFlags
, IsDS
, out merged
))
1669 ace
.AceFlags
= ( merged
| ( ace
.AceFlags
& AceFlags
.AuditFlags
));
1680 // Returns 'true' if the ACL is in canonical order; 'false' otherwise
1683 private bool CanonicalCheck( bool 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
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
;
1717 QualifiedAce qualifiedAce
= ace
as QualifiedAce
;
1719 if ( qualifiedAce
== null )
1722 // Explicit ACE is not recognized - this is not a canonical ACL
1728 if ( qualifiedAce
.AceQualifier
== AceQualifier
.AccessAllowed
)
1730 aceStage
= AccessAllowed
;
1732 else if ( qualifiedAce
.AceQualifier
== AceQualifier
.AccessDenied
)
1734 aceStage
= AccessDenied
;
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" );
1747 if ( aceStage
== Unknown
)
1752 if ( aceStage
> currentStage
)
1754 currentStage
= aceStage
;
1756 else if ( aceStage
< currentStage
)
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
1781 for ( int i
= 0; i
< _acl
.Count
; i
++ )
1783 int aceStage
= Unknown
;
1785 GenericAce ace
= _acl
[i
];
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?" );
1798 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
1800 aceStage
= Inherited
;
1804 QualifiedAce qualifiedAce
= ace
as QualifiedAce
;
1806 if ( qualifiedAce
== null )
1809 // Explicit ACE is not recognized - this is not a canonical ACL
1815 if ( qualifiedAce
.AceQualifier
== AceQualifier
.SystemAudit
||
1816 qualifiedAce
.AceQualifier
== AceQualifier
.SystemAlarm
)
1818 aceStage
= Explicit
;
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" );
1831 if ( aceStage
> currentStage
)
1833 currentStage
= aceStage
;
1835 else if ( aceStage
< currentStage
)
1846 private void ThrowIfNotCanonical()
1848 if ( !_isCanonical
)
1850 throw new InvalidOperationException( Environment
.GetResourceString( "InvalidOperation_ModificationOfNonCanonicalAcl" ));
1856 #region Constructors
1859 // Creates an empty ACL
1862 internal CommonAcl( bool isContainer
, bool isDS
, byte revision
, int capacity
)
1865 _isContainer
= isContainer
;
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
)
1882 if ( rawAcl
== null )
1884 throw new ArgumentNullException( "rawAcl" );
1886 Contract
.EndContractBlock();
1888 _isContainer
= isContainer
;
1894 // In the trusted case, we take over ownership of the ACL passed in
1899 RemoveMeaninglessAcesAndFlags( isDacl
);
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;
1944 _isCanonical
= false;
1950 #region Internal Properties
1952 internal RawAcl RawAcl
1954 get { return _acl; }
1959 #region Protected Methods
1961 internal void CheckAccessType( AccessControlType accessType
)
1963 if ( accessType
!= AccessControlType
.Allow
&&
1964 accessType
!= AccessControlType
.Deny
)
1966 throw new ArgumentOutOfRangeException(
1968 Environment
.GetResourceString( "ArgumentOutOfRange_Enum" ));
1972 internal void CheckFlags( InheritanceFlags inheritanceFlags
, PropagationFlags propagationFlags
)
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" );
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
)
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" ),
2028 if ( accessMask
== 0 )
2030 throw new ArgumentException(
2031 Environment
.GetResourceString( "Argument_ArgumentZero" ),
2037 if (( !IsDS
) || ( objectFlags
== ObjectAceFlags
.None
))
2039 newAce
= new CommonAce( flags
, qualifier
, accessMask
, sid
, false, null );
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
)))
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
;
2068 if ( true == MergeAces( ref ace
, newAce
as QualifiedAce
))
2076 // Couldn't modify any existing entry, so add a new one
2081 _acl
.InsertAce( _acl
.Count
, newAce
);
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
)
2096 throw new ArgumentNullException( "sid" );
2099 if ( qualifier
== AceQualifier
.SystemAudit
&&
2100 (( flags
& AceFlags
.AuditFlags
) == 0 ))
2102 throw new ArgumentException(
2103 Environment
.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
2107 if ( accessMask
== 0 )
2109 throw new ArgumentException(
2110 Environment
.GetResourceString( "Argument_ArgumentZero" ),
2113 Contract
.EndContractBlock();
2114 ThrowIfNotCanonical();
2118 if (( !IsDS
) || ( objectFlags
== ObjectAceFlags
.None
))
2120 newAce
= new CommonAce( flags
, qualifier
, accessMask
, sid
, false, null );
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
)))
2136 for ( int i
= 0; i
< Count
; i
++ )
2138 QualifiedAce ace
= _acl
[i
] as QualifiedAce
;
2141 // Not a qualified ACE - keep going
2150 // Only interested in explicit (non-inherited) ACEs
2153 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
2159 // Only interested in ACEs with the specified qualifier
2162 if ( ace
.AceQualifier
!= qualifier
)
2168 // Only interested in ACEs with the specified SID
2171 if ( ace
.SecurityIdentifier
!= sid
)
2177 // This ACE corresponds to the SID and qualifier in question - remove it
2180 _acl
.RemoveAce( 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
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" ),
2212 if ( qualifier
== AceQualifier
.SystemAudit
&&
2213 (( flags
& AceFlags
.AuditFlags
) == 0 ))
2215 throw new ArgumentException(
2216 Environment
.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
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
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 );
2259 for ( int i
= 0; i
< Count
; i
++ )
2261 QualifiedAce ace
= _acl
[i
] as QualifiedAce
;
2264 // Not a qualified ACE - keep going
2273 // Only interested in explicit (non-inherited) ACEs
2276 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
2282 // Only interested in ACEs with the specified qualifier
2285 if ( ace
.AceQualifier
!= qualifier
)
2291 // Only interested in ACEs with the specified SID
2294 if ( ace
.SecurityIdentifier
!= sid
)
2300 // If access masks have nothing in common, skip the whole exercise
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 )
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
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 ))
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;
2362 if (( ace
.AccessMask
& accessMask
) == 0 )
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 ))
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)
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;
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
;
2554 newObjectAce
= new ObjectAce( ps_AceFlags
, qualifier
, ps_AccessMask
, ace
.SecurityIdentifier
, ps_ObjectAceFlags
, ps_ObjectAceType
, ps_InheritedObjectAceType
, false, null );
2555 _acl
.InsertAce( i
, newObjectAce
);
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
;
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 );
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 );
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 );
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" ),
2657 if ( qualifier
== AceQualifier
.SystemAudit
&&
2658 (( flags
& AceFlags
.AuditFlags
) == 0 ))
2660 throw new ArgumentException(
2661 Environment
.GetResourceString( "Arg_EnumAtLeastOneFlag" ),
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
2686 // Only interested in explicit (non-inherited) ACEs
2689 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
2695 // Only interested in ACEs with the specified qualifier
2698 if ( ace
.AceQualifier
!= qualifier
)
2704 // Only interested in ACEs with the specified SID
2707 if ( ace
.SecurityIdentifier
!= sid
)
2713 // Only interested in exact ACE flag matches
2716 if ( ace
.AceFlags
!= flags
)
2722 // Only interested in exact access mask matches
2725 if ( ace
.AccessMask
!= accessMask
)
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
)))
2750 else if (( ace
is ObjectAce
) || ( objectFlags
!= ObjectAceFlags
.None
))
2752 // one is object ace and the other is not, so no match
2758 // Got our exact match; now remove it
2762 i
--; // keep the array index honest
2764 OnAclModificationTried();
2767 internal virtual void OnAclModificationTried()
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();
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; }
2825 get { return _isDS; }
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
)
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
2906 // Skip over inherited ACEs
2909 if (( ace
.AceFlags
& AceFlags
.Inherited
) != 0 )
2915 // SID matches - ACE is out
2918 if ( ace
.SecurityIdentifier
== sid
)
2920 _acl
.RemoveAce( i
);
2923 OnAclModificationTried();
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 )
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
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
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
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
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
);
3080 public sealed class DiscretionaryAcl
: CommonAcl
3083 static private SecurityIdentifier _sidEveryone
= new SecurityIdentifier( WellKnownSidType
.WorldSid
, null );
3084 private bool everyOneFullAccessForNullDacl
= false;
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 )
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
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
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
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
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
);
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
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;
3273 /// This static method will create an "allow everyone full control" single ACE DACL.
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 );
3284 AccessControlType
.Allow
,
3287 isContainer
? ( InheritanceFlags
.ContainerInherit
| InheritanceFlags
.ObjectInherit
) : InheritanceFlags
.None
,
3288 PropagationFlags
.None
);
3290 dcl
.everyOneFullAccessForNullDacl
= true;