Windows PowerShell每周提示(41):与安全描述符共事
对很多人来说,安全描述符总是象征了系统管理脚本的圣地:被神秘和神话所覆盖,安全描述符是每个人所向往但又不期待亲自去理解的。
这很有趣,因为使用脚本来管理安全描述符并非是不可能。毕竟,WMI已经拥有这个能力好几年了,在Windows PowerShell最早的发布版本中也引入了名为Set-ACL的cmdlet。处理安全描述符有什么大不了的?
好的,问题是事实上有些理论上的可能性并不具备实际操作性。你能用WMI管理安全描述符么?当然,尽管这不是为心理软弱之人所准备的。好的,你能使用Set-ACL配置安全描述符么?当然,但是默认情况下,这只适合于你手动配置了另一个对象(比如,一个文件)的安全设置然后将这些设置复制给新的对象。下面是从PowerShell帮助文件中摘取的一个例子:
| $DogACL = get-acl c:\dog.txt set-acl -path C:\cat.txt -AclObject $DogACL |
你从Dog.txt文件中得到安全描述符然后将它们复制给Cat.txt。这很有用,但是这和很多人想的不同。
那么有没有什么方法能使你使用脚本来简单的配置安全描述符?好的,事实证明,有的,只要你愿意使用Windows PowerShell,只要你愿意一头扎进.NET Framework中:
| $colRights = [System.Security.AccessControl.FileSystemRights]"Read, Write" $InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None $objType =[System.Security.AccessControl.AccessControlType]::Allow $objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer") $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $objACL = Get-ACL "C:\Scripts\Test.ps1" $objACL.AddAccessRule($objACE) Set-ACL "C:\Scripts\Test.ps1" $objACL |
就算第一眼看上去这不是一个使你能从脚本中简单配置安全描述符的解决方法。但是请先多多包涵,你也许改变对这个脚本的想法,尤其是在我们解释了这个脚本是如何工作的,及这脚本中的多少代码是你用来管理安全描述符是的标准代码。
再次重申,也许你不会改变的你的想法。但是过一会我们就能发现的。
让我们先从脚本的起始处开始……等等,让我们暂时先不要讨论脚本。在我们深入之前,需要注意的是,脚本专家在处理文件及文件夹的安全描述符上所花的时间并不多。那么能否保证该示例脚本在所有情况都能工作?不能。能否保证该示例脚本不会将已有文件或文件夹的安全权限弄得乱七八糟?不能。能否保证该示例脚本不会在时空连续体中撕裂出一个缺口并将你吸入黑洞之中?不能。我们向你展示的示例脚本对我们来说运行正常,但是这也是我们所能说的全部内容。该示例脚本所带来的潜在风险将由你自己承担,并且希望你能明智地使用它。(比如,在测试机上使用,直到你确信它不会给你带来任何问题)。这将让你的心情放松,不是么?
好的,让我们从头开始。在代码的第一行,我们创建了一个文件系统权限的数组,这是[System.Security.AccessControl.FileSystemRights] (一个.NET Framework文件系统枚举)结构所代表的含义。在示例脚本中,我们将两个权限(读和写)赋值给名为$colRights的变量,当我们实际创建新的访问控制项(ACE,access control entry)时将会使用这个变量。
| $colRights = [System.Security.AccessControl.FileSystemRights]"Read, Write" |
读和写是唯一能在脚本中指定的两个文件系统权限么?不。理论上(之所以说“理论上”是因为我们没有尝试所有的可能性)你可以指定以下权限:
| • | AppendData | 附加数据 |
| • | ChangePermissions | 更改权限 |
| • | CreateDirectories | 创建文件夹 |
| • | CreateFiles | 创建文件 |
| • | Delete | 删除 |
| • | DeleteSubdirectoriesAndFiles | 删除子文件夹及文件 |
| • | ExecuteFile | 执行文件 |
| • | FullControl | 完全控制 |
| • | ListDirectory | 列出文件夹 |
| • | Modify | 修改 |
| • | Read | 读取 |
| • | ReadAndExecute | 读取并执行 |
| • | ReadAttributes | 读取属性 |
| • | ReadData | 读取数据 |
| • | ReadExtendedAttributes | 读取扩展属性 |
| • | ReadPermissions | 读取权限 |
| • | Synchronize | 同步 |
| • | TakeOwnership | 取得所有权 |
| • | Traverse | 遍历 |
| • | Write | 写入 |
| • | WriteAttributes | 写入属性 |
| • | WriteData | 写入数据 |
| • | WriteExtendedAttributes | 写入扩展属性 |
明白了?长话短说,在代码的第一行我们只是指定了想要指定的文件系统权限。
在下面两行代码里,我们指出这些权限如何被继承和传播至子项。(比起文件来更关注的是文件夹。)在两个示例中我们将此值设为无。此外我们将InheritanceFlag设为以下的任一值:
l ContainerInherit(访问控制项被子容器继承,比如子文件夹)
l ObjectInherit(访问控制项被子对象基础,比如文件)
l None
在同一行,我们可以将PropagtionFlag设置为以下的任一值:
l InheritOnly(访问控制项传播到所有子对象)
l NoInheritOnly(访问控制项不会传播到所有子对象)
l None
你怎么知道所有这些值到底代表什么含义?我们的建议是创建测试文件夹,并将测试文件放入其中,按后尝试多种InheritFlags及PropagateFlags组合来确认将会发生什么。
接下来我们声明访问控制项的类型,访问控制项对对象来说可以是Allow或Deny两者之一。在例子中我们打算使用允许访问:
| $objType =[System.Security.AccessControl.AccessControlType]::Allow |
明白了?这不坏,不是么?下面一行代码也是,该代码创建将被授予这些权利的一个新的用户对象($objUser):
| $objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer") |
此时我们已经准备好在内存中创建新的访问控制项。通过创建System.Security.AccessControl.FileSystemAccessRule类的新实例,将所有新变量以参数形式小心传递给New-Object cmdlet:
| $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) |
所有这些都是可重复利用的代码,只要你将正确的值赋给独立的变量那么这行代码就如同呈现的那样处理任何安全描述符。
但是等一下,别离开,我们还没有完成所有的事。目前为止我们只是在内存中创建了新的访问控制项,实际上并没有将它添加到文件或文件夹中。为了完成此事,我们首先需要从该文件或者文件夹中提取安全描述符,也就是以下这行代码所完成的:
| $objACL = Get-ACL "C:\Scripts\Test.ps1" |
对,这很简单,不是么?我们所做的是使用Get-ACL cmdlet来从文件C:\Scripts\Test.ps1中提取安全描述符,并将此储存在名为$objACL的变量中。
一旦我们有了Test.ps1安全描述符的对象引用,那么就能使用AddAccessRule方法来添加新的访问控制项:
| $objACL.AddAccessRule($objACE) |
现在剩下的就是使用Set-ACL cmdlet来将修改过的安全描述符赋值回Test.ps1并且以列表的方式呈现它:
| Get-ACL "C:\Scripts\Test.ps1" | Format-List |
现在然我们看下用户fabrikam\kenmyer是否出现在安全描述符中的任意位置:
| Path : Microsoft.PowerShell.Core\FileSystem::C:\scripts\test.ps1 Owner : FABRIKAM\pilarackerman Group : FABRIKAM\Domain Users Access : FABRIKAM\kenmyer Allow Write, Read, Synchronize BUILTIN\Administrators Allow FullControl NT AUTHORITY\SYSTEM Allow FullControl FABRIKAM\ pilarackerman Allow FullControl BUILTIN\Users Allow ReadAndExecute, Synchronize |
好的,你知道了:
| Access : FABRIKAM\kenmyer Allow Write, Read, Synchronize |
像我们所说的,看上去一切工作正常。但是,如同我们开始所说的,在你开始在真实机上合并安全描述符之前,最好在测试机上测试该脚本。
现在,你的注意发生了变化并决定摆脱某个访问控制项?好的,有两种方法来完成。首先,你能使用RemoveAccessRule方法来移除单一权限。例如,下面的脚本从kenmyer访问控制项中移除了读取权限:
| $colRights = [System.Security.AccessControl.FileSystemRights]"Write" $InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None $objType =[System.Security.AccessControl.AccessControlType]::Allow $objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer") $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $objACL = Get-ACL "c:\scripts\test.ps1" $objACL.RemoveAccessRule($objACE) Set-ACL "C:\Scripts\Test.ps1" $objACL |
如你所见,方法几乎是一样。事实上,只有两处不同。首先,我们只将读取权限赋值给我们的文件系统权限列表。这是因为这是我们唯一项处理的权限:
| $colRights = [System.Security.AccessControl.FileSystemRights]"Write" |
其次,我们使用RemoveAccessRule方法来从安全描述符中移除这项权限:
| $objACL.RemoveAccessRule($objACE) |
此外,我们可以使用RemoveAccessRuleAll方法来从安全描述符中完整移除用户fabrikam\kenmyer:
| $colRights = [System.Security.AccessControl.FileSystemRights]"Read" $InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None $objType =[System.Security.AccessControl.AccessControlType]::Allow $objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer") $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $objACL = Get-ACL "c:\scripts\test.ps1" $objACL.RemoveAccessRuleAll($objACE) Set-ACL "C:\Scripts\Test.ps1" $objACL |
这次,我们只添加了读取这个单一权限至文件系统权限集合。我们尽可能地告诉你不得不需要将某些东西作为文件系统权限列出,尽管这不会有什么太大影响。如果访问控制项包含了所有权限也没什么太大关系,只需向要完成的任务集合中添加一项权限。
此外,我们也在脚本中使用了不同的方法,RemoveAccessRuleAll
| $objACL.RemoveAccessRuleAll($objACE) |
该方法将实现些什么?你知道:它将移除整个访问控制项。再次运行Get-ACL并看下Test1.ps1的安全描述符:
| Path : Microsoft.PowerShell.Core\FileSystem::C:\scripts\test.ps1 Owner : FABRIKAM\pilarackerman Group : FABRIKAM\Domain Users Access : BUILTIN\Administrators Allow FullControl NT AUTHORITY\SYSTEM Allow FullControl FABRIKAM\ pilarackerman Allow FullControl BUILTIN\Users Allow ReadAndExecute, Synchronize |
还可以。
无可否认的是,有点草率的介绍了如何处理安全描述符,但是这就是本周的所有内容。试一试(对,在一台测试机上),同时,脚本专家将会继续和这个而话题打交道。在我们两者之间,最后发现了系统管理脚本的圣地:一个程序化管理安全描述符的方法。
下周见。
英文原文
http://www.microsoft.com/technet/scriptcenter/resources/pstips/may08/pstip0516.mspx