We have want to clear an NTFS ACL
of a folder completely and only have the ACL
populated with the ACE
Builtin\Administrator : Full Control
. The code below works fine for most folders but fails on one specific folder:
$Path = 'E:\DEPARTMENTS\Gemensam'
$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$AdminFullControlAce = New-Object System.Security.AccessControl.FileSystemAccessRule(
$BuiltinAdmin,
[System.Security.AccessControl.FileSystemRights]::FullControl,
[System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow
)
(Get-Acl $Path).Access
$FolderItem = Get-Item -Path $Path -EA Stop
$Acl = $FolderItem.GetAccessControl()
Write-Verbose 'Set owner'
$Acl.SetOwner($BuiltinAdmin)
$FolderItem.SetAccessControl($Acl)
Write-Verbose 'Disable inheritance'
$Acl = $FolderItem.GetAccessControl()
$Acl.SetAccessRuleProtection($True, $False)
$FolderItem.SetAccessControl($Acl)
Write-Verbose 'Remove all ACEs from the ACL'
$Acl = $FolderItem.GetAccessControl()
$Acl.Access.ForEach({$Acl.RemoveAccessRule($_)})
Write-Verbose 'Add Admin and set the new ACL'
$acl.AddAccessRule($AdminFullControlAce)
$FolderItem.SetAccessControl($Acl)
Write-Verbose 'ACL corrected'
(Get-Acl $Path).Access
The output of this code is:
FileSystemRights : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited : False
InheritanceFlags : ContainerInherit, ObjectInherit
PropagationFlags : None
FileSystemRights : Modify, Synchronize
AccessControlType : Allow
IdentityReference : GROUPHC\SWE CEM KVB EV
IsInherited : False
InheritanceFlags : ContainerInherit, ObjectInherit
PropagationFlags : None
VERBOSE: Set owner
VERBOSE: Disable inheritance
VERBOSE: Remove all ACEs from the ACL
True
True
VERBOSE: Add Admin and set the new ACL
VERBOSE: ACL corrected
FileSystemRights : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited : False
InheritanceFlags : ContainerInherit, ObjectInherit
PropagationFlags : None
FileSystemRights : Modify, Synchronize
AccessControlType : Allow
IdentityReference : GROUPHC\SWE CEM KVB EV
IsInherited : False
InheritanceFlags : ContainerInherit, ObjectInherit
PropagationFlags : None
For one reason or another it seems to be impossible to remove the ACE
of GROUPHC\SWE CEM KVB EV
. Even with Get-ACL
and Set-ACL
it doesn't work. We've also tried to push the ACL
after every change as indicated here, but that doesn't work either. According to the docs the inheritance is properly removed, so it can't be an inherited ACE
.
Any help would be much appreciated.
To avoid issues with ownership we run the following code first:
#region Get super powers
$AdjustTokenPrivileges = @"
using System;
using System.Runtime.InteropServices;
public class TokenManipulator
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr
phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name,
ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool AddPrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
catch (Exception ex)
{
throw ex;
}
}
public static bool RemovePrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_DISABLED;
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
catch (Exception ex)
{
throw ex;
}
}
}
"@
Try {
Write-Verbose 'Get super powers'
Add-Type $AdjustTokenPrivileges
[void][TokenManipulator]::AddPrivilege('SeRestorePrivilege')
[void][TokenManipulator]::AddPrivilege('SeBackupPrivilege')
[void][TokenManipulator]::AddPrivilege('SeTakeOwnershipPrivilege')
}
Catch {
throw "Failed getting super powers: $_"
}
#endregion
See my comment above:
Import-Module PSCX
Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeRestorePrivilege", $true) # Necessary to set Owner Permissions
Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeBackupPrivilege", $true) # Necessary to bypass Traverse Checking
Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeTakeOwnershipPrivilege", $true) # Necessary to override FilePermissions & take Ownership
Now use Get/Set ACL commands recursively on the target
Thanks to Theo from the comments we found a workaround for the problem. We just have to create an empty ACL
and then add our properties to the object, after that it can simply be applied:
$Path = 'E:\DEPARTMENTS\Gemensam'
$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$AdminFullControlAce = New-Object System.Security.AccessControl.FileSystemAccessRule(
$BuiltinAdmin,
[System.Security.AccessControl.FileSystemRights]::FullControl,
[System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow
)
#region Create new ACL
$NewAcl = New-Object System.Security.AccessControl.DirectorySecurity
$NewAcl.SetOwner($BuiltinAdmin)
$NewAcl.SetAccessRuleProtection($true,$false)
$NewAcl.AddAccessRule($AdminFullControlAce)
#endregion
$FolderItem = Get-Item -Path $Path -EA Stop
$FolderItem.SetAccessControl($NewAcl)
$FolderAcl = $FolderItem.GetAccessControl()
$FolderAcl.Access | select IdentityReference, FileSystemRights
Or apply the ACL twice:
$M = Get-Item -Path 'C:\YourFolder'
$Acl = $M.GetAccessControl()
$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$InheritedDirAcl = New-Object System.Security.AccessControl.DirectorySecurity
$InheritedDirAcl.SetOwner($BuiltinAdmin)
$InheritedDirAcl.SetAccessRuleProtection($false,$false)
# This is a workaround for non inherited permissions
# that do not get removed when simply applying the new ACL
$Acl.Access | ForEach-Object {
$Acl.RemoveAccessRuleSpecific($_)
}
$M.SetAccessControl($Acl)
$M.SetAccessControl($InheritedDirAcl)
User contributions licensed under CC BY-SA 3.0