使用 Powershell 为域组设置文件夹权限时收到“错误排序”错误消息

ehh*_*ehh 0 powershell acl file-permissions ntfs

我正在尝试通过 Powershell 为文件夹设置权限以下是代码:

$acl = Get-Acl $folderPath
$acl.SetAccessRuleProtection($True, $True)

$ruleOwner = New-Object System.Security.AccessControl.FileSystemAccessRule($group,"Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
$acl.AddAccessRule($ruleOwner)

Set-Acl $folderPath $acl
Run Code Online (Sandbox Code Playgroud)

运行此代码并尝试打开相关文件夹的“安全”选项卡后,我收到错误消息:

[文件夹名称]的权限排序不正确,可能会导致某些条目无效。

将文件夹权限设置为特定组的正确方法是什么?

The*_*heo 5

访问规则 (ACE) 需要ACL 内以某种方式排序。基本上,顺序是

  1. 所有显式 ACE 都放置在任何继承的 ACE 之前的组中。
  2. 在显式 ACE 组中,拒绝访问的 ACE 放置在允许访问的 ACE 之前。
  3. 继承的 ACE 按照它们被继承的顺序放置。首先是从子对象的父对象继承的 ACE,然后是从祖父对象继承的 ACE,依此类推。
  4. 对于每一级继承的 ACE,拒绝访问的 ACE 放置在允许访问的 ACE 之前。

如果此顺序以某种方式混淆,您将看到“权限顺序错误”错误消息。

要重新排列权限的顺序,您可以使用以下函数:

function Repair-DirectoryPermissions {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateScript({Test-Path $_ -PathType Container})]
        [string]$Path
    )

    $acl = Get-Acl -Path $Path
    # create a new empty ACL object
    $newAcl = New-Object System.Security.AccessControl.DirectorySecurity

    # copy the access rules from the existing ACL to the new one in the correct order
    # first the explicit DENY rules
    $acl.Access | Where-Object { !$_.IsInherited -and $_.AccessControlType -eq 'Deny' } | ForEach-Object {
        $newAcl.AddAccessRule($_)
    }
    # next the explicit ALLOW rules
    $acl.Access | Where-Object { !$_.IsInherited -and $_.AccessControlType -eq 'Allow' } | ForEach-Object {
        $newAcl.AddAccessRule($_)
    }
    # finally the inherited rules
    $acl.Access | Where-Object { $_.IsInherited } | ForEach-Object {
        $newAcl.AddAccessRule($_)
    }

    # set the the reordered ACL to the directory object
    Set-Acl -Path $Path -AclObject $newAcl
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

Repair-DirectoryPermissions -Path 'D:\Blah'
Run Code Online (Sandbox Code Playgroud)

执行此操作时,您可能会收到一个异常,告诉您需要SeSecurityPrivilege获得执行此操作的权限。
为此,请在脚本之上添加另一个函数:

function Enable-Privilege {
    [CmdletBinding(ConfirmImpact = 'low', SupportsShouldProcess = $false)]  
    [OutputType('System.Boolean')]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateSet(
            "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege", "SeChangeNotifyPrivilege", 
            "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege", "SeCreatePermanentPrivilege", 
            "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege", "SeDebugPrivilege", "SeEnableDelegationPrivilege", 
            "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege", "SeIncreaseQuotaPrivilege", 
            "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege", "SeLockMemoryPrivilege", 
            "SeMachineAccountPrivilege", "SeManageVolumePrivilege", "SeProfileSingleProcessPrivilege", 
            "SeRelabelPrivilege", "SeRemoteShutdownPrivilege", "SeRestorePrivilege", "SeSecurityPrivilege", 
            "SeShutdownPrivilege", "SeSyncAgentPrivilege", "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", 
            "SeSystemtimePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", 
            "SeTrustedCredManAccessPrivilege", "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
        [String]$Privilege,

        [Parameter(Position = 1)]
        $ProcessId = $PID,

        [switch]$Disable
        )

    begin {
        Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;

public class Privilege {
    [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("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_ENABLED    = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED   = 0x00000000;
    internal const int TOKEN_QUERY             = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

    public static bool EnablePrivilege(long processHandle, string privilege, bool disable) {
        bool retVal;
        TokPriv1Luid tp;
        IntPtr hproc = new IntPtr(processHandle);
        IntPtr htok = IntPtr.Zero;
        retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
        tp.Count = 1;
        tp.Luid = 0;
        if(disable) { tp.Attr = SE_PRIVILEGE_DISABLED; }
        else { 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;
    }
}
'@
    }
    process {
        try {
            $proc   = Get-Process -Id $ProcessId -ErrorAction Stop
            $name   = $proc.ProcessName
            $handle = $proc.Handle
            $action = if ($Disable) { 'Disabling' } else { 'Enabling' }
            Write-Verbose ("{0} '{1}' for process {2}" -f $action, $Privilege, $name)
            [Privilege]::EnablePrivilege($handle, $Privilege, [bool]$Disable)
        }
        catch {
            throw
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并调用这两个函数:

Enable-Privilege -Privilege SeSecurityPrivilege
Repair-DirectoryPermissions -Path 'D:\Blah'
Run Code Online (Sandbox Code Playgroud)