首页 > 解决方案 > 使用 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

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

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

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

标签: powershellfile-permissionsaclntfs

解决方案


访问规则 (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
}

并像这样使用它:

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'

推荐阅读