2 // System.Security.AccessControl.CommonAcl implementation
5 // Dick Porter <dick@ximian.com>
6 // Atsushi Enomoto <atsushi@ximian.com>
7 // James Bellinger <jfb@zer7.com>
9 // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2012 James Bellinger
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Collections
.Generic
;
33 using System
.Security
.Principal
;
35 namespace System
.Security
.AccessControl
37 /* NB: Note the Remarks section in the CommonAcl class docs
38 * concerning ACE management
40 public abstract class CommonAcl
: GenericAcl
42 const int default_capacity
= 10; // FIXME: not verified
44 internal delegate bool RemoveAcesCallback
<T
> (T ace
);
46 internal CommonAcl (bool isContainer
, bool isDS
, RawAcl rawAcl
)
49 rawAcl
= new RawAcl (isDS
? AclRevisionDS
: AclRevision
, default_capacity
);
51 // The RawAcl ACEs are cloned.
52 byte[] binaryForm
= new byte [rawAcl
.BinaryLength
];
53 rawAcl
.GetBinaryForm (binaryForm
, 0);
54 rawAcl
= new RawAcl (binaryForm
, 0);
57 Init (isContainer
, isDS
, rawAcl
);
60 internal CommonAcl (bool isContainer
, bool isDS
, byte revision
, int capacity
)
62 Init (isContainer
, isDS
, new RawAcl (revision
, capacity
));
65 internal CommonAcl (bool isContainer
, bool isDS
, int capacity
)
66 : this (isContainer
, isDS
, isDS
? AclRevisionDS
: AclRevision
, capacity
)
70 void Init (bool isContainer
, bool isDS
, RawAcl rawAcl
)
72 is_container
= isContainer
;
75 CanonicalizeAndClearAefa ();
78 bool is_aefa
, is_canonical
, is_container
, is_ds
;
79 internal RawAcl raw_acl
;
81 public override sealed int BinaryLength
{
82 get { return raw_acl.BinaryLength; }
85 public override sealed int Count
{
86 get { return raw_acl.Count; }
89 public bool IsCanonical
{
90 get { return is_canonical; }
93 public bool IsContainer
{
94 get { return is_container; }
101 // See CommonSecurityDescriptorTest's AefaModifiedFlagIsStoredOnDiscretionaryAcl unit test.
102 internal bool IsAefa
{
103 get { return is_aefa; }
104 set { is_aefa = value; }
107 public override sealed byte Revision
{
108 get { return raw_acl.Revision; }
111 public override sealed GenericAce
this[int index
] {
112 get { return CopyAce (raw_acl [index]); }
113 set { throw new NotSupportedException (); }
116 public override sealed void GetBinaryForm (byte[] binaryForm
, int offset
)
118 raw_acl
.GetBinaryForm (binaryForm
, offset
);
121 public void Purge (SecurityIdentifier sid
)
123 RequireCanonicity ();
124 RemoveAces
<KnownAce
> (ace
=> ace
.SecurityIdentifier
== sid
);
127 public void RemoveInheritedAces ()
129 RequireCanonicity ();
130 RemoveAces
<GenericAce
> (ace
=> ace
.IsInherited
);
133 internal void RequireCanonicity ()
136 throw new InvalidOperationException("ACL is not canonical.");
139 internal void CanonicalizeAndClearAefa ()
141 RemoveAces
<GenericAce
> (IsAceMeaningless
);
143 is_canonical
= TestCanonicity ();
146 ApplyCanonicalSortToExplicitAces ();
147 MergeExplicitAces ();
153 internal virtual bool IsAceMeaningless (GenericAce ace
)
155 AceFlags flags
= ace
.AceFlags
;
157 KnownAce knownAce
= ace
as KnownAce
;
158 if (knownAce
!= null) {
159 if (0 == knownAce
.AccessMask
) return true;
160 if (0 != (flags
& AceFlags
.InheritOnly
)) {
161 if (knownAce
is ObjectAce
) return true;
162 if (!IsContainer
) return true;
163 if (0 == (flags
& (AceFlags
.ContainerInherit
|AceFlags
.ObjectInherit
))) return true;
170 bool TestCanonicity ()
172 foreach (GenericAce ace
in this) {
173 if (!(ace
is QualifiedAce
)) return false;
176 bool gotInheritedAce
= false;
177 foreach (QualifiedAce ace
in this) {
178 if (ace
.IsInherited
) {
179 gotInheritedAce
= true;
181 if (gotInheritedAce
) return false;
185 bool gotExplicitAllow
= false;
186 foreach (QualifiedAce ace
in this) {
187 if (ace
.IsInherited
) break;
188 if (AceQualifier
.AccessAllowed
== ace
.AceQualifier
) {
189 gotExplicitAllow
= true;
190 } else if (AceQualifier
.AccessDenied
== ace
.AceQualifier
) {
191 if (gotExplicitAllow
) return false;
198 internal int GetCanonicalExplicitDenyAceCount ()
201 for (i
= 0; i
< Count
; i
++) {
202 if (raw_acl
[i
].IsInherited
) break;
204 QualifiedAce ace
= raw_acl
[i
] as QualifiedAce
;
205 if (ace
== null || ace
.AceQualifier
!= AceQualifier
.AccessDenied
) break;
210 internal int GetCanonicalExplicitAceCount ()
213 for (i
= 0; i
< Count
; i
++)
214 if (raw_acl
[i
].IsInherited
) break;
218 void MergeExplicitAces ()
220 int explicitCount
= GetCanonicalExplicitAceCount ();
222 for (int i
= 0; i
< explicitCount
- 1; ) {
223 GenericAce mergedAce
= MergeExplicitAcePair (raw_acl
[i
], raw_acl
[i
+ 1]);
224 if (null != mergedAce
) {
225 raw_acl
[i
] = mergedAce
;
226 raw_acl
.RemoveAce (i
+ 1);
234 GenericAce
MergeExplicitAcePair (GenericAce ace1
, GenericAce ace2
)
236 QualifiedAce qace1
= ace1
as QualifiedAce
;
237 QualifiedAce qace2
= ace2
as QualifiedAce
;
238 if (!(null != qace1
&& null != qace2
)) return null;
239 if (!(qace1
.AceQualifier
== qace2
.AceQualifier
)) return null;
240 if (!(qace1
.SecurityIdentifier
== qace2
.SecurityIdentifier
)) return null;
242 AceFlags aceFlags1
= qace1
.AceFlags
, aceFlags2
= qace2
.AceFlags
, aceFlagsNew
;
243 int accessMask1
= qace1
.AccessMask
, accessMask2
= qace2
.AccessMask
, accessMaskNew
;
246 aceFlags1
&= ~AceFlags
.InheritanceFlags
;
247 aceFlags2
&= ~AceFlags
.InheritanceFlags
;
250 if (aceFlags1
!= aceFlags2
) {
251 if (accessMask1
!= accessMask2
) return null;
252 if ((aceFlags1
& ~
(AceFlags
.ContainerInherit
|AceFlags
.ObjectInherit
)) ==
253 (aceFlags2
& ~
(AceFlags
.ContainerInherit
|AceFlags
.ObjectInherit
))) {
254 aceFlagsNew
= aceFlags1
|aceFlags2
; // merge InheritanceFlags
255 accessMaskNew
= accessMask1
;
256 } else if ((aceFlags1
& ~
(AceFlags
.SuccessfulAccess
|AceFlags
.FailedAccess
)) ==
257 (aceFlags2
& ~
(AceFlags
.SuccessfulAccess
|AceFlags
.FailedAccess
))) {
258 aceFlagsNew
= aceFlags1
|aceFlags2
; // merge AuditFlags
259 accessMaskNew
= accessMask1
;
264 aceFlagsNew
= aceFlags1
;
265 accessMaskNew
= accessMask1
|accessMask2
;
268 CommonAce cace1
= ace1
as CommonAce
;
269 CommonAce cace2
= ace2
as CommonAce
;
270 if (null != cace1
&& null != cace2
) {
271 return new CommonAce (aceFlagsNew
, cace1
.AceQualifier
, accessMaskNew
,
272 cace1
.SecurityIdentifier
, cace1
.IsCallback
, cace1
.GetOpaque());
275 ObjectAce oace1
= ace1
as ObjectAce
;
276 ObjectAce oace2
= ace2
as ObjectAce
;
277 if (null != oace1
&& null != oace2
) {
278 // See DiscretionaryAclTest.GuidEmptyMergesRegardlessOfFlagsAndOpaqueDataIsNotConsidered
279 Guid type1
, inheritedType1
; GetObjectAceTypeGuids(oace1
, out type1
, out inheritedType1
);
280 Guid type2
, inheritedType2
; GetObjectAceTypeGuids(oace2
, out type2
, out inheritedType2
);
282 if (type1
== type2
&& inheritedType1
== inheritedType2
) {
283 return new ObjectAce (aceFlagsNew
, oace1
.AceQualifier
, accessMaskNew
,
284 oace1
.SecurityIdentifier
,
285 oace1
.ObjectAceFlags
, oace1
.ObjectAceType
, oace1
.InheritedObjectAceType
,
286 oace1
.IsCallback
, oace1
.GetOpaque());
293 static void GetObjectAceTypeGuids(ObjectAce ace
, out Guid type
, out Guid inheritedType
)
295 type
= Guid
.Empty
; inheritedType
= Guid
.Empty
;
296 if (0 != (ace
.ObjectAceFlags
& ObjectAceFlags
.ObjectAceTypePresent
))
297 type
= ace
.ObjectAceType
;
298 if (0 != (ace
.ObjectAceFlags
& ObjectAceFlags
.InheritedObjectAceTypePresent
))
299 inheritedType
= ace
.InheritedObjectAceType
;
302 internal abstract void ApplyCanonicalSortToExplicitAces ();
304 internal void ApplyCanonicalSortToExplicitAces (int start
, int count
)
307 for (i
= start
+ 1; i
< start
+ count
; i
++)
309 KnownAce ace
= (KnownAce
)raw_acl
[i
];
310 SecurityIdentifier sid
= ace
.SecurityIdentifier
;
311 for (j
= i
; j
> start
&& ((KnownAce
)raw_acl
[j
- 1]).SecurityIdentifier
.CompareTo (sid
) > 0; j
--)
312 raw_acl
[j
] = raw_acl
[j
- 1];
317 internal override string GetSddlForm (ControlFlags sdFlags
, bool isDacl
)
319 return raw_acl
.GetSddlForm (sdFlags
, isDacl
);
322 internal void RemoveAces
<T
> (RemoveAcesCallback
<T
> callback
)
325 for (int i
= 0; i
< raw_acl
.Count
; ) {
326 if (raw_acl
[i
] is T
&& callback ((T
)raw_acl
[i
])) {
327 raw_acl
.RemoveAce (i
);
334 // DiscretionaryAcl/SystemAcl shared implementation below...
335 internal void AddAce (AceQualifier aceQualifier
,
336 SecurityIdentifier sid
, int accessMask
,
337 InheritanceFlags inheritanceFlags
,
338 PropagationFlags propagationFlags
,
339 AuditFlags auditFlags
)
341 QualifiedAce ace
= AddAceGetQualifiedAce (aceQualifier
, sid
, accessMask
,
342 inheritanceFlags
, propagationFlags
, auditFlags
);
346 internal void AddAce (AceQualifier aceQualifier
,
347 SecurityIdentifier sid
, int accessMask
,
348 InheritanceFlags inheritanceFlags
,
349 PropagationFlags propagationFlags
,
350 AuditFlags auditFlags
,
351 ObjectAceFlags objectFlags
,
353 Guid inheritedObjectType
)
355 QualifiedAce ace
= AddAceGetQualifiedAce (aceQualifier
, sid
, accessMask
,
356 inheritanceFlags
, propagationFlags
, auditFlags
,
357 objectFlags
, objectType
, inheritedObjectType
);
361 QualifiedAce
AddAceGetQualifiedAce (AceQualifier aceQualifier
,
362 SecurityIdentifier sid
, int accessMask
,
363 InheritanceFlags inheritanceFlags
,
364 PropagationFlags propagationFlags
,
365 AuditFlags auditFlags
,
366 ObjectAceFlags objectFlags
,
368 Guid inheritedObjectType
)
371 throw new InvalidOperationException ("For this overload, IsDS must be true.");
373 if (ObjectAceFlags
.None
== objectFlags
)
374 return AddAceGetQualifiedAce (aceQualifier
, sid
, accessMask
,
375 inheritanceFlags
, propagationFlags
, auditFlags
);
377 AceFlags flags
= GetAceFlags (inheritanceFlags
, propagationFlags
, auditFlags
);
378 return new ObjectAce (flags
, aceQualifier
, accessMask
, sid
,
379 objectFlags
, objectType
, inheritedObjectType
, false, null);
382 QualifiedAce
AddAceGetQualifiedAce (AceQualifier aceQualifier
,
383 SecurityIdentifier sid
, int accessMask
,
384 InheritanceFlags inheritanceFlags
,
385 PropagationFlags propagationFlags
,
386 AuditFlags auditFlags
)
388 AceFlags flags
= GetAceFlags (inheritanceFlags
, propagationFlags
, auditFlags
);
389 return new CommonAce (flags
, aceQualifier
, accessMask
, sid
, false, null);
392 void AddAce (QualifiedAce newAce
)
394 RequireCanonicity ();
396 int pos
= GetAceInsertPosition (newAce
.AceQualifier
);
397 raw_acl
.InsertAce (pos
, CopyAce (newAce
));
398 CanonicalizeAndClearAefa ();
401 static GenericAce
CopyAce (GenericAce ace
)
403 byte[] binaryForm
= new byte[ace
.BinaryLength
];
404 ace
.GetBinaryForm (binaryForm
, 0);
405 return GenericAce
.CreateFromBinaryForm (binaryForm
, 0);
408 internal abstract int GetAceInsertPosition (AceQualifier aceQualifier
);
410 AceFlags
GetAceFlags (InheritanceFlags inheritanceFlags
, PropagationFlags propagationFlags
, AuditFlags auditFlags
)
412 if (InheritanceFlags
.None
!= inheritanceFlags
&& !IsContainer
)
413 throw new ArgumentException ("Flags only work with containers.", "inheritanceFlags");
415 if (InheritanceFlags
.None
== inheritanceFlags
&& PropagationFlags
.None
!= propagationFlags
)
416 throw new ArgumentException ("Propagation flags need inheritance flags.", "propagationFlags");
418 AceFlags flags
= AceFlags
.None
;
419 if (0 != (InheritanceFlags
.ContainerInherit
& inheritanceFlags
))
420 flags
|= AceFlags
.ContainerInherit
;
421 if (0 != (InheritanceFlags
.ObjectInherit
& inheritanceFlags
))
422 flags
|= AceFlags
.ObjectInherit
;
423 if (0 != (PropagationFlags
.InheritOnly
& propagationFlags
))
424 flags
|= AceFlags
.InheritOnly
;
425 if (0 != (PropagationFlags
.NoPropagateInherit
& propagationFlags
))
426 flags
|= AceFlags
.NoPropagateInherit
;
427 if (0 != (AuditFlags
.Success
& auditFlags
))
428 flags
|= AceFlags
.SuccessfulAccess
;
429 if (0 != (AuditFlags
.Failure
& auditFlags
))
430 flags
|= AceFlags
.FailedAccess
;
434 internal void RemoveAceSpecific (AceQualifier aceQualifier
,
435 SecurityIdentifier sid
,
437 InheritanceFlags inheritanceFlags
,
438 PropagationFlags propagationFlags
,
439 AuditFlags auditFlags
)
441 RequireCanonicity ();
442 RemoveAces
<CommonAce
> (ace
=>
444 if (ace
.AccessMask
!= accessMask
) return false;
445 if (ace
.AceQualifier
!= aceQualifier
) return false;
446 if (ace
.SecurityIdentifier
!= sid
) return false;
447 if (ace
.InheritanceFlags
!= inheritanceFlags
) return false;
448 if (InheritanceFlags
.None
!= inheritanceFlags
)
449 if (ace
.PropagationFlags
!= propagationFlags
) return false;
450 if (ace
.AuditFlags
!= auditFlags
) return false;
453 CanonicalizeAndClearAefa ();
456 internal void RemoveAceSpecific (AceQualifier aceQualifier
,
457 SecurityIdentifier sid
,
459 InheritanceFlags inheritanceFlags
,
460 PropagationFlags propagationFlags
,
461 AuditFlags auditFlags
,
462 ObjectAceFlags objectFlags
,
464 Guid inheritedObjectType
)
467 throw new InvalidOperationException ("For this overload, IsDS must be true.");
469 if (ObjectAceFlags
.None
== objectFlags
) {
470 RemoveAceSpecific (aceQualifier
, sid
, accessMask
, inheritanceFlags
, propagationFlags
, auditFlags
);
474 RequireCanonicity ();
475 RemoveAces
<ObjectAce
> (ace
=>
477 if (ace
.AccessMask
!= accessMask
) return false;
478 if (ace
.AceQualifier
!= aceQualifier
) return false;
479 if (ace
.SecurityIdentifier
!= sid
) return false;
480 if (ace
.InheritanceFlags
!= inheritanceFlags
) return false;
481 if (InheritanceFlags
.None
!= inheritanceFlags
)
482 if (ace
.PropagationFlags
!= propagationFlags
) return false;
483 if (ace
.AuditFlags
!= auditFlags
) return false;
484 if (ace
.ObjectAceFlags
!= objectFlags
) return false;
485 if (0 != (objectFlags
& ObjectAceFlags
.ObjectAceTypePresent
))
486 if (ace
.ObjectAceType
!= objectType
) return false;
487 if (0 != (objectFlags
& ObjectAceFlags
.InheritedObjectAceTypePresent
))
488 if (ace
.InheritedObjectAceType
!= objectType
) return false;
491 CanonicalizeAndClearAefa ();
494 internal void SetAce (AceQualifier aceQualifier
,
495 SecurityIdentifier sid
,
497 InheritanceFlags inheritanceFlags
,
498 PropagationFlags propagationFlags
,
499 AuditFlags auditFlags
)
501 QualifiedAce ace
= AddAceGetQualifiedAce (aceQualifier
, sid
, accessMask
,
502 inheritanceFlags
, propagationFlags
, auditFlags
);
506 internal void SetAce (AceQualifier aceQualifier
,
507 SecurityIdentifier sid
,
509 InheritanceFlags inheritanceFlags
,
510 PropagationFlags propagationFlags
,
511 AuditFlags auditFlags
,
512 ObjectAceFlags objectFlags
,
514 Guid inheritedObjectType
)
516 QualifiedAce ace
= AddAceGetQualifiedAce (aceQualifier
, sid
, accessMask
,
517 inheritanceFlags
, propagationFlags
, auditFlags
,
518 objectFlags
, objectType
, inheritedObjectType
);
522 void SetAce (QualifiedAce newAce
)
524 RequireCanonicity ();
526 RemoveAces
<QualifiedAce
> (oldAce
=>
528 return oldAce
.AceQualifier
== newAce
.AceQualifier
&&
529 oldAce
.SecurityIdentifier
== newAce
.SecurityIdentifier
;
531 CanonicalizeAndClearAefa ();