From ec3eb7a088256076c9c1fb86d42369159f2b7604 Mon Sep 17 00:00:00 2001 From: Yanbing Date: Tue, 24 Apr 2018 11:50:44 -0700 Subject: [PATCH] Fix issue install-sshd.ps1 failed on Nano, update it to match inbox manifest, and add setup and uninstall tests (#305) 1. Fix issue install-sshd.ps1 failed on Nano 2. Update settings of services in install-sshd.ps1 to match windows inbox 3. added setup tests and update the test helper scripts to run setup tests before changing configurations on the machine 4. added uninstallation tests --- appveyor.yml | 5 - contrib/win32/openssh/AppveyorHelper.psm1 | 69 ++- contrib/win32/openssh/OpenSSHTestHelper.psm1 | 251 ++++++---- contrib/win32/openssh/install-sshd.ps1 | 24 +- regress/pesterTests/Setup.Tests.ps1 | 490 +++++++++++++++++++ regress/pesterTests/Uninstall.Tests.ps1 | 196 ++++++++ 6 files changed, 931 insertions(+), 104 deletions(-) create mode 100644 regress/pesterTests/Setup.Tests.ps1 create mode 100644 regress/pesterTests/Uninstall.Tests.ps1 diff --git a/appveyor.yml b/appveyor.yml index 40c1ebd69..9aa7a779d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,11 +16,6 @@ after_build: Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 Install-OpenSSH -before_test: - - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 - Set-OpenSSHTestEnvironment -Confirm:$false - test_script: - ps: | Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 diff --git a/contrib/win32/openssh/AppveyorHelper.psm1 b/contrib/win32/openssh/AppveyorHelper.psm1 index c136ab77b..d4e2f7b3a 100644 --- a/contrib/win32/openssh/AppveyorHelper.psm1 +++ b/contrib/win32/openssh/AppveyorHelper.psm1 @@ -271,8 +271,10 @@ function Publish-Artifact if($Global:OpenSSHTestInfo) { + Add-Artifact -artifacts $artifacts -FileToAdd $Global:OpenSSHTestInfo["SetupTestResultsFile"] Add-Artifact -artifacts $artifacts -FileToAdd $Global:OpenSSHTestInfo["UnitTestResultsFile"] Add-Artifact -artifacts $artifacts -FileToAdd $Global:OpenSSHTestInfo["E2ETestResultsFile"] + Add-Artifact -artifacts $artifacts -FileToAdd $Global:OpenSSHTestInfo["UninstallTestResultsFile"] Add-Artifact -artifacts $artifacts -FileToAdd $Global:OpenSSHTestInfo["TestSetupLogFile"] } @@ -289,6 +291,27 @@ function Publish-Artifact #> function Invoke-OpenSSHTests { + Set-BasicTestInfo -Confirm:$false + Invoke-OpenSSHSetupTest + if (($OpenSSHTestInfo -eq $null) -or (-not (Test-Path $OpenSSHTestInfo["SetupTestResultsFile"]))) + { + Write-Warning "Test result file $OpenSSHTestInfo["SetupTestResultsFile"] not found after tests." + Write-BuildMessage -Message "Test result file $OpenSSHTestInfo["SetupTestResultsFile"] not found after tests." -Category Error + Set-BuildVariable TestPassed False + Write-Warning "Stop running further tests!" + return + } + $xml = [xml](Get-Content $OpenSSHTestInfo["SetupTestResultsFile"] | out-string) + if ([int]$xml.'test-results'.failures -gt 0) + { + $errorMessage = "$($xml.'test-results'.failures) setup tests in regress\pesterTests failed. Detail test log is at $($OpenSSHTestInfo["SetupTestResultsFile"])." + Write-Warning $errorMessage + Write-BuildMessage -Message $errorMessage -Category Error + Set-BuildVariable TestPassed False + Write-Warning "Stop running further tests!" + return + } + Write-Host "Start running unit tests" $unitTestFailed = Invoke-OpenSSHUnitTest @@ -303,13 +326,17 @@ function Invoke-OpenSSHTests Write-Host "All Unit tests passed!" Write-BuildMessage -Message "All Unit tests passed!" -Category Information } + # Run all E2E tests. + Set-OpenSSHTestEnvironment -Confirm:$false Invoke-OpenSSHE2ETest if (($OpenSSHTestInfo -eq $null) -or (-not (Test-Path $OpenSSHTestInfo["E2ETestResultsFile"]))) { Write-Warning "Test result file $OpenSSHTestInfo["E2ETestResultsFile"] not found after tests." Write-BuildMessage -Message "Test result file $OpenSSHTestInfo["E2ETestResultsFile"] not found after tests." -Category Error Set-BuildVariable TestPassed False + Write-Warning "Stop running further tests!" + return } $xml = [xml](Get-Content $OpenSSHTestInfo["E2ETestResultsFile"] | out-string) if ([int]$xml.'test-results'.failures -gt 0) @@ -318,6 +345,26 @@ function Invoke-OpenSSHTests Write-Warning $errorMessage Write-BuildMessage -Message $errorMessage -Category Error Set-BuildVariable TestPassed False + Write-Warning "Stop running further tests!" + return + } + + Invoke-OpenSSHUninstallTest + if (($OpenSSHTestInfo -eq $null) -or (-not (Test-Path $OpenSSHTestInfo["UninstallTestResultsFile"]))) + { + Write-Warning "Test result file $OpenSSHTestInfo["UninstallTestResultsFile"] not found after tests." + Write-BuildMessage -Message "Test result file $OpenSSHTestInfo["UninstallTestResultsFile"] not found after tests." -Category Error + Set-BuildVariable TestPassed False + } + else { + $xml = [xml](Get-Content $OpenSSHTestInfo["UninstallTestResultsFile"] | out-string) + if ([int]$xml.'test-results'.failures -gt 0) + { + $errorMessage = "$($xml.'test-results'.failures) uninstall tests in regress\pesterTests failed. Detail test log is at $($OpenSSHTestInfo["UninstallTestResultsFile"])." + Write-Warning $errorMessage + Write-BuildMessage -Message $errorMessage -Category Error + Set-BuildVariable TestPassed False + } } # Writing out warning when the $Error.Count is non-zero. Tests Should clean $Error after success. @@ -335,11 +382,25 @@ function Publish-OpenSSHTestResults { if ($env:APPVEYOR_JOB_ID) { - $resultFile = Resolve-Path $Global:OpenSSHTestInfo["E2ETestResultsFile"] -ErrorAction Ignore - if( (Test-Path $Global:OpenSSHTestInfo["E2ETestResultsFile"]) -and $resultFile) + $setupresultFile = Resolve-Path $Global:OpenSSHTestInfo["SetupTestResultsFile"] -ErrorAction Ignore + if( (Test-Path $Global:OpenSSHTestInfo["SetupTestResultsFile"]) -and $setupresultFile) { - (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $resultFile) - Write-BuildMessage -Message "Test results uploaded!" -Category Information + (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $setupresultFile) + Write-BuildMessage -Message "Setup test results uploaded!" -Category Information + } + + $E2EresultFile = Resolve-Path $Global:OpenSSHTestInfo["E2ETestResultsFile"] -ErrorAction Ignore + if( (Test-Path $Global:OpenSSHTestInfo["E2ETestResultsFile"]) -and $E2EresultFile) + { + (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $E2EresultFile) + Write-BuildMessage -Message "E2E test results uploaded!" -Category Information + } + + $uninstallResultFile = Resolve-Path $Global:OpenSSHTestInfo["UninstallTestResultsFile"] -ErrorAction Ignore + if( (Test-Path $Global:OpenSSHTestInfo["UninstallTestResultsFile"]) -and $uninstallResultFile) + { + (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $uninstallResultFile) + Write-BuildMessage -Message "Uninstall test results uploaded!" -Category Information } } diff --git a/contrib/win32/openssh/OpenSSHTestHelper.psm1 b/contrib/win32/openssh/OpenSSHTestHelper.psm1 index 93e1881fb..4f11115a0 100644 --- a/contrib/win32/openssh/OpenSSHTestHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHTestHelper.psm1 @@ -5,6 +5,8 @@ Import-Module $PSScriptRoot\OpenSSHUtils -Force [System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot # test environment parameters initialized with defaults +$SetupTestResultsFileName = "setupTestResults.xml" +$UninstallTestResultsFileName = "UninstallTestResults.xml" $E2ETestResultsFileName = "E2ETestResults.xml" $UnitTestResultsFileName = "UnitTestResults.txt" $TestSetupLogFileName = "TestSetupLog.txt" @@ -16,6 +18,8 @@ $OpenSSHTestAccounts = $Script:SSOUser, $Script:PubKeyUser, $Script:PasswdUser $OpenSSHConfigPath = Join-Path $env:ProgramData "ssh" $Script:TestDataPath = "$env:SystemDrive\OpenSSHTests" +$Script:SetupTestResultsFile = Join-Path $TestDataPath $SetupTestResultsFileName +$Script:UninstallTestResultsFile = Join-Path $TestDataPath $UninstallTestResultsFileName $Script:E2ETestResultsFile = Join-Path $TestDataPath $E2ETestResultsFileName $Script:UnitTestResultsFile = Join-Path $TestDataPath $UnitTestResultsFileName $Script:TestSetupLogFile = Join-Path $TestDataPath $TestSetupLogFileName @@ -42,100 +46,43 @@ function Set-OpenSSHTestEnvironment [Switch] $PostmortemDebugging, [Switch] $NoLibreSSL ) + + $params = $PSBoundParameters + $params.Remove("DebugMode") | Out-Null + $params.Remove("NoAppVerifier") | Out-Null + $params.Remove("PostmortemDebugging") | Out-Null if($PSBoundParameters.ContainsKey("Verbose")) { $verboseInfo = ($PSBoundParameters['Verbose']).IsPresent } - - if($Global:OpenSSHTestInfo -ne $null) - { - $Global:OpenSSHTestInfo.Clear() - $Global:OpenSSHTestInfo = $null - } - $Script:TestDataPath = $TestDataPath; - $Script:E2ETestResultsFile = Join-Path $TestDataPath "E2ETestResults.xml" - $Script:UnitTestResultsFile = Join-Path $TestDataPath "UnitTestResults.txt" - $Script:TestSetupLogFile = Join-Path $TestDataPath "TestSetupLog.txt" - $Script:UnitTestDirectory = Get-UnitTestDirectory + + Set-BasicTestInfo @params + + $Global:OpenSSHTestInfo.Add("Target", "localhost") # test listener name + $Global:OpenSSHTestInfo.Add("Port", "47002") # test listener port + $Global:OpenSSHTestInfo.Add("SSOUser", $SSOUser) # test user with single sign on capability + $Global:OpenSSHTestInfo.Add("PubKeyUser", $PubKeyUser) # test user to be used with explicit key for key auth + $Global:OpenSSHTestInfo.Add("PasswdUser", $PasswdUser) # test user to be used for password auth + $Global:OpenSSHTestInfo.Add("TestAccountPW", $OpenSSHTestAccountsPassword) # common password for all test accounts + $Global:OpenSSHTestInfo.Add("DebugMode", $DebugMode.IsPresent) # run openssh E2E in debug mode + + $Script:EnableAppVerifier = -not ($NoAppVerifier.IsPresent) - $Script:NoLibreSSL = $NoLibreSSL.IsPresent + if($Script:WindowsInBox = $true) + { + $Script:EnableAppVerifier = $false + } + $Global:OpenSSHTestInfo.Add("EnableAppVerifier", $Script:EnableAppVerifier) + if($Script:EnableAppVerifier) { $Script:PostmortemDebugging = $PostmortemDebugging.IsPresent - } - - $Global:OpenSSHTestInfo = @{ - "Target"= "localhost"; # test listener name - "Port"= "47002"; # test listener port - "SSOUser"= $SSOUser; # test user with single sign on capability - "PubKeyUser"= $PubKeyUser; # test user to be used with explicit key for key auth - "PasswdUser"= $PasswdUser; # common password for all test accounts - "TestAccountPW"= $OpenSSHTestAccountsPassword; # common password for all test accounts - "TestDataPath" = $TestDataPath; # openssh tests path - "TestSetupLogFile" = $Script:TestSetupLogFile; # openssh test setup log file - "E2ETestResultsFile" = $Script:E2ETestResultsFile; # openssh E2E test results file - "UnitTestResultsFile" = $Script:UnitTestResultsFile; # openssh unittest test results file - "E2ETestDirectory" = $Script:E2ETestDirectory # the directory of E2E tests - "UnitTestDirectory" = $Script:UnitTestDirectory # the directory of unit tests - "DebugMode" = $DebugMode.IsPresent # run openssh E2E in debug mode - "EnableAppVerifier" = $Script:EnableAppVerifier - "PostmortemDebugging" = $Script:PostmortemDebugging - "NoLibreSSL" = $Script:NoLibreSSL - } + } + $Global:OpenSSHTestInfo.Add("PostmortemDebugging", $Script:PostmortemDebugging) #start service if not already started Start-Service -Name sshd - - #if user does not set path, pick it up - if([string]::IsNullOrEmpty($OpenSSHBinPath)) - { - $sshcmd = get-command ssh.exe -ErrorAction SilentlyContinue - if($sshcmd -eq $null) - { - Throw "Cannot find ssh.exe. Please specify -OpenSSHBinPath to the OpenSSH installed location." - } - else - { - $dirToCheck = split-path $sshcmd.Path - $description = "Pick up ssh.exe from $dirToCheck." - $prompt = "Are you sure you want to pick up ssh.exe from $($dirToCheck)?" - $caption = "Found ssh.exe from $dirToCheck" - if(-not $pscmdlet.ShouldProcess($description, $prompt, $caption)) - { - Write-Host "User decided not to pick up ssh.exe from $dirToCheck. Please specify -OpenSSHBinPath to the OpenSSH installed location." - return - } - $script:OpenSSHBinPath = $dirToCheck - } - } - else - { - if (-not (Test-Path (Join-Path $OpenSSHBinPath ssh.exe) -PathType Leaf)) - { - Throw "Cannot find OpenSSH binaries under $OpenSSHBinPath. Please specify -OpenSSHBinPath to the OpenSSH installed location" - } - else - { - $script:OpenSSHBinPath = $OpenSSHBinPath - } - } - - $Global:OpenSSHTestInfo.Add("OpenSSHBinPath", $script:OpenSSHBinPath) - if (-not ($env:Path.ToLower().Contains($script:OpenSSHBinPath.ToLower()))) - { - $env:Path = "$($script:OpenSSHBinPath);$($env:path)" - } - - $acl = get-acl (join-path $script:OpenSSHBinPath "ssh.exe") - - if($acl.Owner -ieq "NT SERVICE\TrustedInstaller") - { - $Script:WindowsInBox = $true - $Global:OpenSSHTestInfo.Add("WindowsInBox", $true) - $Global:OpenSSHTestInfo["EnableAppVerifier"] = $false - $Script:EnableAppVerifier = $false - } $description = @" WARNING: Following changes will be made to OpenSSH configuration @@ -160,12 +107,7 @@ WARNING: Following changes will be made to OpenSSH configuration return } - Install-OpenSSHTestDependencies - - if(-not (Test-path $TestDataPath -PathType Container)) - { - New-Item -ItemType Directory -Path $TestDataPath -Force -ErrorAction SilentlyContinue | out-null - } + Install-OpenSSHTestDependencies $backupConfigPath = Join-Path $OpenSSHConfigPath sshd_config.ori $targetsshdConfig = Join-Path $OpenSSHConfigPath sshd_config @@ -194,7 +136,7 @@ WARNING: Following changes will be made to OpenSSH configuration #copy ca private key to test dir $ca_priv_key = (Join-Path $Global:OpenSSHTestInfo["TestDataPath"] sshtest_ca_userkeys) Copy-Item (Join-Path $Script:E2ETestDirectory sshtest_ca_userkeys) $ca_priv_key -Force - Repair-UserSshConfigPermission -FilePath $ca_priv_key -confirm:$false + Repair-UserSshConfigPermission -FilePath $ca_priv_key -confirm:$false $Global:OpenSSHTestInfo["CA_Private_Key"] = $ca_priv_key Restart-Service sshd -Force @@ -271,6 +213,98 @@ WARNING: Following changes will be made to OpenSSH configuration Backup-OpenSSHTestInfo } + +function Set-BasicTestInfo +{ + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] + param + ( + [string] $OpenSSHBinPath, + [string] $TestDataPath = "$env:SystemDrive\OpenSSHTests", + [Switch] $NoLibreSSL + ) + + if($Global:OpenSSHTestInfo -ne $null) + { + $Global:OpenSSHTestInfo.Clear() + $Global:OpenSSHTestInfo = $null + } + $Script:TestDataPath = $TestDataPath; + $Script:E2ETestResultsFile = Join-Path $TestDataPath $E2ETestResultsFileName + $Script:SetupTestResultsFile = Join-Path $TestDataPath $SetupTestResultsFileName + $Script:UninstallTestResultsFile = Join-Path $TestDataPath $UninstallTestResultsFileName + $Script:UnitTestResultsFile = Join-Path $TestDataPath $UnitTestResultsFileName + $Script:TestSetupLogFile = Join-Path $TestDataPath $TestSetupLogFileName + $Script:UnitTestDirectory = Get-UnitTestDirectory + $Script:NoLibreSSL = $NoLibreSSL.IsPresent + + $Global:OpenSSHTestInfo = @{ + "TestDataPath" = $TestDataPath; # openssh tests path + "TestSetupLogFile" = $Script:TestSetupLogFile; # openssh test setup log file + "E2ETestResultsFile" = $Script:E2ETestResultsFile; # openssh E2E test results file + "SetupTestResultsFile" = $Script:SetupTestResultsFile; # openssh setup test test results file + "UninstallTestResultsFile" = $Script:UninstallTestResultsFile; # openssh Uninstall test test results file + "UnitTestResultsFile" = $Script:UnitTestResultsFile; # openssh unittest test results file + "E2ETestDirectory" = $Script:E2ETestDirectory # the directory of E2E tests + "UnitTestDirectory" = $Script:UnitTestDirectory # the directory of unit tests + "NoLibreSSL" = $Script:NoLibreSSL + "WindowsInBox" = $Script:WindowsInBox + } + #if user does not set path, pick it up + if([string]::IsNullOrEmpty($OpenSSHBinPath)) + { + $sshcmd = get-command ssh.exe -ErrorAction SilentlyContinue + if($sshcmd -eq $null) + { + Throw "Cannot find ssh.exe. Please specify -OpenSSHBinPath to the OpenSSH installed location." + } + else + { + $dirToCheck = split-path $sshcmd.Path + $description = "Pick up ssh.exe from $dirToCheck." + $prompt = "Are you sure you want to pick up ssh.exe from $($dirToCheck)?" + $caption = "Found ssh.exe from $dirToCheck" + if(-not $pscmdlet.ShouldProcess($description, $prompt, $caption)) + { + Write-Host "User decided not to pick up ssh.exe from $dirToCheck. Please specify -OpenSSHBinPath to the OpenSSH installed location." + return + } + $script:OpenSSHBinPath = $dirToCheck + } + } + else + { + if (-not (Test-Path (Join-Path $OpenSSHBinPath ssh.exe) -PathType Leaf)) + { + Throw "Cannot find OpenSSH binaries under $OpenSSHBinPath. Please specify -OpenSSHBinPath to the OpenSSH installed location" + } + else + { + $script:OpenSSHBinPath = $OpenSSHBinPath + } + } + + $Global:OpenSSHTestInfo.Add("OpenSSHBinPath", $script:OpenSSHBinPath) + if (-not ($env:Path.ToLower().Contains($script:OpenSSHBinPath.ToLower()))) + { + $env:Path = "$($script:OpenSSHBinPath);$($env:path)" + } + + $acl = get-acl (join-path $script:OpenSSHBinPath "ssh.exe") + + if($acl.Owner -ieq "NT SERVICE\TrustedInstaller") + { + $Script:WindowsInBox = $true + $Global:OpenSSHTestInfo["WindowsInBox"]= $true + } + + Install-OpenSSHTestDependencies -TestHarness + if(-not (Test-path $TestDataPath -PathType Container)) + { + New-Item -ItemType Directory -Path $TestDataPath -Force -ErrorAction SilentlyContinue | out-null + } +} + #TODO - this is Windows specific. Need to be in PAL function Get-LocalUserProfile { @@ -299,7 +333,7 @@ function Get-LocalUserProfile function Install-OpenSSHTestDependencies { [CmdletBinding()] - param () + param ([Switch] $TestHarness) #$isOpenSSHUtilsAvailable = Get-Module 'OpenSSHUtils' -ListAvailable #if (-not ($isOpenSSHUtilsAvailable)) @@ -328,6 +362,11 @@ function Install-OpenSSHTestDependencies choco install Pester --version 3.4.6 -y --force --limitoutput 2>&1 >> $Script:TestSetupLogFile } + if($TestHarness) + { + return + } + if($Script:PostmortemDebugging -or (($OpenSSHTestInfo -ne $null) -and ($OpenSSHTestInfo["PostmortemDebugging"]))) { $folderName = "x86" @@ -568,6 +607,36 @@ function Get-UnitTestDirectory $unitTestDir } +<# + .Synopsis + Run OpenSSH Setup tests. +#> +function Invoke-OpenSSHSetupTest +{ + # Discover all CI tests and run them. + Import-Module pester -force -global + Push-Location $Script:E2ETestDirectory + Write-Log -Message "Running OpenSSH Setup tests..." + $testFolders = @(Get-ChildItem *.tests.ps1 -Recurse | ForEach-Object{ Split-Path $_.FullName} | Sort-Object -Unique) + Invoke-Pester $testFolders -OutputFormat NUnitXml -OutputFile $Script:SetupTestResultsFile -Tag 'Setup' -PassThru + Pop-Location +} + +<# + .Synopsis + Run OpenSSH uninstall tests. +#> +function Invoke-OpenSSHUninstallTest +{ + # Discover all CI tests and run them. + Import-Module pester -force -global + Push-Location $Script:E2ETestDirectory + Write-Log -Message "Running OpenSSH Uninstall tests..." + $testFolders = @(Get-ChildItem *.tests.ps1 -Recurse | ForEach-Object{ Split-Path $_.FullName} | Sort-Object -Unique) + Invoke-Pester $testFolders -OutputFormat NUnitXml -OutputFile $Script:UninstallTestResultsFile -Tag 'Uninstall' -PassThru + Pop-Location +} + <# .Synopsis Run OpenSSH pester tests. @@ -726,4 +795,4 @@ function Write-Log } } -Export-ModuleMember -Function Set-OpenSSHTestEnvironment, Clear-OpenSSHTestEnvironment, Invoke-OpenSSHUnitTest, Invoke-OpenSSHE2ETest, Backup-OpenSSHTestInfo, Restore-OpenSSHTestInfo +Export-ModuleMember -Function Set-BasicTestInfo, Set-OpenSSHTestEnvironment, Clear-OpenSSHTestEnvironment, Invoke-OpenSSHSetupTest, Invoke-OpenSSHUnitTest, Invoke-OpenSSHE2ETest, Invoke-OpenSSHUninstallTest, Backup-OpenSSHTestInfo, Restore-OpenSSHTestInfo diff --git a/contrib/win32/openssh/install-sshd.ps1 b/contrib/win32/openssh/install-sshd.ps1 index 1a0d900d5..70bec18b5 100644 --- a/contrib/win32/openssh/install-sshd.ps1 +++ b/contrib/win32/openssh/install-sshd.ps1 @@ -33,14 +33,30 @@ wevtutil um `"$etwman`" [XML]$xml = Get-Content $etwman $xml.instrumentationManifest.instrumentation.events.provider.resourceFileName = $sshagentpath.ToString() $xml.instrumentationManifest.instrumentation.events.provider.messageFileName = $sshagentpath.ToString() -$xml.Save($etwman) + +$streamWriter = $null +$xmlWriter = $null +try { + $streamWriter = new-object System.IO.StreamWriter($etwman) + $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) + $xml.Save($xmlWriter) +} +finally { + if($streamWriter) { + $streamWriter.Close() + } +} #register etw provider wevtutil im `"$etwman`" -New-Service -Name ssh-agent -BinaryPathName `"$sshagentpath`" -Description "SSH Agent" -StartupType Manual | Out-Null -cmd.exe /c 'sc.exe sdset ssh-agent D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)' +$agentDesc = "Agent to hold private keys used for public key authentication." +New-Service -Name ssh-agent -DisplayName "OpenSSH Authentication Agent" -BinaryPathName `"$sshagentpath`" -Description $agentDesc -StartupType Manual | Out-Null +sc.exe sdset ssh-agent "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)" +sc.exe privs ssh-agent SeImpersonatePrivilege -New-Service -Name sshd -BinaryPathName `"$sshdpath`" -Description "SSH Daemon" -StartupType Manual | Out-Null +$sshdDesc = "SSH protocol based service to provide secure encrypted communications between two untrusted hosts over an insecure network." +New-Service -Name sshd -DisplayName "OpenSSH SSH Server" -BinaryPathName `"$sshdpath`" -Description $sshdDesc -StartupType Manual | Out-Null +sc.exe privs sshd SeAssignPrimaryTokenPrivilege/SeTcbPrivilege/SeBackupPrivilege/SeRestorePrivilege/SeImpersonatePrivilege Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed" diff --git a/regress/pesterTests/Setup.Tests.ps1 b/regress/pesterTests/Setup.Tests.ps1 new file mode 100644 index 000000000..dbeb636a0 --- /dev/null +++ b/regress/pesterTests/Setup.Tests.ps1 @@ -0,0 +1,490 @@ +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force +$suite = "Setup" +$tC = 1 +$tI = 0 +Describe "Setup Tests" -Tags "Setup" { + BeforeAll { + if($OpenSSHTestInfo -eq $null) + { + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." + } + + $windowsInBox = $OpenSSHTestInfo["WindowsInBox"] + $binPath = $OpenSSHTestInfo["OpenSSHBinPath"] + $dataPath = Join-path $env:ProgramData ssh + + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $usersSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinUsersSid) + $authenticatedUserSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::AuthenticatedUserSid) + $trustedInstallerSid = Get-UserSID -User "NT SERVICE\TrustedInstaller" + $allApplicationPackagesSid = Get-UserSID -User "ALL APPLICATION PACKAGES" + $allRestrictedApplicationPackagesSid = Get-UserSID -User "ALL RESTRICTED APPLICATION PACKAGES" + + $FSReadAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::ReadAndExecute.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + $FSReadWriteAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::ReadAndExecute.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Write.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Modify.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + + $FSFullControlPerm = [System.UInt32] [System.Security.AccessControl.FileSystemRights]::FullControl.value__ + $FSReadAndExecutePerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::ReadAndExecute.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + + $RegReadKeyPerm = ([System.UInt32] [System.Security.AccessControl.RegistryRights]::ReadKey.value__) + $RegFullControlPerm = [System.UInt32] [System.Security.AccessControl.RegistryRights]::FullControl.value__ + + #only validate owner and ACEs of the registry + function ValidateRegistryACL { + param([string]$RegPath, $Ownersid = $adminsSid, $IdAcls) + Test-Path -Path $RegPath | Should Be $true + $myACL = Get-ACL $RegPath + $OwnerSid = Get-UserSid -User $myACL.Owner + $OwnerSid.Equals($Ownersid) | Should Be $true + $myACL.Access | Should Not Be $null + $CAPABILITY_SID = "S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681" + $nonPropagate = $myACL.Access | ? {($_.PropagationFlags -eq ([System.Security.AccessControl.PropagationFlags]::None)) -and ($_.IdentityReference -ine $CAPABILITY_SID)} + + foreach ($a in $nonPropagate) { + $findItem = $IdAcls | ? { + ($a.IdentityReference -eq (Get-UserAccount -UserSid ($_.Identity))) -and ` + ($a.IsInherited -eq $_.IsInherited) -and ` + ($a.AccessControlType -eq ([System.Security.AccessControl.AccessControlType]::Allow)) -and ` + (([System.Int32]$a.RegistryRights.value__) -eq ($_.RegistryRights)) + } + $findItem | Should Not Be $null + } + + foreach ($expected in $IdAcls) { + $findItem = $nonPropagate | ? { + ((Get-UserAccount -UserSid ($expected.Identity)) -eq $_.IdentityReference) -and ` + ($expected.IsInherited -eq $_.IsInherited) -and ` + ($expected.RegistryRights -eq ([System.Int32]$_.RegistryRights.value__)) + } + $findItem | Should Not Be $null + } + } + + #only validate owner and ACEs of the file + function ValidateFileSystem { + param( + [string]$FilePath, + [bool]$IsDirectory = $false, + [switch]$IsDataFile, + $OwnerSid = $trustedInstallerSid) + + if($IsDirectory) + { + Test-Path -Path $FilePath -PathType Container | Should Be $true + } + else + { + Test-Path -Path $FilePath -PathType Leaf | Should Be $true + } + + $myACL = Get-ACL $FilePath + $currentOwnerSid = Get-UserSid -User $myACL.Owner + if(-not $windowsInBox) {return} + $currentOwnerSid.Equals($OwnerSid) | Should Be $true + $myACL.Access | Should Not Be $null + if($IsDirectory) + { + $identities = @($systemSid, $adminsSid) + } + elseif($IsDataFile) + { + $identities = @($systemSid, $adminsSid, $authenticatedUserSid) + } + else + { + $identities = @($systemSid, $adminsSid, $trustedInstallerSid, $allApplicationPackagesSid, $allRestrictedApplicationPackagesSid, $usersSid) + } + + $identities | % { + (Get-UserAccount -UserSid $_) | Should BeIn $myACL.Access.IdentityReference + } + + foreach ($a in $myACL.Access) { + $id = Get-UserSid -User $a.IdentityReference + if($id -eq $null) + { + $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] + $id = Get-UserSID -User $idRefShortValue + } + + $id | Should BeIn $identities + + switch ($id) + { + {@($systemSid, $adminsSid) -contains $_} + { + if($IsDataFile) + { + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSFullControlPerm + } + else + { + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm + } + break; + } + {@($usersSid, $allApplicationPackagesSid, $allRestrictedApplicationPackagesSid, $authenticatedUserSid) -contains $_} + { + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm + break; + } + $trustedInstallerSid + { + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSFullControlPerm + break; + } + } + + $a.AccessControlType | Should Be ([System.Security.AccessControl.AccessControlType]::Allow) + if($IsDirectory) + { + $a.InheritanceFlags | Should Be (([System.Security.AccessControl.InheritanceFlags]::ContainerInherit.value__ -bor ` + [System.Security.AccessControl.InheritanceFlags]::ObjectInherit.value__)) + } + else + { + $a.InheritanceFlags | Should Be ([System.Security.AccessControl.InheritanceFlags]::None) + } + $a.PropagationFlags | Should Be ([System.Security.AccessControl.PropagationFlags]::None) + } + } + } + + Context "$tC - Validate Openssh binary files" { + + BeforeAll { + $tI=1 + $binaries = @( + @{ + Name = 'sshd.exe' + }, + @{ + Name = 'ssh.exe' + }, + @{ + Name = 'ssh-agent.exe' + }, + @{ + Name = 'ssh-add.exe' + }, + @{ + Name = 'sftp.exe' + }, + @{ + Name = 'sftp-server.exe' + }, + @{ + Name = 'scp.exe' + }, + @{ + Name = 'ssh-shellhost.exe' + }, + @{ + Name = 'ssh-agent.exe' + }, + @{ + Name = 'ssh-keyscan.exe' + } + ) + $dataFile = @( + @{ + Name = 'sshd_config_default' + }, + @{ + Name = 'install-sshd.ps1' + }, + @{ + Name = 'uninstall-sshd.ps1' + }, + @{ + Name = 'FixHostFilePermissions.ps1' + }, + @{ + Name = 'FixUserFilePermissions.ps1' + }, + @{ + Name = 'OpenSSHUtils.psm1' + }, + @{ + Name = 'OpenSSHUtils.psd1' + }, + @{ + Name = 'openssh-events.man' + } + ) + + $dataFile1 = @( + @{ + Name = "sshd_config" + } + @{ + Name = "logs" + IsDirectory = $true + } + ) + } + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Openssh binary files--" -TestCases:$binaries{ + param([string]$Name, [boolean]$IsDirectory = $false) + ValidateFileSystem -FilePath (join-path $binPath $Name) + } + It "$tC.$tI - Validate Openssh script files--" -TestCases:$dataFile { + param([string]$Name, [boolean]$IsDirectory = $false) + if(-not $WindowsInbox) { ValidateFileSystem -FilePath (join-path $binPath $Name) } + } + + It "$tC.$tI - Validate data files--" -TestCases:$dataFile1 { + param([string]$Name, [boolean]$IsDirectory = $false) + if(-not (Test-Path $dataPath -PathType Container)) + { + Start-Service sshd + } + + ValidateFileSystem -FilePath (join-path $dataPath $Name) -IsDirectory $IsDirectory -OwnerSid $adminsSid -IsDataFile + } + } + + Context "$tC - Validate Openssh registry entries" { + BeforeAll { + $tI=1 + $servicePath = "HKLM:\SYSTEM\ControlSet001\Services" + $opensshRegPath = "HKLM:\SOFTWARE\OpenSSH" + + $opensshACLs = @( + @{ + Identity=$systemSid + IsInherited = $false + RegistryRights = $RegFullControlPerm + PropagationFlags = "None" + }, + @{ + Identity=$adminsSid + IsInherited = $false + RegistryRights = $RegFullControlPerm + PropagationFlags = "None" + }, + @{ + Identity=$authenticatedUserSid + IsInherited = $false + RegistryRights = $RegReadKeyPerm -bor ([System.UInt32] [System.Security.AccessControl.RegistryRights]::SetValue.value__) + PropagationFlags = "None" + } + ) + } + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Registry key ssh-agent\Description" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Description" + $p | Should Not Be $null + } + + It "$tC.$tI - Validate Registry key ssh-agent\ErrorControl" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "ErrorControl" + $p | Should Be 1 + } + + It "$tC.$tI - Validate Registry key ssh-agent\ImagePath" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "ImagePath" + $imagePath = (Join-Path $binPath "ssh-agent.exe").ToLower() + $p | Should Match "[`"]?$($imagePath.Replace("\", "\\"))[`"]?" + } + + It "$tC.$tI - Validate Registry key ssh-agent\ObjectName" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "ObjectName" + $p | Should Be "LocalSystem" + } + + It "$tC.$tI - Validate Registry key ssh-agent\Start" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Start" + $p | Should Be 3 + } + + It "$tC.$tI - Validate Registry key ssh-agent\Type" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Type" + $p | Should Be 16 + } + + It "$tC.$tI - Validate Registry key to ssh-agent\Security\Security" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent\Security") -Name Security + $p.Gettype() | Should Be byte[] + } + + It "$tC.$tI - Validate Registry key sshd\Description" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Description" + $p | Should not Be $null + } + + It "$tC.$tI - Validate Registry key sshd\ErrorControl" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "ErrorControl" + $p | Should Be 1 + } + + It "$tC.$tI - Validate Registry key sshd\ImagePath" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "ImagePath" + $imagePath = (Join-Path $binPath "sshd.exe").ToLower() + $p | Should Match "[`"]?$($imagePath.Replace("\", "\\"))[`"]?" + } + + It "$tC.$tI - Validate Registry key sshd\ObjectName" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "ObjectName" + $p | Should Be "LocalSystem" + } + + It "$tC.$tI - Validate Registry key sshd\Start" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Start" + $p | Should Be 3 + } + + It "$tC.$tI - Validate Registry key sshd\Type" { + $p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Type" + $p | Should Be 16 + } + + It "$tC.$tI - Validate Registry openssh entry" { + ValidateRegistryACL -RegPath $opensshRegPath -IdAcls $opensshACLs + } + It "$tC.$tI - Validate Registry openssh\agent entry" { + $agentPath = Join-Path $opensshRegPath "Agent" + if(-not (Test-Path $agentPath -PathType Container)) + { + Start-Service ssh-agent + } + + ValidateRegistryACL -RegPath $agentPath -IdAcls $opensshACLs + } + } + + Context "$tC - Validate service settings" { + BeforeAll { + $tI=1 + } + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate properties of ssh-agent service" { + $sshdSvc = Get-service ssh-agent + $sshdSvc.StartType | Should Be ([System.ServiceProcess.ServiceStartMode]::Manual) + $sshdSvc.ServiceType | Should Be ([System.ServiceProcess.ServiceType]::Win32OwnProcess) + $sshdSvc.ServiceName | Should Be "ssh-agent" + $sshdSvc.DisplayName | Should BeLike "OpenSSH*" + $sshdSvc.Name | Should Be "ssh-agent" + ($sshdSvc.DependentServices).Count | Should Be 0 + ($sshdSvc.ServicesDependedOn).Count | Should Be 0 + ($sshdSvc.RequiredServices).Count | Should Be 0 + } + + It "$tC.$tI - Validate properties of sshd service" { + $sshdSvc = Get-service sshd + $sshdSvc.StartType | Should Be ([System.ServiceProcess.ServiceStartMode]::Manual) + $sshdSvc.ServiceType | Should Be ([System.ServiceProcess.ServiceType]::Win32OwnProcess) + $sshdSvc.ServiceName | Should Be "sshd" + $sshdSvc.DisplayName | Should BeLike "OpenSSH*" + $sshdSvc.Name | Should Be "sshd" + ($sshdSvc.DependentServices).Count | Should Be 0 + ($sshdSvc.ServicesDependedOn).Count | Should Be 0 + ($sshdSvc.RequiredServices).Count | Should Be 0 + } + + It "$tC.$tI - Validate RequiredPrivileges of ssh-agent" { + $a = sc.exe qprivs ssh-agent 256 + $p = @($a | % { if($_ -match "Se[\w]+Privilege" ) {$start = $_.IndexOf("Se");$_.Substring($start, $_.length-$start)}}) + $p.count | Should Be 1 + $p[0] | Should Be "SeImpersonatePrivilege" + } + + It "$tC.$tI - Validate RequiredPrivileges of sshd" { + $expected = @("SeAssignPrimaryTokenPrivilege", "SeTcbPrivilege", "SeBackupPrivilege", "SeRestorePrivilege", "SeImpersonatePrivilege") + $a = sc.exe qprivs sshd 256 + $p = $a | % { if($_ -match "Se[\w]+Privilege" ) {$start = $_.IndexOf("Se");$_.Substring($start, $_.length-$start)}} + $expected | % { + $_ | Should BeIn $p + } + + $p | % { + $_ | Should BeIn $expected + } + } + + It "$tC.$tI - Validate security access to ssh-agent service" { + $a = @(sc.exe sdshow ssh-agent) + $b = $a[-1] -split "[D|S]:" + + $expected_dacl_aces = @("(A;;CCLCSWRPWPDTLOCRRC;;;SY)", "(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)", "(A;;CCLCSWLOCRRC;;;IU)", "(A;;CCLCSWLOCRRC;;;SU)", "(A;;RP;;;AU)") + $c = @($b | ? { -not [string]::IsNullOrWhiteSpace($_) }) + $dacl = $c[0] + $dacl_aces = $dacl -split "(\([;|\w]+\))" + $actual_dacl_aces = $dacl_aces | ? { -not [string]::IsNullOrWhiteSpace($_) } + + $expected_dacl_aces | % { + $_ | Should BeIn $actual_dacl_aces + } + $actual_dacl_aces | % { + $_ | Should BeIn $expected_dacl_aces + } + + <# ignore sacl for now + if($c.Count -gt 1) { + $c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)" + }#> + } + + It "$tC.$tI - Validate security access to sshd service" { + $a = @(sc.exe sdshow sshd) + $b = $a[-1] -split "[D|S]:" + + $expected_dacl_aces = @("(A;;CCLCSWRPWPDTLOCRRC;;;SY)", "(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)", "(A;;CCLCSWLOCRRC;;;IU)", "(A;;CCLCSWLOCRRC;;;SU)") + $c = @($b | ? { -not [string]::IsNullOrWhiteSpace($_) }) + $dacl = $c[0] + $dacl_aces = $dacl -split "(\([;|\w]+\))" + $actual_dacl_aces = $dacl_aces | ? { -not [string]::IsNullOrWhiteSpace($_) } + + $expected_dacl_aces | % { + $_ | Should BeIn $actual_dacl_aces + } + $actual_dacl_aces | % { + $_ | Should BeIn $expected_dacl_aces + } + + <# ignore sacl for now + if($c.Count -gt 1) { + $c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)" + }#> + } + } + + Context "$tC - Validate Firewall settings" { + BeforeAll { + $firwallRuleName = "OpenSSH-Server-In-TCP" + $tI=1 + } + + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Firewall settings" -skip:(!$windowsInBox) { + $rule = Get-NetFirewallRule -Name $firwallRuleName + $rule.Group | Should BeLike "OpenSSH*" + $rule.Description | Should BeLike "*OpenSSH*" + $rule.DisplayName | Should BeLike "OpenSSH*" + $rule.Enabled | Should Be $true + $rule.Profile.ToString() | Should Be 'Any' + $rule.Direction.ToString() | Should Be 'Inbound' + $rule.Action.ToString() | Should Be 'Allow' + $rule.StatusCode | Should Be 65536 + $fwportFilter = $rule | Get-NetFirewallPortFilter + $fwportFilter.Protocol | Should Be 'TCP' + $fwportFilter.LocalPort | Should Be 22 + $fwportFilter.RemotePort | Should Be 'Any' + } + } +} diff --git a/regress/pesterTests/Uninstall.Tests.ps1 b/regress/pesterTests/Uninstall.Tests.ps1 new file mode 100644 index 000000000..5d20e45fd --- /dev/null +++ b/regress/pesterTests/Uninstall.Tests.ps1 @@ -0,0 +1,196 @@ +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force +$suite = "Uninstall" +$tC = 1 +$tI = 0 +Describe "Uninstall Tests" -Tags "Uninstall" { + BeforeAll { + if($OpenSSHTestInfo -eq $null) + { + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." + } + + $windowsInBox = $OpenSSHTestInfo["WindowsInBox"] + $binPath = $OpenSSHTestInfo["OpenSSHBinPath"] + $dataPath = Join-path $env:ProgramData ssh + + Stop-Service sshd -ErrorAction SilentlyContinue + Stop-Service ssh-agent -ErrorAction SilentlyContinue + if(Get-Service sshd -ErrorAction SilentlyContinue) + { + if($windowsInBox) { + Remove-WindowsCapability -online -name OpenSSH.Server~~~~0.0.1.0 + } + else { + & (Join-Path $binPath "uninstall-sshd.ps1") + } + } + if(Get-Service ssh-agent -ErrorAction SilentlyContinue) + { + if($windowsInBox) { + Remove-WindowsCapability -online -name OpenSSH.Client~~~~0.0.1.0 + } + else + { + & (Join-Path $binPath "uninstall-sshd.ps1") + } + } + + + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $authenticatedUserSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::AuthenticatedUserSid) + + $RegReadKeyPerm = ([System.UInt32] [System.Security.AccessControl.RegistryRights]::ReadKey.value__) + $RegFullControlPerm = [System.UInt32] [System.Security.AccessControl.RegistryRights]::FullControl.value__ + + #only validate owner and ACEs of the registry + function ValidateRegistryACL { + param([string]$RegPath, $Ownersid = $adminsSid, $IdAcls) + Test-Path -Path $RegPath | Should Be $true + $myACL = Get-ACL $RegPath + $OwnerSid = Get-UserSid -User $myACL.Owner + $OwnerSid.Equals($Ownersid) | Should Be $true + $myACL.Access | Should Not Be $null + $CAPABILITY_SID = "S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681" + $nonPropagate = $myACL.Access | ? {($_.PropagationFlags -eq ([System.Security.AccessControl.PropagationFlags]::None)) -and ($_.IdentityReference -ine $CAPABILITY_SID)} + + foreach ($a in $nonPropagate) { + $findItem = $IdAcls | ? { + ($a.IdentityReference -eq (Get-UserAccount -UserSid ($_.Identity))) -and ` + ($a.IsInherited -eq $_.IsInherited) -and ` + ($a.AccessControlType -eq ([System.Security.AccessControl.AccessControlType]::Allow)) -and ` + ($a.PropagationFlags -eq ([System.Security.AccessControl.PropagationFlags]::None) -and ` + (([System.Int32]$a.RegistryRights.value__) -eq ($_.RegistryRights))) + } + $findItem | Should Not Be $null + } + } + } + + Context "$tC - Validate Openssh binary files" { + BeforeAll { + if(-not $Windowsbox) + { + $binaries = $null + return + } + $tI=1 + $binaries = @( + @{ + Name = 'sshd.exe' + }, + @{ + Name = 'ssh.exe' + }, + @{ + Name = 'ssh-agent.exe' + }, + @{ + Name = 'ssh-add.exe' + }, + @{ + Name = 'sftp.exe' + }, + @{ + Name = 'sftp-server.exe' + }, + @{ + Name = 'scp.exe' + }, + @{ + Name = 'ssh-shellhost.exe' + }, + @{ + Name = 'ssh-agent.exe' + }, + @{ + Name = 'ssh-keyscan.exe' + } + ) + } + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Openssh binary files-- is removed" -TestCases:$binaries{ + param([string]$Name, [boolean]$IsDirectory = $false) + if(-not [string]::IsNullOrWhiteSpace($Name)) { + (join-path $binPath $Name) | Should Not Exist + } + } + } + + Context "$tC - Validate Openssh registry entries" { + BeforeAll { + $tI=1 + $servicePath = "HKLM:\SYSTEM\ControlSet001\Services" + $opensshRegPath = "HKLM:\SOFTWARE\OpenSSH" + + $opensshACLs = @( + @{ + Identity=$systemSid + IsInherited = $false + RegistryRights = $RegFullControlPerm + PropagationFlags = "None" + }, + @{ + Identity=$adminsSid + IsInherited = $false + RegistryRights = $RegFullControlPerm + PropagationFlags = "None" + }, + @{ + Identity=$authenticatedUserSid + IsInherited = $false + RegistryRights = $RegReadKeyPerm -bor ([System.UInt32] [System.Security.AccessControl.RegistryRights]::SetValue.value__) + PropagationFlags = "None" + } + ) + } + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Registry key ssh-agent is removed" { + (Join-Path $servicePath "ssh-agent") | Should Not Exist + } + + It "$tC.$tI - Validate Registry key sshd is removed" { + (Join-Path $servicePath "sshd") | Should Not Exist + } + + It "$tC.$tI - Validate Registry openssh entry" { + ValidateRegistryACL -RegPath $opensshRegPath -IdAcls $opensshACLs + } + } + + Context "$tC - Validate service is removed" { + BeforeAll { + $tI=1 + } + + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate ssh-agent is removed" { + Get-Service ssh-agent -ErrorAction SilentlyContinue | Should Be $null + } + + It "$tC.$tI - Validate sshd is removed" { + Get-Service sshd -ErrorAction SilentlyContinue | Should Be $null + } + } + + Context "$tC - Validate Firewall settings" { + BeforeAll { + $firwallRuleName = "OpenSSH-Server-In-TCP" + $tI=1 + } + + AfterAll{$tC++} + AfterEach { $tI++ } + + It "$tC.$tI - Validate Firewall settings" -skip:(!$windowsInBox) { + Get-NetFirewallRule -Name $firwallRuleName -ErrorAction SilentlyContinue | Should Be $null + } + } +}