使用 Powershell 为域组设置文件夹权限时收到 "Incorrectly ordered" 错误消息

Getting "Incorrectly ordered" error message when setting a folder permission to a domain group using Powershell

我正在尝试通过 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

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

The permissions on [folder name] are incorrectly ordered, which may cause some entries to be ineffective.

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

访问规则 (ACE) 需要 ordered in a certain way 在 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
}

并像这样使用它:

Repair-DirectoryPermissions -Path 'D:\Blah'

执行此操作时,您可能会遇到异常,告诉您需要 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
        }
    }
}

并调用两个函数:

Enable-Privilege -Privilege SeSecurityPrivilege
Repair-DirectoryPermissions -Path 'D:\Blah'