mirror of https://github.com/raandree/NTFSSecurity
Browse Source
fix: write only the sections of the security descriptor that a cmdlet changes; prepare 5.0.0-rc3pull/113/head 5.0.0-rc3
committed by
GitHub
29 changed files with 736 additions and 35 deletions
@ -0,0 +1,183 @@ |
|||
<# |
|||
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" |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue