1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 /*============================================================
6 **
7 ** Classes:  Common Object Security class
8 **
9 **
10 ===========================================================*/
11 
12 using Microsoft.Win32;
13 using System;
14 using System.Collections;
15 using System.Diagnostics;
16 using System.Runtime.InteropServices;
17 using System.Security.Principal;
18 
19 namespace System.Security.AccessControl
20 {
21     public abstract class CommonObjectSecurity : ObjectSecurity
22     {
23         #region Constructors
24 
CommonObjectSecurity(bool isContainer)25         protected CommonObjectSecurity(bool isContainer)
26             : base(isContainer, false)
27         {
28         }
29 
CommonObjectSecurity(CommonSecurityDescriptor securityDescriptor)30         internal CommonObjectSecurity(CommonSecurityDescriptor securityDescriptor)
31             : base(securityDescriptor)
32         {
33         }
34 
35         #endregion
36 
37         #region Private Methods
38         // Ported from NDP\clr\src\BCL\System\Security\Principal\SID.cs since we can't access System.Security.Principal.IdentityReference's internals
IsValidTargetTypeStatic(Type targetType)39         private static bool IsValidTargetTypeStatic(Type targetType)
40         {
41             if (targetType == typeof(NTAccount))
42             {
43                 return true;
44             }
45             else if (targetType == typeof(SecurityIdentifier))
46             {
47                 return true;
48             }
49             else
50             {
51                 return false;
52             }
53         }
54 
GetRules(bool access, bool includeExplicit, bool includeInherited, System.Type targetType)55         private AuthorizationRuleCollection GetRules(bool access, bool includeExplicit, bool includeInherited, System.Type targetType)
56         {
57             ReadLock();
58 
59             try
60             {
61                 AuthorizationRuleCollection result = new AuthorizationRuleCollection();
62 
63                 if (!IsValidTargetTypeStatic(targetType))
64                 {
65                     throw new ArgumentException(
66                         SR.Arg_MustBeIdentityReferenceType,
67 nameof(targetType));
68                 }
69 
70                 CommonAcl acl = null;
71 
72                 if (access)
73                 {
74                     if ((_securityDescriptor.ControlFlags & ControlFlags.DiscretionaryAclPresent) != 0)
75                     {
76                         acl = _securityDescriptor.DiscretionaryAcl;
77                     }
78                 }
79                 else // !access == audit
80                 {
81                     if ((_securityDescriptor.ControlFlags & ControlFlags.SystemAclPresent) != 0)
82                     {
83                         acl = _securityDescriptor.SystemAcl;
84                     }
85                 }
86 
87                 if (acl == null)
88                 {
89                     //
90                     // The required ACL was not present; return an empty collection.
91                     //
92                     return result;
93                 }
94 
95                 IdentityReferenceCollection irTarget = null;
96 
97                 if (targetType != typeof(SecurityIdentifier))
98                 {
99                     IdentityReferenceCollection irSource = new IdentityReferenceCollection(acl.Count);
100 
101                     for (int i = 0; i < acl.Count; i++)
102                     {
103                         //
104                         // Calling the indexer on a common ACL results in cloning,
105                         // (which would not be the case if we were to use the internal RawAcl property)
106                         // but also ensures that the resulting order of ACEs is proper
107                         // However, this is a big price to pay - cloning all the ACEs just so that
108                         // the canonical order could be ascertained just once.
109                         // A better way would be to have an internal method that would canonicalize the ACL
110                         // and call it once, then use the RawAcl.
111                         //
112                         CommonAce ace = acl[i] as CommonAce;
113                         if (AceNeedsTranslation(ace, access, includeExplicit, includeInherited))
114                         {
115                             irSource.Add(ace.SecurityIdentifier);
116                         }
117                     }
118 
119                     irTarget = irSource.Translate(targetType);
120                 }
121 
122                 int targetIndex = 0;
123                 for (int i = 0; i < acl.Count; i++)
124                 {
125                     //
126                     // Calling the indexer on a common ACL results in cloning,
127                     // (which would not be the case if we were to use the internal RawAcl property)
128                     // but also ensures that the resulting order of ACEs is proper
129                     // However, this is a big price to pay - cloning all the ACEs just so that
130                     // the canonical order could be ascertained just once.
131                     // A better way would be to have an internal method that would canonicalize the ACL
132                     // and call it once, then use the RawAcl.
133                     //
134 
135                     CommonAce ace = acl[i] as CommonAce;
136                     if (AceNeedsTranslation(ace, access, includeExplicit, includeInherited))
137                     {
138                         IdentityReference iref = (targetType == typeof(SecurityIdentifier)) ? ace.SecurityIdentifier : irTarget[targetIndex++];
139 
140                         if (access)
141                         {
142                             AccessControlType type;
143 
144                             if (ace.AceQualifier == AceQualifier.AccessAllowed)
145                             {
146                                 type = AccessControlType.Allow;
147                             }
148                             else
149                             {
150                                 type = AccessControlType.Deny;
151                             }
152 
153                             result.AddRule(
154                                 AccessRuleFactory(
155                                     iref,
156                                     ace.AccessMask,
157                                     ace.IsInherited,
158                                     ace.InheritanceFlags,
159                                     ace.PropagationFlags,
160                                     type));
161                         }
162                         else
163                         {
164                             result.AddRule(
165                                 AuditRuleFactory(
166                                     iref,
167                                     ace.AccessMask,
168                                     ace.IsInherited,
169                                     ace.InheritanceFlags,
170                                     ace.PropagationFlags,
171                                     ace.AuditFlags));
172                         }
173                     }
174                 }
175 
176                 return result;
177             }
178             finally
179             {
180                 ReadUnlock();
181             }
182         }
183 
AceNeedsTranslation(CommonAce ace, bool isAccessAce, bool includeExplicit, bool includeInherited)184         private bool AceNeedsTranslation(CommonAce ace, bool isAccessAce, bool includeExplicit, bool includeInherited)
185         {
186             if (ace == null)
187             {
188                 //
189                 // Only consider common ACEs
190                 //
191 
192                 return false;
193             }
194 
195             if (isAccessAce)
196             {
197                 if (ace.AceQualifier != AceQualifier.AccessAllowed &&
198                     ace.AceQualifier != AceQualifier.AccessDenied)
199                 {
200                     return false;
201                 }
202             }
203             else
204             {
205                 if (ace.AceQualifier != AceQualifier.SystemAudit)
206                 {
207                     return false;
208                 }
209             }
210 
211             if ((includeExplicit &&
212                 ((ace.AceFlags & AceFlags.Inherited) == 0)) ||
213                 (includeInherited &&
214                 ((ace.AceFlags & AceFlags.Inherited) != 0)))
215             {
216                 return true;
217             }
218 
219             return false;
220         }
221 
222         //
223         // Modifies the DACL
224         //
ModifyAccess(AccessControlModification modification, AccessRule rule, out bool modified)225         protected override bool ModifyAccess(AccessControlModification modification, AccessRule rule, out bool modified)
226         {
227             if (rule == null)
228             {
229                 throw new ArgumentNullException(nameof(rule));
230             }
231 
232             WriteLock();
233             try
234             {
235                 bool result = true;
236 
237                 if (_securityDescriptor.DiscretionaryAcl == null)
238                 {
239                     if (modification == AccessControlModification.Remove ||
240                         modification == AccessControlModification.RemoveAll ||
241                         modification == AccessControlModification.RemoveSpecific)
242                     {
243                         modified = false;
244                         return result;
245                     }
246 
247                     _securityDescriptor.DiscretionaryAcl = new DiscretionaryAcl(IsContainer, IsDS, GenericAcl.AclRevision, 1);
248                     _securityDescriptor.AddControlFlags(ControlFlags.DiscretionaryAclPresent);
249                 }
250 
251                 SecurityIdentifier sid = rule.IdentityReference.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
252 
253                 if (rule.AccessControlType == AccessControlType.Allow)
254                 {
255                     switch (modification)
256                     {
257                         case AccessControlModification.Add:
258                             _securityDescriptor.DiscretionaryAcl.AddAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
259                             break;
260 
261                         case AccessControlModification.Set:
262                             _securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
263                             break;
264 
265                         case AccessControlModification.Reset:
266                             _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, -1, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, 0);
267                             _securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
268                             break;
269 
270                         case AccessControlModification.Remove:
271                             result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
272                             break;
273 
274                         case AccessControlModification.RemoveAll:
275                             result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, -1, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, 0);
276                             if (result == false)
277                             {
278                                 Debug.Assert(false, "Invalid operation");
279                                 throw new InvalidOperationException();
280                             }
281 
282                             break;
283 
284                         case AccessControlModification.RemoveSpecific:
285                             _securityDescriptor.DiscretionaryAcl.RemoveAccessSpecific(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
286                             break;
287 
288                         default:
289                             throw new ArgumentOutOfRangeException(
290 nameof(modification),
291                                 SR.ArgumentOutOfRange_Enum);
292                     }
293                 }
294                 else if (rule.AccessControlType == AccessControlType.Deny)
295                 {
296                     switch (modification)
297                     {
298                         case AccessControlModification.Add:
299                             _securityDescriptor.DiscretionaryAcl.AddAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
300                             break;
301 
302                         case AccessControlModification.Set:
303                             _securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
304                             break;
305 
306                         case AccessControlModification.Reset:
307                             _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, -1, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, 0);
308                             _securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
309                             break;
310 
311                         case AccessControlModification.Remove:
312                             result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
313                             break;
314 
315                         case AccessControlModification.RemoveAll:
316                             result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, -1, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, 0);
317                             if (result == false)
318                             {
319                                 Debug.Assert(false, "Invalid operation");
320                                 throw new InvalidOperationException();
321                             }
322 
323                             break;
324 
325                         case AccessControlModification.RemoveSpecific:
326                             _securityDescriptor.DiscretionaryAcl.RemoveAccessSpecific(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
327                             break;
328 
329                         default:
330                             throw new ArgumentOutOfRangeException(
331 nameof(modification),
332                                 SR.ArgumentOutOfRange_Enum);
333                     }
334                 }
335                 else
336                 {
337                     Debug.Assert(false, "rule.AccessControlType unrecognized");
338                     throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)rule.AccessControlType), "rule.AccessControlType");
339                 }
340 
341                 modified = result;
342                 AccessRulesModified |= modified;
343                 return result;
344             }
345             finally
346             {
347                 WriteUnlock();
348             }
349         }
350 
351         //
352         // Modifies the SACL
353         //
354 
ModifyAudit(AccessControlModification modification, AuditRule rule, out bool modified)355         protected override bool ModifyAudit(AccessControlModification modification, AuditRule rule, out bool modified)
356         {
357             if (rule == null)
358             {
359                 throw new ArgumentNullException(nameof(rule));
360             }
361 
362             WriteLock();
363             try
364             {
365                 bool result = true;
366 
367                 if (_securityDescriptor.SystemAcl == null)
368                 {
369                     if (modification == AccessControlModification.Remove ||
370                         modification == AccessControlModification.RemoveAll ||
371                         modification == AccessControlModification.RemoveSpecific)
372                     {
373                         modified = false;
374                         return result;
375                     }
376 
377                     _securityDescriptor.SystemAcl = new SystemAcl(IsContainer, IsDS, GenericAcl.AclRevision, 1);
378                     _securityDescriptor.AddControlFlags(ControlFlags.SystemAclPresent);
379                 }
380 
381                 SecurityIdentifier sid = rule.IdentityReference.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
382 
383                 switch (modification)
384                 {
385                     case AccessControlModification.Add:
386                         _securityDescriptor.SystemAcl.AddAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
387                         break;
388 
389                     case AccessControlModification.Set:
390                         _securityDescriptor.SystemAcl.SetAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
391                         break;
392 
393                     case AccessControlModification.Reset:
394                         _securityDescriptor.SystemAcl.SetAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
395                         break;
396 
397                     case AccessControlModification.Remove:
398                         result = _securityDescriptor.SystemAcl.RemoveAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
399                         break;
400 
401                     case AccessControlModification.RemoveAll:
402                         result = _securityDescriptor.SystemAcl.RemoveAudit(AuditFlags.Failure | AuditFlags.Success, sid, -1, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, 0);
403                         if (result == false)
404                         {
405                             throw new InvalidOperationException();
406                         }
407 
408                         break;
409 
410                     case AccessControlModification.RemoveSpecific:
411                         _securityDescriptor.SystemAcl.RemoveAuditSpecific(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags);
412                         break;
413 
414                     default:
415                         throw new ArgumentOutOfRangeException(
416 nameof(modification),
417                             SR.ArgumentOutOfRange_Enum);
418                 }
419 
420                 modified = result;
421                 AuditRulesModified |= modified;
422                 return result;
423             }
424             finally
425             {
426                 WriteUnlock();
427             }
428         }
429 
430         #endregion
431 
432         #region Protected Methods
433 
434         #endregion
435 
436         #region Public Methods
437 
AddAccessRule(AccessRule rule)438         protected void AddAccessRule(AccessRule rule)
439         {
440             if (rule == null)
441             {
442                 throw new ArgumentNullException(nameof(rule));
443             }
444 
445             WriteLock();
446 
447             try
448             {
449                 bool modified;
450                 ModifyAccess(AccessControlModification.Add, rule, out modified);
451             }
452             finally
453             {
454                 WriteUnlock();
455             }
456         }
457 
SetAccessRule(AccessRule rule)458         protected void SetAccessRule(AccessRule rule)
459         {
460             if (rule == null)
461             {
462                 throw new ArgumentNullException(nameof(rule));
463             }
464 
465             WriteLock();
466 
467             try
468             {
469                 bool modified;
470                 ModifyAccess(AccessControlModification.Set, rule, out modified);
471             }
472             finally
473             {
474                 WriteUnlock();
475             }
476         }
477 
ResetAccessRule(AccessRule rule)478         protected void ResetAccessRule(AccessRule rule)
479         {
480             if (rule == null)
481             {
482                 throw new ArgumentNullException(nameof(rule));
483             }
484 
485             WriteLock();
486 
487             try
488             {
489                 bool modified;
490                 ModifyAccess(AccessControlModification.Reset, rule, out modified);
491             }
492             finally
493             {
494                 WriteUnlock();
495             }
496 
497             return;
498         }
499 
RemoveAccessRule(AccessRule rule)500         protected bool RemoveAccessRule(AccessRule rule)
501         {
502             if (rule == null)
503             {
504                 throw new ArgumentNullException(nameof(rule));
505             }
506 
507             WriteLock();
508 
509             try
510             {
511                 if (_securityDescriptor == null)
512                 {
513                     return true;
514                 }
515 
516                 bool modified;
517                 return ModifyAccess(AccessControlModification.Remove, rule, out modified);
518             }
519             finally
520             {
521                 WriteUnlock();
522             }
523         }
524 
RemoveAccessRuleAll(AccessRule rule)525         protected void RemoveAccessRuleAll(AccessRule rule)
526         {
527             if (rule == null)
528             {
529                 throw new ArgumentNullException(nameof(rule));
530             }
531 
532             WriteLock();
533 
534             try
535             {
536                 if (_securityDescriptor == null)
537                 {
538                     return;
539                 }
540 
541                 bool modified;
542                 ModifyAccess(AccessControlModification.RemoveAll, rule, out modified);
543             }
544             finally
545             {
546                 WriteUnlock();
547             }
548 
549             return;
550         }
551 
RemoveAccessRuleSpecific(AccessRule rule)552         protected void RemoveAccessRuleSpecific(AccessRule rule)
553         {
554             if (rule == null)
555             {
556                 throw new ArgumentNullException(nameof(rule));
557             }
558 
559             WriteLock();
560 
561             try
562             {
563                 if (_securityDescriptor == null)
564                 {
565                     return;
566                 }
567 
568                 bool modified;
569                 ModifyAccess(AccessControlModification.RemoveSpecific, rule, out modified);
570             }
571             finally
572             {
573                 WriteUnlock();
574             }
575         }
576 
AddAuditRule(AuditRule rule)577         protected void AddAuditRule(AuditRule rule)
578         {
579             if (rule == null)
580             {
581                 throw new ArgumentNullException(nameof(rule));
582             }
583 
584             WriteLock();
585 
586             try
587             {
588                 bool modified;
589                 ModifyAudit(AccessControlModification.Add, rule, out modified);
590             }
591             finally
592             {
593                 WriteUnlock();
594             }
595         }
596 
SetAuditRule(AuditRule rule)597         protected void SetAuditRule(AuditRule rule)
598         {
599             if (rule == null)
600             {
601                 throw new ArgumentNullException(nameof(rule));
602             }
603 
604             WriteLock();
605 
606             try
607             {
608                 bool modified;
609                 ModifyAudit(AccessControlModification.Set, rule, out modified);
610             }
611             finally
612             {
613                 WriteUnlock();
614             }
615         }
616 
RemoveAuditRule(AuditRule rule)617         protected bool RemoveAuditRule(AuditRule rule)
618         {
619             if (rule == null)
620             {
621                 throw new ArgumentNullException(nameof(rule));
622             }
623 
624             WriteLock();
625 
626             try
627             {
628                 bool modified;
629                 return ModifyAudit(AccessControlModification.Remove, rule, out modified);
630             }
631             finally
632             {
633                 WriteUnlock();
634             }
635         }
636 
RemoveAuditRuleAll(AuditRule rule)637         protected void RemoveAuditRuleAll(AuditRule rule)
638         {
639             if (rule == null)
640             {
641                 throw new ArgumentNullException(nameof(rule));
642             }
643 
644             WriteLock();
645 
646             try
647             {
648                 bool modified;
649                 ModifyAudit(AccessControlModification.RemoveAll, rule, out modified);
650             }
651             finally
652             {
653                 WriteUnlock();
654             }
655         }
656 
RemoveAuditRuleSpecific(AuditRule rule)657         protected void RemoveAuditRuleSpecific(AuditRule rule)
658         {
659             if (rule == null)
660             {
661                 throw new ArgumentNullException(nameof(rule));
662             }
663 
664             WriteLock();
665 
666             try
667             {
668                 bool modified;
669                 ModifyAudit(AccessControlModification.RemoveSpecific, rule, out modified);
670             }
671             finally
672             {
673                 WriteUnlock();
674             }
675         }
676 
GetAccessRules(bool includeExplicit, bool includeInherited, System.Type targetType)677         public AuthorizationRuleCollection GetAccessRules(bool includeExplicit, bool includeInherited, System.Type targetType)
678         {
679             return GetRules(true, includeExplicit, includeInherited, targetType);
680         }
681 
GetAuditRules(bool includeExplicit, bool includeInherited, System.Type targetType)682         public AuthorizationRuleCollection GetAuditRules(bool includeExplicit, bool includeInherited, System.Type targetType)
683         {
684             return GetRules(false, includeExplicit, includeInherited, targetType);
685         }
686         #endregion
687     }
688 }
689