mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
12 KiB
236 lines
12 KiB
<#
|
|
Tests Get-NTFSSecurityDescriptor and Set-NTFSSecurityDescriptor of the module built in NTFSSecurity\bin\Release on
|
|
files in a sandbox folder. Tests that need the Security or the Restore privilege skip without it; CI runs them
|
|
elevated.
|
|
#>
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
|
|
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
|
|
)]
|
|
param ()
|
|
|
|
BeforeDiscovery {
|
|
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
|
|
$holdsSecurityPrivilege = Test-PrivilegeHeld -Name 'SeSecurityPrivilege'
|
|
# Assigning an owner other than the user or one of its groups needs the Restore privilege.
|
|
$canAssignAnyOwner = Test-PrivilegeHeld -Name 'SeRestorePrivilege'
|
|
}
|
|
|
|
BeforeAll {
|
|
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
|
|
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release\NTFSSecurity.psd1'
|
|
Import-Module -Name $modulePath -Force -ErrorAction Stop
|
|
$sandbox = New-TestSandbox -Name 'SecurityDescriptor'
|
|
Push-Location -LiteralPath $sandbox
|
|
|
|
$privateData = (Get-Module -Name NTFSSecurity).PrivateData
|
|
$enablePrivileges = $privateData['EnablePrivileges']
|
|
$sidType = [System.Security.Principal.SecurityIdentifier]
|
|
# An owner that the user can assign only with the Restore privilege
|
|
$trustedInstaller = 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464'
|
|
|
|
function Get-RestorePrivilegeState {
|
|
(Get-Privileges | Where-Object -Property Privilege -EQ -Value 'Restore').PrivilegeState
|
|
}
|
|
|
|
function Get-EveryoneRule ([string] $Path) {
|
|
(Get-Acl -LiteralPath $Path).GetAccessRules($true, $false, $sidType) |
|
|
Where-Object -FilterScript { $_.IdentityReference.Value -eq 'S-1-1-0' }
|
|
}
|
|
}
|
|
|
|
AfterAll {
|
|
$privateData['EnablePrivileges'] = $enablePrivileges
|
|
Pop-Location
|
|
Remove-TestSandbox -Sandbox $sandbox
|
|
Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
Describe 'Get-NTFSSecurityDescriptor' {
|
|
# Before 5.0.0-rc3, the cmdlet read the DACL together with the SACL when the process held the Security privilege.
|
|
# When the folder has no SACL, Windows then returns the inherited entries of a DACL without the auto-inherit flag,
|
|
# such as that of a file in the temp folder of the user, without their inherited flag.
|
|
It 'Should report the inherited access entries as inherited' -Skip:(-not $holdsSecurityPrivilege) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Inherited'
|
|
$inheritedCount = @((Get-Acl -LiteralPath $file).GetAccessRules($false, $true, $sidType)).Count
|
|
$inheritedCount | Should -BeGreaterThan 0
|
|
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
|
|
@($sd.SecurityDescriptor.GetAccessRules($true, $false, $sidType)) | Should -BeNullOrEmpty
|
|
@($sd.SecurityDescriptor.GetAccessRules($false, $true, $sidType)) | Should -HaveCount $inheritedCount
|
|
}
|
|
|
|
It 'Should read the owner and the audit entries with the access entries' -Skip:(-not $holdsSecurityPrivilege) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Sections'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
|
|
Add-NTFSAudit -Path $file -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None
|
|
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
|
|
$sd.SecurityDescriptor.GetOwner($sidType).Value | Should -Be (Get-Acl -LiteralPath $file).GetOwner($sidType).Value
|
|
@($sd.SecurityDescriptor.GetAuditRules($true, $false, $sidType)) | Should -HaveCount 1
|
|
}
|
|
}
|
|
|
|
Describe 'Set-NTFSSecurityDescriptor' {
|
|
Context 'When a cmdlet added an access entry to the descriptor' {
|
|
It 'Should write the entry as the only explicit entry of the item' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'AddedEntry'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
Add-NTFSAccess -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd
|
|
|
|
@(Get-EveryoneRule -Path $file) | Should -HaveCount 1
|
|
@((Get-Acl -LiteralPath $file).GetAccessRules($true, $false, $sidType)) | Should -HaveCount 1
|
|
}
|
|
}
|
|
|
|
Context 'When the item has an owner that the user cannot assign' {
|
|
BeforeAll {
|
|
$privateData['EnablePrivileges'] = $false
|
|
}
|
|
|
|
AfterAll {
|
|
$privateData['EnablePrivileges'] = $enablePrivileges
|
|
}
|
|
|
|
# Before 5.0.0-rc3, the cmdlet wrote every section that Get-NTFSSecurityDescriptor read, also the unchanged
|
|
# owner, which Windows refuses without the Restore privilege, like a file server that refuses the owner (#34).
|
|
It 'Should write an added access entry and keep the owner' -Skip:(-not $canAssignAnyOwner) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'OtherOwner'
|
|
Set-TestOwner -Sandbox $sandbox -Path $file -Sid $trustedInstaller
|
|
Get-RestorePrivilegeState | Should -Be 'Disabled'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
Add-NTFSAccess -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorVariable setErrors -ErrorAction SilentlyContinue
|
|
|
|
$setErrors | Should -BeNullOrEmpty
|
|
(Get-Acl -LiteralPath $file).GetOwner($sidType).Value | Should -Be $trustedInstaller
|
|
@(Get-EveryoneRule -Path $file) | Should -HaveCount 1
|
|
}
|
|
|
|
It 'Should write an added audit entry and keep the owner' -Skip:(-not ($holdsSecurityPrivilege -and $canAssignAnyOwner)) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'OtherOwnerAudit'
|
|
Set-TestOwner -Sandbox $sandbox -Path $file -Sid $trustedInstaller
|
|
Get-RestorePrivilegeState | Should -Be 'Disabled'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
Add-NTFSAudit -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData -InheritanceFlags None -PropagationFlags None
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorVariable setErrors -ErrorAction SilentlyContinue
|
|
|
|
$setErrors | Should -BeNullOrEmpty
|
|
(Get-Acl -LiteralPath $file).GetOwner($sidType).Value | Should -Be $trustedInstaller
|
|
@(Get-NTFSAudit -Path $file -ExcludeInherited) | Should -HaveCount 1
|
|
}
|
|
}
|
|
|
|
Context 'When the descriptor has sections that it did not change' {
|
|
# Before 5.0.0-rc3, the cmdlet wrote every section that Get-NTFSSecurityDescriptor read, so it also undid the
|
|
# changes that were made to the item after it was read.
|
|
It 'Should not write back the access entries of an unchanged descriptor' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'Unchanged'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
$acl = Get-Acl -LiteralPath $file
|
|
$acl.AddAccessRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList (
|
|
(New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'),
|
|
[System.Security.AccessControl.FileSystemRights]::ReadData, [System.Security.AccessControl.AccessControlType]::Allow
|
|
)))
|
|
Set-Acl -LiteralPath $file -AclObject $acl
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd
|
|
|
|
@(Get-EveryoneRule -Path $file) | Should -HaveCount 1
|
|
}
|
|
|
|
It 'Should write the owner when only the owner changed' -Skip:(-not $canAssignAnyOwner) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'NewOwner'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
$sd.SecurityDescriptor.SetOwner((New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList $trustedInstaller))
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd
|
|
|
|
(Get-Acl -LiteralPath $file).GetOwner($sidType).Value | Should -Be $trustedInstaller
|
|
}
|
|
}
|
|
|
|
Context 'With -Verbose' {
|
|
It 'Should name the sections that it writes' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'VerboseChanged'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
Add-NTFSAccess -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData
|
|
|
|
$messages = Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -Verbose 4>&1
|
|
|
|
$messages.Message | Should -Contain "Writing the changed sections of the security descriptor of '$($sd.FullName)': Access"
|
|
}
|
|
|
|
It 'Should say that it writes nothing for an unchanged descriptor' {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'VerboseUnchanged'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
|
|
$messages = Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -Verbose 4>&1
|
|
|
|
$messages.Message |
|
|
Should -Contain "No section of the security descriptor of '$($sd.FullName)' changed since it was read or last written; nothing is written"
|
|
}
|
|
}
|
|
|
|
Context 'When the write is denied until the cmdlet takes ownership' {
|
|
BeforeAll {
|
|
$privateData['EnablePrivileges'] = $false
|
|
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
}
|
|
|
|
AfterAll {
|
|
$privateData['EnablePrivileges'] = $enablePrivileges
|
|
}
|
|
|
|
# Before 5.0.0-rc4, the cmdlet set the previous owner back after the write, which undid an owner that the
|
|
# descriptor set, and failed for a previous owner that the user can't assign. The deny entry for the user stops
|
|
# the first write; as the owner, the user may change the permissions.
|
|
It 'Should keep the owner that the descriptor sets when the write succeeds' -Skip:(-not $canAssignAnyOwner) {
|
|
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'RetryOwner'
|
|
Add-TestDenyRule -Sandbox $sandbox -Path $file -Rights @{ $currentUser = 'ChangePermissions' }
|
|
Set-TestOwner -Sandbox $sandbox -Path $file -Sid $trustedInstaller
|
|
Get-RestorePrivilegeState | Should -Be 'Disabled'
|
|
$sd = Get-NTFSSecurityDescriptor -Path $file
|
|
Add-NTFSAccess -SecurityDescriptor $sd -Account 'Everyone' -AccessRights ReadData
|
|
$sd.SecurityDescriptor.SetOwner((New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-5-32-544'))
|
|
|
|
Set-NTFSSecurityDescriptor -SecurityDescriptor $sd -ErrorVariable setErrors -ErrorAction SilentlyContinue
|
|
|
|
$setErrors | Should -BeNullOrEmpty
|
|
(Get-Acl -LiteralPath $file).GetOwner($sidType).Value | Should -Be 'S-1-5-32-544'
|
|
}
|
|
}
|
|
}
|
|
|
|
Describe 'FileSystemSecurity2.Write with another item' {
|
|
# Before 5.0.0-rc4, Write wrote every section that the descriptor held to the other item, also the owner that
|
|
# Windows returns with a DACL without the auto-inherit flag, which fails for an owner that the user can't assign.
|
|
It 'Should write only the sections that were read, given the item as <_>' -Skip:(-not $canAssignAnyOwner) -ForEach @(
|
|
'FileSystemInfo', 'String'
|
|
) {
|
|
$source = New-TestSandboxItem -Sandbox $sandbox -Name 'Source'
|
|
$target = New-TestSandboxItem -Sandbox $sandbox -Name 'Target'
|
|
Set-TestOwner -Sandbox $sandbox -Path $source -Sid $trustedInstaller
|
|
$targetOwner = (Get-Acl -LiteralPath $target).GetOwner($sidType).Value
|
|
Get-RestorePrivilegeState | Should -Be 'Disabled'
|
|
$sd = New-Object -TypeName 'Security2.FileSystemSecurity2' -ArgumentList (
|
|
(Get-Item2 -Path $source), [System.Security.AccessControl.AccessControlSections]::Access
|
|
)
|
|
$sd.SecurityDescriptor.GetOwner($sidType).Value | Should -Be $trustedInstaller
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $target
|
|
$destination = if ($_ -eq 'String') { $target } else { Get-Item2 -Path $target }
|
|
|
|
{ $sd.Write($destination) } | Should -Not -Throw
|
|
|
|
(Get-Acl -LiteralPath $target).GetOwner($sidType).Value | Should -Be $targetOwner
|
|
}
|
|
}
|
|
|