This can be done in PowerShell. I have a module here that has a function named ‘Repair-AclCanonicalOrder’. It’s slow, but it should work until version 4.0 is ready (just don’t try it on AD objects).
If you don’t want to use the module, you can still use some native PowerShell to try to fix this:
function RepairAclCanonicalOrder {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
param(
[Parameter(ValueFromPipeline=$true)]
[System.Security.AccessControl.FileSystemSecurity] $Acl
)
process {
if ($Acl.AreAccessRulesCanonical) {
Write-Verbose "Rules are already in canonical order for '$(Convert-Path $Acl.Path)'"
return
}
Write-Verbose "Working on '$(Convert-Path $Acl.Path)'"
# Convert ACL to a raw security descriptor:
$RawSD = New-Object System.Security.AccessControl.RawSecurityDescriptor($Acl.Sddl)
# Create a new, empty DACL
$NewDacl = New-Object System.Security.AccessControl.RawAcl(
[System.Security.AccessControl.RawAcl]::AclRevision,
$RawSD.DiscretionaryAcl.Count # Capacity of ACL
)
# Put in reverse canonical order and insert each ACE (I originally had a different method that
# preserved the order as much as it could, but that order isn't preserved later when we put this
# back into a DirectorySecurity object, so I went with this shorter command)
$RawSD.DiscretionaryAcl | Sort-Object @{E={$_.IsInherited}; Descending=$true}, AceQualifier | ForEach-Object {
$NewDacl.InsertAce(0, $_)
}
# Replace the DACL with the re-ordered one
$RawSD.DiscretionaryAcl = $NewDacl
# Commit those changes back to the original SD object (but not to disk yet):
$Acl.SetSecurityDescriptorSddlForm($RawSD.GetSddlForm("Access"))
# Commit changes to disk
if ($PSCmdlet.ShouldProcess(
"Saved the ACL for '$(Convert-Path $Acl.Path)'",
"Save the ACL for '$(Convert-Path $Acl.Path)'?",
"Saving ACLs"
)) {
$Acl | Set-Acl
}
}
}
# Use the function like this:
Get-Acl $PathToBadFileOrFolder | RepairAclCanonicalOrder -Verbose
Give that a shot and let me know if it works.
By the way, if anyone wants to test this out themselves, here’s some code that will give you a DACL that is not in canonical order:
# First, create two folders:
$Parent = New-Item -Path "$env:temp\break_canonical_order" -ItemType directory -Force
$Child = New-Item -Path "$Parent\child_folder" -ItemType directory -Force
# Add a deny ACE on the parent:
$ParentAcl = Get-Acl $Parent
$ParentAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
"Guest",
"Write",
"ContainerInherit, ObjectInherit",
"None",
"Deny"
)))
$ParentAcl | Set-Acl
# Add an allow ACE on the child:
$ChildAcl = Get-Acl $Child
$ChildAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
"Users",
"Read",
"ContainerInherit, ObjectInherit",
"None",
"Allow"
)))
# Disable inheritance on the child:
$ChildAcl.SetAccessRuleProtection($true, $true)
$ChildAcl | Set-Acl
# Confirm DACL is not in canonical order:
Get-Acl $Child | Format-List Path, Owner, AccessToString, AreAccessRulesCanonical