diff --git a/auth-passwd.c b/auth-passwd.c index 2c04017..8cd1966 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -231,7 +231,7 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) #elif defined(WINDOWS) HANDLE password_auth_token = NULL; -HANDLE process_custom_lsa_auth(char*, const char*, char*); +HANDLE process_custom_lsa_auth(const char*, const char*, const char*); void sys_auth_passwd_lsa(Authctxt *authctxt, const char *password) @@ -253,8 +253,7 @@ sys_auth_passwd_lsa(Authctxt *authctxt, const char *password) lsa_auth_pkg = utf16_to_utf8(lsa_auth_pkg_w); if (!lsa_auth_pkg) fatal("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w); - - debug("Authenticating using LSA Auth Package:%ls", lsa_auth_pkg_w); + password_auth_token = process_custom_lsa_auth(authctxt->pw->pw_name, password, lsa_auth_pkg); } } diff --git a/auth.c b/auth.c index 682136a..9db22bb 100644 --- a/auth.c +++ b/auth.c @@ -430,7 +430,7 @@ expand_authorized_keys(const char *filename, struct passwd *pw) #ifdef WINDOWS /* Return if the path is absolute. If not, prepend the '%h\\' */ - if ((strlen(file) > 1) && (file[1] == ':')) + if(is_absolute_path(file)) return (file); i = snprintf(ret, sizeof(ret), "%s\\%s", pw->pw_dir, file); diff --git a/channels.c b/channels.c index 5a3ee46..539b10a 100644 --- a/channels.c +++ b/channels.c @@ -3950,8 +3950,6 @@ channel_disable_adm_local_opens(struct ssh *ssh) void channel_clear_permitted_opens(struct ssh *ssh) { - if(ssh == NULL) - return; struct ssh_channels *sc = ssh->chanctxt; sc->permitted_opens = xrecallocarray(sc->permitted_opens, diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 index 029ef62..f9f5ebc 100644 --- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 @@ -197,7 +197,8 @@ function Start-OpenSSHBootstrap Write-BuildMsg -AsVerbose -Message "$gitCmdPath already present in Path environment variable" -Silent:$silent } - $nativeMSBuildPath = Get-VS2015BuildToolPath + $VS2015Path = Get-VS2015BuildToolPath + $VS2017Path = Get-VS2017BuildToolPath # Update machine environment path if ($newMachineEnvironmentPath -ne $machinePath) @@ -206,9 +207,24 @@ function Start-OpenSSHBootstrap } $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" - $packageName = "vcbuildtools" - If (($nativeMSBuildPath -eq $null) -or (-not (Test-Path $VcVars)) -or (-not (Test-Path $sdkPath))) { + $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\8.1\bin\x86\register_app.vbs" + #use vs2017 build tool if exists + if($VS2017Path -ne $null) + { + If (-not (Test-Path $sdkPath)) + { + $packageName = "windows-sdk-8.1" + Write-BuildMsg -AsInfo -Message "$packageName not present. Installing $packageName ..." + choco install $packageName -y --force --limitoutput --execution-timeout 10000 2>&1 >> $script:BuildLogFile + } + + if(-not (Test-Path $VcVars)) + { + Write-BuildMsg -AsError -ErrorAction Stop -Message "VC++ 2015.3 v140 toolset are not installed." + } + } + elseIf (($VS2015Path -eq $null) -or (-not (Test-Path $VcVars)) -or (-not (Test-Path $sdkPath))) { + $packageName = "vcbuildtools" 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 @@ -234,9 +250,9 @@ function Start-OpenSSHBootstrap Write-BuildMsg -AsError -ErrorAction Stop -Message "User choose not to restart the machine to apply the changes." } } - else + elseif($errorCode -ne 0) { - Write-BuildMsg -AsError -ErrorAction Stop -Message "$packageName installation failed with error code $errorCode" + Write-BuildMsg -AsError -ErrorAction Stop -Message "$packageName installation failed with error code $errorCode." } } else @@ -244,14 +260,11 @@ function Start-OpenSSHBootstrap Write-BuildMsg -AsVerbose -Message 'VC++ 2015 Build Tools already present.' } - if($NativeHostArch.ToLower().Startswith('arm')) - { - $nativeMSBuildPath = Get-VS2017BuildToolPath - If ($nativeMSBuildPath -eq $null) - { - #todo, install vs 2017 build tools - Write-BuildMsg -AsError -ErrorAction Stop -Message "The required msbuild 15.0 is not installed on the machine." - } + if($NativeHostArch.ToLower().Startswith('arm') -and ($VS2017Path -eq $null)) + { + + #todo, install vs 2017 build tools + Write-BuildMsg -AsError -ErrorAction Stop -Message "The required msbuild 15.0 is not installed on the machine." } if($OneCore -or ($NativeHostArch.ToLower().Startswith('arm'))) @@ -268,7 +281,7 @@ function Start-OpenSSHBootstrap # Ensure the VS C toolset is installed if ($null -eq $env:VS140COMNTOOLS) { - Write-BuildMsg -AsError -ErrorAction Stop -Message "Cannot find Visual Studio 2015 Environment variable VS140COMNTOOlS" + Write-BuildMsg -AsError -ErrorAction Stop -Message "Cannot find Visual Studio 2015 Environment variable VS140COMNTOOlS." } $item = Get-Item(Join-Path -Path $env:VS140COMNTOOLS -ChildPath '../../vc') @@ -314,7 +327,7 @@ function Copy-LibreSSLSDK Copy-Item -Container -Path $sourcePath -Destination $PSScriptRoot -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable e if($e -ne $null) { - Write-BuildMsg -AsError -ErrorAction Stop -Message "Copy LibreSSLSDK from $sourcePath to $PSScriptRoot failed" + Write-BuildMsg -AsError -ErrorAction Stop -Message "Copy LibreSSLSDK from $sourcePath to $PSScriptRoot failed." } } @@ -330,7 +343,8 @@ function Start-OpenSSHPackage [string]$Configuration = "Release", # Copy payload to DestinationPath instead of packaging - [string]$DestinationPath = "" + [string]$DestinationPath = "", + [switch]$NoOpenSSL ) [System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot @@ -390,7 +404,10 @@ function Start-OpenSSHPackage #copy libcrypto dll $libreSSLSDKPath = Join-Path $PSScriptRoot $script:libreSSLSDKStr - Copy-Item -Path $(Join-Path $libreSSLSDKPath "$NativeHostArch\libcrypto.dll") -Destination $packageDir -Force -ErrorAction Stop + if (-not $NoOpenSSL.IsPresent) + { + Copy-Item -Path $(Join-Path $libreSSLSDKPath "$NativeHostArch\libcrypto.dll") -Destination $packageDir -Force -ErrorAction Stop + } if ($DestinationPath -ne "") { if (Test-Path $DestinationPath) { @@ -400,7 +417,7 @@ function Start-OpenSSHPackage New-Item -ItemType Directory $DestinationPath -Force | Out-Null } Copy-Item -Path $packageDir\* -Destination $DestinationPath -Force -Recurse - Write-BuildMsg -AsInfo -Message "Copied payload to $DestinationPath" + Write-BuildMsg -AsInfo -Message "Copied payload to $DestinationPath." } else { Remove-Item ($packageDir + '.zip') -Force -ErrorAction SilentlyContinue @@ -411,7 +428,7 @@ function Start-OpenSSHPackage } else { - Write-BuildMsg -AsInfo -Message "Packaged Payload not compressed." + Write-BuildMsg -AsInfo -Message "Packaged Payload not compressed." } } Remove-Item $packageDir -Recurse -Force -ErrorAction SilentlyContinue @@ -470,7 +487,7 @@ function Start-OpenSSHBuild Remove-Item -Path $script:BuildLogFile -force } - Write-BuildMsg -AsInfo -Message "Starting Open SSH build; Build Log: $($script:BuildLogFile)" + Write-BuildMsg -AsInfo -Message "Starting Open SSH build; Build Log: $($script:BuildLogFile)." Start-OpenSSHBootstrap -OneCore:$OneCore @@ -523,17 +540,25 @@ function Start-OpenSSHBuild $xml.Project.PropertyGroup.WindowsSDKVersion = $win10SDKVer.ToString() $xml.Project.PropertyGroup.AdditionalDependentLibs = 'onecore.lib' $xml.Project.PropertyGroup.MinimalCoreWin = 'true' + + #Use onecore libcrypto binaries + $xml.Project.PropertyGroup."LibreSSL-x86-Path" = '$(SolutionDir)\LibreSSLSDK\onecore\x86\' + $xml.Project.PropertyGroup."LibreSSL-x64-Path" = '$(SolutionDir)\LibreSSLSDK\onecore\x64\' + $xml.Project.PropertyGroup."LibreSSL-arm-Path" = '$(SolutionDir)\LibreSSLSDK\onecore\arm\' + $xml.Project.PropertyGroup."LibreSSL-arm64-Path" = '$(SolutionDir)\LibreSSLSDK\onecore\arm64\' + $xml.Save($PathTargets) } $solutionFile = Get-SolutionFile -root $repositoryRoot.FullName - $cmdMsg = @("${solutionFile}", "/p:Platform=${NativeHostArch}", "/p:Configuration=${Configuration}", "/m", "/noconlog", "/nologo", "/fl", "/flp:LogFile=${script:BuildLogFile}`;Append`;Verbosity=diagnostic") - - if($NativeHostArch.ToLower().Startswith('arm')) + $cmdMsg = @("${solutionFile}", "/t:Rebuild", "/p:Platform=${NativeHostArch}", "/p:Configuration=${Configuration}", "/m", "/nologo", "/fl", "/flp:LogFile=${script:BuildLogFile}`;Append`;Verbosity=diagnostic") + if($silent) { - $msbuildCmd = Get-VS2017BuildToolPath + $cmdMsg += "/noconlog" } - else + + $msbuildCmd = Get-VS2017BuildToolPath + if($msbuildCmd -eq $null) { $msbuildCmd = Get-VS2015BuildToolPath } diff --git a/contrib/win32/openssh/OpenSSHTestHelper.psm1 b/contrib/win32/openssh/OpenSSHTestHelper.psm1 index 1d33594..c0fbdfc 100644 --- a/contrib/win32/openssh/OpenSSHTestHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHTestHelper.psm1 @@ -162,8 +162,14 @@ WARNING: Following changes will be made to OpenSSH configuration New-Item -ItemType Directory -Path $TestDataPath -Force -ErrorAction SilentlyContinue | out-null } - #Backup existing OpenSSH configuration + + if(-not (Test-Path $OpenSSHConfigPath -pathType Container)) + { + #starting the service will create ssh config folder + start-service sshd + } $backupConfigPath = Join-Path $OpenSSHConfigPath sshd_config.ori + #Backup existing OpenSSH configuration if (-not (Test-Path $backupConfigPath -PathType Leaf)) { Copy-Item (Join-Path $OpenSSHConfigPath sshd_config) $backupConfigPath -Force } @@ -244,7 +250,7 @@ WARNING: Following changes will be made to OpenSSH configuration $authorizedKeyPath = Join-Path $ssouserProfile .ssh\authorized_keys $testPubKeyPath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519.pub Copy-Item $testPubKeyPath $authorizedKeyPath -Force -ErrorAction SilentlyContinue - Repair-AuthorizedKeyPermission -FilePath $authorizedKeyPath -confirm:$false + Repair-AuthorizedKeyPermission -FilePath $authorizedKeyPath -confirm:$false copy-item (Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519) $Global:OpenSSHTestInfo["TestDataPath"] $testPriKeypath = Join-Path $Global:OpenSSHTestInfo["TestDataPath"] sshtest_userssokey_ed25519 diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs index a5e8184..eab416c 100644 --- a/contrib/win32/openssh/config.h.vs +++ b/contrib/win32/openssh/config.h.vs @@ -1565,7 +1565,7 @@ #define _PATH_PASSWD_PROG "/usr/bin/passwd" /* Specify location of ssh.pid */ -#define _PATH_SSH_PIDDIR "." +/* #undef _PATH_SSH_PIDDIR */ /* Define if we don't have struct __res_state in resolv.h */ #define __res_state state @@ -1693,6 +1693,7 @@ #define _PATH_PRIVSEP_CHROOT_DIR "." #define SSHDIR "__PROGRAMDATA__\\ssh" +#define _PATH_SSH_PIDDIR SSHDIR #define _PATH_SFTP_SERVER "sftp-server.exe" #define _PATH_SSH_PROGRAM "ssh.exe" #define _PATH_LS "dir" diff --git a/contrib/win32/openssh/paths.targets b/contrib/win32/openssh/paths.targets index 94f5221..20bea49 100644 --- a/contrib/win32/openssh/paths.targets +++ b/contrib/win32/openssh/paths.targets @@ -12,7 +12,7 @@ true libcrypto.lib; 8.1 - bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Netapi32.lib;Rpcrt4.lib + bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Netapi32.lib;Rpcrt4.lib;ntdll.lib false \ No newline at end of file diff --git a/contrib/win32/openssh/ssh-add-hostkey.ps1 b/contrib/win32/openssh/ssh-add-hostkey.ps1 deleted file mode 100644 index ab03f27..0000000 --- a/contrib/win32/openssh/ssh-add-hostkey.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -<# - Author: manoj.ampalam@microsoft.com - - Description: ssh-add.exe like Powershell utility to do host key management. - Input parameter mimic ssh-add.exe cmdline arguments. - - Host keys on Windows need to be registered as SYSTEM (i.e ssh-add.exe would - need to run as SYSTEM while talking to ssh-agent). This typically requires - an external utility like psexec. - - This script tries to use the Task scheduler option: - - registers a task scheduler task to run ssh-add.exe operation as SYSTEM - - actual output of ssh-add.exe is written to file (both stdout and stderr) - - Dumps the file contents to console - -#> - -# see https://linux.die.net/man/1/ssh-add for what the arguments mean -[CmdletBinding(DefaultParameterSetName='Add_key')] -Param( - [Parameter(ParameterSetName="List_fingerprints")] - [switch]$List_fingerprints, #ssh-add -l - [Parameter(ParameterSetName="List_pubkeys")] - [switch]$List_pubkeys, #ssh-add -L - [Parameter(ParameterSetName="Delete_key")] - [switch]$Delete_key, #ssh-add -d - [Parameter(ParameterSetName="Delete_all")] - [switch]$Delete_all, #ssh-add -D - [Parameter(Mandatory, Position=0, ParameterSetName="Delete_key")] - [Parameter(Mandatory, Position=0, ParameterSetName="Add_key")] - [ValidateNotNullOrEmpty()] - [string]$key -) - -$ssh_add_cmd = get-command ssh-add.exe -ErrorAction Ignore -if($ssh_add_cmd -eq $null) -{ - Throw "Cannot find ssh-add.exe." -} - -#create ssh-add cmdlinet -$ssh_add_cmd_str = $ssh_add_cmd.Path -if ($List_fingerprints) { $ssh_add_cmd_str += " -l" } -elseif ($List_pubkeys) { $ssh_add_cmd_str += " -L" } -elseif ($Delete_key) { $ssh_add_cmd_str += " -d $key" } -elseif ($Delete_all) { $ssh_add_cmd_str += " -D" } -else -{ - if ( ($key.Length -gt 0) -and (-not($key.Contains("host"))) ) { - Do { - $input = Read-Host -Prompt "Are you sure the provided key is a host key? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) - { - $input = 'Y' - } - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] - if (-not($result.ToLower().Startswith('y'))) { exit } - } - $ssh_add_cmd_str += " $key" -} - -#globals -$taskfolder = "\OpenSSHUtils\hostkey_tasks\" -$taskname = "hostkey_task" -$ssh_add_output = Join-Path (pwd).Path "ssh-add-hostkey-tmp.txt" -$task_argument = "/c `"$ssh_add_cmd_str > $ssh_add_output 2>&1 `"" - -#create TaskScheduler task -$ac = New-ScheduledTaskAction -Execute "cmd.exe" -Argument $task_argument -WorkingDirectory (pwd).path -$task = Register-ScheduledTask -TaskName $taskname -User System -Action $ac -TaskPath $taskfolder -Force - -#run the task -if (Test-Path $ssh_add_output) {Remove-Item $ssh_add_output -Force} -Start-ScheduledTask -TaskPath $taskfolder -TaskName $taskname - -#if still running, wait a little while for task to complete -$num = 0 -while ((Get-ScheduledTask -TaskName $taskname -TaskPath $taskfolder).State -eq "Running") -{ - sleep 1 - $num++ - if($num -gt 20) { break } -} -if (-not(Test-Path $ssh_add_output)) {throw "cannot find task output file. Something went WRONG!!! "} - -#dump output to console -Get-Content $ssh_add_output - -#cleanup task and output file -Remove-Item $ssh_add_output -Force -Unregister-ScheduledTask -TaskPath $taskfolder -TaskName $taskname -Confirm:$false - - - - diff --git a/contrib/win32/openssh/sshd_config b/contrib/win32/openssh/sshd_config index ccf5113..dfd050b 100644 --- a/contrib/win32/openssh/sshd_config +++ b/contrib/win32/openssh/sshd_config @@ -11,10 +11,10 @@ #ListenAddress 0.0.0.0 #ListenAddress :: -#HostKey /etc/ssh/ssh_host_rsa_key -#HostKey /etc/ssh/ssh_host_dsa_key -#HostKey /etc/ssh/ssh_host_ecdsa_key -#HostKey /etc/ssh/ssh_host_ed25519_key +#HostKey __PROGRAMDATA__/ssh/ssh_host_rsa_key +#HostKey __PROGRAMDATA__/ssh/ssh_host_dsa_key +#HostKey __PROGRAMDATA__/ssh/ssh_host_ecdsa_key +#HostKey __PROGRAMDATA__/ssh/ssh_host_ed25519_key # Ciphers and keying #RekeyLimit default none @@ -39,7 +39,7 @@ AuthorizedKeysFile .ssh/authorized_keys #AuthorizedPrincipalsFile none -# For this to work you will also need host keys in %windir%/programdata/openssh/config/ssh_known_hosts +# For this to work you will also need host keys in %programData%/ssh/ssh_known_hosts #HostbasedAuthentication no # Change to yes if you don't trust ~/.ssh/known_hosts for # HostbasedAuthentication diff --git a/contrib/win32/openssh/version.rc b/contrib/win32/openssh/version.rc index 4fb3216..e08cf30 100644 Binary files a/contrib/win32/openssh/version.rc and b/contrib/win32/openssh/version.rc differ diff --git a/contrib/win32/win32compat/lsa_missingdefs.h b/contrib/win32/win32compat/lsa_missingdefs.h new file mode 100644 index 0000000..87472fc --- /dev/null +++ b/contrib/win32/win32compat/lsa_missingdefs.h @@ -0,0 +1,55 @@ +/* +* Missing public definitions from Ntsecapi.h +*/ + + +typedef enum _LSA_SID_NAME_MAPPING_OPERATION_TYPE { + LsaSidNameMappingOperation_Add, + LsaSidNameMappingOperation_Remove, + LsaSidNameMappingOperation_AddMultiple, +} LSA_SID_NAME_MAPPING_OPERATION_TYPE, *PLSA_SID_NAME_MAPPING_OPERATION_TYPE; + +typedef enum _LSA_SID_NAME_MAPPING_OPERATION_ERROR { + LsaSidNameMappingOperation_Success, + LsaSidNameMappingOperation_NonMappingError, + LsaSidNameMappingOperation_NameCollision, + LsaSidNameMappingOperation_SidCollision, + LsaSidNameMappingOperation_DomainNotFound, + LsaSidNameMappingOperation_DomainSidPrefixMismatch, + LsaSidNameMappingOperation_MappingNotFound, +} LSA_SID_NAME_MAPPING_OPERATION_ERROR, *PLSA_SID_NAME_MAPPING_OPERATION_ERROR; + +typedef struct _LSA_SID_NAME_MAPPING_OPERATION_ADD_INPUT { + UNICODE_STRING DomainName; + UNICODE_STRING AccountName; + PSID Sid; + ULONG Flags; +} LSA_SID_NAME_MAPPING_OPERATION_ADD_INPUT, *PLSA_SID_NAME_MAPPING_OPERATION_ADD_INPUT; + +typedef struct _LSA_SID_NAME_MAPPING_OPERATION_REMOVE_INPUT { + UNICODE_STRING DomainName; + UNICODE_STRING AccountName; +} LSA_SID_NAME_MAPPING_OPERATION_REMOVE_INPUT, *PLSA_SID_NAME_MAPPING_OPERATION_REMOVE_INPUT; + +typedef union _LSA_SID_NAME_MAPPING_OPERATION_INPUT { + LSA_SID_NAME_MAPPING_OPERATION_ADD_INPUT AddInput; + LSA_SID_NAME_MAPPING_OPERATION_REMOVE_INPUT RemoveInput; +} LSA_SID_NAME_MAPPING_OPERATION_INPUT, *PLSA_SID_NAME_MAPPING_OPERATION_INPUT; + +typedef struct _LSA_SID_NAME_MAPPING_OPERATION_GENERIC_OUTPUT { + LSA_SID_NAME_MAPPING_OPERATION_ERROR ErrorCode; +} LSA_SID_NAME_MAPPING_OPERATION_GENERIC_OUTPUT, *PLSA_SID_NAME_MAPPING_OPERATION_GENERIC_OUTPUT; + +typedef LSA_SID_NAME_MAPPING_OPERATION_GENERIC_OUTPUT LSA_SID_NAME_MAPPING_OPERATION_ADD_OUTPUT, *PLSA_SID_NAME_MAPPING_OPERATION_ADD_OUTPUT; +typedef LSA_SID_NAME_MAPPING_OPERATION_GENERIC_OUTPUT LSA_SID_NAME_MAPPING_OPERATION_REMOVE_OUTPUT, *PLSA_SID_NAME_MAPPING_OPERATION_REMOVE_OUTPUT; + +typedef union _LSA_SID_NAME_MAPPING_OPERATION_OUTPUT { + LSA_SID_NAME_MAPPING_OPERATION_ADD_OUTPUT AddOutput; + LSA_SID_NAME_MAPPING_OPERATION_REMOVE_OUTPUT RemoveOutput; +} LSA_SID_NAME_MAPPING_OPERATION_OUTPUT, *PLSA_SID_NAME_MAPPING_OPERATION_OUTPUT; + +NTSTATUS WINAPI LsaManageSidNameMapping( + LSA_SID_NAME_MAPPING_OPERATION_TYPE OpType, + PLSA_SID_NAME_MAPPING_OPERATION_INPUT OpInput, + PLSA_SID_NAME_MAPPING_OPERATION_OUTPUT *OpOutput +); diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 9ce5e87..79eb1b6 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -1460,9 +1460,86 @@ int is_absolute_path(char *path) { int retVal = 0; - if (*path == '/' || *path == '\\' || (*path != '\0' && path[1] == ':') || + if(*path == '\"') /* skip double quote if path is "c:\abc" */ + path++; + + if (*path == '/' || *path == '\\' || (*path != '\0' && isalpha(*path) && path[1] == ':') || ((strlen(path) >= strlen(PROGRAM_DATA)) && (memcmp(path, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0))) retVal = 1; - + return retVal; } + +/* return -1 - in case of failure, 0 - success */ +int +create_directory_withsddl(char *path, char *sddl) +{ + struct stat st; + if (stat(path, &st) < 0) { + PSECURITY_DESCRIPTOR pSD = NULL; + SECURITY_ATTRIBUTES sa; + memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = FALSE; + + wchar_t *path_w = utf8_to_utf16(path); + if (!path_w) { + error("%s utf8_to_utf16() has failed to convert string:%s", __func__, path); + return -1; + } + + wchar_t *sddl_w = utf8_to_utf16(sddl); + if (!sddl_w) { + error("%s utf8_to_utf16() has failed to convert string:%s", __func__, sddl); + return -1; + } + + if (ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl_w, SDDL_REVISION, &pSD, NULL) == FALSE) { + error("ConvertStringSecurityDescriptorToSecurityDescriptorW failed with error code %d", GetLastError()); + return -1; + } + + if (IsValidSecurityDescriptor(pSD) == FALSE) { + error("IsValidSecurityDescriptor return FALSE"); + return -1; + } + + sa.lpSecurityDescriptor = pSD; + if (!CreateDirectoryW(path_w, &sa)) { + error("Failed to create directory:%ls error:%d", path_w, GetLastError()); + return -1; + } + } + + return 0; +} + +/* return -1 - in case of failure, 0 - success */ +int +copy_file(char *source, char *destination) +{ + if (!source || !destination) return 0; + + struct stat st; + if ((stat(source, &st) >= 0) && (stat(destination, &st) < 0)) { + wchar_t *source_w = utf8_to_utf16(source); + if (!source_w) { + error("%s utf8_to_utf16() has failed to convert string:%s", __func__, source_w); + return -1; + } + + wchar_t *destination_w = utf8_to_utf16(destination); + if (!destination_w) { + error("%s utf8_to_utf16() has failed to convert string:%s", __func__, destination_w); + return -1; + } + + if (!CopyFileW(source_w, destination_w, FALSE)) { + error("Failed to copy %ls to %ls, error:%d", source_w, destination_w, GetLastError()); + return -1; + } + } + + return 0; +} + diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h index b7d39c8..f66bfff 100644 --- a/contrib/win32/win32compat/misc_internal.h +++ b/contrib/win32/win32compat/misc_internal.h @@ -2,7 +2,7 @@ #include #define PATH_MAX MAX_PATH - +#define SSH_REGISTRY_ROOT L"SOFTWARE\\OpenSSH" #define GOTO_CLEANUP_IF(_cond_,_err_) do { \ if ((_cond_)) { \ hr = _err_; \ @@ -40,3 +40,5 @@ int get_machine_domain_name(wchar_t *domain, int size); char* get_program_data_path(); HANDLE get_user_token(char* user); int load_user_profile(HANDLE user_token, char* user); +int copy_file(char *source, char *destination); +int create_directory_withsddl(char *path, char *sddl); \ No newline at end of file diff --git a/contrib/win32/win32compat/pwd.c b/contrib/win32/win32compat/pwd.c index f4e3f04..b776449 100644 --- a/contrib/win32/win32compat/pwd.c +++ b/contrib/win32/win32compat/pwd.c @@ -111,6 +111,8 @@ get_passwd(const char *user_utf8, LPWSTR user_sid) int tmp_len = PATH_MAX; PDOMAIN_CONTROLLER_INFOW pdc = NULL; DWORD dsStatus, uname_upn_len = 0, uname_len = 0, udom_len = 0; + wchar_t wmachine_name[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD wmachine_name_len = MAX_COMPUTERNAME_LENGTH + 1; errno_t r = 0; errno = 0; @@ -135,6 +137,14 @@ get_passwd(const char *user_utf8, LPWSTR user_sid) udom_utf16 = NULL; } + if (udom_utf16) { + /* this should never fail */ + GetComputerNameW(wmachine_name, &wmachine_name_len); + /* If this is a local account (domain part and computer name are the same), strip out domain */ + if (_wcsicmp(udom_utf16, wmachine_name) == 0) + udom_utf16 = NULL; + } + if (user_sid == NULL) { NET_API_STATUS status; if ((status = NetUserGetInfo(udom_utf16, uname_utf16, 23, &user_info)) != NERR_Success) { diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index 31f1619..41847f5 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -1790,7 +1790,7 @@ wmain(int ac, wchar_t **av) } memset(&job_info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); - job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK; if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)) || !AssignProcessToJobObject(job, GetCurrentProcess())) { diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h index e5fe79c..d8943c6 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.h +++ b/contrib/win32/win32compat/ssh-agent/agent.h @@ -1,11 +1,11 @@ #include #include #include "Debug.h" +#include "misc_internal.h" #define MAX_MESSAGE_SIZE 256 * 1024 -#define SSH_ROOT L"SOFTWARE\\OpenSSH" -#define SSH_AGENT_ROOT SSH_ROOT L"\\Agent" +#define SSH_AGENT_ROOT SSH_REGISTRY_ROOT L"\\Agent" #define SSH_KEYS_KEY L"Keys" #define SSH_KEYS_ROOT SSH_AGENT_ROOT L"\\" SSH_KEYS_KEY diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index 93527db..f2e46d1 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -37,6 +37,7 @@ #include "inc\fcntl.h" #include "inc\sys\un.h" #include "inc\utf.h" +#include "inc\stdio.h" #include "w32fd.h" #include "signal_internal.h" @@ -953,16 +954,14 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL int add_module_path = 0, ret = -1; /* should module path be added */ - do { - if (!cmd) - break; - t = cmd; - if (*t == '\"') - t++; - if (t[0] == '\0' || t[0] == '\\' || t[0] == '.' || t[1] == ':') - break; + if (!cmd) { + error("%s invalid argument cmd:%s", __func__, cmd); + return -1; + } + + t = cmd; + if (!is_absolute_path(t)) add_module_path = 1; - } while (0); /* compute total cmdline len*/ if (add_module_path) @@ -1040,6 +1039,7 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL } else { errno = GetLastError(); + error("%s failed error:%d", (as_user?"CreateProcessAsUserW":"CreateProcessW"), GetLastError()); goto cleanup; } diff --git a/contrib/win32/win32compat/win32_usertoken_utils.c b/contrib/win32/win32compat/win32_usertoken_utils.c index 7cb6fd2..82b16f9 100644 --- a/contrib/win32/win32compat/win32_usertoken_utils.c +++ b/contrib/win32/win32compat/win32_usertoken_utils.c @@ -35,11 +35,16 @@ #include #include #include +#include + #include "inc\utf.h" #include "logonuser.h" #include +#include +#include #include #include "misc_internal.h" +#include "lsa_missingdefs.h" #include "Debug.h" #pragma warning(push, 3) @@ -116,7 +121,7 @@ done: #define MAX_PW_LEN 64 static HANDLE -generate_user_token(wchar_t* user_cpn) { +generate_s4u_user_token(wchar_t* user_cpn) { HANDLE lsa_handle = 0, token = 0; LSA_OPERATIONAL_MODE mode; ULONG auth_package_id; @@ -223,14 +228,14 @@ done: } HANDLE -process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) +process_custom_lsa_auth(const char* user, const char* pwd, const char* lsa_pkg) { - wchar_t *userw = NULL, *pwdw = NULL, *domw = NULL, *tmp, *providerw = NULL; + wchar_t *providerw = NULL; HANDLE token = NULL, lsa_handle = NULL; LSA_OPERATIONAL_MODE mode; ULONG auth_package_id, logon_info_size = 0; NTSTATUS ret, subStatus; - wchar_t *logon_info = NULL; + wchar_t *logon_info_w = NULL; LSA_STRING logon_process_name, lsa_auth_package_name, originName; TOKEN_SOURCE sourceContext; PVOID pProfile = NULL; @@ -238,19 +243,34 @@ process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) QUOTA_LIMITS quotas; DWORD cbProfile; int retVal = -1; + char *domain = NULL, *logon_info = NULL, user_name[UNLEN] = { 0, }, *tmp = NULL; - debug("LSA auth request, user:%s lsa_pkg:%s ", user, lsa_pkg); + debug3("LSA auth request, user:%s lsa_pkg:%s ", user, lsa_pkg); - if ((userw = utf8_to_utf16(user)) == NULL || - (pwdw = utf8_to_utf16(pwd)) == NULL) { - debug("out of memory"); - goto done; + logon_info_size = (ULONG)(strlen(user) + strlen(pwd) + 2); // 1 - ";", 1 - "\0" + strcpy_s(user_name, _countof(user_name), user); + if (tmp = strstr(user_name, "@")) { + domain = tmp + 1; + *tmp = '\0'; + logon_info_size++; // 1 - ";" } - /* split user and domain */ - if ((tmp = wcschr(userw, L'@')) != NULL) { - domw = tmp + 1; - *tmp = L'\0'; + logon_info = malloc(logon_info_size); + if(!logon_info) + fatal("%s out of memory", __func__); + + strcpy_s(logon_info, logon_info_size, user_name); + strcat_s(logon_info, logon_info_size, ";"); + strcat_s(logon_info, logon_info_size, pwd); + + if (domain) { + strcat_s(logon_info, logon_info_size, ";"); + strcat_s(logon_info, logon_info_size, domain); + } + + if (NULL == (logon_info_w = utf8_to_utf16(logon_info))) { + error("utf8_to_utf16 failed to convert %s", logon_info); + goto done; } /* call into LSA provider , get and duplicate token */ @@ -268,30 +288,19 @@ process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) goto done; } - logon_info_size = (ULONG)((wcslen(userw) + wcslen(pwdw) + wcslen(domw) + 3) * sizeof(wchar_t)); - logon_info = (wchar_t *)malloc(logon_info_size); - if (NULL == logon_info) - fatal("%s:out of memory", __func__); - - wcscpy_s(logon_info, logon_info_size, userw); - wcscat_s(logon_info, logon_info_size, L";"); - wcscat_s(logon_info, logon_info_size, pwdw); - wcscat_s(logon_info, logon_info_size, L";"); - wcscat_s(logon_info, logon_info_size, domw); - memcpy(sourceContext.SourceName, "sshd", sizeof(sourceContext.SourceName)); if (!AllocateLocallyUniqueId(&sourceContext.SourceIdentifier)) { error("AllocateLocallyUniqueId failed, error:%d", GetLastError()); goto done; - } + } if ((ret = LsaLogonUser(lsa_handle, &originName, Network, auth_package_id, - logon_info, - logon_info_size, + logon_info_w, + logon_info_size * sizeof(wchar_t), NULL, &sourceContext, &pProfile, @@ -299,8 +308,8 @@ process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) &logonId, &token, "as, - &subStatus)) != STATUS_SUCCESS) { - if(ret == STATUS_ACCOUNT_RESTRICTION) + &subStatus)) != STATUS_SUCCESS) { + if (ret == STATUS_ACCOUNT_RESTRICTION) error("LsaLogonUser failed, error:%x subStatus:%ld", ret, subStatus); else error("LsaLogonUser failed error:%x", ret); @@ -308,23 +317,23 @@ process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) goto done; } + debug3("LSA auth request is successful for user:%s ", user); retVal = 0; done: - /* delete allocated memory*/ if (lsa_handle) LsaDeregisterLogonProcess(lsa_handle); - if (logon_info) - free(logon_info); if (pProfile) LsaFreeReturnBuffer(pProfile); - if (userw) - free(userw); - if (pwdw) - free(pwdw); + if (logon_info) + free(logon_info); + if (logon_info_w) + free(logon_info_w); return token; } +HANDLE generate_sshd_virtual_token(); + HANDLE get_user_token(char* user) { HANDLE token = NULL; @@ -335,12 +344,18 @@ get_user_token(char* user) { goto done; } - if ((token = generate_user_token(user_utf16)) == 0) { + if (wcscmp(user_utf16, L"sshd") == 0) { + if ((token = generate_sshd_virtual_token()) != 0) + goto done; + debug3("unable to generate sshd virtual token, falling back to s4u"); + } + + if ((token = generate_s4u_user_token(user_utf16)) == 0) { error("unable to generate token for user %ls", user_utf16); /* work around for https://github.com/PowerShell/Win32-OpenSSH/issues/727 by doing a fake login */ LogonUserExExWHelper(L"FakeUser", L"FakeDomain", L"FakePasswd", LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL); - if ((token = generate_user_token(user_utf16)) == 0) { + if ((token = generate_s4u_user_token(user_utf16)) == 0) { error("unable to generate token on 2nd attempt for user %ls", user_utf16); goto done; } @@ -378,4 +393,209 @@ done: return r; } + +/* *** virtual account token generation logic ***/ + +char* LSAMappingErrorDetails[] = { + "LsaSidNameMappingOperation_Success", + "LsaSidNameMappingOperation_NonMappingError", + "LsaSidNameMappingOperation_NameCollision", + "LsaSidNameMappingOperation_SidCollision", + "LsaSidNameMappingOperation_DomainNotFound", + "LsaSidNameMappingOperation_DomainSidPrefixMismatch", + "LsaSidNameMappingOperation_MappingNotFound" +}; + +#define VIRTUALUSER_DOMAIN L"VIRTUAL USERS" +#define VIRTUALUSER_GROUP_NAME L"ALL VIRTUAL USERS" + +/* returns 0 on success -1 on failure */ +int +AddSidMappingToLsa(PUNICODE_STRING domain_name, + PUNICODE_STRING account_name, + PSID sid) +{ + LSA_SID_NAME_MAPPING_OPERATION_INPUT input = { 0 }; + PLSA_SID_NAME_MAPPING_OPERATION_OUTPUT p_output = NULL; + LSA_SID_NAME_MAPPING_OPERATION_ERROR op_result = + LsaSidNameMappingOperation_NonMappingError; + NTSTATUS status = STATUS_SUCCESS; + int ret = 0; + + input.AddInput.DomainName = *domain_name; + if (account_name) + input.AddInput.AccountName = *account_name; + input.AddInput.Sid = sid; + + status = LsaManageSidNameMapping(LsaSidNameMappingOperation_Add, + &input, + &p_output); + if (status != STATUS_SUCCESS) { + ret = -1; + if (p_output) { + op_result = p_output->AddOutput.ErrorCode; + if (op_result == LsaSidNameMappingOperation_NameCollision || op_result == LsaSidNameMappingOperation_SidCollision) + ret = 0; /* OK as it failed due to collision */ + else + error("LsaManageSidNameMapping failed with : %s \n", LSAMappingErrorDetails[op_result]); + } + else + error("LsaManageSidNameMapping failed with ntstatus: %d \n", status); + } + + /* TODO - Free p_output */ + /*if (p_output) + LsaFreeMemory(p_output);*/ + + return ret; +} + + +int RemoveVirtualAccountLSAMapping(PUNICODE_STRING domain_name, + PUNICODE_STRING account_name) +{ + int ret = 0; + + LSA_SID_NAME_MAPPING_OPERATION_INPUT input = { 0 }; + PLSA_SID_NAME_MAPPING_OPERATION_OUTPUT p_output = NULL; + PLSA_SID_NAME_MAPPING_OPERATION_REMOVE_INPUT remove_input = &input.RemoveInput; + + remove_input->DomainName = *domain_name; + if (account_name) + remove_input->AccountName = *account_name; + + NTSTATUS status = LsaManageSidNameMapping(LsaSidNameMappingOperation_Remove, + &input, + &p_output); + if (status != STATUS_SUCCESS) + ret = -1; + + /* TODO - Free p_output */ + /*if (p_output) + LsaFreeMemory(p_output);*/ + + return ret; +} + +void +InitUnicodeString(PUNICODE_STRING dest, PCWSTR source) +{ + dest->Buffer = source; + dest->Length = wcslen(source) * sizeof(wchar_t); + dest->MaximumLength = dest->Length + 2; +} + +HANDLE generate_sshd_virtual_token() +{ + SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; + UNICODE_STRING domain, group, account; + WCHAR va_name[32]; /* enough to accomodate sshd_123457890 */ + + PSID sid_domain = NULL, sid_group = NULL, sid_user = NULL; + HANDLE va_token = 0, va_token_restricted = 0; + + StringCchPrintfW(va_name, 32, L"%s_%d", L"sshd", GetCurrentProcessId()); + + InitUnicodeString(&domain, VIRTUALUSER_DOMAIN); + InitUnicodeString(&group, VIRTUALUSER_GROUP_NAME); + InitUnicodeString(&account, va_name); + + /* Initialize SIDs */ + /* domain SID - S-1-5-111 */ + if (!(AllocateAndInitializeSid(&nt_authority, + 1, + 111, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &sid_domain))) + goto cleanup; + + /* group SID - S-1-5-111-0 */ + if (!(AllocateAndInitializeSid(&nt_authority, + 2, + 111, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &sid_group))) + goto cleanup; + + /* + * account SID + * this is derived from higher RIDs in sshd service account SID to ensure there are no conflicts + * S-1-5-80-3847866527-469524349-687026318-516638107-1125189541 (Well Known group: NT SERVICE\sshd) + * Ex account SID - S-1-5-111-3847866527-469524349-687026318-516638107-1125189541-123 + */ + if (!(AllocateAndInitializeSid(&nt_authority, + 7, + 111, + 3847866527, + 469524349, + 687026318, + 516638107, + 1125189541, + GetCurrentProcessId(), + 0, + &sid_user))) + goto cleanup; + + /* Map the domain SID */ + if (AddSidMappingToLsa(&domain, NULL, sid_domain) != 0) + goto cleanup; + + /* Map the group SID */ + if (AddSidMappingToLsa(&domain, &group, sid_group) != 0) + goto cleanup; + + /* Map the user SID */ + if (AddSidMappingToLsa(&domain, &account, sid_user) != 0) + goto cleanup; + + /* Logon virtual and create token */ + if (!LogonUserExExWHelper( + va_name, + VIRTUALUSER_DOMAIN, + L"", + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_VIRTUAL, + NULL, + &va_token, + NULL, + NULL, + NULL, + NULL)) { + debug3("LogonUserExExW failed with %d \n", GetLastError()); + goto cleanup; + } + + /* remove all privileges */ + if (!CreateRestrictedToken(va_token, DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL, 0, NULL, &va_token_restricted )) + debug3("CreateRestrictedToken failed with %d \n", GetLastError()); + + CloseHandle(va_token); + +cleanup: + RemoveVirtualAccountLSAMapping(&domain, &account); + + if (sid_domain) + FreeSid(sid_domain); + if (sid_user) + FreeSid(sid_user); + if (sid_group) + FreeSid(sid_group); + + return va_token_restricted; +} + + + #pragma warning(pop) \ No newline at end of file diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index c98ec5a..1072839 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -35,6 +35,8 @@ #include #include #include +#include + #include "inc\utf.h" #include "misc_internal.h" @@ -99,7 +101,7 @@ static VOID WINAPI service_handler(DWORD dwControl) #define SSH_HOSTKEY_GEN_CMDLINE L"ssh-keygen -A" static void -prereq_setup() +generate_host_keys() { TOKEN_USER* info = NULL; DWORD info_len = 0, dwError = 0; @@ -127,7 +129,7 @@ prereq_setup() ui.usri1_priv = USER_PRIV_USER; ui.usri1_home_dir = NULL; ui.usri1_comment = NULL; - ui.usri1_flags = UF_SCRIPT; + ui.usri1_flags = UF_SCRIPT | UF_DONT_EXPIRE_PASSWD; ui.usri1_script_path = NULL; NetUserAdd(NULL, 1, (LPBYTE)&ui, &dwError); @@ -151,14 +153,96 @@ cleanup: free(info); } +/* +* 1) Create %programdata%\ssh - Administrator group(F), system(F), authorized users(RX). +* 2) Create %programdata%\ssh\logs - Administrator group(F), system(F) +* 3) copy \sshd_config_default to %programdata%\ssh\sshd_config +*/ +static void +create_prgdata_ssh_folder() +{ + /* create ssh cfg folder */ + char ssh_cfg_dir[PATH_MAX] = { 0, }; + strcpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), get_program_data_path()); + strcat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), "\\ssh"); + if (create_directory_withsddl(ssh_cfg_dir, "O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") < 0) { + printf("failed to create %s", ssh_cfg_dir); + exit(255); + } + + /* create logs folder */ + char logs_dir[PATH_MAX] = { 0, }; + strcat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir); + strcat_s(logs_dir, _countof(logs_dir), "\\logs"); + if (create_directory_withsddl(logs_dir, "O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)") < 0) { + printf("failed to create %s", logs_dir); + exit(255); + } + + /* COPY sshd_config_default to %programData%\openssh\sshd_config */ + char sshd_config_path[PATH_MAX] = { 0, }; + strcat_s(sshd_config_path, _countof(sshd_config_path), ssh_cfg_dir); + strcat_s(sshd_config_path, _countof(sshd_config_path), "\\sshd_config"); + struct stat st; + if (stat(sshd_config_path, &st) < 0) { + char sshd_config_default_path[PATH_MAX] = { 0, }; + strcat_s(sshd_config_default_path, _countof(sshd_config_default_path), w32_programdir()); + strcat_s(sshd_config_default_path, _countof(sshd_config_default_path), "\\sshd_config_default"); + + if (copy_file(sshd_config_default_path, sshd_config_path) < 0) { + printf("Failed to copy %s to %s, error:%d", sshd_config_default_path, sshd_config_path, GetLastError()); + exit(255); + } + } +} + +/* Create HKLM\Software\OpenSSH windows registry key */ +static void +create_openssh_registry_key() +{ + HKEY ssh_registry_root = NULL; + wchar_t* sddl_str; + SECURITY_ATTRIBUTES sa; + int r; + + memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(sa); + + /* + * SDDL - FullAcess to System and Builtin/Admins and restricted access to Authenticated users + * 0x12019b - FILE_GENERIC_READ/WRITE minus FILE_CREATE_PIPE_INSTANCE + */ + sddl_str = L"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x12019b;;;AU)"; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl_str, SDDL_REVISION_1, &sa.lpSecurityDescriptor, &sa.nLength)) { + printf("cannot convert sddl ERROR:%d", GetLastError()); + return; + } + + if ((r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SSH_REGISTRY_ROOT, 0, 0, 0, KEY_WRITE, &sa, &ssh_registry_root, 0)) == ERROR_SUCCESS) + RegCloseKey(ssh_registry_root); + else + printf("cannot create ssh root reg key, ERROR:%d", r); +} + +static void +prereq_setup() +{ + create_prgdata_ssh_folder(); + generate_host_keys(); + create_openssh_registry_key(); +} + int sshd_main(int argc, wchar_t **wargv) { char** argv = NULL; int i, r; _set_invalid_parameter_handler(invalid_parameter_handler); if (argc) { - if ((argv = malloc(argc * sizeof(char*))) == NULL) - fatal("out of memory"); + if ((argv = malloc(argc * sizeof(char*))) == NULL) { + printf("out of memory"); + exit(255); + } + for (i = 0; i < argc; i++) argv[i] = utf16_to_utf8(wargv[i]); } @@ -177,7 +261,7 @@ int wmain(int argc, wchar_t **wargv) { wchar_t* path_utf16; argc_original = argc; wargv_original = wargv; - + /* change current directory to sshd.exe root */ if ( (path_utf16 = utf8_to_utf16(w32_programdir())) == NULL) return -1; diff --git a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 index 885955f..0734698 100644 --- a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 +++ b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 @@ -17,42 +17,22 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue } - $fileName = "test.txt" - $logName = "sshdlog.txt" + $sshLogName = "test.txt" + $sshdLogName = "sshdlog.txt" $server = $OpenSSHTestInfo["Target"] $port = 47003 $ssouser = $OpenSSHTestInfo["SSOUser"] $PwdUser = $OpenSSHTestInfo["PasswdUser"] $ssouserProfile = $OpenSSHTestInfo["SSOUserProfile"] - Remove-Item -Path (Join-Path $testDir "*$fileName") -Force -ErrorAction SilentlyContinue + $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] + Remove-Item -Path (Join-Path $testDir "*$sshLogName") -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 - } - - $Taskfolder = "\OpenSSHTestTasks\" - $Taskname = "StartTestDaemon" - - function Start-SSHD-TestDaemon - { - param([string] $Arguments) - $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] - - $ac = New-ScheduledTaskAction -Execute (join-path $opensshbinpath "sshd") -WorkingDirectory $opensshbinpath -Argument $Arguments - $task = Register-ScheduledTask -TaskName $Taskname -User system -Action $ac -TaskPath $Taskfolder -Force - Start-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname - } - - function Stop-SSHD-TestDaemon - { - Stop-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname - #stop-scheduledTask does not wait for worker process to end. Kill it if still running. Logic below assume sshd service is running - $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] - (gps sshd).id | foreach { if ((-not($_ -eq $svcpid))) {Stop-Process $_ -Force} } - } + } } AfterEach { $tI++ } @@ -78,11 +58,13 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { $authorizedkeyPath = Join-Path $ssouserProfile .testssh\authorized_keys $Source = Join-Path $ssouserProfile .ssh\authorized_keys $testknownhosts = Join-path $PSScriptRoot testdata\test_known_hosts - Copy-Item $Source $ssouserSSHProfilePath -Force -ErrorAction Stop - + Copy-Item $Source $ssouserSSHProfilePath -Force -ErrorAction Stop Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false - - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + if(-not $skip) + { + Stop-SSHDTestDaemon + } + #add wrong password so ssh does not prompt password if failed with authorized keys Add-PasswordSetting -Pass "WrongPass" $tI=1 @@ -102,73 +84,73 @@ 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 + $sshlog = Join-Path $testDir "$tC.$tI.$sshLogName" + $sshdlog = Join-Path $testDir "$tC.$tI.$sshdLogName" + if(-not $skip) + { + Stop-SSHDTestDaemon + } } - It "$tC.$tI-authorized_keys-positive(pwd user is the owner and running process can access to the file)" { + It "$tC.$tI-authorized_keys-positive(pwd user is the owner and running process can access to the file)" -skip:$skip { #setup to have ssouser as owner and grant ssouser read and write, admins group, and local system full control Repair-FilePermission -Filepath $authorizedkeyPath -Owners $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" - } - It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by local system)" { + It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by local system)" -skip:$skip { #setup to have system as owner and grant it full control Repair-FilePermission -Filepath $authorizedkeyPath -Owner $systemSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" - } - It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd does not have explict ACE)" { + It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd does not have explict ACE)" -skip:$skip { #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-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" - } - It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd have explict ACE)" { + It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd have explict ACE)" -skip:$skip { #setup to have admin group as owner and grant it full control Repair-FilePermission -Filepath $authorizedkeyPath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon - $o | Should Be "1234" - + Stop-SSHDTestDaemon + $o | Should Be "1234" } - It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other admin user)" { + It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other admin user)" -skip:$skip { #setup to have current user (admin user) as owner and grant it full control Repair-FilePermission -Filepath $authorizedkeyPath -Owner $currentUserSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run - Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "Authentication refused." + Stop-SSHDTestDaemon + $sshlog | Should Contain "Permission denied" + $sshdlog | Should Contain "Authentication refused." } - It "$tC.$tI-authorized_keys-negative(other account can access private key file)" { + It "$tC.$tI-authorized_keys-negative(other account can access private key file)" -skip:$skip { #setup to have current user as owner and grant it full control Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false @@ -177,24 +159,26 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Set-FilePermission -FilePath $authorizedkeyPath -User $objPwdUserSid -Perm "Read" #Run - Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 - $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "Authentication refused." + Start-SSHDTestDaemon -workDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 + $LASTEXITCODE | Should Not Be 0 + Stop-SSHDTestDaemon + $sshlog | Should Contain "Permission denied" + $sshdlog | Should Contain "Authentication refused." } - It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other non-admin user)" { + It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other non-admin user)" -skip:$skip { #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-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" - ssh -p $port -E $FilePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $sshdlog" + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "Authentication refused." + Stop-SSHDTestDaemon + $sshlog | Should Contain "Permission denied" + $sshdlog | Should Contain "Authentication refused." } } } diff --git a/regress/pesterTests/CommonUtils.psm1 b/regress/pesterTests/CommonUtils.psm1 index ffcd1f8..08911b3 100644 --- a/regress/pesterTests/CommonUtils.psm1 +++ b/regress/pesterTests/CommonUtils.psm1 @@ -105,4 +105,52 @@ function Remove-PasswordSetting { if ($env:DISPLAY -eq 1) { Remove-Item env:\DISPLAY } Remove-item "env:SSH_ASKPASS" -ErrorAction SilentlyContinue +} + +$Taskfolder = "\OpenSSHTestTasks\" +$Taskname = "StartTestDaemon" + +function Start-SSHDTestDaemon +{ + param( + [string] $Arguments, + [string] $Workdir) + + $ac = New-ScheduledTaskAction -Execute (join-path $workdir "sshd") -WorkingDirectory $workdir -Argument $Arguments + $task = Register-ScheduledTask -TaskName $Taskname -User system -Action $ac -TaskPath $Taskfolder -Force + Start-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] + #sleep for 1 seconds for process to ready to listener + $num = 0 + while((Get-Process sshd | Where-Object {$_.Id -ne $svcpid}) -eq $null) + { + start-sleep 1 + $num++ + if($num -gt 30) { break } + } +} + +function Stop-SSHDTestDaemon +{ + $task = Get-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname -ErrorAction SilentlyContinue + if($task) + { + if($task.State -eq "Running") + { + Stop-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + } + Unregister-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname -Confirm:$false + } + #if still running, wait a little while for task to complete + #stop-scheduledTask does not wait for worker process to end. Kill it if still running. Logic below assume sshd service is running + $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] + Get-Process sshd -ErrorAction SilentlyContinue | Where-Object {$_.Id -ne $svcpid} | Stop-Process -Force -ErrorAction SilentlyContinue + $num = 0 + while((Get-Process sshd | Where-Object {$_.Id -ne $svcpid})) + { + # sshd process is still running; wait 1 more seconds" + start-sleep 1 + $num++ + if($num -gt 30) { break } + } } \ No newline at end of file diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1 index 66bc92c..38534ab 100644 --- a/regress/pesterTests/SSH.Tests.ps1 +++ b/regress/pesterTests/SSH.Tests.ps1 @@ -106,7 +106,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { It "$tC.$tI - test version" { iex "cmd /c `"ssh -V 2> $stderrFile`"" - $stderrFile | Should Contain "OpenSSH_" + $stderrFile | Should Contain "OpenSSH_for_Windows" } It "$tC.$tI - test help" { @@ -147,7 +147,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { $o | Should Be "1234" } - It "$tC.$tI - stdin from PS object" { + It "$tC.$tI - stdin from PS object" -skip:$skip { # execute this script that dumps the length of input data, on the remote end $str = "begin {} process { Write-Output `$input.Length} end { }" $EncodedText =[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($str)) @@ -157,7 +157,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { $o | Should Be "8" } - It "$tC.$tI - stream file in and out" { + It "$tC.$tI - stream file in and out" -skip:$skip { # prep a file of size > 10KB (https://github.com/PowerShell/Win32-OpenSSH/issues/908 was caught with such file size) $str = "" (1..100) | foreach {$str += "1234567890"} @@ -191,7 +191,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue } - It "$tC.$tI - default shell as powershell" { + It "$tC.$tI - default shell as powershell" -skip:$skip { $shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path if($shell_path -ne $null) { ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c" diff --git a/regress/pesterTests/SSHDConfig.tests.ps1 b/regress/pesterTests/SSHDConfig.tests.ps1 index 6b1c583..3c02e5b 100644 --- a/regress/pesterTests/SSHDConfig.tests.ps1 +++ b/regress/pesterTests/SSHDConfig.tests.ps1 @@ -14,39 +14,20 @@ Describe "Tests of sshd_config" -Tags "CI" { if( -not (Test-path $testDir -PathType Container)) { $null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue - } + } - $fileName = "test.txt" - $logName = "sshdlog.txt" + $sshLogName = "test.txt" + $sshdLogName = "sshdlog.txt" $server = $OpenSSHTestInfo["Target"] + $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] $port = 47003 - Remove-Item -Path (Join-Path $testDir "*$fileName") -Force -ErrorAction SilentlyContinue + Remove-Item -Path (Join-Path $testDir "*$sshLogName") -Force -ErrorAction SilentlyContinue Add-Type -AssemblyName System.DirectoryServices.AccountManagement $ContextName = $env:COMPUTERNAME $ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Machine $PrincipalContext = new-object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList @($ContextType, $ContextName) - $IdentityType = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName - $Taskfolder = "\OpenSSHTestTasks\" - $Taskname = "StartTestDaemon" - - function Start-SSHD-TestDaemon - { - param([string] $Arguments) - $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] - - $ac = New-ScheduledTaskAction -Execute (join-path $opensshbinpath "sshd") -WorkingDirectory $opensshbinpath -Argument $Arguments - $task = Register-ScheduledTask -TaskName $Taskname -User system -Action $ac -TaskPath $Taskfolder -Force - Start-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname - } - - function Stop-SSHD-TestDaemon - { - Stop-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname - #stop-scheduledTask does not wait for worker process to end. Kill it if still running. Logic below assume sshd service is running - $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] - (gps sshd).id | foreach { if ((-not($_ -eq $svcpid))) {Stop-Process $_ -Force} } - } + $IdentityType = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName function Add-LocalUser { @@ -133,6 +114,10 @@ Describe "Tests of sshd_config" -Tags "CI" { } $platform = Get-Platform $skip = ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -le 2) + if(-not $skip) + { + Stop-SSHDTestDaemon + } if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) { #suppress the firewall blocking dialogue on win7 @@ -142,7 +127,7 @@ Describe "Tests of sshd_config" -Tags "CI" { AfterEach { $tI++ } - AfterAll { + AfterAll { $PrincipalContext.Dispose() if(($platform -eq [PlatformType]::Windows) -and ($psversiontable.BuildVersion.Major -le 6)) { @@ -190,8 +175,12 @@ Describe "Tests of sshd_config" -Tags "CI" { } BeforeEach { - $filePath = Join-Path $testDir "$tC.$tI.$fileName" - $logPath = Join-Path $testDir "$tC.$tI.$logName" + $sshlog = Join-Path $testDir "$tC.$tI.$sshLogName" + $sshdlog = Join-Path $testDir "$tC.$tI.$sshdLogName" + if(-not $skip) + { + Stop-SSHDTestDaemon + } } AfterAll { @@ -199,148 +188,162 @@ Describe "Tests of sshd_config" -Tags "CI" { $tC++ } - It "$tC.$tI-User with full name in the list of AllowUsers" { + It "$tC.$tI-User with full name in the list of AllowUsers" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $allowUser1 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser1@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser1 -GroupName $allowGroup1 } - It "$tC.$tI-User with * wildcard" { + It "$tC.$tI-User with * wildcard" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $allowUser2 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser2@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser2 -GroupName $allowGroup1 } - It "$tC.$tI-User with ? wildcard" { + It "$tC.$tI-User with ? wildcard" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $allowUser3 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser3@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 - Stop-SSHD-TestDaemon + Stop-SSHDTestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser3 -GroupName $allowGroup1 } - It "$tC.$tI-User with full name in the list of AllowUsers but not in any AllowGroups" { + It "$tC.$tI-User with full name in the list of AllowUsers but not in any AllowGroups" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-LocalUser -UserName $allowUser4 -Password $password - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $allowUser4@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $allowUser4@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because not in any group" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because not in any group" } - It "$tC.$tI-User with full name in the list of DenyUsers" { + It "$tC.$tI-User with full name in the list of DenyUsers" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $denyUser1 -Password $password -GroupName $allowGroup1 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser1@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $denyUser1@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because listed in DenyUsers" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because listed in DenyUsers" Remove-UserFromLocalGroup -UserName $denyUser1 -GroupName $allowGroup1 } - It "$tC.$tI-User with * wildcard in the list of DenyUsers" { + It "$tC.$tI-User with * wildcard in the list of DenyUsers" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $denyUser2 -Password $password -GroupName $allowGroup1 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser2@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $denyUser2@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because listed in DenyUsers" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because listed in DenyUsers" Remove-UserFromLocalGroup -UserName $denyUser2 -GroupName $allowGroup1 } - It "$tC.$tI-User with ? wildcard in the list of DenyUsers" { + It "$tC.$tI-User with ? wildcard in the list of DenyUsers" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $denyUser3 -Password $password -GroupName $allowGroup1 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser3@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $denyUser3@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because not listed in AllowUsers" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because not listed in AllowUsers" Remove-UserFromLocalGroup -UserName $denyUser3 -GroupName $allowGroup1 } - It "$tC.$tI-User is listed in the list of AllowUsers but also in a full name DenyGroups and AllowGroups" { + It "$tC.$tI-User is listed in the list of AllowUsers but also in a full name DenyGroups and AllowGroups" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $localuser1 -Password $password -GroupName $allowGroup1 Add-UserToLocalGroup -UserName $localuser1 -Password $password -GroupName $denyGroup1 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser1@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $localuser1@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because a group is listed in DenyGroups" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser1 -GroupName $allowGroup1 Remove-UserFromLocalGroup -UserName $localuser1 -GroupName $denyGroup1 } - It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard * DenyGroups" { + It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard * DenyGroups" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $localuser2 -Password $password -GroupName $denyGroup2 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser2@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $localuser2@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because a group is listed in DenyGroups" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser2 -GroupName $denyGroup2 } - It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard ? DenyGroups" { + It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard ? DenyGroups" -skip:$skip { #Run - Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" Add-UserToLocalGroup -UserName $localuser3 -Password $password -GroupName $denyGroup3 - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser3@$server echo 1234 + ssh -p $port -E $sshlog -o "UserKnownHostsFile $testknownhosts" $localuser3@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - Stop-SSHD-TestDaemon - $logPath | Should Contain "not allowed because a group is listed in DenyGroups" + Stop-SSHDTestDaemon + $sshdlog | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser3 -GroupName $denyGroup3 } + + It "$tC.$tI - Match User block with ForceCommand" -skip:$skip { + Start-SSHDTestDaemon -WorkDir $opensshbinpath -Arguments "-d -f $sshdConfigPath -E $sshdlog" + $matchuser = "matchuser" + Add-UserToLocalGroup -UserName $matchuser -Password $password -GroupName $allowGroup1 + + $o = ssh -p $port -T -o "UserKnownHostsFile $testknownhosts" $matchuser@$server randomcommand + # Match block's ForceCommand returns output of "whoami & set SSH_ORIGINAL_COMMAND" + $o[0].Contains($matchuser) | Should Be $true + $o[1].Contains("randomcommand") | Should Be $true + + Stop-SSHDTestDaemon + Remove-UserFromLocalGroup -UserName $matchuser -GroupName $allowGroup1 + } #> } } diff --git a/regress/pesterTests/testdata/SSHD_Config b/regress/pesterTests/testdata/SSHD_Config index b50697a..089ce7d 100644 --- a/regress/pesterTests/testdata/SSHD_Config +++ b/regress/pesterTests/testdata/SSHD_Config @@ -113,6 +113,9 @@ Subsystem sftp sftp-server.exe -l DEBUG3 PubkeyAcceptedKeyTypes ssh-ed25519* DenyUsers denyuser1 deny*2 denyuse?3, -AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin +AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin matchuser DenyGroups denygroup1 denygr*p2 deny?rou?3 AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* + +Match User matchuser + ForceCommand cmd.exe /c "whoami & set SSH_ORIGINAL_COMMAND" diff --git a/sftp.c b/sftp.c index 22a18a2..47c7ca1 100644 --- a/sftp.c +++ b/sftp.c @@ -397,7 +397,7 @@ make_absolute(char *p, const char *pwd) * Need to follow up with community if this makes sense in common code */ char *s1, *s2; - if (p && p[0] != '/' && (p[0] == '\0' || p[1] != ':')) { + if (!is_absolute_path(p)) { abs_str = path_append(pwd, p); free(p); p = abs_str; diff --git a/ssh-keygen.c b/ssh-keygen.c index 98e7f14..026cf26 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1544,9 +1544,16 @@ do_change_comment(struct passwd *pw) fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) fatal("Could not save your public key in %s", identity_file); +#ifdef WINDOWS + /* Windows POSIX adpater does not support fdopen() on open(file)*/ + close(fd); + if ((f = fopen(identity_file, "w")) == NULL) + fatal("fopen %s failed: %s", identity_file, strerror(errno)); +#else /* !WINDOWS */ f = fdopen(fd, "w"); if (f == NULL) fatal("fdopen %s failed: %s", identity_file, strerror(errno)); +#endif /* !WINDOWS */ if ((r = sshkey_write(public, f)) != 0) fatal("write key failed: %s", ssh_err(r)); sshkey_free(public); diff --git a/sshconnect.c b/sshconnect.c index c950c4d..cfd6f4e 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -199,10 +199,6 @@ static int ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, const char *proxy_command) { -#ifdef WINDOWS - fatal("Proxy connect is not supported in Windows yet"); - return 0; -#else /* !WINDOWS */ char *command_string; int pin[2], pout[2]; pid_t pid; @@ -220,6 +216,28 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, host, port); debug("Executing proxy command: %.500s", command_string); + +#ifdef FORK_NOT_SUPPORTED + { + posix_spawn_file_actions_t actions; + char* spawn_argv[2]; + /* + * expand_proxy_command prefixes cmdline with "exec " + */ + spawn_argv[0] = command_string + 5; + spawn_argv[1] = NULL; + pid = -1; + + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, pin[0], STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, pout[1], STDOUT_FILENO) != 0) + fatal("posix_spawn initialization failed"); + else if (posix_spawn(&pid, spawn_argv[0], &actions, NULL, spawn_argv, NULL) != 0) + fatal("posix_spawn: %s", strerror(errno)); + + posix_spawn_file_actions_destroy(&actions); + } +#else /* Fork and execute the proxy command. */ if ((pid = fork()) == 0) { char *argv[10]; @@ -254,6 +272,7 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, perror(argv[0]); exit(1); } +#endif /* Parent. */ if (pid < 0) fatal("fork failed: %.100s", strerror(errno)); @@ -272,7 +291,6 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, return -1; /* ssh_packet_set_connection logs error */ return 0; -#endif /* !WINDOWS */ } void diff --git a/sshd.c b/sshd.c index c7061f5..28563f6 100644 --- a/sshd.c +++ b/sshd.c @@ -742,7 +742,9 @@ privsep_preauth(Authctxt *authctxt) #ifdef FORK_NOT_SUPPORTED if (privsep_auth_child) { - authctxt->pw = w32_getpwuid(1); + struct passwd* me = getpwuid(geteuid()); + /* this re-does the user specific config */ + authctxt->pw = getpwnamallow(xstrdup(me->pw_name)); authctxt->valid = 1; return 1; } @@ -774,7 +776,7 @@ privsep_preauth(Authctxt *authctxt) else { char** argv = privsep_child_cmdline(0); if (__posix_spawn_asuser(&pid, argv[0], &actions, NULL, argv, NULL, SSH_PRIVSEP_USER) != 0) - error("posix_spawn failed"); + error("%s, posix_spawn failed", __func__); posix_spawn_file_actions_destroy(&actions); } close(pmonitor->m_recvfd); @@ -880,7 +882,7 @@ privsep_postauth(Authctxt *authctxt) else { char** argv = privsep_child_cmdline(1); if (__posix_spawn_asuser(&pmonitor->m_pid, argv[0], &actions, NULL, argv, NULL, authctxt->pw->pw_name) != 0) - error("posix_spawn failed"); + error("%s, posix_spawn failed", __func__); posix_spawn_file_actions_destroy(&actions); } @@ -1545,7 +1547,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) error("posix_spawn initialization failed"); else { if (posix_spawn(&pid, rexec_argv[0], &actions, &attributes, rexec_argv, NULL) != 0) - error("posix_spawn failed"); + error("%s, posix_spawn failed", __func__); posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attributes); } diff --git a/version.h b/version.h index f87e2ca..9bcbf31 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ /* $OpenBSD: version.h,v 1.80 2017/09/30 22:26:33 djm Exp $ */ -#define SSH_VERSION "OpenSSH_7.6" +#define SSH_VERSION "OpenSSH_for_Windows_7.6" #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE