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"
This commit is contained in:
parent
0840af06bf
commit
af4e4113b2
10
auth.c
10
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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
#define HAVE_FREEZERO
|
||||
#define FILESYSTEM_NO_BACKSLASH
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\spawn.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\w32api_proxies.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32_usertoken_utils.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\conhost_conpty.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\w32fd.h" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
#include STDLIB_H
|
||||
|
||||
#define environ _environ
|
||||
void freezero(void *, size_t);
|
||||
void freezero(void *, size_t);
|
||||
int setenv(const char *name, const char *value, int rewrite);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 "<e:\openssh\regress\ssh-log-wrapper.sh>"
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -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) ||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <binary_location>\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);
|
||||
}
|
||||
|
||||
|
||||
|
|
10
misc.c
10
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) {
|
||||
|
|
38
monitor.c
38
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 */
|
||||
|
||||
|
||||
|
|
13
readconf.c
13
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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -49,11 +49,18 @@ EOD
|
|||
[ X${SKIP_IPV6} = Xyes ] || cat >> $OBJ/sshd_config.1 <<EOD
|
||||
listenaddress ::1
|
||||
EOD
|
||||
|
||||
($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"
|
||||
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 <<EOD
|
||||
${SSHD_KEYS}
|
||||
|
@ -65,11 +72,18 @@ EOD
|
|||
[ X${SKIP_IPV6} = Xyes ] || cat >> $OBJ/sshd_config.1 <<EOD
|
||||
listenaddress ::1
|
||||
EOD
|
||||
|
||||
($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"
|
||||
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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <<EOE
|
||||
somehost
|
||||
|
|
|
@ -11,10 +11,19 @@ r=$?
|
|||
[ $r -ne 0 ] && fatal "could not start ssh-agent: exit code $r"
|
||||
|
||||
grep -vi 'hostkey' $OBJ/sshd_proxy > $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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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\"" | \
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<sshd.log> 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
64
scp.c
64
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");
|
||||
|
|
41
servconf.c
41
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
|
||||
}
|
||||
|
||||
|
|
118
session.c
118
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;
|
||||
}
|
||||
|
|
42
sftp.c
42
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);
|
||||
}
|
||||
|
|
19
sshconnect.c
19
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;
|
||||
|
|
53
sshd.c
53
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 */
|
||||
|
|
23
sshkey.c
23
sshkey.c
|
@ -59,6 +59,9 @@
|
|||
#include "match.h"
|
||||
|
||||
#include "xmss_fast.h"
|
||||
#ifdef WINDOWS
|
||||
#include <lmcons.h>
|
||||
#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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue