From 9c95d8e2bb14b43a72b3490fa1ba364d2ed5d843 Mon Sep 17 00:00:00 2001 From: bagajjal Date: Mon, 16 Oct 2017 10:45:11 -0700 Subject: [PATCH] allowing users to configure the default shell through windows registry (#215) --- contrib/win32/win32compat/shell-host.c | 94 ++++++++++++++++++++++---- regress/pesterTests/SSH.Tests.ps1 | 46 ++++++++++++- 2 files changed, 125 insertions(+), 15 deletions(-) diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index 5a5afbbea..0388a27ca 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -255,8 +255,11 @@ struct key_translation keys[] = { static SHORT lastX = 0; static SHORT lastY = 0; -static wchar_t system32_path[PATH_MAX]; -static wchar_t cmd_exe_path[PATH_MAX]; +static wchar_t system32_path[PATH_MAX + 1] = { 0, }; +static wchar_t cmd_exe_path[PATH_MAX + 1] = { 0, }; +static wchar_t default_shell_path[PATH_MAX + 3] = { 0, }; /* 2 - quotes, 1 - Null terminator */ +static wchar_t default_shell_cmd_option[10] = { 0, }; /* for cmd.exe/powershell it is "/c", for bash.exe it is "-c" */ +static BOOL is_default_shell_configured = FALSE; SHORT currentLine = 0; consoleEvent* head = NULL; @@ -1192,19 +1195,70 @@ cleanup: } wchar_t * -w32_cmd_path() +get_default_shell_path() { + HKEY reg_key = 0; + int tmp_len = PATH_MAX; errno_t r = 0; - if ((r = wcsncpy_s(cmd_exe_path, _countof(cmd_exe_path), system32_path, wcsnlen(system32_path, _countof(system32_path)) + 1)) != 0) { - printf_s("wcsncpy_s failed with error: %d.", r); + REGSAM mask = STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY; + wchar_t *tmp = malloc(PATH_MAX + 1); + + if (!tmp) { + printf_s("get_default_shell_path(), Unable to allocate memory"); exit(255); } - if ((r = wcscat_s(cmd_exe_path, _countof(cmd_exe_path), L"\\cmd.exe")) != 0) { - printf_s("wcscat_s failed with error: %d.", r); + memset(tmp, 0, PATH_MAX + 1); + memset(default_shell_path, 0, _countof(default_shell_path)); + + if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\OpenSSH", 0, mask, ®_key) == ERROR_SUCCESS) && + (RegQueryValueExW(reg_key, L"DefaultShell", 0, NULL, (LPBYTE)tmp, &tmp_len) == ERROR_SUCCESS) && + (tmp)) { + is_default_shell_configured = TRUE; + + /* If required, add quotes to the default shell. */ + if (tmp[0] != L'"') { + default_shell_path[0] = L'\"'; + wcscat_s(default_shell_path, _countof(default_shell_path), tmp); + wcscat_s(default_shell_path, _countof(default_shell_path), L"\""); + } else + wcscat_s(default_shell_path, _countof(default_shell_path), tmp); + + /* Fetch the default shell command option. + * For cmd.exe/powershell.exe it is "/c", for bash.exe it is "-c". + * For cmd.exe/powershell.exe/bash.exe, verify if present otherwise auto-populate. + */ + memset(tmp, 0, PATH_MAX + 1); + + if ((RegQueryValueExW(reg_key, L"DefaultShellCommandOption", 0, NULL, (LPBYTE)tmp, &tmp_len) == ERROR_SUCCESS)) { + wcscat_s(default_shell_cmd_option, _countof(default_shell_cmd_option), L" "); + wcscat_s(default_shell_cmd_option, _countof(default_shell_cmd_option), tmp); + wcscat_s(default_shell_cmd_option, _countof(default_shell_cmd_option), L" "); + } + } + + if (((r = wcsncpy_s(cmd_exe_path, _countof(cmd_exe_path), system32_path, wcsnlen(system32_path, _countof(system32_path)) + 1)) != 0) || + ((r = wcscat_s(cmd_exe_path, _countof(cmd_exe_path), L"\\cmd.exe")) != 0)) { + printf_s("get_default_shell_path(), wcscat_s failed with error: %d.", r); exit(255); } - return cmd_exe_path; + + /* if default shell is not configured then use cmd.exe as the default shell */ + if (!is_default_shell_configured) + wcscat_s(default_shell_path, _countof(default_shell_path), cmd_exe_path); + + memset(default_shell_cmd_option, 0, _countof(default_shell_cmd_option)); + if (!default_shell_cmd_option[0]) { + if (wcsstr(default_shell_path, L"cmd.exe") || wcsstr(default_shell_path, L"powershell.exe")) + wcscat_s(default_shell_cmd_option, _countof(default_shell_cmd_option), L" /c "); + else if (wcsstr(default_shell_path, L"bash.exe")) + wcscat_s(default_shell_cmd_option, _countof(default_shell_cmd_option), L" -c "); + } + + if (tmp) + free(tmp); + + return default_shell_path; } int @@ -1280,10 +1334,24 @@ start_with_pty(wchar_t *command) GOTO_CLEANUP_ON_FALSE(SetHandleInformation(pipe_in, HANDLE_FLAG_INHERIT, 0)); cmd[0] = L'\0'; - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, w32_cmd_path())); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, get_default_shell_path())); if (command) { - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c ")); + if(default_shell_cmd_option[0]) + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, default_shell_cmd_option)); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, command)); + } else { + /* Launch the default shell through cmd.exe. + * If we don't launch default shell through cmd.exe then the powershell colors are rendered badly to the ssh client. + */ + if (is_default_shell_configured) { + wchar_t tmp_cmd[PATH_MAX + 1] = {0,}; + wcscat_s(tmp_cmd, _countof(tmp_cmd), cmd); + cmd[0] = L'\0'; + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, cmd_exe_path)); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c ")); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, tmp_cmd)); + } } SetConsoleCtrlHandler(NULL, FALSE); @@ -1454,9 +1522,11 @@ start_withno_pty(wchar_t *command) /* if above failed with FILE_NOT_FOUND, try running the provided command under cmd*/ if (run_under_cmd) { cmd[0] = L'\0'; - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, w32_cmd_path())); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, get_default_shell_path())); if (command) { - GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c ")); + if (default_shell_cmd_option[0]) + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, default_shell_cmd_option)); + GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, command)); } diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1 index ab750de0f..2abb31928 100644 --- a/regress/pesterTests/SSH.Tests.ps1 +++ b/regress/pesterTests/SSH.Tests.ps1 @@ -67,7 +67,19 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { Options = '-i $identifyFile -l $($server.localAdminUserName)' } )#> - + $defaultRegistryPath = "HKLM:\Software\OpenSSH" + $registryKeyName = "DefaultShell" + + function ConfigureDefaultShell { + param([string]$default_shell_path) + + if (!(Test-Path $defaultRegistryPath)) { + New-Item -Path $defaultRegistryPath -Force | Out-Null + New-ItemProperty -Path $defaultRegistryPath -Name $registryKeyName -Value $default_shell_path -PropertyType String -Force + } else { + New-ItemProperty -Path $defaultRegistryPath -Name $registryKeyName -Value $default_shell_path -PropertyType String -Force + } + } } BeforeEach { @@ -98,7 +110,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { } } - + Context "$tC - exit code (exit-status.sh)" { BeforeAll {$tI=1} AfterAll{$tC++} @@ -131,8 +143,36 @@ Describe "E2E scenarios for ssh client" -Tags "CI" { 0 | ssh -p $port $ssouser@$server pause $true | Should Be $true }#> - } + } + + Context "$tC - configure default shell Scenarios" { + BeforeAll {$tI=1} + AfterAll{$tC++} + AfterEach { + Remove-ItemProperty -Name $registryKeyName -Path $defaultRegistryPath -ErrorAction SilentlyContinue + } + It "$tC.$tI - default shell as powershell" { + $shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path + if($shell_path -ne $null) { + ConfigureDefaultShell -default_shell_path $shell_path + + $o = ssh test_target Write-Output 1234 + $o | Should Be "1234" + } + } + + It "$tC.$tI - default shell as cmd" { + $shell_path = (Get-Command cmd.exe -ErrorAction SilentlyContinue).path + if($shell_path -ne $null) { + ConfigureDefaultShell -default_shell_path $shell_path + + $o = ssh test_target where cmd + $o | Should Contain "cmd" + } + } + } + Context "$tC - cmdline parameters" { BeforeAll {$tI=1}