mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-31 01:35:11 +02:00
Compare commits
27 Commits
v9.8.1.0
...
latestw_al
Author | SHA1 | Date | |
---|---|---|---|
|
139a1b413d | ||
|
c1a8d54998 | ||
|
a2d4e942df | ||
|
bdacf9868f | ||
|
cb6bfef9c1 | ||
|
efa17c848b | ||
|
e0ae79ff3c | ||
|
fdde2326f9 | ||
|
de4c0c7c59 | ||
|
31f8d13ab6 | ||
|
ae72d833fd | ||
|
0096029101 | ||
|
a96b3fbae4 | ||
|
8514f78233 | ||
|
41734eb591 | ||
|
8fe096c7b7 | ||
|
b36bc85f47 | ||
|
7baad0a474 | ||
|
86bc0d7df9 | ||
|
cdcc8d34d8 | ||
|
0c3137f621 | ||
|
265df19787 | ||
|
348084cc9a | ||
|
0dd6d2cd21 | ||
|
27f6cfa7b0 | ||
|
796d297a66 | ||
|
a915f06c78 |
13
.azdo/ci.yml
13
.azdo/ci.yml
@ -89,6 +89,8 @@ stages:
|
||||
pool:
|
||||
vmImage: windows-latest
|
||||
displayName: Win32-OpenSSH On Windows
|
||||
variables:
|
||||
testFilesDrivePath: '**'
|
||||
steps:
|
||||
- powershell: |
|
||||
$powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell'
|
||||
@ -164,6 +166,17 @@ stages:
|
||||
Invoke-OpenSSHTests -OpenSSHBinPath "$env:SystemDrive/OpenSSH"
|
||||
displayName: Run tests
|
||||
|
||||
- pwsh: |
|
||||
Write-Host "##vso[task.setvariable variable=testFilesDrivePath;]$env:SystemDrive"
|
||||
displayName: Set variable
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '$(testFilesDrivePath)/OpenSSHTests/*.xml'
|
||||
failTaskOnFailedTests: true
|
||||
condition: always()
|
||||
|
||||
- pwsh: |
|
||||
Import-Module -Name "$(Build.SourcesDirectory)/contrib/win32/openssh/AzDOBuildTools" -Force
|
||||
#
|
||||
|
@ -515,11 +515,19 @@ sshkey_save_public(const struct sshkey *key, const char *path,
|
||||
|
||||
if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
|
||||
return SSH_ERR_SYSTEM_ERROR;
|
||||
#ifdef WINDOWS
|
||||
/* Windows POSIX adapter does not support fdopen() on open(file)
|
||||
but still want file created with same owner as upstream */
|
||||
close(fd);
|
||||
if ((f = fopen(path, "w")) == NULL)
|
||||
return SSH_ERR_SYSTEM_ERROR;
|
||||
#else /* WINDOWS */
|
||||
if ((f = fdopen(fd, "w")) == NULL) {
|
||||
r = SSH_ERR_SYSTEM_ERROR;
|
||||
close(fd);
|
||||
goto fail;
|
||||
}
|
||||
#endif /* WINDOWS */
|
||||
if ((r = sshkey_write(key, f)) != 0)
|
||||
goto fail;
|
||||
fprintf(f, " %s\n", comment);
|
||||
|
@ -35,13 +35,13 @@ $Script:PostmortemDebugging = $false
|
||||
<#
|
||||
.Synopsis
|
||||
Set-OpenSSHTestEnvironment
|
||||
TODO - split these steps into client and server side
|
||||
TODO - split these steps into client and server side
|
||||
#>
|
||||
function Set-OpenSSHTestEnvironment
|
||||
{
|
||||
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
|
||||
param
|
||||
(
|
||||
(
|
||||
[string] $OpenSSHBinPath,
|
||||
[string] $TestDataPath = "$env:SystemDrive\OpenSSHTests",
|
||||
[Switch] $DebugMode,
|
||||
@ -54,7 +54,7 @@ function Set-OpenSSHTestEnvironment
|
||||
$params.Remove("DebugMode") | Out-Null
|
||||
$params.Remove("NoAppVerifier") | Out-Null
|
||||
$params.Remove("PostmortemDebugging") | Out-Null
|
||||
|
||||
|
||||
if($PSBoundParameters.ContainsKey("Verbose"))
|
||||
{
|
||||
$verboseInfo = ($PSBoundParameters['Verbose']).IsPresent
|
||||
@ -72,6 +72,7 @@ function Set-OpenSSHTestEnvironment
|
||||
$Global:OpenSSHTestInfo.Add("TestAccountPW", $OpenSSHTestAccountsPassword) # common password for all test accounts
|
||||
$Global:OpenSSHTestInfo.Add("DebugMode", $DebugMode.IsPresent) # run openssh E2E in debug mode
|
||||
$Global:OpenSSHTestInfo.Add("DelayTime", 3) # delay between stoppig sshd service and trying to access log files
|
||||
$Global:OpenSSHTestInfo.Add("SshdServiceName", $SSHDTestSvcName) # sshd service name
|
||||
|
||||
$Script:EnableAppVerifier = -not ($NoAppVerifier.IsPresent)
|
||||
if($Script:WindowsInBox = $true)
|
||||
@ -83,7 +84,7 @@ function Set-OpenSSHTestEnvironment
|
||||
if($Script:EnableAppVerifier)
|
||||
{
|
||||
$Script:PostmortemDebugging = $PostmortemDebugging.IsPresent
|
||||
}
|
||||
}
|
||||
$Global:OpenSSHTestInfo.Add("PostmortemDebugging", $Script:PostmortemDebugging)
|
||||
|
||||
$description = @"
|
||||
@ -94,8 +95,8 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
- test accounts - ssouser, pubkeyuser, and passwduser will be added
|
||||
- Setup single signon for ssouser
|
||||
- To cleanup - Run Clear-OpenSSHTestEnvironment
|
||||
"@
|
||||
|
||||
"@
|
||||
|
||||
$prompt = "Are you sure you want to perform the above operations?"
|
||||
$caption = $description
|
||||
if(-not $pscmdlet.ShouldProcess($description, $prompt, $caption))
|
||||
@ -108,7 +109,7 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
|
||||
##### START: install sshd test service
|
||||
#delete service if exists
|
||||
if (Get-Service $SSHDTestSvcName -ErrorAction SilentlyContinue)
|
||||
if (Get-Service $SSHDTestSvcName -ErrorAction SilentlyContinue)
|
||||
{
|
||||
Stop-Service $SSHDTestSvcName
|
||||
sc.exe delete $SSHDTestSvcName 1>$null
|
||||
@ -119,22 +120,22 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
Remove-Item $testSvcConfigDir -Force -Recurse -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $testSvcConfigDir
|
||||
$Global:OpenSSHTestInfo["ServiceConfigDir"] = $testSvcConfigDir
|
||||
|
||||
|
||||
#copy sshd_config
|
||||
$testSshdConfig = Join-Path $testSvcConfigDir sshd_config
|
||||
Copy-Item (Join-Path $Script:E2ETestDataDirectory sshd_config) $testSshdConfig -Force
|
||||
$con = (Get-Content $testSshdConfig | Out-String).Replace("___TEST_SERVICE_CONFIG_DIR___", $testSvcConfigDir)
|
||||
Set-Content -Path $testSshdConfig -Value "$con" -Force
|
||||
Set-Content -Path $testSshdConfig -Value "$con" -Force
|
||||
if($DebugMode) {
|
||||
$con = (Get-Content $testSshdConfig | Out-String).Replace("#SyslogFacility AUTH","SyslogFacility LOCAL0")
|
||||
Set-Content -Path $testSshdConfig -Value "$con" -Force
|
||||
Set-Content -Path $testSshdConfig -Value "$con" -Force
|
||||
}
|
||||
|
||||
#copy sshtest keys
|
||||
Copy-Item "$($Script:E2ETestDataDirectory)\sshtest*hostkey*" $testSvcConfigDir -Force
|
||||
|
||||
Copy-Item "$($Script:E2ETestDataDirectory)\sshtest*hostkey*" $testSvcConfigDir -Force
|
||||
|
||||
#copy ca pubkey to ssh config path
|
||||
Copy-Item "$($Script:E2ETestDataDirectory)\sshtest_ca_userkeys.pub" $testSvcConfigDir -Force
|
||||
Copy-Item "$($Script:E2ETestDataDirectory)\sshtest_ca_userkeys.pub" $testSvcConfigDir -Force
|
||||
|
||||
$acl = New-Object System.Security.AccessControl.DirectorySecurity
|
||||
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","Allow")
|
||||
@ -156,7 +157,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:E2ETestDataDirectory sshtest_ca_userkeys) $ca_priv_key -Force
|
||||
Copy-Item (Join-Path $Script:E2ETestDataDirectory sshtest_ca_userkeys) $ca_priv_key -Force
|
||||
Repair-UserSshConfigPermission -FilePath $ca_priv_key -confirm:$false
|
||||
$Global:OpenSSHTestInfo["CA_Private_Key"] = $ca_priv_key
|
||||
|
||||
@ -180,16 +181,16 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
}
|
||||
$con = Get-Content $knowHostsFilePath
|
||||
if (($con -eq $null) -or (-not($con.Contains("###OpenSSHE2ETests")))) {
|
||||
Get-Content (Join-Path $Script:E2ETestDataDirectory known_hosts) | Add-Content $knowHostsFilePath
|
||||
Get-Content (Join-Path $Script:E2ETestDataDirectory known_hosts) | Add-Content $knowHostsFilePath
|
||||
}
|
||||
|
||||
$sshConfigFilePath = Join-Path $dotSshDirectoryPath config
|
||||
if (-not (Test-Path (Join-Path $dotSshDirectoryPath config) -PathType Leaf)) {
|
||||
Copy-Item (Join-Path $Script:E2ETestDataDirectory ssh_config) $sshConfigFilePath -Force
|
||||
Copy-Item (Join-Path $Script:E2ETestDataDirectory ssh_config) $sshConfigFilePath -Force
|
||||
}
|
||||
$con = Get-Content $sshConfigFilePath
|
||||
if (($con -eq $null) -or (-not($con.Contains("###OpenSSHE2ETests")))) {
|
||||
Get-Content (Join-Path $Script:E2ETestDataDirectory ssh_config) | Add-Content $sshConfigFilePath
|
||||
Get-Content (Join-Path $Script:E2ETestDataDirectory ssh_config) | Add-Content $sshConfigFilePath
|
||||
}
|
||||
|
||||
Copy-Item (Join-Path $Script:E2ETestDataDirectory ssh_config) $sshConfigFilePath -Force
|
||||
@ -206,7 +207,7 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
}
|
||||
catch
|
||||
{
|
||||
#only add the local user when it does not exists on the machine
|
||||
#only add the local user when it does not exists on the machine
|
||||
net user $user $Script:OpenSSHTestAccountsPassword /ADD 2>&1 >> $Script:TestSetupLogFile
|
||||
}
|
||||
}
|
||||
@ -231,17 +232,17 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
$authorizedKeyPath = Join-Path $ssouserProfile .ssh\authorized_keys
|
||||
$testPubKeyPath = Join-Path $Script:E2ETestDataDirectory sshtest_userssokey_ed25519.pub
|
||||
Copy-Item $testPubKeyPath $authorizedKeyPath -Force -ErrorAction SilentlyContinue
|
||||
Repair-AuthorizedKeyPermission -FilePath $authorizedKeyPath -confirm:$false
|
||||
|
||||
Repair-AuthorizedKeyPermission -FilePath $authorizedKeyPath -confirm:$false
|
||||
|
||||
copy-item (Join-Path $Script:E2ETestDataDirectory sshtest_userssokey_ed25519) $Global:OpenSSHTestInfo["TestDataPath"]
|
||||
$testPriKeypath = Join-Path $Global:OpenSSHTestInfo["TestDataPath"] sshtest_userssokey_ed25519
|
||||
$testPriKeypath = Join-Path $Global:OpenSSHTestInfo["TestDataPath"] sshtest_userssokey_ed25519
|
||||
cmd /c "ssh-add -D 2>&1 >> $Script:TestSetupLogFile"
|
||||
Repair-UserKeyPermission -FilePath $testPriKeypath -confirm:$false
|
||||
cmd /c "ssh-add $testPriKeypath 2>&1 >> $Script:TestSetupLogFile"
|
||||
|
||||
#Enable AppVerifier
|
||||
if($Script:EnableAppVerifier)
|
||||
{
|
||||
{
|
||||
# clear all applications in application verifier first
|
||||
& $env:windir\System32\appverif.exe -disable * -for * | out-null
|
||||
Get-ChildItem "$($script:OpenSSHBinPath)\*.exe" | % {
|
||||
@ -249,8 +250,8 @@ WARNING: Following changes will be made to OpenSSH configuration
|
||||
}
|
||||
|
||||
if($Script:PostmortemDebugging -and (Test-path $Script:WindbgPath))
|
||||
{
|
||||
# enable Postmortem debugger
|
||||
{
|
||||
# enable Postmortem debugger
|
||||
New-ItemProperty "HKLM:Software\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Debugger -Type String -Value "`"$Script:WindbgPath`" -p %ld -e %ld -g" -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
New-ItemProperty "HKLM:Software\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Auto -Type String -Value "1" -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
@ -261,9 +262,9 @@ function Set-BasicTestInfo
|
||||
{
|
||||
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
|
||||
param
|
||||
(
|
||||
(
|
||||
[string] $OpenSSHBinPath,
|
||||
[string] $TestDataPath = "$env:SystemDrive\OpenSSHTests",
|
||||
[string] $TestDataPath = "$env:SystemDrive\OpenSSHTests",
|
||||
[Switch] $NoLibreSSL
|
||||
)
|
||||
|
||||
@ -276,7 +277,7 @@ function Set-BasicTestInfo
|
||||
$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:UnitTestResultsFile = Join-Path $TestDataPath $UnitTestResultsFileName
|
||||
$Script:TestSetupLogFile = Join-Path $TestDataPath $TestSetupLogFileName
|
||||
$Script:UnitTestDirectory = Get-UnitTestDirectory
|
||||
$Script:NoLibreSSL = $NoLibreSSL.IsPresent
|
||||
@ -296,7 +297,7 @@ function Set-BasicTestInfo
|
||||
#if user does not set path, pick it up
|
||||
if([string]::IsNullOrEmpty($OpenSSHBinPath))
|
||||
{
|
||||
$sshcmd = get-command ssh.exe -ErrorAction SilentlyContinue
|
||||
$sshcmd = get-command ssh.exe -ErrorAction SilentlyContinue
|
||||
if($sshcmd -eq $null)
|
||||
{
|
||||
Throw "Cannot find ssh.exe. Please specify -OpenSSHBinPath to the OpenSSH installed location."
|
||||
@ -305,7 +306,7 @@ function Set-BasicTestInfo
|
||||
{
|
||||
$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)?"
|
||||
$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))
|
||||
{
|
||||
@ -313,7 +314,7 @@ function Set-BasicTestInfo
|
||||
return
|
||||
}
|
||||
$script:OpenSSHBinPath = $dirToCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -332,7 +333,7 @@ function Set-BasicTestInfo
|
||||
}
|
||||
|
||||
$acl = get-acl (join-path $script:OpenSSHBinPath "ssh.exe")
|
||||
|
||||
|
||||
if($acl.Owner -ieq "NT SERVICE\TrustedInstaller")
|
||||
{
|
||||
$Script:WindowsInBox = $true
|
||||
@ -352,7 +353,7 @@ function Get-LocalUserProfile
|
||||
param([string]$User)
|
||||
$sid = Get-UserSID -User $User
|
||||
$userProfileRegistry = Join-Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $sid
|
||||
if (-not (Test-Path $userProfileRegistry) ) {
|
||||
if (-not (Test-Path $userProfileRegistry) ) {
|
||||
#create profile
|
||||
if (-not($env:DISPLAY)) { $env:DISPLAY = 1 }
|
||||
$askpass_util = Join-Path $Script:E2ETestDirectory "utilities\askpass_util\askpass_util.exe"
|
||||
@ -364,9 +365,9 @@ function Get-LocalUserProfile
|
||||
Remove-item "env:SSH_ASKPASS" -ErrorAction SilentlyContinue
|
||||
Remove-item "env:ASKPASS_PASSWORD" -ErrorAction SilentlyContinue
|
||||
Remove-item "env:SSH_ASKPASS_REQUIRE" -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
(Get-ItemProperty -Path $userProfileRegistry -Name 'ProfileImagePath').ProfileImagePath
|
||||
}
|
||||
|
||||
(Get-ItemProperty -Path $userProfileRegistry -Name 'ProfileImagePath').ProfileImagePath
|
||||
}
|
||||
|
||||
|
||||
@ -380,10 +381,10 @@ function Install-OpenSSHTestDependencies
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param ([Switch] $TestHarness)
|
||||
|
||||
|
||||
#$isOpenSSHUtilsAvailable = Get-Module 'OpenSSHUtils' -ListAvailable
|
||||
#if (-not ($isOpenSSHUtilsAvailable))
|
||||
#{
|
||||
#{
|
||||
Write-Log -Message "Installing Module OpenSSHUtils..."
|
||||
Install-OpenSSHUtilsModule -SourceDir $PSScriptRoot
|
||||
#}
|
||||
@ -404,8 +405,8 @@ function Install-OpenSSHTestDependencies
|
||||
# Pester 5.x is not compatible with tests.
|
||||
$InstalledPesters = Get-Module -Name 'Pester' -ListAvailable | Where-Object { $_.Version -lt '5.0' }
|
||||
if ($InstalledPesters.Count -eq 0)
|
||||
{
|
||||
Write-Log -Message "Installing Pester..."
|
||||
{
|
||||
Write-Log -Message "Installing Pester..."
|
||||
# Install-Module -Name 'Pester' -RequiredVersion 3.4.6
|
||||
choco install Pester --version 3.4.6 -y --force --limitoutput 2>&1 >> $Script:TestSetupLogFile
|
||||
}
|
||||
@ -431,8 +432,8 @@ function Install-OpenSSHTestDependencies
|
||||
if(-not (Test-Path $Script:WindbgPath))
|
||||
{
|
||||
choco install windbg -y --force --limitoutput 2>&1 >> $Script:TestSetupLogFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(($Script:EnableAppVerifier -or (($OpenSSHTestInfo -ne $null) -and ($OpenSSHTestInfo["EnableAppVerifier"]))) -and (-not (Test-path $env:windir\System32\appverif.exe)))
|
||||
@ -444,19 +445,19 @@ function Install-OpenSSHTestDependencies
|
||||
function Install-OpenSSHUtilsModule
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
param(
|
||||
[string]$TargetDir = (Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\OpenSSHUtils"),
|
||||
[string]$SourceDir)
|
||||
|
||||
$manifestFile = Join-Path -Path $SourceDir -ChildPath OpenSSHUtils.psd1
|
||||
|
||||
$manifestFile = Join-Path -Path $SourceDir -ChildPath OpenSSHUtils.psd1
|
||||
$moduleFile = Join-Path -Path $SourceDir -ChildPath OpenSSHUtils.psm1
|
||||
$targetDirectory = $TargetDir
|
||||
$manifest = Test-ModuleManifest -Path $manifestFile -WarningAction SilentlyContinue -ErrorAction Stop
|
||||
if ($PSVersionTable.PSVersion.Major -ge 5)
|
||||
{
|
||||
{
|
||||
$targetDirectory = Join-Path -Path $targetDir -ChildPath $manifest.Version.ToString()
|
||||
}
|
||||
|
||||
|
||||
$modulePath = Join-Path -Path $env:ProgramFiles -ChildPath WindowsPowerShell\Modules
|
||||
if(-not (Test-Path "$targetDirectory" -PathType Container))
|
||||
{
|
||||
@ -464,7 +465,7 @@ function Install-OpenSSHUtilsModule
|
||||
}
|
||||
Copy-item "$manifestFile" -Destination "$targetDirectory" -Force -ErrorAction SilentlyContinue | out-null
|
||||
Copy-item "$moduleFile" -Destination "$targetDirectory" -Force -ErrorAction SilentlyContinue | out-null
|
||||
|
||||
|
||||
if ($PSVersionTable.PSVersion.Major -lt 4)
|
||||
{
|
||||
$modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'
|
||||
@ -488,12 +489,12 @@ function Install-OpenSSHUtilsModule
|
||||
function Uninstall-OpenSSHUtilsModule
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param([string]$TargetDir = (Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\OpenSSHUtils"))
|
||||
|
||||
param([string]$TargetDir = (Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\OpenSSHUtils"))
|
||||
|
||||
if(Test-Path $TargetDir -PathType Container)
|
||||
{
|
||||
Remove-item $TargetDir -Recurse -Force -ErrorAction SilentlyContinue | out-null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
@ -503,13 +504,13 @@ function Uninstall-OpenSSHUtilsModule
|
||||
function Get-UserSID
|
||||
{
|
||||
param
|
||||
(
|
||||
[string]$Domain,
|
||||
(
|
||||
[string]$Domain,
|
||||
[string]$User
|
||||
)
|
||||
if([string]::IsNullOrEmpty($Domain))
|
||||
{
|
||||
$objUser = New-Object System.Security.Principal.NTAccount($User)
|
||||
$objUser = New-Object System.Security.Principal.NTAccount($User)
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -533,12 +534,12 @@ function Clear-OpenSSHTestEnvironment
|
||||
|
||||
$sshBinPath = $Global:OpenSSHTestInfo["OpenSSHBinPath"]
|
||||
|
||||
# .exe - Windows specific. TODO - PAL
|
||||
# .exe - Windows specific. TODO - PAL
|
||||
if (-not (Test-Path (Join-Path $sshBinPath ssh.exe) -PathType Leaf))
|
||||
{
|
||||
Throw "Cannot find OpenSSH binaries under $script:OpenSSHBinPath. "
|
||||
}
|
||||
|
||||
|
||||
if($Global:OpenSSHTestInfo["EnableAppVerifier"] -and (Test-path $env:windir\System32\appverif.exe))
|
||||
{
|
||||
# clear all applications in application verifier
|
||||
@ -552,19 +553,19 @@ function Clear-OpenSSHTestEnvironment
|
||||
}
|
||||
|
||||
#delete service if exists
|
||||
if (Get-Service $SSHDTestSvcName -ErrorAction SilentlyContinue)
|
||||
if (Get-Service $SSHDTestSvcName -ErrorAction SilentlyContinue)
|
||||
{
|
||||
Stop-Service $SSHDTestSvcName
|
||||
sc.exe delete $SSHDTestSvcName 1>$null
|
||||
}
|
||||
|
||||
|
||||
#Delete accounts
|
||||
foreach ($user in $OpenSSHTestAccounts)
|
||||
{
|
||||
net user $user /delete
|
||||
}
|
||||
|
||||
# remove registered keys
|
||||
|
||||
# remove registered keys
|
||||
cmd /c "ssh-add -d (Join-Path $Script:E2ETestDataDirectory sshtest_userssokey_ed25519) 2>&1 >> $Script:TestSetupLogFile"
|
||||
|
||||
if($Global:OpenSSHTestInfo -ne $null)
|
||||
@ -572,10 +573,10 @@ function Clear-OpenSSHTestEnvironment
|
||||
$Global:OpenSSHTestInfo.Clear()
|
||||
$Global:OpenSSHTestInfo = $null
|
||||
}
|
||||
|
||||
|
||||
$isOpenSSHUtilsAvailable = Get-Module 'OpenSSHUtils' -ListAvailable
|
||||
if ($isOpenSSHUtilsAvailable)
|
||||
{
|
||||
{
|
||||
Write-Log -Message "Uninstalling Module OpenSSHUtils..."
|
||||
Uninstall-OpenSSHUtilsModule
|
||||
}
|
||||
@ -632,7 +633,7 @@ function Get-UnitTestDirectory
|
||||
else
|
||||
{
|
||||
$RealConfiguration = $Configuration
|
||||
}
|
||||
}
|
||||
$unitTestdir = Join-Path $repositoryRoot.FullName -ChildPath "bin\$folderName\$RealConfiguration"
|
||||
$unitTestDir
|
||||
}
|
||||
@ -746,7 +747,7 @@ function Invoke-OpenSSHUnitTest
|
||||
{
|
||||
$Script:UnitTestDirectory = $OpenSSHTestInfo["UnitTestDirectory"]
|
||||
}
|
||||
|
||||
|
||||
Push-Location $Script:UnitTestDirectory
|
||||
Write-Log -Message "Running OpenSSH unit tests..."
|
||||
if (Test-Path $Script:UnitTestResultsFile)
|
||||
@ -763,7 +764,7 @@ function Invoke-OpenSSHUnitTest
|
||||
$unittestFile = "$(Split-Path $_ -Leaf).exe"
|
||||
$unittestFilePath = join-path $_ $unittestFile
|
||||
if(Test-Path $unittestFilePath -pathtype leaf)
|
||||
{
|
||||
{
|
||||
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$pinfo.FileName = "$unittestFilePath"
|
||||
$pinfo.RedirectStandardError = $true
|
||||
@ -790,13 +791,13 @@ function Invoke-OpenSSHUnitTest
|
||||
{
|
||||
$testfailed = $true
|
||||
$errorMessage = "$unittestFile failed.`nExitCode: $errorCode. Detail test log is at $($Script:UnitTestResultsFile)."
|
||||
Write-Warning $errorMessage
|
||||
Write-Warning $errorMessage
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "$unittestFile passed!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Pop-Location
|
||||
@ -804,7 +805,7 @@ function Invoke-OpenSSHUnitTest
|
||||
}
|
||||
|
||||
<#
|
||||
Write-Log
|
||||
Write-Log
|
||||
#>
|
||||
function Write-Log
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
<OpenSSH-Src-Path>$(SolutionDir)..\..\..\</OpenSSH-Src-Path>
|
||||
<OpenSSH-Bin-Path>$(SolutionDir)..\..\..\bin\</OpenSSH-Bin-Path>
|
||||
<OpenSSH-Lib-Path>$(SolutionDir)lib\</OpenSSH-Lib-Path>
|
||||
<LibreSSLVersion>3.9.2.0</LibreSSLVersion>
|
||||
<LibreSSLVersion>4.0.0.0</LibreSSLVersion>
|
||||
<ZLibVersion>1.3.1</ZLibVersion>
|
||||
<fido2Version>1.15.0</fido2Version>
|
||||
<!--libcbor version is not used in the build; it is needed for pipeline compliance tasks-->
|
||||
|
@ -12,7 +12,6 @@
|
||||
#ListenAddress ::
|
||||
|
||||
#HostKey __PROGRAMDATA__/ssh/ssh_host_rsa_key
|
||||
#HostKey __PROGRAMDATA__/ssh/ssh_host_dsa_key
|
||||
#HostKey __PROGRAMDATA__/ssh/ssh_host_ecdsa_key
|
||||
#HostKey __PROGRAMDATA__/ssh/ssh_host_ed25519_key
|
||||
|
||||
@ -61,12 +60,11 @@ AuthorizedKeysFile .ssh/authorized_keys
|
||||
#PrintMotd yes
|
||||
#PrintLastLog yes
|
||||
#TCPKeepAlive yes
|
||||
#UseLogin no
|
||||
#PermitUserEnvironment no
|
||||
#Compression delayed
|
||||
#ClientAliveInterval 0
|
||||
#ClientAliveCountMax 3
|
||||
#UseDNS no
|
||||
#PidFile /var/run/sshd.pid
|
||||
#MaxStartups 10:30:100
|
||||
#PermitTunnel no
|
||||
#ChrootDirectory none
|
||||
|
@ -51,8 +51,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 9,8,1,0
|
||||
PRODUCTVERSION 9,8,1,0
|
||||
FILEVERSION 9,8,3,0
|
||||
PRODUCTVERSION 9,8,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -67,9 +67,9 @@ BEGIN
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "FileVersion", "9.8.1.0"
|
||||
VALUE "FileVersion", "9.8.3.0"
|
||||
VALUE "ProductName", "OpenSSH for Windows"
|
||||
VALUE "ProductVersion", "OpenSSH_9.8p1 for Windows"
|
||||
VALUE "ProductVersion", "OpenSSH_9.8p2 for Windows"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -435,14 +435,20 @@ file_in_chroot_jail(HANDLE handle) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return file_in_chroot_jail_helper(final_path);
|
||||
}
|
||||
|
||||
/* returns 1 if true, 0 otherwise */
|
||||
int
|
||||
file_in_chroot_jail_helper(wchar_t* final_path) {
|
||||
/* ensure final path is within chroot */
|
||||
to_wlower_case(final_path);
|
||||
if ((wcslen(final_path) < wcslen(chroot_pathw)) ||
|
||||
memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
|
||||
final_path[wcslen(chroot_pathw)] != '\\') {
|
||||
memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
|
||||
final_path[wcslen(chroot_pathw)] != '\\') {
|
||||
debug3("access denied due to attempt to escape chroot jail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1268,6 +1274,7 @@ fileio_symlink(const char *target, const char *linkpath)
|
||||
DWORD ret = -1;
|
||||
char target_modified[PATH_MAX] = { 0 };
|
||||
char *linkpath_resolved = NULL, *target_resolved = NULL;
|
||||
wchar_t *linkpath_utf16 = NULL, *resolved_target_utf16 = NULL, *resolved_target_chroot = NULL;
|
||||
|
||||
if (target == NULL || linkpath == NULL) {
|
||||
errno = EFAULT;
|
||||
@ -1301,13 +1308,21 @@ fileio_symlink(const char *target, const char *linkpath)
|
||||
strcpy_s(target_modified, _countof(target_modified), target_resolved);
|
||||
}
|
||||
|
||||
wchar_t *linkpath_utf16 = resolved_path_utf16(linkpath);
|
||||
wchar_t *resolved_target_utf16 = utf8_to_utf16(target_modified);
|
||||
if (resolved_target_utf16 == NULL || linkpath_utf16 == NULL) {
|
||||
if ((linkpath_utf16 = resolved_path_utf16(linkpath)) == NULL ||
|
||||
(resolved_target_utf16 = utf8_to_utf16(target_modified)) == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* if chroot, get full path for target, similar to behavior in realpath() in misc.c
|
||||
note: _wfullpath() is required to resolve paths containing unicode characters */
|
||||
if (chroot_pathw != NULL &&
|
||||
(resolved_target_chroot = _wfullpath(NULL, resolved_target_utf16, 0)) != NULL &&
|
||||
file_in_chroot_jail_helper(resolved_target_chroot) != 1) {
|
||||
errno = EPERM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* unlike other platforms, we need to know whether the symbolic link target is
|
||||
* a file or a directory. the only way we can confidently do this is to
|
||||
* get the attributes of the target. therefore, our symlink() has the
|
||||
@ -1338,15 +1353,18 @@ fileio_symlink(const char *target, const char *linkpath)
|
||||
ret = 0;
|
||||
cleanup:
|
||||
|
||||
if (linkpath_resolved)
|
||||
free(linkpath_resolved);
|
||||
|
||||
if (linkpath_utf16)
|
||||
free(linkpath_utf16);
|
||||
|
||||
if (resolved_target_chroot)
|
||||
free(resolved_target_chroot);
|
||||
|
||||
if (resolved_target_utf16)
|
||||
free(resolved_target_utf16);
|
||||
|
||||
if (linkpath_resolved)
|
||||
free(linkpath_resolved);
|
||||
|
||||
if (target_resolved)
|
||||
free(target_resolved);
|
||||
|
||||
|
@ -808,7 +808,7 @@ done:
|
||||
OM_uint32
|
||||
gss_accept_sec_context(_Out_ OM_uint32 * minor_status, _Inout_opt_ gss_ctx_id_t * context_handle,
|
||||
_In_opt_ gss_cred_id_t acceptor_cred_handle, _In_ gss_buffer_t input_token_buffer, _In_opt_ gss_channel_bindings_t input_chan_bindings,
|
||||
_Out_opt_ gss_name_t * src_name, _Out_opt_ gss_OID * mech_type, _Outptr_ gss_buffer_t output_token,
|
||||
_Out_opt_ gss_name_t * src_name, _Out_opt_ gss_OID * mech_type, _Out_ gss_buffer_t output_token,
|
||||
_Out_ OM_uint32 * ret_flags, _Out_opt_ OM_uint32 * time_rec, _Outptr_opt_ gss_cred_id_t * delegated_cred_handle)
|
||||
{
|
||||
OM_uint32 ret = GSS_S_FAILURE;
|
||||
|
@ -177,7 +177,7 @@ OM_uint32
|
||||
gss_accept_sec_context(_Out_ OM_uint32 * minor_status, _Inout_opt_ gss_ctx_id_t * context_handle,
|
||||
_In_opt_ gss_cred_id_t acceptor_cred_handle, _In_ gss_buffer_t input_token_buffer,
|
||||
_In_opt_ gss_channel_bindings_t input_chan_bindings, _Out_opt_ gss_name_t * src_name,
|
||||
_Out_opt_ gss_OID * mech_type, _Outptr_ gss_buffer_t output_token, _Out_ OM_uint32 * ret_flags,
|
||||
_Out_opt_ gss_OID * mech_type, _Out_ gss_buffer_t output_token, _Out_ OM_uint32 * ret_flags,
|
||||
_Out_opt_ OM_uint32 * time_rec, _Outptr_opt_ gss_cred_id_t * delegated_cred_handle);
|
||||
|
||||
OM_uint32
|
||||
|
@ -27,7 +27,7 @@ typedef struct w32_fd_set_ {
|
||||
#define FD_SETSIZE MAX_FDS
|
||||
|
||||
int w32_select(int fds, w32_fd_set * , w32_fd_set * , w32_fd_set * ,
|
||||
const struct timeval *);
|
||||
const struct w32_timeval *);
|
||||
#define select(a,b,c,d,e) w32_select((a), (b), (c), (d), (e))
|
||||
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
#pragma once
|
||||
#include <sys\utime.h>
|
||||
|
||||
#define utimbuf _utimbuf
|
||||
#define utimes w32_utimes
|
||||
|
||||
#define timeval w32_timeval
|
||||
struct timeval
|
||||
{
|
||||
long long tv_sec;
|
||||
long tv_usec;
|
||||
};
|
||||
|
||||
int usleep(unsigned int);
|
||||
int gettimeofday(struct timeval *, void *);
|
||||
int nanosleep(const struct timespec *, struct timespec *);
|
||||
|
@ -207,7 +207,7 @@ gettimeofday(struct timeval *tv, void *tz)
|
||||
us = (timehelper.ns - EPOCH_DELTA) / 10;
|
||||
|
||||
/* Stuff result into the timeval */
|
||||
tv->tv_sec = (long)(us / USEC_IN_SEC);
|
||||
tv->tv_sec = (long long)(us / USEC_IN_SEC);
|
||||
tv->tv_usec = (long)(us % USEC_IN_SEC);
|
||||
|
||||
return 0;
|
||||
@ -1419,7 +1419,7 @@ is_absolute_path(const char *path)
|
||||
|
||||
/* return -1 - in case of failure, 0 - success */
|
||||
int
|
||||
create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
|
||||
create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w, BOOL check_permissions)
|
||||
{
|
||||
if (GetFileAttributesW(path_w) == INVALID_FILE_ATTRIBUTES) {
|
||||
PSECURITY_DESCRIPTOR pSD = NULL;
|
||||
@ -1444,12 +1444,9 @@ create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (check_permissions) {
|
||||
// directory already exists; need to confirm permissions are correct
|
||||
if (check_secure_folder_permission(path_w, 1) != 0) {
|
||||
error("Directory already exists but folder permissions are invalid");
|
||||
return -1;
|
||||
}
|
||||
check_secure_folder_permission(path_w, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -67,9 +67,10 @@ void to_lower_case(char *s);
|
||||
void to_wlower_case(wchar_t *s);
|
||||
HANDLE get_user_token(const char* user, int impersonation);
|
||||
int load_user_profile(HANDLE user_token, char* user);
|
||||
int create_directory_withsddl(wchar_t *path, wchar_t *sddl);
|
||||
int create_directory_withsddl(wchar_t *path, wchar_t *sddl, BOOL check_permissions);
|
||||
int is_absolute_path(const char *);
|
||||
int file_in_chroot_jail(HANDLE);
|
||||
int file_in_chroot_jail_helper(wchar_t*);
|
||||
PSID lookup_sid(const wchar_t* name_utf16, PSID psid, DWORD * psid_len);
|
||||
PSID get_sid(const char*);
|
||||
int am_system();
|
||||
|
@ -341,8 +341,12 @@ sigaction(int signum, const struct sigaction * act, struct sigaction * oldact)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (act)
|
||||
oldact->sa_handler = w32_signal(signum, act->sa_handler);
|
||||
if (act) {
|
||||
sighandler_t old_handler = w32_signal(signum, act->sa_handler);
|
||||
if (oldact) {
|
||||
oldact->sa_handler = old_handler;
|
||||
}
|
||||
}
|
||||
else if (oldact)
|
||||
oldact->sa_handler = sig_handlers[signum];
|
||||
|
||||
|
@ -121,6 +121,7 @@ agent_listen_loop()
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
|
||||
PIPE_TYPE_BYTE | // message type pipe
|
||||
PIPE_READMODE_BYTE | // message-read mode
|
||||
PIPE_REJECT_REMOTE_CLIENTS | // no remote client connections allowed
|
||||
PIPE_WAIT, // blocking mode
|
||||
PIPE_UNLIMITED_INSTANCES, // max. instances
|
||||
BUFSIZE, // output buffer size
|
||||
@ -414,26 +415,41 @@ void
|
||||
agent_initialize_allow_list() {
|
||||
/*
|
||||
* allowed paths for PKCS11 libraries,
|
||||
* initialize to ProgramFiles and ProgramFiles(x86) by default
|
||||
* attempt to initialize to ProgramFiles and ProgramFiles(x86) by default
|
||||
* upstream uses /usr/lib/* and /usr/local/lib/*
|
||||
*/
|
||||
size_t prog_files_len = 0, prog_files_x86_len = 0;
|
||||
char* prog_files = NULL, * prog_files_x86 = NULL;
|
||||
size_t allowed_len = 0, prog_files_len = 0, prog_files_x86_len = 0;
|
||||
char* allowed_path = NULL, *prog_files = NULL, *prog_files_x86 = NULL;
|
||||
|
||||
_dupenv_s(&prog_files, &prog_files_len, "ProgramFiles");
|
||||
if (!prog_files)
|
||||
fatal("couldn't find ProgramFiles environment variable");
|
||||
convertToForwardslash(prog_files);
|
||||
|
||||
_dupenv_s(&prog_files_x86, &prog_files_x86_len, "ProgramFiles(x86)");
|
||||
if (!prog_files_x86)
|
||||
fatal("couldn't find ProgramFiles environment variable");
|
||||
convertToForwardslash(prog_files_x86);
|
||||
|
||||
size_t allowed_providers_len = 1 + prog_files_len + 4 + prog_files_x86_len + 3;
|
||||
allowed_providers = xmalloc(allowed_providers_len);
|
||||
sprintf_s(allowed_providers, allowed_providers_len, "/%s/*,/%s/*", prog_files, prog_files_x86);
|
||||
if (!prog_files && !prog_files_x86) {
|
||||
allowed_providers = xstrdup("");
|
||||
return;
|
||||
}
|
||||
|
||||
free(prog_files);
|
||||
free(prog_files_x86);
|
||||
if (prog_files && prog_files_x86) {
|
||||
allowed_len = prog_files_len + 3 + prog_files_x86_len + 1;
|
||||
allowed_path = xmalloc(allowed_len);
|
||||
sprintf_s(allowed_path, allowed_len, "%s\\*,%s", prog_files, prog_files_x86);
|
||||
free(prog_files);
|
||||
free(prog_files_x86);
|
||||
}
|
||||
else if (prog_files) {
|
||||
allowed_len = prog_files_len;
|
||||
allowed_path = prog_files;
|
||||
}
|
||||
else if (prog_files_x86) {
|
||||
allowed_len = prog_files_x86_len;
|
||||
allowed_path = prog_files_x86;
|
||||
}
|
||||
|
||||
allowed_len += 3; /* for additional characters below */
|
||||
allowed_providers = xmalloc(allowed_len);
|
||||
sprintf_s(allowed_providers, allowed_len, "%s\\*", allowed_path);
|
||||
|
||||
if (allowed_path) {
|
||||
free(allowed_path);
|
||||
}
|
||||
}
|
||||
|
@ -677,9 +677,12 @@ int process_add_smartcard_key(struct sshbuf* request, struct sshbuf* response, s
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
|
||||
to_lower_case(provider);
|
||||
verbose("provider realpath: \"%.100s\"", provider);
|
||||
verbose("allowed provider paths: \"%.100s\"", allowed_providers);
|
||||
if (match_pattern_list(provider, allowed_providers, 1) != 1) {
|
||||
verbose("refusing PKCS#11 add of \"%.100s\": "
|
||||
"provider not allowed", canonical_provider);
|
||||
"provider not allowed", provider);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -992,6 +995,7 @@ process_ext_session_bind(struct sshbuf* request, struct agent_connection* con)
|
||||
/* record new key/sid */
|
||||
if (con->nsession_ids >= AGENT_MAX_SESSION_IDS) {
|
||||
error_f("too many session IDs recorded");
|
||||
r = -1;
|
||||
goto out;
|
||||
}
|
||||
con->session_ids = xrecallocarray(con->session_ids, con->nsession_ids,
|
||||
|
@ -48,8 +48,6 @@
|
||||
#include "tnnet.h"
|
||||
#include "misc_internal.h"
|
||||
|
||||
#define TERM_IO_BUF_SIZE 2048
|
||||
|
||||
extern int in_raw_mode;
|
||||
BOOL isFirstTime = TRUE;
|
||||
|
||||
@ -293,7 +291,10 @@ syncio_close(struct w32_io* pio)
|
||||
CancelSynchronousIo(pio->read_overlapped.hEvent);
|
||||
}
|
||||
|
||||
WaitForSingleObject(pio->read_overlapped.hEvent, INFINITE);
|
||||
// give the read thread some time to wind down, but don't block syncio_close
|
||||
if (WAIT_TIMEOUT == WaitForSingleObject(pio->read_overlapped.hEvent, 1000)) {
|
||||
debug4("read_overlapped thread timed out");
|
||||
}
|
||||
}
|
||||
|
||||
/* drain queued APCs */
|
||||
|
@ -124,11 +124,84 @@ GetModifierKey(DWORD dwControlKeyState)
|
||||
return modKey;
|
||||
}
|
||||
|
||||
// ReadConsoleForTermEmul() but for ENABLE_VIRTUAL_TERMINAL_INPUT.
|
||||
static int
|
||||
ReadConsoleForTermEmulModern(HANDLE hInput, char *destin, int destinlen)
|
||||
{
|
||||
// If the previous input ended on a lead (high) surrogate,
|
||||
// we stash it here to combine it with the next input.
|
||||
static wchar_t s_previous_lead;
|
||||
|
||||
INPUT_RECORD records[TERM_IO_BUF_SIZE_UTF16];
|
||||
DWORD records_cap = ARRAYSIZE(records);
|
||||
DWORD records_len = 0;
|
||||
wchar_t text[TERM_IO_BUF_SIZE_UTF16];
|
||||
int text_len = 0;
|
||||
|
||||
// If we'll restore the previous lead surrogate, we can only read
|
||||
// ARRAYSIZE(records)-1 records before the storage overflows.
|
||||
if (s_previous_lead) {
|
||||
records_cap--;
|
||||
}
|
||||
|
||||
// As this application heavily relies on APCs, it's important that we call
|
||||
// DataAvailable(), because it calls WaitForSingleObjectEx with bAlertable=TRUE.
|
||||
if (!DataAvailable(hInput) ||
|
||||
!ReadConsoleInputW(hInput, records, records_cap, &records_len) ||
|
||||
records_len == 0)
|
||||
return 0;
|
||||
|
||||
// Restore the previous lead surrogate if we have one.
|
||||
if (s_previous_lead) {
|
||||
text[text_len++] = s_previous_lead;
|
||||
s_previous_lead = 0;
|
||||
}
|
||||
|
||||
// Accumulate the UTF-16 text.
|
||||
for (DWORD i = 0; i < records_len; i++) {
|
||||
switch (records[i].EventType) {
|
||||
case WINDOW_BUFFER_SIZE_EVENT:
|
||||
queue_terminal_window_change_event();
|
||||
break;
|
||||
case KEY_EVENT: {
|
||||
const KEY_EVENT_RECORD* k = &records[i].Event.KeyEvent;
|
||||
if (
|
||||
// The old Windows console added support for Unicode by encoding the characters in the
|
||||
// current code page as usual, while stuffing a UCS2 value into a trailing VK_MENU event.
|
||||
// Modern terminals on Windows stopped doing this and the Windows console may as well at some point.
|
||||
(k->bKeyDown || k->wVirtualKeyCode == VK_MENU) &&
|
||||
// Current versions of ConPTY suffer from a bug where pressing modifier keys enqueues
|
||||
// a KEY_EVENT with UnicodeChar=0 despite ENABLE_VIRTUAL_TERMINAL_INPUT being enabled.
|
||||
// They can be identified by the fact that their UnicodeChar value is zero,
|
||||
// but they still have a non-zero wVirtualScanCode.
|
||||
(k->uChar.UnicodeChar != L'\0' || k->wVirtualScanCode == 0))
|
||||
text[text_len++] = k->uChar.UnicodeChar;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pop any lone lead surrogate from the input for later.
|
||||
const wchar_t last_char = text[text_len - 1];
|
||||
if (IS_HIGH_SURROGATE(last_char)) {
|
||||
s_previous_lead = last_char;
|
||||
text_len--;
|
||||
}
|
||||
|
||||
// ...and finally convert everything to UTF-8.
|
||||
// It'll always fit, because we sized TERM_IO_BUF_SIZE to be large enough.
|
||||
return WideCharToMultiByte(CP_UTF8, 0, text, text_len, destin, destinlen, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen)
|
||||
{
|
||||
HANDLE hHandle[] = { hInput, NULL };
|
||||
DWORD nHandle = 1;
|
||||
if (isConsoleVTSeqAvailable) {
|
||||
return ReadConsoleForTermEmulModern(hInput, destin, destinlen);
|
||||
}
|
||||
|
||||
DWORD dwInput = 0;
|
||||
DWORD rc = 0;
|
||||
unsigned char octets[20];
|
||||
@ -187,23 +260,7 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isConsoleVTSeqAvailable) {
|
||||
if (inputRecord.Event.KeyEvent.uChar.UnicodeChar != L'\0' || inputRecord.Event.KeyEvent.wVirtualScanCode == 0) {
|
||||
n = WideCharToMultiByte(
|
||||
CP_UTF8,
|
||||
0,
|
||||
&(inputRecord.Event.KeyEvent.uChar.UnicodeChar),
|
||||
1,
|
||||
(LPSTR)octets,
|
||||
20,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
WriteToBuffer((char *)octets, n);
|
||||
}
|
||||
} else {
|
||||
GetVTSeqFromKeyStroke(inputRecord);
|
||||
}
|
||||
GetVTSeqFromKeyStroke(inputRecord);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -37,6 +37,9 @@
|
||||
|
||||
#include "console.h"
|
||||
|
||||
#define TERM_IO_BUF_SIZE_UTF16 1024
|
||||
#define TERM_IO_BUF_SIZE (3 * TERM_IO_BUF_SIZE_UTF16)
|
||||
|
||||
#define UP_ARROW "\x1b[A"
|
||||
#define DOWN_ARROW "\x1b[B"
|
||||
#define RIGHT_ARROW "\x1b[C"
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <Aclapi.h>
|
||||
#include <lm.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "inc\pwd.h"
|
||||
#include "sshfileperm.h"
|
||||
@ -40,6 +42,12 @@
|
||||
#include "misc_internal.h"
|
||||
#include "config.h"
|
||||
|
||||
#define NULL_TERMINATOR_LEN 1
|
||||
#define COMMA_SPACE_LEN 2
|
||||
#define BACKSLASH_LEN 1
|
||||
|
||||
extern int log_on_stderr;
|
||||
|
||||
/*
|
||||
* The function is to check if current user is secure to access to the file.
|
||||
* Check the owner of the file is one of these types: Local Administrators groups, system account, current user account
|
||||
@ -178,18 +186,22 @@ cleanup:
|
||||
* Check the owner of the file is one of these types: Local Administrators groups or system account
|
||||
* Check the users have access permission to the file don't violate the following rules:
|
||||
1. no user other than local administrators group and system account have write permission on the folder
|
||||
* Returns 0 on success and -1 on failure
|
||||
* Logs a message if the rules are violated, but does not prevent further execution
|
||||
*/
|
||||
int
|
||||
void
|
||||
check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
|
||||
{
|
||||
PSECURITY_DESCRIPTOR pSD = NULL;
|
||||
PSID owner_sid = NULL, ti_sid = NULL;
|
||||
PACL dacl = NULL;
|
||||
DWORD error_code = ERROR_SUCCESS;
|
||||
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE;
|
||||
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE, need_log_msg = FALSE, is_first = TRUE;
|
||||
wchar_t* bad_user = NULL;
|
||||
int ret = 0;
|
||||
size_t log_msg_len = (DNLEN + BACKSLASH_LEN + UNLEN) * 2 + COMMA_SPACE_LEN + NULL_TERMINATOR_LEN;
|
||||
wchar_t* log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
|
||||
if (log_msg != NULL) {
|
||||
log_msg[0] = '\0';
|
||||
}
|
||||
|
||||
/*Get the owner sid of the file.*/
|
||||
if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT,
|
||||
@ -197,18 +209,15 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
|
||||
&owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) {
|
||||
printf("failed to retrieve the owner sid and dacl of file %S with error code: %d", path_utf16, error_code);
|
||||
errno = EOTHER;
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
if (((is_valid_sid = IsValidSid(owner_sid)) == FALSE) || ((is_valid_acl = IsValidAcl(dacl)) == FALSE)) {
|
||||
printf("IsValidSid: %d; is_valid_acl: %d", is_valid_sid, is_valid_acl);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) &&
|
||||
!IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
|
||||
printf("Bad owner on %S", path_utf16);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
/*
|
||||
@ -224,7 +233,6 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
|
||||
if (!GetAce(dacl, i, ¤t_ace)) {
|
||||
printf("GetAce() failed");
|
||||
errno = EOTHER;
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -247,15 +255,112 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
ret = -1;
|
||||
/* collect all SIDs with write permissions */
|
||||
wchar_t resolved_trustee[UNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
|
||||
wchar_t resolved_trustee_domain[DNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
|
||||
DWORD resolved_trustee_len = _countof(resolved_trustee), resolved_trustee_domain_len = _countof(resolved_trustee_domain);
|
||||
SID_NAME_USE resolved_trustee_type;
|
||||
|
||||
need_log_msg = TRUE;
|
||||
|
||||
if (log_msg != NULL &&
|
||||
LookupAccountSidW(NULL, current_trustee_sid, resolved_trustee, &resolved_trustee_len,
|
||||
resolved_trustee_domain, &resolved_trustee_domain_len, &resolved_trustee_type) != 0) {
|
||||
if (is_first) {
|
||||
_snwprintf_s(log_msg, log_msg_len, _TRUNCATE, L"%ls\\%ls", resolved_trustee_domain, resolved_trustee);
|
||||
is_first = FALSE;
|
||||
}
|
||||
else {
|
||||
size_t currentLength = wcslen(log_msg);
|
||||
size_t userLength = resolved_trustee_domain_len + BACKSLASH_LEN + resolved_trustee_len + COMMA_SPACE_LEN;
|
||||
if (wcslen(log_msg) + userLength + NULL_TERMINATOR_LEN > log_msg_len) {
|
||||
log_msg_len *= 2;
|
||||
wchar_t* temp_log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
|
||||
if (temp_log_msg == NULL) {
|
||||
break;
|
||||
}
|
||||
wcscpy_s(temp_log_msg, log_msg_len, log_msg);
|
||||
if (log_msg)
|
||||
free(log_msg);
|
||||
log_msg = temp_log_msg;
|
||||
}
|
||||
_snwprintf_s(log_msg + currentLength, log_msg_len - currentLength, _TRUNCATE,
|
||||
L", %ls\\%ls", resolved_trustee_domain, resolved_trustee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_log_msg) {
|
||||
log_folder_perms_msg_etw(path_utf16, log_msg);
|
||||
}
|
||||
cleanup:
|
||||
if (bad_user)
|
||||
if (bad_user) {
|
||||
LocalFree(bad_user);
|
||||
if (pSD)
|
||||
}
|
||||
if (log_msg) {
|
||||
free(log_msg);
|
||||
}
|
||||
if (pSD) {
|
||||
LocalFree(pSD);
|
||||
if (ti_sid)
|
||||
}
|
||||
if (ti_sid) {
|
||||
free(ti_sid);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function takes in the full path to the ProgramData\ssh folder
|
||||
* and a string of comma-separated domain\usernames. The function converts
|
||||
* the well-known built-in Administrators group sid and the Local System
|
||||
* sid to their corresponding names. With these names, and the input string,
|
||||
* it logs a message to the Event Viewer. If logging the detailed message fails,
|
||||
* a generic log message is written to the Event Viewer instead.
|
||||
*/
|
||||
void log_folder_perms_msg_etw(const wchar_t* path_utf16, wchar_t* log_msg) {
|
||||
PSID adminSid = NULL;
|
||||
WCHAR adminName[UNLEN + NULL_TERMINATOR_LEN];
|
||||
WCHAR adminDomain[DNLEN + NULL_TERMINATOR_LEN];
|
||||
DWORD adminNameSize = UNLEN + NULL_TERMINATOR_LEN;
|
||||
DWORD adminDomainSize = DNLEN + NULL_TERMINATOR_LEN;
|
||||
DWORD adminSidSize = SECURITY_MAX_SID_SIZE;
|
||||
PSID systemSid = NULL;
|
||||
WCHAR systemName[UNLEN + NULL_TERMINATOR_LEN];
|
||||
WCHAR systemDomain[DNLEN + NULL_TERMINATOR_LEN];
|
||||
DWORD systemNameSize = UNLEN + NULL_TERMINATOR_LEN;
|
||||
DWORD systemDomainSize = DNLEN + NULL_TERMINATOR_LEN;
|
||||
DWORD systemSidSize = SECURITY_MAX_SID_SIZE;
|
||||
SID_NAME_USE sidType;
|
||||
BOOL needLog = TRUE;
|
||||
int temp_log_on_stderr = log_on_stderr;
|
||||
log_on_stderr = 0;
|
||||
|
||||
adminSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
|
||||
if (log_msg != NULL && adminSid != NULL &&
|
||||
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, adminSid, &adminSidSize) != 0 &&
|
||||
LookupAccountSidW(NULL, adminSid, adminName, &adminNameSize, adminDomain, &adminDomainSize, &sidType) != 0) {
|
||||
systemSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
|
||||
if (systemSid != NULL &&
|
||||
CreateWellKnownSid(WinLocalSystemSid, NULL, systemSid, &systemSidSize) != 0 &&
|
||||
LookupAccountSidW(NULL, systemSid, systemName, &systemNameSize, systemDomain, &systemDomainSize, &sidType) != 0) {
|
||||
logit("For '%S' folder, write access is granted to the following users: %S. "
|
||||
"Consider reviewing users to ensure that only %S\\%S, and the %S\\%S group, and its members, have write access.",
|
||||
path_utf16, log_msg, systemDomain, systemName, adminDomain, adminName);
|
||||
needLog = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (needLog) {
|
||||
/* log generic warning message in unlikely case that lookup for either well-known SID fails or user list is empty */
|
||||
logit("for '%S' folder, consider downgrading permissions for any users with unnecessary write access.", path_utf16);
|
||||
}
|
||||
|
||||
log_on_stderr = temp_log_on_stderr;
|
||||
|
||||
if (adminSid) {
|
||||
free(adminSid);
|
||||
}
|
||||
if (systemSid) {
|
||||
free(systemSid);
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "inc\sys\socket.h"
|
||||
#include "inc\sys\select.h"
|
||||
#include "inc\sys\uio.h"
|
||||
#include "inc\sys\time.h"
|
||||
#include "inc\sys\types.h"
|
||||
#include "inc\sys\stat.h"
|
||||
#include "inc\unistd.h"
|
||||
@ -88,6 +89,19 @@ fd_table_initialize()
|
||||
{
|
||||
struct w32_io *pio;
|
||||
HANDLE wh;
|
||||
char *stdio_mode_env;
|
||||
int stdio_mode = NONSOCK_SYNC_FD;
|
||||
|
||||
stdio_mode_env = getenv("OPENSSH_STDIO_MODE");
|
||||
if (stdio_mode_env != NULL) {
|
||||
if (strcmp(stdio_mode_env, "sock") == 0)
|
||||
stdio_mode = SOCK_FD;
|
||||
else if (strcmp(stdio_mode_env, "nonsock") == 0)
|
||||
stdio_mode = NONSOCK_FD;
|
||||
else if (strcmp(stdio_mode_env, "nonsock_sync") == 0)
|
||||
stdio_mode = NONSOCK_SYNC_FD;
|
||||
}
|
||||
|
||||
/* table entries representing std in, out and error*/
|
||||
DWORD wh_index[] = { STD_INPUT_HANDLE , STD_OUTPUT_HANDLE , STD_ERROR_HANDLE };
|
||||
int fd_num = 0;
|
||||
@ -104,7 +118,7 @@ fd_table_initialize()
|
||||
return -1;
|
||||
}
|
||||
memset(pio, 0, sizeof(struct w32_io));
|
||||
pio->type = NONSOCK_SYNC_FD;
|
||||
pio->type = stdio_mode;
|
||||
pio->handle = wh;
|
||||
fd_table_set(pio, fd_num);
|
||||
}
|
||||
@ -715,12 +729,11 @@ w32_fcntl(int fd, int cmd, ... /* arg */)
|
||||
int
|
||||
w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* exceptfds, const struct timeval *timeout)
|
||||
{
|
||||
ULONGLONG ticks_start = GetTickCount64(), ticks_spent;
|
||||
ULONGLONG ticks_start = GetTickCount64(), ticks_spent, timeout_ms = 0, time_rem = 0;
|
||||
w32_fd_set read_ready_fds, write_ready_fds;
|
||||
HANDLE events[SELECT_EVENT_LIMIT];
|
||||
int num_events = 0;
|
||||
int in_set_fds = 0, out_ready_fds = 0, i;
|
||||
unsigned int timeout_ms = 0, time_rem = 0;
|
||||
|
||||
errno = 0;
|
||||
/* TODO - the size of these can be reduced based on fds */
|
||||
@ -843,7 +856,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep
|
||||
else
|
||||
time_rem = INFINITE;
|
||||
|
||||
if (0 != wait_for_any_event(events, num_events, time_rem))
|
||||
if (0 != wait_for_any_event(events, num_events, (DWORD)time_rem))
|
||||
return -1;
|
||||
|
||||
/* check on fd status */
|
||||
@ -1071,7 +1084,7 @@ spawn_child_internal(const char* cmd, char *const argv[], HANDLE in, HANDLE out,
|
||||
si.hStdError = err;
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
if (strstr(cmd, "sshd.exe")) {
|
||||
if (strstr(cmd, "sshd-session.exe")) {
|
||||
flags |= DETACHED_PROCESS;
|
||||
}
|
||||
|
||||
|
@ -43,13 +43,12 @@ wmain(int argc, wchar_t **wargv) {
|
||||
char** argv = NULL;
|
||||
int i, r;
|
||||
_set_invalid_parameter_handler(invalid_parameter_handler);
|
||||
if (argc) {
|
||||
if ((argv = malloc(argc * sizeof(char*))) == NULL)
|
||||
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
|
||||
fatal("out of memory");
|
||||
for (i = 0; i < argc; i++)
|
||||
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
|
||||
fatal("out of memory");
|
||||
for (i = 0; i < argc; i++)
|
||||
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
|
||||
fatal("out of memory");
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
if (getenv("SSH_AUTH_SOCK") == NULL)
|
||||
_putenv("SSH_AUTH_SOCK=\\\\.\\pipe\\openssh-ssh-agent");
|
||||
|
@ -50,15 +50,13 @@ int sshd_session_main(int argc, wchar_t **wargv) {
|
||||
int i, r;
|
||||
_set_invalid_parameter_handler(invalid_parameter_handler);
|
||||
|
||||
if (argc) {
|
||||
if ((argv = malloc(argc * sizeof(char*))) == NULL) {
|
||||
printf("out of memory");
|
||||
exit(255);
|
||||
}
|
||||
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
|
||||
fatal("out of memory");
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
argv[i] = utf16_to_utf8(wargv[i]);
|
||||
}
|
||||
for (i = 0; i < argc; i++)
|
||||
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
|
||||
fatal("out of memory");
|
||||
argv[argc] = NULL;
|
||||
|
||||
w32posix_initialize();
|
||||
|
||||
|
@ -135,7 +135,7 @@ create_prgdata_ssh_folder()
|
||||
wchar_t ssh_cfg_dir[PATH_MAX] = { 0, };
|
||||
wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata);
|
||||
wcscat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), L"\\ssh");
|
||||
if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") < 0) {
|
||||
if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)", TRUE) < 0) {
|
||||
printf("failed to create %S", ssh_cfg_dir);
|
||||
exit(255);
|
||||
}
|
||||
@ -144,7 +144,7 @@ create_prgdata_ssh_folder()
|
||||
wchar_t logs_dir[PATH_MAX] = { 0, };
|
||||
wcscat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir);
|
||||
wcscat_s(logs_dir, _countof(logs_dir), L"\\logs");
|
||||
if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)") < 0) {
|
||||
if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)", FALSE) < 0) {
|
||||
printf("failed to create %S", logs_dir);
|
||||
exit(255);
|
||||
}
|
||||
@ -202,15 +202,14 @@ int sshd_main(int argc, wchar_t **wargv) {
|
||||
int i, r;
|
||||
_set_invalid_parameter_handler(invalid_parameter_handler);
|
||||
|
||||
if (argc) {
|
||||
if ((argv = malloc(argc * sizeof(char*))) == NULL) {
|
||||
printf("out of memory");
|
||||
exit(255);
|
||||
}
|
||||
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
|
||||
fatal("out of memory");
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
argv[i] = utf16_to_utf8(wargv[i]);
|
||||
}
|
||||
for (i = 0; i < argc; i++)
|
||||
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
|
||||
fatal("out of memory");
|
||||
|
||||
argv[argc] = NULL;
|
||||
|
||||
w32posix_initialize();
|
||||
|
||||
|
4
krl.c
4
krl.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: krl.c,v 1.59 2023/07/17 05:22:30 djm Exp $ */
|
||||
/* $OpenBSD: krl.c,v 1.60 2025/02/18 08:02:48 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2012 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
@ -674,6 +674,7 @@ revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
|
||||
break;
|
||||
case KRL_SECTION_CERT_SERIAL_BITMAP:
|
||||
if (rs->lo - bitmap_start > INT_MAX) {
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
error_f("insane bitmap gap");
|
||||
goto out;
|
||||
}
|
||||
@ -1059,6 +1060,7 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp)
|
||||
}
|
||||
|
||||
if ((krl = ssh_krl_init()) == NULL) {
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
error_f("alloc failed");
|
||||
goto out;
|
||||
}
|
||||
|
4
log.c
4
log.c
@ -54,7 +54,11 @@
|
||||
#include "match.h"
|
||||
|
||||
static LogLevel log_level = SYSLOG_LEVEL_INFO;
|
||||
#ifdef WINDOWS
|
||||
int log_on_stderr = 1;
|
||||
#else
|
||||
static int log_on_stderr = 1;
|
||||
#endif /* WINDOWS */
|
||||
static int log_stderr_fd = STDERR_FILENO;
|
||||
static int log_facility = LOG_AUTH;
|
||||
static const char *argv0;
|
||||
|
9
misc.c
9
misc.c
@ -1261,6 +1261,15 @@ tilde_expand(const char *filename, uid_t uid, char **retp)
|
||||
path = NULL; /* ~/ */
|
||||
else
|
||||
path = copy; /* ~/path */
|
||||
#ifdef WINDOWS
|
||||
// also need to account for backward slashes on Windows
|
||||
} else if (*copy == '\\') {
|
||||
copy += strspn(copy, "\\");
|
||||
if (*copy == '\0')
|
||||
path = NULL; /* ~\ */
|
||||
else
|
||||
path = copy; /* ~\path */
|
||||
#endif /* WINDOWS */
|
||||
} else {
|
||||
user = copy;
|
||||
if ((path = strchr(copy, '/')) != NULL) {
|
||||
|
@ -483,14 +483,14 @@ monitor_read_log(struct monitor *pmonitor)
|
||||
|
||||
/*log it*/
|
||||
if (authctxt->authenticated == 0)
|
||||
sshlogdirect(level, forced, "%s [preauth]", msg);
|
||||
sshlogdirect(level, forced, "user: %s: %s [preauth]", authctxt->user, msg);
|
||||
else {
|
||||
if (strcmp(pname, "sftp-server") == 0) {
|
||||
log_init(pname, sftp_log_level, sftp_log_facility, sftp_log_stderr);
|
||||
sshlogdirect(level, forced, "%s", msg);
|
||||
sshlogdirect(level, forced, "user: %s: %s", authctxt->user, msg);
|
||||
log_init("sshd", options.log_level, options.log_facility, log_stderr);
|
||||
} else
|
||||
sshlogdirect(level, forced, "%s", msg);
|
||||
sshlogdirect(level, forced, "user: %s: %s", authctxt->user, msg);
|
||||
}
|
||||
#else
|
||||
/*log it*/
|
||||
|
14
myproposal.h
14
myproposal.h
@ -24,6 +24,19 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef WINDOWS
|
||||
// these should be in the same order as upstream, without the ones we don't support
|
||||
#define KEX_SERVER_KEX \
|
||||
"curve25519-sha256," \
|
||||
"curve25519-sha256@libssh.org," \
|
||||
"ecdh-sha2-nistp256," \
|
||||
"ecdh-sha2-nistp384," \
|
||||
"ecdh-sha2-nistp521," \
|
||||
"diffie-hellman-group-exchange-sha256," \
|
||||
"diffie-hellman-group16-sha512," \
|
||||
"diffie-hellman-group18-sha512," \
|
||||
"diffie-hellman-group14-sha256"
|
||||
#else
|
||||
#define KEX_SERVER_KEX \
|
||||
"sntrup761x25519-sha512@openssh.com," \
|
||||
"curve25519-sha256," \
|
||||
@ -35,6 +48,7 @@
|
||||
"diffie-hellman-group16-sha512," \
|
||||
"diffie-hellman-group18-sha512," \
|
||||
"diffie-hellman-group14-sha256"
|
||||
#endif
|
||||
|
||||
#define KEX_CLIENT_KEX KEX_SERVER_KEX
|
||||
|
||||
|
10
packet.c
10
packet.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: packet.c,v 1.315 2024/05/31 08:49:35 djm Exp $ */
|
||||
/* $OpenBSD: packet.c,v 1.318 2025/02/18 08:02:12 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -1867,6 +1867,14 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
|
||||
if ((r = sshpkt_get_string_direct(ssh, &d, &len)) != 0)
|
||||
return r;
|
||||
DBG(debug("Received SSH2_MSG_PING len %zu", len));
|
||||
if (!ssh->state->after_authentication) {
|
||||
DBG(debug("Won't reply to PING in preauth"));
|
||||
break;
|
||||
}
|
||||
if (ssh_packet_is_rekeying(ssh)) {
|
||||
DBG(debug("Won't reply to PING during KEX"));
|
||||
break;
|
||||
}
|
||||
if ((r = sshpkt_start(ssh, SSH2_MSG_PONG)) != 0 ||
|
||||
(r = sshpkt_put_string(ssh, d, len)) != 0 ||
|
||||
(r = sshpkt_send(ssh)) != 0)
|
||||
|
185
regress/pesterTests/EventLogging.Tests.ps1
Normal file
185
regress/pesterTests/EventLogging.Tests.ps1
Normal file
@ -0,0 +1,185 @@
|
||||
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
|
||||
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
|
||||
Import-Module OpenSSHUtils -Force
|
||||
$tC = 1
|
||||
$tI = 0
|
||||
$suite = "EventLogging"
|
||||
Describe "Tests for admin and non-admin event logs" -Tags "CI" {
|
||||
BeforeAll {
|
||||
if($OpenSSHTestInfo -eq $null)
|
||||
{
|
||||
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
|
||||
}
|
||||
|
||||
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite"
|
||||
if( -not (Test-path $testDir -PathType Container))
|
||||
{
|
||||
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$server = $OpenSSHTestInfo["Target"]
|
||||
$nonadminusername = $OpenSSHTestInfo['NonAdminUser']
|
||||
$adminusername = $OpenSSHTestInfo['AdminUser']
|
||||
$opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath']
|
||||
$password = $OpenSSHTestInfo['TestAccountPW']
|
||||
$port = 47003
|
||||
$sshdDelay = $OpenSSHTestInfo["DelayTime"]
|
||||
|
||||
# Register OpenSSH events in Event Viewer
|
||||
$etwman = Join-Path $opensshbinpath "openssh-events.man"
|
||||
if (-not (Test-Path $etwman -PathType Leaf)) {
|
||||
throw "openssh events manifest is not present in OpenSSH binary path"
|
||||
}
|
||||
wevtutil im "$etwman" | Out-Null
|
||||
}
|
||||
|
||||
AfterEach { $tI++ }
|
||||
|
||||
AfterAll {
|
||||
# Unregister etw provider
|
||||
wevtutil um "$etwman"
|
||||
}
|
||||
|
||||
Context "Tests Logs for SSH connections" {
|
||||
BeforeAll {
|
||||
Add-PasswordSetting -Pass $password
|
||||
$tI=1
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
# disable the OpenSSH log channels
|
||||
wevtutil sl "OpenSSH/Debug" /e:false /q:true | Out-Null
|
||||
wevtutil sl "OpenSSH/Operational" /e:false /q:true | Out-Null
|
||||
# clear any existing logs
|
||||
wevtutil cl "OpenSSH/Debug" | Out-Null
|
||||
wevtutil cl "OpenSSH/Operational" | Out-Null
|
||||
# enable the OpenSSH log channels
|
||||
wevtutil sl "OpenSSH/Debug" /e:true /q:true | Out-Null
|
||||
wevtutil sl "OpenSSH/Operational" /e:true /q:true | Out-Null
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Remove-PasswordSetting
|
||||
$tC++
|
||||
}
|
||||
|
||||
It "$tC.$tI-Nonadmin SSH Connection" {
|
||||
$o = ssh -l $nonadminusername test_target echo 1234
|
||||
$o | Should Be 1234
|
||||
Start-Sleep $sshdDelay
|
||||
# query the OpenSSH log channels to make sure events were captured
|
||||
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
|
||||
$eventLogDebug | Should Not Be $null
|
||||
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
|
||||
$eventLogOperational | Should Not Be $null
|
||||
}
|
||||
|
||||
It "$tC.$tI-Admin SSH Connection" {
|
||||
$o = ssh -l $adminusername test_target echo 1234
|
||||
$o | Should Be 1234
|
||||
Start-Sleep $sshdDelay
|
||||
# query the OpenSSH log channels to make sure events were captured
|
||||
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
|
||||
$eventLogDebug | Should Not Be $null
|
||||
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
|
||||
$eventLogOperational | Should Not Be $null
|
||||
}
|
||||
}
|
||||
|
||||
Context "Tests Logs for SFTP connections" {
|
||||
|
||||
BeforeAll {
|
||||
|
||||
function Setup-KeyBasedAuth
|
||||
{
|
||||
param([string] $Username, [string] $KeyFilePath, [string] $UserProfile)
|
||||
|
||||
$userSSHProfilePath = Join-Path $UserProfile .ssh
|
||||
|
||||
if (-not (Test-Path $userSSHProfilePath -PathType Container)) {
|
||||
New-Item $userSSHProfilePath -ItemType directory -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
|
||||
$authorizedkeyPath = Join-Path $userSSHProfilePath authorized_keys
|
||||
|
||||
if($OpenSSHTestInfo["NoLibreSSL"])
|
||||
{
|
||||
ssh-keygen.exe -t ed25519 -f $KeyFilePath -Z -P "" aes128-ctr
|
||||
}
|
||||
else
|
||||
{
|
||||
ssh-keygen.exe -t ed25519 -f $KeyFilePath -P ""
|
||||
}
|
||||
Copy-Item "$KeyFilePath.pub" $authorizedkeyPath -Force -ErrorAction SilentlyContinue
|
||||
Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false
|
||||
}
|
||||
|
||||
$AdminUserProfile = $OpenSSHTestInfo['AdminUserProfile']
|
||||
$NonAdminUserProfile = $OpenSSHTestInfo['NonAdminUserProfile']
|
||||
|
||||
$KeyFileName = $nonadminusername + "_sshtest_EventLog_ed25519"
|
||||
$NonadminKeyFilePath = Join-Path $testDir $keyFileName
|
||||
Remove-Item -path "$NonadminKeyFilePath*" -Force -ErrorAction SilentlyContinue
|
||||
Setup-KeyBasedAuth -Username $nonadminusername -KeyFilePath $NonadminKeyFilePath -UserProfile $NonAdminUserProfile
|
||||
|
||||
$KeyFileName = $adminusername + "_sshtest_EventLog_ed25519"
|
||||
$AdminKeyFilePath = Join-Path $testDir $keyFileName
|
||||
Remove-Item -path "$AdminKeyFilePath*" -Force -ErrorAction SilentlyContinue
|
||||
Setup-KeyBasedAuth -Username $adminusername -KeyFilePath $AdminKeyFilePath -UserProfile $AdminUserProfile
|
||||
|
||||
#create batch file
|
||||
$commands =
|
||||
"ls
|
||||
exit"
|
||||
$batchFilePath = Join-Path $testDir "$tC.$tI.commands.txt"
|
||||
Set-Content $batchFilePath -Encoding UTF8 -value $commands
|
||||
|
||||
$tI = 1
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
# disable the OpenSSH log channels
|
||||
wevtutil sl "OpenSSH/Debug" /e:false /q:true | Out-Null
|
||||
wevtutil sl "OpenSSH/Operational" /e:false /q:true | Out-Null
|
||||
# clear any existing logs
|
||||
wevtutil cl "OpenSSH/Debug" | Out-Null
|
||||
wevtutil cl "OpenSSH/Operational" | Out-Null
|
||||
# enable the OpenSSH log channels
|
||||
wevtutil sl "OpenSSH/Debug" /e:true /q:true | Out-Null
|
||||
wevtutil sl "OpenSSH/Operational" /e:true /q:true | Out-Null
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Remove-Item -path "$NonadminKeyFilePath*" -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -path "$AdminKeyFilePath*" -Force -ErrorAction SilentlyContinue
|
||||
|
||||
$authorized_key = Join-Path '.ssh' authorized_keys
|
||||
$AdminAuthKeysPath = Join-Path $AdminUserProfile $authorized_key
|
||||
$NonAdminAuthKeysPath = Join-Path $NonAdminUserProfile $authorized_key
|
||||
Remove-Item -path "$AdminAuthKeysPath*" -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -path "$NonAdminAuthKeysPath*" -Force -ErrorAction SilentlyContinue
|
||||
|
||||
$tC++
|
||||
}
|
||||
|
||||
It "$tC.$tI-Nonadmin SFTP Connection" {
|
||||
sftp -i $NonadminKeyFilePath -b $batchFilePath -o User=$nonadminusername test_target
|
||||
Start-Sleep $sshdDelay
|
||||
# query the OpenSSH log channels to make sure events were captured
|
||||
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
|
||||
$eventLogDebug | Should Not Be $null
|
||||
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
|
||||
$eventLogOperational | Should Not Be $null
|
||||
}
|
||||
|
||||
It "$tC.$tI-Admin SFTP Connection" {
|
||||
sftp -i $AdminKeyFilePath -b $batchFilePath -o User=$adminusername test_target
|
||||
Start-Sleep $sshdDelay
|
||||
# query the OpenSSH log channels to make sure events were captured
|
||||
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
|
||||
$eventLogDebug | Should Not Be $null
|
||||
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
|
||||
$eventLogOperational | Should Not Be $null
|
||||
}
|
||||
}
|
||||
}
|
@ -200,9 +200,9 @@ exit"
|
||||
|
||||
$sshdlog | Should Contain "Accepted publickey for $nonadminusername"
|
||||
$sshdlog | Should Contain "KEX done \[preauth\]"
|
||||
$sshdlog | Should Contain "debug2: subsystem request for sftp by user $nonadminusername"
|
||||
$sshdlog | Should Contain "debug2: user: $nonadminusername`: subsystem request for sftp by user $nonadminusername"
|
||||
$sftplog | Should Contain "session opened for local user $nonadminusername"
|
||||
$sftplog | Should Contain "debug3: request 3: opendir"
|
||||
$sftplog | Should Contain "debug3: user: $nonadminusername`: request 3: opendir"
|
||||
$sftplog | Should Contain "session closed for local user $nonadminusername"
|
||||
}
|
||||
|
||||
@ -216,9 +216,9 @@ exit"
|
||||
|
||||
$sshdlog | Should Contain "Accepted publickey for $adminusername"
|
||||
$sshdlog | Should Contain "KEX done \[preauth\]"
|
||||
$sshdlog | Should Contain "debug2: subsystem request for sftp by user $adminusername"
|
||||
$sshdlog | Should Contain "debug2: user: $adminusername`: subsystem request for sftp by user $adminusername"
|
||||
$sftplog | Should Contain "session opened for local user $adminusername"
|
||||
$sftplog | Should Contain "debug3: request 3: opendir"
|
||||
$sftplog | Should Contain "debug3: user: $adminusername`: request 3: opendir"
|
||||
$sftplog | Should Contain "session closed for local user $adminusername"
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" {
|
||||
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$pkcs11Pin = "testpin"
|
||||
$keypassphrase = "testpassword"
|
||||
$NoLibreSSL = $OpenSSHTestInfo["NoLibreSSL"]
|
||||
if($NoLibreSSL)
|
||||
@ -298,6 +299,33 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" {
|
||||
$allkeys = @(ssh-add -L)
|
||||
ValidateRegistryACL -count $allkeys.count
|
||||
}
|
||||
|
||||
It "$tC.$tI - ssh-add - pkcs11 library (if available)" {
|
||||
$pkcs11Path = "C:\\Program Files\\OpenSC Project\\OpenSC\\pkcs11\\opensc-pkcs11.dll"
|
||||
if (Test-Path $pkcs11Path) {
|
||||
#set up SSH_ASKPASS
|
||||
Add-PasswordSetting -Pass $pkcs11Pin
|
||||
|
||||
ssh-add -s "$pkcs11Path"
|
||||
$LASTEXITCODE | Should Be 0
|
||||
#remove SSH_ASKPASS
|
||||
Remove-PasswordSetting
|
||||
|
||||
#ensure added keys are listed
|
||||
$allkeys = ssh-add -L
|
||||
$allKeys -notmatch "The agent has no identities." | Should Be $True
|
||||
|
||||
#delete added keys
|
||||
iex "cmd /c `"ssh-add -D 2> nul `""
|
||||
|
||||
#check keys are deleted
|
||||
$allkeys = ssh-add -L
|
||||
$allKeys -match "The agent has no identities." | Should Be $True
|
||||
}
|
||||
else {
|
||||
Write-Host "skipping pkcs11 test because provider not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC ssh-keygen known_hosts operations" {
|
||||
|
@ -5,9 +5,9 @@ Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
|
||||
$tC = 1
|
||||
$tI = 0
|
||||
$suite = "sshclient"
|
||||
|
||||
|
||||
Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
BeforeAll {
|
||||
BeforeAll {
|
||||
if($OpenSSHTestInfo -eq $null)
|
||||
{
|
||||
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
|
||||
@ -27,12 +27,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($ssouser, $rights, "ContainerInherit,Objectinherit", "None", "Allow")
|
||||
$acl.SetAccessRule($accessRule)
|
||||
Set-Acl -Path $testDir -AclObject $acl
|
||||
#skip on ps 2 becase non-interactive cmd require a ENTER before it returns on ps2
|
||||
#skip on ps 2 because non-interactive cmd require a ENTER before it returns on ps2
|
||||
$skip = $IsWindows -and ($PSVersionTable.PSVersion.Major -le 2)
|
||||
|
||||
<#$testData = @(
|
||||
@{
|
||||
Title = 'Simple logon no option';
|
||||
Title = 'Simple logon no option';
|
||||
LogonStr = "$($server.localAdminUserName)@$($server.MachineName)"
|
||||
Options = ""
|
||||
},
|
||||
@ -42,7 +42,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
Options = "-C -l $($server.localAdminUserName)"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
$testData1 = @(
|
||||
@{
|
||||
Title = "logon using -i -q option"
|
||||
@ -84,7 +84,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
[string] $default_shell_path,
|
||||
[string] $default_shell_cmd_option_val = $null
|
||||
)
|
||||
|
||||
|
||||
if (!(Test-Path $dfltShellRegPath)) {
|
||||
New-Item -Path $dfltShellRegPath -Force | Out-Null
|
||||
}
|
||||
@ -99,12 +99,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
$stderrFile=Join-Path $testDir "$tC.$tI.stderr.txt"
|
||||
$stdoutFile=Join-Path $testDir "$tC.$tI.stdout.txt"
|
||||
$logFile = Join-Path $testDir "$tC.$tI.log.txt"
|
||||
}
|
||||
}
|
||||
|
||||
AfterEach {$tI++;}
|
||||
|
||||
Context "$tC - Basic Scenarios" {
|
||||
|
||||
|
||||
BeforeAll {$tI=1}
|
||||
AfterAll{$tC++}
|
||||
|
||||
@ -117,13 +117,13 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
iex "cmd /c `"ssh -? 2> $stderrFile`""
|
||||
$stderrFile | Should Contain "usage: ssh"
|
||||
}
|
||||
|
||||
|
||||
It "$tC.$tI - remote echo command" {
|
||||
iex "$sshDefaultCmd echo 1234" | Should Be "1234"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Context "$tC - exit code (exit-status.sh)" {
|
||||
BeforeAll {$tI=1}
|
||||
AfterAll{$tC++}
|
||||
@ -132,12 +132,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
foreach ($i in (0,1,4,5,44)) {
|
||||
ssh -p $port $ssouser@$server exit $i
|
||||
$LASTEXITCODE | Should Be $i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - Redirection Scenarios" {
|
||||
|
||||
|
||||
BeforeAll {$tI=1}
|
||||
AfterAll{$tC++}
|
||||
|
||||
@ -192,7 +192,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Context "$tC - configure powershell default shell Scenarios" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
@ -205,13 +205,13 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
$tC++
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "$tC.$tI - basic powershell" -skip:$skip {
|
||||
$o = ssh test_target Write-Output 1234
|
||||
$o | Should Be "1234"
|
||||
}
|
||||
|
||||
|
||||
It "$tC.$tI - basic in powershell cmdlet" -skip:$skip {
|
||||
$o = ssh test_target "cd `$env:ProgramFiles;pwd"
|
||||
$LASTEXITCODE | Should Be 0
|
||||
@ -240,26 +240,59 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
|
||||
# actual command line ssh target echo '$env:computername'
|
||||
$o = ssh test_target "echo '`$env:computername'"
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$o | Should Be `$env:computername
|
||||
}
|
||||
It "$tC.$tI - exiting ssh session exits sshd session child processes" -skip:$skip {
|
||||
$sshdPidCountBefore = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
|
||||
ssh test_target "echo '`$env:computername'"
|
||||
Start-Sleep -Seconds 2
|
||||
$sshdPidCountAfter = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
|
||||
$sshdPidCountAfter | Should Be $sshdPidCountBefore
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - configure powershell as default shell with admin user" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
$shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path
|
||||
if ($shell_path -ne $null) {
|
||||
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "-c"
|
||||
}
|
||||
$password = $OpenSSHTestInfo['TestAccountPW']
|
||||
Add-PasswordSetting -Pass $password
|
||||
}
|
||||
AfterAll {
|
||||
$tC++
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
||||
Remove-PasswordSetting
|
||||
}
|
||||
|
||||
It "$tC.$tI - admin session can write to console" -skip:$skip {
|
||||
$adminusername = $OpenSSHTestInfo['AdminUser']
|
||||
$o = ssh $adminusername@test_target "Get-ComputerInfo"
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$o | Select-String -Pattern "WindowsVersion" | Should Match "WindowsVersion"
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - configure cmd as default shell" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
$shell_path = (Get-Command cmd.exe -ErrorAction SilentlyContinue).path
|
||||
if($shell_path -ne $null) {
|
||||
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
|
||||
}
|
||||
}
|
||||
}
|
||||
AfterAll{
|
||||
AfterAll {
|
||||
$tC++
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
||||
}
|
||||
It "$tC.$tI - default shell as cmd" -skip:$skip {
|
||||
It "$tC.$tI - default shell as cmd" -skip:$skip {
|
||||
$o = ssh test_target where cmd
|
||||
$o | Should Contain "cmd"
|
||||
$o | Should Contain "cmd"
|
||||
}
|
||||
It "$tC.$tI - cmd as default shell and double quotes in cmdline" {
|
||||
# actual command line ssh target echo "hello"
|
||||
@ -269,7 +302,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
|
||||
# actual command line ssh target echo '$env:computername'
|
||||
$o = ssh test_target "echo 'hello'"
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$o | Should Be "'hello'"
|
||||
}
|
||||
}
|
||||
@ -290,15 +323,15 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
$o | Should Be "`"hello`""
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - cmdline parameters" {
|
||||
|
||||
Context "$tC - cmdline parameters" {
|
||||
BeforeAll {$tI=1}
|
||||
AfterAll{$tC++}
|
||||
|
||||
It "$tC.$tI - verbose to file (-v -E)" {
|
||||
$o = ssh -v -E $logFile test_target echo 1234
|
||||
$o | Should Be "1234"
|
||||
#TODO - checks below are very inefficient (time taking).
|
||||
#TODO - checks below are very inefficient (time taking).
|
||||
$logFile | Should Contain "OpenSSH_"
|
||||
$logFile | Should Contain "Exit Status 0"
|
||||
}
|
||||
@ -333,11 +366,11 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
" Port $port" | Add-Content $goodConfigFile
|
||||
" User $ssouser" | Add-Content $goodConfigFile
|
||||
$o = ssh -F $goodConfigFile myhost echo 1234
|
||||
$o | Should Be "1234"
|
||||
$o | Should Be "1234"
|
||||
}
|
||||
|
||||
It "$tC.$tI - IP options - (-4) (-6)" {
|
||||
# TODO - this test assumes target is localhost.
|
||||
# TODO - this test assumes target is localhost.
|
||||
# make it work independent of target
|
||||
#-4
|
||||
$o = ssh -4 -v -E $logFile test_target echo 1234
|
||||
@ -346,11 +379,23 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
||||
#-4
|
||||
$o = ssh -6 -v -E $logFile test_target echo 1234
|
||||
$o | Should Be "1234"
|
||||
$logFile | Should Contain "[::1]"
|
||||
$logFile | Should Contain "[::1]"
|
||||
}
|
||||
|
||||
It "$tC.$tI - tilde expand for path with forward slash" {
|
||||
$o = ssh -v -i ~/test/key/path -E $logFile test_target echo 1234
|
||||
$o | Should Be "1234"
|
||||
$logFile | Should Not Contain "tilde_expand: No such user"
|
||||
}
|
||||
|
||||
It "$tC.$tI - tilde expand for path with backslash" {
|
||||
$o = ssh -v -i ~\test\key\path -E $logFile test_target echo 1234
|
||||
$o | Should Be "1234"
|
||||
$logFile | Should Not Contain "tilde_expand: No such user"
|
||||
}
|
||||
|
||||
It "$tC.$tI - auto populate known hosts" {
|
||||
|
||||
|
||||
$kh = Join-Path $testDir "$tC.$tI.known_hosts"
|
||||
$nul | Set-Content $kh
|
||||
# doing via cmd to intercept and drain stderr output
|
||||
|
81
regress/pesterTests/SSHD.Tests.ps1
Normal file
81
regress/pesterTests/SSHD.Tests.ps1
Normal file
@ -0,0 +1,81 @@
|
||||
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
|
||||
|
||||
Describe "E2E scenarios for sshd" -Tags "CI" {
|
||||
BeforeAll {
|
||||
if($OpenSSHTestInfo -eq $null)
|
||||
{
|
||||
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
|
||||
}
|
||||
|
||||
$server = $OpenSSHTestInfo["Target"]
|
||||
$port = $OpenSSHTestInfo["Port"]
|
||||
$user = $OpenSSHTestInfo["PasswdUser"]
|
||||
}
|
||||
|
||||
Context "SSHD scenarios" {
|
||||
BeforeAll {
|
||||
# configure logingracetime to 10 seconds and presrerve the original config
|
||||
$sshdconfig = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config
|
||||
$sshdconfig_temp = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config_temp
|
||||
if (Test-Path $sshdconfig_temp) {
|
||||
Remove-Item $sshdconfig_temp -Force
|
||||
}
|
||||
Copy-Item $sshdconfig $sshdconfig_temp
|
||||
$content = Get-Content -Path $sshdconfig
|
||||
$newContent = $content -replace "#LoginGraceTime 2m", "LoginGraceTime 10"
|
||||
$newContent | Set-Content -Path $sshdconfig
|
||||
}
|
||||
|
||||
BeforeEach {
|
||||
Restart-Service -Name $OpenSSHTestInfo["SshdServiceName"] -Force
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
# restore original config
|
||||
Copy-Item $sshdconfig_temp $sshdconfig -Force
|
||||
Restart-Service -Name $OpenSSHTestInfo["SshdServiceName"] -Force
|
||||
Remove-Item $sshdconfig_temp -Force
|
||||
}
|
||||
|
||||
It "sshd child process ends when LoginGraceTime is exceeded" {
|
||||
# Get a count of any sshd processes before a connection in case there's another service running on the system
|
||||
# should be at least 1 sshd process for the test service
|
||||
$sshdPidCountBefore = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
|
||||
# Start ssh process (do not authenticate)
|
||||
$sshProc = Start-Process -FilePath ssh -ArgumentList "-l $user test_target" -PassThru
|
||||
Start-Sleep -Seconds 2
|
||||
$sshdPidsCountWithConn = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
|
||||
# Wait for LoginGraceTime to expire
|
||||
Start-Sleep -Seconds 10
|
||||
$sshdPidsCountAfter = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
|
||||
|
||||
if ($sshProc -and !$sshProc.HasExited) {
|
||||
$sshProc | Stop-Process -Force
|
||||
}
|
||||
|
||||
# with a connection, there should be two additional session processes
|
||||
$sshdPidsCountWithConn | Should Be (2 + $sshdPidCountBefore)
|
||||
# after LoginGraceTime expires, one of the session processes should exit
|
||||
$sshdPidsCountAfter | Should Be (1 + $sshdPidCountBefore)
|
||||
}
|
||||
|
||||
It "sshd pre-auth process is spawned under runtime generated virtual account" {
|
||||
$sshProc = Start-Process -FilePath ssh -ArgumentList "-l $user test_target" -PassThru
|
||||
Start-Sleep -Seconds 2
|
||||
$sshdProcessUsers = Get-Process -Name sshd* -IncludeUsername | Select-Object -ExpandProperty UserName
|
||||
$foundVirtualAccount = $false
|
||||
foreach ($username in $sshdProcessUsers) {
|
||||
if ($username -match '^VIRTUAL USERS\\sshd_\d+$') {
|
||||
$foundVirtualAccount = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($sshProc -and !$sshProc.HasExited) {
|
||||
$sshProc | Stop-Process -Force
|
||||
}
|
||||
|
||||
$foundVirtualAccount | Should Be $true
|
||||
}
|
||||
}
|
||||
}
|
@ -134,6 +134,20 @@ Match User matchuser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Set-SSHDConfigLine
|
||||
{
|
||||
param([string]$line, [string]$file)
|
||||
$sshdconfig_ori = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config
|
||||
if (Test-Path $file) {
|
||||
Remove-Item $file -Force
|
||||
}
|
||||
Copy-Item $sshdconfig_ori $file
|
||||
get-acl $sshdconfig_ori | set-acl $file
|
||||
$content = Get-Content -Path $file
|
||||
$newContent = @($line) + $content
|
||||
Set-Content -Path $file -Value $newContent
|
||||
}
|
||||
|
||||
#skip when the task schedular (*-ScheduledTask) cmdlets does not exist
|
||||
$ts = (get-command get-ScheduledTask -ErrorAction SilentlyContinue)
|
||||
@ -365,4 +379,50 @@ Match User matchuser
|
||||
Remove-UserFromLocalGroup -UserName $matchuser -GroupName $allowGroup1
|
||||
}
|
||||
}
|
||||
|
||||
Context "Tests of Other SSHD Config Directives via -T" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
$absoluteFilePath = Join-Path $testDir "includeFile"
|
||||
$relativeFilePath = "includeFile"
|
||||
$progDataPath = Join-Path $env:ProgramData $relativeFilePath
|
||||
# adding a line that would not be in a default sshd_config file
|
||||
$content = "loglevel DEBUG3"
|
||||
$content | Set-Content $absoluteFilePath
|
||||
$content | Set-Content $progDataPath
|
||||
$sshdconfig_custom = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config_custom
|
||||
$binPath = Join-Path $($OpenSSHTestInfo['OpenSSHBinPath']) "sshd.exe"
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$tC++
|
||||
if (Test-Path $absoluteFilePath) {
|
||||
Remove-Item $absoluteFilePath -force
|
||||
}
|
||||
if (Test-Path $progDataPath) {
|
||||
Remove-Item $progDataPath -force
|
||||
}
|
||||
if (Test-Path $sshdconfig_custom) {
|
||||
Remove-Item $sshdconfig_custom -force
|
||||
}
|
||||
}
|
||||
|
||||
It "$tC.$tI - Include Directive with absolute path starting with forward slash" {
|
||||
Set-SSHDConfigLine -line "Include /$absoluteFilePath" -file $sshdconfig_custom
|
||||
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
|
||||
$result.Contains($content) | Should Be $true
|
||||
}
|
||||
|
||||
It "$tC.$tI - Include Directive with absolute path starting with drive" {
|
||||
Set-SSHDConfigLine -line "Include $absoluteFilePath" -file $sshdconfig_custom
|
||||
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
|
||||
$result.Contains($content) | Should Be $true
|
||||
}
|
||||
|
||||
It "$tC.$tI - Include Directive with filename, relative to ProgramData" {
|
||||
Set-SSHDConfigLine -line "Include $relativeFilePath" -file $sshdconfig_custom
|
||||
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
|
||||
$result.Contains($content) | Should Be $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
|
||||
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
|
||||
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
|
||||
$suite = "Setup"
|
||||
$tC = 1
|
||||
@ -8,12 +8,12 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
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
|
||||
|
||||
$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)
|
||||
@ -34,24 +34,24 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
([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__
|
||||
$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
|
||||
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"
|
||||
$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.AccessControlType -eq ([System.Security.AccessControl.AccessControlType]::Allow)) -and `
|
||||
(([System.Int32]$a.RegistryRights.value__) -eq ($_.RegistryRights))
|
||||
}
|
||||
$findItem | Should Not Be $null
|
||||
@ -60,11 +60,11 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
foreach ($expected in $IdAcls) {
|
||||
$findItem = $nonPropagate | ? {
|
||||
((Get-UserAccount -UserSid ($expected.Identity)) -eq $_.IdentityReference) -and `
|
||||
($expected.IsInherited -eq $_.IsInherited) -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
|
||||
@ -86,8 +86,8 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
|
||||
$myACL = Get-ACL $FilePath
|
||||
$currentOwnerSid = Get-UserSid -User $myACL.Owner
|
||||
if(-not $windowsInBox) {return}
|
||||
$currentOwnerSid.Equals($OwnerSid) | Should Be $true
|
||||
if(-not $windowsInBox) {return}
|
||||
$currentOwnerSid.Equals($OwnerSid) | Should Be $true
|
||||
$myACL.Access | Should Not Be $null
|
||||
if($IsDirectory)
|
||||
{
|
||||
@ -111,7 +111,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
if($id -eq $null)
|
||||
{
|
||||
$idRefShortValue = ($a.IdentityReference.Value).split('\')[-1]
|
||||
$id = Get-UserSID -User $idRefShortValue
|
||||
$id = Get-UserSID -User $idRefShortValue
|
||||
}
|
||||
|
||||
$identities -contains $id | Should be $true
|
||||
@ -127,12 +127,12 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
else
|
||||
{
|
||||
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
{@($usersSid, $allApplicationPackagesSid, $allRestrictedApplicationPackagesSid, $authenticatedUserSid) -contains $_}
|
||||
{
|
||||
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
|
||||
{
|
||||
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
|
||||
break;
|
||||
}
|
||||
$trustedInstallerSid
|
||||
@ -141,7 +141,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$a.AccessControlType | Should Be ([System.Security.AccessControl.AccessControlType]::Allow)
|
||||
if($IsDirectory)
|
||||
{
|
||||
@ -154,8 +154,8 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
}
|
||||
$a.PropagationFlags | Should Be ([System.Security.AccessControl.PropagationFlags]::None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - Validate Openssh binary files" {
|
||||
|
||||
@ -165,6 +165,9 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
@{
|
||||
Name = 'sshd.exe'
|
||||
},
|
||||
@{
|
||||
Name = 'sshd-session.exe'
|
||||
},
|
||||
@{
|
||||
Name = 'ssh.exe'
|
||||
},
|
||||
@ -230,7 +233,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
}
|
||||
)
|
||||
}
|
||||
AfterAll{$tC++}
|
||||
AfterAll{$tC++}
|
||||
AfterEach { $tI++ }
|
||||
|
||||
It "$tC.$tI - Validate Openssh binary files--<Name>" -TestCases:$binaries{
|
||||
@ -238,7 +241,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
ValidateFileSystem -FilePath (join-path $binPath $Name)
|
||||
}
|
||||
It "$tC.$tI - Validate Openssh script files--<Name>" -TestCases:$dataFile {
|
||||
param([string]$Name, [boolean]$IsDirectory = $false)
|
||||
param([string]$Name, [boolean]$IsDirectory = $false)
|
||||
if(-not $WindowsInbox) { ValidateFileSystem -FilePath (join-path $binPath $Name) }
|
||||
}
|
||||
|
||||
@ -248,17 +251,48 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
{
|
||||
Start-Service sshd
|
||||
}
|
||||
|
||||
|
||||
ValidateFileSystem -FilePath (join-path $dataPath $Name) -IsDirectory $IsDirectory -OwnerSid $adminsSid -IsDataFile
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Context "$tC - Validate OpenSSH version" {
|
||||
BeforeAll {
|
||||
$tI = 1
|
||||
$sshExePath = Join-Path $binPath "ssh.exe"
|
||||
if (-not (Test-Path -Path $sshExePath)) {
|
||||
Throw "ssh.exe not found at $sshExePath"
|
||||
}
|
||||
}
|
||||
AfterAll { $tC++ }
|
||||
AfterEach { $tI++ }
|
||||
|
||||
It "$tC.$tI - Validate ssh -V output matches ssh.exe properties" {
|
||||
$pattern = "\d+\.\dp\d" # i.e. 9.2p2
|
||||
|
||||
$sshVersionOutput = & $sshExePath -V 2>&1 | Select-String -Pattern "OpenSSH"
|
||||
$match = $sshVersionOutput.Line -match $pattern
|
||||
if (-not $match) {
|
||||
throw "No matching version pattern found from ssh -V output"
|
||||
}
|
||||
$versionNumber = $Matches[0]
|
||||
|
||||
$fileVersionInfo = Get-Item $sshExePath | Select-Object -ExpandProperty VersionInfo
|
||||
$fileVersion = $fileVersionInfo.ProductVersion
|
||||
$match = $fileVersion -match $pattern
|
||||
if (-not $match) {
|
||||
throw "No matching version pattern found from ssh.exe properties"
|
||||
}
|
||||
$versionNumber | Should Match $Matches[0]
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - Validate Openssh registry entries" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
$servicePath = "HKLM:\SYSTEM\ControlSet001\Services"
|
||||
$opensshRegPath = "HKLM:\SOFTWARE\OpenSSH"
|
||||
|
||||
|
||||
$opensshACLs = @(
|
||||
@{
|
||||
Identity=$systemSid
|
||||
@ -271,7 +305,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
IsInherited = $false
|
||||
RegistryRights = $RegFullControlPerm
|
||||
PropagationFlags = "None"
|
||||
},
|
||||
},
|
||||
@{
|
||||
Identity=$authenticatedUserSid
|
||||
IsInherited = $false
|
||||
@ -294,9 +328,9 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
PropagationFlags = "None"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
AfterAll{$tC++}
|
||||
AfterEach { $tI++ }
|
||||
AfterEach { $tI++ }
|
||||
|
||||
It "$tC.$tI - Validate Registry key ssh-agent\Description" {
|
||||
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Description"
|
||||
@ -317,10 +351,10 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
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 = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Start"
|
||||
if($windowsInBox) {
|
||||
$p | Should Be 4
|
||||
}
|
||||
@ -332,12 +366,12 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
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" {
|
||||
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"
|
||||
@ -356,17 +390,17 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
}
|
||||
|
||||
It "$tC.$tI - Validate Registry key sshd\ObjectName" {
|
||||
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "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 = 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 = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Type"
|
||||
$p | Should Be 16
|
||||
}
|
||||
|
||||
@ -383,18 +417,18 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
{
|
||||
Start-Service ssh-agent
|
||||
ValidateRegistryACL -RegPath $agentPath -IdAcls $opensshAgentACLs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - Validate service settings" {
|
||||
BeforeAll {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
}
|
||||
}
|
||||
AfterAll{$tC++}
|
||||
AfterEach { $tI++ }
|
||||
|
||||
It "$tC.$tI - Validate properties of ssh-agent service" {
|
||||
It "$tC.$tI - Validate properties of ssh-agent service" {
|
||||
$sshdSvc = Get-service ssh-agent
|
||||
if($windowsInBox) {
|
||||
$sshdSvc.StartType | Should Be ([System.ServiceProcess.ServiceStartMode]::Disabled)
|
||||
@ -411,7 +445,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
($sshdSvc.RequiredServices).Count | Should Be 0
|
||||
}
|
||||
|
||||
It "$tC.$tI - Validate properties of sshd service" {
|
||||
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)
|
||||
@ -422,7 +456,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
($sshdSvc.ServicesDependedOn).Count | Should Be 0
|
||||
($sshdSvc.RequiredServices).Count | Should Be 0
|
||||
}
|
||||
|
||||
|
||||
It "$tC.$tI - Validate RequiredPrivileges of ssh-agent" {
|
||||
$expected = @("SeAssignPrimaryTokenPrivilege", "SeTcbPrivilege", "SeBackupPrivilege", "SeRestorePrivilege", "SeImpersonatePrivilege")
|
||||
$a = sc.exe qprivs ssh-agent 256
|
||||
@ -449,7 +483,7 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
}
|
||||
}
|
||||
|
||||
It "$tC.$tI - Validate security access to ssh-agent service" {
|
||||
It "$tC.$tI - Validate security access to ssh-agent service" {
|
||||
$a = @(sc.exe sdshow ssh-agent)
|
||||
$b = $a[-1] -split "[D|S]:"
|
||||
|
||||
@ -460,19 +494,19 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
$actual_dacl_aces = $dacl_aces | ? { -not [string]::IsNullOrWhiteSpace($_) }
|
||||
|
||||
$expected_dacl_aces | % {
|
||||
$actual_dacl_aces -contains $_ | Should be $true
|
||||
$actual_dacl_aces -contains $_ | Should be $true
|
||||
}
|
||||
$actual_dacl_aces | % {
|
||||
$expected_dacl_aces -contains $_ | Should be $true
|
||||
}
|
||||
|
||||
<# ignore sacl for now
|
||||
if($c.Count -gt 1) {
|
||||
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
|
||||
if($c.Count -gt 1) {
|
||||
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
|
||||
}#>
|
||||
}
|
||||
|
||||
It "$tC.$tI - Validate security access to sshd service" {
|
||||
It "$tC.$tI - Validate security access to sshd service" {
|
||||
$a = @(sc.exe sdshow sshd)
|
||||
$b = $a[-1] -split "[D|S]:"
|
||||
|
||||
@ -490,8 +524,8 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
}
|
||||
|
||||
<# ignore sacl for now
|
||||
if($c.Count -gt 1) {
|
||||
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
|
||||
if($c.Count -gt 1) {
|
||||
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
|
||||
}#>
|
||||
}
|
||||
}
|
||||
@ -501,12 +535,12 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
$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 = Get-NetFirewallRule -Name $firwallRuleName
|
||||
$rule.Group | Should BeLike "OpenSSH*"
|
||||
$rule.Description | Should BeLike "*OpenSSH*"
|
||||
$rule.DisplayName | Should BeLike "OpenSSH*"
|
||||
@ -519,6 +553,57 @@ Describe "Setup Tests" -Tags "Setup" {
|
||||
$fwportFilter.Protocol | Should Be 'TCP'
|
||||
$fwportFilter.LocalPort | Should Be 22
|
||||
$fwportFilter.RemotePort | Should Be 'Any'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "$tC - Validate SSHD service startup" {
|
||||
BeforeAll {
|
||||
$tI=1
|
||||
$sshFolderPath = Join-Path $env:ProgramData "ssh"
|
||||
$sshACL = $null
|
||||
if (Test-Path -Path $sshFolderPath) {
|
||||
$sshACL = Get-Acl $sshFolderPath
|
||||
}
|
||||
$logFolderPath = Join-Path $env:ProgramData "ssh" "logs"
|
||||
$logACL = $null
|
||||
if (Test-Path -Path $logFolderPath) {
|
||||
$logACL = Get-Acl $logFolderPath
|
||||
}
|
||||
if ((Get-Service sshd).Status -eq 'Running') {
|
||||
net stop sshd
|
||||
}
|
||||
}
|
||||
AfterAll {
|
||||
$tC++
|
||||
if ($logACL -eq $null) {
|
||||
Remove-Item -Path $logFolderPath -Recurse -Force
|
||||
}
|
||||
if ($sshACL -eq $null) {
|
||||
Remove-Item -Path $sshFolderPath -Recurse -Force
|
||||
}
|
||||
}
|
||||
AfterEach {
|
||||
$tI++
|
||||
net stop sshd
|
||||
if ($sshACL -ne $null) {
|
||||
Set-Acl -Path $sshFolderPath -AclObject $sshACL
|
||||
}
|
||||
if ($logACL -ne $null) {
|
||||
Set-Acl -Path $logFolderPath -AclObject $logACL
|
||||
}
|
||||
}
|
||||
|
||||
It "$tC.$tI - SSHD starts up successfully when Authenticated Users have read control over log folder" {
|
||||
if (-not (Test-Path -Path $logFolderPath)) {
|
||||
New-Item -Path $logFolderPath -ItemType Directory -Force
|
||||
}
|
||||
# Set ACLs on the folder
|
||||
$acl = Get-Acl $logFolderPath
|
||||
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($authenticatedUserSid, "ReadAndExecute", "Allow")
|
||||
$acl.SetAccessRule($accessRule)
|
||||
Set-Acl -Path $logFolderPath -AclObject $acl
|
||||
net start sshd
|
||||
$LASTEXITCODE | Should Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2319,7 +2319,11 @@ process_server_config_line_depth(ServerOptions *options, char *line,
|
||||
}
|
||||
value++;
|
||||
found = 0;
|
||||
#ifdef WINDOWS
|
||||
if (!path_absolute(arg2) && *arg2 != '~') {
|
||||
#else
|
||||
if (*arg2 != '/' && *arg2 != '~') {
|
||||
#endif
|
||||
xasprintf(&arg, "%s/%s", SSHDIR, arg2);
|
||||
} else
|
||||
arg = xstrdup(arg2);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh-agent.c,v 1.306 2024/03/09 05:12:13 djm Exp $ */
|
||||
/* $OpenBSD: ssh-agent.c,v 1.310 2025/02/18 08:02:48 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -1208,6 +1208,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
||||
"restrict-destination-v00@openssh.com") == 0) {
|
||||
if (*dcsp != NULL) {
|
||||
error_f("%s already set", ext_name);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
if ((r = sshbuf_froms(m, &b)) != 0) {
|
||||
@ -1217,6 +1218,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
||||
while (sshbuf_len(b) != 0) {
|
||||
if (*ndcsp >= AGENT_MAX_DEST_CONSTRAINTS) {
|
||||
error_f("too many %s constraints", ext_name);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
*dcsp = xrecallocarray(*dcsp, *ndcsp, *ndcsp + 1,
|
||||
@ -1234,6 +1236,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
||||
}
|
||||
if (*certs != NULL) {
|
||||
error_f("%s already set", ext_name);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
if ((r = sshbuf_get_u8(m, &v)) != 0 ||
|
||||
@ -1245,6 +1248,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
||||
while (sshbuf_len(b) != 0) {
|
||||
if (*ncerts >= AGENT_MAX_EXT_CERTS) {
|
||||
error_f("too many %s constraints", ext_name);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
*certs = xrecallocarray(*certs, *ncerts, *ncerts + 1,
|
||||
@ -1741,6 +1745,7 @@ process_ext_session_bind(SocketEntry *e)
|
||||
/* record new key/sid */
|
||||
if (e->nsession_ids >= AGENT_MAX_SESSION_IDS) {
|
||||
error_f("too many session IDs recorded");
|
||||
r = -1;
|
||||
goto out;
|
||||
}
|
||||
e->session_ids = xrecallocarray(e->session_ids, e->nsession_ids,
|
||||
|
12
ssh-keygen.c
12
ssh-keygen.c
@ -67,7 +67,11 @@
|
||||
#include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
|
||||
#include "cipher.h"
|
||||
|
||||
#ifdef WINDOWS
|
||||
#define DEFAULT_KEY_TYPE_NAME "ecdsa"
|
||||
#else
|
||||
#define DEFAULT_KEY_TYPE_NAME "ed25519"
|
||||
#endif /* WINDOWS */
|
||||
|
||||
/*
|
||||
* Default number of bits in the RSA, DSA and ECDSA keys. These value can be
|
||||
@ -82,7 +86,11 @@
|
||||
*/
|
||||
#define DEFAULT_BITS 3072
|
||||
#define DEFAULT_BITS_DSA 1024
|
||||
#ifdef WINDOWS
|
||||
#define DEFAULT_BITS_ECDSA 384
|
||||
#else
|
||||
#define DEFAULT_BITS_ECDSA 256
|
||||
#endif /* WINDOWS */
|
||||
|
||||
static int quiet = 0;
|
||||
|
||||
@ -259,7 +267,11 @@ ask_filename(struct passwd *pw, const char *prompt)
|
||||
char *name = NULL;
|
||||
|
||||
if (key_type_name == NULL)
|
||||
#ifdef WINDOWS
|
||||
name = _PATH_SSH_CLIENT_ID_ECDSA;
|
||||
#else
|
||||
name = _PATH_SSH_CLIENT_ID_ED25519;
|
||||
#endif /* WINDOWS */
|
||||
else {
|
||||
switch (sshkey_type_from_name(key_type_name)) {
|
||||
#ifdef WITH_DSA
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */
|
||||
/* $OpenBSD: ssh-sk-client.c,v 1.13 2025/02/18 08:02:48 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
@ -570,6 +570,7 @@ sshsk_load_resident(const char *provider_path, const char *device,
|
||||
}
|
||||
if ((srk = calloc(1, sizeof(*srk))) == NULL) {
|
||||
error_f("calloc failed");
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
srk->key = key;
|
||||
@ -581,6 +582,7 @@ sshsk_load_resident(const char *provider_path, const char *device,
|
||||
if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
|
||||
sizeof(*srks))) == NULL) {
|
||||
error_f("recallocarray keys failed");
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
debug_f("srks[%zu]: %s %s uidlen %zu", nsrks,
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshconnect2.c,v 1.373 2024/05/17 06:38:00 jsg Exp $ */
|
||||
/* $OpenBSD: sshconnect2.c,v 1.377 2025/02/18 08:02:48 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
@ -103,7 +103,7 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
|
||||
options.required_rsa_size)) != 0)
|
||||
fatal_r(r, "Bad server host key");
|
||||
if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
|
||||
xxx_conn_info) == -1)
|
||||
xxx_conn_info) != 0)
|
||||
fatal("Host key verification failed.");
|
||||
return 0;
|
||||
}
|
||||
@ -722,6 +722,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
|
||||
send_pubkey_telemetry("server sent unknown pkalg");
|
||||
#endif
|
||||
debug_f("server sent unknown pkalg %s", pkalg);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
|
||||
@ -738,6 +739,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
|
||||
error("input_userauth_pk_ok: type mismatch "
|
||||
"for decoded key (received %d, expected %d)",
|
||||
key->type, pktype);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -757,6 +759,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
|
||||
SSH_FP_DEFAULT);
|
||||
error_f("server replied with unknown key: %s %s",
|
||||
sshkey_type(key), fp == NULL ? "<ERROR>" : fp);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto done;
|
||||
}
|
||||
ident = format_identity(id);
|
||||
|
@ -26,5 +26,6 @@
|
||||
#define _SSH_FILE_PERM_H
|
||||
|
||||
int check_secure_file_permission(const char *, struct passwd *, int);
|
||||
int check_secure_folder_permission(const wchar_t*, int);
|
||||
void check_secure_folder_permission(const wchar_t*, int);
|
||||
void log_folder_perms_msg_etw(const wchar_t*, wchar_t*);
|
||||
#endif /* _SSH_FILE_PERM_H */
|
||||
|
3
sshsig.c
3
sshsig.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshsig.c,v 1.35 2024/03/08 22:16:32 djm Exp $ */
|
||||
/* $OpenBSD: sshsig.c,v 1.38 2025/02/18 08:02:48 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
@ -907,6 +907,7 @@ cert_filter_principals(const char *path, u_long linenum,
|
||||
}
|
||||
if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
|
||||
error_f("buffer error");
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
/* success */
|
||||
|
Loading…
x
Reference in New Issue
Block a user