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