|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Classes: DirectoryObjectSecurity class
**
**
===========================================================*/
using Microsoft.Win32;
using System;
using System.Collections;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
namespace System.Security.AccessControl
{
public abstract class DirectoryObjectSecurity: ObjectSecurity
{
#region Constructors
protected DirectoryObjectSecurity()
: base(true, true)
{
return;
}
protected DirectoryObjectSecurity(CommonSecurityDescriptor securityDescriptor)
: base(securityDescriptor)
{
return;
}
#endregion
#region Private Methods
private AuthorizationRuleCollection GetRules(bool access, bool includeExplicit, bool includeInherited, System.Type targetType)
{
ReadLock();
try
{
AuthorizationRuleCollection result = new AuthorizationRuleCollection();
if (!SecurityIdentifier.IsValidTargetTypeStatic(targetType))
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeIdentityReferenceType"), "targetType");
}
CommonAcl acl = null;
if (access)
{
if ((_securityDescriptor.ControlFlags & ControlFlags.DiscretionaryAclPresent) != 0)
{
acl = _securityDescriptor.DiscretionaryAcl;
}
}
else // !access == audit
{
if ((_securityDescriptor.ControlFlags & ControlFlags.SystemAclPresent) != 0)
{
acl = _securityDescriptor.SystemAcl;
}
}
if (acl == null)
{
//
// The required ACL was not present; return an empty collection.
//
return result;
}
IdentityReferenceCollection irTarget = null;
if (targetType != typeof(SecurityIdentifier ))
{
IdentityReferenceCollection irSource = new IdentityReferenceCollection(acl.Count);
for (int i = 0; i < acl.Count; i++)
{
//
// Calling the indexer on a common ACL results in cloning,
// (which would not be the case if we were to use the internal RawAcl property)
// but also ensures that the resulting order of ACEs is proper
// However, this is a big price to pay - cloning all the ACEs just so that
// the canonical order could be ascertained just once.
// A better way would be to have an internal method that would canonicalize the ACL
// and call it once, then use the RawAcl.
//
QualifiedAce ace = acl[i] as QualifiedAce;
if (ace == null)
{
//
// Only consider qualified ACEs
//
continue;
}
if (ace.IsCallback == true)
{
//
// Ignore callback ACEs
//
continue;
}
if (access)
{
if (ace.AceQualifier != AceQualifier.AccessAllowed && ace.AceQualifier != AceQualifier.AccessDenied)
{
continue;
}
}
else
{
if (ace.AceQualifier != AceQualifier.SystemAudit)
{
continue;
}
}
irSource.Add( ace.SecurityIdentifier );
}
irTarget = irSource.Translate(targetType);
}
for (int i = 0; i < acl.Count; i++)
{
//
// Calling the indexer on a common ACL results in cloning,
// (which would not be the case if we were to use the internal RawAcl property)
// but also ensures that the resulting order of ACEs is proper
// However, this is a big price to pay - cloning all the ACEs just so that
// the canonical order could be ascertained just once.
// A better way would be to have an internal method that would canonicalize the ACL
// and call it once, then use the RawAcl.
//
QualifiedAce ace = acl[i] as CommonAce;
if (ace == null)
{
ace = acl[i] as ObjectAce;
if (ace == null)
{
//
// Only consider common or object ACEs
//
continue;
}
}
if (ace.IsCallback == true)
{
//
// Ignore callback ACEs
//
continue;
}
if (access)
{
if (ace.AceQualifier != AceQualifier.AccessAllowed && ace.AceQualifier != AceQualifier.AccessDenied)
{
continue;
}
}
else
{
if (ace.AceQualifier != AceQualifier.SystemAudit)
{
continue;
}
}
if ((includeExplicit && ((ace.AceFlags & AceFlags.Inherited) == 0)) || (includeInherited && ((ace.AceFlags & AceFlags.Inherited) != 0)))
{
IdentityReference iref = (targetType == typeof(SecurityIdentifier )) ? ace.SecurityIdentifier : irTarget[i];
if (access)
{
AccessControlType type;
if (ace.AceQualifier == AceQualifier.AccessAllowed)
{
type = AccessControlType.Allow;
}
else
{
type = AccessControlType.Deny;
}
if (ace is ObjectAce)
{
ObjectAce objectAce = ace as ObjectAce;
result.AddRule(AccessRuleFactory(iref, objectAce.AccessMask, objectAce.IsInherited, objectAce.InheritanceFlags, objectAce.PropagationFlags, type, objectAce.ObjectAceType, objectAce.InheritedObjectAceType));
}
else
{
CommonAce commonAce = ace as CommonAce;
if (commonAce == null)
{
continue;
}
result.AddRule(AccessRuleFactory(iref, commonAce.AccessMask, commonAce.IsInherited, commonAce.InheritanceFlags, commonAce.PropagationFlags, type));
}
}
else
{
if (ace is ObjectAce)
{
ObjectAce objectAce = ace as ObjectAce;
result.AddRule(AuditRuleFactory(iref, objectAce.AccessMask, objectAce.IsInherited, objectAce.InheritanceFlags, objectAce.PropagationFlags, objectAce.AuditFlags, objectAce.ObjectAceType, objectAce.InheritedObjectAceType));
}
else
{
CommonAce commonAce = ace as CommonAce;
if (commonAce == null)
{
continue;
}
result.AddRule(AuditRuleFactory(iref, commonAce.AccessMask, commonAce.IsInherited, commonAce.InheritanceFlags, commonAce.PropagationFlags, commonAce.AuditFlags));
}
}
}
}
return result;
}
finally
{
ReadUnlock();
}
}
//
// Modifies the DACL
//
private bool ModifyAccess(AccessControlModification modification, ObjectAccessRule rule, out bool modified)
{
bool result = true;
if (_securityDescriptor.DiscretionaryAcl == null)
{
if (modification == AccessControlModification.Remove || modification == AccessControlModification.RemoveAll || modification == AccessControlModification.RemoveSpecific)
{
modified = false;
return result;
}
_securityDescriptor.DiscretionaryAcl = new DiscretionaryAcl(IsContainer, IsDS, GenericAcl.AclRevisionDS, 1);
_securityDescriptor.AddControlFlags(ControlFlags.DiscretionaryAclPresent);
}
else if ((modification == AccessControlModification.Add || modification == AccessControlModification.Set || modification == AccessControlModification.Reset ) &&
( rule.ObjectFlags != ObjectAceFlags.None ))
{
//
// This will result in an object ace being added to the dacl, so the dacl revision must be AclRevisionDS
//
if ( _securityDescriptor.DiscretionaryAcl.Revision < GenericAcl.AclRevisionDS )
{
//
// we need to create a new dacl with the same aces as the existing one but the revision should be AclRevisionDS
//
byte[] binaryForm = new byte[_securityDescriptor.DiscretionaryAcl.BinaryLength];
_securityDescriptor.DiscretionaryAcl.GetBinaryForm(binaryForm, 0);
binaryForm[0] = GenericAcl.AclRevisionDS; // revision is the first byte of the binary form
_securityDescriptor.DiscretionaryAcl = new DiscretionaryAcl(IsContainer, IsDS, new RawAcl(binaryForm, 0));
}
}
SecurityIdentifier sid = rule.IdentityReference.Translate(typeof(SecurityIdentifier )) as SecurityIdentifier;
if (rule.AccessControlType == AccessControlType.Allow)
{
switch (modification)
{
case AccessControlModification.Add :
_securityDescriptor.DiscretionaryAcl.AddAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Set :
_securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Reset :
_securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
_securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Remove :
result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.RemoveAll :
result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
if (result == false)
{
Contract.Assert(false, "Invalid operation");
throw new SystemException();
}
break;
case AccessControlModification.RemoveSpecific :
_securityDescriptor.DiscretionaryAcl.RemoveAccessSpecific(AccessControlType.Allow, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
default :
throw new ArgumentOutOfRangeException(
"modification",
Environment.GetResourceString( "ArgumentOutOfRange_Enum" ));
}
}
else if (rule.AccessControlType == AccessControlType.Deny)
{
switch (modification)
{
case AccessControlModification.Add :
_securityDescriptor.DiscretionaryAcl.AddAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Set :
_securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Reset :
_securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
_securityDescriptor.DiscretionaryAcl.SetAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Remove :
result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.RemoveAll :
result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
if (result == false)
{
Contract.Assert(false, "Invalid operation");
throw new SystemException();
}
break;
case AccessControlModification.RemoveSpecific :
_securityDescriptor.DiscretionaryAcl.RemoveAccessSpecific(AccessControlType.Deny, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
default :
throw new ArgumentOutOfRangeException(
"modification",
Environment.GetResourceString( "ArgumentOutOfRange_Enum" ));
}
}
else
{
Contract.Assert(false, "rule.AccessControlType unrecognized");
throw new SystemException();
}
modified = result;
AccessRulesModified |= modified;
return result;
}
//
// Modifies the SACL
//
private bool ModifyAudit(AccessControlModification modification, ObjectAuditRule rule, out bool modified)
{
bool result = true;
if (_securityDescriptor.SystemAcl == null)
{
if (modification == AccessControlModification.Remove || modification == AccessControlModification.RemoveAll || modification == AccessControlModification.RemoveSpecific)
{
modified = false;
return result;
}
_securityDescriptor.SystemAcl = new SystemAcl(IsContainer, IsDS, GenericAcl.AclRevisionDS, 1);
_securityDescriptor.AddControlFlags(ControlFlags.SystemAclPresent);
}
else if ((modification == AccessControlModification.Add || modification == AccessControlModification.Set || modification == AccessControlModification.Reset ) &&
( rule.ObjectFlags != ObjectAceFlags.None ))
{
//
// This will result in an object ace being added to the sacl, so the sacl revision must be AclRevisionDS
//
if ( _securityDescriptor.SystemAcl.Revision < GenericAcl.AclRevisionDS )
{
//
// we need to create a new sacl with the same aces as the existing one but the revision should be AclRevisionDS
//
byte[] binaryForm = new byte[_securityDescriptor.SystemAcl.BinaryLength];
_securityDescriptor.SystemAcl.GetBinaryForm(binaryForm, 0);
binaryForm[0] = GenericAcl.AclRevisionDS; // revision is the first byte of the binary form
_securityDescriptor.SystemAcl = new SystemAcl(IsContainer, IsDS, new RawAcl(binaryForm, 0));
}
}
SecurityIdentifier sid = rule.IdentityReference.Translate(typeof(SecurityIdentifier )) as SecurityIdentifier;
switch (modification)
{
case AccessControlModification.Add :
_securityDescriptor.SystemAcl.AddAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Set :
_securityDescriptor.SystemAcl.SetAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Reset :
_securityDescriptor.SystemAcl.RemoveAudit(AuditFlags.Failure | AuditFlags.Success, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
_securityDescriptor.SystemAcl.SetAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.Remove :
result = _securityDescriptor.SystemAcl.RemoveAudit(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
case AccessControlModification.RemoveAll :
result = _securityDescriptor.SystemAcl.RemoveAudit(AuditFlags.Failure | AuditFlags.Success, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty);
if (result == false)
{
Contract.Assert(false, "Invalid operation");
throw new SystemException();
}
break;
case AccessControlModification.RemoveSpecific :
_securityDescriptor.SystemAcl.RemoveAuditSpecific(rule.AuditFlags, sid, rule.AccessMask, rule.InheritanceFlags, rule.PropagationFlags, rule.ObjectFlags, rule.ObjectType, rule.InheritedObjectType);
break;
default :
throw new ArgumentOutOfRangeException(
"modification",
Environment.GetResourceString( "ArgumentOutOfRange_Enum" ));
}
modified = result;
AuditRulesModified |= modified;
return result;
}
#endregion
#region public Methods
public virtual AccessRule AccessRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type, Guid objectType, Guid inheritedObjectType)
{
throw new NotImplementedException();
}
public virtual AuditRule AuditRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags, Guid objectType, Guid inheritedObjectType)
{
throw new NotImplementedException();
}
protected override bool ModifyAccess(AccessControlModification modification, AccessRule rule, out bool modified)
{
if ( !this.AccessRuleType.IsAssignableFrom(rule.GetType()) )
{
throw new ArgumentException(
Environment.GetResourceString("AccessControl_InvalidAccessRuleType"),
"rule");
}
Contract.EndContractBlock();
return ModifyAccess(modification, rule as ObjectAccessRule, out modified);
}
protected override bool ModifyAudit(AccessControlModification modification, AuditRule rule, out bool modified)
{
if ( !this.AuditRuleType.IsAssignableFrom(rule.GetType()) )
{
throw new ArgumentException(
Environment.GetResourceString("AccessControl_InvalidAuditRuleType"),
"rule");
}
Contract.EndContractBlock();
return ModifyAudit(modification, rule as ObjectAuditRule, out modified);
}
#endregion
#region Public Methods
protected void AddAccessRule(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAccess(AccessControlModification.Add, rule, out modified);
}
finally
{
WriteUnlock();
}
return;
}
protected void SetAccessRule(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAccess(AccessControlModification.Set, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void ResetAccessRule(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAccess(AccessControlModification.Reset, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected bool RemoveAccessRule(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
if (_securityDescriptor == null)
{
return true;
}
bool modified;
return ModifyAccess(AccessControlModification.Remove, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void RemoveAccessRuleAll(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
if (_securityDescriptor == null)
{
return;
}
bool modified;
ModifyAccess(AccessControlModification.RemoveAll, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void RemoveAccessRuleSpecific(ObjectAccessRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
if (_securityDescriptor == null)
{
return;
}
WriteLock();
try
{
bool modified;
ModifyAccess(AccessControlModification.RemoveSpecific, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void AddAuditRule(ObjectAuditRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAudit(AccessControlModification.Add, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void SetAuditRule(ObjectAuditRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAudit(AccessControlModification.Set, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected bool RemoveAuditRule(ObjectAuditRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
return ModifyAudit(AccessControlModification.Remove, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void RemoveAuditRuleAll(ObjectAuditRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAudit(AccessControlModification.RemoveAll, rule, out modified);
}
finally
{
WriteUnlock();
}
}
protected void RemoveAuditRuleSpecific(ObjectAuditRule rule)
{
if (rule == null)
{
throw new ArgumentNullException("rule");
}
Contract.EndContractBlock();
WriteLock();
try
{
bool modified;
ModifyAudit(AccessControlModification.RemoveSpecific, rule, out modified);
}
finally
{
WriteUnlock();
}
}
public AuthorizationRuleCollection GetAccessRules(bool includeExplicit, bool includeInherited, System.Type targetType)
{
return GetRules(true, includeExplicit, includeInherited, targetType);
}
public AuthorizationRuleCollection GetAuditRules(bool includeExplicit, bool includeInherited, System.Type targetType)
{
return GetRules(false, includeExplicit, includeInherited, targetType);
}
#endregion
}
}
|