From af4e4113b270e9586c5b8c7f296d7765c115cb69 Mon Sep 17 00:00:00 2001 From: Manoj Ampalam Date: Thu, 4 Oct 2018 14:16:02 -0700 Subject: [PATCH] Ported bash based E2E tests and integrated security fix for cve-2018-15473(#346) - Updated code to dynamic load Lsa functions until RS5 SDK includes them - Add conpty support in openssh - Fixed Wierd characters (?25l) are seen, when logged in from ssh client - Backspace doesn't work in powershell window - Changes to support ssh-shellhost as an alternative shell - Added support to have ssh-shellhost work as a standby shell (ssh-shellhost -c "cmdline") simply executes cmdline via CreateProcess - Added E2E test cases and fixed unittests broken from prior changes - Added PTY launch interface that supports both conpty and ssh-shellhost pty. - Implemented PTY control channel in ssh-shellhost that supports Window resize events. - Fixed regression with starting a PTY session with an explicit command - modified ssh-shellhost pty argument to ---pty to remove ambiguity in cases when both -p and -c are present in commandline. Ex. ssh-shellhost.exe -c "myprogram -p -c argument" --- auth.c | 10 +- auth2-pubkey.c | 23 +- .../win32/openssh/bash_script_iterator.ps1 | 276 +++++++++++++++++ contrib/win32/openssh/config.h.vs | 5 +- .../openssh/win32iocompat.vcxproj.filters | 1 + contrib/win32/win32compat/fileio.c | 70 +++-- contrib/win32/win32compat/inc/stdlib.h | 3 +- contrib/win32/win32compat/misc.c | 293 ++++++++++++------ contrib/win32/win32compat/misc_internal.h | 9 +- contrib/win32/win32compat/pwd.c | 24 +- contrib/win32/win32compat/termio.c | 3 +- contrib/win32/win32compat/tncon.c | 116 +++++-- contrib/win32/win32compat/tncon.h | 109 +++++-- contrib/win32/win32compat/w32fd.c | 65 +++- contrib/win32/win32compat/w32log.c | 2 +- contrib/win32/win32compat/win32_groupaccess.c | 15 +- contrib/win32/win32compat/win32_pty.c | 10 +- contrib/win32/win32compat/wmain_sshd.c | 18 +- misc.c | 10 + monitor.c | 38 +++ readconf.c | 13 +- regress/.gitattributes | 5 + regress/addrmatch.sh | 3 + regress/agent-timeout.sh | 5 +- regress/agent.sh | 6 +- regress/banner.sh | 15 +- regress/cert-hostkey.sh | 10 +- regress/cert-userkey.sh | 8 +- regress/cfginclude.sh | 17 + regress/cfgmatch.sh | 36 ++- regress/cfgparse.sh | 34 +- regress/dynamic-forward.sh | 6 + regress/envpass.sh | 5 + regress/forcecommand.sh | 7 +- regress/forwarding.sh | 6 + regress/host-expand.sh | 9 +- regress/hostkey-agent.sh | 16 + regress/hostkey-rotate.sh | 6 + regress/integrity.sh | 18 +- regress/kextype.sh | 11 +- regress/keygen-change.sh | 6 +- regress/keygen-convert.sh | 11 +- regress/keygen-knownhosts.sh | 56 +++- regress/keyscan.sh | 7 +- regress/krl.sh | 7 +- regress/limit-keytype.sh | 11 +- regress/localcommand.sh | 5 + regress/multiplex.sh | 7 + regress/reconfigure.sh | 7 +- regress/reexec.sh | 4 +- regress/rekey.sh | 19 +- regress/scp-uri.sh | 21 +- regress/scp.sh | 35 ++- regress/sftp-cmds.sh | 10 +- regress/sftp-glob.sh | 15 +- regress/sftp-perm.sh | 45 ++- regress/sftp-uri.sh | 8 +- regress/sshcfgparse.sh | 18 ++ regress/test-exec.sh | 173 ++++++++--- regress/try-ciphers.sh | 4 + regress/unittests/win32compat/file_tests.c | 7 +- .../win32compat/miscellaneous_tests.c | 6 + regress/unittests/win32compat/socket_tests.c | 2 +- scp.c | 64 ++-- servconf.c | 41 ++- session.c | 118 +++---- sftp.c | 42 ++- sshconnect.c | 19 +- sshd.c | 53 +++- sshkey.c | 23 ++ 70 files changed, 1678 insertions(+), 502 deletions(-) create mode 100644 contrib/win32/openssh/bash_script_iterator.ps1 create mode 100644 regress/.gitattributes diff --git a/auth.c b/auth.c index da903c3a2..dea0a2b8b 100644 --- a/auth.c +++ b/auth.c @@ -589,8 +589,13 @@ getpwnamallow(const char *user) #endif struct passwd *pw; struct connection_info *ci = get_connection_info(1, options.use_dns); - +#ifdef WINDOWS + /* getpwname - normalizes the incoming user and makes it lowercase */ + pw = getpwnam(user); + ci->user = pw? pw->pw_name: user; +#else ci->user = user; +#endif // WINDOWS parse_server_match_config(&options, ci); log_change_level(options.log_level); process_permitopen(ssh, &options); @@ -598,8 +603,9 @@ getpwnamallow(const char *user) #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_setauthdb(user); #endif - +#ifndef WINDOWS pw = getpwnam(user); +#endif #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_restoreauthdb(); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index ddb891e94..67b126c9f 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -90,19 +90,15 @@ userauth_pubkey(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; struct passwd *pw = authctxt->pw; - struct sshbuf *b; + struct sshbuf *b = NULL; struct sshkey *key = NULL; - char *pkalg, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; - u_char *pkblob, *sig, have_sig; + char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; + u_char *pkblob = NULL, *sig = NULL, have_sig; size_t blen, slen; int r, pktype; int authenticated = 0; struct sshauthopt *authopts = NULL; - if (!authctxt->valid) { - debug2("%s: disabled because of invalid user", __func__); - return 0; - } if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) @@ -169,6 +165,11 @@ userauth_pubkey(struct ssh *ssh) fatal("%s: sshbuf_put_string session id: %s", __func__, ssh_err(r)); } + if (!authctxt->valid || authctxt->user == NULL) { + debug2("%s: disabled because of invalid user", + __func__); + goto done; + } /* reconstruct packet */ xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", @@ -185,7 +186,6 @@ userauth_pubkey(struct ssh *ssh) #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif - /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && @@ -194,7 +194,6 @@ userauth_pubkey(struct ssh *ssh) authenticated = 1; } sshbuf_free(b); - free(sig); auth2_record_key(authctxt, authenticated, key); } else { debug("%s: test pkalg %s pkblob %s%s%s", @@ -205,6 +204,11 @@ userauth_pubkey(struct ssh *ssh) if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); + if (!authctxt->valid || authctxt->user == NULL) { + debug2("%s: disabled because of invalid user", + __func__); + goto done; + } /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed @@ -238,6 +242,7 @@ done: free(pkblob); free(key_s); free(ca_s); + free(sig); return authenticated; } diff --git a/contrib/win32/openssh/bash_script_iterator.ps1 b/contrib/win32/openssh/bash_script_iterator.ps1 new file mode 100644 index 000000000..fb915eb63 --- /dev/null +++ b/contrib/win32/openssh/bash_script_iterator.ps1 @@ -0,0 +1,276 @@ +param ( + # Path to openssh binaries + [Parameter(Mandatory=$true)] [string] $OpenSSHBinPath, + # Path of regress folder which has all the bash testcases. + [Parameter(Mandatory=$true)] [string] $BashTestsPath, + # Path to CYGWIN / WSL. + [Parameter(Mandatory=$true)] [string] $ShellPath, + # Individual bash test file (Ex - connect.sh) + [Parameter(Mandatory=$false)] [string[]] $TestFilePath, + [Parameter(Mandatory=$false)] [string] $ArtifactsPath=".", + [switch] $SkipCleanup, + [switch] $SkipInstallSSHD +) + +# Resolve the relative paths +$OpenSSHBinPath=resolve-path $OpenSSHBinPath -ErrorAction Stop | select -ExpandProperty Path +$BashTestsPath=resolve-path $BashTestsPath -ErrorAction Stop | select -ExpandProperty Path +$ShellPath=resolve-path $ShellPath -ErrorAction Stop | select -ExpandProperty Path +$ArtifactsPath=resolve-path $ArtifactsPath -ErrorAction Stop | select -ExpandProperty Path +if ($TestFilePath) { + $TestFilePath=resolve-path $TestFilePath -ErrorAction Stop | select -ExpandProperty Path + # convert to bash format + $TestFilePath=$TestFilePath -replace "\\","/" +} + +# Make sure config.h exists. It is used by bashstests (Ex - sftp-glob.sh, cfgparse.sh) +# first check in $BashTestsPath folder. If not then it's parent folder. If not then in the $OpenSSHBinPath +if(Test-Path "$BashTestsPath\config.h" -PathType Leaf) { + $configPath="$BashTestsPath\config.h" +} elseif(Test-Path "$BashTestsPath\..\config.h" -PathType Leaf) { + $configPath=resolve-path "$BashTestsPath\..\config.h" -ErrorAction Stop | select -ExpandProperty Path +} elseif(Test-Path "$OpenSSHBinPath\config.h" -PathType Leaf) { + $configPath="$OpenSSHBinPath\config.h" +} else { + Write-Error "Couldn't find config.h" + exit +} + +# store user directory +$user_pwd=pwd | select -ExpandProperty Path + +# If we are using a SKU with desired OpenSSH binaries then we can skip these steps. +if(!$SkipInstallSSHD) { + # Make sure install-sshd.ps1 exists. + if(!(Test-Path "$PSScriptRoot\install-sshd.ps1" -PathType Leaf)) { + Write-Error "$PSScriptRoot\install-sshd.ps1 doesn't exists" + exit + } + + # Make sure uninstall-sshd.ps1 exists. + if(!(Test-Path "$PSScriptRoot\uninstall-sshd.ps1" -PathType Leaf)) { + Write-Error "$PSScriptRoot\uninstall-sshd.ps1 doesn't exists" + exit + } + + #copy to binary folder and execute install-sshd.ps1 + Copy-Item $PSScriptRoot\install-sshd.ps1 -Force $OpenSSHBinPath + Copy-Item $PSScriptRoot\uninstall-sshd.ps1 -Force $OpenSSHBinPath + + # We need ssh-agent to be installed as service to run some bash tests. + & "$OpenSSHBinPath\install-sshd.ps1" +} + +try +{ + # set the default shell + $registryPath = "HKLM:\Software\OpenSSH" + $dfltShell = "DefaultShell" + # Fetch the user configured default shell. + $out=(Get-ItemProperty -Path $registryPath -Name $dfltShell -ErrorAction SilentlyContinue) + if($out) { + $user_default_shell = $out.$dfltShell + Write-Output "User configured default shell: $user_default_shell" + } + + if (!(Test-Path $registryPath)) { + # start and stop the sshd so that "HKLM:\Software\OpenSSH" registry path is created. + Start-Service sshd -ErrorAction Stop + Stop-Service sshd -ErrorAction SilentlyContinue + } + + Set-ItemProperty -Path $registryPath -Name $dfltShell -Value $ShellPath -Force + $out=(Get-ItemProperty -Path $registryPath -Name $dfltShell -ErrorAction SilentlyContinue) + if($out.$dfltShell -ne $ShellPath) { + Write-Output "Failed to set HKLM:\Software\OpenSSH\DefaultShell to $ShellPath" + exit + } + + # Prepend shell path to PATH. This is required to make the shell commands (like sleep, cat, etc) work properly. + $env:TEST_SHELL_PATH=$ShellPath -replace "\\","/" + $TEST_SHELL_DIR=split-path $ShellPath + if(!$env:path.StartsWith($TEST_SHELL_DIR, "CurrentCultureIgnoreCase")) + { + $env:path=$TEST_SHELL_DIR+";"+$env:path + } + + $BashTestsPath=$BashTestsPath -replace "\\","/" + Push-location $BashTestsPath + + # BUILDDIR: config.h location. + # BUILDDIR is used by bashstests (Ex - sftp-glob.sh, cfgparse.sh) + $BUILDDIR=resolve-path(split-path $configpath) | select -ExpandProperty Path + $tmp=&$ShellPath -c pwd + if ($tmp.StartsWith("/cygdrive/")) { + $shell_drv_fmt="/cygdrive/" # "/cygdrive/" - cygwin + $BUILDDIR=&$ShellPath -c "cygpath -u '$BUILDDIR'" + $OpenSSHBinPath_shell_fmt=&$ShellPath -c "cygpath -u '$OpenSSHBinPath'" + $BashTestsPath=&$ShellPath -c "cygpath -u '$BashTestsPath'" + } elseif ($tmp.StartsWith("/mnt/")) { + $shell_drv_fmt="/mnt/" # "/mnt/" - WSL bash + $BUILDDIR=&$ShellPath -c "wslpath -u '$BUILDDIR'" + $OpenSSHBinPath_shell_fmt=&$ShellPath -c "wslpath -u '$OpenSSHBinPath'" + $BashTestsPath=&$ShellPath -c "wslpath -u '$BashTestsPath'" + } + + #set the environment variables. + $env:ShellPath=$ShellPath + $env:SSH_TEST_ENVIRONMENT=1 + $env:TEST_SSH_TRACE="yes" + $env:TEST_SHELL="/bin/sh" + $env:TEST_SSH_PORT=22 + $env:TEST_SSH_SSH=$OpenSSHBinPath_shell_fmt+"/ssh.exe" + $env:TEST_SSH_SSHD=$OpenSSHBinPath_shell_fmt+"/sshd.exe" + $env:TEST_SSH_SSHAGENT=$OpenSSHBinPath_shell_fmt+"/ssh-agent.exe" + $env:TEST_SSH_SSHADD=$OpenSSHBinPath_shell_fmt+"/ssh-add.exe" + $env:TEST_SSH_SSHKEYGEN=$OpenSSHBinPath_shell_fmt+"/ssh-keygen.exe" + $env:TEST_SSH_SSHKEYSCAN=$OpenSSHBinPath_shell_fmt+"/ssh-keyscan.exe" + $env:TEST_SSH_SFTP=$OpenSSHBinPath_shell_fmt+"/sftp.exe" + $env:TEST_SSH_SFTPSERVER=$OpenSSHBinPath_shell_fmt+"/sftp-server.exe" + $env:TEST_SSH_SCP=$OpenSSHBinPath_shell_fmt+"/scp.exe" + $env:BUILDDIR=$BUILDDIR + $env:TEST_WINDOWS_SSH=1 + $user = &"$env:windir\system32\whoami.exe" + if($user.Contains($env:COMPUTERNAME.ToLower())) { + # for local accounts, skip COMPUTERNAME + $user = Split-Path $user -leaf + $env:TEST_SSH_USER=$user + } else { + # for domain user convert "domain\user" to "domain/user". + $user = $user -replace "\\","/" + $env:TEST_SSH_USER = $user + $env:TEST_SSH_USER_DOMAIN = Split-Path $user + } + + # output to terminal + Write-Output "USER: $env:TEST_SSH_USER" + Write-Output "DOMAIN: $env:TEST_SSH_USER_DOMAIN" + Write-Output "OpenSSHBinPath: $OpenSSHBinPath_shell_fmt" + Write-Output "BUILDDIR: $BUILDDIR" + Write-Output "BashTestsPath: $BashTestsPath" + + # remove, create the temp test directory + $temp_test_path="temp_test" + $null = Remove-Item -Recurse -Force $temp_test_path -ErrorAction SilentlyContinue + $null = New-Item -ItemType directory -Path $temp_test_path -ErrorAction Stop + + # remove the summary, output files. + $test_summary="$ArtifactsPath\\bashtest_summary.txt" + $test_output="$ArtifactsPath\\bashtest_output.txt" + $null = Remove-Item -Force $test_summary -ErrorAction SilentlyContinue + $null = Remove-Item -Force $test_output -ErrorAction SilentlyContinue + [int]$total_tests = 0 + [int]$total_tests_passed = 0 + [int]$total_tests_failed = 0 + [string]$failed_testcases = [string]::Empty + + # These are the known failed testcases. + # agent.sh, krl.sh - User Cert authentication fails + # key-options.sh - pty testcases are failing (bug in conpty. conpty fails to launch cygwin bash) + # integrity.sh - It's dependent on regress\modpipe.exe, test is complicated. Need to debug more + # authinfo.sh - spawned conpty inherits all the environment variables from sshd. + # forward-control.sh - Need to debug more. + [string]$known_failed_testcases = "agent.sh key-options.sh forward-control.sh integrity.sh krl.sh authinfo.sh" + [string]$known_failed_testcases_skipped = [string]::Empty + + $start_time = (Get-Date) + + if($TestFilePath) { + # User can specify individual test file path. + $all_tests=$TestFilePath + } else { + # picking the gawk.exe from bash folder. + # TODO - check if gawk.exe is present in WSL. + $all_tests=gawk.exe 'sub(/.*LTESTS=/,""""){f=1} f{print $1; if (!/\\\\/) exit}' Makefile + } + + foreach($test_case in $all_tests) { + if($TestFilePath) { + $TEST=$test_case + } else { + if(!$test_case.Contains(".sh")) { + $TEST=$BashTestsPath+"/"+$test_case+".sh" + } else { + $TEST=$BashTestsPath+"/"+$test_case + } + } + + $test_case_name = [System.IO.Path]::GetFileName($TEST) + if($known_failed_testcases.Contains($test_case_name)) + { + Write-Output "Skip the known failed test:$test_case_name" + $known_failed_testcases_skipped = $known_failed_testcases_skipped + "$test_case_name " + } + else + { + $msg="Executing $test_case_name " +[System.DateTime]::Now + Write-Output $msg + &$env:ShellPath -c "/usr/bin/sh $BashTestsPath/test-exec.sh $BashTestsPath/$temp_test_path $TEST" #>$null 2>&1 + if($?) + { + $msg="$test_case_name PASSED " +[System.DateTime]::Now + Write-Output $msg + $total_tests_passed++ + } + else + { + $msg="$test_case_name FAILED " +[System.DateTime]::Now + Write-Output $msg + $total_tests_failed++ + $failed_testcases=$failed_testcases + "$test_case_name " + } + } + $total_tests++ + } + + $end_time = (Get-Date) + + # Create artifacts + "Start time: $start_time" | Out-File -FilePath $test_summary -Append + "End time: $end_time" | Out-File -FilePath $test_summary -Append + "Total execution time: " + (NEW-TIMESPAN -Start $start_time -End $end_time).ToString("hh\:mm\:ss") | Out-File -FilePath $test_summary -Append + "Tests executed: $total_tests" | Out-File -FilePath $test_summary -Append + "Tests passed: $total_tests_passed" | Out-File -FilePath $test_summary -Append + "Tests failed: $total_tests_failed" | Out-File -FilePath $test_summary -Append + "Failed tests: $failed_testcases" | Out-File -FilePath $test_summary -Append + "Skipped known failed tests: $known_failed_testcases_skipped" | Out-File -FilePath $test_summary -Append + + Write-Output "Artifacts are saved to $ArtifactsPath" + + #output the summary + Write-Output "=================================" + cat $test_summary + Write-Output "=================================" +} +finally +{ + # remove temp test directory + if (!$SkipCleanup) + { + # remove temp test folder + &$ShellPath -c "rm -rf $BashTestsPath/temp_test" + + if(!$SkipInstallSSHD) { + # Uninstall the sshd, ssh-agent service + & "$PSScriptRoot\uninstall-sshd.ps1" + } + + # Remove the test environment variable + Remove-Item ENV:\SSH_TEST_ENVIRONMENT + + # Revert to user configured default shell. + if($user_default_shell) { + Set-ItemProperty -Path $registryPath -Name $dfltShell -Value $user_default_shell -Force + $out=(Get-ItemProperty -Path $registryPath -Name $dfltShell -ErrorAction SilentlyContinue) + if($out.$dfltShell -eq $user_default_shell) { + Write-Output "Reverted user configured default shell to $user_default_shell" + } else { + Write-Output "Failed to set HKLM:\Software\OpenSSH\DefaultShell to $user_default_shell" + } + } else { + Remove-ItemProperty -Path $registryPath -Name $dfltShell -ErrorAction SilentlyContinue + } + } + + Push-location $user_pwd +} diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs index 0b2257c74..8358c77b6 100644 --- a/contrib/win32/openssh/config.h.vs +++ b/contrib/win32/openssh/config.h.vs @@ -819,7 +819,7 @@ /* #undef HAVE_SETEGID */ /* Define to 1 if you have the `setenv' function. */ -/* #undef HAVE_SETENV */ +#define HAVE_SETENV 1 /* Define to 1 if you have the `seteuid' function. */ /* #undef HAVE_SETEUID */ @@ -1699,4 +1699,5 @@ #define _PATH_SSH_PROGRAM "ssh.exe" #define _PATH_LS "dir" #define FORK_NOT_SUPPORTED 1 -#define HAVE_FREEZERO \ No newline at end of file +#define HAVE_FREEZERO +#define FILESYSTEM_NO_BACKSLASH diff --git a/contrib/win32/openssh/win32iocompat.vcxproj.filters b/contrib/win32/openssh/win32iocompat.vcxproj.filters index f2ce0776f..c77e688ca 100644 --- a/contrib/win32/openssh/win32iocompat.vcxproj.filters +++ b/contrib/win32/openssh/win32iocompat.vcxproj.filters @@ -23,6 +23,7 @@ + diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index 8c5b58f49..a2d599b9d 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -330,6 +330,7 @@ createFile_flags_setup(int flags, mode_t mode, struct createFile_flags* cf_flags break; case O_WRONLY: cf_flags->dwDesiredAccess = GENERIC_WRITE; + cf_flags->dwShareMode = FILE_SHARE_WRITE; break; case O_RDWR: cf_flags->dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; @@ -474,7 +475,7 @@ fileio_open(const char *path_utf8, int flags, mode_t mode) if (handle == INVALID_HANDLE_VALUE) { errno = errno_from_Win32LastError(); - debug3("failed to open file:%s error:%d", path_utf8, GetLastError()); + debug3("failed to open file:%S error:%d", path_utf16, GetLastError()); goto cleanup; } @@ -1157,45 +1158,46 @@ int fileio_symlink(const char *target, const char *linkpath) { DWORD ret = -1; + char target_modified[PATH_MAX] = { 0 }; + char *linkpath_resolved = NULL, *target_resolved = NULL; if (target == NULL || linkpath == NULL) { errno = EFAULT; return -1; } - wchar_t *target_utf16 = resolved_path_utf16(target); - wchar_t *linkpath_utf16 = resolved_path_utf16(linkpath); - wchar_t *resolved_utf16 = _wcsdup(target_utf16); - if (target_utf16 == NULL || linkpath_utf16 == NULL) + /* First resolve linkpath */ + if (NULL == (linkpath_resolved = resolved_path_utf8(linkpath))) goto cleanup; - if (resolved_utf16 == NULL) { - errno = ENOMEM; - goto cleanup; - } - /* Relative targets are relative to the link and not our current directory * so attempt to calculate a resolvable path by removing the link file name * leaving only the parent path and then append the relative link: * C:\Path\Link with Link->SubDir\Target to C:\Path\SubDir\Target */ if (!is_absolute_path(target)) { - - /* allocate area to hold the total possible path */ - free(resolved_utf16); - size_t resolved_len = (wcslen(target_utf16) + wcslen(linkpath_utf16) + 1); - resolved_utf16 = malloc(resolved_len * sizeof(wchar_t)); - if (resolved_utf16 == NULL) { - errno = ENOMEM; - goto cleanup; - } + strcpy_s(target_modified, _countof(target_modified), linkpath_resolved); + convertToBackslash(target_modified); + char *tmp = NULL; /* copy the relative target to the end of the link's parent */ - wcscpy_s(resolved_utf16, resolved_len, linkpath_utf16); - convertToBackslashW(resolved_utf16); - wchar_t * ptr = wcsrchr(resolved_utf16, L'\\'); - if (ptr == NULL) wcscpy_s(resolved_utf16, resolved_len, target_utf16); - else wcscpy_s(ptr + 1, resolved_len - (ptr + 1 - resolved_utf16), target_utf16); + if (tmp = strrchr(target_modified, '\\')) + strcpy_s(tmp + 1, _countof(target_modified) - (tmp + 1 - target_modified), target); + else + strcpy_s(target_modified, _countof(target_modified), target); + } else { + /* resolve target */ + if (NULL == (target_resolved = resolved_path_utf8(target))) + goto cleanup; + + strcpy_s(target_modified, _countof(target_modified), target_resolved); + } + + wchar_t *linkpath_utf16 = resolved_path_utf16(linkpath); + wchar_t *resolved_target_utf16 = utf8_to_utf16(target_modified); + if (resolved_target_utf16 == NULL || linkpath_utf16 == NULL) { + errno = ENOMEM; + goto cleanup; } /* unlike other platforms, we need to know whether the symbolic link target is @@ -1204,7 +1206,7 @@ fileio_symlink(const char *target, const char *linkpath) * limitation of only creating symlink with valid targets */ WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 }; - if (GetFileAttributesExW(resolved_utf16, GetFileExInfoStandard, &attributes) == FALSE) { + if (GetFileAttributesExW(resolved_target_utf16, GetFileExInfoStandard, &attributes) == FALSE) { errno = errno_from_Win32LastError(); goto cleanup; } @@ -1218,8 +1220,8 @@ fileio_symlink(const char *target, const char *linkpath) * context so we try both operations, attempting privileged version first. * note: 0x2 = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE */ - if (CreateSymbolicLinkW(linkpath_utf16, target_utf16, create_flags) == 0) { - if (CreateSymbolicLinkW(linkpath_utf16, target_utf16, create_flags | 0x2) == 0) { + if (CreateSymbolicLinkW(linkpath_utf16, resolved_target_utf16, create_flags) == 0) { + if (CreateSymbolicLinkW(linkpath_utf16, resolved_target_utf16, create_flags | 0x2) == 0) { errno = errno_from_Win32LastError(); goto cleanup; } @@ -1228,12 +1230,18 @@ fileio_symlink(const char *target, const char *linkpath) ret = 0; cleanup: - if (target_utf16) - free(target_utf16); if (linkpath_utf16) free(linkpath_utf16); - if (resolved_utf16) - free(resolved_utf16); + + if (resolved_target_utf16) + free(resolved_target_utf16); + + if (linkpath_resolved) + free(linkpath_resolved); + + if (target_resolved) + free(target_resolved); + return ret; } diff --git a/contrib/win32/win32compat/inc/stdlib.h b/contrib/win32/win32compat/inc/stdlib.h index 610cd5478..04c62371f 100644 --- a/contrib/win32/win32compat/inc/stdlib.h +++ b/contrib/win32/win32compat/inc/stdlib.h @@ -2,4 +2,5 @@ #include STDLIB_H #define environ _environ -void freezero(void *, size_t); \ No newline at end of file +void freezero(void *, size_t); +int setenv(const char *name, const char *value, int rewrite); diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 76e79a003..97b40ee3a 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -279,7 +279,7 @@ w32_fopen_utf8(const char *input_path, const char *mode) goto cleanup; if ((_wfopen_s(&f, wpath, wmode) != 0) || (f == NULL)) { - debug3("Failed to open file:%s error:%d", input_path, errno); + debug3("Failed to open file:%S error:%d", wpath, errno); goto cleanup; } @@ -512,6 +512,10 @@ strmode(mode_t mode, char *p) int w32_chmod(const char *pathname, mode_t mode) { + /* TODO - + * _wchmod() doesn't behave like unix "chmod" command. + * _wchmod() only toggles the read-only bit and it doesn't touch ACL. + */ int ret; wchar_t *resolvedPathName_utf16 = resolved_path_utf16(pathname); if (resolvedPathName_utf16 == NULL) @@ -685,11 +689,16 @@ w32_rename(const char *old_name, const char *new_name) * 1) if the new_name is file, then delete it so that _wrename will succeed. * 2) if the new_name is directory and it is empty then delete it so that _wrename will succeed. */ - struct w32_stat st; - if (w32_stat(new_name, &st) != -1) { - if (((st.st_mode & _S_IFMT) == _S_IFREG)) + struct _stat64 st_new; + struct _stat64 st_old; + if ((fileio_stat(new_name, &st_new) != -1) && + (fileio_stat(old_name, &st_old) != -1)) { + if (((st_old.st_mode & _S_IFMT) == _S_IFREG) && + ((st_new.st_mode & _S_IFMT) == _S_IFREG)) w32_unlink(new_name); - else { + + if (((st_old.st_mode & _S_IFMT) == _S_IFDIR) && + ((st_new.st_mode & _S_IFMT) == _S_IFDIR)) { DIR *dirp = opendir(new_name); if (NULL != dirp) { struct dirent *dp = readdir(dirp); @@ -769,7 +778,7 @@ w32_getcwd(char *buffer, int maxlen) return NULL; } - if (strcpy_s(buffer, maxlen, putf8)) + if (strcpy_s(buffer, maxlen, putf8)) return NULL; free(putf8); @@ -782,7 +791,7 @@ w32_getcwd(char *buffer, int maxlen) memcmp(chroot_path, buffer, chroot_path_len) != 0 || (c != '\0' && c!= '\\') ) { errno = EOTHER; - error("cwb is not currently within chroot"); + error("cwd is not currently within chroot"); return NULL; } @@ -795,7 +804,7 @@ w32_getcwd(char *buffer, int maxlen) char *tail = buffer + chroot_path_len; memmove_s(buffer, maxlen, tail, strlen(tail) + 1); } - } + } return buffer; } @@ -880,15 +889,36 @@ convertToForwardslash(char *str) * path to produce a canonicalized absolute pathname. */ char * -realpath(const char *path, char resolved[PATH_MAX]) +realpath(const char *inputpath, char resolved[PATH_MAX]) { - if (!path || !resolved) return NULL; + char path[PATH_MAX] = { 0, }, tempPath[PATH_MAX] = { 0, }, *ret = NULL; + int is_win_path = 1; - char tempPath[PATH_MAX]; - size_t path_len = strlen(path); + if (!inputpath || !resolved) + return NULL; + + size_t path_len = strlen(inputpath); resolved[0] = '\0'; - if (path_len > PATH_MAX - 1) { + if (path_len > PATH_MAX) { + errno = EINVAL; + return NULL; + } + + if (is_bash_test_env() && bash_to_win_path(inputpath, path, _countof(path))) + is_win_path = 0; + + if (is_win_path) { + if (_strnicmp(inputpath, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0) { + strcat_s(path, PATH_MAX, __progdata); + strcat_s(path, PATH_MAX, &inputpath[strlen(PROGRAM_DATA)]); + } else { + memcpy_s(path, PATH_MAX, inputpath, strlen(inputpath)); + } + } + + path_len = strlen(path); + if (path_len > PATH_MAX) { errno = EINVAL; return NULL; } @@ -897,7 +927,8 @@ realpath(const char *path, char resolved[PATH_MAX]) if (path_len == 1 && (path[0] == '/' || path[0] == '\\')) { resolved[0] = '/'; resolved[1] = '\0'; - return resolved; + ret = resolved; + goto done; } /* resolve this common case scenario to root */ @@ -909,7 +940,8 @@ realpath(const char *path, char resolved[PATH_MAX]) if (strcmp(tmplate, resolved) == 0) { resolved[0] = '/'; resolved[1] = '\0'; - return resolved; + ret = resolved; + goto done; } } @@ -921,17 +953,22 @@ realpath(const char *path, char resolved[PATH_MAX]) w32_getcwd(resolved + chroot_path_len, PATH_MAX - chroot_path_len); strcat_s(resolved, PATH_MAX, "/"); } + /* TODO - This logic will fail if the chroot_path is more than PATH_MAX/2. + * resolved variable is of PATH_MAX. + * We first copy chroot_path to resolved variable then incoming path (which can be again chroot_path). + * In this case strcat_s will thrown a run time insufficient buffer exception. + */ strcat_s(resolved, PATH_MAX, path); } else if ((path_len >= 2) && (path[0] == '/') && path[1] && (path[2] == ':')) { if((errno = strncpy_s(resolved, PATH_MAX, path + 1, path_len)) != 0 ) /* skip the first '/' */ { debug3("memcpy_s failed with error: %d.", errno); - return NULL; + goto done; } } else if(( errno = strncpy_s(resolved, PATH_MAX, path, path_len + 1)) != 0) { debug3("memcpy_s failed with error: %d.", errno); - return NULL; + goto done; } if ((resolved[0]) && (resolved[1] == ':') && (resolved[2] == '\0')) { /* make "x:" as "x:\\" */ @@ -941,21 +978,20 @@ realpath(const char *path, char resolved[PATH_MAX]) if (_fullpath(tempPath, resolved, PATH_MAX) == NULL) { errno = EINVAL; - return NULL; + goto done; } if (chroot_path) { if (strlen(tempPath) < strlen(chroot_path)) { errno = EACCES; - return NULL; + goto done; } if (memcmp(chroot_path, tempPath, strlen(chroot_path)) != 0) { errno = EACCES; - return NULL; + goto done; } resolved[0] = '\0'; - if (strlen(tempPath) == strlen(chroot_path)) /* realpath is the same as chroot_path */ @@ -965,21 +1001,41 @@ realpath(const char *path, char resolved[PATH_MAX]) if (resolved[0] != '\\') { errno = EACCES; - return NULL; + goto done; } convertToForwardslash(resolved); - return resolved; + ret = resolved; + goto done; } else { convertToForwardslash(tempPath); resolved[0] = '/'; /* will be our first slash in /x:/users/test1 format */ if ((errno = strncpy_s(resolved + 1, PATH_MAX - 1, tempPath, sizeof(tempPath) - 1)) != 0) { debug3("memcpy_s failed with error: %d.", errno); - return NULL; + goto done; } - return resolved; + ret = resolved; + goto done; } + +done: + return ret; +} + +/* on error returns NULL and sets errno */ +char* +resolved_path_utf8(const char *input_path) +{ + wchar_t *resolved_path_w = resolved_path_utf16(input_path); + char *resolved_path = NULL; + + if (resolved_path_w) { + resolved_path = utf16_to_utf8(resolved_path_w); + free(resolved_path_w); + } + + return resolved_path; } /* on error returns NULL and sets errno */ @@ -987,70 +1043,26 @@ wchar_t* resolved_path_utf16(const char *input_path) { wchar_t *resolved_path = NULL; + char real_path[PATH_MAX]; if (!input_path) { errno = EINVAL; return NULL; } - if (chroot_path) { - char actual_path[MAX_PATH], jail_path[MAX_PATH]; - - if (realpath(input_path, jail_path) == NULL) - return NULL; - - actual_path[0] = '\0'; - strcat_s(actual_path, MAX_PATH, chroot_path); - strcat_s(actual_path, MAX_PATH, jail_path); - resolved_path = utf8_to_utf16(actual_path); - } - else - resolved_path = utf8_to_utf16(input_path); - - if (resolved_path == NULL) { - errno = ENOMEM; + if (realpath(input_path, real_path) == NULL) return NULL; - } - int resolved_len = (int) wcslen(resolved_path); - const int variable_len = (int) wcslen(PROGRAM_DATAW); - - /* search for program data flag and switch it with the real path */ - if (_wcsnicmp(resolved_path, PROGRAM_DATAW, variable_len) == 0) { - wchar_t * program_data = get_program_data_path(); - const int programdata_len = (int) wcslen(program_data); - const int changed_req = programdata_len - variable_len; - - /* allocate more memory if required */ - if (changed_req > 0) { - wchar_t * resolved_path_new = realloc(resolved_path, - (resolved_len + changed_req + 1) * sizeof(wchar_t)); - if (resolved_path_new == NULL) { - debug3("%s: memory allocation failed.", __FUNCTION__); - free(resolved_path); - errno = ENOMEM; - return NULL; - } - else resolved_path = resolved_path_new; - } - - /* shift memory contents over based on side of the new string */ - wmemmove_s(&resolved_path[variable_len + changed_req], resolved_len - variable_len + 1, - &resolved_path[variable_len], resolved_len - variable_len + 1); - resolved_len += changed_req; - wmemcpy_s(resolved_path, resolved_len + 1, program_data, programdata_len); - } - - if (resolved_path[0] == L'/' && iswalpha(resolved_path[1]) && resolved_path[2] == L':') { - - /* shift memory to remove forward slash including null terminator */ - wmemmove_s(resolved_path, resolved_len + 1, resolved_path + 1, (resolved_len + 1 - 1)); - - /* if just a drive letter path, make x: into x:\ */ - if (resolved_path[2] == L'\0') { - resolved_path[2] = L'\\'; - resolved_path[3] = L'\0'; - } + if (chroot_path) { + char actual_path[PATH_MAX] = { 0 }; + strcat_s(actual_path, _countof(actual_path), chroot_path); + strcat_s(actual_path, _countof(actual_path), real_path); + resolved_path = utf8_to_utf16(actual_path); + } else { + if ((strlen(real_path) == 1) && (real_path[0] == '/')) + resolved_path = utf8_to_utf16(real_path); + else + resolved_path = utf8_to_utf16(real_path + 1); /* account for preceding / in real_path */ } return resolved_path; @@ -1301,21 +1313,6 @@ cleanup: return ret; } -wchar_t* -get_program_data_path() -{ - static wchar_t ssh_cfg_dir_path_w[PATH_MAX] = L""; - if (wcslen(ssh_cfg_dir_path_w) > 0) return ssh_cfg_dir_path_w; - - int return_val = ExpandEnvironmentStringsW(L"%ProgramData%", ssh_cfg_dir_path_w, PATH_MAX); - if (return_val > PATH_MAX) - fatal("%s, buffer too small to expand:%s", __func__, "%ProgramData%"); - else if (!return_val) - fatal("%s, failed to expand:%s error:%s", __func__, "%ProgramData%", GetLastError()); - - return ssh_cfg_dir_path_w; -} - /* Windows absolute paths - \abc, /abc, c:\abc, c:/abc, __PROGRAMDATA__\openssh\sshd_config */ int is_absolute_path(const char *path) @@ -1406,6 +1403,30 @@ freezero(void *ptr, size_t sz) free(ptr); } +int +setenv(const char *name, const char *value, int rewrite) +{ + errno_t result = 0; + + /* If rewrite is 0, then set only if the variable name doesn't already exist in environment */ + if (!rewrite) { + char *envValue = NULL; + size_t len = 0; + _dupenv_s(&envValue, &len, name); + + if (envValue) + return result; /* return success (as per setenv manpage) */ + } + + if (!(result = _putenv_s(name, value))) + return 0; + else { + error("failed to set the environment variable:%s to value:%s, error:%d", name, value, result); + errno = result; + return -1; + } +} + int chroot(const char *path) { @@ -1567,7 +1588,7 @@ cleanup: char* build_session_commandline(const char *shell, const char* shell_arg, const char *command) { - enum sh_type { SH_OTHER, SH_CMD, SH_PS, SH_BASH } shell_type = SH_OTHER; + enum sh_type { SH_OTHER, SH_CMD, SH_PS, SH_BASH, SH_CYGWIN } shell_type = SH_OTHER; enum cmd_type { CMD_OTHER, CMD_SFTP, CMD_SCP } command_type = CMD_OTHER; char *progdir = __progdir, *cmd_sp = NULL, *cmdline = NULL, *ret = NULL, *p; int len, progdir_len = (int)strlen(progdir); @@ -1586,6 +1607,9 @@ do { \ shell_type = SH_PS; else if (strstr(shell, "\\bash")) shell_type = SH_BASH; + else if (strstr(shell, "cygwin")) { + shell_type = SH_CYGWIN; + } /* special case where incoming command needs to be adjusted */ do { @@ -1659,7 +1683,7 @@ do { \ p = cmd_sp; - if (shell_type == SH_CMD) { + if ((shell_type == SH_CMD) || (shell_type == SH_CYGWIN)) { CMDLINE_APPEND(p, "\""); CMDLINE_APPEND(p, progdir); @@ -1667,7 +1691,6 @@ do { \ CMDLINE_APPEND(p, "\\scp.exe\""); else CMDLINE_APPEND(p, "\\sftp-server.exe\""); - } else { if (command_type == CMD_SCP) CMDLINE_APPEND(p, "scp.exe"); @@ -1675,6 +1698,11 @@ do { \ CMDLINE_APPEND(p, "sftp-server.exe"); } + if (shell_type == SH_CYGWIN) { + *p = '\0'; + convertToForwardslash(cmd_sp); + } + CMDLINE_APPEND(p, command_args); *p = '\0'; command = cmd_sp; @@ -1684,7 +1712,12 @@ do { \ len +=(int) strlen(shell) + 3;/* 3 for " around shell path and trailing space */ if (command) { len += 15; /* for shell command argument, typically -c or /c */ - len += (int)strlen(command) + 5; /* 5 for possible " around command and null term*/ + + int extra_buffer_len = 0; + if (is_bash_test_env()) + extra_buffer_len = 50; /* 50 - To escape double quotes or backslash in command (Ex - yes-head.sh) */ + + len += (int)strlen(command) + 5 + extra_buffer_len; /* 5 for possible " around command and null term*/ } if ((cmdline = malloc(len)) == NULL) { @@ -1709,7 +1742,28 @@ do { \ /* Add double quotes around command */ CMDLINE_APPEND(p, "\""); - CMDLINE_APPEND(p, command); + if (is_bash_test_env()) { + /* Escape the double quotes and backslash as per CRT rules. + * Right now this logic is applied only in bash test environment. + * TODO - verify if this logic is applicable to all the shells. + */ + for (int i = 0; i < strlen(command); i++) { + if (command[i] == '\\') { + CMDLINE_APPEND(p, "\\"); + CMDLINE_APPEND(p, "\\"); // For every backslash add another backslash. + } + else if (command[i] == '\"') { + CMDLINE_APPEND(p, "\\"); // Add backslash for every double quote. + CMDLINE_APPEND(p, "\""); + } + else { + *p++ = command[i]; + } + } + } else { + CMDLINE_APPEND(p, command); + } + CMDLINE_APPEND(p, "\""); } *p = '\0'; @@ -1722,4 +1776,39 @@ done: free(cmdline); return ret; -} \ No newline at end of file +} + +BOOL +is_bash_test_env() +{ + char *envValue = NULL; + size_t len = 0; + BOOL retVal = FALSE; + _dupenv_s(&envValue, &len, "SSH_TEST_ENVIRONMENT"); + + if ((NULL != envValue) && atoi(envValue)) + retVal = TRUE; + + if (envValue) + free(envValue); + + return retVal; +} + +int +bash_to_win_path(const char *in, char *out, const size_t out_len) +{ + int retVal = 0; + const size_t cygwin_path_prefix_len = strlen(CYGWIN_PATH_PREFIX); + if (_strnicmp(in, CYGWIN_PATH_PREFIX, cygwin_path_prefix_len) == 0) { + memset(out, 0, out_len); + out[0] = in[cygwin_path_prefix_len]; + out[1] = ':'; + strcat_s(out, out_len, &in[cygwin_path_prefix_len + 1]); + retVal = 1; + } else { + strcat_s(out, out_len, in); + } + + return retVal; +} diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h index 966271359..7d7870935 100644 --- a/contrib/win32/win32compat/misc_internal.h +++ b/contrib/win32/win32compat/misc_internal.h @@ -19,6 +19,7 @@ #define IS_VALID_HANDLE(h) (!IS_INVALID_HANDLE(h)) #define PROGRAM_DATA "__PROGRAMDATA__" #define PROGRAM_DATAW L"__PROGRAMDATA__" +#define CYGWIN_PATH_PREFIX "/cygdrive/" #define errno_from_Win32LastError() errno_from_Win32Error(GetLastError()) @@ -36,6 +37,10 @@ extern char* __progname; extern char* __progdir; extern wchar_t* __wprogdir; +/* %programdata% value */ +extern char* __progdata; +extern wchar_t* __wprogdata; + static char *machine_domain_name; extern char* chroot_path; @@ -44,6 +49,7 @@ extern wchar_t* chroot_pathw; /* removes first '/' for Windows paths that are unix styled. Ex: /c:/ab.cd */ wchar_t * resolved_path_utf16(const char *); +char* resolved_path_utf8(const char *); void w32posix_initialize(); void w32posix_done(); void init_prog_paths(); @@ -57,7 +63,6 @@ int file_attr_to_st_mode(wchar_t * path, DWORD attributes); void invalid_parameter_handler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t); void to_lower_case(char *s); void to_wlower_case(wchar_t *s); -wchar_t* get_program_data_path(); HANDLE get_user_token(const char* user, int impersonation); int load_user_profile(HANDLE user_token, char* user); int create_directory_withsddl(wchar_t *path, wchar_t *sddl); @@ -71,3 +76,5 @@ int exec_command_with_pty(wchar_t*, STARTUPINFOW*, PROCESS_INFORMATION*, int); char* get_custom_lsa_package(); wchar_t* get_final_path_by_handle(HANDLE h); int lookup_principal_name(const wchar_t * sam_account_name, wchar_t * user_principal_name); +BOOL is_bash_test_env(); +int bash_to_win_path(const char *in, char *out, const size_t out_len); diff --git a/contrib/win32/win32compat/pwd.c b/contrib/win32/win32compat/pwd.c index 99f7a3077..d98f0264d 100644 --- a/contrib/win32/win32compat/pwd.c +++ b/contrib/win32/win32compat/pwd.c @@ -157,7 +157,7 @@ get_passwd(const wchar_t * user_utf16, PSID sid) { wchar_t user_resolved[DNLEN + 1 + UNLEN + 1]; struct passwd *ret = NULL; - wchar_t *sid_string = NULL; + wchar_t *sid_string = NULL, *tmp = NULL, *user_utf16_modified = NULL; wchar_t reg_path[PATH_MAX], profile_home[PATH_MAX], profile_home_exp[PATH_MAX]; DWORD reg_path_len = PATH_MAX; HKEY reg_key = 0; @@ -171,13 +171,29 @@ get_passwd(const wchar_t * user_utf16, PSID sid) errno = 0; if (reset_pw() != 0) return NULL; + + /* + * We support both "domain\user" and "domain/user" formats. + * But win32 APIs only accept domain\user format so convert it. + */ + if (user_utf16) { + user_utf16_modified = _wcsdup(user_utf16); + if (!user_utf16_modified) { + errno = ENOMEM; + error("%s failed to duplicate %s", __func__, user_utf16); + goto cleanup; + } + + if (tmp = wcsstr(user_utf16_modified, L"/")) + *tmp = L'\\'; + } /* skip forward lookup on name if sid was passed in */ if (sid != NULL) CopySid(sizeof(binary_sid), binary_sid, sid); /* else attempt to lookup the account; this will verify the account is valid and * is will return its sid and the realm that owns it */ - else if(LookupAccountNameW(NULL, user_utf16, binary_sid, &sid_size, + else if(LookupAccountNameW(NULL, user_utf16_modified, binary_sid, &sid_size, domain_name, &domain_name_size, &account_type) == 0) { errno = ENOENT; debug("%s: LookupAccountName() failed: %d.", __FUNCTION__, GetLastError()); @@ -215,8 +231,8 @@ get_passwd(const wchar_t * user_utf16, PSID sid) goto cleanup; } - /* if standard local user name, just use name without decoration */ - if (_wcsicmp(domain_name, computer_name) == 0) + /* If standard local user name, just use name without decoration */ + if ((_wcsicmp(domain_name, computer_name) == 0)) wcscpy_s(user_resolved, ARRAYSIZE(user_resolved), user_name); /* put any other format in sam compatible format */ diff --git a/contrib/win32/win32compat/termio.c b/contrib/win32/win32compat/termio.c index 54bbe02e8..08ec4c910 100644 --- a/contrib/win32/win32compat/termio.c +++ b/contrib/win32/win32compat/termio.c @@ -115,8 +115,7 @@ ReadThread(_In_ LPVOID lpParameter) if (p) { *p = '\0'; - pio->read_details.buf_size = (DWORD)strlen(pio->read_details.buf); - pio->sync_read_status.transferred = pio->read_details.buf_size; + pio->sync_read_status.transferred = (DWORD)strlen(pio->read_details.buf); } } } diff --git a/contrib/win32/win32compat/tncon.c b/contrib/win32/win32compat/tncon.c index 897f051f4..24495acf7 100644 --- a/contrib/win32/win32compat/tncon.c +++ b/contrib/win32/win32compat/tncon.c @@ -42,6 +42,7 @@ #include "tnnet.h" extern bool gbVTAppMode; +extern BOOL isAnsiParsingRequired; char *glob_out = NULL; int glob_outlen = 0; int glob_space = 0; @@ -139,6 +140,14 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) BOOL bCapsOn = FALSE; BOOL bShift = FALSE; int modKey = 0; + char *FN_KEY = NULL; + char *SHIFT_FN_KEY = NULL; + char *ALT_FN_KEY = NULL; + char *CTRL_FN_KEY = NULL; + char *SHIFT_ALT_FN_KEY = NULL; + char *SHIFT_CTRL_FN_KEY = NULL; + char *ALT_CTRL_FN_KEY = NULL; + char *SHIFT_ALT_CTRL_FN_KEY = NULL; glob_out = destin; glob_space = destinlen; @@ -353,125 +362,168 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) case VK_CAPITAL: break; /* NOP on these */ case VK_F1: + /* If isAnsiParsingRequired is false then we use XTERM VT sequence */ + FN_KEY = isAnsiParsingRequired ? PF1_KEY : XTERM_PF1_KEY; + SHIFT_FN_KEY = isAnsiParsingRequired ? SHIFT_PF1_KEY : XTERM_SHIFT_PF1_KEY; + ALT_FN_KEY = isAnsiParsingRequired ? ALT_PF1_KEY : XTERM_ALT_PF1_KEY; + CTRL_FN_KEY = isAnsiParsingRequired ? CTRL_PF1_KEY : XTERM_CTRL_PF1_KEY; + SHIFT_ALT_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_PF1_KEY : XTERM_SHIFT_ALT_PF1_KEY; + SHIFT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_CTRL_PF1_KEY : XTERM_SHIFT_CTRL_PF1_KEY; + ALT_CTRL_FN_KEY = isAnsiParsingRequired ? ALT_CTRL_PF1_KEY : XTERM_ALT_CTRL_PF1_KEY; + SHIFT_ALT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_CTRL_PF1_KEY : XTERM_SHIFT_ALT_CTRL_PF1_KEY; + if (dwControlKeyState == 0) - NetWriteString2(pParams->Socket, (char *)PF1_KEY, strlen(PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)FN_KEY, strlen(FN_KEY), 0); else if (dwControlKeyState == SHIFT_PRESSED) - NetWriteString2(pParams->Socket, (char *)SHIFT_PF1_KEY, strlen(SHIFT_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_FN_KEY, strlen(SHIFT_FN_KEY), 0); else if (dwControlKeyState == LEFT_CTRL_PRESSED || dwControlKeyState == RIGHT_CTRL_PRESSED) - NetWriteString2(pParams->Socket, (char *)CTRL_PF1_KEY, strlen(CTRL_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)CTRL_FN_KEY, strlen(CTRL_FN_KEY), 0); else if (dwControlKeyState == LEFT_ALT_PRESSED || dwControlKeyState == RIGHT_ALT_PRESSED) - NetWriteString2(pParams->Socket, (char *)ALT_PF1_KEY, strlen(ALT_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_FN_KEY, strlen(ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED)) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_PF1_KEY, strlen(SHIFT_ALT_CTRL_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_FN_KEY, strlen(SHIFT_ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)ALT_CTRL_PF1_KEY, strlen(ALT_CTRL_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_CTRL_FN_KEY, strlen(ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_PF1_KEY, strlen(SHIFT_ALT_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_FN_KEY, strlen(SHIFT_ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_PF1_KEY, strlen(SHIFT_CTRL_PF1_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_FN_KEY, strlen(SHIFT_CTRL_FN_KEY), 0); break; case VK_F2: + /* If isAnsiParsingRequired is false then we use XTERM VT sequence */ + FN_KEY = isAnsiParsingRequired ? PF2_KEY : XTERM_PF2_KEY; + SHIFT_FN_KEY = isAnsiParsingRequired ? SHIFT_PF2_KEY : XTERM_SHIFT_PF2_KEY; + ALT_FN_KEY = isAnsiParsingRequired ? ALT_PF2_KEY : XTERM_ALT_PF2_KEY; + CTRL_FN_KEY = isAnsiParsingRequired ? CTRL_PF2_KEY : XTERM_CTRL_PF2_KEY; + SHIFT_ALT_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_PF2_KEY : XTERM_SHIFT_ALT_PF2_KEY; + SHIFT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_CTRL_PF2_KEY : XTERM_SHIFT_CTRL_PF2_KEY; + ALT_CTRL_FN_KEY = isAnsiParsingRequired ? ALT_CTRL_PF2_KEY : XTERM_ALT_CTRL_PF2_KEY; + SHIFT_ALT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_CTRL_PF2_KEY : XTERM_SHIFT_ALT_CTRL_PF2_KEY; + if (dwControlKeyState == 0) - NetWriteString2(pParams->Socket, (char *)PF2_KEY, strlen(PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)FN_KEY, strlen(FN_KEY), 0); else if (dwControlKeyState == SHIFT_PRESSED) - NetWriteString2(pParams->Socket, (char *)SHIFT_PF2_KEY, strlen(SHIFT_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_FN_KEY, strlen(SHIFT_FN_KEY), 0); else if (dwControlKeyState == LEFT_CTRL_PRESSED || dwControlKeyState == RIGHT_CTRL_PRESSED) - NetWriteString2(pParams->Socket, (char *)CTRL_PF2_KEY, strlen(CTRL_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)CTRL_FN_KEY, strlen(CTRL_FN_KEY), 0); else if (dwControlKeyState == LEFT_ALT_PRESSED || dwControlKeyState == RIGHT_ALT_PRESSED) - NetWriteString2(pParams->Socket, (char *)ALT_PF2_KEY, strlen(ALT_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_FN_KEY, strlen(ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED)) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_PF2_KEY, strlen(SHIFT_ALT_CTRL_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_FN_KEY, strlen(SHIFT_ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)ALT_CTRL_PF2_KEY, strlen(ALT_CTRL_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_CTRL_FN_KEY, strlen(ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_PF2_KEY, strlen(SHIFT_ALT_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_FN_KEY, strlen(SHIFT_ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_PF2_KEY, strlen(SHIFT_CTRL_PF2_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_FN_KEY, strlen(SHIFT_CTRL_FN_KEY), 0); + break; case VK_F3: + /* If isAnsiParsingRequired is false then we use XTERM VT sequence */ + FN_KEY = isAnsiParsingRequired ? PF3_KEY : XTERM_PF3_KEY; + SHIFT_FN_KEY = isAnsiParsingRequired ? SHIFT_PF3_KEY : XTERM_SHIFT_PF3_KEY; + ALT_FN_KEY = isAnsiParsingRequired ? ALT_PF3_KEY : XTERM_ALT_PF3_KEY; + CTRL_FN_KEY = isAnsiParsingRequired ? CTRL_PF3_KEY : XTERM_CTRL_PF3_KEY; + SHIFT_ALT_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_PF3_KEY : XTERM_SHIFT_ALT_PF3_KEY; + SHIFT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_CTRL_PF3_KEY : XTERM_SHIFT_CTRL_PF3_KEY; + ALT_CTRL_FN_KEY = isAnsiParsingRequired ? ALT_CTRL_PF3_KEY : XTERM_ALT_CTRL_PF3_KEY; + SHIFT_ALT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_CTRL_PF3_KEY : XTERM_SHIFT_ALT_CTRL_PF3_KEY; + if (dwControlKeyState == 0) - NetWriteString2(pParams->Socket, (char *)PF3_KEY, strlen(PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)FN_KEY, strlen(FN_KEY), 0); else if (dwControlKeyState == SHIFT_PRESSED) - NetWriteString2(pParams->Socket, (char *)SHIFT_PF3_KEY, strlen(SHIFT_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_FN_KEY, strlen(SHIFT_FN_KEY), 0); else if (dwControlKeyState == LEFT_CTRL_PRESSED || dwControlKeyState == RIGHT_CTRL_PRESSED) - NetWriteString2(pParams->Socket, (char *)CTRL_PF3_KEY, strlen(CTRL_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)CTRL_FN_KEY, strlen(CTRL_FN_KEY), 0); else if (dwControlKeyState == LEFT_ALT_PRESSED || dwControlKeyState == RIGHT_ALT_PRESSED) - NetWriteString2(pParams->Socket, (char *)ALT_PF3_KEY, strlen(ALT_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_FN_KEY, strlen(ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED)) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_PF3_KEY, strlen(SHIFT_ALT_CTRL_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_FN_KEY, strlen(SHIFT_ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)ALT_CTRL_PF3_KEY, strlen(ALT_CTRL_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_CTRL_FN_KEY, strlen(ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_PF3_KEY, strlen(SHIFT_ALT_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_FN_KEY, strlen(SHIFT_ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_PF3_KEY, strlen(SHIFT_CTRL_PF3_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_FN_KEY, strlen(SHIFT_CTRL_FN_KEY), 0); + break; case VK_F4: + /* If isAnsiParsingRequired is false then we use XTERM VT sequence */ + FN_KEY = isAnsiParsingRequired ? PF4_KEY : XTERM_PF4_KEY; + SHIFT_FN_KEY = isAnsiParsingRequired ? SHIFT_PF4_KEY : XTERM_SHIFT_PF4_KEY; + ALT_FN_KEY = isAnsiParsingRequired ? ALT_PF4_KEY : XTERM_ALT_PF4_KEY; + CTRL_FN_KEY = isAnsiParsingRequired ? CTRL_PF4_KEY : XTERM_CTRL_PF4_KEY; + SHIFT_ALT_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_PF4_KEY : XTERM_SHIFT_ALT_PF4_KEY; + SHIFT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_CTRL_PF4_KEY : XTERM_SHIFT_CTRL_PF4_KEY; + ALT_CTRL_FN_KEY = isAnsiParsingRequired ? ALT_CTRL_PF4_KEY : XTERM_ALT_CTRL_PF4_KEY; + SHIFT_ALT_CTRL_FN_KEY = isAnsiParsingRequired ? SHIFT_ALT_CTRL_PF4_KEY : XTERM_SHIFT_ALT_CTRL_PF4_KEY; + if (dwControlKeyState == 0) - NetWriteString2(pParams->Socket, (char *)PF4_KEY, strlen(PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)FN_KEY, strlen(FN_KEY), 0); else if (dwControlKeyState == SHIFT_PRESSED) - NetWriteString2(pParams->Socket, (char *)SHIFT_PF4_KEY, strlen(SHIFT_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_FN_KEY, strlen(SHIFT_FN_KEY), 0); else if (dwControlKeyState == LEFT_CTRL_PRESSED || dwControlKeyState == RIGHT_CTRL_PRESSED) - NetWriteString2(pParams->Socket, (char *)CTRL_PF4_KEY, strlen(CTRL_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)CTRL_FN_KEY, strlen(CTRL_FN_KEY), 0); else if (dwControlKeyState == LEFT_ALT_PRESSED || dwControlKeyState == RIGHT_ALT_PRESSED) - NetWriteString2(pParams->Socket, (char *)ALT_PF4_KEY, strlen(ALT_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_FN_KEY, strlen(ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED)) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_PF4_KEY, strlen(SHIFT_ALT_CTRL_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_CTRL_FN_KEY, strlen(SHIFT_ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)ALT_CTRL_PF4_KEY, strlen(ALT_CTRL_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)ALT_CTRL_FN_KEY, strlen(ALT_CTRL_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & RIGHT_ALT_PRESSED) || (dwControlKeyState & LEFT_ALT_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_PF4_KEY, strlen(SHIFT_ALT_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_ALT_FN_KEY, strlen(SHIFT_ALT_FN_KEY), 0); else if ((dwControlKeyState & SHIFT_PRESSED) && ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED))) - NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_PF4_KEY, strlen(SHIFT_CTRL_PF4_KEY), 0); + NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_FN_KEY, strlen(SHIFT_CTRL_FN_KEY), 0); + break; case VK_F5: if (dwControlKeyState == 0) diff --git a/contrib/win32/win32compat/tncon.h b/contrib/win32/win32compat/tncon.h index 6b625d73d..1f4bcdc1b 100644 --- a/contrib/win32/win32compat/tncon.h +++ b/contrib/win32/win32compat/tncon.h @@ -94,8 +94,8 @@ #define SHIFT_PF8_KEY "\x1b[19;2~" #define SHIFT_PF9_KEY "\x1b[20;2~" #define SHIFT_PF10_KEY "\x1b[21;2~" -#define SHIFT_PF11_KEY "\x1b[24;2~" -#define SHIFT_PF12_KEY "\x1b[25;2~" +#define SHIFT_PF11_KEY "\x1b[23;2~" +#define SHIFT_PF12_KEY "\x1b[24;2~" #define ALT_PF1_KEY "\x1b[11;3~" #define ALT_PF2_KEY "\x1b[12;3~" @@ -107,21 +107,21 @@ #define ALT_PF8_KEY "\x1b[19;3~" #define ALT_PF9_KEY "\x1b[20;3~" #define ALT_PF10_KEY "\x1b[21;3~" -#define ALT_PF11_KEY "\x1b[24;3~" -#define ALT_PF12_KEY "\x1b[25;3~" +#define ALT_PF11_KEY "\x1b[23;3~" +#define ALT_PF12_KEY "\x1b[24;3~" -#define CTRL_PF1_KEY "\x1b[11;4~" -#define CTRL_PF2_KEY "\x1b[12;4~" -#define CTRL_PF3_KEY "\x1b[13;4~" -#define CTRL_PF4_KEY "\x1b[14;4~" -#define CTRL_PF5_KEY "\x1b[15;4~" -#define CTRL_PF6_KEY "\x1b[17;4~" -#define CTRL_PF7_KEY "\x1b[18;4~" -#define CTRL_PF8_KEY "\x1b[19;4~" -#define CTRL_PF9_KEY "\x1b[20;4~" -#define CTRL_PF10_KEY "\x1b[21;4~" -#define CTRL_PF11_KEY "\x1b[24;4~" -#define CTRL_PF12_KEY "\x1b[25;4~" +#define CTRL_PF1_KEY "\x1b[11;5~" +#define CTRL_PF2_KEY "\x1b[12;5~" +#define CTRL_PF3_KEY "\x1b[13;5~" +#define CTRL_PF4_KEY "\x1b[14;5~" +#define CTRL_PF5_KEY "\x1b[15;5~" +#define CTRL_PF6_KEY "\x1b[17;5~" +#define CTRL_PF7_KEY "\x1b[18;5~" +#define CTRL_PF8_KEY "\x1b[19;5~" +#define CTRL_PF9_KEY "\x1b[20;5~" +#define CTRL_PF10_KEY "\x1b[21;5~" +#define CTRL_PF11_KEY "\x1b[23;5~" +#define CTRL_PF12_KEY "\x1b[24;5~" #define SHIFT_CTRL_PF1_KEY "\x1b[11;6~" #define SHIFT_CTRL_PF2_KEY "\x1b[12;6~" @@ -133,21 +133,21 @@ #define SHIFT_CTRL_PF8_KEY "\x1b[19;6~" #define SHIFT_CTRL_PF9_KEY "\x1b[20;6~" #define SHIFT_CTRL_PF10_KEY "\x1b[21;6~" -#define SHIFT_CTRL_PF11_KEY "\x1b[24;6~" -#define SHIFT_CTRL_PF12_KEY "\x1b[25;6~" +#define SHIFT_CTRL_PF11_KEY "\x1b[23;6~" +#define SHIFT_CTRL_PF12_KEY "\x1b[24;6~" -#define SHIFT_ALT_PF1_KEY "\x1b[11;5~" -#define SHIFT_ALT_PF2_KEY "\x1b[12;5~" -#define SHIFT_ALT_PF3_KEY "\x1b[13;5~" -#define SHIFT_ALT_PF4_KEY "\x1b[14;5~" -#define SHIFT_ALT_PF5_KEY "\x1b[15;5~" -#define SHIFT_ALT_PF6_KEY "\x1b[17;5~" -#define SHIFT_ALT_PF7_KEY "\x1b[18;5~" -#define SHIFT_ALT_PF8_KEY "\x1b[19;5~" -#define SHIFT_ALT_PF9_KEY "\x1b[20;5~" -#define SHIFT_ALT_PF10_KEY "\x1b[21;5~" -#define SHIFT_ALT_PF11_KEY "\x1b[24;5~" -#define SHIFT_ALT_PF12_KEY "\x1b[25;5~" +#define SHIFT_ALT_PF1_KEY "\x1b[11;4~" +#define SHIFT_ALT_PF2_KEY "\x1b[12;4~" +#define SHIFT_ALT_PF3_KEY "\x1b[13;4~" +#define SHIFT_ALT_PF4_KEY "\x1b[14;4~" +#define SHIFT_ALT_PF5_KEY "\x1b[15;4~" +#define SHIFT_ALT_PF6_KEY "\x1b[17;4~" +#define SHIFT_ALT_PF7_KEY "\x1b[18;4~" +#define SHIFT_ALT_PF8_KEY "\x1b[19;4~" +#define SHIFT_ALT_PF9_KEY "\x1b[20;4~" +#define SHIFT_ALT_PF10_KEY "\x1b[21;4~" +#define SHIFT_ALT_PF11_KEY "\x1b[23;4~" +#define SHIFT_ALT_PF12_KEY "\x1b[24;4~" #define ALT_CTRL_PF1_KEY "\x1b[11;7~" #define ALT_CTRL_PF2_KEY "\x1b[12;7~" @@ -159,8 +159,8 @@ #define ALT_CTRL_PF8_KEY "\x1b[19;7~" #define ALT_CTRL_PF9_KEY "\x1b[20;7~" #define ALT_CTRL_PF10_KEY "\x1b[21;7~" -#define ALT_CTRL_PF11_KEY "\x1b[24;7~" -#define ALT_CTRL_PF12_KEY "\x1b[25;7~" +#define ALT_CTRL_PF11_KEY "\x1b[23;7~" +#define ALT_CTRL_PF12_KEY "\x1b[24;7~" #define SHIFT_ALT_CTRL_PF1_KEY "\x1b[11;8~" #define SHIFT_ALT_CTRL_PF2_KEY "\x1b[12;8~" @@ -172,8 +172,49 @@ #define SHIFT_ALT_CTRL_PF8_KEY "\x1b[19;8~" #define SHIFT_ALT_CTRL_PF9_KEY "\x1b[20;8~" #define SHIFT_ALT_CTRL_PF10_KEY "\x1b[21;8~" -#define SHIFT_ALT_CTRL_PF11_KEY "\x1b[24;8~" -#define SHIFT_ALT_CTRL_PF12_KEY "\x1b[25;8~" +#define SHIFT_ALT_CTRL_PF11_KEY "\x1b[23;8~" +#define SHIFT_ALT_CTRL_PF12_KEY "\x1b[24;8~" + +/* XTERM (https://github.com/mintty/mintty/wiki/Keycodes#function-keys) */ +#define XTERM_PF1_KEY "\x1bOP" +#define XTERM_PF2_KEY "\x1bOQ" +#define XTERM_PF3_KEY "\x1bOR" +#define XTERM_PF4_KEY "\x1bOS" + +#define XTERM_SHIFT_PF1_KEY "\x1b[1;2P" +#define XTERM_SHIFT_PF2_KEY "\x1b[1;2Q" +#define XTERM_SHIFT_PF3_KEY "\x1b[1;2R" +#define XTERM_SHIFT_PF4_KEY "\x1b[1;2S" + +#define XTERM_ALT_PF1_KEY "\x1b[1;3P" +#define XTERM_ALT_PF2_KEY "\x1b[1;3Q" +#define XTERM_ALT_PF3_KEY "\x1b[1;3R" +#define XTERM_ALT_PF4_KEY "\x1b[1;3S" + +#define XTERM_CTRL_PF1_KEY "\x1b[1;5P" +#define XTERM_CTRL_PF2_KEY "\x1b[1;5Q" +#define XTERM_CTRL_PF3_KEY "\x1b[1;5R" +#define XTERM_CTRL_PF4_KEY "\x1b[1;5S" + +#define XTERM_SHIFT_ALT_PF1_KEY "\x1b[1;4P" +#define XTERM_SHIFT_ALT_PF2_KEY "\x1b[1;4Q" +#define XTERM_SHIFT_ALT_PF3_KEY "\x1b[1;4R" +#define XTERM_SHIFT_ALT_PF4_KEY "\x1b[1;4S" + +#define XTERM_SHIFT_CTRL_PF1_KEY "\x1b[1;6P" +#define XTERM_SHIFT_CTRL_PF2_KEY "\x1b[1;6Q" +#define XTERM_SHIFT_CTRL_PF3_KEY "\x1b[1;6R" +#define XTERM_SHIFT_CTRL_PF4_KEY "\x1b[1;6S" + +#define XTERM_ALT_CTRL_PF1_KEY "\x1b[1;7P" +#define XTERM_ALT_CTRL_PF2_KEY "\x1b[1;7Q" +#define XTERM_ALT_CTRL_PF3_KEY "\x1b[1;7R" +#define XTERM_ALT_CTRL_PF4_KEY "\x1b[1;7S" + +#define XTERM_SHIFT_ALT_CTRL_PF1_KEY "\x1b[1;8P" +#define XTERM_SHIFT_ALT_CTRL_PF2_KEY "\x1b[1;8Q" +#define XTERM_SHIFT_ALT_CTRL_PF3_KEY "\x1b[1;8R" +#define XTERM_SHIFT_ALT_CTRL_PF4_KEY "\x1b[1;8S" #define TERMINAL_ID "\x1b[?1;2c" #define STATUS_REPORT "\x1b[2;5R" diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index 2c787611e..2261283c6 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -78,6 +78,9 @@ char* __progname = ""; char* __progdir = ""; wchar_t* __wprogdir = L""; +/* __progdata */ +char* __progdata = ""; +wchar_t* __wprogdata = L""; /* initializes mapping table*/ static int @@ -196,7 +199,7 @@ init_prog_paths() if ((__wprogdir = _wcsdup(wpgmptr)) == NULL || (__progdir = utf16_to_utf8(__wprogdir)) == NULL) - fatal("out of memory"); + fatal("%s out of memory", __func__); __progname = strrchr(__progdir, '\\') + 1; /* TODO: retain trailing \ at the end of progdir* variants ? */ @@ -206,6 +209,16 @@ init_prog_paths() /* strip .exe off __progname */ *(__progname + strlen(__progname) - 4) = '\0'; + /* get %programdata% value */ + size_t len = 0; + _dupenv_s(&__progdata, &len, "ProgramData"); + + if (!__progdata) + fatal("couldn't find ProgramData environment variable"); + + if(!(__wprogdata = utf8_to_utf16(__progdata))) + fatal("%s out of memory", __func__, __LINE__); + processed = 1; } @@ -1028,22 +1041,33 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL DWORD cmdline_len = 0; wchar_t * cmdline_utf16 = NULL; int add_module_path = 0, ret = -1; + char *path = NULL; - /* should module path be added */ if (!cmd) { error("%s invalid argument cmd:%s", __func__, cmd); - return -1; + return ret; } - t = cmd; - if (!is_absolute_path(t) && prepend_module_path) + if (!(path = _strdup(cmd))) { + error("failed to duplicate %s", cmd); + return ret; + } + + if (is_bash_test_env()) { + size_t len = strlen(path) + 1; + memset(path, 0, len); + + bash_to_win_path(cmd, path, len); + } + + if (!is_absolute_path(path) && prepend_module_path) add_module_path = 1; /* compute total cmdline len*/ if (add_module_path) - cmdline_len += (DWORD)strlen(__progdir) + 1 + (DWORD)strlen(cmd) + 1 + 2; + cmdline_len += (DWORD)strlen(__progdir) + 1 + (DWORD)strlen(path) + 1 + 2; else - cmdline_len += (DWORD)strlen(cmd) + 1 + 2; + cmdline_len += (DWORD)strlen(path) + 1 + 2; if (argv) { t1 = argv; @@ -1058,19 +1082,34 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL /* add current module path to start if needed */ t = cmdline; - if (argv && argv[0]) - *t++ = '\"'; + *t++ = '\"'; if (add_module_path) { memcpy(t, __progdir, strlen(__progdir)); t += strlen(__progdir); *t++ = '\\'; } - memcpy(t, cmd, strlen(cmd)); - t += strlen(cmd); + /* Add double quotes around the executable path + * path can be c:\cygwin64\bin\sh.exe "" + * Please note that, this logic is not just bash test specific. + */ + const char *exe_extenstion = ".exe"; + const char *tmp = NULL; + if ((tmp = strstr(path, exe_extenstion)) && (strlen(tmp) > strlen(exe_extenstion))) { + tmp += strlen(exe_extenstion); /* move the pointer to the end of ".exe" */ + + memcpy(t, path, strlen(path)-strlen(tmp)); + t += strlen(path) - strlen(tmp); - if (argv && argv[0]) *t++ = '\"'; + memcpy(t, tmp, strlen(tmp)); + t += strlen(tmp); + } else { + memcpy(t, path, strlen(path)); + t += strlen(path); + + *t++ = '\"'; + } if (argv) { t1 = argv; @@ -1125,6 +1164,8 @@ cleanup: free(cmdline); if (cmdline_utf16) free(cmdline_utf16); + if (path) + free(path); return ret; } diff --git a/contrib/win32/win32compat/w32log.c b/contrib/win32/win32compat/w32log.c index 6d64bc5bf..365c99c21 100644 --- a/contrib/win32/win32compat/w32log.c +++ b/contrib/win32/win32compat/w32log.c @@ -111,7 +111,7 @@ openlog_file() tail--; wchar_t ssh_cfg_path[PATH_MAX] = {0 ,}; - wcscat_s(ssh_cfg_path, _countof(ssh_cfg_path), get_program_data_path()); /* "%programData%" */ + wcscat_s(ssh_cfg_path, _countof(ssh_cfg_path), __wprogdata); /* "%programData%" */ wcscat_s(ssh_cfg_path, _countof(ssh_cfg_path), L"\\ssh"); /* "%programData%\\ssh" */ if ((wcsncat_s(log_file, PATH_MAX + 12, ssh_cfg_path, wcslen(ssh_cfg_path)) != 0) || diff --git a/contrib/win32/win32compat/win32_groupaccess.c b/contrib/win32/win32compat/win32_groupaccess.c index b95d0cd87..f18377ebd 100644 --- a/contrib/win32/win32compat/win32_groupaccess.c +++ b/contrib/win32/win32compat/win32_groupaccess.c @@ -219,9 +219,18 @@ ga_init(const char *user, gid_t base) user_name = xstrdup(user); - if ((user_token = get_user_token(user_name, 0)) == NULL) - fatal("%s, unable to resolve user %s", __func__, user_name); - + if ((user_token = get_user_token(user_name, 0)) == NULL) { + /* + * TODO - We need to fatal() all the times when we fail to generate the user token. + */ + if (get_custom_lsa_package()) { + error("%s, unable to resolve user %s", __func__, user_name); + return 0; + } else { + fatal("%s, unable to resolve user %s", __func__, user_name); + } + } + /* * supposed to retun number of groups associated with user * since we do lazy group evaluation, returning 1 here diff --git a/contrib/win32/win32compat/win32_pty.c b/contrib/win32/win32compat/win32_pty.c index 3d725065c..7c3636798 100644 --- a/contrib/win32/win32compat/win32_pty.c +++ b/contrib/win32/win32compat/win32_pty.c @@ -73,18 +73,26 @@ CreateConPty(const wchar_t *cmdline, debug3("pty commandline: %ls", conhostCmdline); + /* + * process CTRL+C input. + * Child processes will inherit this behavior. + */ + SetConsoleCtrlHandler(NULL, FALSE); if (0 == CreateProcessW(NULL, conhostCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, piPty)) { debug("%s - failed to execute %ls, error:%d", __func__, conhostCmdline, GetLastError()); errno = EOTHER; return -1; } + /* disable Ctrl+C hander in this process*/ + SetConsoleCtrlHandler(NULL, TRUE); + return 0; } int is_conpty_supported() { - /* TODO - enable this once conpty changes are validated */ + /* TODO - put conditional logic in here that determines if conpty is supported */ return 0; } diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index 1e40a8eda..32ec88160 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -81,8 +81,8 @@ static VOID WINAPI service_handler(DWORD dwControl) case SERVICE_CONTROL_STOP: { ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 500); ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); - /* TOTO - GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); doesn't seem to be invoking - * signal handler (native_sig_handler) when sshd runs as service + /* TODO - GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); doesn't seem to be invoking + * signal handler (native_sig_handler) when sshd runs as service * So calling the signal handler directly to interrupt the deamon's main thread * This is being called after reporting SERVICE_STOPPED because main thread does a exit() * as part of handling Crtl+c @@ -100,7 +100,7 @@ static VOID WINAPI service_handler(DWORD dwControl) } #define SSH_HOSTKEY_GEN_CMDLINE L"ssh-keygen -A" -static void +static void generate_host_keys() { DWORD dwError = 0; @@ -162,12 +162,12 @@ generate_host_keys() * 2) Create %programdata%\ssh\logs - Administrator group(F), system(F) * 3) copy \sshd_config_default to %programdata%\ssh\sshd_config */ -static void +static void create_prgdata_ssh_folder() { /* create ssh cfg folder */ wchar_t ssh_cfg_dir[PATH_MAX] = { 0, }; - wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), get_program_data_path()); + wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata); wcscat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), L"\\ssh"); if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") < 0) { printf("failed to create %s", ssh_cfg_dir); @@ -228,7 +228,7 @@ create_openssh_registry_key() static void prereq_setup() -{ +{ create_prgdata_ssh_folder(); generate_host_keys(); create_openssh_registry_key(); @@ -251,7 +251,7 @@ int sshd_main(int argc, wchar_t **wargv) { w32posix_initialize(); - r = main(argc, argv); + r = main(argc, argv); w32posix_done(); return r; } @@ -263,7 +263,7 @@ int wmain(int argc, wchar_t **wargv) { wchar_t* path_utf16; argc_original = argc; wargv_original = wargv; - + init_prog_paths(); /* change current directory to sshd.exe root */ _wchdir(__wprogdir); @@ -287,5 +287,3 @@ int scm_start_service(DWORD num, LPWSTR* args) { ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); return sshd_main(argc_original, wargv_original); } - - diff --git a/misc.c b/misc.c index e875de0b9..160d2127e 100644 --- a/misc.c +++ b/misc.c @@ -784,6 +784,16 @@ parse_uri(const char *scheme, const char *uri, char **userp, char **hostp, if ((cp = strchr(tmp, '@')) != NULL) { char *delim; +#ifdef WINDOWS + /* TODO - This looks to be a core bug in unix code as user can be in UPN format + * The above line should be strrchr() instead of strchr. + * For time being, special handling when username is in User@domain format + */ + + char *cp_1 = cp; + if ((cp_1 = strchr(cp + 1, '@')) != NULL) + cp = cp_1; +#endif *cp = '\0'; /* Extract username and connection params */ if ((delim = strchr(tmp, ';')) != NULL) { diff --git a/monitor.c b/monitor.c index 755ca35f9..e8ca777f2 100644 --- a/monitor.c +++ b/monitor.c @@ -1661,6 +1661,43 @@ monitor_clear_keystate(struct monitor *pmonitor) child_state = NULL; } +void +monitor_send_authopt(struct monitor *pmonitor, int untrusted) { + struct sshbuf *m = NULL; + int r = 0; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + if (auth_opts != NULL && (r = sshauthopt_serialise(auth_opts, m, untrusted)) != 0) + fatal("%s: sshauthopt_serialise: %s", __func__, ssh_err(r)); + + if (ssh_msg_send(pmonitor->m_sendfd, 0, m) == -1) + fatal("%s: ssh_msg_send failed", __func__); + + sshbuf_free(m); +} + +void +monitor_recv_authopt(struct monitor*pmonitor) { + Buffer m; + int r = 0; + + buffer_init(&m); + + if (ssh_msg_recv(pmonitor->m_recvfd, &m) == -1) + fatal("%s: ssh_msg_recv failed", __func__); + + if (buffer_get_char(&m) != 0) + fatal("%s: version mismatch", __func__); + + if ((r = sshauthopt_deserialise(&m, &auth_opts)) != 0) + fatal("%s: sshauthopt_deserialise: %s", + __func__, ssh_err(r)); + + buffer_free(&m); +} + void monitor_apply_keystate(struct monitor *pmonitor) { @@ -1889,3 +1926,4 @@ mm_answer_gss_userok(int sock, Buffer *m) } #endif /* GSSAPI */ + diff --git a/readconf.c b/readconf.c index 2a1a1d6de..e3cd62937 100644 --- a/readconf.c +++ b/readconf.c @@ -480,11 +480,18 @@ default_ssh_port(void) static int execute_in_shell(const char *cmd) { - char *shell; #ifdef WINDOWS - fatal("LocalCommand execution is not supported on Windows yet"); - return 0; + int retVal = -1; + wchar_t *cmd_w = utf8_to_utf16(cmd); + + if (cmd_w) { + retVal = _wsystem(cmd_w); + free(cmd_w); + } + + return retVal; #else /* !WINDOWS */ + char *shell; pid_t pid; int devnull, status; extern uid_t original_real_uid; diff --git a/regress/.gitattributes b/regress/.gitattributes new file mode 100644 index 000000000..434a50934 --- /dev/null +++ b/regress/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Declare files that will always have LF line endings on checkout. +*.sh text eol=lf \ No newline at end of file diff --git a/regress/addrmatch.sh b/regress/addrmatch.sh index 1584bd405..2741389ee 100644 --- a/regress/addrmatch.sh +++ b/regress/addrmatch.sh @@ -14,6 +14,9 @@ run_trial() result=`${SSHD} -f $OBJ/sshd_proxy -T \ -C user=${user},addr=${addr},host=${host},laddr=${laddr},lport=${lport} | \ awk '/^forcecommand/ {print $2}'` + if [ "$os" == "windows" ]; then + result=${result/$'\r'/} # remove CR (carriage return) + fi if [ "$result" != "$expected" ]; then fail "failed '$descr' expected $expected got $result" fi diff --git a/regress/agent-timeout.sh b/regress/agent-timeout.sh index 9598c2032..8d7b53ccb 100644 --- a/regress/agent-timeout.sh +++ b/regress/agent-timeout.sh @@ -2,7 +2,10 @@ # Placed in the Public Domain. tid="agent timeout test" - +if [ "$os" == "windows" ]; then + echo "skipped (not supported on WINDOWS platform)" + exit 0 +fi SSHAGENT_TIMEOUT=10 trace "start agent" diff --git a/regress/agent.sh b/regress/agent.sh index 7111056c9..eaec6e907 100644 --- a/regress/agent.sh +++ b/regress/agent.sh @@ -14,7 +14,11 @@ r=$? if [ $r -ne 0 ]; then fatal "could not start ssh-agent: exit code $r" fi - +if [ "$os" == "windows" ]; then + #windows ssh-agent doesn't support "-s" option so we need to set SSH_AUTH_SOCK env here. + SSH_AUTH_SOCK="\\\\.\\pipe\\openssh-ssh-agent" + ${SSHADD} -D +fi ${SSHADD} -l > /dev/null 2>&1 if [ $? -ne 1 ]; then fail "ssh-add -l did not fail with exit code 1" diff --git a/regress/banner.sh b/regress/banner.sh index 0d9654fe2..63f2b569b 100644 --- a/regress/banner.sh +++ b/regress/banner.sh @@ -30,9 +30,18 @@ for s in 0 10 100 1000 10000 100000 ; do trace "test banner size $s" verbose "test $tid: size $s" - ( ${SSH} -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ - cmp $OBJ/banner.in $OBJ/banner.out ) || \ - fail "banner size $s mismatch" + if [ "$os" == "windows" ]; then + # For windows, compare files by ignoring line breaks (CR vs CRLF). + # CYGWIN created files (banner.in) will have CR. + # SSH output files (banner.out) will have CRLF. + ( ${SSH} -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ + diff --strip-trailing-cr $OBJ/banner.in $OBJ/banner.out ) || \ + fail "banner size $s mismatch" + else + ( ${SSH} -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ + cmp $OBJ/banner.in $OBJ/banner.out ) || \ + fail "banner size $s mismatch" + fi done trace "test suppress banner (-q)" diff --git a/regress/cert-hostkey.sh b/regress/cert-hostkey.sh index 3d5732a5d..3e4b0d426 100644 --- a/regress/cert-hostkey.sh +++ b/regress/cert-hostkey.sh @@ -9,6 +9,9 @@ rm -f $OBJ/cert_host_key* $OBJ/host_krl_* # Allow all hostkey/pubkey types, prefer certs for the client types="" for i in `$SSH -Q key`; do + if [ "$os" == "windows" ]; then + i=${i/$'\r'/} # remove CR (carriage return) + fi if [ -z "$types" ]; then types="$i" continue @@ -59,7 +62,12 @@ touch $OBJ/host_revoked_plain touch $OBJ/host_revoked_cert cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca -PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` +if [ "$os" == "windows" ]; then + # remove CR (carriage return) + PLAIN_TYPES=`$SSH -Q key-plain | sed 's/\r$//' | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` +else + PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` +fi if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" diff --git a/regress/cert-userkey.sh b/regress/cert-userkey.sh index 30c2c156d..c6adbbfa5 100644 --- a/regress/cert-userkey.sh +++ b/regress/cert-userkey.sh @@ -6,8 +6,12 @@ tid="certified user keys" rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key* cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak - -PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` +if [ "$os" == "windows" ]; then + # remove CR (carriage return) + PLAIN_TYPES=`$SSH -Q key-plain | sed 's/\r$//' | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` +else + PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` +fi EXTRA_TYPES="" if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then diff --git a/regress/cfginclude.sh b/regress/cfginclude.sh index 2fc39ce45..3cfee77ab 100644 --- a/regress/cfginclude.sh +++ b/regress/cfginclude.sh @@ -86,9 +86,17 @@ _EOF trial() { _host="$1" _exp="$2" + if [ "$os" == "windows" ]; then + # Fix the file permissions (ACLs) + OBJ_WIN=`windows_path $OBJ` + powershell.exe /c "get-acl $OBJ_WIN/authorized_keys_$USER | set-acl $OBJ_WIN/ssh_config.i.*" + fi ${REAL_SSH} -F $OBJ/ssh_config.i -G "$_host" > $OBJ/ssh_config.out || fatal "ssh config parse failed" _got=`grep -i '^hostname ' $OBJ/ssh_config.out | awk '{print $2}'` + if [ "$os" == "windows" ]; then + _got=`echo $_got | sed 's/\r$//'` # remove CR (carriage return) + fi if test "x$_exp" != "x$_got" ; then fail "host $_host include fail: expected $_exp got $_got" fi @@ -227,9 +235,18 @@ _EOF trial() { _host="$1" _exp="$2" + if [ "$os" == "windows" ]; then + OBJ_WIN=`windows_path $OBJ` + # Fix the file permissions (ACLs) + powershell.exe /c "get-acl $OBJ_WIN/authorized_keys_$USER | set-acl $OBJ_WIN/ssh_config.i.*" + fi ${REAL_SSH} -F $OBJ/ssh_config.i -G "$_host" > $OBJ/ssh_config.out || fatal "ssh config parse failed" _got=`grep -i '^hostname ' $OBJ/ssh_config.out | awk '{print $2}'` + if [ "$os" == "windows" ]; then + # remove CR (carriage return) + _got=`echo $_got | sed 's/\r$//'` + fi if test "x$_exp" != "x$_got" ; then fail "host $_host include fail: expected $_exp got $_got" fi diff --git a/regress/cfgmatch.sh b/regress/cfgmatch.sh index dd11e404d..768a89753 100644 --- a/regress/cfgmatch.sh +++ b/regress/cfgmatch.sh @@ -23,19 +23,30 @@ start_client() sleep 1 n=`expr $n + 1` if test $n -gt 60; then - kill $client_pid + if [ "$os" == "windows" ]; then + # We can't kill windows process from cygwin / wsl so use "stop-process" + powershell.exe /c "stop-process -id $client_pid" + else + kill $client_pid + fi fatal "timeout waiting for background ssh" fi - done + done } stop_client() { pid=`cat $pidfile` - if [ ! -z "$pid" ]; then - kill $pid + if [ "$os" == "windows" ]; then + # We can't kill windows process from cygwin / wsl so use "stop-process" + powershell.exe /c "stop-process -id $pid" + powershell.exe /c "stop-process -name sleep" + else + if [ ! -z "$pid" ]; then + kill $pid + fi + wait fi - wait } cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak @@ -46,7 +57,13 @@ echo "PermitOpen 127.0.0.1:2 127.0.0.1:3 127.0.0.1:$PORT" >>$OBJ/sshd_config grep -v AuthorizedKeysFile $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy echo "AuthorizedKeysFile /dev/null" >>$OBJ/sshd_proxy echo "PermitOpen 127.0.0.1:1" >>$OBJ/sshd_proxy -echo "Match user $USER" >>$OBJ/sshd_proxy +if [ "$os" == "windows" ]; then + # If User is domainuser then it will be in "domain/user" so convert it to "domain\user" + echo "Match user ${USER//\//\\}" >>$OBJ/sshd_proxy +else + echo "Match user $USER" >>$OBJ/sshd_proxy +fi + echo "AuthorizedKeysFile /dev/null $OBJ/authorized_keys_%u" >>$OBJ/sshd_proxy echo "Match Address 127.0.0.1" >>$OBJ/sshd_proxy echo "PermitOpen 127.0.0.1:2 127.0.0.1:3 127.0.0.1:$PORT" >>$OBJ/sshd_proxy @@ -91,7 +108,12 @@ stop_client cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy echo "PermitOpen 127.0.0.1:1 127.0.0.1:$PORT 127.0.0.2:2" >>$OBJ/sshd_proxy -echo "Match User $USER" >>$OBJ/sshd_proxy +if [ "$os" == "windows" ]; then + # If User is domainuser then it will be in "domain/user" so convert it to "domain\user" + echo "Match user ${USER//\//\\}" >>$OBJ/sshd_proxy +else + echo "Match user $USER" >>$OBJ/sshd_proxy +fi echo "PermitOpen 127.0.0.1:1 127.0.0.1:2" >>$OBJ/sshd_proxy # Test that a Match overrides a PermitOpen in the global section diff --git a/regress/cfgparse.sh b/regress/cfgparse.sh index ccf511f6b..6919accfa 100644 --- a/regress/cfgparse.sh +++ b/regress/cfgparse.sh @@ -49,11 +49,18 @@ EOD [ X${SKIP_IPV6} = Xyes ] || cat >> $OBJ/sshd_config.1 <$OBJ/sshd_config.2 && - diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ - fail "listenaddress order 1" +if [ "$os" == "windows" ]; then + # Ignore the CR (carriage return) during diff + ($SUDO ${SSHD} -T -f $OBJ/sshd_config.1 | \ + grep 'listenaddress ' >$OBJ/sshd_config.2 && + diff --strip-trailing-cr $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 1" +else + ($SUDO ${SSHD} -T -f $OBJ/sshd_config.1 | \ + grep 'listenaddress ' >$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 1" +fi # test 2: listenaddress first cat > $OBJ/sshd_config.1 <> $OBJ/sshd_config.1 <$OBJ/sshd_config.2 && - diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ - fail "listenaddress order 2" +if [ "$os" == "windows" ]; then + # Ignore the CR (carriage return) during diff + ($SUDO ${SSHD} -T -f $OBJ/sshd_config.1 | \ + grep 'listenaddress ' >$OBJ/sshd_config.2 && + diff --strip-trailing-cr $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 2" +else + ($SUDO ${SSHD} -T -f $OBJ/sshd_config.1 | \ + grep 'listenaddress ' >$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 2" +fi # cleanup rm -f $OBJ/sshd_config.[012] diff --git a/regress/dynamic-forward.sh b/regress/dynamic-forward.sh index 84f8ee192..afca70019 100644 --- a/regress/dynamic-forward.sh +++ b/regress/dynamic-forward.sh @@ -3,6 +3,12 @@ tid="dynamic forwarding" +if [ "$os" == "windows" ]; then + # Windows, ssh.exe -S option is not supported on windows + echo "skipped, not applicable on windows OS" + exit 0 +fi + FWDPORT=`expr $PORT + 1` if have_prog nc && nc -h 2>&1 | grep "proxy address" >/dev/null; then diff --git a/regress/envpass.sh b/regress/envpass.sh index af7eafe3d..bd3305c1e 100644 --- a/regress/envpass.sh +++ b/regress/envpass.sh @@ -2,6 +2,11 @@ # Placed in the Public Domain. tid="environment passing" +if [ "$os" == "windows" ]; then + # Windows, ssh client hungs.. To be investigated.. + echo "skipped, not applicable on windows OS" + exit 0 +fi # NB accepted env vars are in test-exec.sh (_XXX_TEST_* and _XXX_TEST) diff --git a/regress/forcecommand.sh b/regress/forcecommand.sh index e059f1fdb..14bd9d225 100644 --- a/regress/forcecommand.sh +++ b/regress/forcecommand.sh @@ -28,7 +28,12 @@ ${SSH} -F $OBJ/ssh_proxy somehost false || fail "forced command in key" cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy echo "ForceCommand false" >> $OBJ/sshd_proxy -echo "Match User $USER" >> $OBJ/sshd_proxy +if [ "$os" == "windows" ]; then + # If User is domainuser then it will be in "domain/user" so convert it to "domain\user" + echo "Match user ${USER//\//\\}" >>$OBJ/sshd_proxy +else + echo "Match User $USER" >>$OBJ/sshd_proxy +fi echo " ForceCommand true" >> $OBJ/sshd_proxy trace "forced command with match" diff --git a/regress/forwarding.sh b/regress/forwarding.sh index 39fccba73..9b2f6dbf1 100644 --- a/regress/forwarding.sh +++ b/regress/forwarding.sh @@ -3,6 +3,12 @@ tid="local and remote forwarding" +if [ "$os" == "windows" ]; then + # Windows, ssh.exe -S option is not supported on windows + echo "skipped, not applicable on windows OS" + exit 0 +fi + DATA=/bin/ls${EXEEXT} start_sshd diff --git a/regress/host-expand.sh b/regress/host-expand.sh index 9444f7fb6..8d7276cca 100644 --- a/regress/host-expand.sh +++ b/regress/host-expand.sh @@ -4,7 +4,14 @@ tid="expand %h and %n" echo 'PermitLocalCommand yes' >> $OBJ/ssh_proxy -printf 'LocalCommand printf "%%%%s\\n" "%%n" "%%h"\n' >> $OBJ/ssh_proxy +if [ "$os" == "windows" ]; then + # Use bash shell for local command execution as the default shell in windows is cmd.exe + printf 'LocalCommand ' >> $OBJ/ssh_proxy + printf $TEST_SHELL_PATH >> $OBJ/ssh_proxy + printf ' -c "printf \\"%%%%s\\n\\" \\"%%n\\" \\"%%h\\""\n' >> $OBJ/ssh_proxy +else + printf 'LocalCommand printf "%%%%s\\n" "%%n" "%%h"\n' >> $OBJ/ssh_proxy +fi cat >$OBJ/expect < $OBJ/sshd_proxy.orig +if [ "$os" == "windows" ]; then + # Windows ssh-agent doesn't support "-s" option so we need to set SSH_AUTH_SOCK env here. + SSH_AUTH_SOCK="\\\\.\\pipe\\openssh-ssh-agent" + ${SSHADD} -D +fi echo "HostKeyAgent $SSH_AUTH_SOCK" >> $OBJ/sshd_proxy.orig trace "load hostkeys" for k in `${SSH} -Q key-plain` ; do + if [ "$os" == "windows" ]; then + k=${k/$'\r'/} # remove CR (carriage return) + fi + ${SSHKEYGEN} -qt $k -f $OBJ/agent-key.$k -N '' || fatal "ssh-keygen $k" ( printf 'localhost-with-alias,127.0.0.1,::1 ' @@ -32,6 +41,9 @@ unset SSH_AUTH_SOCK for ps in no yes; do for k in `${SSH} -Q key-plain` ; do + if [ "$os" == "windows" ]; then + k=${k/$'\r'/} # remove CR (carriage return) + fi verbose "key type $k privsep=$ps" cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy echo "UsePrivilegeSeparation $ps" >> $OBJ/sshd_proxy @@ -48,6 +60,10 @@ for ps in no yes; do done done +if [ "$os" == "windows" ]; then + #keys added through ssh-add are stored in windows registry so delete them. + ${SSHADD} -D +fi trace "kill agent" ${SSHAGENT} -k > /dev/null diff --git a/regress/hostkey-rotate.sh b/regress/hostkey-rotate.sh index d69de3255..8f23303de 100644 --- a/regress/hostkey-rotate.sh +++ b/regress/hostkey-rotate.sh @@ -16,6 +16,9 @@ trace "prepare hostkeys" nkeys=0 all_algs="" for k in `${SSH} -Q key-plain` ; do + if [ "$os" == "windows" ]; then + k=`echo $k | sed 's/\r$//'` # remove CR (carriage return) + fi ${SSHKEYGEN} -qt $k -f $OBJ/hkr.$k -N '' || fatal "ssh-keygen $k" echo "Hostkey $OBJ/hkr.${k}" >> $OBJ/sshd_proxy.orig nkeys=`expr $nkeys + 1` @@ -63,6 +66,9 @@ check_key_present ssh-rsa || fail "didn't learn keys" # Check each key type for k in `${SSH} -Q key-plain` ; do + if [ "$os" == "windows" ]; then + k=`echo $k | sed 's/\r$//'` # remove CR (carriage return) + fi verbose "learn additional hostkeys, type=$k" dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=$k,$all_algs expect_nkeys $nkeys "learn hostkeys $k" diff --git a/regress/integrity.sh b/regress/integrity.sh index 3eda40f0a..3fe83b1f5 100644 --- a/regress/integrity.sh +++ b/regress/integrity.sh @@ -7,10 +7,17 @@ cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak # start at byte 2900 (i.e. after kex) and corrupt at different offsets tries=10 startoffset=2900 -macs=`${SSH} -Q mac` + # The following are not MACs, but ciphers with integrated integrity. They are # handled specially below. -macs="$macs `${SSH} -Q cipher-auth`" +if [ "$os" == "windows" ]; then + # remove CR (Carriage return) + macs=`${SSH} -Q mac | sed 's/\r$//'` + macs="$macs `${SSH} -Q cipher-auth | sed 's/\r$//'`" +else + macs=`${SSH} -Q mac` + macs="$macs `${SSH} -Q cipher-auth`" +fi # avoid DH group exchange as the extra traffic makes it harder to get the # offset into the stream right. @@ -28,7 +35,12 @@ for m in $macs; do etmo=0 ecnt=0 skip=0 - for off in `jot $tries $startoffset`; do + if [ "$os" == "windows" ]; then + offsets=$(seq $startoffset 1 $((startoffset+tries))) # use seq instead of jot + else + offsets=`jot $tries $startoffset` + fi + for off in $offsets; do skip=`expr $skip - 1` if [ $skip -gt 0 ]; then # avoid modifying the high bytes of the length diff --git a/regress/kextype.sh b/regress/kextype.sh index e27189904..1949eb5ed 100644 --- a/regress/kextype.sh +++ b/regress/kextype.sh @@ -8,12 +8,21 @@ cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak # Make server accept all key exchanges. -ALLKEX=`${SSH} -Q kex` + +if [ "$os" == "windows" ]; then + # Remove CR (carriage return) + ALLKEX=`${SSH} -Q kex | sed 's/\r$//'` +else + ALLKEX=`${SSH} -Q kex` +fi KEXOPT=`echo $ALLKEX | tr ' ' ,` echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy tries="1 2 3 4" for k in `${SSH} -Q kex`; do + if [ "$os" == "windows" ]; then + k=${k/$'\r'/} # Remove CR (carriage return) + fi verbose "kex $k" for i in $tries; do ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true diff --git a/regress/keygen-change.sh b/regress/keygen-change.sh index 8b8acd52f..2204b8bb7 100644 --- a/regress/keygen-change.sh +++ b/regress/keygen-change.sh @@ -6,7 +6,11 @@ tid="change passphrase for key" S1="secret1" S2="2secret" -KEYTYPES=`${SSH} -Q key-plain` +if [ "$os" == "windows" ]; then + KEYTYPES=`${SSH} -Q key-plain | sed 's/\r$//'` # remove CR (carriage return) +else + KEYTYPES=`${SSH} -Q key-plain` +fi for t in $KEYTYPES; do # generate user key for agent diff --git a/regress/keygen-convert.sh b/regress/keygen-convert.sh index ad0e9c637..3d0b9ce4b 100644 --- a/regress/keygen-convert.sh +++ b/regress/keygen-convert.sh @@ -25,9 +25,14 @@ for t in rsa dsa; do fail "$t import rfc4716 public" cut -f1,2 -d " " $OBJ/$t-key.pub >$OBJ/$t-key-nocomment.pub - cmp $OBJ/$t-key-nocomment.pub $OBJ/$t-rfc-imported || \ - fail "$t imported differs from original" - + if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/$t-key-nocomment.pub $OBJ/$t-rfc-imported || \ + fail "$t imported differs from original" + else + cmp $OBJ/$t-key-nocomment.pub $OBJ/$t-rfc-imported || \ + fail "$t imported differs from original" + fi rm -f $OBJ/$t-key $OBJ/$t-key.pub $OBJ/$t-key-rfc $OBJ/$t-key-rfc.pub \ $OBJ/$t-rfc-imported $OBJ/$t-key-nocomment.pub done diff --git a/regress/keygen-knownhosts.sh b/regress/keygen-knownhosts.sh index 693cd0e75..808d4408a 100644 --- a/regress/keygen-knownhosts.sh +++ b/regress/keygen-knownhosts.sh @@ -135,47 +135,87 @@ check_hashed_find host-h "find multiple hosts" # Attempt remove key on invalid file. cp $OBJ/kh.invalid.orig $OBJ/kh.invalid ${SSHKEYGEN} -qf $OBJ/kh.invalid -R host-a 2>/dev/null -diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "remove on invalid succeeded" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "remove on invalid succeeded" +else + diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "remove on invalid succeeded" +fi # Remove key cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-a 2>/dev/null grep -v "^host-a " $OBJ/kh.hosts.orig > $OBJ/kh.expect -diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove simple" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts $OBJ/kh.expect || fail "remove simple" +else + diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove simple" +fi # Remove CA key cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-c 2>/dev/null # CA key should not be removed. -diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove CA" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove CA" +else + diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove CA" +fi # Remove revoked key cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-d 2>/dev/null # revoked key should not be removed. -diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove revoked" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove revoked" +else + diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove revoked" +fi # Remove wildcard cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-e.blahblah 2>/dev/null grep -v "^host-e[*] " $OBJ/kh.hosts.orig > $OBJ/kh.expect -diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +else + diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +fi # Remove multiple cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-h 2>/dev/null grep -v "^host-f," $OBJ/kh.hosts.orig > $OBJ/kh.expect -diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +else + diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" +fi # Attempt hash on invalid file cp $OBJ/kh.invalid.orig $OBJ/kh.invalid ${SSHKEYGEN} -qf $OBJ/kh.invalid -H 2>/dev/null && fail "hash invalid succeeded" -diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "invalid file modified" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "invalid file modified" +else + diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "invalid file modified" +fi # Hash valid file cp $OBJ/kh.hosts.orig $OBJ/kh.hosts ${SSHKEYGEN} -qf $OBJ/kh.hosts -H 2>/dev/null || fail "hash failed" -diff $OBJ/kh.hosts.old $OBJ/kh.hosts.orig || fail "backup differs" +if [ "$os" == "windows" ]; then + # Ignore CR (carriage return) while comparing files + diff --strip-trailing-cr $OBJ/kh.hosts.old $OBJ/kh.hosts.orig || fail "backup differs" +else + diff $OBJ/kh.hosts.old $OBJ/kh.hosts.orig || fail "backup differs" +fi grep "^host-[abfgh]" $OBJ/kh.hosts && fail "original hostnames persist" cp $OBJ/kh.hosts $OBJ/kh.hashed.orig diff --git a/regress/keyscan.sh b/regress/keyscan.sh index 3bde1219a..3eb05e75c 100644 --- a/regress/keyscan.sh +++ b/regress/keyscan.sh @@ -8,7 +8,12 @@ rm -f ${OBJ}/host.dsa start_sshd -KEYTYPES=`${SSH} -Q key-plain` +if [ "$os" == "windows" ]; then + # Remove CR (carriage return) + KEYTYPES=`${SSH} -Q key-plain | sed 's/\r$//'` +else + KEYTYPES=`${SSH} -Q key-plain` +fi for t in $KEYTYPES; do trace "keyscan type $t" ${SSHKEYSCAN} -t $t -p $PORT 127.0.0.1 127.0.0.1 127.0.0.1 \ diff --git a/regress/krl.sh b/regress/krl.sh index 1077358ff..9b1719eb0 100644 --- a/regress/krl.sh +++ b/regress/krl.sh @@ -46,7 +46,12 @@ EOF # A specification that revokes some certificated by key ID. touch $OBJ/revoked-keyid -for n in 1 2 3 4 10 15 30 50 `jot 500 300` 999 1000 1001 1002; do +if [ "$os" == "windows" ]; then + revokedids=$(seq 300 1 $((500+300))) # use seq instead of jot +else + revokedids=`jot 500 300` +fi +for n in 1 2 3 4 10 15 30 50 $revokedids 999 1000 1001 1002; do test "x$n" = "x499" && continue # Fill in by-ID revocation spec. echo "id: revoked $n" >> $OBJ/revoked-keyid diff --git a/regress/limit-keytype.sh b/regress/limit-keytype.sh index 04f11977e..f0579b00c 100644 --- a/regress/limit-keytype.sh +++ b/regress/limit-keytype.sh @@ -90,8 +90,15 @@ ${SSH} $opts -i $OBJ/user_key2 proxy true || fatal "key2 failed" # Allow only DSA in main config, Ed25519 for user. verbose "match w/ matching" -prepare_config "PubkeyAcceptedKeyTypes ssh-dss" \ - "Match user $USER" "PubkeyAcceptedKeyTypes +ssh-ed25519" +if [ "$os" == "windows" ]; then + # If User is domainuser then it will be in "domain/user" so convert it to "domain\user" + prepare_config "PubkeyAcceptedKeyTypes ssh-dss" \ + "Match user ${USER//\//\\}" "PubkeyAcceptedKeyTypes +ssh-ed25519" +else + prepare_config "PubkeyAcceptedKeyTypes ssh-dss" \ + "Match user $USER" "PubkeyAcceptedKeyTypes +ssh-ed25519" +fi + ${SSH} $certopts proxy true || fatal "cert failed" ${SSH} $opts -i $OBJ/user_key1 proxy true || fatal "key1 failed" ${SSH} $opts -i $OBJ/user_key4 proxy true && fatal "key4 succeeded" diff --git a/regress/localcommand.sh b/regress/localcommand.sh index 5224a16b2..c40681f56 100644 --- a/regress/localcommand.sh +++ b/regress/localcommand.sh @@ -8,6 +8,11 @@ echo 'LocalCommand echo foo' >> $OBJ/ssh_proxy verbose "test $tid: proto $p localcommand" a=`${SSH} -F $OBJ/ssh_proxy somehost true` + +if [ "$os" == "windows" ]; then + a=`echo $a | tr -d '\r\n'` # Remove CR (carriage return) +fi + if [ "$a" != "foo" ] ; then fail "$tid proto $p" fi diff --git a/regress/multiplex.sh b/regress/multiplex.sh index 078a53a88..09398e983 100644 --- a/regress/multiplex.sh +++ b/regress/multiplex.sh @@ -5,6 +5,13 @@ CTL=/tmp/openssh.regress.ctl-sock.$$ tid="connection multiplexing" +if [ "$os" == "windows" ]; then + # Windows, ssh.exe -S option is not supported on windows + echo "skipped, not applicable on windows OS" + exit 0 +fi + + NC=$OBJ/netcat trace "will use ProxyCommand $proxycmd" diff --git a/regress/reconfigure.sh b/regress/reconfigure.sh index dd15eddb2..0cfcd96f7 100644 --- a/regress/reconfigure.sh +++ b/regress/reconfigure.sh @@ -2,7 +2,12 @@ # Placed in the Public Domain. tid="simple connect after reconfigure" - +if [ "$os" == "windows" ]; then + # In windows, sshd service process will not restart if we kill it. + # This test case is not applicable to windows OS. + echo "skipped, not applicable on windows OS" + exit 0 +fi # we need the full path to sshd for -HUP if test "x$USE_VALGRIND" = "x" ; then case $SSHD in diff --git a/regress/reexec.sh b/regress/reexec.sh index 2192456cd..83fcda220 100644 --- a/regress/reexec.sh +++ b/regress/reexec.sh @@ -40,8 +40,8 @@ stop_sshd cp $OBJ/sshd_config.orig $OBJ/sshd_config -# cygwin can't fork a deleted binary -if [ "$os" != "cygwin" ]; then +# cygwin, windows can't fork a deleted binary +if [ "$os" != "cygwin" ] && [ "$os" != "windows" ]; then verbose "test reexec fallback" diff --git a/regress/rekey.sh b/regress/rekey.sh index ae145bc8b..fcb7efbae 100644 --- a/regress/rekey.sh +++ b/regress/rekey.sh @@ -21,7 +21,11 @@ ssh_data_rekeying() fi rm -f ${COPY} ${LOG} _opts="$_opts -oCompression=no" - ${SSH} <${DATA} $_opts -v -F $OBJ/ssh_proxy somehost "cat > ${COPY}" + if [ "$os" == "windows" ]; then + cat ${DATA} | ${SSH} $_opts -v -F $OBJ/ssh_proxy somehost "cat > ${COPY}" + else + ${SSH} <${DATA} $_opts -v -F $OBJ/ssh_proxy somehost "cat > ${COPY}" + fi if [ $? -ne 0 ]; then fail "ssh failed ($@)" fi @@ -70,8 +74,13 @@ done for s in 5 10; do verbose "client rekeylimit default ${s}" rm -f ${COPY} ${LOG} - ${SSH} < ${DATA} -oCompression=no -oRekeyLimit="default $s" -F \ - $OBJ/ssh_proxy somehost "cat >${COPY};sleep $s;sleep 3" + if [ "$os" == "windows" ]; then + cat ${DATA} | ${SSH} -oCompression=no -oRekeyLimit="default $s" -F \ + $OBJ/ssh_proxy somehost "cat >${COPY};sleep $s;sleep 3" + else + ${SSH} < ${DATA} -oCompression=no -oRekeyLimit="default $s" -F \ + $OBJ/ssh_proxy somehost "cat >${COPY};sleep $s;sleep 3" + fi if [ $? -ne 0 ]; then fail "ssh failed" fi @@ -159,7 +168,9 @@ for size in 16 1k 1K 1m 1M 1g 1G 4G 8G; do awk '/rekeylimit/{print $2}'` s=`$SUDO ${SSHD} -T -o "rekeylimit $size $time" -f $OBJ/sshd_proxy | \ awk '/rekeylimit/{print $3}'` - + if [ "$os" == "windows" ]; then + s=${s/$'\r'/} # Remove CR (carriage return) + fi if [ "$bytes" != "$b" ]; then fatal "rekeylimit size: expected $bytes bytes got $b" fi diff --git a/regress/scp-uri.sh b/regress/scp-uri.sh index c03d8bbe0..86d12dcf3 100644 --- a/regress/scp-uri.sh +++ b/regress/scp-uri.sh @@ -11,8 +11,13 @@ DIR2=${COPY}.dd2 SRC=`dirname ${SCRIPT}` cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp -chmod 755 ${OBJ}/scp-ssh-wrapper.scp -scpopts="-q -S ${OBJ}/scp-ssh-wrapper.scp" +# scpopts should be an array to preverse the double quotes +if [ "$os" == "windows" ]; then + scpopts=(-q -S "$TEST_SHELL_PATH ${OBJ}/scp-ssh-wrapper.scp") +else + chmod 755 ${OBJ}/scp-ssh-wrapper.scp + scpopts=(-q -S ${OBJ}/scp-ssh-wrapper.scp) +fi export SCP # used in scp-ssh-wrapper.scp scpclean() { @@ -26,31 +31,31 @@ egrep -v '^ +(Port|User) +.*$' $OBJ/ssh_config.orig > $OBJ/ssh_config verbose "$tid: simple copy local file to remote file" scpclean -$SCP $scpopts ${DATA} "scp://${USER}@somehost:${PORT}/${COPY}" || fail "copy failed" +$SCP "${scpopts[@]}" ${DATA} "scp://${USER}@somehost:${PORT}/${COPY}" || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: simple copy remote file to local file" scpclean -$SCP $scpopts "scp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" +$SCP "${scpopts[@]}" "scp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: simple copy local file to remote dir" scpclean cp ${DATA} ${COPY} -$SCP $scpopts ${COPY} "scp://${USER}@somehost:${PORT}/${DIR}" || fail "copy failed" +$SCP "${scpopts[@]}" ${COPY} "scp://${USER}@somehost:${PORT}/${DIR}" || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: simple copy remote file to local dir" scpclean cp ${DATA} ${COPY} -$SCP $scpopts "scp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" +$SCP "${scpopts[@]}" "scp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: recursive local dir to remote dir" scpclean rm -rf ${DIR2} cp ${DATA} ${DIR}/copy -$SCP $scpopts -r ${DIR} "scp://${USER}@somehost:${PORT}/${DIR2}" || fail "copy failed" +$SCP "${scpopts[@]}" -r ${DIR} "scp://${USER}@somehost:${PORT}/${DIR2}" || fail "copy failed" for i in $(cd ${DIR} && echo *); do cmp ${DIR}/$i ${DIR2}/$i || fail "corrupted copy" done @@ -59,7 +64,7 @@ verbose "$tid: recursive remote dir to local dir" scpclean rm -rf ${DIR2} cp ${DATA} ${DIR}/copy -$SCP $scpopts -r "scp://${USER}@somehost:${PORT}/${DIR}" ${DIR2} || fail "copy failed" +$SCP "${scpopts[@]}" -r "scp://${USER}@somehost:${PORT}/${DIR}" ${DIR2} || fail "copy failed" for i in $(cd ${DIR} && echo *); do cmp ${DIR}/$i ${DIR2}/$i || fail "corrupted copy" done diff --git a/regress/scp.sh b/regress/scp.sh index 57cc77066..fbeadcb81 100644 --- a/regress/scp.sh +++ b/regress/scp.sh @@ -19,7 +19,12 @@ DIR2=${COPY}.dd2 SRC=`dirname ${SCRIPT}` cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp chmod 755 ${OBJ}/scp-ssh-wrapper.scp -scpopts="-q -S ${OBJ}/scp-ssh-wrapper.scp" +# scpopts should be an array to preverse the double quotes +if [ "$os" == "windows" ]; then + scpopts=(-q -S "$TEST_SHELL_PATH ${OBJ}/scp-ssh-wrapper.scp") +else + scpopts=(-q -S ${OBJ}/scp-ssh-wrapper.scp) +fi export SCP # used in scp-ssh-wrapper.scp scpclean() { @@ -29,63 +34,63 @@ scpclean() { verbose "$tid: simple copy local file to local file" scpclean -$SCP $scpopts ${DATA} ${COPY} || fail "copy failed" +$SCP "${scpopts[@]}" ${DATA} ${COPY} 2>&1 1>/dev/null || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: simple copy local file to remote file" scpclean -$SCP $scpopts ${DATA} somehost:${COPY} || fail "copy failed" +$SCP "${scpopts[@]}" ${DATA} somehost:${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: simple copy remote file to local file" scpclean -$SCP $scpopts somehost:${DATA} ${COPY} || fail "copy failed" +$SCP "${scpopts[@]}" somehost:${DATA} ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: simple copy local file to remote dir" scpclean cp ${DATA} ${COPY} -$SCP $scpopts ${COPY} somehost:${DIR} || fail "copy failed" +$SCP "${scpopts[@]}" ${COPY} somehost:${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: simple copy local file to local dir" scpclean cp ${DATA} ${COPY} -$SCP $scpopts ${COPY} ${DIR} || fail "copy failed" +$SCP "${scpopts[@]}" ${COPY} ${DIR} 2>&1 1>/dev/null || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: simple copy remote file to local dir" scpclean cp ${DATA} ${COPY} -$SCP $scpopts somehost:${COPY} ${DIR} || fail "copy failed" +$SCP "${scpopts[@]}" somehost:${COPY} ${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: recursive local dir to remote dir" scpclean rm -rf ${DIR2} cp ${DATA} ${DIR}/copy -$SCP $scpopts -r ${DIR} somehost:${DIR2} || fail "copy failed" +$SCP "${scpopts[@]}" -r ${DIR} somehost:${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tid: recursive local dir to local dir" scpclean rm -rf ${DIR2} cp ${DATA} ${DIR}/copy -$SCP $scpopts -r ${DIR} ${DIR2} || fail "copy failed" +$SCP "${scpopts[@]}" -r ${DIR} ${DIR2} 2>&1 1>/dev/null || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tid: recursive remote dir to local dir" scpclean rm -rf ${DIR2} cp ${DATA} ${DIR}/copy -$SCP $scpopts -r somehost:${DIR} ${DIR2} || fail "copy failed" +$SCP "${scpopts[@]}" -r somehost:${DIR} ${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tid: shell metacharacters" scpclean (cd ${DIR} && \ touch '`touch metachartest`' && \ -$SCP $scpopts *metachar* ${DIR2} 2>/dev/null; \ +$SCP "${scpopts[@]}" *metachar* ${DIR2} 2>&1 2>/dev/null; \ [ ! -f metachartest ] ) || fail "shell metacharacters" if [ ! -z "$SUDO" ]; then @@ -96,7 +101,7 @@ if [ ! -z "$SUDO" ]; then cp ${DATA} ${DIR2}/copy chmod 660 ${DIR2}/copy $SUDO chown root ${DIR2}/copy - $SCP -p $scpopts somehost:${DIR}/\* ${DIR2} >/dev/null 2>&1 + $SCP -p "${scpopts[@]}" somehost:${DIR}/\* ${DIR2} >/dev/null 2>&1 $SUDO diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" $SUDO rm ${DIR2}/copy fi @@ -106,12 +111,12 @@ for i in 0 1 2 3 4; do SCPTESTMODE=badserver_$i export DIR SCPTESTMODE scpclean - $SCP $scpopts somehost:${DATA} ${DIR} >/dev/null 2>/dev/null + $SCP "${scpopts[@]}" somehost:${DATA} ${DIR} >/dev/null 2>/dev/null [ -d {$DIR}/rootpathdir ] && fail "allows dir relative to root dir" [ -d ${DIR}/dotpathdir ] && fail "allows dir creation in non-recursive mode" scpclean - $SCP -r $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null + $SCP -r "${scpopts[@]}" somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null [ -d ${DIR}/dotpathdir ] && fail "allows dir creation outside of subdir" done @@ -119,7 +124,7 @@ verbose "$tid: detect non-directory target" scpclean echo a > ${COPY} echo b > ${COPY2} -$SCP $scpopts ${DATA} ${COPY} ${COPY2} +$SCP "${scpopts[@]}" ${DATA} ${COPY} ${COPY2} cmp ${COPY} ${COPY2} >/dev/null && fail "corrupt target" scpclean diff --git a/regress/sftp-cmds.sh b/regress/sftp-cmds.sh index aad7fcac2..840f34776 100644 --- a/regress/sftp-cmds.sh +++ b/regress/sftp-cmds.sh @@ -20,7 +20,11 @@ QUOTECOPY=${COPY}".\"blah\"" QUOTECOPY_ARG=${COPY}'.\"blah\"' # File with spaces SPACECOPY="${COPY} this has spaces.txt" -SPACECOPY_ARG="${COPY}\ this\ has\ spaces.txt" +if [ "$os" == "windows" ]; then + SPACECOPY_ARG="\"${COPY} this has spaces.txt\"" +else + SPACECOPY_ARG="${COPY}\ this\ has\ spaces.txt" +fi # File with glob metacharacters GLOBMETACOPY="${COPY} [metachar].txt" @@ -77,7 +81,7 @@ echo "get \"$DATA\" $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ || fail "get failed" cmp $DATA ${COPY} || fail "corrupted copy after get" -if [ "$os" != "cygwin" ]; then +if [ "$os" != "cygwin" ] && [ "$os" != "windows" ]; then rm -f ${QUOTECOPY} cp $DATA ${QUOTECOPY} verbose "$tid: get filename with quotes" @@ -136,7 +140,7 @@ echo "put $DATA $COPY" | \ ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "put failed" cmp $DATA ${COPY} || fail "corrupted copy after put" -if [ "$os" != "cygwin" ]; then +if [ "$os" != "cygwin" ] && [ "$os" != "windows" ]; then rm -f ${QUOTECOPY} verbose "$tid: put filename with quotes" echo "put $DATA \"$QUOTECOPY_ARG\"" | \ diff --git a/regress/sftp-glob.sh b/regress/sftp-glob.sh index 8d4df2c98..827c4fd14 100644 --- a/regress/sftp-glob.sh +++ b/regress/sftp-glob.sh @@ -57,12 +57,15 @@ test "x$nobs" = "x" && touch "${QSLASH}" "${ESLASH}" "${SLASH}" # target message expected unexpected sftp_ls "${DIR}/fil*" "file glob" "${DATA}" "" sftp_ls "${BASE}/d*" "dir glob" "`basename ${DATA}`" "" -sftp_ls "${DIR}/g-wild\"*\"" "quoted glob" "g-wild*" "g-wildx" -sftp_ls "${DIR}/g-wild\*" "escaped glob" "g-wild*" "g-wildx" -sftp_ls "${DIR}/g-quote\\\"" "escaped quote" "g-quote\"" "" -sftp_ls "\"${DIR}/g-quote\\\"\"" "quoted quote" "g-quote\"" "" -sftp_ls "'${DIR}/g-quote\"'" "single-quoted quote" "g-quote\"" "" -sftp_ls "${DIR}/g-q\\ space" "escaped space" "g-q space" "" +if [ "$os" != "windows" ]; then + sftp_ls "${DIR}/g-wild\"*\"" "quoted glob" "g-wild*" "g-wildx" + sftp_ls "${DIR}/g-wild\*" "escaped glob" "g-wild*" "g-wildx" + sftp_ls "${DIR}/g-quote\\\"" "escaped quote" "g-quote\"" "" + sftp_ls "\"${DIR}/g-quote\\\"\"" "quoted quote" "g-quote\"" "" + sftp_ls "'${DIR}/g-quote\"'" "single-quoted quote" "g-quote\"" "" + sftp_ls "${DIR}/g-q\\ space" "escaped space" "g-q space" "" +fi + sftp_ls "'${DIR}/g-q space'" "quoted space" "g-q space" "" sftp_ls "${DIR}/g-sl\\\\ash" "escaped slash" "g-sl\\ash" "" "$nobs" sftp_ls "'${DIR}/g-sl\\\\ash'" "quoted slash" "g-sl\\ash" "" "$nobs" diff --git a/regress/sftp-perm.sh b/regress/sftp-perm.sh index 304ca0ac5..34d68fc5c 100644 --- a/regress/sftp-perm.sh +++ b/regress/sftp-perm.sh @@ -14,7 +14,11 @@ prepare_server() { } run_client() { - echo "$@" | ${SFTP} -D ${TEST_SFTP_SERVER} -vvvb - >$CLIENT_LOG 2>&1 + if [ "$os" == "windows" ]; then + echo "$@" | ${SFTP} -D "$TEST_SHELL_PATH ${TEST_SFTP_SERVER}" -vvvb - >$CLIENT_LOG 2>&1 + else + echo "$@" | ${SFTP} -D ${TEST_SFTP_SERVER} -vvvb - >$CLIENT_LOG 2>&1 + fi } prepare_files() { @@ -22,6 +26,10 @@ prepare_files() { rm -f ${COPY} ${COPY}.1 test -d ${COPY}.dd && { rmdir ${COPY}.dd || fatal "rmdir ${COPY}.dd"; } test -z "$_prep" && return + if [ "$os" == "windows" ]; then + # Remove starting, ending double-quotes + _prep=`echo $_prep | sed -e 's/^"//' -e 's/"$//'` + fi sh -c "$_prep" || fail "preparation failed: \"$_prep\"" } @@ -88,12 +96,21 @@ ro_test \ "cmp $DATA $COPY" \ "test ! -f $COPY" -ro_test \ +if [ "$os" == "windows" ]; then + ro_test \ + "setstat" \ + "chmod 0700 $COPY" \ + "\"powershell.exe /c new-item `windows_path $OBJ`/copy 1>/dev/null; powershell.exe /c set-itemproperty `windows_path $OBJ`/copy -Name IsReadOnly -Value 1\"" \ + "powershell.exe /c \"(Get-ChildItem `windows_path $OBJ`/copy).IsReadOnly\" 1>/dev/null" \ + "powershell.exe /c \"!(Get-ChildItem $`windows_path $OBJ`/copy).IsReadOnly\" 1>/dev/null" +else + ro_test \ "setstat" \ "chmod 0700 $COPY" \ "touch $COPY; chmod 0400 $COPY" \ "test -x $COPY" \ "test ! -x $COPY" +fi ro_test \ "rm" \ @@ -188,13 +205,23 @@ perm_test \ "realpath,opendir,stat,lstat" \ "ls -ln $OBJ" -perm_test \ - "setstat" \ - "realpath,stat,lstat" \ - "chmod 0700 $COPY" \ - "touch $COPY; chmod 0400 $COPY" \ - "test -x $COPY" \ - "test ! -x $COPY" +if [ "$os" == "windows" ]; then + perm_test \ + "setstat" \ + "realpath,stat,lstat" \ + "chmod 0700 $COPY" \ + "\"powershell.exe /c new-item `windows_path $OBJ`/copy 1>/dev/null; powershell.exe /c set-itemproperty `windows_path $OBJ`/copy -Name IsReadOnly -Value 1\"" \ + "powershell.exe /c \"(Get-ChildItem `windows_path $OBJ`/copy).IsReadOnly\" 1>/dev/null" \ + "powershell.exe /c \"!(Get-ChildItem `windows_path $OBJ`/copy).IsReadOnly\" 1>/dev/null" +else + perm_test \ + "setstat" \ + "realpath,stat,lstat" \ + "chmod 0700 $COPY" \ + "touch $COPY; chmod 0400 $COPY" \ + "test -x $COPY" \ + "test ! -x $COPY" +fi perm_test \ "remove" \ diff --git a/regress/sftp-uri.sh b/regress/sftp-uri.sh index 7be104dfb..1cf3be103 100644 --- a/regress/sftp-uri.sh +++ b/regress/sftp-uri.sh @@ -23,18 +23,18 @@ egrep -v '^ +(Port|User) +.*$' $OBJ/ssh_config.orig > $OBJ/ssh_config verbose "$tid: non-interactive fetch to local file" sftpclean -${SFTP} -q -S "$SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" +${SFTP} -q -S "$TEST_SHELL_PATH $SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tid: non-interactive fetch to local dir" sftpclean cp ${DATA} ${COPY} -${SFTP} -q -S "$SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" +${SFTP} -q -S "$TEST_SHELL_PATH $SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tid: put to remote directory (trailing slash)" sftpclean -${SFTP} -q -S "$SSH" -F $OBJ/ssh_config -b - \ +${SFTP} -q -S "$TEST_SHELL_PATH $SSH" -F $OBJ/ssh_config -b - \ "sftp://${USER}@somehost:${PORT}/${DIR}/" > /dev/null 2>&1 << EOF version put ${DATA} copy @@ -48,7 +48,7 @@ fi verbose "$tid: put to remote directory (no slash)" sftpclean -${SFTP} -q -S "$SSH" -F $OBJ/ssh_config -b - \ +${SFTP} -q -S "$TEST_SHELL_PATH $SSH" -F $OBJ/ssh_config -b - \ "sftp://${USER}@somehost:${PORT}/${DIR}" > /dev/null 2>&1 << EOF version put ${DATA} copy diff --git a/regress/sshcfgparse.sh b/regress/sshcfgparse.sh index 010e02865..d00b6f341 100644 --- a/regress/sshcfgparse.sh +++ b/regress/sshcfgparse.sh @@ -10,19 +10,37 @@ verbose "reparse minimal config" verbose "ssh -W opts" f=`${SSH} -GF $OBJ/ssh_config host | awk '/exitonforwardfailure/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "no" || fail "exitonforwardfailure default" f=`${SSH} -GF $OBJ/ssh_config -W a:1 h | awk '/exitonforwardfailure/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "yes" || fail "exitonforwardfailure enable" f=`${SSH} -GF $OBJ/ssh_config -W a:1 -o exitonforwardfailure=no h | \ awk '/exitonforwardfailure/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "no" || fail "exitonforwardfailure override" f=`${SSH} -GF $OBJ/ssh_config host | awk '/clearallforwardings/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "no" || fail "clearallforwardings default" f=`${SSH} -GF $OBJ/ssh_config -W a:1 h | awk '/clearallforwardings/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "yes" || fail "clearallforwardings enable" f=`${SSH} -GF $OBJ/ssh_config -W a:1 -o clearallforwardings=no h | \ awk '/clearallforwardings/{print $2}'` +if [ "$os" == "windows" ]; then + f=${f/$'\r'/} # remove CR (carriage return) +fi test "$f" = "no" || fail "clearallforwardings override" # cleanup diff --git a/regress/test-exec.sh b/regress/test-exec.sh index b6169f157..cdbe12cc4 100644 --- a/regress/test-exec.sh +++ b/regress/test-exec.sh @@ -7,19 +7,26 @@ _POSIX2_VERSION=199209 export _POSIX2_VERSION -case `uname -s 2>/dev/null` in -OSF1*) - BIN_SH=xpg4 - export BIN_SH - ;; -CYGWIN_NT-5.0) - os=cygwin - TEST_SSH_IPV6=no - ;; -CYGWIN*) - os=cygwin - ;; -esac +if [ "x$TEST_WINDOWS_SSH" != "x" ]; then + os="windows" +fi + +if [ "$os" != "windows" ]; then + case `uname -s 2>/dev/null` in + OSF1*) + BIN_SH=xpg4 + export BIN_SH + ;; + CYGWIN_NT-5.0) + os=cygwin + TEST_SSH_IPV6=no + ;; + CYGWIN*) + os=cygwin + ;; + esac +fi + if [ ! -z "$TEST_SSH_PORT" ]; then PORT="$TEST_SSH_PORT" @@ -27,14 +34,20 @@ else PORT=4242 fi -if [ -x /usr/ucb/whoami ]; then - USER=`/usr/ucb/whoami` -elif whoami >/dev/null 2>&1; then - USER=`whoami` -elif logname >/dev/null 2>&1; then - USER=`logname` +if [ "$os" == "windows" ]; then + USER=$TEST_SSH_USER + USER_DOMAIN=$TEST_SSH_USER_DOMAIN + LOGNAME=$USER else - USER=`id -un` + if [ -x /usr/ucb/whoami ]; then + USER=`/usr/ucb/whoami` + elif whoami >/dev/null 2>&1; then + USER=`whoami` + elif logname >/dev/null 2>&1; then + USER=`logname` + else + USER=`id -un` + fi fi OBJ=$1 @@ -213,7 +226,11 @@ fi # because sftp and scp don't handle spaces in arguments. SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh echo "#!/bin/sh" > $SSHLOGWRAP -echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP +if [ "$os" == "windows" ]; then + echo "exec ${SSH} -T -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP +else + echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP +fi chmod a+rx $OBJ/ssh-log-wrapper.sh REAL_SSH="$SSH" @@ -229,6 +246,9 @@ cat ${SSHAGENT_BIN} >${DATA} chmod u+w ${DATA} COPY=$OBJ/copy rm -f ${COPY} +if [ "$os" == "windows" ]; then + EXEEXT=".exe" +fi increase_datafile_size() { @@ -242,6 +262,11 @@ export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP #echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP # Portable specific functions +windows_path() +{ + cygpath -m $1 +} + have_prog() { saved_IFS="$IFS" @@ -289,29 +314,34 @@ md5 () { stop_sshd () { - if [ -f $PIDFILE ]; then - pid=`$SUDO cat $PIDFILE` - if [ "X$pid" = "X" ]; then - echo no sshd running - else - if [ $pid -lt 2 ]; then - echo bad pid for sshd: $pid + # windows process can't be stopped using kill command so use stop-process + if [ "$os" == "windows" ]; then + powershell.exe /c "stop-process -Name sshd -Force" >/dev/null 2>&1 + else + if [ -f $PIDFILE ]; then + pid=`$SUDO cat $PIDFILE` + if [ "X$pid" = "X" ]; then + echo no sshd running else - $SUDO kill $pid - trace "wait for sshd to exit" - i=0; - while [ -f $PIDFILE -a $i -lt 5 ]; do - i=`expr $i + 1` - sleep $i - done - if test -f $PIDFILE; then - if $SUDO kill -0 $pid; then - echo "sshd didn't exit " \ - "port $PORT pid $pid" - else - echo "sshd died without cleanup" + if [ $pid -lt 2 ]; then + echo bad pid for sshd: $pid + else + $SUDO kill $pid + trace "wait for sshd to exit" + i=0; + while [ -f $PIDFILE -a $i -lt 5 ]; do + i=`expr $i + 1` + sleep $i + done + if test -f $PIDFILE; then + if $SUDO kill -0 $pid; then + echo "sshd didn't exit " \ + "port $PORT pid $pid" + else + echo "sshd died without cleanup" + fi + exit 1 fi - exit 1 fi fi fi @@ -321,11 +351,19 @@ stop_sshd () # helper cleanup () { - if [ "x$SSH_PID" != "x" ]; then - if [ $SSH_PID -lt 2 ]; then - echo bad pid for ssh: $SSH_PID - else - kill $SSH_PID + # windows process can't be stopped using kill command so use stop-process + if [ "$os" == "windows" ]; then + powershell.exe /c "stop-process -Name ssh-agent -Force" >/dev/null 2>&1 + if [ "x$SSH_PID" != "x" ]; then + powershell.exe /c "stop-process -Id $SSH_PID -Force" >/dev/null 2>&1 + fi + else + if [ "x$SSH_PID" != "x" ]; then + if [ $SSH_PID -lt 2 ]; then + echo bad pid for ssh: $SSH_PID + else + kill $SSH_PID + fi fi fi stop_sshd @@ -451,6 +489,21 @@ fi rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER SSH_KEYTYPES="rsa ed25519" +if [ "$os" == "windows" ]; then + first_key_type=${SSH_KEYTYPES%% *} + if [ "x$USER_DOMAIN" != "x" ]; then + # For domain user, create folders + if [ ! -d $OBJ/authorized_keys_$USER_DOMAIN ]; then + mkdir $OBJ/authorized_keys_$USER_DOMAIN + fi + if [ ! -d $OBJ/authorized_principals_$USER_DOMAIN ]; then + mkdir $OBJ/authorized_principals_$USER_DOMAIN + fi + if [ ! -d /var/run/principals_command_$USER_DOMAIN ]; then + mkdir /var/run/principals_command_$USER_DOMAIN + fi + fi +fi trace "generate keys" for t in ${SSH_KEYTYPES}; do @@ -473,12 +526,21 @@ for t in ${SSH_KEYTYPES}; do # use key as host key, too $SUDO cp $OBJ/$t $OBJ/host.$t + if [ "$os" == "windows" ]; then + # set the file permissions (ACLs) properly + powershell.exe /c "get-acl `windows_path $OBJ`/$t | set-acl `windows_path $OBJ`/host.$t" + fi + echo HostKey $OBJ/host.$t >> $OBJ/sshd_config # don't use SUDO for proxy connect echo HostKey $OBJ/$t >> $OBJ/sshd_proxy done -chmod 644 $OBJ/authorized_keys_$USER + +if [ "$os" == "windows" ]; then + # set the file permissions (ACLs) properly + powershell.exe /c "get-acl `windows_path $OBJ`/$first_key_type | set-acl `windows_path $OBJ`/authorized_keys_$USER" +fi # Activate Twisted Conch tests if the binary is present REGRESS_INTEROP_CONCH=no @@ -536,7 +598,11 @@ fi # create a proxy version of the client config ( cat $OBJ/ssh_config - echo proxycommand ${SUDO} sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy + if [ "$os" == "windows" ]; then + echo proxycommand `windows_path ${SSHD}` -i -f `windows_path $OBJ`/sshd_proxy + else + echo proxycommand ${SUDO} sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy + fi ) > $OBJ/ssh_proxy # check proxy config @@ -546,7 +612,14 @@ start_sshd () { # start sshd $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken" - $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE + if [ "$os" == "windows" ]; then + # In windows, we need to explicitly remove the sshd pid file. + rm -rf $PIDFILE + #TODO (Code BUG) : -E is writing the data the cygwin terminal. + ${SSHD} -f $OBJ/sshd_config "$@" & + else + $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE + fi trace "wait for sshd" i=0; diff --git a/regress/try-ciphers.sh b/regress/try-ciphers.sh index e04268ba3..44da190fc 100644 --- a/regress/try-ciphers.sh +++ b/regress/try-ciphers.sh @@ -8,6 +8,10 @@ cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak for c in `${SSH} -Q cipher`; do n=0 for m in `${SSH} -Q mac`; do + if [ "$os" == "windows" ]; then + c=${c/$'\r'/} # remove CR (carriage return) + m=${m/$'\r'/} # remove CR (carriage return) + fi trace "cipher $c mac $m" verbose "test $tid: cipher $c mac $m" cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy diff --git a/regress/unittests/win32compat/file_tests.c b/regress/unittests/win32compat/file_tests.c index 9de284d8c..040237551 100644 --- a/regress/unittests/win32compat/file_tests.c +++ b/regress/unittests/win32compat/file_tests.c @@ -579,9 +579,12 @@ file_symlink_tests() /* verify readlink() output against symlink() input */ char readlink_buf[MAX_PATH] = ""; + /* readlink returns the absolute path */ int readlink_ret = readlink(lnk_utf8, readlink_buf, MAX_PATH); - ASSERT_INT_EQ(readlink_ret, strlen(tgt_name_utf8)); - ASSERT_INT_EQ(0, memcmp(readlink_buf, tgt_name_utf8, readlink_ret)); + char tgt_name_uft8_realpath[PATH_MAX] = { 0 }; + realpath(tgt_utf8, tgt_name_uft8_realpath); + ASSERT_INT_EQ(readlink_ret, strlen(tgt_name_uft8_realpath)); + ASSERT_INT_EQ(0, memcmp(readlink_buf, tgt_name_uft8_realpath, readlink_ret)); /* verify lstat() gets the reference to the link */ struct w32_stat statbuf; diff --git a/regress/unittests/win32compat/miscellaneous_tests.c b/regress/unittests/win32compat/miscellaneous_tests.c index 7dd438253..fbebca88d 100644 --- a/regress/unittests/win32compat/miscellaneous_tests.c +++ b/regress/unittests/win32compat/miscellaneous_tests.c @@ -72,6 +72,8 @@ test_sanitizedpath() wchar_t *win32prgdir = utf8_to_utf16(win32prgdir_utf8); wchar_t *ret = resolved_path_utf16(win32prgdir_utf8); + /* resolved path will return in unix file format ex - c:/test/1/ */ + convertToBackslashW(ret); retValue = wcscmp(win32prgdir, ret); ASSERT_INT_EQ(retValue, 0); free(ret); @@ -83,6 +85,8 @@ test_sanitizedpath() tmp_path[win32prgdir_len+1] = '\0'; ret = resolved_path_utf16(tmp_path); + /* resolved path will return in unix file format ex - c:/test/1/ */ + convertToBackslashW(ret); retValue = wcscmp(win32prgdir, ret); ASSERT_INT_EQ(retValue, 0); free(ret); @@ -92,6 +96,8 @@ test_sanitizedpath() s1[0] = '/', s1[1] = win32prgdir[0], s1[2] = ':', s1[3] = '\0'; s2[0] = win32prgdir[0], s2[1] = ':', s2[2] = '\\', s2[3] = '\0'; ret = resolved_path_utf16(s1); + /* resolved path will return in unix file format ex - c:/test/1/ */ + convertToBackslashW(ret); retValue = wcscmp(ret, s2); ASSERT_INT_EQ(retValue, 0); free(ret); diff --git a/regress/unittests/win32compat/socket_tests.c b/regress/unittests/win32compat/socket_tests.c index c186b64dc..7279f243d 100644 --- a/regress/unittests/win32compat/socket_tests.c +++ b/regress/unittests/win32compat/socket_tests.c @@ -462,7 +462,7 @@ socket_nonblocking_io_tests() void socket_select_tests() { int s, r; - int num_bytes = 1024 * 1024 * 2; //2 MB + int num_bytes = 1024 * 1024 * 4; //4 MB int bytes_sent = 0; int bytes_received = 0; int seed = 326; diff --git a/scp.c b/scp.c index 799a92975..783ce4195 100644 --- a/scp.c +++ b/scp.c @@ -214,13 +214,34 @@ do_local_cmd(arglist *a) cmd = xmalloc(cmdlen); cmd[0] = '\0'; for (i = 0; i < a->num; i++) { - if(i != 0) - strcat_s(cmd, cmdlen, " "); - strcat_s(cmd, cmdlen, a->list[i]); + char *path = a->list[i]; + if (is_bash_test_env()) { + char resolved[PATH_MAX] = { 0, }; + convertToForwardslash(path); + + if(bash_to_win_path(path, resolved, _countof(resolved))) + convertToBackslash(resolved); + + strcat(cmd, " "); + strcat(cmd, resolved); + } else { + if (i != 0) + strcat_s(cmd, cmdlen, " "); + strcat_s(cmd, cmdlen, a->list[i]); + } + } + + wchar_t *cmd_w = utf8_to_utf16(cmd); + if (cmd_w) { + if (_wsystem(cmd_w)) + return -1; + + free(cmd_w); + return 0; + } else { + error("%s out of memory", __func__); + return -1; } - if (system(cmd)) - return -1; - return 0; } #else /* !WINDOWS */ @@ -298,7 +319,7 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) /* Fork a child to execute the command on the remote host using ssh. */ #ifdef FORK_NOT_SUPPORTED - replacearg(&args, 0, "%s", ssh_program); + replacearg(&args, 0, "%s", ssh_program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); @@ -389,8 +410,8 @@ do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) /* Fork a child to execute the command on the remote host using ssh. */ #ifdef FORK_NOT_SUPPORTED - /* generate command line and spawn_child */ - replacearg(&args, 0, "%s", ssh_program); + /* generate command line and spawn_child */ + replacearg(&args, 0, "%s", ssh_program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); @@ -910,16 +931,23 @@ tolocal(int argc, char **argv) addargs(&alist, "/S /E /H"); if (pflag) addargs(&alist, "/K /X"); - addargs(&alist, "/Y /F /I"); - addargs(&alist, "%s", argv[i]); + addargs(&alist, "/Y /F /I"); + addargs(&alist, "%s", argv[i]); - if ((last = strrchr(argv[i], '\\')) == NULL) - last = argv[i]; - else - ++last; - - addargs(&alist, "%s%s%s", argv[argc - 1], - strcmp(argv[argc - 1], "\\") ? "\\" : "", last); + /* This logic is added to align with UNIX behavior. + * If the argv[argc-1] exists then append direcorty name from argv[i] + */ + if (0 == stat(argv[argc - 1], &stb) && (S_ISDIR(stb.st_mode))) { + if ((last = strrchr(argv[i], '\\')) == NULL) + last = argv[i]; + else + ++last; + + addargs(&alist, "%s%s%s", argv[argc - 1], + strcmp(argv[argc - 1], "\\") ? "\\" : "", last); + } else { + addargs(&alist, "%s", argv[argc - 1]); + } } else { addargs(&alist, "%s", _PATH_COPY); addargs(&alist, "/Y"); diff --git a/servconf.c b/servconf.c index b0aa085eb..fb47656e9 100644 --- a/servconf.c +++ b/servconf.c @@ -982,7 +982,11 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) } if (ci->user == NULL) match_test_missing_fatal("User", "user"); +#ifdef WINDOWS + if (match_pattern_list(ci->user, arg, 1) != 1) +#else if (match_pattern_list(ci->user, arg, 0) != 1) +#endif result = 0; else debug("user %.100s matched 'User %.100s' at " @@ -2279,17 +2283,46 @@ parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, * h) user@domain@hostip */ /* convert the users, user groups to lower case */ - for(int i = 0; i < options->num_allow_users; i++) + char *tmp = NULL; + for (int i = 0; i < options->num_allow_users; i++) { + /* For domain user we need special handling. + * We support both "domain\user" and "domain/user" formats. + */ + if (tmp = strstr(options->allow_users[i], "/")) + *tmp = '\\'; + lowercase(options->allow_users[i]); + } + + for (int i = 0; i < options->num_deny_users; i++) { + /* For domain user we need special handling. + * We support both "domain\user" and "domain/user" formats. + */ + if (tmp = strstr(options->deny_users[i], "/")) + *tmp = '\\'; - for (int i = 0; i < options->num_deny_users; i++) lowercase(options->deny_users[i]); + } + + for (int i = 0; i < options->num_allow_groups; i++) { + /* For domain group we need special handling. + * We support both "domain\group" and "domain/group" formats. + */ + if (tmp = strstr(options->allow_groups[i], "/")) + *tmp = '\\'; - for (int i = 0; i < options->num_allow_groups; i++) lowercase(options->allow_groups[i]); + } + + for (int i = 0; i < options->num_deny_groups; i++) { + /* For domain group we need special handling. + * We support both "domain\group" and "domain/group" formats. + */ + if (tmp = strstr(options->deny_groups[i], "/")) + *tmp = '\\'; - for (int i = 0; i < options->num_deny_groups; i++) lowercase(options->deny_groups[i]); + } #endif // WINDOWS } diff --git a/session.c b/session.c index b608dcb84..afb113a9a 100644 --- a/session.c +++ b/session.c @@ -369,7 +369,7 @@ xauth_valid_string(const char *s) * - Interactive shell/commands are executed using ssh-shellhost.exe * - ssh-shellhost.exe implements server-side PTY for Windows */ - +static char ** do_setup_env(struct ssh *ssh, Session *s, const char *shell); #define UTF8_TO_UTF16_WITH_CLEANUP(o, i) do { \ if (o != NULL) free(o); \ @@ -382,8 +382,8 @@ xauth_valid_string(const char *s) goto cleanup; \ } while(0) - /* TODO - built env var set and pass it along with CreateProcess */ - /* set user environment variables from user profile */ +/* TODO - built env var set and pass it along with CreateProcess */ +/* set user environment variables from user profile */ static void setup_session_user_vars(wchar_t* profile_path) { @@ -396,11 +396,22 @@ setup_session_user_vars(wchar_t* profile_path) LONG ret; SetEnvironmentVariableW(L"USERPROFILE", profile_path); + + if (profile_path[0] && profile_path[1] == L':') { + SetEnvironmentVariableW(L"HOMEPATH", profile_path + 2); + wchar_t wc = profile_path[2]; + profile_path[2] = L'\0'; + SetEnvironmentVariableW(L"HOMEDRIVE", profile_path); + profile_path[2] = wc; + } else { + SetEnvironmentVariableW(L"HOMEPATH", profile_path); + } + swprintf_s(path, _countof(path), L"%s\\AppData\\Local", profile_path); SetEnvironmentVariableW(L"LOCALAPPDATA", path); swprintf_s(path, _countof(path), L"%s\\AppData\\Roaming", profile_path); SetEnvironmentVariableW(L"APPDATA", path); - + ret = RegOpenKeyExW(HKEY_CURRENT_USER, L"Environment", 0, KEY_QUERY_VALUE, ®_key); if (ret != ERROR_SUCCESS) //error("Error retrieving user environment variables. RegOpenKeyExW returned %d", ret); @@ -462,55 +473,20 @@ cleanup: free(path_value); } -static int -setup_session_vars(Session* s) +static int +setup_session_env(struct ssh *ssh, Session* s) { - wchar_t *pw_dir_w = NULL, *tmp = NULL; + int i = 0, ret = -1; + char *env_name = NULL, *env_value = NULL, *t = NULL, **env = NULL, *path_env_val = NULL; char buf[1024] = { 0 }; - wchar_t wbuf[1024] = { 0 }; + wchar_t *env_name_w = NULL, *env_value_w = NULL, *pw_dir_w = NULL, *tmp = NULL, wbuf[1024] = { 0, }; char *laddr, *c; - int ret = -1; - - struct ssh *ssh = active_state; /* XXX */ UTF8_TO_UTF16_WITH_CLEANUP(pw_dir_w, s->pw->pw_dir); - /* skip domain part (if there) while setting USERNAME */ + /* skip domain part (if present) while setting USERNAME */ c = strchr(s->pw->pw_name, '\\'); UTF8_TO_UTF16_WITH_CLEANUP(tmp, c ? c + 1 : s->pw->pw_name); SetEnvironmentVariableW(L"USERNAME", tmp); - if (s->display) { - UTF8_TO_UTF16_WITH_CLEANUP(tmp, s->display); - SetEnvironmentVariableW(L"DISPLAY", tmp); - } - SetEnvironmentVariableW(L"USERPROFILE", pw_dir_w); - - if (pw_dir_w[0] && pw_dir_w[1] == L':') { - SetEnvironmentVariableW(L"HOMEPATH", pw_dir_w + 2); - wchar_t wc = pw_dir_w[2]; - pw_dir_w[2] = L'\0'; - SetEnvironmentVariableW(L"HOMEDRIVE", pw_dir_w); - pw_dir_w[2] = wc; - } else { - SetEnvironmentVariableW(L"HOMEPATH", pw_dir_w); - } - - snprintf(buf, sizeof buf, "%.50s %d %d", - ssh->remote_ipaddr, ssh->remote_port, ssh->local_port); - SetEnvironmentVariableA("SSH_CLIENT", buf); - - laddr = get_local_ipaddr(packet_get_connection_in()); - snprintf(buf, sizeof buf, "%.50s %d %.50s %d", - ssh->remote_ipaddr, ssh->remote_port, laddr, ssh->local_port); - free(laddr); - SetEnvironmentVariableA("SSH_CONNECTION", buf); - - if (original_command) { - UTF8_TO_UTF16_WITH_CLEANUP(tmp, original_command); - SetEnvironmentVariableW(L"SSH_ORIGINAL_COMMAND", tmp); - } - - if ((s->term) && (s->term[0])) - SetEnvironmentVariableA("TERM", s->term); if (!s->is_subsystem) { _snprintf(buf, ARRAYSIZE(buf), "%s@%s", s->pw->pw_name, getenv("COMPUTERNAME")); @@ -525,13 +501,50 @@ setup_session_vars(Session* s) SetEnvironmentVariableW(L"PROMPT", wbuf); } - /* setup any user specific env variables */ - setup_session_user_vars(pw_dir_w); + setup_session_user_vars(pw_dir_w); /* setup user specific env variables */ + env = do_setup_env(ssh, s, s->pw->pw_shell); + while (env_name = env[i]) { + if (t = strstr(env[i++], "=")) { + /* SKIP, if not applicable on WINDOWS + PATH is already set. + MAIL is not applicable. + */ + if ((0 == strncmp(env_name, "PATH=", strlen("PATH="))) || + (0 == strncmp(env_name, "MAIL=", strlen("MAIL=")))) { + continue; + } + + env_value = t + 1; + *t = '\0'; + UTF8_TO_UTF16_WITH_CLEANUP(env_name_w, env_name); + UTF8_TO_UTF16_WITH_CLEANUP(env_value_w, env_value); + + SetEnvironmentVariableW(env_name_w, env_value_w); + } + } + ret = 0; -cleanup: - free(pw_dir_w); - free(tmp); +cleanup : + if (pw_dir_w) + free(pw_dir_w); + + if (tmp) + free(tmp); + + if (env_name_w) + free(env_name_w); + + if (env_value_w) + free(env_value_w); + + if (env) { + i = 0; + while (t = env[i++]) + free(t); + + free(env); + } return ret; } @@ -568,7 +581,7 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { if (environment_set) break; - if (setup_session_vars(s) != 0) + if (setup_session_env(ssh, s) != 0) goto cleanup; environment_set = 1; @@ -637,7 +650,7 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { !SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)) || !AssignProcessToJobObject(job, pi.hProcess) || !DuplicateHandle(GetCurrentProcess(), job, pi.hProcess, &job_dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - error("cannot associate job object: %d", GetLastError()); + error("cannot associate job object: %d", GetLastError()); errno = EOTHER; TerminateProcess(pi.hProcess, 255); CloseHandle(pi.hProcess); @@ -2207,6 +2220,7 @@ session_window_change_req(struct ssh *ssh, Session *s) s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); packet_check_eom(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } diff --git a/sftp.c b/sftp.c index 47c7ca151..d9e044c11 100644 --- a/sftp.c +++ b/sftp.c @@ -70,6 +70,9 @@ typedef void EditLine; #include "sshbuf.h" #include "sftp-common.h" #include "sftp-client.h" +#ifdef WINDOWS +#include "misc_internal.h" +#endif // WINDOWS #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ @@ -295,15 +298,30 @@ static void local_do_shell(const char *args) { #ifdef WINDOWS - /* execute via system call in Windows*/ - char cmd_path[PATH_MAX] = { 0, }; - if (!*args){ + /* execute via system call in Windows*/ + if (!*args) { + char cmd_path[PATH_MAX] = { 0, }; if (!GetSystemDirectory(cmd_path, sizeof(cmd_path))) fatal("GetSystemDirectory failed"); strcat_s(cmd_path, PATH_MAX, "\\cmd.exe"); args = cmd_path; } else { + if (is_bash_test_env()) { + char *cygwin_path_prefix_start = NULL; + if (cygwin_path_prefix_start = strstr(args, CYGWIN_PATH_PREFIX)) { + int len = strlen(cygwin_path_prefix_start) + 1; + char *tmp = malloc(len); + memset(tmp, 0, len); + + bash_to_win_path(cygwin_path_prefix_start, tmp, len); + strcpy_s(cygwin_path_prefix_start, len, tmp); /* override the original string */ + + if (tmp) + free(tmp); + } + } + convertToBackslash((char *) args); } @@ -403,9 +421,6 @@ make_absolute(char *p, const char *pwd) p = abs_str; } - /* convert '\\' to '/' */ - convertToForwardslash(p); - /* Append "/" if needed to the absolute windows path */ if (p && p[0] != '\0' && p[1] == ':') { s1 = path_append("/", p); @@ -2556,11 +2571,13 @@ main(int argc, char **argv) } break; } - - /* TODO: need to debug this. this parameter doesn't make sense - file2 = *(argv + 1); - */ +#ifdef WINDOWS + if (argc == (optind + 2)) + file2 = *(argv + 1); +#else + file2 = *(argv + 1); +#endif if (!*host) { fprintf(stderr, "Missing hostname\n"); usage(); @@ -2586,7 +2603,12 @@ main(int argc, char **argv) connect_to_server(ssh_program, args.list, &in, &out); } else { args.list = NULL; + +#ifdef WINDOWS + addargs(&args, "sftp-server.exe"); +#elif addargs(&args, "sftp-server"); +#endif // WINDOWS connect_to_server(sftp_direct, args.list, &in, &out); } diff --git a/sshconnect.c b/sshconnect.c index 63d44a611..22360df6f 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -232,6 +232,10 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, spawn_argv[1] = NULL; pid = -1; + /* disable inheritance */ + fcntl(pin[1], F_SETFD, FD_CLOEXEC); + fcntl(pout[0], F_SETFD, FD_CLOEXEC); + 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) @@ -1572,8 +1576,19 @@ int ssh_local_cmd(const char *args) { #ifdef WINDOWS - fatal("executing local command is not supported in Windows yet"); - return 0; + if (!options.permit_local_command || + args == NULL || !*args) + return (1); + + int retVal = -1; + wchar_t *args_w = utf8_to_utf16(args); + + if (args_w) { + retVal = _wsystem(args_w); + free(args_w); + } + + return retVal; #else /* !WINDOWS */ char *shell; pid_t pid; diff --git a/sshd.c b/sshd.c index c042b6d07..52d202dea 100644 --- a/sshd.c +++ b/sshd.c @@ -199,7 +199,8 @@ int have_agent = 0; int privsep_unauth_child = 0; int privsep_auth_child = 0; -int tmp_sock = 0; +int io_sock_in = 0; +int io_sock_out = 0; /* * Any really sensitive data in the application is contained in this @@ -684,13 +685,13 @@ recv_hostkeys_state(int fd) fatal("%s: ssh_msg_recv failed", __func__); if (buffer_get_char(m) != 0) - fatal("%s: recv_hostkeys_state version mismatch", __func__); + fatal("%s: version mismatch", __func__); - int num = buffer_get_int(m); - sensitive_data.host_keys = xcalloc(num, sizeof(struct sshkey *)); - sensitive_data.host_pubkeys = xcalloc(num, sizeof(struct sshkey *)); - sensitive_data.host_certificates = xcalloc(num, sizeof(struct sshkey *)); - for (int i = 0; i < num; i++) { + int num_host_key_files = buffer_get_int(m); + sensitive_data.host_keys = xcalloc(num_host_key_files, sizeof(struct sshkey *)); + sensitive_data.host_pubkeys = xcalloc(num_host_key_files, sizeof(struct sshkey *)); + sensitive_data.host_certificates = xcalloc(num_host_key_files, sizeof(struct sshkey *)); + for (int i = 0; i < num_host_key_files; i++) { blob = buffer_get_string_ptr(m, &blen); sensitive_data.host_pubkeys[i] = NULL; sensitive_data.host_keys[i] = NULL; @@ -700,7 +701,8 @@ recv_hostkeys_state(int fd) sensitive_data.host_pubkeys[i] = key; } } - for (int i = 0; i < num; i++) { + + for (int i = 0; i < num_host_key_files; i++) { blob = buffer_get_string_ptr(m, &blen); sensitive_data.host_certificates[i] = NULL; if (blen) { @@ -750,7 +752,27 @@ static char** privsep_child_cmdline(int authenticated) { char** argv = rexec_argv ? rexec_argv : saved_argv; - int argc = rexec_argv ? rexec_argc : saved_argc - 1; + int argc = 0; + + if (rexec_argv) + argc = rexec_argc; + else { + if (rexeced_flag) + argc = saved_argc - 1; // override '-R' + else { + char **tmp = xcalloc(saved_argc + 1 + 1, sizeof(*saved_argv)); // 1 - extra argument "-y/-z", 1 - NULL + int i = 0; + for (i = 0; (int)i < saved_argc; i++) { + tmp[i] = xstrdup(saved_argv[i]); + free(saved_argv[i]); + } + + free(saved_argv); + argv = saved_argv = tmp; + argc = saved_argc; + } + } + if (authenticated) argv[argc] = "-z"; else @@ -801,8 +823,8 @@ privsep_preauth(Authctxt *authctxt) posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init(&actions) != 0 || - posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDIN_FILENO) != 0 || - posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, io_sock_in, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, io_sock_out, STDOUT_FILENO) != 0 || posix_spawn_file_actions_adddup2(&actions, pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) != 0 || posix_spawn_file_actions_adddup2(&actions, pmonitor->m_log_sendfd, PRIVSEP_LOG_FD) != 0 ) fatal("posix_spawn initialization failed"); @@ -923,8 +945,8 @@ privsep_postauth(Authctxt *authctxt) posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init(&actions) != 0 || - posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDIN_FILENO) != 0 || - posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, io_sock_in, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, io_sock_out, STDOUT_FILENO) != 0 || posix_spawn_file_actions_adddup2(&actions, pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) != 0) fatal("posix_spawn initialization failed"); @@ -942,6 +964,7 @@ privsep_postauth(Authctxt *authctxt) send_autxctx_state(authctxt, pmonitor->m_sendfd); monitor_send_keystate(pmonitor); monitor_clear_keystate(pmonitor); + monitor_send_authopt(pmonitor, 0); // 0 - trusted. monitor_child_postauth(pmonitor); /* NEVERREACHED */ exit(0); @@ -956,6 +979,7 @@ privsep_postauth(Authctxt *authctxt) do_setusercontext(authctxt->pw); monitor_apply_keystate(pmonitor); + monitor_recv_authopt(pmonitor); packet_set_authenticated(); skip: return; @@ -2383,7 +2407,8 @@ done_loading_hostkeys: * Register our connection. This turns encryption off because we do * not have a key. */ - tmp_sock = sock_in; + io_sock_in = sock_in; + io_sock_out = sock_out; packet_set_connection(sock_in, sock_out); packet_set_server(); ssh = active_state; /* XXX */ diff --git a/sshkey.c b/sshkey.c index 4b664f04a..0dea5b248 100644 --- a/sshkey.c +++ b/sshkey.c @@ -59,6 +59,9 @@ #include "match.h" #include "xmss_fast.h" +#ifdef WINDOWS +#include +#endif /* openssh private key file format */ #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" @@ -2646,7 +2649,27 @@ sshkey_cert_check_authority(const struct sshkey *k, } else if (name != NULL) { principal_matches = 0; for (i = 0; i < k->cert->nprincipals; i++) { +#ifdef WINDOWS + char cert_principal_name_copy[UNLEN + DNLEN + 1 + 1] = { 0, }; + strcpy_s(cert_principal_name_copy, _countof(cert_principal_name_copy), k->cert->principals[i]); + + /* + * For domain user we need special handling. + * We support both "domain\user" and "domain/user" formats. + * compare user, domain separately. + */ + if (strstr(name, "/") || strstr(name, "\\")) { + char *tmp = NULL; + if (tmp = strstr(cert_principal_name_copy, "/")) + *tmp = '\\'; + } + + /* In windows, usernames are case insensitive */ + if (_strcmpi(name, cert_principal_name_copy) == 0) { +#else if (strcmp(name, k->cert->principals[i]) == 0) { +#endif + principal_matches = 1; break; }