diff --git a/appveyor.yml b/appveyor.yml index d18cc0a..c713ebf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,40 +1,39 @@ -version: 0.0.16.0.{build} +version: 0.0.17.0.{build} image: Visual Studio 2015 branches: only: - latestw_all - - latestw_all_openssl init: - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) build_script: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 Invoke-AppVeyorBuild after_build: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 Install-OpenSSH before_test: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking - Setup-OpenSSHTestEnvironment -Quiet + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 + Set-OpenSSHTestEnvironment -Confirm:$false test_script: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking - Run-OpenSSHTests + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 + Invoke-OpenSSHTests after_test: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking - Upload-OpenSSHTestResults + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 + Publish-OpenSSHTestResults on_finish: - ps: | - Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 -DisableNameChecking + Import-Module $env:APPVEYOR_BUILD_FOLDER\contrib\win32\openssh\AppveyorHelper.psm1 Publish-Artifact \ No newline at end of file diff --git a/auth-passwd.c b/auth-passwd.c index cfb829c..8804dc4 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -226,38 +226,45 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) #elif defined(WINDOWS) /* -* Authenticate on Windows - Pass credentials to ssh-agent and retrieve token -* upon successful authentication -* TODO - password is sent in plain text over IPC. Consider implications. +* Authenticate on Windows - Call LogonUser and retrieve user token */ int sys_auth_passwd(Authctxt *authctxt, const char *password) { - struct sshbuf *msg = NULL; - size_t blen = 0; - DWORD token = 0; - extern int auth_sock; + wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; + HANDLE token = NULL; int r = 0; - int ssh_request_reply(int, struct sshbuf *, struct sshbuf *); - msg = sshbuf_new(); - if (!msg) - fatal("%s: out of memory", __func__); - - if (sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE) != 0 || - sshbuf_put_cstring(msg, PASSWD_AUTH_REQUEST) != 0 || - sshbuf_put_cstring(msg, authctxt->pw->pw_name) != 0 || - sshbuf_put_cstring(msg, password) != 0 || - ssh_request_reply(auth_sock, msg, msg) != 0 || - sshbuf_get_u32(msg, &token) != 0) { - debug("auth agent did not authorize client %s", authctxt->user); - r = 0; + if ((user_utf16 = utf8_to_utf16(authctxt->pw->pw_name)) == NULL || + (pwd_utf16 = utf8_to_utf16(password)) == NULL) { + fatal("out of memory"); goto done; } - authctxt->methoddata = (void*)(INT_PTR)token; + + if ((tmp = wcschr(user_utf16, L'@')) != NULL) { + udom_utf16 = tmp + 1; + *tmp = L'\0'; + } + + if (LogonUserW(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, + LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { + if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE) + /* + * TODO - need to add support to force password change + * by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ + */ + error("password for user %s has expired", authctxt->pw->pw_name); + else + debug("failed to logon user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); + goto done; + } + + authctxt->auth_token = (void*)(INT_PTR)token; r = 1; done: - if (msg) - sshbuf_free(msg); + if (user_utf16) + free(user_utf16); + if (pwd_utf16) + SecureZeroMemory(pwd_utf16, sizeof(wchar_t) * wcslen(pwd_utf16)); return r; } #endif /* WINDOWS */ diff --git a/auth.c b/auth.c index 9084958..2f18cf8 100644 --- a/auth.c +++ b/auth.c @@ -405,6 +405,13 @@ expand_authorized_keys(const char *filename, struct passwd *pw) file = percent_expand(filename, "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); +#ifdef WINDOWS + /* Return if the path is absolute. If not, prepend the '%h\\' */ + if ((strlen(file) > 1) && (file[1] == ':')) + return (file); + + i = snprintf(ret, sizeof(ret), "%s\\%s", pw->pw_dir, file); +#else /* * Ensure that filename starts anchored. If not, be backward * compatible and prepend the '%h/' @@ -413,6 +420,8 @@ expand_authorized_keys(const char *filename, struct passwd *pw) return (file); i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); +#endif // WINDOWS + if (i < 0 || (size_t)i >= sizeof(ret)) fatal("expand_authorized_keys: path too long"); free(file); diff --git a/auth.h b/auth.h index 6779354..a5cd750 100644 --- a/auth.h +++ b/auth.h @@ -78,7 +78,9 @@ struct Authctxt { #endif Buffer *loginmsg; void *methoddata; - +#ifdef WINDOWS + void *auth_token; +#endif struct sshkey **prev_userkeys; u_int nprev_userkeys; }; diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 084cbf0..db44036 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -200,54 +200,14 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; -#ifdef WINDOWS - /* Pass key challenge material to ssh-agent to retrieve token upon successful authentication */ - { - struct sshbuf *msg = NULL; - u_char *blob = NULL; - size_t blen = 0; - DWORD token = 0; - extern int auth_sock; - int r = 0; - int ssh_request_reply(int , struct sshbuf *, struct sshbuf *); - - while (1) { - msg = sshbuf_new(); - if (!msg) - fatal("%s: out of memory", __func__); - if ((r = sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE)) != 0 || - (r = sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST)) != 0 || - (r = sshkey_to_blob(key, &blob, &blen)) != 0 || - (r = sshbuf_put_string(msg, blob, blen)) != 0 || - (r = sshbuf_put_cstring(msg, authctxt->pw->pw_name)) != 0 || - (r = sshbuf_put_string(msg, sig, slen)) != 0 || - (r = sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b))) != 0 || - (r = ssh_request_reply(auth_sock, msg, msg)) != 0 || - (r = sshbuf_get_u32(msg, &token)) != 0) { - debug("auth agent did not authorize client %s", authctxt->user); - break; - } - - debug3("auth agent authenticated %s", authctxt->user); - break; - - } - if (blob) - free(blob); - if (msg) - sshbuf_free(msg); - - if (token) { - authenticated = 1; - authctxt->methoddata = (void*)(INT_PTR)token; - } - - } - -#else /* !WINDOWS */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && +#ifdef WINDOWS + (authctxt->auth_token = mm_auth_pubkey(authctxt->pw->pw_name, + key, sig, slen, b)) != NULL) { +#else PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), ssh->compat)) == 0) { +#endif authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); @@ -255,7 +215,6 @@ userauth_pubkey(struct ssh *ssh) } sshbuf_free(b); free(sig); -#endif /* !WINDOWS */ } else { debug("%s: test whether pkalg/pkblob are acceptable for %s %s", diff --git a/authfd.h b/authfd.h index a9874e1..5804f18 100644 --- a/authfd.h +++ b/authfd.h @@ -43,6 +43,7 @@ int ssh_agent_sign(int sock, struct sshkey *key, const u_char *data, size_t datalen, const char *alg, u_int compat); /* Messages for the authentication agent connection. */ +/* Message Id 0 is reserved */ #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 #define SSH_AGENTC_RSA_CHALLENGE 3 @@ -88,12 +89,4 @@ int ssh_agent_sign(int sock, struct sshkey *key, #define SSH_AGENT_RSA_SHA2_256 0x02 #define SSH_AGENT_RSA_SHA2_512 0x04 -/* -* Following are used in Windows implementation -* ssh-agent in Windows also serves user authentication -*/ -#define SSH_AGENT_AUTHENTICATE 200 -#define PUBKEY_AUTH_REQUEST "pubkey" -#define PASSWD_AUTH_REQUEST "password" - #endif /* AUTHFD_H */ diff --git a/contrib/win32/openssh/AppveyorHelper.psm1 b/contrib/win32/openssh/AppveyorHelper.psm1 index 7a851a2..b20fbd0 100644 --- a/contrib/win32/openssh/AppveyorHelper.psm1 +++ b/contrib/win32/openssh/AppveyorHelper.psm1 @@ -1,12 +1,14 @@ $ErrorActionPreference = 'Stop' -Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -Force -DisableNameChecking -Import-Module $PSScriptRoot\OpenSSHBuildHelper.psm1 -Force -DisableNameChecking -Import-Module $PSScriptRoot\OpenSSHTestHelper.psm1 -Force -DisableNameChecking +Set-StrictMode -Version 2.0 +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -Force +Import-Module $PSScriptRoot\OpenSSHBuildHelper.psm1 -Force +Import-Module $PSScriptRoot\OpenSSHTestHelper.psm1 -Force $repoRoot = Get-RepositoryRoot $script:messageFile = join-path $repoRoot.FullName "BuildMessage.log" -# Sets a build variable +# Write the build message Function Write-BuildMessage { param( @@ -77,12 +79,11 @@ function Invoke-AppVeyorFull { $env:APPVEYOR_SCHEDULED_BUILD = 'True' } - try { - Set-OpenSSHTestParams + try { Invoke-AppVeyorBuild Install-OpenSSH - Setup-OpenSSHTestEnvironment - Run-OpenSSHTests + Set-OpenSSHTestEnvironment -confirm:$false + Invoke-OpenSSHTests Publish-Artifact } finally { @@ -97,8 +98,8 @@ function Invoke-AppVeyorFull function Invoke-AppVeyorBuild { Set-BuildVariable TestPassed True - Build-OpenSSH -Configuration Release -NativeHostArch x64 - Build-OpenSSH -Configuration Debug -NativeHostArch x86 + Start-OpenSSHBuild -Configuration Release -NativeHostArch x64 + Start-OpenSSHBuild -Configuration Release -NativeHostArch x86 Write-BuildMessage -Message "OpenSSH binaries build success!" -Category Information } @@ -136,6 +137,100 @@ function Add-BuildLog } } +<# + .Synopsis + Deploy all required files to a location and install the binaries +#> +function Install-OpenSSH +{ + [CmdletBinding()] + param + ( + [ValidateSet('Debug', 'Release')] + [string]$Configuration = "Release", + + [ValidateSet('x86', 'x64', '')] + [string]$NativeHostArch = "", + + [string]$OpenSSHDir = "$env:SystemDrive\OpenSSH" + ) + + if ($NativeHostArch -eq "") + { + $NativeHostArch = 'x64' + if ($env:PROCESSOR_ARCHITECTURE -eq 'x86') { + $NativeHostArch = 'x86' + } + } + + Start-OpenSSHPackage -NativeHostArch $NativeHostArch -Configuration $Configuration -DestinationPath $OpenSSHDir + + Push-Location $OpenSSHDir + & "$OpenSSHDir\install-sshd.ps1" + & "$OpenSSHDir\ssh-keygen.exe" -A + & "$OpenSSHDir\FixHostFilePermissions.ps1" -Confirm:$false + + #machine will be reboot after Install-openssh anyway + $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') + $newMachineEnvironmentPath = $machinePath + if (-not ($machinePath.ToLower().Contains($OpenSSHDir.ToLower()))) + { + $newMachineEnvironmentPath = "$OpenSSHDir;$newMachineEnvironmentPath" + $env:Path = "$OpenSSHDir;$env:Path" + } + # Update machine environment path + if ($newMachineEnvironmentPath -ne $machinePath) + { + [Environment]::SetEnvironmentVariable('Path', $newMachineEnvironmentPath, 'MACHINE') + } + + Set-Service sshd -StartupType Automatic + Set-Service ssh-agent -StartupType Automatic + + Pop-Location + Write-BuildMessage -Message "OpenSSH installed!" -Category Information +} + +<# + .Synopsis + uninstalled sshd +#> +function UnInstall-OpenSSH +{ + [CmdletBinding()] + param + ( + [string]$OpenSSHDir = "$env:SystemDrive\OpenSSH" + ) + + if (-not (Test-Path $OpenSSHDir -PathType Container)) + { + return + } + + Push-Location $OpenSSHDir + if((Get-Service ssh-agent -ErrorAction SilentlyContinue) -ne $null) { + Stop-Service ssh-agent -Force + } + & "$OpenSSHDir\uninstall-sshd.ps1" + + $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') + $newMachineEnvironmentPath = $machinePath + if ($machinePath.ToLower().Contains($OpenSSHDir.ToLower())) + { + $newMachineEnvironmentPath = $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '') + $env:Path = $env:Path.Replace("$OpenSSHDir;", '') + } + + if ($newMachineEnvironmentPath -ne $machinePath) + { + [Environment]::SetEnvironmentVariable('Path', $newMachineEnvironmentPath, 'MACHINE') + } + + Pop-Location + Remove-Item -Path $OpenSSHDir -Recurse -Force -ErrorAction SilentlyContinue +} + <# .Synopsis Publishes package build artifacts. @@ -170,11 +265,11 @@ function Add-Artifact function Publish-Artifact { Write-Host -ForegroundColor Yellow "Publishing project artifacts" - [System.Collections.ArrayList] $artifacts = [System.Collections.ArrayList]::new() + [System.Collections.ArrayList] $artifacts = new-object System.Collections.ArrayList # Get the build.log file for each build configuration Add-BuildLog -artifacts $artifacts -buildLog (Get-BuildLogFile -root $repoRoot.FullName -Configuration Release -NativeHostArch x64) - Add-BuildLog -artifacts $artifacts -buildLog (Get-BuildLogFile -root $repoRoot.FullName -Configuration Debug -NativeHostArch x86) + Add-BuildLog -artifacts $artifacts -buildLog (Get-BuildLogFile -root $repoRoot.FullName -Configuration Release -NativeHostArch x86) if($Global:OpenSSHTestInfo) { @@ -186,7 +281,6 @@ function Publish-Artifact foreach ($artifact in $artifacts) { Write-Host "Publishing $artifact as Appveyor artifact" - # NOTE: attempt to publish subsequent artifacts even if the current one fails Push-AppveyorArtifact $artifact -ErrorAction Continue } } @@ -195,10 +289,10 @@ function Publish-Artifact .Synopsis Runs the tests for this repo #> -function Run-OpenSSHTests +function Invoke-OpenSSHTests { Write-Host "Start running unit tests" - $unitTestFailed = Run-OpenSSHUnitTest + $unitTestFailed = Invoke-OpenSSHUnitTest if($unitTestFailed) { @@ -212,14 +306,14 @@ function Run-OpenSSHTests Write-BuildMessage -Message "All Unit tests passed!" -Category Information } # Run all E2E tests. - Run-OpenSSHE2ETest + Invoke-OpenSSHE2ETest if (($OpenSSHTestInfo -eq $null) -or (-not (Test-Path $OpenSSHTestInfo["E2ETestResultsFile"]))) { Write-Warning "Test result file $OpenSSHTestInfo["E2ETestResultsFile"] not found after tests." Write-BuildMessage -Message "Test result file $OpenSSHTestInfo["E2ETestResultsFile"] not found after tests." -Category Error Set-BuildVariable TestPassed False } - $xml = [xml](Get-Content -raw $OpenSSHTestInfo["E2ETestResultsFile"]) + $xml = [xml](Get-Content $OpenSSHTestInfo["E2ETestResultsFile"] | out-string) if ([int]$xml.'test-results'.failures -gt 0) { $errorMessage = "$($xml.'test-results'.failures) tests in regress\pesterTests failed. Detail test log is at $($OpenSSHTestInfo["E2ETestResultsFile"])." @@ -239,7 +333,7 @@ function Run-OpenSSHTests .Synopsis upload OpenSSH pester test results. #> -function Upload-OpenSSHTestResults +function Publish-OpenSSHTestResults { if ($env:APPVEYOR_JOB_ID) { diff --git a/contrib/win32/openssh/FixHostFilePermissions.ps1 b/contrib/win32/openssh/FixHostFilePermissions.ps1 index e15e16f..fbff664 100644 --- a/contrib/win32/openssh/FixHostFilePermissions.ps1 +++ b/contrib/win32/openssh/FixHostFilePermissions.ps1 @@ -1,7 +1,7 @@ [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param () Set-StrictMode -Version 2.0 -If (!(Test-Path variable:PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition} +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} Import-Module $PSScriptRoot\OpenSSHUtils -Force #check sshd config file diff --git a/contrib/win32/openssh/FixUserFilePermissions.ps1 b/contrib/win32/openssh/FixUserFilePermissions.ps1 index 93e6468..b6259b3 100644 --- a/contrib/win32/openssh/FixUserFilePermissions.ps1 +++ b/contrib/win32/openssh/FixUserFilePermissions.ps1 @@ -1,7 +1,7 @@ [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param () Set-StrictMode -Version 2.0 -If (!(Test-Path variable:PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition} +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} Import-Module $PSScriptRoot\OpenSSHUtils -Force diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 index dca5521..2d43390 100644 --- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 @@ -1,7 +1,7 @@ -Set-StrictMode -Version Latest +Set-StrictMode -Version 2.0 +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -Force -Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -force -DisableNameChecking -[string] $script:platform = $env:PROCESSOR_ARCHITECTURE [string] $script:vcPath = $null [System.IO.DirectoryInfo] $script:OpenSSHRoot = $null [System.IO.DirectoryInfo] $script:gitRoot = $null @@ -83,7 +83,7 @@ function Write-BuildMsg [switch] $Silent ) - if ($AsVerbose) + if($PSBoundParameters.ContainsKey("AsVerbose")) { if ($script:Verbose) { @@ -96,17 +96,24 @@ function Write-BuildMsg return } - if ($AsInfo) + if($PSBoundParameters.ContainsKey("AsInfo")) { Write-Log -Message "INFO: $message" if (-not $Silent) { - Write-Information -MessageData $message -InformationAction Continue + if(Get-Command "Write-Information" -ErrorAction SilentlyContinue ) + { + Write-Information -MessageData $message -InformationAction Continue + } + else + { + Write-Verbose -Message $message -Verbose + } } return } - if ($AsWarning) + if($PSBoundParameters.ContainsKey("AsWarning")) { Write-Log -Message "WARNING: $message" if (-not $Silent) @@ -116,7 +123,7 @@ function Write-BuildMsg return } - if ($AsError) + if($PSBoundParameters.ContainsKey("AsError")) { Write-Log -Message "ERROR: $message" if (-not $Silent) @@ -137,8 +144,6 @@ function Write-BuildMsg function Start-OpenSSHBootstrap { [bool] $silent = -not $script:Verbose - - Set-StrictMode -Version Latest Write-BuildMsg -AsInfo -Message "Checking tools and dependencies" -Silent:$silent $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') @@ -146,7 +151,7 @@ function Start-OpenSSHBootstrap # Install chocolatey $chocolateyPath = "$env:AllUsersProfile\chocolatey\bin" - if(Get-Command "choco" -ErrorAction SilentlyContinue) + if(Get-Command choco -ErrorAction SilentlyContinue) { Write-BuildMsg -AsVerbose -Message "Chocolatey is already installed. Skipping installation." -Silent:$silent } @@ -156,20 +161,38 @@ function Start-OpenSSHBootstrap Invoke-Expression ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')) 2>&1 >> $script:BuildLogFile } + if (-not ($machinePath.ToLower().Contains($chocolateyPath.ToLower()))) + { + Write-BuildMsg -AsVerbose -Message "Adding $chocolateyPath to Path environment variable" -Silent:$silent + $newMachineEnvironmentPath = "$chocolateyPath;$newMachineEnvironmentPath" + if(-not ($env:Path.ToLower().Contains($chocolateyPath.ToLower()))) + { + $env:Path = "$chocolateyPath;$env:Path" + } + } + else + { + Write-BuildMsg -AsVerbose -Message "$chocolateyPath already present in Path environment variable" -Silent:$silent + } + # Add git\cmd to the path $gitCmdPath = "$env:ProgramFiles\git\cmd" if (-not ($machinePath.ToLower().Contains($gitCmdPath.ToLower()))) { Write-BuildMsg -AsVerbose -Message "Adding $gitCmdPath to Path environment variable" -Silent:$silent $newMachineEnvironmentPath = "$gitCmdPath;$newMachineEnvironmentPath" + if(-not ($env:Path.ToLower().Contains($gitCmdPath.ToLower()))) + { + $env:Path = "$gitCmdPath;$env:Path" + } } else { Write-BuildMsg -AsVerbose -Message "$gitCmdPath already present in Path environment variable" -Silent:$silent } - - $nativeMSBuildPath = "${env:ProgramFiles(x86)}\MSBuild\14.0\bin" - if($script:platform -ieq "AMD64") + + $nativeMSBuildPath = "${env:ProgramFiles(x86)}\MSBuild\14.0\bin" + if($env:PROCESSOR_ARCHITECTURE -ieq "AMD64") { $nativeMSBuildPath += "\amd64" } @@ -178,12 +201,15 @@ function Start-OpenSSHBootstrap { Write-BuildMsg -AsVerbose -Message "Adding $nativeMSBuildPath to Path environment variable" -Silent:$silent $newMachineEnvironmentPath += ";$nativeMSBuildPath" - $env:Path += ";$nativeMSBuildPath" + if(-not ($env:Path.ToLower().Contains($nativeMSBuildPath.ToLower()))) + { + $env:Path += ";$nativeMSBuildPath" + } } else { Write-BuildMsg -AsVerbose -Message "$nativeMSBuildPath already present in Path environment variable" -Silent:$silent - } + } # Update machine environment path if ($newMachineEnvironmentPath -ne $machinePath) @@ -191,42 +217,53 @@ function Start-OpenSSHBootstrap [Environment]::SetEnvironmentVariable('Path', $newMachineEnvironmentPath, 'MACHINE') } - # Install Visual Studio 2015 Community - $packageName = "VisualStudio2015Community" - $VSPackageInstalled = Get-ItemProperty "HKLM:\software\WOW6432Node\Microsoft\VisualStudio\14.0\setup\vs" -ErrorAction SilentlyContinue - - if ($null -eq $VSPackageInstalled) + $VCTargetsPath = "${env:ProgramFiles(x86)}\MSBuild\Microsoft.Cpp\v4.0\V140" + if([Environment]::GetEnvironmentVariable('VCTargetsPath', 'MACHINE') -eq $null) { - Write-BuildMsg -AsInfo -Message "$packageName not present. Installing $packageName." - $adminFilePath = "$script:OpenSSHRoot\contrib\win32\openssh\VSWithBuildTools.xml" - choco install $packageName -packageParameters "--AdminFile $adminFilePath" -y --force --limitoutput --execution-timeout 10000 2>&1 >> $script:BuildLogFile + [Environment]::SetEnvironmentVariable('VCTargetsPath', $VCTargetsPath, 'MACHINE') } - else + if ($env:VCTargetsPath -eq $null) { - Write-BuildMsg -AsVerbose -Message "$packageName present. Skipping installation." -Silent:$silent + $env:VCTargetsPath = $VCTargetsPath } - # Install Windows 8.1 SDK - $packageName = "windows-sdk-8.1" + $vcVars = "${env:ProgramFiles(x86)}\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat" $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\8.1\bin\x86\register_app.vbs" - - if (-not (Test-Path -Path $sdkPath)) - { - Write-BuildMsg -AsInfo -Message "Windows 8.1 SDK not present. Installing $packageName." - choco install $packageName -y --limitoutput --force 2>&1 >> $script:BuildLogFile + $packageName = "vcbuildtools" + If ((-not (Test-Path $nativeMSBuildPath)) -or (-not (Test-Path $VcVars)) -or (-not (Test-Path $sdkPath))) { + Write-BuildMsg -AsInfo -Message "$packageName not present. Installing $packageName ..." + choco install $packageName -ia "/InstallSelectableItems VisualCppBuildTools_ATLMFC_SDK;VisualCppBuildTools_NETFX_SDK;Win81SDK_CppBuildSKUV1" -y --force --limitoutput --execution-timeout 10000 2>&1 >> $script:BuildLogFile + $errorCode = $LASTEXITCODE + if ($errorCode -eq 3010) + { + Write-Host "The recent package changes indicate a reboot is necessary. please reboot the machine, open a new powershell window and call Start-SSHBuild or Start-OpenSSHBootstrap again." -ForegroundColor Black -BackgroundColor Yellow + Do { + $input = Read-Host -Prompt "Reboot the machine? [Yes] Y; [No] N (default is `"Y`")" + if([string]::IsNullOrEmpty($input)) + { + $input = 'Y' + } + } until ($input -match "^(y(es)?|N(o)?)$") + [string]$ret = $Matches[0] + if ($ret.ToLower().Startswith('y')) + { + Write-BuildMsg -AsWarning -Message "restarting machine ..." + Restart-Computer -Force + exit + } + else + { + Write-BuildMsg -AsError -ErrorAction Stop -Message "User choose not to restart the machine to apply the changes." + } + } + else + { + Write-BuildMsg -AsError -ErrorAction Stop -Message "$packageName installation failed with error code $errorCode" + } } else { - Write-BuildMsg -AsInfo -Message "$packageName present. Skipping installation." -Silent:$silent - } - - # Require restarting PowerShell session - if ($null -eq $VSPackageInstalled) - { - Write-Host "To apply changes, please close this PowerShell window, open a new one and call Start-SSHBuild or Start-DscBootstrap again." -ForegroundColor Black -BackgroundColor Yellow - Write-Host -NoNewLine 'Press any key to close this PowerShell window...' -ForegroundColor Black -BackgroundColor Yellow - $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') - exit + Write-BuildMsg -AsVerbose -Message 'VC++ 2015 Build Tools already present.' } # Ensure the VS C toolset is installed @@ -241,11 +278,11 @@ function Start-OpenSSHBootstrap Write-BuildMsg -AsVerbose -Message "vcPath: $script:vcPath" -Silent:$silent if ((Test-Path -Path "$script:vcPath\vcvarsall.bat") -eq $false) { - Write-BuildMsg -AsError -ErrorAction Stop -Message "Could not find Visual Studio vcvarsall.bat at$script:vcPath, which means some required develop kits are missing on the machine." + Write-BuildMsg -AsError -ErrorAction Stop -Message "Could not find Visual Studio vcvarsall.bat at $script:vcPath, which means some required develop kits are missing on the machine." } } -function Clone-Win32OpenSSH +function Get-Win32OpenSSHRepo { [bool] $silent = -not $script:Verbose @@ -264,7 +301,7 @@ function Clone-Win32OpenSSH Pop-Location } -function Delete-Win32OpenSSH +function Remove-Win32OpenSSHRepo { Remove-Item -Path $script:win32OpenSSHPath -Recurse -Force -ErrorAction SilentlyContinue } @@ -282,7 +319,7 @@ function Copy-LibreSSLSDK } } -function Package-OpenSSH +function Start-OpenSSHPackage { [CmdletBinding(SupportsShouldProcess=$false)] param @@ -290,7 +327,7 @@ function Package-OpenSSH [ValidateSet('x86', 'x64')] [string]$NativeHostArch = "x64", - [ValidateSet('Debug', 'Release', '')] + [ValidateSet('Debug', 'Release')] [string]$Configuration = "Release", # Copy payload to DestinationPath instead of packaging @@ -315,7 +352,7 @@ function Package-OpenSSH if ($NativeHostArch -ieq 'x86') { $packageName = "OpenSSH-Win32" } - while((($service = Get-Service ssh-agent -ErrorAction Ignore) -ne $null) -and ($service.Status -ine 'Stopped')) + while((($service = Get-Service ssh-agent -ErrorAction SilentlyContinue) -ne $null) -and ($service.Status -ine 'Stopped')) { Stop-Service ssh-agent -Force #sleep to wait the servicelog file write @@ -361,11 +398,17 @@ function Package-OpenSSH } else { Remove-Item ($packageDir + '.zip') -Force -ErrorAction SilentlyContinue - Compress-Archive -Path $packageDir -DestinationPath ($packageDir + '.zip') - Write-BuildMsg -AsInfo -Message "Packaged Payload - '$packageDir'.zip" + if(get-command Compress-Archive -ErrorAction SilentlyContinue) + { + Compress-Archive -Path $packageDir -DestinationPath ($packageDir + '.zip') + Write-BuildMsg -AsInfo -Message "Packaged Payload - '$packageDir.zip'" + } + else + { + Write-BuildMsg -AsInfo -Message "Packaged Payload not compressed." + } } Remove-Item $packageDir -Recurse -Force -ErrorAction SilentlyContinue - if ($DestinationPath -ne "") { Copy-Item -Path $symbolsDir\* -Destination $DestinationPath -Force -Recurse @@ -373,13 +416,20 @@ function Package-OpenSSH } else { Remove-Item ($symbolsDir + '.zip') -Force -ErrorAction SilentlyContinue - Compress-Archive -Path $symbolsDir -DestinationPath ($symbolsDir + '.zip') - Write-BuildMsg -AsInfo -Message "Packaged Symbols - '$symbolsDir'.zip" + if(get-command Compress-Archive -ErrorAction SilentlyContinue) + { + Compress-Archive -Path $symbolsDir -DestinationPath ($symbolsDir + '.zip') + Write-BuildMsg -AsInfo -Message "Packaged Symbols - '$symbolsDir.zip'" + } + else + { + Write-BuildMsg -AsInfo -Message "Packaged Symbols not compressed." + } } Remove-Item $symbolsDir -Recurse -Force -ErrorAction SilentlyContinue } -function Build-OpenSSH +function Start-OpenSSHBuild { [CmdletBinding(SupportsShouldProcess=$false)] param @@ -387,12 +437,11 @@ function Build-OpenSSH [ValidateSet('x86', 'x64')] [string]$NativeHostArch = "x64", - [ValidateSet('Debug', 'Release', '')] + [ValidateSet('Debug', 'Release')] [string]$Configuration = "Release", [switch]$NoOpenSSL - ) - Set-StrictMode -Version Latest + ) $script:BuildLogFile = $null [System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot @@ -420,9 +469,9 @@ function Build-OpenSSH $script:win32OpenSSHPath = join-path $script:gitRoot "Win32-OpenSSH" if (-not (Test-Path (Join-Path $PSScriptRoot LibreSSLSDK))) { - Clone-Win32OpenSSH + Get-Win32OpenSSHRepo Copy-LibreSSLSDK - Delete-Win32OpenSSH + Remove-Win32OpenSSHRepo } if ($NoOpenSSL) @@ -465,7 +514,7 @@ function Get-BuildLogFile [ValidateSet('x86', 'x64')] [string]$NativeHostArch = "x64", - [ValidateSet('Debug', 'Release', '')] + [ValidateSet('Debug', 'Release')] [string]$Configuration = "Release" ) @@ -483,122 +532,6 @@ function Get-SolutionFile return Join-Path -Path $root -ChildPath "contrib\win32\openssh\Win32-OpenSSH.sln" } -<# - .Synopsis - Deploy all required files to a location and install the binaries -#> -function Install-OpenSSH -{ - [CmdletBinding()] - param - ( - [ValidateSet('Debug', 'Release', '')] - [string]$Configuration = "", - [ValidateSet('x86', 'x64', '')] - [string]$NativeHostArch = "", - [string]$OpenSSHDir = "$env:SystemDrive\OpenSSH" - ) - - if ($Configuration -eq "") - { - $Configuration = 'Release' - } - - if ($NativeHostArch -eq "") - { - $NativeHostArch = 'x64' - if ($env:PROCESSOR_ARCHITECTURE -eq 'x86') { - $NativeHostArch = 'x86' - } - } - - Package-OpenSSH -NativeHostArch $NativeHostArch -Configuration $Configuration -DestinationPath $OpenSSHDir - - Push-Location $OpenSSHDir - & "$OpenSSHDir\install-sshd.ps1" - & "$OpenSSHDir\ssh-keygen.exe" -A - - $keyFiles = Get-ChildItem "$OpenSSHDir\ssh_host_*_key*" | % { - Adjust-HostKeyFileACL -FilePath $_.FullName - } - - #machine will be reboot after Install-openssh anyway - $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') - $newMachineEnvironmentPath = $machinePath - if (-not ($machinePath.ToLower().Contains($OpenSSHDir.ToLower()))) - { - $newMachineEnvironmentPath = "$OpenSSHDir;$newMachineEnvironmentPath" - $env:Path = "$OpenSSHDir;$env:Path" - } - # Update machine environment path - if ($newMachineEnvironmentPath -ne $machinePath) - { - [Environment]::SetEnvironmentVariable('Path', $newMachineEnvironmentPath, 'MACHINE') - } - - Set-Service sshd -StartupType Automatic - Set-Service ssh-agent -StartupType Automatic - - Pop-Location - Write-Log -Message "OpenSSH installed!" -} - -<# - .Synopsis - uninstalled sshd and sshla -#> -function UnInstall-OpenSSH -{ - [CmdletBinding()] - param - ( - [string]$OpenSSHDir = "$env:SystemDrive\OpenSSH" - ) - - if (-not (Test-Path $OpenSSHDir)) - { - return - } - - Push-Location $OpenSSHDir - if((Get-Service ssh-agent -ErrorAction Ignore) -ne $null) { - Stop-Service ssh-agent -Force - } - &( "$OpenSSHDir\uninstall-sshd.ps1") - - $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') - $newMachineEnvironmentPath = $machinePath - if ($machinePath.ToLower().Contains($OpenSSHDir.ToLower())) - { - $newMachineEnvironmentPath = $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '') - $env:Path = $env:Path.Replace("$OpenSSHDir;", '') - } - - if(Test-Path -Path $OpenSSHDir) - { - Push-Location $OpenSSHDir - &( "$OpenSSHDir\uninstall-sshd.ps1") - - $machinePath = [Environment]::GetEnvironmentVariable('Path', 'MACHINE') - $newMachineEnvironmentPath = $machinePath - if ($machinePath.ToLower().Contains($OpenSSHDir.ToLower())) - { - $newMachineEnvironmentPath.Replace("$OpenSSHDir;", '') - $env:Path = $env:Path.Replace("$OpenSSHDir;", '') - } - - # Update machine environment path - # machine will be reboot after Uninstall-OpenSSH - if ($newMachineEnvironmentPath -ne $machinePath) - { - [Environment]::SetEnvironmentVariable('Path', $newMachineEnvironmentPath, 'MACHINE') - } - Pop-Location - - Remove-Item -Path $OpenSSHDir -Recurse -Force -ErrorAction SilentlyContinue - } -} - -Export-ModuleMember -Function Build-OpenSSH, Get-BuildLogFile, Install-OpenSSH, UnInstall-OpenSSH, Package-OpenSSH +Export-ModuleMember -Function Start-OpenSSHBuild, Get-BuildLogFile, Start-OpenSSHPackage diff --git a/contrib/win32/openssh/OpenSSHCommonUtils.psm1 b/contrib/win32/openssh/OpenSSHCommonUtils.psm1 index d2d4f3f..3a58971 100644 --- a/contrib/win32/openssh/OpenSSHCommonUtils.psm1 +++ b/contrib/win32/openssh/OpenSSHCommonUtils.psm1 @@ -1,20 +1,18 @@ -<# +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\OpenSSHUtils -Force +<# .Synopsis Finds the root of the git repository .Outputs - A System.IO.DirectoryInfo for the location of the root. + A System.IO.DirectoryInfo for the location of the root if root is found; otherwise, script root. .Inputs None - -.Notes - FileNotFoundException is thrown if the current directory does not contain a CMakeLists.txt file. #> function Get-RepositoryRoot -{ - $currentDir = (Get-Item -Path $PSCommandPath).Directory - +{ + $start = $currentDir = (Get-Item -Path $PSScriptRoot) while ($null -ne $currentDir.Parent) { $path = Join-Path -Path $currentDir.FullName -ChildPath '.git' @@ -24,147 +22,7 @@ function Get-RepositoryRoot } $currentDir = $currentDir.Parent } - - throw new-object System.IO.DirectoryNotFoundException("Could not find the root of the GIT repository") -} - -<# -.Synopsis - Set owner of the file to by LOCALSYSTEM account - Set private host key be fully controlled by LOCALSYSTEM and Administrators - Set public host key be fully controlled by LOCALSYSTEM and Administrators, read access by everyone - -.Outputs - N/A - -.Inputs - FilePath - The path to the file -#> -function Adjust-HostKeyFileACL -{ - param ( - [parameter(Mandatory=$true)] - [string]$FilePath - ) - - $myACL = Get-ACL $FilePath - $myACL.SetAccessRuleProtection($True, $FALSE) - Set-Acl -Path $FilePath -AclObject $myACL - - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $everyoneAccount = New-Object System.Security.Principal.NTAccount("EveryOne") - $myACL = Get-ACL $FilePath - - $myACL.SetOwner($systemAccount) - - if($myACL.Access) - { - $myACL.Access | % { - if(-not ($myACL.RemoveAccessRule($_))) - { - throw "failed to remove access of $($_.IdentityReference.Value) rule in setup " - } - } - } - - $adminACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($adminAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($adminACE) - - $systemACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($systemAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($systemACE) - - if($FilePath.EndsWith(".pub")) - { - $everyoneAce = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ("Everyone", "Read", "None", "None", "Allow") - $myACL.AddAccessRule($everyoneAce) - } - - Set-Acl -Path $FilePath -AclObject $myACL -} - -<# -.Synopsis - Set owner of the user key file - Set ACL to have private user key be fully controlled by LOCALSYSTEM and Administrators, Read, write access by owner - Set public user key be fully controlled by LOCALSYSTEM and Administrators, Read, write access by owner, read access by everyone - -.Outputs - N/A - -.Inputs - FilePath - The path to the file - Owner - owner of the file - OwnerPerms - the permissions grant to the owner -#> -function Adjust-UserKeyFileACL -{ - param ( - [parameter(Mandatory=$true)] - [string]$FilePath, - [System.Security.Principal.NTAccount] $Owner = $null, - [System.Security.AccessControl.FileSystemRights[]] $OwnerPerms = $null - ) - - $myACL = Get-ACL $FilePath - $myACL.SetAccessRuleProtection($True, $FALSE) - Set-Acl -Path $FilePath -AclObject $myACL - - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $everyoneAccount = New-Object System.Security.Principal.NTAccount("EveryOne") - $myACL = Get-ACL $FilePath - - $actualOwner = $null - if($Owner -eq $null) - { - $actualOwner = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - } - else - { - $actualOwner = $Owner - } - - $myACL.SetOwner($actualOwner) - - if($myACL.Access) - { - $myACL.Access | % { - if(-not ($myACL.RemoveAccessRule($_))) - { - throw "failed to remove access of $($_.IdentityReference.Value) rule in setup " - } - } - } - - $adminACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($adminAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($adminACE) - - $systemACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($systemAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($systemACE) - - if($OwnerPerms) - { - $OwnerPerms | % { - $ownerACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($actualOwner, $_, "None", "None", "Allow") - $myACL.AddAccessRule($ownerACE) - } - } - - if($FilePath.EndsWith(".pub")) - { - $everyoneAce = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ("Everyone", "Read", "None", "None", "Allow") - $myACL.AddAccessRule($everyoneAce) - } - - Set-Acl -Path $FilePath -AclObject $myACL + return $start } <# @@ -177,9 +35,9 @@ function Adjust-UserKeyFileACL .Inputs FilePath - The path to the file User - account name - Perm - The permission to grant. + Perms - The permission to grant. #> -function Add-PermissionToFileACL +function Add-PermissionToFileACL { param ( [parameter(Mandatory=$true)] @@ -200,8 +58,8 @@ function Add-PermissionToFileACL $myACL.AddAccessRule($userACE) } } - + Enable-Privilege SeRestorePrivilege | out-null Set-Acl -Path $FilePath -AclObject $myACL } -Export-ModuleMember -Function Get-RepositoryRoot, Add-PermissionToFileACL, Adjust-HostKeyFileACL, Adjust-UserKeyFileACL \ No newline at end of file +Export-ModuleMember -Function Get-RepositoryRoot, Add-PermissionToFileACL \ No newline at end of file diff --git a/contrib/win32/openssh/OpenSSHTestHelper.psm1 b/contrib/win32/openssh/OpenSSHTestHelper.psm1 index 4001203..bd1a341 100644 --- a/contrib/win32/openssh/OpenSSHTestHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHTestHelper.psm1 @@ -1,5 +1,7 @@ $ErrorActionPreference = 'Stop' -Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -DisableNameChecking -Force +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\OpenSSHCommonUtils.psm1 -Force +Import-Module $PSScriptRoot\OpenSSHUtils -Force [System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot # test environment parameters initialized with defaults @@ -17,23 +19,28 @@ $Script:E2ETestResultsFile = Join-Path $TestDataPath $E2ETestResultsFileName $Script:UnitTestResultsFile = Join-Path $TestDataPath $UnitTestResultsFileName $Script:TestSetupLogFile = Join-Path $TestDataPath $TestSetupLogFileName $Script:E2ETestDirectory = Join-Path $repositoryRoot.FullName -ChildPath "regress\pesterTests" - +$Script:WindowsInBox = $false + <# .Synopsis - Setup-OpenSSHTestEnvironment + Set-OpenSSHTestEnvironment TODO - split these steps into client and server side #> -function Setup-OpenSSHTestEnvironment +function Set-OpenSSHTestEnvironment { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param - ( - [switch] $Quiet, + ( [string] $OpenSSHBinPath, [string] $TestDataPath = "$env:SystemDrive\OpenSSHTests", [Boolean] $DebugMode = $false ) + if($PSBoundParameters.ContainsKey("Verbose")) + { + $verboseInfo = ($PSBoundParameters['Verbose']).IsPresent + } + if($Global:OpenSSHTestInfo -ne $null) { $Global:OpenSSHTestInfo.Clear() @@ -65,41 +72,30 @@ function Setup-OpenSSHTestEnvironment #if user does not set path, pick it up if([string]::IsNullOrEmpty($OpenSSHBinPath)) { - $sshcmd = get-command ssh.exe -ErrorAction Ignore + $sshcmd = get-command ssh.exe -ErrorAction SilentlyContinue if($sshcmd -eq $null) { Throw "Cannot find ssh.exe. Please specify -OpenSSHBinPath to the OpenSSH installed location." } - elseif($Quiet) - { - $dirToCheck = split-path $sshcmd.Path - $script:OpenSSHBinPath = $dirToCheck - } else { $dirToCheck = split-path $sshcmd.Path - $message = "Do you want to test openssh installed at $($dirToCheck)? [Yes] Y; [No] N (default is `"Y`")" - $response = Read-Host -Prompt $message - if( ($response -eq "") -or ($response -ieq "Y") -or ($response -ieq "Yes") ) - { - $script:OpenSSHBinPath = $dirToCheck - } - elseif( ($response -ieq "N") -or ($response -ieq "No") ) + $description = "Pick up ssh.exe from $dirToCheck." + $prompt = "Are you sure you want to pick up ssh.exe from $($dirToCheck)?" + $caption = "Found ssh.exe from $dirToCheck" + if(-not $pscmdlet.ShouldProcess($description, $prompt, $caption)) { Write-Host "User decided not to pick up ssh.exe from $dirToCheck. Please specify -OpenSSHBinPath to the OpenSSH installed location." return } - else - { - Throw "User entered invalid option ($response). Please specify -OpenSSHBinPath to the OpenSSH installed location" - } + $script:OpenSSHBinPath = $dirToCheck } } else { if (-not (Test-Path (Join-Path $OpenSSHBinPath ssh.exe) -PathType Leaf)) { - Throw "Cannot find OpenSSH binaries under $OpenSSHBinPath. Please specify -OpenSSHBinPathto the OpenSSH installed location" + Throw "Cannot find OpenSSH binaries under $OpenSSHBinPath. Please specify -OpenSSHBinPath to the OpenSSH installed location" } else { @@ -113,7 +109,15 @@ function Setup-OpenSSHTestEnvironment $env:Path = "$($script:OpenSSHBinPath);$($env:path)" } - $warning = @" + $acl = get-acl (join-path $script:OpenSSHBinPath "ssh.exe") + + if($acl.Owner -ieq "NT SERVICE\TrustedInstaller") + { + $Script:WindowsInBox = $true + $Global:OpenSSHTestInfo.Add("WindowsInBox", $true) + } + + $description = @" WARNING: Following changes will be made to OpenSSH configuration - sshd_config will be backed up as sshd_config.ori - will be replaced with a test sshd_config @@ -125,21 +129,15 @@ WARNING: Following changes will be made to OpenSSH configuration - $HOME\.ssh\known_hosts will be modified with test host key entry - test accounts - ssouser, pubkeyuser, and passwduser will be added - Setup single signon for ssouser - - To cleanup - Run Cleanup-OpenSSHTestEnvironment -"@ - - if (-not $Quiet) { - Write-Warning $warning - $continue = Read-Host -Prompt "Do you want to continue with the above changes? [Yes] Y; [No] N (default is `"Y`")" - if( ($continue -ieq "N") -or ($continue -ieq "No") ) - { - Write-Host "User decided not to make the changes." - return - } - elseif(($continue -ne "") -and ($continue -ine "Y") -and ($continue -ine "Yes")) - { - Throw "User entered invalid option ($continue). Exit now." - } + - 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)) + { + Write-Host "User decided not to make the changes." + return } Install-OpenSSHTestDependencies @@ -154,9 +152,15 @@ WARNING: Following changes will be made to OpenSSH configuration if (-not (Test-Path $backupConfigPath -PathType Leaf)) { Copy-Item (Join-Path $script:OpenSSHBinPath sshd_config) $backupConfigPath -Force } - + $targetsshdConfig = Join-Path $script:OpenSSHBinPath sshd_config # copy new sshd_config - Copy-Item (Join-Path $Script:E2ETestDirectory sshd_config) (Join-Path $script:OpenSSHBinPath sshd_config) -Force + if($Script:WindowsInBox -and (Test-Path $targetsshdConfig)) + { + $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) + Add-PermissionToFileACL -FilePath $targetsshdConfig -User $currentUser -Perm "Read,Write" + } + + Copy-Item (Join-Path $Script:E2ETestDirectory sshd_config) $targetsshdConfig -Force Start-Service ssh-agent @@ -164,12 +168,18 @@ WARNING: Following changes will be made to OpenSSH configuration Copy-Item "$($Script:E2ETestDirectory)\sshtest*hostkey*" $script:OpenSSHBinPath -Force Get-ChildItem "$($script:OpenSSHBinPath)\sshtest*hostkey*"| % { #workaround for the cariggage new line added by git before copy them - (Get-Content $_.FullName -Raw).Replace("`r`n","`n") | Set-Content $_.FullName -Force - Adjust-HostKeyFileACL -FilePath $_.FullName - if (-not ($_.Name.EndsWith(".pub"))) { - #register private key with agent - ssh-add-hostkey.ps1 $_.FullName - } + $filePath = "$($_.FullName)" + $con = (Get-Content $filePath | Out-String).Replace("`r`n","`n") + Set-Content -Path $filePath -Value "$con" + if (-not ($_.Name.EndsWith(".pub"))) + { + Repair-SshdHostKeyPermission -FilePath $_.FullName -confirm:$false + if($psversiontable.BuildVersion.Major -gt 6) + { + #register private key with agent + ssh-add-hostkey.ps1 $_.FullName + } + } } Restart-Service sshd -Force @@ -192,7 +202,7 @@ WARNING: Following changes will be made to OpenSSH configuration Copy-Item $sshConfigFilePath (Join-Path $dotSshDirectoryPath config.ori) -Force } Copy-Item (Join-Path $Script:E2ETestDirectory ssh_config) $sshConfigFilePath -Force - Adjust-UserKeyFileACL -FilePath $sshConfigFilePath -OwnerPerms "Read,Write" + Repair-UserSshConfigPermission -FilePath $sshConfigFilePath -confirm:$false # create test accounts #TODO - this is Windows specific. Need to be in PAL @@ -210,21 +220,22 @@ WARNING: Following changes will be made to OpenSSH configuration } } - #setup single sign on for ssouser + #setup single sign on for ssouser $ssouserProfile = Get-LocalUserProfile -User $SSOUser $Global:OpenSSHTestInfo.Add("SSOUserProfile", $ssouserProfile) - $Global:OpenSSHTestInfo.Add("PubKeyUserProfile", (Get-LocalUserProfile -User $PubKeyUser)) + $Global:OpenSSHTestInfo.Add("PubKeyUserProfile", (Get-LocalUserProfile -User $PubKeyUser)) New-Item -ItemType Directory -Path (Join-Path $ssouserProfile .ssh) -Force -ErrorAction SilentlyContinue | out-null $authorizedKeyPath = Join-Path $ssouserProfile .ssh\authorized_keys - $testPubKeyPath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519.pub + $testPubKeyPath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519.pub Copy-Item $testPubKeyPath $authorizedKeyPath -Force -ErrorAction SilentlyContinue - $owner = New-Object System.Security.Principal.NTAccount($SSOUser) - Adjust-UserKeyFileACL -FilePath $authorizedKeyPath -Owner $owner -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $authorizedKeyPath -User "NT Service\sshd" -Perm "Read" + Repair-AuthorizedKeyPermission -FilePath $authorizedKeyPath -confirm:$false + $testPriKeypath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519 - (Get-Content $testPriKeypath -Raw).Replace("`r`n","`n") | Set-Content $testPriKeypath -Force - Adjust-UserKeyFileACL -FilePath $testPriKeypath -OwnerPerms "Read, Write" + $con = (Get-Content $testPriKeypath | Out-String).Replace("`r`n","`n") + Set-Content -Path $testPriKeypath -Value "$con" + cmd /c "ssh-add -D 2>&1 >> $Script:TestSetupLogFile" + Repair-UserKeyPermission -FilePath $testPriKeypath -confirm:$false cmd /c "ssh-add $testPriKeypath 2>&1 >> $Script:TestSetupLogFile" Backup-OpenSSHTestInfo } @@ -256,6 +267,19 @@ function Install-OpenSSHTestDependencies { [CmdletBinding()] param () + + #$isOpenSSHUtilsAvailable = Get-Module 'OpenSSHUtils' -ListAvailable + #if (-not ($isOpenSSHUtilsAvailable)) + #{ + Write-Log -Message "Installing Module OpenSSHUtils..." + Install-OpenSSHUtilsModule -SourceDir $PSScriptRoot + #} + Import-Module OpensshUtils -Force + + if($Script:WindowsInBox) + { + return + } # Install chocolatey if(-not (Get-Command "choco" -ErrorAction SilentlyContinue)) @@ -267,10 +291,66 @@ function Install-OpenSSHTestDependencies $isModuleAvailable = Get-Module 'Pester' -ListAvailable if (-not ($isModuleAvailable)) { - Write-Log -Message "Installing Pester..." - choco install Pester -y --force --limitoutput 2>&1 >> $Script:TestSetupLogFile + Write-Log -Message "Installing Pester..." + choco install Pester -y --force --limitoutput 2>&1 >> $Script:TestSetupLogFile } } + +function Install-OpenSSHUtilsModule +{ + [CmdletBinding()] + param( + [string]$TargetDir = (Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\OpenSSHUtils"), + [string]$SourceDir) + + $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)) + { + New-Item -ItemType Directory -Path $targetDirectory -Force -ErrorAction SilentlyContinue | out-null + } + 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 ';' + if ($modulePaths -notcontains $modulePath) + { + Write-Verbose -Message "Adding '$modulePath' to PSModulePath." + + $modulePaths = @( + $modulePath + $modulePaths + ) + + $newModulePath = $modulePaths -join ';' + + [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine') + $env:PSModulePath += ";$modulePath" + } + } +} + +function Uninstall-OpenSSHUtilsModule +{ + [CmdletBinding()] + 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 + } +} + <# .Synopsis Get-UserSID @@ -296,12 +376,12 @@ function Get-UserSID <# .Synopsis - Cleanup-OpenSSHTestEnvironment + Clear-OpenSSHTestEnvironment #> -function Cleanup-OpenSSHTestEnvironment +function Clear-OpenSSHTestEnvironment { if($Global:OpenSSHTestInfo -eq $null) { - throw "OpenSSHTestInfo is not set. Did you run Setup-OpenSShTestEnvironment?" + throw "OpenSSHTestInfo is not set. Did you run Set-OpenSShTestEnvironment?" } $sshBinPath = $Global:OpenSSHTestInfo["OpenSSHBinPath"] @@ -356,6 +436,13 @@ function Cleanup-OpenSSHTestEnvironment $Global:OpenSSHTestInfo.Clear() $Global:OpenSSHTestInfo = $null } + + $isOpenSSHUtilsAvailable = Get-Module 'OpenSSHUtils' -ListAvailable + if ($isOpenSSHUtilsAvailable) + { + Write-Log -Message "Uninstalling Module OpenSSHUtils..." + Uninstall-OpenSSHUtilsModule + } } <# @@ -418,12 +505,13 @@ function Get-UnitTestDirectory .Synopsis Run OpenSSH pester tests. #> -function Run-OpenSSHE2ETest +function Invoke-OpenSSHE2ETest { - # Discover all CI tests and run them. + # Discover all CI tests and run them. + Import-Module pester -force -global Push-Location $Script:E2ETestDirectory Write-Log -Message "Running OpenSSH E2E tests..." - $testFolders = Get-ChildItem *.tests.ps1 -Recurse | ForEach-Object{ Split-Path $_.FullName} | Sort-Object -Unique + $testFolders = @(Get-ChildItem *.tests.ps1 -Recurse | ForEach-Object{ Split-Path $_.FullName} | Sort-Object -Unique) Invoke-Pester $testFolders -OutputFormat NUnitXml -OutputFile $Script:E2ETestResultsFile -Tag 'CI' Pop-Location } @@ -432,35 +520,45 @@ function Run-OpenSSHE2ETest .Synopsis Run openssh unit tests. #> -function Run-OpenSSHUnitTest +function Invoke-OpenSSHUnitTest { # Discover all CI tests and run them. + if([string]::Isnullorempty($Script:UnitTestDirectory)) + { + $Script:UnitTestDirectory = $OpenSSHTestInfo["UnitTestDirectory"] + } Push-Location $Script:UnitTestDirectory Write-Log -Message "Running OpenSSH unit tests..." - if (Test-Path $Script:UnitTestResultsFile) + if (Test-Path $Script:UnitTestResultsFile) { $null = Remove-Item -Path $Script:UnitTestResultsFile -Force -ErrorAction SilentlyContinue } - $testFolders = Get-ChildItem unittest-*.exe -Recurse -Exclude unittest-sshkey.exe,unittest-kex.exe | + $testFolders = Get-ChildItem -filter unittest-*.exe -Recurse -Exclude unittest-sshkey.exe,unittest-kex.exe | ForEach-Object{ Split-Path $_.FullName} | Sort-Object -Unique $testfailed = $false if ($testFolders -ne $null) { - $testFolders | % { - Push-Location $_ + $testFolders | % { $unittestFile = "$(Split-Path $_ -Leaf).exe" - Write-log "Running OpenSSH unit $unittestFile ..." - & .\$unittestFile >> $Script:UnitTestResultsFile + $unittestFilePath = join-path $_ $unittestFile + $Error.clear() + $LASTEXITCODE=0 + if(Test-Path $unittestFilePath -pathtype leaf) + { + Push-Location $_ + Write-Log "Running OpenSSH unit $unittestFile ..." + & "$unittestFilePath" >> $Script:UnitTestResultsFile + Pop-Location + } $errorCode = $LASTEXITCODE if ($errorCode -ne 0) { $testfailed = $true - $errorMessage = "$($_.FullName) test failed for OpenSSH.`nExitCode: $errorCode. Detail test log is at $($Script:UnitTestResultsFile)." + $errorMessage = "$_ test failed for OpenSSH.`nExitCode: $errorCode. Detail test log is at $($Script:UnitTestResultsFile)." Write-Warning $errorMessage - } - Pop-Location + } } } Pop-Location @@ -475,7 +573,7 @@ function Backup-OpenSSHTestInfo ) if ($Global:OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Did you run Setup-OpenSSHTestEnvironment yet?" + Throw "`$OpenSSHTestInfo is null. Did you run Set-OpenSSHTestEnvironment yet?" } $testInfo = $Global:OpenSSHTestInfo @@ -492,7 +590,7 @@ function Backup-OpenSSHTestInfo } } -function Recover-OpenSSHTestInfo +function Restore-OpenSSHTestInfo { param ( @@ -538,4 +636,4 @@ function Write-Log } } -Export-ModuleMember -Function Setup-OpenSSHTestEnvironment, Cleanup-OpenSSHTestEnvironment, Run-OpenSSHUnitTest, Run-OpenSSHE2ETest, Backup-OpenSSHTestInfo, Recover-OpenSSHTestInfo +Export-ModuleMember -Function Set-OpenSSHTestEnvironment, Clear-OpenSSHTestEnvironment, Invoke-OpenSSHUnitTest, Invoke-OpenSSHE2ETest, Backup-OpenSSHTestInfo, Restore-OpenSSHTestInfo diff --git a/contrib/win32/openssh/OpenSSHUtils.psm1 b/contrib/win32/openssh/OpenSSHUtils.psm1 index 8654fa5..b32f096 100644 --- a/contrib/win32/openssh/OpenSSHUtils.psm1 +++ b/contrib/win32/openssh/OpenSSHUtils.psm1 @@ -1,9 +1,49 @@ Set-StrictMode -Version 2.0 -$systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") -$adminsAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") -$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) -$everyone = New-Object System.Security.Principal.NTAccount("EveryOne") -$sshdAccount = New-Object System.Security.Principal.NTAccount("NT SERVICE","sshd") + +<# + .Synopsis + Get-UserSID +#> +function Get-UserSID +{ + [CmdletBinding(DefaultParameterSetName='User')] + param + ( [parameter(Mandatory=$true, ParameterSetName="User")] + [ValidateNotNull()] + [System.Security.Principal.NTAccount]$User, + [parameter(Mandatory=$true, ParameterSetName="WellKnownSidType")] + [ValidateNotNull()] + [System.Security.Principal.WellKnownSidType]$WellKnownSidType + ) + try + { + if($PSBoundParameters.ContainsKey("User")) + { + $sid = $User.Translate([System.Security.Principal.SecurityIdentifier]) + } + elseif($PSBoundParameters.ContainsKey("WellKnownSidType")) + { + $sid = New-Object System.Security.Principal.SecurityIdentifier($WellKnownSidType, $null) + } + $sid + } + catch { + return $null + } +} + +# get the local System user +$systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + +# get the Administrators group +$adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + +# get the everyone +$everyoneSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::WorldSid) + +$sshdSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-80-3847866527-469524349-687026318-516638107-1125189541") + +$currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" #Taken from P/Invoke.NET with minor adjustments. $definition = @' @@ -72,7 +112,7 @@ function Repair-SshdConfigPermission [ValidateNotNullOrEmpty()] [string]$FilePath) - Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters + Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessNeeded $sshdSid @psBoundParameters } <# @@ -86,18 +126,18 @@ function Repair-SshdHostKeyPermission [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [string]$FilePath) + [ValidateNotNullOrEmpty()] + [string]$FilePath) if($PSBoundParameters["FilePath"].EndsWith(".pub")) { $PSBoundParameters["FilePath"] = $PSBoundParameters["FilePath"].Replace(".pub", "") } - Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters + Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessNeeded $sshdSid @psBoundParameters - $PSBoundParameters["FilePath"] += ".pub" - Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessOK $everyone -ReadAccessNeeded $sshdAccount @psBoundParameters + $PSBoundParameters["FilePath"] += ".pub" + Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessOK $everyoneSid -ReadAccessNeeded $sshdSid @psBoundParameters } <# @@ -127,20 +167,14 @@ function Repair-AuthorizedKeyPermission { $userProfilePath = $properties.ProfileImagePath } - $fullPath -ieq "$userProfilePath\.ssh\authorized_keys" + $userProfilePath = $userProfilePath.Replace("\", "\\") + $fullPath -match "^$userProfilePath[\\|\W|\w]+authorized_keys$" } if($profileItem) { - $userSid = $profileItem.PSChildName - $account = Get-UserAccount -UserSid $userSid - if($account) - { - Repair-FilePermission -Owners $account,$adminsAccount,$systemAccount -AnyAccessOK $account -ReadAccessNeeded $sshdAccount @psBoundParameters - } - else - { - Write-host "Can't translate $userSid to an account. skip checking $fullPath..." -ForegroundColor Yellow - } + $userSid = $profileItem.PSChildName + Repair-FilePermission -Owners $userSid,$adminsSid,$systemSid -AnyAccessOK $userSid -ReadAccessNeeded $sshdSid @psBoundParameters + } else { @@ -162,16 +196,16 @@ function Repair-UserKeyPermission [parameter(Mandatory=$true, Position = 0)] [ValidateNotNullOrEmpty()] [string]$FilePath, - [System.Security.Principal.NTAccount] $User = $currentUser) + [System.Security.Principal.SecurityIdentifier] $UserSid = $currentUserSid) if($PSBoundParameters["FilePath"].EndsWith(".pub")) { $PSBoundParameters["FilePath"] = $PSBoundParameters["FilePath"].Replace(".pub", "") } - Repair-FilePermission -Owners $User, $adminsAccount,$systemAccount -AnyAccessOK $User @psBoundParameters + Repair-FilePermission -Owners $UserSid, $adminsSid,$systemSid -AnyAccessOK $UserSid @psBoundParameters $PSBoundParameters["FilePath"] += ".pub" - Repair-FilePermission -Owners $User, $adminsAccount,$systemAccount -AnyAccessOK $User -ReadAccessOK $everyone @psBoundParameters + Repair-FilePermission -Owners $UserSid, $adminsSid,$systemSid -AnyAccessOK $UserSid -ReadAccessOK $everyoneSid @psBoundParameters } <# @@ -185,8 +219,9 @@ function Repair-UserSshConfigPermission param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] - [string]$FilePath) - Repair-FilePermission -Owners $currentUser,$adminsAccount,$systemAccount -AnyAccessOK $currentUser @psBoundParameters + [string]$FilePath, + [System.Security.Principal.SecurityIdentifier] $UserSid = $currentUserSid) + Repair-FilePermission -Owners $UserSid,$adminsSid,$systemSid -AnyAccessOK $UserSid @psBoundParameters } <# @@ -202,10 +237,11 @@ function Repair-FilePermission [ValidateNotNullOrEmpty()] [string]$FilePath, [ValidateNotNull()] - [System.Security.Principal.NTAccount[]] $Owners = $currentUser, - [System.Security.Principal.NTAccount[]] $AnyAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessNeeded + [System.Security.Principal.SecurityIdentifier[]] $Owners = $currentUserSid, + [System.Security.Principal.SecurityIdentifier[]] $AnyAccessOK = $null, + [System.Security.Principal.SecurityIdentifier[]] $FullAccessNeeded = $null, + [System.Security.Principal.SecurityIdentifier[]] $ReadAccessOK = $null, + [System.Security.Principal.SecurityIdentifier[]] $ReadAccessNeeded = $null ) if(-not (Test-Path $FilePath -PathType Leaf)) @@ -235,10 +271,11 @@ function Repair-FilePermissionInternal { [ValidateNotNullOrEmpty()] [string]$FilePath, [ValidateNotNull()] - [System.Security.Principal.NTAccount[]] $Owners = $currentUser, - [System.Security.Principal.NTAccount[]] $AnyAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessNeeded + [System.Security.Principal.SecurityIdentifier[]] $Owners = $currentUserSid, + [System.Security.Principal.SecurityIdentifier[]] $AnyAccessOK = $null, + [System.Security.Principal.SecurityIdentifier[]] $FullAccessNeeded = $null, + [System.Security.Principal.SecurityIdentifier[]] $ReadAccessOK = $null, + [System.Security.Principal.SecurityIdentifier[]] $ReadAccessNeeded = $null ) $acl = Get-Acl $FilePath @@ -246,26 +283,19 @@ function Repair-FilePermissionInternal { $health = $true $paras = @{} $PSBoundParameters.GetEnumerator() | % { if((-not $_.key.Contains("Owners")) -and (-not $_.key.Contains("Access"))) { $paras.Add($_.key,$_.Value) } } - - $validOwner = $owners | ? { $_.equals([System.Security.Principal.NTAccount]$acl.owner)} - if($validOwner -eq $null) - { - $caption = "Current owner: '$($acl.Owner)'. '$($Owners[0])' should own '$FilePath'." + + $currentOwnerSid = Get-UserSid -User $acl.owner + if($owners -notcontains $currentOwnerSid) + { + $newOwner = Get-UserAccount -User $Owners[0] + $caption = "Current owner: '$($acl.Owner)'. '$newOwner' should own '$FilePath'." $prompt = "Shall I set the file owner?" - $description = "Set '$($Owners[0])' as owner of '$FilePath'." + $description = "Set '$newOwner' as owner of '$FilePath'." if($pscmdlet.ShouldProcess($description, $prompt, $caption)) - { + { Enable-Privilege SeRestorePrivilege | out-null - $acl.SetOwner($Owners[0]) - Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false - if($e) - { - Write-Warning "Set owner failed with error: $($e[0].ToString())." - } - else - { - Write-Host "'$($Owners[0])' now owns '$FilePath'. " -ForegroundColor Green - } + $acl.SetOwner($newOwner) + Set-Acl -Path $FilePath -AclObject $acl -Confirm:$false } else { @@ -279,56 +309,126 @@ function Repair-FilePermissionInternal { $ReadAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Read.value__) -bor ` ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + $FullControlPerm = [System.UInt32] [System.Security.AccessControl.FileSystemRights]::FullControl.value__ #system and admin groups can have any access to the file; plus the account in the AnyAccessOK list - $realAnyAccessOKList = $AnyAccessOK + @($systemAccount, $adminsAccount) - - #if accounts in the ReadAccessNeeded already part of dacl, they are okay; need to make sure they have read access only - $realReadAcessOKList = $ReadAccessOK + $ReadAccessNeeded - - #this is orginal list requested by the user, the account will be removed from the list if they already part of the dacl + $realAnyAccessOKList = @($systemSid, $adminsSid) + if($AnyAccessOK) + { + $realAnyAccessOKList += $AnyAccessOK + } + + $realFullAccessNeeded = $FullAccessNeeded $realReadAccessNeeded = $ReadAccessNeeded - - #'APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES'- can't translate fully qualified name. it is a win32 API bug. - #'ALL APPLICATION PACKAGES' exists only on Win2k12 and Win2k16 and 'ALL RESTRICTED APPLICATION PACKAGES' exists only in Win2k16 - $specialIdRefs = "ALL APPLICATION PACKAGES","ALL RESTRICTED APPLICATION PACKAGES" + if($realFullAccessNeeded -contains $everyoneSid) + { + $realFullAccessNeeded = @($everyoneSid) + $realReadAccessNeeded = $null + } + + if($realReadAccessNeeded -contains $everyoneSid) + { + $realReadAccessNeeded = @($everyoneSid) + } + #this is orginal list requested by the user, the account will be removed from the list if they already part of the dacl + if($realReadAccessNeeded) + { + $realReadAccessNeeded = $realReadAccessNeeded | ? { ($_ -ne $null) -and ($realFullAccessNeeded -notcontains $_) } + } + + #if accounts in the ReadAccessNeeded or $realFullAccessNeeded already part of dacl, they are okay; + #need to make sure they have read access only + $realReadAcessOKList = $ReadAccessOK + $realReadAccessNeeded foreach($a in $acl.Access) { - if($realAnyAccessOKList -and (($realAnyAccessOKList | ? { $_.equals($a.IdentityReference)}) -ne $null)) + $IdentityReferenceSid = Get-UserSid -User $a.IdentityReference + if($IdentityReferenceSid -eq $null) + { + $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] + $IdentityReferenceSid = Get-UserSID -User $idRefShortValue + if($IdentityReferenceSid -eq $null) + { + Write-Warning "Can't translate '$idRefShortValue'. " + continue + } + } + + if($realFullAccessNeeded -contains ($IdentityReferenceSid)) + { + $realFullAccessNeeded = $realFullAccessNeeded | ? { ($_ -ne $null) -and (-not $_.Equals($IdentityReferenceSid)) } + if($realReadAccessNeeded) + { + $realReadAccessNeeded = $realReadAccessNeeded | ? { ($_ -ne $null) -and (-not $_.Equals($IdentityReferenceSid)) } + } + if (($a.AccessControlType.Equals([System.Security.AccessControl.AccessControlType]::Allow)) -and ` + ((([System.UInt32]$a.FileSystemRights.value__) -band $FullControlPerm) -eq $FullControlPerm)) + { + continue; + } + #update the account to full control + if($a.IsInherited) + { + if($needChange) + { + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $acl -Confirm:$false + } + + return Remove-RuleProtection @paras + } + $caption = "'$($a.IdentityReference)' has the following access to '$FilePath': '$($a.AccessControlType)'-'$($a.FileSystemRights)'." + $prompt = "Shall I make it Allow FullControl?" + $description = "Grant '$($a.IdentityReference)' FullControl access to '$FilePath'. " + + if($pscmdlet.ShouldProcess($description, $prompt, $caption)) + { + $needChange = $true + $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` + ($IdentityReferenceSid, "FullControl", "None", "None", "Allow") + + $acl.SetAccessRule($ace) + Write-Host "'$($a.IdentityReference)' now has FullControl access to '$FilePath'. " -ForegroundColor Green + } + else + { + $health = $false + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "'$($a.IdentityReference)' still has these access to '$FilePath': '$($a.AccessControlType)'-'$($a.FileSystemRights)'." -ForegroundColor Yellow + } + } + } + elseif(($realAnyAccessOKList -contains $everyoneSid) -or ($realAnyAccessOKList -contains $IdentityReferenceSid)) { #ignore those accounts listed in the AnyAccessOK list. + continue; } #If everyone is in the ReadAccessOK list, any user can have read access; # below block make sure they are granted Read access only - elseif($realReadAcessOKList -and ((($realReadAcessOKList | ? { $_.Equals($everyone)}) -ne $null) -or ` - (($realReadAcessOKList | ? { $_.equals($a.IdentityReference)}) -ne $null))) + elseif(($realReadAcessOKList -contains $everyoneSid) -or ($realReadAcessOKList -contains $IdentityReferenceSid)) { - if($realReadAccessNeeded -and ($a.IdentityReference.Equals($everyone))) + if($realReadAccessNeeded -and ($IdentityReferenceSid.Equals($everyoneSid))) { - $realReadAccessNeeded=@() + $realReadAccessNeeded= $null } elseif($realReadAccessNeeded) { - $realReadAccessNeeded = $realReadAccessNeeded | ? { -not $_.Equals($a.IdentityReference) } + $realReadAccessNeeded = $realReadAccessNeeded | ? { ($_ -ne $null ) -and (-not $_.Equals($IdentityReferenceSid)) } } if (-not ($a.AccessControlType.Equals([System.Security.AccessControl.AccessControlType]::Allow)) -or ` (-not (([System.UInt32]$a.FileSystemRights.value__) -band (-bnot $ReadAccessPerm)))) { continue; - } + } if($a.IsInherited) { if($needChange) { Enable-Privilege SeRestorePrivilege | out-null - Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false - if($e) - { - Write-Warning "Repair permission failed with error: $($e[0].ToString())." - } + Set-Acl -Path $FilePath -AclObject $acl -Confirm:$false } return Remove-RuleProtection @paras @@ -340,28 +440,11 @@ function Repair-FilePermissionInternal { if($pscmdlet.ShouldProcess($description, $prompt, $caption)) { $needChange = $true - $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] - if ($specialIdRefs -icontains $idRefShortValue ) - { - $ruleIdentity = Get-UserSID -User (New-Object Security.Principal.NTAccount $idRefShortValue) - if($ruleIdentity) - { - $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($ruleIdentity, "Read", "None", "None", "Allow") - } - else - { - Write-Warning "Can't translate '$idRefShortValue'. " - continue - } - } - else - { - $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($a.IdentityReference, "Read", "None", "None", "Allow") - } + $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` + ($IdentityReferenceSid, "Read", "None", "None", "Allow") + $acl.SetAccessRule($ace) - Write-Host "'$($a.IdentityReference)' now has Read access to '$FilePath'. " -ForegroundColor Green + Write-Host "'$($a.IdentityReference)' now has Read access to '$FilePath'. " -ForegroundColor Green } else { @@ -381,11 +464,7 @@ function Repair-FilePermissionInternal { if($needChange) { Enable-Privilege SeRestorePrivilege | out-null - Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false - if($e) - { - Write-Warning "Repair permission failed with error: $($e[0].ToString())." - } + Set-Acl -Path $FilePath -AclObject $acl -Confirm:$false } return Remove-RuleProtection @paras } @@ -395,23 +474,9 @@ function Repair-FilePermissionInternal { if($pscmdlet.ShouldProcess($description, $prompt, "$caption.")) { - $needChange = $true - $ace = $a - $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] - if ($specialIdRefs -icontains $idRefShortValue) - { - $ruleIdentity = Get-UserSID -User (New-Object Security.Principal.NTAccount $idRefShortValue) - if($ruleIdentity) - { - $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($ruleIdentity, $a.FileSystemRights, $a.InheritanceFlags, $a.PropagationFlags, $a.AccessControlType) - } - else - { - Write-Warning "Can't translate '$idRefShortValue'. " - continue - } - } + $needChange = $true + $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` + ($IdentityReferenceSid, $a.FileSystemRights, $a.InheritanceFlags, $a.PropagationFlags, $a.AccessControlType) if(-not ($acl.RemoveAccessRule($ace))) { @@ -432,20 +497,55 @@ function Repair-FilePermissionInternal { } } } + + if($realFullAccessNeeded) + { + $realFullAccessNeeded | % { + $account = Get-UserAccount -UserSid $_ + if($account -eq $null) + { + Write-Warning "'$_' needs FullControl access to '$FilePath', but it can't be translated on the machine." + } + else + { + $caption = "'$account' needs FullControl access to '$FilePath'." + $prompt = "Shall I make the above change?" + $description = "Set '$account' FullControl access to '$FilePath'. " + + if($pscmdlet.ShouldProcess($description, $prompt, $caption)) + { + $needChange = $true + $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` + ($_, "FullControl", "None", "None", "Allow") + $acl.AddAccessRule($ace) + Write-Host "'$account' now has FullControl to '$FilePath'." -ForegroundColor Green + } + else + { + $health = $false + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "'$account' does not have FullControl to '$FilePath'." -ForegroundColor Yellow + } + } + } + } + } #This is the real account list we need to add read access to the file if($realReadAccessNeeded) { $realReadAccessNeeded | % { - if((Get-UserSID -User $_) -eq $null) + $account = Get-UserAccount -UserSid $_ + if($account -eq $null) { Write-Warning "'$_' needs Read access to '$FilePath', but it can't be translated on the machine." } else { - $caption = "'$_' needs Read access to '$FilePath'." + $caption = "'$account' needs Read access to '$FilePath'." $prompt = "Shall I make the above change?" - $description = "Set '$_' Read only access to '$FilePath'. " + $description = "Set '$account' Read only access to '$FilePath'. " if($pscmdlet.ShouldProcess($description, $prompt, $caption)) { @@ -453,14 +553,14 @@ function Repair-FilePermissionInternal { $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($_, "Read", "None", "None", "Allow") $acl.AddAccessRule($ace) - Write-Host "'$_' now has Read access to '$FilePath'." -ForegroundColor Green + Write-Host "'$account' now has Read access to '$FilePath'." -ForegroundColor Green } else { $health = $false if(-not $PSBoundParameters.ContainsKey("WhatIf")) { - Write-Host "'$_' does not have Read access to '$FilePath'." -ForegroundColor Yellow + Write-Host "'$account' does not have Read access to '$FilePath'." -ForegroundColor Yellow } } } @@ -470,11 +570,7 @@ function Repair-FilePermissionInternal { if($needChange) { Enable-Privilege SeRestorePrivilege | out-null - Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false - if($e) - { - Write-Warning "Repair permission failed with error: $($e[0].ToString())." - } + Set-Acl -Path $FilePath -AclObject $acl -Confirm:$false } if($health) { @@ -525,41 +621,44 @@ function Remove-RuleProtection return $false } } + <# .Synopsis Get-UserAccount #> function Get-UserAccount { + [CmdletBinding(DefaultParameterSetName='Sid')] param - ( [parameter(Mandatory=$true)] - [string]$UserSid + ( [parameter(Mandatory=$true, ParameterSetName="Sid")] + [ValidateNotNull()] + [System.Security.Principal.SecurityIdentifier]$UserSid, + [parameter(Mandatory=$true, ParameterSetName="WellKnownSidType")] + [ValidateNotNull()] + [System.Security.Principal.WellKnownSidType]$WellKnownSidType ) try { - $objSID = New-Object System.Security.Principal.SecurityIdentifier($UserSid) - $objUser = $objSID.Translate( [System.Security.Principal.NTAccount]) + if($PSBoundParameters.ContainsKey("UserSid")) + { + $objUser = $UserSid.Translate([System.Security.Principal.NTAccount]) + } + elseif($PSBoundParameters.ContainsKey("WellKnownSidType")) + { + $objSID = New-Object System.Security.Principal.SecurityIdentifier($WellKnownSidType, $null) + $objUser = $objSID.Translate( [System.Security.Principal.NTAccount]) + } $objUser } catch { + return $null } } <# .Synopsis - Get-UserSID + Enable-Privilege #> -function Get-UserSID -{ - param ([System.Security.Principal.NTAccount]$User) - try - { - $User.Translate([System.Security.Principal.SecurityIdentifier]) - } - catch { - } -} - function Enable-Privilege { param( #The privilege to adjust. This set is taken from @@ -584,4 +683,4 @@ function Enable-Privilege { $type[0]::EnablePrivilege($Privilege, $Disable) } -Export-ModuleMember -Function Repair-FilePermission, Repair-SshdConfigPermission, Repair-SshdHostKeyPermission, Repair-AuthorizedKeyPermission, Repair-UserKeyPermission, Repair-UserSshConfigPermission +Export-ModuleMember -Function Repair-FilePermission, Repair-SshdConfigPermission, Repair-SshdHostKeyPermission, Repair-AuthorizedKeyPermission, Repair-UserKeyPermission, Repair-UserSshConfigPermission, Enable-Privilege, Get-UserAccount, Get-UserSID diff --git a/contrib/win32/openssh/VSWithBuildTools.xml b/contrib/win32/openssh/VSWithBuildTools.xml deleted file mode 100644 index 279fedf..0000000 --- a/contrib/win32/openssh/VSWithBuildTools.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - diff --git a/contrib/win32/openssh/sshd.vcxproj b/contrib/win32/openssh/sshd.vcxproj index 2061408..c68b4e1 100644 --- a/contrib/win32/openssh/sshd.vcxproj +++ b/contrib/win32/openssh/sshd.vcxproj @@ -246,6 +246,7 @@ + diff --git a/contrib/win32/openssh/sshd.vcxproj.filters b/contrib/win32/openssh/sshd.vcxproj.filters index e3254ed..f5c2282 100644 --- a/contrib/win32/openssh/sshd.vcxproj.filters +++ b/contrib/win32/openssh/sshd.vcxproj.filters @@ -150,6 +150,9 @@ Source Files + + Source Files + diff --git a/contrib/win32/openssh/sshd_config b/contrib/win32/openssh/sshd_config index 651af0b..7a25ba8 100644 --- a/contrib/win32/openssh/sshd_config +++ b/contrib/win32/openssh/sshd_config @@ -119,4 +119,5 @@ Subsystem sftp sftp-server.exe # X11Forwarding no # AllowTcpForwarding no # ForceCommand cvs server -# PubkeyAcceptedKeyTypes ssh-ed25519* \ No newline at end of file +# PubkeyAcceptedKeyTypes ssh-ed25519* +hostkeyagent \\.\pipe\openssh-ssh-agent \ No newline at end of file diff --git a/contrib/win32/openssh/version.rc b/contrib/win32/openssh/version.rc index 7b37b2b..b43900b 100644 Binary files a/contrib/win32/openssh/version.rc and b/contrib/win32/openssh/version.rc differ diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index de9fbe8..c876adb 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -114,9 +114,8 @@ int fileio_connect(struct w32_io* pio, char* name) { wchar_t* name_w = NULL; - wchar_t pipe_name[PATH_MAX]; HANDLE h = INVALID_HANDLE_VALUE; - int ret = 0, r; + int ret = 0; if (pio->handle != 0 && pio->handle != INVALID_HANDLE_VALUE) { debug3("fileio_connect called in unexpected state, pio = %p", pio); @@ -129,16 +128,9 @@ fileio_connect(struct w32_io* pio, char* name) errno = ENOMEM; return -1; } - r = _snwprintf_s(pipe_name, PATH_MAX, PATH_MAX, L"\\\\.\\pipe\\%ls", name_w); - if (r < 0 || r >= PATH_MAX) { - debug3("cannot create pipe name with %s", name); - errno = EOTHER; - return -1; - } - - + do { - h = CreateFileW(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, + h = CreateFileW(name_w, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, NULL); if (h != INVALID_HANDLE_VALUE) diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index f016eaa..144589d 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -478,11 +478,23 @@ strmode(mode_t mode, char *p) break; } - /* The below code is commented as the group, other is not applicable on the windows. - * As of now we are keeping "*" for everything. - * TODO - figure out if there is a better option - */ - const char *permissions = "********* "; + /* group, other are not applicable on the windows */ + + /* usr */ + if (mode & S_IREAD) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWRITE) + *p++ = 'w'; + else + *p++ = '-'; + if (mode & S_IEXEC) + *p++ = 'x'; + else + *p++ = '-'; + + const char *permissions = "****** "; for(int i = 0; i < strlen(permissions); i++) *p++ = permissions[i]; @@ -950,7 +962,8 @@ w32_strerror(int errnum) } char * -readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags) { +readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags) +{ int current_index = 0; char ch; wchar_t* wtmp = NULL; diff --git a/contrib/win32/win32compat/priv-agent.h b/contrib/win32/win32compat/priv-agent.h new file mode 100644 index 0000000..f98ac15 --- /dev/null +++ b/contrib/win32/win32compat/priv-agent.h @@ -0,0 +1,8 @@ +/* + * Definitions for IPC between sshd and privileged agent + */ +#pragma once + +#define SSH_PRIV_AGENT_MSG_ID 0 +#define PUBKEY_AUTH_REQUEST "pubkey" +#define LOAD_USER_PROFILE_REQUEST "loadprofile" diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index f50443f..019a657 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -102,6 +102,9 @@ struct key_translation keys[] = { { L"\x1b[B", VK_DOWN, 0 , 0}, { L"\x1b[C", VK_RIGHT, 0 , 0}, { L"\x1b[D", VK_LEFT, 0 , 0}, + { L"\x1b[F", VK_END, 0 , 0 }, /* KeyPad END */ + { L"\x1b[H", VK_HOME, 0 , 0 }, /* KeyPad HOME */ + { L"\x1b[Z", 0, 0 , 0 }, /* ignore Shift+TAB */ { L"\x1b[1~", VK_HOME, 0 , 0}, { L"\x1b[2~", VK_INSERT, 0 , 0}, { L"\x1b[3~", VK_DELETE, 0 , 0}, @@ -120,11 +123,17 @@ struct key_translation keys[] = { { L"\x1b[21~", VK_F10, 0 , 0}, { L"\x1b[23~", VK_F11, 0 , 0}, { L"\x1b[24~", VK_F12, 0 , 0}, + { L"\x1bOP", VK_F1, 0 , 0 }, + { L"\x1bOQ", VK_F2, 0 , 0 }, + { L"\x1bOR", VK_F3, 0 , 0 }, + { L"\x1bOS", VK_F4, 0 , 0 }, { L"\x1b", VK_ESCAPE, L'\x1b' , 0} }; static SHORT lastX = 0; static SHORT lastY = 0; +static wchar_t cmd_exe_path[PATH_MAX]; + SHORT currentLine = 0; consoleEvent* head = NULL; consoleEvent* tail = NULL; @@ -939,6 +948,19 @@ cleanup: return 0; } +wchar_t * +w32_cmd_path() +{ + ZeroMemory(cmd_exe_path, PATH_MAX); + if (!GetSystemDirectory(cmd_exe_path, sizeof(cmd_exe_path))) { + printf("GetSystemDirectory failed"); + exit(255); + } + + wcscat_s(cmd_exe_path, sizeof(cmd_exe_path), L"\\cmd.exe"); + return cmd_exe_path; +} + int start_with_pty(wchar_t *command) { @@ -1004,9 +1026,8 @@ start_with_pty(wchar_t *command) /* disable inheritance on pipe_in*/ GOTO_CLEANUP_ON_FALSE(SetHandleInformation(pipe_in, HANDLE_FLAG_INHERIT, 0)); - /*TODO - pick this up from system32*/ cmd[0] = L'\0'; - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L"cmd.exe")); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, w32_cmd_path())); if (command) { GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c")); @@ -1178,10 +1199,9 @@ start_withno_pty(wchar_t *command) run_under_cmd = TRUE; /* if above failed with FILE_NOT_FOUND, try running the provided command under cmd*/ - if (run_under_cmd) { - /*TODO - pick this up from system32*/ + if (run_under_cmd) { cmd[0] = L'\0'; - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L"cmd.exe")); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, w32_cmd_path())); if (command) { GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c")); GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" ")); diff --git a/contrib/win32/win32compat/ssh-agent/agent-request.h b/contrib/win32/win32compat/ssh-agent/agent-request.h index 3d7c3df..2ee6222 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-request.h +++ b/contrib/win32/win32compat/ssh-agent/agent-request.h @@ -16,6 +16,6 @@ int process_request_identities(struct sshbuf*, struct sshbuf*, struct agent_conn int process_sign_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_key(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_all(struct sshbuf*, struct sshbuf*, struct agent_connection*); -int process_authagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); +int process_privagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); /* auth */ diff --git a/contrib/win32/win32compat/ssh-agent/agent.c b/contrib/win32/win32compat/ssh-agent/agent.c index d1042d4..54f4a5f 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.c +++ b/contrib/win32/win32compat/ssh-agent/agent.c @@ -37,7 +37,7 @@ static HANDLE ioc_port = NULL; static BOOL debug_mode = FALSE; -#define AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-agent" +#define AGENT_PIPE_ID L"\\\\.\\pipe\\openssh-ssh-agent" static HANDLE event_stop_agent; static OVERLAPPED ol; @@ -168,12 +168,14 @@ agent_cleanup_connection(struct agent_connection* con) { debug("connection %p clean up", con); CloseHandle(con->pipe_handle); - if (con->hProfile) - UnloadUserProfile(con->auth_token, con->hProfile); - if (con->auth_token) - CloseHandle(con->auth_token); + if (con->profile_handle) + UnloadUserProfile(con->profile_token, con->profile_handle); + if (con->profile_token) + CloseHandle(con->profile_token); if (con->client_impersonation_token) CloseHandle(con->client_impersonation_token); + if (con->client_process_handle) + CloseHandle(con->client_process_handle); free(con); CloseHandle(ioc_port); ioc_port = NULL; @@ -251,13 +253,13 @@ get_con_client_info(struct agent_connection* con) DWORD sshd_sid_len = 0; PSID sshd_sid = NULL; SID_NAME_USE nuse; - HANDLE client_primary_token = NULL, client_impersonation_token = NULL, client_proc_handle = NULL; + HANDLE client_primary_token = NULL, client_impersonation_token = NULL, client_process_handle = NULL; TOKEN_USER* info = NULL; BOOL isMember = FALSE; if (GetNamedPipeClientProcessId(con->pipe_handle, &client_pid) == FALSE || - (client_proc_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, client_pid)) == NULL || - OpenProcessToken(client_proc_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || + (client_process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL || + OpenProcessToken(client_process_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || DuplicateToken(client_primary_token, SecurityImpersonation, &client_impersonation_token) == FALSE) { error("cannot retrieve client impersonatin token"); goto done; @@ -341,15 +343,19 @@ done: free(ref_dom); if (info) free(info); - if (client_proc_handle) - CloseHandle(client_proc_handle); if (client_primary_token) CloseHandle(client_primary_token); - if (r == 0) + if (r == 0) { + con->client_process_handle = client_process_handle; con->client_impersonation_token = client_impersonation_token; - else if (client_impersonation_token) - CloseHandle(client_impersonation_token); + } + else { + if (client_process_handle) + CloseHandle(client_process_handle); + if (client_impersonation_token) + CloseHandle(client_impersonation_token); + } return r; } diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h index ea2da0a..f732908 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.h +++ b/contrib/win32/win32compat/ssh-agent/agent.h @@ -4,17 +4,18 @@ #include "log.h" #define MAX_MESSAGE_SIZE 256 * 1024 -#define SSH_ROOT L"SOFTWARE\\SSH" +#define SSH_ROOT L"SOFTWARE\\OpenSSH" #define SSH_AGENT_ROOT SSH_ROOT L"\\Agent" #define SSH_KEYS_KEY L"Keys" -#define SSH_KEYS_ROOT SSH_ROOT L"\\" SSH_KEYS_KEY +#define SSH_KEYS_ROOT SSH_AGENT_ROOT L"\\" SSH_KEYS_KEY #define HEADER_SIZE 4 struct agent_connection { OVERLAPPED ol; HANDLE pipe_handle; - HANDLE client_impersonation_token; + HANDLE client_impersonation_token; + HANDLE client_process_handle; struct { DWORD num_bytes; DWORD transferred; @@ -36,8 +37,9 @@ struct agent_connection { SYSTEM, /* client is running as System */ SERVICE, /* client is running as LS or NS */ } client_type; - HANDLE auth_token; - HANDLE hProfile; + /* user profile related members */ + HANDLE profile_token; + HANDLE profile_handle; }; void agent_connection_on_io(struct agent_connection*, DWORD, OVERLAPPED*); diff --git a/contrib/win32/win32compat/ssh-agent/agentconfig.c b/contrib/win32/win32compat/ssh-agent/agentconfig.c index 836616e..954e4e0 100644 --- a/contrib/win32/win32compat/ssh-agent/agentconfig.c +++ b/contrib/win32/win32compat/ssh-agent/agentconfig.c @@ -72,6 +72,12 @@ mm_user_key_allowed(struct passwd *pw, Key *k, int i) return 0; } +void* mm_auth_pubkey(const char* user_name, const struct sshkey *key, + const u_char *sig, size_t slen, struct sshbuf* b) +{ + return NULL; +} + int kexgex_server(struct ssh * sh) { return -1; diff --git a/contrib/win32/win32compat/ssh-agent/authagent-request.c b/contrib/win32/win32compat/ssh-agent/authagent-request.c index 32603b9..3aafdf8 100644 --- a/contrib/win32/win32compat/ssh-agent/authagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/authagent-request.c @@ -39,6 +39,7 @@ #include "agent-request.h" #include "key.h" #include "inc\utf.h" +#include "..\priv-agent.h" #pragma warning(push, 3) @@ -83,9 +84,11 @@ done: } -void -LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) { +static HANDLE +LoadProfile(HANDLE user_token, wchar_t* user, wchar_t* domain) { PROFILEINFOW profileInfo; + HANDLE ret = NULL; + profileInfo.dwFlags = PI_NOUI; profileInfo.lpProfilePath = NULL; profileInfo.lpUserName = user; @@ -96,12 +99,16 @@ LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) { profileInfo.dwSize = sizeof(profileInfo); EnablePrivilege("SeBackupPrivilege", 1); EnablePrivilege("SeRestorePrivilege", 1); - if (LoadUserProfileW(con->auth_token, &profileInfo) == FALSE) + if (LoadUserProfileW(user_token, &profileInfo) == FALSE) { debug("Loading user (%ls,%ls) profile failed ERROR: %d", user, domain, GetLastError()); + goto done; + } else - con->hProfile = profileInfo.hProfile; + ret = profileInfo.hProfile; +done: EnablePrivilege("SeBackupPrivilege", 0); EnablePrivilege("SeRestorePrivilege", 0); + return ret; } #define MAX_USER_LEN 64 @@ -238,73 +245,13 @@ done: return dup_t; } -/* TODO - SecureZeroMemory password */ -int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { - char *user = NULL, *domain = NULL, *pwd = NULL; - size_t user_len, pwd_len; - wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; - int r = -1; - HANDLE token = 0, dup_token; - - if (sshbuf_get_cstring(request, &user, &user_len) != 0 || - sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 || - user_len == 0 || - pwd_len == 0 || - user_len > MAX_USER_LEN + MAX_FQDN_LEN || - pwd_len > MAX_PW_LEN) { - debug("bad password auth request"); - goto done; - } - - if ((user_utf16 = utf8_to_utf16(user)) == NULL || - (pwd_utf16 = utf8_to_utf16(pwd)) == NULL) { - debug("out of memory"); - goto done; - } - - if ((tmp = wcschr(user_utf16, L'@') ) != NULL ) { - udom_utf16 = tmp + 1; - *tmp = L'\0'; - } - - if (LogonUserW(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { - debug("failed to logon user: %ls domain: %ls", user_utf16, udom_utf16); - goto done; - } - - if ((dup_token = duplicate_token_for_client(con, token)) == NULL) - goto done; - - if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0) - goto done; - - con->auth_token = token; - LoadProfile(con, user_utf16, udom_utf16); - r = 0; -done: - /* TODO Fix this hacky protocol*/ - if ((r == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0)) - r = 0; - - if (user) - free(user); - if (pwd) - free(pwd); - if (user_utf16) - free(user_utf16); - if (pwd_utf16) - free(pwd_utf16); - - return r; -} - int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int r = -1; char *key_blob, *user, *sig, *blob; size_t key_blob_len, user_len, sig_len, blob_len; struct sshkey *key = NULL; HANDLE token = NULL, dup_token = NULL; - wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *tmp; + wchar_t *user_utf16 = NULL; PWSTR wuser_home = NULL; @@ -346,14 +293,6 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0) goto done; - con->auth_token = token; - token = NULL; - if ((tmp = wcschr(user_utf16, L'@')) != NULL) { - udom_utf16 = tmp + 1; - *tmp = L'\0'; - } - LoadProfile(con, user_utf16, udom_utf16); - r = 0; done: /* TODO Fix this hacky protocol*/ @@ -373,7 +312,60 @@ done: return r; } -int process_authagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { +int process_loadprofile_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { + int r = 0, success = 0; + char *user; + size_t user_len; + u_int32_t user_token_int = 0; + HANDLE user_token = NULL; + wchar_t *user_utf16 = NULL, *dom_utf16 = NULL, *tmp; + + /* is profile already loaded */ + if (con->profile_handle) { + success = 1; + goto done; + } + + if (sshbuf_get_cstring(request, &user, &user_len) != 0 || + user_len > MAX_USER_LEN || + sshbuf_get_u32(request, &user_token_int) != 0){ + debug("invalid loadprofile request"); + goto done; + } + + if (DuplicateHandle(con->client_process_handle, (HANDLE)(INT_PTR)user_token_int, GetCurrentProcess(), + &user_token, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, FALSE, 0) == FALSE) { + debug("cannot duplicate user token, error: %d", GetLastError()); + goto done; + } + + if ((user_utf16 = utf8_to_utf16(user)) == NULL) { + debug("out of memory"); + goto done; + } + + /* split user and domain */ + if ((tmp = wcschr(user_utf16, L'@')) != NULL) { + dom_utf16 = tmp + 1; + *tmp = L'\0'; + } + + if ((con->profile_handle = LoadProfile(user_token, user_utf16, dom_utf16)) == NULL) + goto done; + + con->profile_token = user_token; + user_token = NULL; + success = 1; +done: + if (sshbuf_put_u8(response, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE) != 0) + r = -1; + + if (user_token) + CloseHandle(user_token); + return r; +} + +int process_privagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { char *opn; size_t opn_len; if (sshbuf_get_string_direct(request, &opn, &opn_len) != 0) { @@ -383,14 +375,14 @@ int process_authagent_request(struct sshbuf* request, struct sshbuf* response, s /* allow only admins and NT Service\sshd to send auth requests */ if (con->client_type != SSHD_SERVICE && con->client_type != ADMIN_USER) { - error("cannot authenticate: client process is not admin or sshd"); + error("cannot process request: client process is not admin or sshd"); return -1; } if (memcmp(opn, PUBKEY_AUTH_REQUEST, opn_len) == 0) return process_pubkeyauth_request(request, response, con); - else if (memcmp(opn, PASSWD_AUTH_REQUEST, opn_len) == 0) - return process_passwordauth_request(request, response, con); + else if (memcmp(opn, LOAD_USER_PROFILE_REQUEST, opn_len) == 0) + return process_loadprofile_request(request, response, con); else { debug("unknown auth request: %s", opn); return -1; diff --git a/contrib/win32/win32compat/ssh-agent/connection.c b/contrib/win32/win32compat/ssh-agent/connection.c index d6ae7c2..ecb71e2 100644 --- a/contrib/win32/win32compat/ssh-agent/connection.c +++ b/contrib/win32/win32compat/ssh-agent/connection.c @@ -30,6 +30,7 @@ */ #include "agent.h" #include "agent-request.h" +#include "..\priv-agent.h" #pragma warning(push, 3) @@ -168,8 +169,8 @@ process_request(struct agent_connection* con) case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: r = process_remove_all(request, response, con); break; - case SSH_AGENT_AUTHENTICATE: - r = process_authagent_request(request, response, con); + case SSH_PRIV_AGENT_MSG_ID: + r = process_privagent_request(request, response, con); break; default: debug("unknown agent request %d", type); diff --git a/contrib/win32/win32compat/win32_monitor_wrap.c b/contrib/win32/win32compat/win32_monitor_wrap.c new file mode 100644 index 0000000..01893c2 --- /dev/null +++ b/contrib/win32/win32compat/win32_monitor_wrap.c @@ -0,0 +1,214 @@ +/* +* Author: Manoj Ampalam +* mm_* routines that delegate privileged operations to privileged +* agent. +* +* Copyright (c) 2015 Microsoft Corp. +* All rights reserved +* +* Microsoft openssh win32 port +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#endif + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#ifdef WITH_OPENSSL +#include "dh.h" +#endif +#include "buffer.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" +#include "packet.h" +#include "mac.h" +#include "log.h" +#include "auth-pam.h" +#include "monitor_wrap.h" +#include "atomicio.h" +#include "monitor_fdpass.h" +#include "misc.h" +#include "uuencode.h" + +#include "channels.h" +#include "session.h" +#include "servconf.h" + +#include "ssherr.h" +#include "priv-agent.h" +#include "authfd.h" + +int priv_agent_sock = -1; +int ssh_request_reply(int, struct sshbuf *, struct sshbuf *); + +/* + * Get socket connected to privileged agent + * In Windows, this is implemented within ssh-agent + * that server both as a key-agent (like in Unix) and + * privileged agent. + * This is a temporary accomodation until Windows has + * Unix like privilege separation (monitor and less + * privileged worker) + */ +int get_priv_agent_sock() +{ + extern int auth_sock; + char env_value[12]; /* enough to accomodate "ssh-agent"*/ + size_t tmp; + + if (priv_agent_sock != -1) + return priv_agent_sock; + + /* check if auth_sock is populated and connected to "ssh-agent"*/ + if (auth_sock != -1 && + getenv_s(&tmp, env_value, 12, SSH_AUTHSOCKET_ENV_NAME) == 0 && + strncmp(env_value, "ssh-agent", 12) == 0 ) + priv_agent_sock = auth_sock; + else { + struct sockaddr_un sunaddr; + int sock; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, "\\\\.\\pipe\\openssh-ssh-agent", sizeof(sunaddr.sun_path)); + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + debug("%s: unable to create AF_UNIX socket, errno:%d", __func__, errno); + return -1; + } + + /* close on exec */ + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || + connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { + close(sock); + debug("%s: unable to connect to privileged agent, errno:%d", __func__, errno); + return -1; + } + + priv_agent_sock = sock; + } + + return priv_agent_sock; +} + + +void* mm_auth_pubkey(const char* user_name, const struct sshkey *key, + const u_char *sig, size_t slen, struct sshbuf* b) +{ + /* Pass key challenge material to privileged agent to retrieve token upon successful authentication */ + struct sshbuf *msg = NULL; + u_char *blob = NULL; + size_t blen = 0; + DWORD token = 0; + int agent_fd; + + while (1) { + if ((agent_fd = get_priv_agent_sock()) == -1) + break; + + msg = sshbuf_new(); + if (!msg) + fatal("%s: out of memory", __func__); + if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 || + sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST) != 0 || + sshkey_to_blob(key, &blob, &blen) != 0 || + sshbuf_put_string(msg, blob, blen) != 0 || + sshbuf_put_cstring(msg, user_name) != 0 || + sshbuf_put_string(msg, sig, slen) != 0 || + sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0 || + ssh_request_reply(agent_fd, msg, msg) != 0) { + debug("unable to send pubkeyauth request"); + break; + } + + if (sshbuf_get_u32(msg, &token) != 0) + break; + + debug3("%s authenticated via pubkey", user_name); + break; + + } + if (blob) + free(blob); + if (msg) + sshbuf_free(msg); + + return (void*)(INT_PTR)token; +} + +int mm_load_profile(const char* user_name, u_int token) +{ + struct sshbuf *msg = NULL; + int agent_fd; + u_char result = 0; + + while (1) { + if ((agent_fd = get_priv_agent_sock()) == -1) + break; + + msg = sshbuf_new(); + if (!msg) + fatal("%s: out of memory", __func__); + if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 || + sshbuf_put_cstring(msg, LOAD_USER_PROFILE_REQUEST) != 0 || + sshbuf_put_cstring(msg, user_name) != 0 || + sshbuf_put_u32(msg, token) != 0 || + ssh_request_reply(agent_fd, msg, msg) != 0) { + debug("unable to send loadprofile request %s", user_name); + break; + } + + if (sshbuf_get_u8(msg, &result) != 0 || result == SSH_AGENT_FAILURE) { + debug("agent failed to load profile for user %s", user_name); + break; + } + + break; + + } + + return result; +} \ No newline at end of file diff --git a/contrib/win32/win32compat/wmain_common.c b/contrib/win32/win32compat/wmain_common.c index 585442d..b78d8cf 100644 --- a/contrib/win32/win32compat/wmain_common.c +++ b/contrib/win32/win32compat/wmain_common.c @@ -51,7 +51,7 @@ wmain(int argc, wchar_t **wargv) { } if (getenv("SSH_AUTH_SOCK") == NULL) - _putenv("SSH_AUTH_SOCK=ssh-agent"); + _putenv("SSH_AUTH_SOCK=\\\\.\\pipe\\openssh-ssh-agent"); w32posix_initialize(); diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index 3f1bc58..5e75d01 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -108,9 +108,6 @@ int sshd_main(int argc, wchar_t **wargv) { argv[i] = utf16_to_utf8(wargv[i]); } - if (getenv("SSH_AUTH_SOCK") == NULL) - _putenv("SSH_AUTH_SOCK=ssh-agent"); - w32posix_initialize(); if (getenv("SSHD_REMSOC")) is_child = 1; diff --git a/monitor_wrap.h b/monitor_wrap.h index 9e032d2..72ba1cd 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -98,4 +98,9 @@ int mm_bsdauth_respond(void *, u_int, char **); int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); int mm_skey_respond(void *, u_int, char **); +/* Windows specific */ +void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t, + struct sshbuf*); +int mm_load_profile(const char*, u_int ); + #endif /* _MM_WRAP_H_ */ diff --git a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 index ffbcb1b..d1f43fe 100644 --- a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 +++ b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 @@ -1,4 +1,6 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +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 = "authorized_keys_fileperm" @@ -6,7 +8,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite" @@ -22,17 +24,31 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $ssouser = $OpenSSHTestInfo["SSOUser"] $PwdUser = $OpenSSHTestInfo["PasswdUser"] $ssouserProfile = $OpenSSHTestInfo["SSOUserProfile"] - Remove-Item -Path (Join-Path $testDir "*$fileName") -Force -ErrorAction ignore + Remove-Item -Path (Join-Path $testDir "*$fileName") -Force -ErrorAction SilentlyContinue + $platform = Get-Platform + $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + #suppress the firewall blocking dialogue on win7 + netsh advfirewall firewall add rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any action=allow dir=in + } } AfterEach { $tI++ } + + AfterAll { + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + netsh advfirewall firewall delete rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any dir=in + } + } Context "Authorized key file permission" { BeforeAll { - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $objUser = New-Object System.Security.Principal.NTAccount($ssouser) - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $objUserSid = Get-UserSID -User $ssouser $ssouserSSHProfilePath = Join-Path $ssouserProfile .testssh if(-not (Test-Path $ssouserSSHProfilePath -PathType Container)) { @@ -43,21 +59,22 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $testknownhosts = Join-path $PSScriptRoot testdata\test_known_hosts Copy-Item $Source $ssouserSSHProfilePath -Force -ErrorAction Stop - Adjust-UserKeyFileACL -Filepath $authorizedkeyPath -Owner $objUser -OwnerPerms "Read, Write" + Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false - Get-Process -Name sshd | Where-Object {$_.SI -ne 0} | Stop-process + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue #add wrong password so ssh does not prompt password if failed with authorized keys Add-PasswordSetting -Pass "WrongPass" $tI=1 } AfterAll { + Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false if(Test-Path $authorizedkeyPath) { - Adjust-UserKeyFileACL -Filepath $authorizedkeyPath -Owner $objUser -OwnerPerms "Read, Write" - Remove-Item $authorizedkeyPath -Force -ErrorAction Ignore + Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false + Remove-Item $authorizedkeyPath -Force -ErrorAction SilentlyContinue } if(Test-Path $ssouserSSHProfilePath) { - Remove-Item $ssouserSSHProfilePath -Force -ErrorAction Ignore -Recurse + Remove-Item $ssouserSSHProfilePath -Force -ErrorAction SilentlyContinue -Recurse } Remove-PasswordSetting $tC++ @@ -66,11 +83,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { BeforeEach { $filePath = Join-Path $testDir "$tC.$tI.$fileName" $logPath = Join-Path $testDir "$tC.$tI.$logName" + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(pwd user is the owner and running process can access to the file)" { #setup to have ssouser as owner and grant ssouser read and write, admins group, and local system full control - Adjust-UserKeyFileACL -Filepath $authorizedkeyPath -Owner $objUser -OwnerPerms "Read, Write" + Repair-FilePermission -Filepath $authorizedkeyPath -Owners $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -78,14 +96,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $o | Should Be "1234" #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by local system)" { #setup to have system as owner and grant it full control - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $adminAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $objUser -Perms "Read, Write" + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $systemSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -93,14 +109,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $o | Should Be "1234" #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd does not have explict ACE)" { - #setup to have admin group as owner and grant it full control - - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" + #setup to have admin group as owner and grant it full control + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -108,15 +122,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $o | Should Be "1234" #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd have explict ACE)" { #setup to have admin group as owner and grant it full control - - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $objUser -Perms "Read, Write" + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -124,14 +135,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $o | Should Be "1234" #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other admin user)" { #setup to have current user (admin user) as owner and grant it full control - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $currentUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $adminAccount -Perms "FullControl" + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $currentUserSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -141,18 +150,16 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $matches.Count | Should BeGreaterThan 2 #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-negative(other account can access private key file)" { - #setup to have current user as owner and grant it full control - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $objUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $adminAccount -Perms "FullControl" + #setup to have current user as owner and grant it full control + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #add $PwdUser to access the file authorized_keys - $objPwdUser = New-Object System.Security.Principal.NTAccount($PwdUser) - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $objPwdUser -Perm "Read" + $objPwdUserSid = Get-UserSid -User $PwdUser + Set-FilePermission -FilePath $authorizedkeyPath -User $objPwdUserSid -Perm "Read" #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -162,15 +169,13 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $matches.Count | Should BeGreaterThan 2 #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other non-admin user)" { - #setup to have PwdUser as owner and grant it full control - $objPwdUser = New-Object System.Security.Principal.NTAccount($PwdUser) - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -owner $objPwdUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $adminAccount -Perms "FullControl" + #setup to have PwdUser as owner and grant it full control + $objPwdUserSid = Get-UserSid -User $PwdUser + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objPwdUserSid -FullAccessNeeded $adminsSid,$systemSid,$objPwdUser -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -180,12 +185,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $matches.Count | Should BeGreaterThan 2 #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } - It "$tC.$tI-authorized_keys-negative(the running process does not have read access to the authorized_keys)" { + It "$tC.$tI-authorized_keys-negative(the running process does not have read access to the authorized_keys)" -skip:$skip { #setup to have ssouser as owner and grant it full control - Set-FileOwnerAndACL -Filepath $authorizedkeyPath -Owner $objUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $systemAccount -Perms "FullControl" + Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objUserSid -FullAccessNeeded $systemSid,$objUserSid -confirm:$false + Set-FilePermission -Filepath $authorizedkeyPath -UserSid $adminsSid -Action Delete #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow @@ -196,7 +201,7 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $matches.Count | Should BeGreaterThan 2 #Cleanup - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } } } diff --git a/regress/pesterTests/Cfginclude.Tests.ps1 b/regress/pesterTests/Cfginclude.Tests.ps1 index 59ac559..3075565 100644 --- a/regress/pesterTests/Cfginclude.Tests.ps1 +++ b/regress/pesterTests/Cfginclude.Tests.ps1 @@ -1,4 +1,5 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force $tC = 1 $tI = 0 $suite = "authorized_keys_fileperm" @@ -6,7 +7,7 @@ Describe "Tests for ssh config" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } if(-not (Test-Path $OpenSSHTestInfo["TestDataPath"])) @@ -27,38 +28,39 @@ Describe "Tests for ssh config" -Tags "CI" { # for the first time, delete the existing log files. if ($OpenSSHTestInfo['DebugMode']) { - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore - Remove-Item -Path (Join-Path $testDir "*log*.log") -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue + Remove-Item -Path (Join-Path $testDir "*log*.log") -Force -ErrorAction SilentlyContinue } - Remove-Item -Path (Join-Path $testDir "*logName") -Force -ErrorAction ignore + Remove-Item -Path (Join-Path $testDir "*logName") -Force -ErrorAction SilentlyContinue } AfterEach { if( $OpenSSHTestInfo["DebugMode"]) { - Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" "$testDir\agentlog$tC.$tI.log" -Force -ErrorAction ignore - Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" "$testDir\sshdlog$tC.$tI.log" -Force -ErrorAction ignore + Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" "$testDir\agentlog$tC.$tI.log" -Force -ErrorAction SilentlyContinue + Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" "$testDir\sshdlog$tC.$tI.log" -Force -ErrorAction SilentlyContinue #Clear the ssh-agent, sshd logs so that next testcase will get fresh logs. - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue } $tI++ } Context "$tC-User SSHConfig--ReadConfig" { BeforeAll { - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $objUser = New-Object System.Security.Principal.NTAccount($ssouser) - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $objUserSid = Get-UserSID -User $ssouser $userConfigFile = Join-Path $home ".ssh\config" if( -not (Test-path $userConfigFile) ) { Copy-item "$PSScriptRoot\testdata\ssh_config" $userConfigFile -force } + Enable-Privilege SeRestorePrivilege | out-null $oldACL = Get-ACL $userConfigFile $tI=1 } @@ -67,8 +69,8 @@ Describe "Tests for ssh config" -Tags "CI" { $logPath = Join-Path $testDir "$tC.$tI.$logName" } - AfterEach { - Set-Acl -Path $userConfigFile -AclObject $oldACL + AfterEach { + Set-Acl -Path $userConfigFile -AclObject $oldACL -confirm:$false } AfterAll { @@ -77,9 +79,7 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig positive (current logon user is the owner)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $currentUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $userConfigFile -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $adminAccount -Perms "FullControl" + Repair-FilePermission -Filepath $userConfigFile -Owners $currentUserSid -FullAccessNeeded $adminsSid,$systemSid,$currentUserSid -confirm:$false #Run $o = ssh test_target echo 1234 @@ -88,8 +88,7 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig positive (local system is the owner)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $adminAccount -Perms "FullControl" + Repair-FilePermission -Filepath $userConfigFile -Owners $systemSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run $o = ssh test_target echo 1234 @@ -98,8 +97,8 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig positive (admin is the owner and current user has no explict ACE)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $systemAccount -Perms "FullControl" + Repair-FilePermission -Filepath $userConfigFile -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false + Set-FilePermission -Filepath $userConfigFile -UserSid $currentUserSid -Action Delete #Run $o = ssh test_target echo 1234 @@ -108,10 +107,8 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig positive (admin is the owner and current user has explict ACE)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $currentUser -Perms "Read, Write" - + Repair-FilePermission -Filepath $userConfigFile -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid,$currentUserSid -confirm:$false + #Run $o = ssh test_target echo 1234 $o | Should Be "1234" @@ -119,9 +116,7 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig negative (wrong owner)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $ssouser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $userConfigFile -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $adminAccount -Perms "FullControl" + Repair-FilePermission -Filepath $userConfigFile -Owners $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run cmd /c "ssh test_target echo 1234 2> $logPath" @@ -131,10 +126,7 @@ Describe "Tests for ssh config" -Tags "CI" { It "$tC.$tI-User SSHConfig-ReadConfig negative (others has permission)" { #setup - Set-FileOwnerAndACL -Filepath $userConfigFile -Owner $currentUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $userConfigFile -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $adminAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $userConfigFile -User $objUser -Perms "Read" + Repair-FilePermission -Filepath $userConfigFile -Owners $currentUserSid -FullAccessNeeded $adminsSid,$systemSid,$currentUserSid -ReadAccessNeeded $objUserSid -confirm:$false #Run cmd /c "ssh test_target echo 1234 2> $logPath" diff --git a/regress/pesterTests/CommonUtils.psm1 b/regress/pesterTests/CommonUtils.psm1 index 49f345a..ffcd1f8 100644 --- a/regress/pesterTests/CommonUtils.psm1 +++ b/regress/pesterTests/CommonUtils.psm1 @@ -1,8 +1,13 @@ -Enum PlatformType { - Windows - Linux - OSX -} +Import-Module OpenSSHUtils -Force + +Add-Type -TypeDefinition @" + public enum PlatformType + { + Windows, + Linux, + OSX + } +"@ function Get-Platform { # Use the .NET Core APIs to determine the current platform; if a runtime @@ -31,157 +36,59 @@ function Get-Platform { } } -<# -.Synopsis - user key should be owned by current user account - private key can be accessed only by the file owner, localsystem and Administrators - public user key can be accessed by only file owner, localsystem and Administrators and read by everyone - -.Outputs - N/A - -.Inputs - FilePath - The path to the file - Owner - The file owner - OwnerPerms - The permissions grant to owner -#> -function Adjust-UserKeyFileACL -{ - param ( - [parameter(Mandatory=$true)] - [string]$FilePath, - [System.Security.Principal.NTAccount] $Owner = $null, - [System.Security.AccessControl.FileSystemRights[]] $OwnerPerms = $null - ) - - $myACL = Get-ACL $FilePath - $myACL.SetAccessRuleProtection($True, $FALSE) - Set-Acl -Path $FilePath -AclObject $myACL - - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $everyoneAccount = New-Object System.Security.Principal.NTAccount("EveryOne") - $myACL = Get-ACL $FilePath - - $actualOwner = $null - if($Owner -eq $null) - { - $actualOwner = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - } - else - { - $actualOwner = $Owner - } - - $myACL.SetOwner($actualOwner) - - if($myACL.Access) - { - $myACL.Access | % { - if(-not ($myACL.RemoveAccessRule($_))) - { - throw "failed to remove access of $($_.IdentityReference.Value) rule in setup " - } - } - } - - $adminACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($adminAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($adminACE) - - $systemACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($systemAccount, "FullControl", "None", "None", "Allow") - $myACL.AddAccessRule($systemACE) - - if(-not ($actualOwner.Equals($adminAccount)) -and (-not $actualOwner.Equals($systemAccount)) -and $OwnerPerms) - { - $OwnerPerms | % { - $ownerACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($actualOwner, $_, "None", "None", "Allow") - $myACL.AddAccessRule($ownerACE) - } - } - - if($FilePath.EndsWith(".pub")) - { - $everyoneAce = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ("Everyone", "Read", "None", "None", "Allow") - $myACL.AddAccessRule($everyoneAce) - } - - Set-Acl -Path $FilePath -AclObject $myACL -} - -function Set-FileOwnerAndACL -{ - param( - [parameter(Mandatory=$true)] - [string]$FilePath, - [System.Security.Principal.NTAccount]$Owner = $null, - [System.Security.AccessControl.FileSystemRights[]] $OwnerPerms = @("Read", "Write") - ) - - $myACL = Get-ACL -Path $FilePath - $myACL.SetAccessRuleProtection($True, $FALSE) - Set-Acl -Path $FilePath -AclObject $myACL - - $myACL = Get-ACL $FilePath - $actualOwner = $null - if($owner -eq $null) - { - $actualOwner = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - } - else - { - $actualOwner = $Owner - } - - $myACL.SetOwner($actualOwner) - - if($myACL.Access) - { - $myACL.Access | % { - if(-not ($myACL.RemoveAccessRule($_))) - { - throw "failed to remove access of $($_.IdentityReference.Value) rule in setup " - } - } - } - - if($OwnerPerms) - { - $OwnerPerms | % { - $ownerACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($actualOwner, $_, "None", "None", "Allow") - $myACL.AddAccessRule($ownerACE) - } - } - - Set-Acl -Path $FilePath -AclObject $myACL -} - -function Add-PermissionToFileACL +function Set-FilePermission { param( [parameter(Mandatory=$true)] [string]$FilePath, - [System.Security.Principal.NTAccount] $User, + [parameter(Mandatory=$true)] + [System.Security.Principal.SecurityIdentifier] $UserSid, [System.Security.AccessControl.FileSystemRights[]]$Perms, - [System.Security.AccessControl.AccessControlType] $AccessType = "Allow" + [System.Security.AccessControl.AccessControlType] $AccessType = "Allow", + [ValidateSet("Add", "Delete")] + [string]$Action = "Add" ) $myACL = Get-ACL $FilePath - - if($Perms) + $account = Get-UserAccount -UserSid $UserSid + if($Action -ieq "Delete") + { + $myACL.SetAccessRuleProtection($True, $True) + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $myACL + $myACL = Get-ACL $FilePath + + if($myACL.Access) + { + $myACL.Access | % { + if($_.IdentityReference.Equals($account)) + { + if($_.IsInherited) + { + $myACL.SetAccessRuleProtection($True, $True) + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $myACL + $myACL = Get-ACL $FilePath + } + + if(-not ($myACL.RemoveAccessRule($_))) + { + throw "failed to remove access of $($_.IdentityReference) rule in setup " + } + } + } + } + } + elseif($Perms) { $Perms | % { $userACE = New-Object System.Security.AccessControl.FileSystemAccessRule ` - ($User, $_, "None", "None", $AccessType) + ($UserSid, $_, "None", "None", $AccessType) $myACL.AddAccessRule($userACE) } - } - - Set-Acl -Path $FilePath -AclObject $myACL + } + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $myACL -confirm:$false } function Add-PasswordSetting diff --git a/regress/pesterTests/Hostkey_fileperm.Tests.ps1 b/regress/pesterTests/Hostkey_fileperm.Tests.ps1 index 365bb20..0bb7bc4 100644 --- a/regress/pesterTests/Hostkey_fileperm.Tests.ps1 +++ b/regress/pesterTests/Hostkey_fileperm.Tests.ps1 @@ -1,4 +1,5 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force $tC = 1 $tI = 0 $suite = "hostkey_fileperm" @@ -6,7 +7,7 @@ Describe "Tests for host keys file permission" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite" @@ -19,30 +20,57 @@ Describe "Tests for host keys file permission" -Tags "CI" { $port = 47003 $ssouser = $OpenSSHTestInfo["SSOUser"] $script:logNum = 0 - Remove-Item -Path (Join-Path $testDir "*$logName") -Force -ErrorAction ignore + Remove-Item -Path (Join-Path $testDir "*$logName") -Force -ErrorAction SilentlyContinue + $platform = Get-Platform + $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + #suppress the firewall blocking dialogue on win7 + netsh advfirewall firewall add rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any action=allow dir=in + } } AfterEach { $tI++ } + AfterAll { + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + netsh advfirewall firewall delete rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any dir=in + } + } Context "$tC - Host key files permission" { BeforeAll { - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $objUser = New-Object System.Security.Principal.NTAccount($ssouser) - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - $everyone = New-Object System.Security.Principal.NTAccount("EveryOne") + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $objUserSid = Get-UserSID -User $ssouser + $everyoneSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::WorldSid) $hostKeyFilePath = join-path $testDir hostkeyFilePermTest_ed25519_key if(Test-path $hostKeyFilePath -PathType Leaf) { - Set-FileOwnerAndACL -filepath $hostKeyFilePath + Repair-SshdHostKeyPermission -filepath $hostKeyFilePath -confirm:$false } - if(Test-path "$hostKeyFilePath.pub" -PathType Leaf){ - Set-FileOwnerAndACL -filepath "$hostKeyFilePath.pub" - } - Remove-Item -path "$hostKeyFilePath*" -Force -ErrorAction Ignore + Remove-Item -path "$hostKeyFilePath*" -Force -ErrorAction SilentlyContinue ssh-keygen.exe -t ed25519 -f $hostKeyFilePath -P `"`" - Get-Process -Name sshd | Where-Object {$_.SI -ne 0} | Stop-process + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue $tI=1 + + function WaitForValidation + { + param([string]$logPath, [int]$length) + $num = 0 + while((-not (Test-Path $logPath -PathType leaf)) -or ((Get-item $logPath).Length -lt $length) -and ($num++ -lt 4)) + { + Start-Sleep -Milliseconds 1000 + } + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + + $num = 0 + while ([string]::IsNullorEmpty($(Get-Content $logPath -ErrorAction SilentlyContinue | Out-String)) -and ($num++ -lt 4)) + { + Start-Sleep -Milliseconds 1000 + } + } } BeforeEach { @@ -50,76 +78,59 @@ Describe "Tests for host keys file permission" -Tags "CI" { } AfterAll { - if(Test-path $hostKeyFilePath -PathType Leaf){ - Adjust-UserKeyFileACL -Filepath $hostKeyFilePath -Owner $systemAccount - } - if(Test-path "$hostKeyFilePath.pub" -PathType Leaf){ - Adjust-UserKeyFileACL -Filepath "$hostKeyFilePath.pub" -Owner $systemAccount + if(Test-path $hostKeyFilePath -PathType Leaf) { + Repair-SshdHostKeyPermission -filepath $hostKeyFilePath -confirm:$false } $tC++ } - It "$tC.$tI-Host keys-positive (both public and private keys are owned by admin groups and running process can access to public key file)" { - Set-FileOwnerAndACL -Filepath $hostKeyFilePath -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $systemAccount -Perms "FullControl" + It "$tC.$tI-Host keys-positive (both public and private keys are owned by admin groups and running process can access to public key file)" { + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false + + #Run + + Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow + WaitForValidation -LogPath $logPath -Length 600 + + #validate file content does not contain unprotected info. + $logPath | Should Not Contain "UNPROTECTED PRIVATE KEY FILE!" - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $everyOne -Perms "Read" + } + + It "$tC.$tI-Host keys-positive (both public and private keys are owned by admin groups and pwd user has explicit ACE)" { + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -ReadAccessNeeded $currentUserSid -confirm:$false + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -ReadAccessNeeded $everyOneSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } + WaitForValidation -LogPath $logPath -Length 600 #validate file content does not contain unprotected info. $logPath | Should Not Contain "UNPROTECTED PRIVATE KEY FILE!" } - It "$tC.$tI-Host keys-positive (both public and private keys are owned by admin groups and pwd user has explicit ACE)" { - Set-FileOwnerAndACL -Filepath $hostKeyFilePath -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $currentUser -Perms "Read" + It "$tC.$tI-Host keys-positive (both public and private keys are owned by system and running process can access to public key file)" -skip:$skip { + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $systemSid -FullAccessNeeded $systemSid,$adminsSid -ReadAccessNeeded $currentUserSid -confirm:$false + Set-FilePermission -Filepath $hostKeyFilePath -UserSid $adminsSid -Action Delete + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $systemSid -FullAccessNeeded $systemSid,$adminsSid -ReadAccessNeeded $currentUserSid -confirm:$false + Set-FilePermission -Filepath "$hostKeyFilePath.pub" -UserSid $adminsSid -Action Delete - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $everyOne -Perms "Read" - #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } - - #validate file content does not contain unprotected info. - $logPath | Should Not Contain "UNPROTECTED PRIVATE KEY FILE!" - } - - It "$tC.$tI-Host keys-positive (both public and private keys are owned by system and running process can access to public key file)" { - Set-FileOwnerAndACL -Filepath $hostKeyFilePath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $adminAccount -Perms "Read" - - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $adminAccount -Perms "Read" - - #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } - + WaitForValidation -LogPath $logPath -Length 600 #validate file content does not contain unprotected info. $logPath | Should Not Contain "UNPROTECTED PRIVATE KEY FILE!" } It "$tC.$tI-Host keys-negative (other account can access private key file)" { - Set-FileOwnerAndACL -Filepath $hostKeyFilePath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $adminAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $objUser -Perms "Read" + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $adminsSid -FullAccessNeeded $systemSid,$adminsSid -ReadAccessNeeded $objUserSid -confirm:$false + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $adminsSid -FullAccessNeeded $systemSid,$adminsSid -ReadAccessNeeded $everyOneSid -confirm:$false - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $everyOne -Perms "Read" - #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } + WaitForValidation -LogPath $logPath -Length 1100 #validate file content contains unprotected info. $logPath | Should Contain "key_load_private: bad permissions" @@ -127,32 +138,26 @@ Describe "Tests for host keys file permission" -Tags "CI" { It "$tC.$tI-Host keys-negative (the private key has wrong owner)" { #setup to have ssouser as owner and grant it full control - Set-FileOwnerAndACL -FilePath $hostKeyFilePath -Owner $objUser -OwnerPerms "Read","Write" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $adminAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $systemAccount -Perms "FullControl" - - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $adminAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $systemAccount -Perms "FullControl" - Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $everyOne -Perms "Read" + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $objUserSid -FullAccessNeeded $systemSid,$adminsSid,$objUserSid -confirm:$false + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $adminsSid -FullAccessNeeded $systemSid,$adminsSid -ReadAccessNeeded $everyOneSid -confirm:$false #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } + WaitForValidation -LogPath $logPath -Length 1100 #validate file content contains unprotected info. $logPath | Should Contain "key_load_private: bad permissions" } - It "$tC.$tI-Host keys-negative (the running process does not have read access to public key)" { + It "$tC.$tI-Host keys-negative (the running process does not have read access to public key)" -skip:$skip { #setup to have ssouser as owner and grant it full control - Set-FileOwnerAndACL -FilePath $hostKeyFilePath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $adminAccount -Perms "Read" - - Set-FileOwnerAndACL -Filepath "$hostKeyFilePath.pub" -Owner $systemAccount -OwnerPerms "FullControl" + Repair-FilePermission -Filepath $hostKeyFilePath -Owners $systemSid -FullAccessNeeded $systemSid,$adminsSid -confirm:$false + Repair-FilePermission -Filepath "$hostKeyFilePath.pub" -Owners $systemSid -FullAccessNeeded $systemSid -confirm:$false + Set-FilePermission -Filepath "$hostKeyFilePath.pub" -UserSid $adminsSid -Action Delete #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $logPath") -NoNewWindow - Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 2; Stop-Process $_; Start-sleep 1 } } + WaitForValidation -LogPath $logPath -Length 1100 #validate file content contains unprotected info. $logPath | Should Contain "key_load_public: Permission denied" diff --git a/regress/pesterTests/KeyUtils.Tests.ps1 b/regress/pesterTests/KeyUtils.Tests.ps1 index 7793dc0..d4f33e2 100644 --- a/regress/pesterTests/KeyUtils.Tests.ps1 +++ b/regress/pesterTests/KeyUtils.Tests.ps1 @@ -1,4 +1,5 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force $tC = 1 $tI = 0 $suite = "keyutils" @@ -7,7 +8,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite" @@ -20,48 +21,58 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { $keytypes = @("rsa","dsa","ecdsa","ed25519") $ssouser = $OpenSSHTestInfo["SSOUser"] - - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminsAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - $everyone = New-Object System.Security.Principal.NTAccount("EveryOne") - $objUser = New-Object System.Security.Principal.NTAccount($ssouser) + + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $objUserSid = Get-UserSID -User $ssouser + $everyoneSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::WorldSid) #only validate owner and ACEs of the file function ValidateKeyFile { param([string]$FilePath) $myACL = Get-ACL $FilePath - $myACL.Owner.Equals($currentUser.Value) | Should Be $true + $currentOwnerSid = Get-UserSid -User $myACL.Owner + $currentOwnerSid.Equals($currentUserSid) | Should Be $true $myACL.Access | Should Not Be $null + + $ReadAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Read.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + $ReadWriteAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Read.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Write.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + $FullControlPerm = [System.UInt32] [System.Security.AccessControl.FileSystemRights]::FullControl.value__ + if($FilePath.EndsWith(".pub")) { $myACL.Access.Count | Should Be 4 - $identities = @($systemAccount.Value, $adminsAccount.Value, $currentUser.Value, $everyone.Value) + $identities = @($systemSid, $adminsSid, $currentUserSid, $everyoneSid) } else { $myACL.Access.Count | Should Be 3 - $identities = @($systemAccount.Value, $adminsAccount.Value, $currentUser.Value) + $identities = @($systemSid, $adminsSid, $currentUserSid) } foreach ($a in $myACL.Access) { - $a.IdentityReference.Value -in $identities | Should Be $true + $id = Get-UserSid -User $a.IdentityReference + $identities -contains $id | Should Be $true - switch ($a.IdentityReference.Value) + switch ($id) { - {$_ -in @($systemAccount.Value, $adminsAccount.Value)} + {@($systemSid, $adminsSid) -contains $_} { - $a.FileSystemRights | Should Be "FullControl" + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FullControlPerm break; } - $currentUser.Value + $currentUserSid { - $a.FileSystemRights | Should Be "Write, Read, Synchronize" + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $ReadWriteAccessPerm break; } - $everyone.Value + $everyoneSid { - $a.FileSystemRights | Should Be "Read, Synchronize" + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $ReadAccessPerm break; } } @@ -106,7 +117,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { foreach($type in $keytypes) { $keyPath = Join-Path $testDir "id_$type" - remove-item $keyPath -ErrorAction ignore + remove-item $keyPath -ErrorAction SilentlyContinue ssh-keygen -t $type -P $keypassphrase -f $keyPath ValidateKeyFile -FilePath $keyPath ValidateKeyFile -FilePath "$keyPath.pub" @@ -116,7 +127,17 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { # This uses keys generated in above context Context "$tC -ssh-add test cases" { - BeforeAll {$tI=1} + BeforeAll { + $tI=1 + function WaitForStatus + { + param([string]$ServiceName, [string]$Status) + while((((Get-Service $ServiceName).Status) -ine $Status) -and ($num++ -lt 4)) + { + Start-Sleep -Milliseconds 1000 + } + } + } AfterAll{$tC++} # Executing ssh-agent will start agent service @@ -131,16 +152,21 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { (Get-Service sshd).Status | Should Be "Stopped" ssh-agent + WaitForStatus -ServiceName ssh-agent -Status "Running" (Get-Service ssh-agent).Status | Should Be "Running" Stop-Service ssh-agent -Force + + WaitForStatus -ServiceName ssh-agent -Status "Stopped" (Get-Service ssh-agent).Status | Should Be "Stopped" (Get-Service sshd).Status | Should Be "Stopped" # this should automatically start both the services Start-Service sshd + + WaitForStatus -ServiceName sshd -Status "Running" (Get-Service ssh-agent).Status | Should Be "Running" (Get-Service sshd).Status | Should Be "Running" } @@ -170,7 +196,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { { $keyPath = Join-Path $testDir "id_$type" $pubkeyraw = ((Get-Content "$keyPath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 } #delete added keys @@ -188,7 +214,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { { $keyPath = Join-Path $testDir "id_$type" $pubkeyraw = ((Get-Content "$keyPath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 } } } @@ -197,7 +223,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { BeforeAll { $keyFileName = "sshadd_userPermTestkey_ed25519" $keyFilePath = Join-Path $testDir $keyFileName - Remove-Item -path "$keyFilePath*" -Force -ErrorAction Ignore + Remove-Item -path "$keyFilePath*" -Force -ErrorAction SilentlyContinue ssh-keygen.exe -t ed25519 -f $keyFilePath -P $keypassphrase #set up SSH_ASKPASS Add-PasswordSetting -Pass $keypassphrase @@ -209,7 +235,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { } AfterEach { if(Test-Path $keyFilePath) { - Adjust-UserKeyFileACL -FilePath $keyFilePath -Owner $currentUser -OwnerPerms "Read, Write" + Repair-FilePermission -FilePath $keyFilePath -Owner $currentUserSid -FullAccessNeeded $currentUserSid,$systemSid,$adminsSid -confirm:$false } } @@ -220,30 +246,30 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { } It "$tC.$tI- ssh-add - positive (Secured private key owned by current user)" { - #setup to have current user as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $currentUser -OwnerPerms "FullControl" + #setup to have current user as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owner $currentUserSid -FullAccessNeeded $currentUserSid,$systemSid,$adminsSid -confirm:$false # for ssh-add to consume SSh_ASKPASS, stdin should not be TTY cmd /c "ssh-add $keyFilePath < $nullFile 2> nul" $LASTEXITCODE | Should Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 #clean up cmd /c "ssh-add -d $keyFilePath 2> nul " } It "$tC.$tI - ssh-add - positive (Secured private key owned by Administrators group and the current user has no explicit ACE)" { - #setup to have local admin group as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $adminsAccount -OwnerPerms "FullControl" + #setup to have local admin group as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false # for ssh-add to consume SSh_ASKPASS, stdin should not be TTY cmd /c "ssh-add $keyFilePath < $nullFile 2> nul " $LASTEXITCODE | Should Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 #clean up cmd /c "ssh-add -d $keyFilePath 2> nul " @@ -251,62 +277,56 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { It "$tC.$tI - ssh-add - positive (Secured private key owned by Administrators group and the current user has explicit ACE)" { #setup to have local admin group as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $adminsAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $keyFilePath -User $currentUser -Perm "Read, Write" + Repair-FilePermission -FilePath $keyFilePath -Owners $adminsSid -FullAccessNeeded $currentUserSid,$adminsSid,$systemSid -confirm:$false # for ssh-add to consume SSh_ASKPASS, stdin should not be TTY cmd /c "ssh-add $keyFilePath < $nullFile 2> nul " $LASTEXITCODE | Should Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 #clean up cmd /c "ssh-add -d $keyFilePath 2> nul " } It "$tC.$tI - ssh-add - positive (Secured private key owned by local system group)" { - #setup to have local admin group as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $keyFilePath -User $adminsAccount -Perm "FullControl" + #setup to have local admin group as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owners $systemSid -FullAccessNeeded $systemSid,$adminsSid -confirm:$false # for ssh-add to consume SSh_ASKPASS, stdin should not be TTY cmd /c "ssh-add $keyFilePath < $nullFile 2> nul " $LASTEXITCODE | Should Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 1 #clean up cmd /c "ssh-add -d $keyFilePath 2> nul " } It "$tC.$tI- ssh-add - negative (other account can access private key file)" { - #setup to have current user as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $currentUser -OwnerPerms "FullControl" - - #add ssouser to access the private key - Add-PermissionToFileACL -FilePath $keyFilePath -User $objUser -Perm "Read" + #setup to have current user as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owners $currentUserSid -FullAccessNeeded $currentUserSid,$adminsSid, $systemSid -ReadAccessNeeded $objUserSid -confirm:$false cmd /c "ssh-add $keyFilePath < $nullFile 2> nul " $LASTEXITCODE | Should Not Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 } It "$tC.$tI - ssh-add - negative (the private key has wrong owner)" { #setup to have ssouser as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -owner $objUser -OwnerPerms "Read, Write" - Add-PermissionToFileACL -FilePath $keyFilePath -User $adminsAccount -Perm "FullControl" + Repair-FilePermission -FilePath $keyFilePath -Owners $objUserSid -FullAccessNeeded $objUserSid,$adminsSid, $systemSid -confirm:$false cmd /c "ssh-add $keyFilePath < $nullFile 2> nul " $LASTEXITCODE | Should Not Be 0 $allkeys = ssh-add -L $pubkeyraw = ((Get-Content "$keyFilePath.pub").Split(' '))[1] - ($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 + @($allkeys | where { $_.contains($pubkeyraw) }).count | Should Be 0 } } @@ -314,7 +334,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { BeforeAll { $tI=1 $port = $OpenSSHTestInfo["Port"] - Remove-item (join-path $testDir "$tC.$tI.out.txt") -force -ErrorAction Ignore + Remove-item (join-path $testDir "$tC.$tI.out.txt") -force -ErrorAction SilentlyContinue } BeforeEach { $outputFile = join-path $testDir "$tC.$tI.out.txt" diff --git a/regress/pesterTests/Log_fileperm.Tests.ps1 b/regress/pesterTests/Log_fileperm.Tests.ps1 index 0dbe490..7b3177e 100644 --- a/regress/pesterTests/Log_fileperm.Tests.ps1 +++ b/regress/pesterTests/Log_fileperm.Tests.ps1 @@ -1,4 +1,6 @@ -$tC = 1 +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force +$tC = 1 $tI = 0 $suite = "log_fileperm" @@ -15,38 +17,52 @@ Describe "Tests for log file permission" -Tags "CI" { $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue } $port = 47003 - $logName = "log.txt" + $logName = "log.txt" + + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminsAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - - Remove-Item (Join-Path $testDir "*$logName") -Force -ErrorAction Ignore + Remove-Item (Join-Path $testDir "*$logName") -Force -ErrorAction SilentlyContinue + + $platform = Get-Platform + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + #suppress the firewall blocking dialogue on win7 + netsh advfirewall firewall add rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any action=allow dir=in + } #only validate owner and ACEs of the file - function ValiLogFilePerm { + function ValidateLogFilePerm { param([string]$FilePath) - + $myACL = Get-ACL $FilePath - $myACL.Owner.Equals($currentUser.Value) | Should Be $true - $myACL.Access | Should Not Be $null + $currentOwnerSid = Get-UserSid -User $myACL.Owner + $currentOwnerSid.Equals($currentUserSid) | Should Be $true + $myACL.Access | Should Not Be $null + + $ReadWriteAccessPerm = ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Read.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Write.value__) -bor ` + ([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__) + $FullControlPerm = [System.UInt32] [System.Security.AccessControl.FileSystemRights]::FullControl.value__ + $myACL.Access.Count | Should Be 3 - $identities = @($systemAccount.Value, $adminsAccount.Value, $currentUser.Value) + $identities = @($systemSid, $adminsSid, $currentUserSid) foreach ($a in $myACL.Access) { - $a.IdentityReference.Value -in $identities | Should Be $true + $id = Get-UserSid -User $a.IdentityReference + $identities -contains $id | Should Be $true - switch ($a.IdentityReference.Value) + switch ($id) { - {$_ -in @($systemAccount.Value, $adminsAccount.Value)} + {@($systemSid, $adminsSid) -contains $_} { - $a.FileSystemRights | Should Be "FullControl" + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $FullControlPerm break; } - - $currentUser.Value + $currentUserSid { - $a.FileSystemRights | Should Be "Write, Read, Synchronize" + ([System.UInt32]$a.FileSystemRights.value__) | Should Be $ReadWriteAccessPerm break; } } @@ -63,11 +79,17 @@ Describe "Tests for log file permission" -Tags "CI" { $logPath = Join-Path $testDir "$tC.$tI.$logName" } - AfterEach {$tI++;} + AfterEach {$tI++;} + AfterAll { + if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) + { + netsh advfirewall firewall delete rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any dir=in + } + } Context "$tC-SSHD -E Log file permission" { BeforeAll { - Get-Process -Name sshd | Where-Object {$_.SI -ne 0} | Stop-process + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue $tI=1 } @@ -79,8 +101,8 @@ Describe "Tests for log file permission" -Tags "CI" { #Run Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-E $logPath") -NoNewWindow Start-sleep 1; - ValiLogFilePerm -FilePath $logPath - Get-Process -Name sshd | % { if($_.SI -ne 0) { Stop-Process $_; Start-sleep 1 } } + ValidateLogFilePerm -FilePath $logPath + Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } } } \ No newline at end of file diff --git a/regress/pesterTests/PortForwarding.Tests.ps1 b/regress/pesterTests/PortForwarding.Tests.ps1 index 44fbcc4..609d6a9 100644 --- a/regress/pesterTests/PortForwarding.Tests.ps1 +++ b/regress/pesterTests/PortForwarding.Tests.ps1 @@ -1,14 +1,22 @@ -$tC = 1 +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force +$tC = 1 $tI = 0 $suite = "portfwd" Describe "E2E scenarios for port forwarding" -Tags "CI" { BeforeAll { + if($OpenSSHTestInfo -eq $null) + { + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." + } $testDir = Join-Path $OpenSSHTestInfo["TestDataPath"] $suite if(-not (Test-Path $testDir)) { $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue } + $platform = Get-Platform + $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) } BeforeEach { @@ -23,12 +31,12 @@ Describe "E2E scenarios for port forwarding" -Tags "CI" { AfterAll{$tC++} #TODO - this relies on winrm (that is windows specific) - It "$tC.$tI - local port forwarding" { + It "$tC.$tI - local port forwarding" -skip:$skip { ssh -L 5432:127.0.0.1:47001 test_target powershell.exe Test-WSMan -computer 127.0.0.1 -port 5432 | Set-Content $stdoutFile $stdoutFile | Should Contain "wsmid" } - It "$tC.$tI - remote port forwarding" { + It "$tC.$tI - remote port forwarding" -skip:$skip { ssh -R 5432:127.0.0.1:47001 test_target powershell.exe Test-WSMan -computer 127.0.0.1 -port 5432 | Set-Content $stdoutFile $stdoutFile | Should Contain "wsmid" } diff --git a/regress/pesterTests/SCP.Tests.ps1 b/regress/pesterTests/SCP.Tests.ps1 index 3f7c994..b2d9ff6 100644 --- a/regress/pesterTests/SCP.Tests.ps1 +++ b/regress/pesterTests/SCP.Tests.ps1 @@ -1,11 +1,12 @@ - +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force #covered -i -p -q -r -v -c -S -C #todo: -F, -l and -P should be tested over the network Describe "Tests for scp command" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $fileName1 = "test.txt" @@ -19,8 +20,8 @@ Describe "Tests for scp command" -Tags "CI" { $NestedSourceFilePath = Join-Path $NestedSourceDir $fileName2 $null = New-Item $SourceDir -ItemType directory -Force -ErrorAction SilentlyContinue $null = New-Item $NestedSourceDir -ItemType directory -Force -ErrorAction SilentlyContinue - $null = New-item -path $SourceFilePath -force -ErrorAction SilentlyContinue - $null = New-item -path $NestedSourceFilePath -force -ErrorAction SilentlyContinue + $null = New-item -path $SourceFilePath -ItemType file -force -ErrorAction SilentlyContinue + $null = New-item -path $NestedSourceFilePath -ItemType file -force -ErrorAction SilentlyContinue "Test content111" | Set-content -Path $SourceFilePath "Test content in nested dir" | Set-content -Path $NestedSourceFilePath $null = New-Item $DestinationDir -ItemType directory -Force -ErrorAction SilentlyContinue @@ -94,8 +95,8 @@ Describe "Tests for scp command" -Tags "CI" { # for the first time, delete the existing log files. if ($OpenSSHTestInfo['DebugMode']) { - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue } function CheckTarget { @@ -110,8 +111,8 @@ Describe "Tests for scp command" -Tags "CI" { $script:logNum++ # clear the ssh-agent, sshd logs so that next testcase will get fresh logs. - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue } return $false @@ -144,6 +145,7 @@ Describe "Tests for scp command" -Tags "CI" { AfterEach { Get-ChildItem $DestinationDir -Recurse | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue + Start-Sleep 1 } @@ -184,7 +186,7 @@ Describe "Tests for scp command" -Tags "CI" { $equal = @(Compare-Object (Get-ChildItem -Recurse -path $SourceDir) (Get-ChildItem -Recurse -path (join-path $DestinationDir $SourceDirName) ) -Property Name, Length).Length -eq 0 $equal | Should Be $true - if($Options.contains("-p")) + if($Options.contains("-p") -and ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -gt 2)) { $equal = @(Compare-Object (Get-ChildItem -Recurse -path $SourceDir).LastWriteTime.DateTime (Get-ChildItem -Recurse -path (join-path $DestinationDir $SourceDirName) ).LastWriteTime.DateTime).Length -eq 0 $equal | Should Be $true diff --git a/regress/pesterTests/SFTP.Tests.ps1 b/regress/pesterTests/SFTP.Tests.ps1 index 3d17df3..cf856ed 100644 --- a/regress/pesterTests/SFTP.Tests.ps1 +++ b/regress/pesterTests/SFTP.Tests.ps1 @@ -1,9 +1,12 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force Describe "SFTP Test Cases" -Tags "CI" { BeforeAll { + $serverDirectory = $null + $clientDirectory = $null if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $rootDirectory = "$($OpenSSHTestInfo["TestDataPath"])\SFTP" @@ -29,8 +32,11 @@ Describe "SFTP Test Cases" -Tags "CI" { $ssouser = $OpenSSHTestInfo["SSOUser"] $script:testId = 1 - Remove-item (Join-Path $rootDirectory "*.$outputFileName") -Force -ErrorAction Ignore - Remove-item (Join-Path $rootDirectory "*.$batchFileName") -Force -ErrorAction Ignore + Remove-item (Join-Path $rootDirectory "*.$outputFileName") -Force -ErrorAction SilentlyContinue + Remove-item (Join-Path $rootDirectory "*.$batchFileName") -Force -ErrorAction SilentlyContinue + + $platform = Get-Platform + $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) $testData1 = @( @{ @@ -162,9 +168,9 @@ Describe "SFTP Test Cases" -Tags "CI" { # for the first time, delete the existing log files. if ($OpenSSHTestInfo['DebugMode']) { - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server.log" -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server.log" -Force -ErrorAction SilentlyContinue } function CopyDebugLogs { @@ -175,21 +181,21 @@ Describe "SFTP Test Cases" -Tags "CI" { Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server.log" "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server_$script:testId.log" -Force # clear the ssh-agent, sshd logs so that next testcase will get fresh logs. - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore - Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server.log" -Force -ErrorAction ignore + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction SilentlyContinue + Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sftp-server.log" -Force -ErrorAction SilentlyContinue } } } AfterAll { - Get-ChildItem $serverDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue - Get-ChildItem $clientDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue + if($serverDirectory) { Get-ChildItem $serverDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } + if($clientDirectory) { Get-ChildItem $clientDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } } BeforeEach { - Get-ChildItem $serverDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue - Get-ChildItem $clientDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue + if($serverDirectory) { Get-ChildItem $serverDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } + if($clientDirectory) { Get-ChildItem $clientDirectory | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } $outputFilePath = Join-Path $rootDirectory "$($script:testId).$outputFileName" $batchFilePath = Join-Path $rootDirectory "$($script:testId).$batchFileName" } @@ -212,6 +218,7 @@ Describe "SFTP Test Cases" -Tags "CI" { It '' -TestCases:$testData2 { param([string]$Title, $Options, $tmpFileName1, $tmpFilePath1, $tmpFileName2, $tmpFilePath2, $tmpDirectoryName1, $tmpDirectoryPath1, $tmpDirectoryName2, $tmpDirectoryPath2) + if($skip) { return } #rm (remove file) $commands = "mkdir $tmpDirectoryPath1 @@ -263,16 +270,19 @@ Describe "SFTP Test Cases" -Tags "CI" { } It "$script:testId-ls lists items the user has no read permission" { + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $permTestHasAccessFile = "permTestHasAccessFile.txt" $permTestHasAccessFilePath = Join-Path $serverDirectory $permTestHasAccessFile - Remove-Item $permTestHasAccessFilePath -Force -ErrorAction Ignore + Remove-Item $permTestHasAccessFilePath -Force -ErrorAction SilentlyContinue New-Item $permTestHasAccessFilePath -ItemType file -Force -value "perm test has access file data" | Out-Null $permTestNoAccessFile = "permTestNoAccessFile.txt" $permTestNoAccessFilePath = Join-Path $serverDirectory $permTestNoAccessFile - Remove-Item $permTestNoAccessFilePath -Force -ErrorAction Ignore + Remove-Item $permTestNoAccessFilePath -Force -ErrorAction SilentlyContinue New-Item $permTestNoAccessFilePath -ItemType file -Force -value "perm test no access file data" | Out-Null - Set-FileOwnerAndACL -Filepath $permTestNoAccessFilePath -OwnerPerms "Read","Write" + Repair-FilePermission -Filepath $permTestNoAccessFilePath -Owners $currentUserSid -FullAccessNeeded $adminsSid,$currentUserSid -confirm:$false $Commands = "ls $serverDirectory" Set-Content $batchFilePath -Encoding UTF8 -value $Commands @@ -282,11 +292,11 @@ Describe "SFTP Test Cases" -Tags "CI" { #cleanup $HasAccessPattern = $permTestHasAccessFilePath.Replace("\", "[/\\]") - $matches = $content | select-string -Pattern "^/$HasAccessPattern\s{0,}$" + $matches = @($content | select-string -Pattern "^/$HasAccessPattern\s{0,}$") $matches.count | Should be 1 $NoAccessPattern = $permTestNoAccessFilePath.Replace("\", "[/\\]") - $matches = $content | select-string -Pattern "^/$NoAccessPattern\s{0,}$" + $matches = @($content | select-string -Pattern "^/$NoAccessPattern\s{0,}$") $matches.count | Should be 1 } } diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1 index 17434df..ab750de 100644 --- a/regress/pesterTests/SSH.Tests.ps1 +++ b/regress/pesterTests/SSH.Tests.ps1 @@ -1,4 +1,6 @@ -#todo: -i -q -v -l -c -C +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force +#todo: -i -q -v -l -c -C #todo: -S -F -V -e $tC = 1 $tI = 0 @@ -8,7 +10,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments." } $server = $OpenSSHTestInfo["Target"] @@ -20,6 +22,8 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { { $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue } + $platform = Get-Platform + $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) <#$testData = @( @{ @@ -112,7 +116,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { BeforeAll {$tI=1} AfterAll{$tC++} - It "$tC.$tI - stdout to file" { + It "$tC.$tI - stdout to file" -skip:$skip { ssh test_target powershell get-process > $stdoutFile $stdoutFile | Should Contain "ProcessName" } diff --git a/regress/pesterTests/SSHDConfig.tests.ps1 b/regress/pesterTests/SSHDConfig.tests.ps1 index 10f884a..722d8ae 100644 --- a/regress/pesterTests/SSHDConfig.tests.ps1 +++ b/regress/pesterTests/SSHDConfig.tests.ps1 @@ -50,7 +50,7 @@ $client.AddPasswordSetting($password) } AfterEach { - Remove-Item -Path $filePath -Force -ea ignore + Remove-Item -Path $filePath -Force -ea SilentlyContinue } AfterAll { diff --git a/regress/pesterTests/SSHD_Config b/regress/pesterTests/SSHD_Config index 4bab2a8..244471f 100644 --- a/regress/pesterTests/SSHD_Config +++ b/regress/pesterTests/SSHD_Config @@ -125,4 +125,5 @@ PubkeyAcceptedKeyTypes ssh-ed25519* #DenyUsers denyuser1 deny*2 denyuse?3, #AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin #DenyGroups denygroup1 denygr*p2 deny?rou?3 -#AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* \ No newline at end of file +#AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* +hostkeyagent \\.\pipe\openssh-ssh-agent diff --git a/regress/pesterTests/Userkey_fileperm.Tests.ps1 b/regress/pesterTests/Userkey_fileperm.Tests.ps1 index 1c4efb2..7e013b9 100644 --- a/regress/pesterTests/Userkey_fileperm.Tests.ps1 +++ b/regress/pesterTests/Userkey_fileperm.Tests.ps1 @@ -1,4 +1,5 @@ -Import-Module $PSScriptRoot\CommonUtils.psm1 -Force -DisableNameChecking +If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} +Import-Module $PSScriptRoot\CommonUtils.psm1 -Force $tC = 1 $tI = 0 @@ -8,7 +9,7 @@ Describe "Tests for user Key file permission" -Tags "CI" { BeforeAll { if($OpenSSHTestInfo -eq $null) { - Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment." + Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to setup test environment." } $testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite" if( -not (Test-path $testDir -PathType Container)) @@ -24,13 +25,13 @@ Describe "Tests for user Key file permission" -Tags "CI" { $server = $OpenSSHTestInfo["Target"] $userName = "$env:USERNAME@$env:USERDOMAIN" $keypassphrase = "testpassword" - - $systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") - $adminsAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") - $objUser = New-Object System.Security.Principal.NTAccount($ssouser) - $pubKeyUserAccount = New-Object System.Security.Principal.NTAccount($pubKeyUser) - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - $everyone = New-Object System.Security.Principal.NTAccount("EveryOne") + + $systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid) + $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) + $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" + $objUserSid = Get-UserSID -User $ssouser + $everyoneSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::WorldSid) + $pubKeyUserAccountSid = Get-UserSID -User $pubKeyUser Add-PasswordSetting -Pass $keypassphrase } @@ -48,7 +49,7 @@ Describe "Tests for user Key file permission" -Tags "CI" { BeforeAll { $keyFileName = "sshtest_userPermTestkey_ed25519" $keyFilePath = Join-Path $testDir $keyFileName - Remove-Item -path "$keyFilePath*" -Force -ErrorAction Ignore + Remove-Item -path "$keyFilePath*" -Force -ErrorAction SilentlyContinue ssh-keygen.exe -t ed25519 -f $keyFilePath -P $keypassphrase $pubKeyUserProfilePath = Join-Path $pubKeyUserProfile .ssh @@ -58,22 +59,22 @@ Describe "Tests for user Key file permission" -Tags "CI" { $testAuthorizedKeyPath = Join-Path $pubKeyUserProfilePath authorized_keys Copy-Item "$keyFilePath.pub" $testAuthorizedKeyPath -Force -ErrorAction SilentlyContinue - Adjust-UserKeyFileACL -FilePath $testAuthorizedKeyPath -Owner $pubKeyUserAccount -OwnerPerms "Read, Write" - Add-PermissionToFileACL -FilePath $testAuthorizedKeyPath -User "NT Service\sshd" -Perm "Read" + Repair-AuthorizedKeyPermission -FilePath $testAuthorizedKeyPath -confirm:$false $tI=1 } AfterAll { if(Test-Path $testAuthorizedKeyPath) { - Remove-Item $testAuthorizedKeyPath -Force -ErrorAction Ignore + Remove-Item $testAuthorizedKeyPath -Force -ErrorAction SilentlyContinue } if(Test-Path $pubKeyUserProfilePath) { - Remove-Item $pubKeyUserProfilePath -Recurse -Force -ErrorAction Ignore + Remove-Item $pubKeyUserProfilePath -Recurse -Force -ErrorAction SilentlyContinue } $tC++ } It "$tC.$tI-ssh with private key file -- positive (Secured private key owned by current user)" { - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $currentUser -OwnerPerms "Read, Write" + Repair-FilePermission -FilePath $keyFilePath -Owners $currentUserSid -FullAccessNeeded $adminsSid,$systemSid,$currentUserSid -confirm:$false + #Run $o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234 $o | Should Be "1234" @@ -81,7 +82,7 @@ Describe "Tests for user Key file permission" -Tags "CI" { It "$tC.$tI-ssh with private key file -- positive(Secured private key owned by Administrators group and current user has no explicit ACE)" { #setup to have local admin group as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $adminsAccount -OwnerPerms "FullControl" + Repair-FilePermission -FilePath $keyFilePath -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run $o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234 @@ -90,8 +91,7 @@ Describe "Tests for user Key file permission" -Tags "CI" { It "$tC.$tI-ssh with private key file -- positive(Secured private key owned by Administrators group and current user has explicit ACE)" { #setup to have local admin group as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $adminsAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $keyFilePath -User $currentUser -Perm "Read" + Repair-FilePermission -FilePath $keyFilePath -Owners $adminsSid -FullAccessNeeded $adminsSid,$systemSid -ReadAccessNeeded $currentUserSid -confirm:$false #Run $o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234 @@ -100,8 +100,7 @@ Describe "Tests for user Key file permission" -Tags "CI" { It "$tC.$tI-ssh with private key file -- positive (Secured private key owned by local system)" { #setup to have local system as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $systemAccount -OwnerPerms "FullControl" - Add-PermissionToFileACL -FilePath $keyFilePath -User $adminsAccount -Perm "Read" + Repair-FilePermission -FilePath $keyFilePath -Owners $systemSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run $o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234 @@ -109,11 +108,8 @@ Describe "Tests for user Key file permission" -Tags "CI" { } It "$tC.$tI-ssh with private key file -- negative(other account can access private key file)" { - #setup to have current user as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $currentUser -OwnerPerms "Read, Write" - - #add ssouser to access the private key - Add-PermissionToFileACL -FilePath $keyFilePath -User $objUser -Perm "Read" + #setup to have current user as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owners $currentUserSid -FullAccessNeeded $currentUser,$adminsSid,$systemSid -ReadAccessNeeded $objUserSid -confirm:$false #Run $o = ssh -p $port -i $keyFilePath -E $logPath $pubKeyUser@$server echo 1234 @@ -123,9 +119,8 @@ Describe "Tests for user Key file permission" -Tags "CI" { } It "$tC.$tI-ssh with private key file -- negative(the private key has wrong owner)" { - #setup to have ssouser as owner and grant it full control - Set-FileOwnerAndACL -FilePath $keyFilePath -Owner $objUser -OwnerPerms "Read, Write" - Add-PermissionToFileACL -FilePath $keyFilePath -User $adminsAccount -Perm "FullControl" + #setup to have ssouser as owner and grant it full control + Repair-FilePermission -FilePath $keyFilePath -Owners $objUserSid -FullAccessNeeded $objUserSid,$adminsSid,$systemSid -ReadAccessNeeded $objUserSid -confirm:$false $o = ssh -p $port -i $keyFilePath -E $logPath $pubKeyUser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 diff --git a/servconf.c b/servconf.c index 39a00a1..32d6ea3 100644 --- a/servconf.c +++ b/servconf.c @@ -605,8 +605,9 @@ derelativise_path(const char *path) return xstrdup("none"); expanded = tilde_expand_filename(path, getuid()); #ifdef WINDOWS - /* Windows absolute paths have a drive letter followed by :*/ - if (*expanded != '\0' && expanded[1] == ':') + /* Windows absolute paths - \abc, /abc, c:\abc, c:/abc*/ + if (*expanded == '/' || *expanded == '\\' || + (*expanded != '\0' && expanded[1] == ':')) #else /* !WINDOWS */ if (*expanded == '/') #endif /* !WINDOWS */ diff --git a/session.c b/session.c index 67720e7..1b40cb9 100644 --- a/session.c +++ b/session.c @@ -477,6 +477,9 @@ int do_exec_windows(Session *s, const char *command, int pty) { *cmd = '\0'; } + /* load user profile */ + mm_load_profile(s->pw->pw_name, ((INT_PTR)s->authctxt->auth_token) & 0xffffffff); + /* start the process */ { memset(&si, 0, sizeof(STARTUPINFO)); @@ -492,7 +495,7 @@ int do_exec_windows(Session *s, const char *command, int pty) { si.hStdError = (HANDLE)w32_fd_to_handle(pipeerr[1]); si.lpDesktop = NULL; - hToken = s->authctxt->methoddata; + hToken = s->authctxt->auth_token; debug("Executing command: %s", exec_command); UTF8_TO_UTF16_FATAL(exec_command_w, exec_command); diff --git a/sftp.c b/sftp.c index f30fb94..856de71 100644 --- a/sftp.c +++ b/sftp.c @@ -296,9 +296,14 @@ static void local_do_shell(const char *args) { #ifdef WINDOWS - /* execute via system call in Windows*/ - if (!*args) { - args = (char *) getenv("ComSpec"); // get name of Windows cmd shell + /* execute via system call in Windows*/ + char cmd_path[PATH_MAX] = { 0, }; + if (!*args){ + if (!GetSystemDirectory(cmd_path, sizeof(cmd_path))) + fatal("GetSystemDirectory failed"); + + strcat_s(cmd_path, PATH_MAX, "\\cmd.exe"); + args = cmd_path; } else { convertToBackslash((char *) args); } diff --git a/sshd.c b/sshd.c index 552f12e..f19275e 100644 --- a/sshd.c +++ b/sshd.c @@ -1765,9 +1765,7 @@ main(int ac, char **av) error("Could not connect to agent \"%s\": %s", options.host_key_agent, ssh_err(r)); } -#ifdef WINDOWS /* Windows version always needs and has agent running */ - have_agent = 1; -#endif + for (i = 0; i < options.num_host_key_files; i++) { if (options.host_key_files[i] == NULL) continue;