diff --git a/contrib/win32/openssh/sshd.vcxproj b/contrib/win32/openssh/sshd.vcxproj
index 5362732aa..0736c53a8 100644
--- a/contrib/win32/openssh/sshd.vcxproj
+++ b/contrib/win32/openssh/sshd.vcxproj
@@ -462,7 +462,8 @@
-
+
+
diff --git a/contrib/win32/openssh/sshd.vcxproj.filters b/contrib/win32/openssh/sshd.vcxproj.filters
index 643ebb7b6..54f52a6d1 100644
--- a/contrib/win32/openssh/sshd.vcxproj.filters
+++ b/contrib/win32/openssh/sshd.vcxproj.filters
@@ -150,10 +150,16 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
Resource Files
-
+
diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c
index 5a83a642f..aab66e9ca 100644
--- a/contrib/win32/win32compat/misc.c
+++ b/contrib/win32/win32compat/misc.c
@@ -910,7 +910,7 @@ realpath(const char *inputpath, char resolved[PATH_MAX])
if (is_win_path) {
if (_strnicmp(inputpath, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0) {
- strcat_s(path, PATH_MAX, __progdata);
+ strcpy_s(path, PATH_MAX, __progdata);
strcat_s(path, PATH_MAX, &inputpath[strlen(PROGRAM_DATA)]);
} else {
memcpy_s(path, PATH_MAX, inputpath, strlen(inputpath));
@@ -1318,7 +1318,7 @@ int
is_absolute_path(const char *path)
{
int retVal = 0;
- if(*path == '\"') /* skip double quote if path is "c:\abc" */
+ if(*path == '\"' || *path == '\'') /* skip double quote if path is "c:\abc" */
path++;
if (*path == '/' || *path == '\\' || (*path != '\0' && isalpha(*path) && path[1] == ':') ||
@@ -1583,201 +1583,222 @@ cleanup:
return ret;
}
-
-/* builds session commandline. returns NULL with errno set on failure, caller should free returned string */
-char*
-build_session_commandline(const char *shell, const char* shell_arg, const char *command)
+/* Interpret scp and sftp executables*/
+char *
+build_exec_command(const char * command)
{
- 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);
+ char *cmd_sp = NULL;
+ int len = 0, command_len;
+ const char *command_args = NULL;
-#define CMDLINE_APPEND(P, S) \
-do { \
- int _S_len = (int)strlen(S); \
- memcpy((P), (S), _S_len); \
- (P) += _S_len; \
-} while(0)
+ if (!command)
+ return NULL;
- /* get shell type */
- if (strstr(shell, "system32\\cmd"))
- shell_type = SH_CMD;
- else if (strstr(shell, "powershell"))
- shell_type = SH_PS;
- else if (strstr(shell, "\\bash"))
- shell_type = SH_BASH;
- else if (strstr(shell, "cygwin")) {
- shell_type = SH_CYGWIN;
+ command_len = (int)strlen(command);
+ /*TODO - replace numbers below with readable compile time operators*/
+ if (command_len >= 13 && _memicmp(command, "internal-sftp", 13) == 0) {
+ command_type = CMD_SFTP;
+ command_args = command + 13;
}
+ else if (command_len >= 11 && _memicmp(command, "sftp-server", 11) == 0) {
+ command_type = CMD_SFTP;
- /* special case where incoming command needs to be adjusted */
- do {
- /*
- * identify scp and sftp sessions
- * we want to launch scp and sftp executables from the same binary directory
- * that sshd is hosted in. This will facilitate hosting and evaluating
- * multiple versions of OpenSSH at the same time.
- *
- * currently we can only accomodate this for cmd.exe, since cmd.exe simply executes
- * its commandline without applying CRT or shell specific rules
- *
- * Ex.
- * this works
- * cmd /c "c:\program files\sftp" -d
- * this following wouldn't work in powershell, cygwin's or WSL bash unless we put
- * some shell specific rules
- * powershell -c "c:\program files\scp" -t
- * cygwin\bash -c "c:\program files\scp" -t
- * bash -c "c:\program files\scp" -t
- *
- * for now, for all non-cmd shells, we launch scp and sftp-server directly -
- * shell -c "scp.exe -t"
- * note that .exe extension and case matters for WSL bash
- * note that double quotes matter for WSL and Cygwin bash, they dont matter for PS
- *
- * consequence -
- * for non-cmd shells - sftp and scp installation path is expected to be in machine wide PATH
- *
- */
-
- int command_len;
- const char *command_args = NULL;
-
- if (!command)
- break;
- command_len = (int)strlen(command);
- /*TODO - replace numbers below with readable compile time operators*/
- if (command_len >= 13 && _memicmp(command, "internal-sftp", 13) == 0) {
- command_type = CMD_SFTP;
- command_args = command + 13;
- } else if (command_len >= 11 && _memicmp(command, "sftp-server", 11) == 0) {
- command_type = CMD_SFTP;
-
- /* account for possible .exe extension */
- if (command_len >= 15 && _memicmp(command + 11, ".exe", 4) == 0)
- command_args = command + 15;
- else
- command_args = command + 11;
- } else if (command_len >= 3 && _memicmp(command, "scp", 3) == 0) {
- command_type = CMD_SCP;
-
- /* account for possible .exe extension */
- if (command_len >= 7 && _memicmp(command + 3, ".exe", 4) == 0)
- command_args = command + 7;
- else
- command_args = command + 3;
- }
-
- if (command_type == CMD_OTHER)
- break;
-
- len = 0;
- len += progdir_len + 4; /* account for " around */
- len += command_len + 4; /* account for possible .exe addition */
-
- if ((cmd_sp = malloc(len)) == NULL) {
- errno = ENOMEM;
- goto done;
- }
-
- p = cmd_sp;
-
- if ((shell_type == SH_CMD) || (shell_type == SH_CYGWIN)) {
- CMDLINE_APPEND(p, "\"");
- CMDLINE_APPEND(p, progdir);
-
- if (command_type == CMD_SCP)
- CMDLINE_APPEND(p, "\\scp.exe\"");
- else
- CMDLINE_APPEND(p, "\\sftp-server.exe\"");
- } else {
- if (command_type == CMD_SCP)
- CMDLINE_APPEND(p, "scp.exe");
- else
- 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;
- } while (0);
-
- len = 0;
- 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 */
-
- 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) {
- errno = ENOMEM;
- goto done;
- }
-
- p = cmdline;
- CMDLINE_APPEND(p, "\"");
- CMDLINE_APPEND(p, shell);
- CMDLINE_APPEND(p, "\"");
- if (command) {
- if (shell_arg) {
- CMDLINE_APPEND(p, " ");
- CMDLINE_APPEND(p, shell_arg);
- CMDLINE_APPEND(p, " ");
- }
- else if (shell_type == SH_CMD)
- CMDLINE_APPEND(p, " /c ");
+ /* account for possible .exe extension */
+ if (command_len >= 15 && _memicmp(command + 11, ".exe", 4) == 0)
+ command_args = command + 15;
else
- CMDLINE_APPEND(p, " -c ");
-
- /* Add double quotes around command */
- CMDLINE_APPEND(p, "\"");
- 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, "\"");
+ command_args = command + 11;
}
- *p = '\0';
- ret = cmdline;
- cmdline = NULL;
-done:
- if (cmd_sp)
- free(cmd_sp);
- if (cmdline)
- free(cmdline);
+ else if (command_len >= 3 && _memicmp(command, "scp", 3) == 0) {
+ command_type = CMD_SCP;
- return ret;
+ /* account for possible .exe extension */
+ if (command_len >= 7 && _memicmp(command + 3, ".exe", 4) == 0)
+ command_args = command + 7;
+ else
+ command_args = command + 3;
+ }
+
+ len = command_len + 5; /* account for possible .exe addition and null term */
+ if ((cmd_sp = malloc(len)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memset(cmd_sp, '\0', len);
+ if (command_type == CMD_SCP) {
+ strcpy_s(cmd_sp, len, "scp.exe");
+ strcat_s(cmd_sp, len, command_args);
+ }
+ else if (command_type == CMD_SFTP) {
+ strcpy_s(cmd_sp, len, "sftp-server.exe");
+ strcat_s(cmd_sp, len, command_args);
+ }
+ else
+ strcpy_s(cmd_sp, len, command);
+ return cmd_sp;
}
+/*
+ * cmd is internally decoarated with a set of '"'
+ * to account for any spaces within the commandline
+ * the double quotes and backslash is escaped if needed
+ * this decoration is done only when additional arguments are passed in argv
+*/
+char *
+build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path)
+{
+ char *cmdline, *t, *tmp = NULL, *path = NULL, *ret = NULL;
+ char * const *t1;
+ DWORD cmdline_len = 0, path_len = 0;
+ int add_module_path = 0;
+
+ if (!cmd) {
+ error("%s invalid argument cmd:%s", __func__, cmd);
+ return NULL;
+ }
+
+ if (!(path = _strdup(cmd))) {
+ error("failed to duplicate %s", cmd);
+ return NULL;
+ }
+
+ path_len = (DWORD)strlen(path);
+
+ if (is_bash_test_env()) {
+ memset(path, 0, path_len + 1);
+ bash_to_win_path(cmd, path, path_len + 1);
+ }
+
+ 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(path) + 1 + 2;
+ else
+ cmdline_len += (DWORD)strlen(path) + 1 + 2;
+
+ if (argv) {
+ t1 = argv;
+ while (*t1) {
+ char *p = *t1++;
+ for (int i = 0; i < (int)strlen(p); i++) {
+ if (p[i] == '\\') {
+ char * b = p + i;
+ int additional_backslash = 0;
+ int backslash_count = 0;
+ /*
+ Backslashes are interpreted literally, unless they immediately
+ precede a double quotation mark.
+ */
+ while (b != NULL && *b == '\\') {
+ backslash_count++;
+ b++;
+ if (b != NULL && *b == '\"') {
+ additional_backslash = 1;
+ break;
+ }
+ }
+ cmdline_len += backslash_count * (additional_backslash + 1);
+ i += backslash_count - 1;
+ }
+ else if (p[i] == '\"')
+ /* backslash will be added for every double quote.*/
+ cmdline_len += 2;
+ else
+ cmdline_len++;
+ }
+ cmdline_len += 1 + 2; /*for "around cmd arg and traling space*/
+ }
+ }
+
+ if ((cmdline = malloc(cmdline_len)) == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ t = cmdline;
+ *t++ = '\"';
+ if (add_module_path) {
+ /* add current module path to start if needed */
+ memcpy(t, __progdir, strlen(__progdir));
+ t += strlen(__progdir);
+ *t++ = '\\';
+ }
+ if (path[0] != '\"') {
+ memcpy(t, path, path_len);
+ t += path_len;
+ *t++ = '\"';
+ }
+ else {
+ /*path already contains "*/
+ memcpy(t, path + 1, path_len - 1);
+ t += path_len - 1;
+ }
+
+ *t = '\0';
+ t = cmdline + strlen(cmdline);
+
+ if (argv) {
+ t1 = argv;
+ while (*t1) {
+ *t++ = ' ';
+ char * p1 = *t1++;
+ BOOL add_quotes = FALSE;
+ /* leave as is if the command is surrounded by single quotes*/
+ if (p1[0] != '\'')
+ for (int i = 0; i < (int)strlen(p1); i++) {
+ if (p1[i] == ' ') {
+ add_quotes = TRUE;
+ break;
+ }
+ }
+ if (add_quotes)
+ *t++ = '\"';
+ for (int i = 0; i < (int)strlen(p1); i++) {
+ if (p1[i] == '\\') {
+ char * b = p1 + i;
+ int additional_backslash = 0;
+ int backslash_count = 0;
+ /*
+ * Backslashes are interpreted literally, unless they immediately
+ * precede a double quotation mark.
+ */
+ while (b != NULL && *b == '\\') {
+ backslash_count++;
+ b++;
+ if (b != NULL && *b == '\"') {
+ additional_backslash = 1;
+ break;
+ }
+ }
+ i += backslash_count - 1;
+ int escaped_backslash_count = backslash_count * (additional_backslash + 1);
+ while (escaped_backslash_count--)
+ *t++ = '\\';
+ }
+ else if (p1[i] == '\"') {
+ /* Add backslash for every double quote.*/
+ *t++ = '\\';
+ *t++ = '\"';
+ }
+ else
+ *t++ = p1[i];
+ }
+ if (add_quotes)
+ *t++ = '\"';
+ }
+ }
+ *t = '\0';
+ ret = cmdline;
+ cmdline = NULL;
+cleanup:
+ if (path)
+ free(path);
+ if (cmdline)
+ free(cmdline);
+ return ret;
+}
BOOL
is_bash_test_env()
{
@@ -1800,15 +1821,14 @@ 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);
+ memset(out, 0, out_len);
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);
- }
+ } else
+ strcpy_s(out, out_len, in);
return retVal;
}
@@ -1838,7 +1858,7 @@ freerrset(struct rrsetinfo *rrset)
return;
}
-void
+void
debug_assert_internal()
{
/* debug break on non-release builds */
@@ -1847,7 +1867,7 @@ debug_assert_internal()
#endif
}
-char
+char
*crypt(const char *key, const char *salt)
{
verbose("%s is not supported", __func__);
@@ -1855,11 +1875,11 @@ char
return NULL;
}
-int
+int
w32_system(const char *command)
{
int ret = -1;
- wchar_t *command_w = NULL;
+ wchar_t *command_w = NULL;
if (!command) {
errno = ENOTSUP;
diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h
index bc4e5068b..0dc05b1de 100644
--- a/contrib/win32/win32compat/misc_internal.h
+++ b/contrib/win32/win32compat/misc_internal.h
@@ -70,9 +70,10 @@ int is_absolute_path(const char *);
int file_in_chroot_jail(HANDLE);
PSID get_sid(const char*);
int am_system();
-char* build_session_commandline(const char *, const char *, const char *);
int is_conpty_supported();
-int exec_command_with_pty(wchar_t*, STARTUPINFOW*, PROCESS_INFORMATION*, int);
+int exec_command_with_pty(int * pid, char* cmd, int in, int out, int err, unsigned int col, unsigned int row, int ttyfd);
+char * build_exec_command(const char * command);
+char * build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path);
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);
diff --git a/contrib/win32/win32compat/pwd.c b/contrib/win32/win32compat/pwd.c
index 5b4fb5eec..d528fe33c 100644
--- a/contrib/win32/win32compat/pwd.c
+++ b/contrib/win32/win32compat/pwd.c
@@ -50,6 +50,7 @@
static struct passwd pw;
static char* pw_shellpath = NULL;
char* shell_command_option = NULL;
+BOOLEAN arg_escape = TRUE;
/* returns 0 on success, and -1 with errno set on failure */
static int
@@ -76,8 +77,12 @@ set_defaultshell()
(path_buf[0] != L'\0')) {
/* fetched default shell path from registry */
tmp_len = _countof(option_buf);
+ DWORD size = sizeof(DWORD);
+ DWORD escape_option = 1;
if (RegQueryValueExW(reg_key, L"DefaultShellCommandOption", 0, NULL, (LPBYTE)option_buf, &tmp_len) != ERROR_SUCCESS)
option_buf[0] = L'\0';
+ if (RegQueryValueExW(reg_key, L"DefaultShellEscapeArguments", 0, NULL, (LPBYTE)&escape_option, &size) == ERROR_SUCCESS)
+ arg_escape = (escape_option != 0) ? TRUE : FALSE;
} else {
if (!GetSystemDirectoryW(path_buf, _countof(path_buf))) {
errno = GetLastError();
diff --git a/contrib/win32/win32compat/spawn-ext.c b/contrib/win32/win32compat/spawn-ext.c
index 80d6dc569..45de4ba9f 100644
--- a/contrib/win32/win32compat/spawn-ext.c
+++ b/contrib/win32/win32compat/spawn-ext.c
@@ -3,7 +3,7 @@
#include "inc\unistd.h"
#include "debug.h"
-int posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token);
+int posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token, BOOLEAN prepend_module_path);
int
__posix_spawn_asuser(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], char* user)
@@ -21,7 +21,7 @@ __posix_spawn_asuser(pid_t *pidp, const char *path, const posix_spawn_file_actio
if (strcmp(user, "sshd"))
load_user_profile(user_token, user);
- r = posix_spawn_internal(pidp, path, file_actions, attrp, argv, envp, user_token);
+ r = posix_spawn_internal(pidp, path, file_actions, attrp, argv, envp, user_token, TRUE);
CloseHandle(user_token);
return r;
}
\ No newline at end of file
diff --git a/contrib/win32/win32compat/w32-doexec.c b/contrib/win32/win32compat/w32-doexec.c
new file mode 100644
index 000000000..5bd55fb1f
--- /dev/null
+++ b/contrib/win32/win32compat/w32-doexec.c
@@ -0,0 +1,447 @@
+/*
+* Author: Yanbing Wang
+*
+* Support execution of commands on Win32 based operating systems.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include "includes.h"
+
+#include
+#include "xmalloc.h"
+#include "packet.h"
+
+#include "channels.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "log.h"
+#include "misc.h"
+#include "servconf.h"
+#include "pal_doexec.h"
+#include "misc_internal.h"
+
+#ifndef SUBSYSTEM_NONE
+#define SUBSYSTEM_NONE 0
+#endif
+#ifndef SUBSYSTEM_EXT
+#define SUBSYSTEM_EXT 1
+#endif
+#ifndef SUBSYSTEM_INT_SFTP
+#define SUBSYSTEM_INT_SFTP 2
+#endif
+#ifndef SUBSYSTEM_INT_SFTP_ERROR
+#define SUBSYSTEM_INT_SFTP_ERROR 3
+#endif
+
+/* import */
+extern ServerOptions options;
+extern struct sshauthopt *auth_opts;
+char **
+do_setup_env_proxy(struct ssh *, Session *, const char *);
+
+/*
+* do_exec* on Windows
+* - Read and set user environment variables from registry
+* - Build subsystem cmdline path
+* - Interactive shell/commands are executed using ssh-shellhost.exe
+* - ssh-shellhost.exe implements server-side PTY for Windows
+*/
+#define UTF8_TO_UTF16_WITH_CLEANUP(o, i) do { \
+ if (o != NULL) free(o); \
+ if ((o = utf8_to_utf16(i)) == NULL) \
+ goto cleanup; \
+} while (0)
+
+#define GOTO_CLEANUP_ON_ERR(exp) do { \
+ if ((exp) != 0) \
+ goto cleanup; \
+} while(0)
+
+/* 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)
+{
+ /* retrieve and set env variables. */
+ HKEY reg_key = 0;
+ wchar_t name[256];
+ wchar_t path[PATH_MAX + 1] = { 0, };
+ wchar_t *data = NULL, *data_expanded = NULL, *path_value = NULL, *to_apply;
+ DWORD type, name_chars = 256, data_chars = 0, data_expanded_chars = 0, required, i = 0;
+ 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);
+ return;
+ else while (1) {
+ to_apply = NULL;
+ required = data_chars * sizeof(wchar_t);
+ name_chars = 256;
+ ret = RegEnumValueW(reg_key, i++, name, &name_chars, 0, &type, (LPBYTE)data, &required);
+ if (ret == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (ret == ERROR_MORE_DATA || required > data_chars * 2) {
+ if (data != NULL)
+ free(data);
+ data = xmalloc(required);
+ data_chars = required / 2;
+ i--;
+ continue;
+ }
+ else if (ret != ERROR_SUCCESS)
+ break;
+
+ if (type == REG_SZ)
+ to_apply = data;
+ else if (type == REG_EXPAND_SZ) {
+ required = ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
+ if (required > data_expanded_chars) {
+ if (data_expanded)
+ free(data_expanded);
+ data_expanded = xmalloc(required * 2);
+ data_expanded_chars = required;
+ ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
+ }
+ to_apply = data_expanded;
+ }
+
+ if (_wcsicmp(name, L"PATH") == 0) {
+ if ((required = GetEnvironmentVariableW(L"PATH", NULL, 0)) != 0) {
+ /* "required" includes null term */
+ path_value = xmalloc((wcslen(to_apply) + 1 + required) * 2);
+ GetEnvironmentVariableW(L"PATH", path_value, required);
+ path_value[required - 1] = L';';
+ GOTO_CLEANUP_ON_ERR(memcpy_s(path_value + required, (wcslen(to_apply) + 1) * 2, to_apply, (wcslen(to_apply) + 1) * 2));
+ to_apply = path_value;
+ }
+
+ }
+ if (to_apply)
+ SetEnvironmentVariableW(name, to_apply);
+ }
+cleanup:
+ if (reg_key)
+ RegCloseKey(reg_key);
+ if (data)
+ free(data);
+ if (data_expanded)
+ free(data_expanded);
+ if (path_value)
+ free(path_value);
+}
+
+static int
+setup_session_env(struct ssh *ssh, Session* s)
+{
+ 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 *env_name_w = NULL, *env_value_w = NULL, *pw_dir_w = NULL, *tmp = NULL, wbuf[1024] = { 0, };
+ char *laddr, *c;
+
+ UTF8_TO_UTF16_WITH_CLEANUP(pw_dir_w, s->pw->pw_dir);
+ /* 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->is_subsystem) {
+ _snprintf(buf, ARRAYSIZE(buf), "%s@%s", s->pw->pw_name, getenv("COMPUTERNAME"));
+ UTF8_TO_UTF16_WITH_CLEANUP(tmp, buf);
+ /* escape $ characters as $$ to distinguish from special prompt characters */
+ for (int i = 0, j = 0; i < wcslen(tmp) && j < ARRAYSIZE(wbuf) - 1; i++) {
+ wbuf[j] = tmp[i];
+ if (wbuf[j++] == L'$')
+ wbuf[j++] = L'$';
+ }
+ wcscat_s(wbuf, ARRAYSIZE(wbuf), L" $P$G");
+ SetEnvironmentVariableW(L"PROMPT", wbuf);
+ }
+
+ setup_session_user_vars(pw_dir_w); /* setup user specific env variables */
+
+ env = do_setup_env_proxy(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:
+ 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;
+}
+
+int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
+ int pipein[2], pipeout[2], pipeerr[2], r, ret = -1;
+ char *exec_command = NULL, *posix_cmd_input = NULL, *shell = NULL;
+ HANDLE job = NULL, process_handle;
+ extern char* shell_command_option;
+ extern BOOLEAN arg_escape;
+
+ /* Create three pipes for stdin, stdout and stderr */
+ if (pipe(pipein) == -1 || pipe(pipeout) == -1 || pipe(pipeerr) == -1)
+ goto cleanup;
+
+ set_nonblock(pipein[0]);
+ set_nonblock(pipein[1]);
+ set_nonblock(pipeout[0]);
+ set_nonblock(pipeout[1]);
+ set_nonblock(pipeerr[0]);
+ set_nonblock(pipeerr[1]);
+
+ fcntl(pipein[1], F_SETFD, FD_CLOEXEC);
+ fcntl(pipeout[0], F_SETFD, FD_CLOEXEC);
+ fcntl(pipeerr[0], F_SETFD, FD_CLOEXEC);
+
+ /* setup Environment varibles */
+ do {
+ static int environment_set = 0;
+
+ if (environment_set)
+ break;
+
+ if (setup_session_env(ssh, s) != 0)
+ goto cleanup;
+
+ environment_set = 1;
+ } while (0);
+ int in_chroot = get_in_chroot();
+ if (!in_chroot)
+ chdir(s->pw->pw_dir);
+
+ if (s->is_subsystem >= SUBSYSTEM_INT_SFTP_ERROR) {
+ command = "echo This service allows sftp connections only.";
+ pty = 0;
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info;
+ HANDLE job_dup;
+ pid_t pid = -1;
+ char * shell_option = NULL;
+ int shell_len = 0;
+ /*account for the quotes and null*/
+ shell_len = strlen(s->pw->pw_shell) + 2 + 1;
+ if ((shell = malloc(shell_len)) == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ sprintf_s(shell, shell_len, "\"%s\"", s->pw->pw_shell);
+ debug3("shell: %s", shell);
+
+ enum sh_type { SH_OTHER, SH_CMD, SH_PS, SH_BASH, SH_CYGWIN, SH_SHELLHOST } shell_type = SH_OTHER;
+ /* get shell type */
+ if (strstr(s->pw->pw_shell, "system32\\cmd"))
+ shell_type = SH_CMD;
+ else if (strstr(s->pw->pw_shell, "powershell"))
+ shell_type = SH_PS;
+ else if (strstr(s->pw->pw_shell, "ssh-shellhost"))
+ shell_type = SH_SHELLHOST;
+ else if (strstr(s->pw->pw_shell, "\\bash"))
+ shell_type = SH_BASH;
+ else if (strstr(s->pw->pw_shell, "cygwin"))
+ shell_type = SH_CYGWIN;
+
+ if (shell_command_option)
+ shell_option = shell_command_option;
+ else if (shell_type == SH_CMD)
+ shell_option = "/c";
+ else
+ shell_option = "-c";
+ debug3("shell_option: %s", shell_option);
+
+ if (pty) {
+ fcntl(s->ptyfd, F_SETFD, FD_CLOEXEC);
+ if (exec_command_with_pty(&pid, shell, pipein[0], pipeout[1], pipeerr[1], s->col, s->row, s->ttyfd) == -1)
+ goto cleanup;
+ close(s->ttyfd);
+ s->ttyfd = -1;
+ }
+ else {
+ posix_spawn_file_actions_t actions;
+ char *spawn_argv[4] = { NULL, };
+ exec_command = build_exec_command(command);
+ debug3("exec_command: %s", exec_command);
+ if (exec_command == NULL)
+ goto cleanup;
+ if (shell_type == SH_PS || shell_type == SH_BASH ||
+ shell_type == SH_CYGWIN || (shell_type == SH_OTHER) && arg_escape) {
+ spawn_argv[0] = shell;
+ spawn_argv[1] = shell_option;
+ spawn_argv[2] = exec_command;
+ }
+ else {
+ /*
+ * no escaping needed for cmd and ssh-shellhost, or escaping is disabled
+ * in registry; pass shell, shell option, and quoted command as cmd path
+ * of posix_spawn to avoid escaping
+ */
+ int posix_cmd_input_len = strlen(shell) + 1;
+ posix_cmd_input_len += strlen(shell_option) + 1;
+ /* account for " around and null */
+ posix_cmd_input_len += strlen(exec_command) + 2 + 1;
+
+ if ((posix_cmd_input = malloc(posix_cmd_input_len)) == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ sprintf_s(posix_cmd_input, posix_cmd_input_len, "%s %s \"%s\"",
+ shell, shell_option, exec_command);
+ spawn_argv[0] = posix_cmd_input;
+ }
+ debug3("arg escape option: %s", arg_escape ? "TRUE":"FALSE");
+ debug3("spawn_argv[0]: %s", spawn_argv[0]);
+
+ if (posix_spawn_file_actions_init(&actions) != 0 ||
+ posix_spawn_file_actions_adddup2(&actions, pipein[0], STDIN_FILENO) != 0 ||
+ posix_spawn_file_actions_adddup2(&actions, pipeout[1], STDOUT_FILENO) != 0 ||
+ posix_spawn_file_actions_adddup2(&actions, pipeerr[1], STDERR_FILENO) != 0) {
+ errno = EOTHER;
+ error("posix_spawn initialization failed");
+ goto cleanup;
+ }
+ if (posix_spawn(&pid, spawn_argv[0], &actions, NULL, spawn_argv, NULL) != 0) {
+ errno = EOTHER;
+ error("posix_spawn: %s", strerror(errno));
+ goto cleanup;
+ }
+ posix_spawn_file_actions_destroy(&actions);
+ }
+
+ memset(&job_info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
+ job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+
+ if ((process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL) {
+ errno = EOTHER;
+ error("cannot get process handle: %d", GetLastError());
+ goto cleanup;
+ }
+
+ /*
+ * assign job object to control processes spawned
+ * 1. create job object
+ * 2. assign child to job object
+ * 3. duplicate job handle into child so it would be the last to close it
+ */
+ if ((job = CreateJobObjectW(NULL, NULL)) == NULL ||
+ !SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)) ||
+ !AssignProcessToJobObject(job, process_handle) ||
+ !DuplicateHandle(GetCurrentProcess(), job, process_handle, &job_dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ errno = EOTHER;
+ error("cannot associate job object: %d", GetLastError());
+ TerminateProcess(process_handle, 255);
+ CloseHandle(process_handle);
+ goto cleanup;
+ }
+ s->pid = pid;
+
+ /* Close the child sides of the socket pairs. */
+ close(pipein[0]);
+ close(pipeout[1]);
+ close(pipeerr[1]);
+
+ /*
+ * Enter the interactive session. Note: server_loop must be able to
+ * handle the case that fdin and fdout are the same.
+ */
+ if (pty) {
+ /* Set interactive/non-interactive mode */
+ packet_set_interactive(1, options.ip_qos_interactive,
+ options.ip_qos_bulk);
+ session_set_fds(ssh, s, pipein[1], pipeout[0], -1, 1, 1);
+ }
+ else {
+ /* Set interactive/non-interactive mode */
+ packet_set_interactive(s->display != NULL, options.ip_qos_interactive,
+ options.ip_qos_bulk);
+ session_set_fds(ssh, s, pipein[1], pipeout[0], pipeerr[0], s->is_subsystem, 0);
+ }
+
+ ret = 0;
+
+cleanup:
+ if (exec_command)
+ free(exec_command);
+ if (posix_cmd_input)
+ free(posix_cmd_input);
+ if (shell)
+ free(shell);
+ if (job)
+ CloseHandle(job);
+
+ return ret;
+}
+
+int
+do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) {
+ return do_exec_windows(ssh, s, command, 0);
+}
+
+int
+do_exec_pty(struct ssh *ssh, Session *s, const char *command) {
+ return do_exec_windows(ssh, s, command, 1);
+}
\ No newline at end of file
diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c
index 6ce773b42..644cff2f7 100644
--- a/contrib/win32/win32compat/w32fd.c
+++ b/contrib/win32/win32compat/w32fd.c
@@ -1034,115 +1034,28 @@ int fork()
verbose("fork is not supported");
return -1;
}
+char * build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path);
/*
* spawn a child process
* - specified by cmd with agruments argv
* - with std handles set to in, out, err
* - flags are passed to CreateProcess call
-*
-* cmd will be internally decoarated with a set of '"'
-* to account for any spaces within the commandline
-* this decoration is done only when additional arguments are passed in argv
-*
* spawned child will run as as_user if its not NULL
*/
-
static int
spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDLE err, unsigned long flags, HANDLE* as_user, BOOLEAN prepend_module_path)
{
PROCESS_INFORMATION pi;
STARTUPINFOW si;
BOOL b;
- char *cmdline, *t;
- char * const *t1;
- DWORD cmdline_len = 0;
+ char *cmdline;
wchar_t * cmdline_utf16 = NULL;
- int add_module_path = 0, ret = -1;
- char *path = NULL;
-
- if (!cmd) {
- error("%s invalid argument cmd:%s", __func__, cmd);
- return ret;
- }
-
- 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(path) + 1 + 2;
- else
- cmdline_len += (DWORD)strlen(path) + 1 + 2;
-
- if (argv) {
- t1 = argv;
- while (*t1)
- cmdline_len += (DWORD)strlen(*t1++) + 1 + 2;
- }
-
- if ((cmdline = malloc(cmdline_len)) == NULL) {
+ int ret = -1;
+ if ((cmdline = build_commandline_string(cmd, argv, prepend_module_path)) == NULL) {
errno = ENOMEM;
goto cleanup;
}
-
- /* add current module path to start if needed */
- t = cmdline;
- *t++ = '\"';
- if (add_module_path) {
- memcpy(t, __progdir, strlen(__progdir));
- t += strlen(__progdir);
- *t++ = '\\';
- }
-
- /* Add double quotes around the executable path
- * path can be c:\cygwin64\bin\sh.exe ""
- * Please note that, this logic is not just bash test specific.
- */
- const char *exe_extenstion = ".exe";
- const char *tmp = NULL;
- if ((tmp = strstr(path, exe_extenstion)) && (strlen(tmp) > strlen(exe_extenstion))) {
- tmp += strlen(exe_extenstion); /* move the pointer to the end of ".exe" */
-
- memcpy(t, path, strlen(path)-strlen(tmp));
- t += strlen(path) - strlen(tmp);
-
- *t++ = '\"';
- memcpy(t, tmp, strlen(tmp));
- t += strlen(tmp);
- } else {
- memcpy(t, path, strlen(path));
- t += strlen(path);
-
- *t++ = '\"';
- }
-
- if (argv) {
- t1 = argv;
- while (*t1) {
- *t++ = ' ';
- *t++ = '\"';
- memcpy(t, *t1, strlen(*t1));
- t += strlen(*t1);
- *t++ = '\"';
- t1++;
- }
- }
-
- *t = '\0';
-
if ((cmdline_utf16 = utf8_to_utf16(cmdline)) == NULL) {
errno = ENOMEM;
goto cleanup;
@@ -1154,13 +1067,19 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL
si.hStdOutput = out;
si.hStdError = err;
si.dwFlags = STARTF_USESTDHANDLES;
-
- debug3("spawning %ls", cmdline_utf16);
- if (as_user)
- b = CreateProcessAsUserW(as_user, NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
- else
- b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
+ wchar_t * t = cmdline_utf16;
+ do {
+ debug3("spawning %ls", t);
+ if (as_user)
+ b = CreateProcessAsUserW(as_user, NULL, t, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
+ else
+ b = CreateProcessW(NULL, t, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
+ if(b || GetLastError() != ERROR_FILE_NOT_FOUND || (argv != NULL && *argv != NULL) || cmd[0] == '\"')
+ break;
+ t++;
+ *(cmdline_utf16 + wcslen(cmdline_utf16) - 1) = L'\0';
+ } while (t == (cmdline_utf16 + 1));
if (b) {
if (register_child(pi.hProcess, pi.dwProcessId) == -1) {
@@ -1169,21 +1088,18 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL
goto cleanup;
}
CloseHandle(pi.hThread);
+ ret = pi.dwProcessId;
}
else {
errno = GetLastError();
- error("%s failed error:%d", (as_user?"CreateProcessAsUserW":"CreateProcessW"), GetLastError());
- goto cleanup;
+ error("%s failed error:%d", (as_user ? "CreateProcessAsUserW" : "CreateProcessW"), GetLastError());
}
- ret = pi.dwProcessId;
cleanup:
if (cmdline)
free(cmdline);
if (cmdline_utf16)
free(cmdline_utf16);
- if (path)
- free(path);
return ret;
}
@@ -1332,7 +1248,7 @@ posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actio
for (i = 0; i < file_actions->num_aux_fds; i++) {
aux_handles[i] = dup_handle(file_actions->aux_fds_info.parent_fd[i]);
if (aux_handles[i] == NULL)
- goto cleanup;
+ goto cleanup;
}
/* set fd info */
diff --git a/contrib/win32/win32compat/win32_pty.c b/contrib/win32/win32compat/win32_pty.c
index 7c3636798..32661b1f6 100644
--- a/contrib/win32/win32compat/win32_pty.c
+++ b/contrib/win32/win32compat/win32_pty.c
@@ -31,7 +31,9 @@
#include
#include "Debug.h"
#include "inc\fcntl.h"
+#include "inc\utf.h"
#include "misc_internal.h"
+#include "signal_internal.h"
// Return Value: 0 for success, -1 for failure
int
@@ -96,33 +98,60 @@ int is_conpty_supported()
return 0;
}
-int exec_command_with_pty(wchar_t* cmd, STARTUPINFOW* si, PROCESS_INFORMATION* pi, int ttyfd)
+int exec_command_with_pty(int * pid, char* cmd, int in, int out, int err, unsigned int col, unsigned int row, int ttyfd)
{
- HANDLE ttyh = (HANDLE)w32_fd_to_handle(ttyfd);
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
wchar_t pty_cmdline[MAX_CMD_LEN] = { 0, };
int ret = -1;
+ HANDLE ttyh = (HANDLE)w32_fd_to_handle(ttyfd);
+ wchar_t * cmd_w = NULL;
+
+ if ((cmd_w = utf8_to_utf16(cmd)) == NULL)
+ return ret;
+
+ memset(&si, 0, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.dwXCountChars = col;
+ si.dwYCountChars = row;
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE | STARTF_USECOUNTCHARS;
+
+ si.hStdInput = (HANDLE)w32_fd_to_handle(in);
+ si.hStdOutput = (HANDLE)w32_fd_to_handle(out);
+ si.hStdError = (HANDLE)w32_fd_to_handle(err);
+ si.lpDesktop = NULL;
if (is_conpty_supported())
- return CreateConPty(cmd, (short)si->dwXCountChars, (short)si->dwYCountChars, si->hStdInput, si->hStdOutput, ttyh, pi);
+ return CreateConPty(cmd_w, (short)si.dwXCountChars, (short)si.dwYCountChars, si.hStdInput, si.hStdOutput, ttyh, &pi);
/* launch via "ssh-shellhost" -p command*/
- _snwprintf_s(pty_cmdline, MAX_CMD_LEN, MAX_CMD_LEN, L"\"%ls\\ssh-shellhost.exe\" ---pty %ls", __wprogdir, cmd);
+ _snwprintf_s(pty_cmdline, MAX_CMD_LEN, MAX_CMD_LEN, L"\"%ls\\ssh-shellhost.exe\" ---pty %ls", __wprogdir, cmd_w);
/*
* In PTY mode, ssh-shellhost takes stderr as control channel
* TODO - fix this and pass control channel pipe as a command line parameter
*/
- si->hStdError = ttyh;
+ si.hStdError = ttyh;
debug3("pty commandline: %ls", pty_cmdline);
- if (!CreateProcessW(NULL, pty_cmdline, NULL, NULL, TRUE, 0, NULL, NULL, si, pi)) {
+ if (CreateProcessW(NULL, pty_cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ if (register_child(pi.hProcess, pi.dwProcessId) == -1) {
+ TerminateProcess(pi.hProcess, 0);
+ CloseHandle(pi.hProcess);
+ goto done;
+ }
+ CloseHandle(pi.hThread);
+ }
+ else {
debug("%s - failed to execute %ls, error:%d", __func__, pty_cmdline, GetLastError());
errno = EOTHER;
goto done;
}
-
+ *pid = pi.dwProcessId;
ret = 0;
done:
+ if (cmd_w)
+ free(cmd_w);
return ret;
}
\ No newline at end of file
diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c
index 73364132b..e81e5db42 100644
--- a/contrib/win32/win32compat/wmain_sshd.c
+++ b/contrib/win32/win32compat/wmain_sshd.c
@@ -224,7 +224,9 @@ int argc_original = 0;
wchar_t **wargv_original = NULL;
int wmain(int argc, wchar_t **wargv) {
- wchar_t* path_utf16;
+ wchar_t *path_value = NULL, *path_new_value;
+ errno_t result = 0;
+ size_t path_new_len = 0, len;
argc_original = argc;
wargv_original = wargv;
@@ -232,6 +234,38 @@ int wmain(int argc, wchar_t **wargv) {
/* change current directory to sshd.exe root */
_wchdir(__wprogdir);
+ /*
+ * we want to launch scp and sftp executables from the binary directory
+ * that sshd is hosted in. This will facilitate hosting and evaluating
+ * multiple versions of OpenSSH at the same time.
+ * it does not work well for powershell, cygwin, etc if program path is
+ * prepended to executable directory.
+ * To achive above, PATH is set to process environment
+ */
+ _wdupenv_s(&path_value, &len, L"PATH");
+ if (!path_value || (wcsstr(path_value, __wprogdir)) == NULL) {
+ path_new_len = wcslen(__wprogdir) + wcslen(path_value) + 2;
+ if ((path_new_value = (wchar_t *) malloc(path_new_len * sizeof(wchar_t))) == NULL) {
+ errno = ENOMEM;
+ error("failed to allocation memory");
+ return -1;
+ }
+ swprintf_s(path_new_value, path_new_len, L"%s%s%s", __wprogdir, path_value ? L";" : L"", path_value);
+ if (result = _wputenv_s(L"PATH", path_new_value)) {
+ error("failed to set PATH environment variable: to value:%s, error:%d", path_new_value, result);
+ errno = result;
+ if (path_new_value)
+ free(path_new_value);
+ if(path_value)
+ free(path_value);
+ return -1;
+ }
+ if (path_new_value)
+ free(path_new_value);
+ if(path_value)
+ free(path_value);
+ }
+
if (!StartServiceCtrlDispatcherW(dispatch_table)) {
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
return sshd_main(argc, wargv); /* sshd running NOT as service*/
diff --git a/pal_doexec.h b/pal_doexec.h
new file mode 100644
index 000000000..b4a798b8a
--- /dev/null
+++ b/pal_doexec.h
@@ -0,0 +1,8 @@
+#ifndef _PAL_DOEXEC_H
+#define _PAL_DOEXEC_H
+
+#include "session.h"
+
+int do_exec_pty(struct ssh *, Session *, const char *);
+int do_exec_no_pty(struct ssh *, Session *, const char *);
+#endif /* _PAL_DOEXEC_H */
\ No newline at end of file
diff --git a/regress/pesterTests/SCP.Tests.ps1 b/regress/pesterTests/SCP.Tests.ps1
index c2973a39b..928f2a106 100644
--- a/regress/pesterTests/SCP.Tests.ps1
+++ b/regress/pesterTests/SCP.Tests.ps1
@@ -51,7 +51,7 @@ Describe "Tests for scp command" -Tags "CI" {
Title = 'Simple copy local file to remote file'
Source = $SourceFilePath
Destination = "test_target:$DestinationFilePath"
- Options = "-S '$sshcmd'"
+ Options = "-S `"$sshcmd`""
},
@{
Title = 'Simple copy remote file to local file'
@@ -181,7 +181,7 @@ Describe "Tests for scp command" -Tags "CI" {
$equal = @(Compare-Object (Get-ChildItem -path $SourceFilePath) (Get-ChildItem -path $DestinationFilePath) -Property Name, Length ).Length -eq 0
$equal | Should Be $true
- if($Options.contains("-p"))
+ if($Options.contains("-p "))
{
$equal = @(Compare-Object (Get-ChildItem -path $SourceFilePath).LastWriteTime.DateTime (Get-ChildItem -path $DestinationFilePath).LastWriteTime.DateTime ).Length -eq 0
$equal | Should Be $true
@@ -198,7 +198,7 @@ Describe "Tests for scp command" -Tags "CI" {
$equal = @(Compare-Object (Get-Item -path $SourceDir ) (Get-Item -path (join-path $DestinationDir $SourceDirName) ) -Property Name, Length).Length -eq 0
$equal | Should Be $true
- if($Options.contains("-p"))
+ if($Options.contains("-p "))
{
$equal = @(Compare-Object (Get-Item -path $SourceDir).LastWriteTime.DateTime (Get-Item -path (join-path $DestinationDir $SourceDirName)).LastWriteTime.DateTime).Length -eq 0
$equal | Should Be $true
@@ -207,7 +207,7 @@ Describe "Tests for scp command" -Tags "CI" {
$equal = @(Compare-Object (Get-ChildItem -Recurse -path $SourceDir) (Get-ChildItem -Recurse -path (join-path $DestinationDir $SourceDirName) ) -Property Name, Length).Length -eq 0
$equal | Should Be $true
- if($Options.contains("-p") -and ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -gt 2))
+ if($Options.contains("-p ") -and ($platform -eq [PlatformType]::Windows) -and ($PSVersionTable.PSVersion.Major -gt 2))
{
$equal = @(Compare-Object (Get-ChildItem -Recurse -path $SourceDir).LastWriteTime.DateTime (Get-ChildItem -Recurse -path (join-path $DestinationDir $SourceDirName) ).LastWriteTime.DateTime).Length -eq 0
$equal | Should Be $true
diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1
index 5fc7d1b99..4eb113b01 100644
--- a/regress/pesterTests/SSH.Tests.ps1
+++ b/regress/pesterTests/SSH.Tests.ps1
@@ -76,6 +76,8 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$dfltShellRegPath = "HKLM:\Software\OpenSSH"
$dfltShellRegKeyName = "DefaultShell"
$dfltShellCmdOptionRegKeyName = "DefaultShellCommandOption"
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
function ConfigureDefaultShell {
param
@@ -102,7 +104,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
AfterEach {$tI++;}
- Context "$tC - Basic Scenarios" {
+ Context "$tC - Basic Scenarios" {
BeforeAll {$tI=1}
AfterAll{$tC++}
@@ -190,47 +192,107 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
(dir $testdst2).Length | Should Be (dir $testsrc).Length
}
- }
+ }
- Context "$tC - configure default shell Scenarios" {
- BeforeAll {$tI=1}
- AfterAll{$tC++}
- AfterEach {
- Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
- Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
- }
-
- It "$tC.$tI - default shell as powershell" -skip:$skip {
+ Context "$tC - configure powershell default shell Scenarios" {
+ BeforeAll {
+ $tI=1
$shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path
if($shell_path -ne $null) {
- ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
-
- $o = ssh test_target Write-Output 1234
- $o | Should Be "1234"
+ ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "-c"
}
}
+ AfterAll{
+ $tC++
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
+ }
- It "$tC.$tI - default shell as cmd" -skip:$skip {
+ It "$tC.$tI - basic powershell" -skip:$skip {
+ $o = ssh test_target Write-Output 1234
+ $o | Should Be "1234"
+ }
+
+ It "$tC.$tI - basic in powershell cmdlet" -skip:$skip {
+ $o = ssh test_target "cd `$env:ProgramFiles;pwd"
+ $LASTEXITCODE | Should Be 0
+ #$o | Should Match "c:\Program Files"
+ }
+ It "$tC.$tI - powershell as default shell and double quotes in cmdline" {
+ # actual command line ssh target echo `"hello`"
+ $o = ssh test_target echo ``\`"hello``\`"
+ $o | Should Be "`"hello`""
+ }
+ It "$tC.$tI - multiple commands with double quotes in powershell cmdlet" -skip:$skip {
+ # actual command line ssh target cd "$env:programfiles";pwd
+ $o = ssh test_target "cd \`"`$env:programfiles\`";pwd"
+ $LASTEXITCODE | Should Be 0
+ $match = $o -match "Program Files"
+ $match.count | Should be 1
+ }
+ It "$tC.$tI - multiple commands with double quotes in powershell cmdlet" -skip:$skip {
+ # actual command line ssh target dir "$env:programfiles";cd "$env:programfiles";pwd
+ $o = ssh test_target "dir \`"`$env:programfiles\`";cd \`"`$env:programfiles\`";pwd"
+ $LASTEXITCODE | Should Be 0
+ #$o -contains "Program Files" | Should Be $True
+ $match = $o -match "Program Files"
+ $match.count | Should Be 3
+ }
+ It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
+ # actual command line ssh target echo '$env:computername'
+ $o = ssh test_target "echo '`$env:computername'"
+ $LASTEXITCODE | Should Be 0
+ $o | Should Be `$env:computername
+ }
+ }
+ Context "$tC - configure cmd as default shell" {
+ BeforeAll {
+ $tI=1
$shell_path = (Get-Command cmd.exe -ErrorAction SilentlyContinue).path
if($shell_path -ne $null) {
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
-
- $o = ssh test_target where cmd
- $o | Should Contain "cmd"
- }
}
-
- It "$tC.$tI - shellhost as default shell and multiple double quotes in cmdline" {
- # actual command line ssh target \"cmd\" /c \"echo hello\"
+ }
+ AfterAll{
+ $tC++
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
+ }
+ It "$tC.$tI - default shell as cmd" -skip:$skip {
+ $o = ssh test_target where cmd
+ $o | Should Contain "cmd"
+ }
+ It "$tC.$tI - cmd as default shell and double quotes in cmdline" {
+ # actual command line ssh target echo "\"hello\""
+ $o = ssh test_target 'echo "\"hello\""'
+ $o | Should Be "`"hello`""
+ }
+ It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
+ # actual command line ssh target echo '$env:computername'
+ $o = ssh test_target "echo 'hello'"
+ $LASTEXITCODE | Should Be 0
+ $o | Should Be "'hello'"
+ }
+ }
+ Context "$tC - configure ssh-shellhost as default shell" {
+ BeforeAll {
+ $tI=1
$shell_path = (Get-Command ssh-shellhost -ErrorAction SilentlyContinue).path
ConfigureDefaultShell -default_shell_path $shell_path
- $o = ssh test_target `\`"cmd`\`" /c `\`"echo hello`\`"
- $o | Should Be "hello"
+ }
+ AfterAll{
+ $tC++
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
+ }
+ It "$tC.$tI - shellhost as default shell and multiple double quotes in cmdline" {
+ # actual command line ssh target \"cmd\" /c \"echo \"hello\"\"
+ $o = ssh test_target `\`"cmd`\`" /c `\`"echo \`"hello\`"`\`"
+ $o | Should Be "`"hello`""
}
}
- Context "$tC - cmdline parameters" {
-
+ Context "$tC - cmdline parameters" {
BeforeAll {$tI=1}
AfterAll{$tC++}
@@ -298,16 +360,17 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
}
It "ProxyCommand with file name only" {
- & cmd /c "ssh -o ProxyCommand=`"cmd.exe /c echo Invalid proxy 1>&2`" abc 2>$stderrFile"
- $stderrFile | Should Contain "Invalid proxy"
+ & cmd /c "ssh -o ProxyCommand=`"cmd.exe /c echo test string for invalid proxy 1>&2`" abc 2>$stderrFile"
+ $stderrFile | Should Contain "test string for invalid proxy"
+ write-host (Get-Content $stderrFile)
$stderrFile | Should Contain "Connection closed by remote host"
}
It "ProxyCommand with absolute path to the file" {
- & cmd /c "ssh -o ProxyCommand=`"$($env:ComSpec) /c echo Invalid proxy 1>&2`" abc 2>$stderrFile"
- $stderrFile | Should Contain "Invalid proxy"
+ & cmd /c "ssh -o ProxyCommand=`"$($env:ComSpec) /c echo test string for invalid proxy 1>&2`" abc 2>$stderrFile"
+ $stderrFile | Should Contain "test string for invalid proxy"
+ write-host (Get-Content $stderrFile)
$stderrFile | Should Contain "Connection closed by remote host"
}
- }
-
+ }
}
diff --git a/regress/unittests/win32compat/miscellaneous_tests.c b/regress/unittests/win32compat/miscellaneous_tests.c
index fbebca88d..00763b8d9 100644
--- a/regress/unittests/win32compat/miscellaneous_tests.c
+++ b/regress/unittests/win32compat/miscellaneous_tests.c
@@ -318,98 +318,99 @@ test_chroot()
}
void
-test_build_session_commandline()
+test_build_exec_command()
{
- char *progdir = __progdir, *out, buf[PATH_MAX * 2];
+ char *out;
- TEST_START("default interactive session tests");
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, NULL);
- ASSERT_STRING_EQ(out, "\"c:\\system32\\cmd.exe\"");
+ TEST_START("arg is null");
+ out = build_exec_command(NULL);
+ ASSERT_PTR_EQ(out, NULL);
TEST_DONE();
- TEST_START("cmd shell tests");
- buf[0] = '\0';
- strcat(buf, "\"c:\\system32\\cmd.exe\" /c \"\"");
- strcat(buf, progdir);
- int len_pg = strlen(buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "internal-sftp -arg");
- buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg\"");
- ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "SFTP-server.exe -arg");
- buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg\"");
- ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sftp-SERVER -arg");
- buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg\"");
- ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sCp -arg");
- buf[len_pg] = '\0';
- strcat(buf, "\\scp.exe\" -arg\"");
- ASSERT_STRING_EQ(out, buf);
+ TEST_START("scp tests");
+ out = build_exec_command("sCp -arg");
+ ASSERT_STRING_EQ(out, "scp.exe -arg");
free(out);
- TEST_DONE();
-
- TEST_START("bash shell tests");
- out = build_session_commandline("c:\\system32\\bash.exe", NULL, "internal-sftp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\system32\\bash.exe\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\system32\\bash", NULL, "internal-sftp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\system32\\bash\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\system32\\bash", NULL, "sFTP-server -arg");
- ASSERT_STRING_EQ(out, "\"c:\\system32\\bash\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\system32\\bash", NULL, "scP -arg");
- ASSERT_STRING_EQ(out, "\"c:\\system32\\bash\" -c \"scp.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\cygwin\\bash.exe", NULL, "internal-sftp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\cygwin\\bash.exe\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\cygwin\\bash", NULL, "sftp-server -arg");
- ASSERT_STRING_EQ(out, "\"c:\\cygwin\\bash\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\cygwin\\bash", NULL, "sftp-seRVer.exe -arg");
- ASSERT_STRING_EQ(out, "\"c:\\cygwin\\bash\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\cygwin\\bash", NULL, "sCp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\cygwin\\bash\" -c \"scp.exe -arg\"");
+ out = build_exec_command("sCp.exe -arg1 -arg2");
+ ASSERT_STRING_EQ(out, "scp.exe -arg1 -arg2");
free(out);
TEST_DONE();
- TEST_START("powershell shell tests");
- out = build_session_commandline("c:\\powershell.exe", NULL, "internal-sftp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c \"sftp-server.exe -arg\"");
+ TEST_START("sftp tests");
+ out = build_exec_command("internal-sftp \"arg1 arg2\"");
+ ASSERT_STRING_EQ(out, "sftp-server.exe \"arg1 arg2\"");
free(out);
- out = build_session_commandline("c:\\powershell", NULL, "sftp-server -arg");
- ASSERT_STRING_EQ(out, "\"c:\\powershell\" -c \"sftp-server.exe -arg\"");
+ out = build_exec_command("SFTP-server.exe -arg");
+ ASSERT_STRING_EQ(out, "sftp-server.exe -arg");
free(out);
- out = build_session_commandline("c:\\powershell.exe", NULL, "sftp-sERver.exe -arg");
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\powershell.exe", NULL, "scP -arg");
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c \"scp.exe -arg\"");
- free(out);
- TEST_DONE();
-
-
- TEST_START("other shell tests");
- out = build_session_commandline("c:\\myshell.exe", NULL, "internal-sftp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\myshell.exe\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\myshell", NULL, "sftp-server -arg");
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\myshell", NULL, "sftp-seRVer.exe -arg");
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c \"sftp-server.exe -arg\"");
- free(out);
- out = build_session_commandline("c:\\myshell", NULL, "sCp -arg");
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c \"scp.exe -arg\"");
+ out = build_exec_command("sftp-SERVER -arg");
+ ASSERT_STRING_EQ(out, "sftp-server.exe -arg");
free(out);
TEST_DONE();
}
+void
+test_build_commandline_string()
+{
+ char *out, in[PATH_MAX], buf[PATH_MAX];
+
+ TEST_START("cmd is null");
+ out = build_commandline_string(NULL, NULL, TRUE);
+ ASSERT_PTR_EQ(out, NULL);
+ TEST_DONE();
+
+ TEST_START("arg is null");
+ out = build_commandline_string("\"c:\\windows\\system32\\cmd.exe\" /c arg", NULL, FALSE);
+ ASSERT_STRING_EQ(out, "\"c:\\windows\\system32\\cmd.exe\" /c arg");
+ free(out);
+ out = build_commandline_string("cmd.exe /c ping.exe", NULL, FALSE);
+ ASSERT_STRING_EQ(out, "\"cmd.exe /c ping.exe\"");
+ sprintf_s(in, PATH_MAX, "\"%s\\%s\"", __progdir, "ssh-shellhost.exe\" -c \"arg1 arg2\"");
+ out = build_commandline_string(in, NULL, TRUE);
+ ASSERT_STRING_EQ(out, in);
+ out = build_commandline_string("\"ssh-shellhost.exe\" -c \"arg1 arg2\"", NULL, TRUE);
+ sprintf_s(buf, PATH_MAX, "\"%s\\%s", __progdir, "ssh-shellhost.exe\" -c \"arg1 arg2\"");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ out = build_commandline_string("\"cmd.exe\" /c \"arg1 arg2\"", NULL, FALSE);
+ ASSERT_STRING_EQ(out, "\"cmd.exe\" /c \"arg1 arg2\"");
+ free(out);
+ TEST_DONE();
+
+ char *argv[4] = { NULL, };
+ argv[0] = "\"C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\"";
+ argv[1] = "-c";
+ argv[2] = "arg1 arg2";
+ TEST_START("arg is not null");
+ out = build_commandline_string(argv[0], argv + 1, TRUE);
+ sprintf_s(buf, PATH_MAX, "%s %s %s", argv[0], argv[1], "\"arg1 arg2\"");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ argv[0] = "C:\\my folder\\bash.exe";
+ argv[2] = "\"arg1\\arg2\"";
+ out = build_commandline_string(argv[0], argv + 1, TRUE);
+ sprintf_s(buf, PATH_MAX, "\"%s\" %s %s", argv[0], argv[1], "\\\"arg1\\arg2\\\"");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ argv[2] = "\"arg1 arg2\\\"";
+ out = build_commandline_string(argv[0], argv + 1, TRUE);
+ sprintf_s(buf, PATH_MAX, "\"%s\" %s %s", argv[0], argv[1], "\"\\\"arg1 arg2\\\\\\\"\"");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ argv[0] = "\"c:\\cygwin64\\bin\\ba.exe\"";
+ argv[2] = "arg1\\arg2";
+ out = build_commandline_string(argv[0], argv + 1, TRUE);
+ sprintf_s(buf, PATH_MAX, "%s %s %s", argv[0], argv[1], "arg1\\arg2");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ argv[0] = "\"c:\\cygwin64\\bin\\ba.exe\"";
+ argv[2] = "'arg1 \\arg2\\\"'";
+ out = build_commandline_string(argv[0], argv + 1, TRUE);
+ sprintf_s(buf, PATH_MAX, "%s %s %s", argv[0], argv[1], "'arg1 \\arg2\\\\\\\"'");
+ ASSERT_STRING_EQ(out, buf);
+ free(out);
+ TEST_DONE();
+}
void
miscellaneous_tests()
@@ -421,5 +422,6 @@ miscellaneous_tests()
test_realpath();
test_statvfs();
test_chroot();
- test_build_session_commandline();
+ test_build_exec_command();
+ test_build_commandline_string();
}
diff --git a/session.c b/session.c
index f9355c08d..2da4f0aa6 100644
--- a/session.c
+++ b/session.c
@@ -96,6 +96,7 @@
#include "monitor_wrap.h"
#include "sftp.h"
#include "atomicio.h"
+#include "pal_doexec.h"
#if defined(KRB5) && defined(USE_AFS)
#include
@@ -118,8 +119,10 @@ void session_set_fds(struct ssh *, Session *, int, int, int, int, int);
void session_pty_cleanup(Session *);
void session_proctitle(Session *);
int session_setup_x11fwd(struct ssh *, Session *);
+#ifndef WINDOWS /* !WINDOWS */
int do_exec_pty(struct ssh *, Session *, const char *);
int do_exec_no_pty(struct ssh *, Session *, const char *);
+#endif
int do_exec(struct ssh *, Session *, const char *);
void do_login(struct ssh *, Session *, const char *);
void do_child(struct ssh *, Session *, const char *);
@@ -385,351 +388,7 @@ xauth_valid_string(const char *s)
#define USE_PIPES 1
-#ifdef WINDOWS
-/*
- * do_exec* on Windows
- * - Read and set user environment variables from registry
- * - Build subsystem cmdline path
- * - 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); \
- if ((o = utf8_to_utf16(i)) == NULL) \
- goto cleanup; \
-} while (0)
-
-#define GOTO_CLEANUP_ON_ERR(exp) do { \
- if ((exp) != 0) \
- goto cleanup; \
-} while(0)
-
-/* 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)
-{
- /* retrieve and set env variables. */
- HKEY reg_key = 0;
- wchar_t name[256];
- wchar_t path[PATH_MAX + 1] = { 0, };
- wchar_t *data = NULL, *data_expanded = NULL, *path_value = NULL, *to_apply;
- DWORD type, name_chars = 256, data_chars = 0, data_expanded_chars = 0, required, i = 0;
- 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);
- return;
- else while (1) {
- to_apply = NULL;
- required = data_chars * sizeof(wchar_t);
- name_chars = 256;
- ret = RegEnumValueW(reg_key, i++, name, &name_chars, 0, &type, (LPBYTE)data, &required);
- if (ret == ERROR_NO_MORE_ITEMS)
- break;
- else if (ret == ERROR_MORE_DATA || required > data_chars * 2) {
- if (data != NULL)
- free(data);
- data = xmalloc(required);
- data_chars = required / 2;
- i--;
- continue;
- }
- else if (ret != ERROR_SUCCESS)
- break;
-
- if (type == REG_SZ)
- to_apply = data;
- else if (type == REG_EXPAND_SZ) {
- required = ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
- if (required > data_expanded_chars) {
- if (data_expanded)
- free(data_expanded);
- data_expanded = xmalloc(required * 2);
- data_expanded_chars = required;
- ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
- }
- to_apply = data_expanded;
- }
-
- if (_wcsicmp(name, L"PATH") == 0) {
- if ((required = GetEnvironmentVariableW(L"PATH", NULL, 0)) != 0) {
- /* "required" includes null term */
- path_value = xmalloc((wcslen(to_apply) + 1 + required) * 2);
- GetEnvironmentVariableW(L"PATH", path_value, required);
- path_value[required - 1] = L';';
- GOTO_CLEANUP_ON_ERR(memcpy_s(path_value + required, (wcslen(to_apply) + 1) * 2, to_apply, (wcslen(to_apply) + 1) * 2));
- to_apply = path_value;
- }
-
- }
- if (to_apply)
- SetEnvironmentVariableW(name, to_apply);
- }
-cleanup:
- if (reg_key)
- RegCloseKey(reg_key);
- if (data)
- free(data);
- if (data_expanded)
- free(data_expanded);
- if (path_value)
- free(path_value);
-}
-
-static int
-setup_session_env(struct ssh *ssh, Session* s)
-{
- 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 *env_name_w = NULL, *env_value_w = NULL, *pw_dir_w = NULL, *tmp = NULL, wbuf[1024] = { 0, };
- char *laddr, *c;
-
- UTF8_TO_UTF16_WITH_CLEANUP(pw_dir_w, s->pw->pw_dir);
- /* 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->is_subsystem) {
- _snprintf(buf, ARRAYSIZE(buf), "%s@%s", s->pw->pw_name, getenv("COMPUTERNAME"));
- UTF8_TO_UTF16_WITH_CLEANUP(tmp, buf);
- /* escape $ characters as $$ to distinguish from special prompt characters */
- for (int i = 0, j = 0; i < wcslen(tmp) && j < ARRAYSIZE(wbuf) - 1; i++) {
- wbuf[j] = tmp[i];
- if (wbuf[j++] == L'$')
- wbuf[j++] = L'$';
- }
- wcscat_s(wbuf, ARRAYSIZE(wbuf), L" $P$G");
- SetEnvironmentVariableW(L"PROMPT", wbuf);
- }
-
- 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 :
- 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;
-}
-
-int register_child(void* child, unsigned long pid);
-char* build_session_commandline(const char *, const char *, const char *);
-
-int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
- int pipein[2], pipeout[2], pipeerr[2], r, ret = -1;
- wchar_t *exec_command_w = NULL;
- char *exec_command = NULL;
- HANDLE job = NULL;
- extern char* shell_command_option;
-
- /* Create three pipes for stdin, stdout and stderr */
- if (pipe(pipein) == -1 || pipe(pipeout) == -1 || pipe(pipeerr) == -1)
- goto cleanup;
-
- set_nonblock(pipein[0]);
- set_nonblock(pipein[1]);
- set_nonblock(pipeout[0]);
- set_nonblock(pipeout[1]);
- set_nonblock(pipeerr[0]);
- set_nonblock(pipeerr[1]);
-
- fcntl(pipein[1], F_SETFD, FD_CLOEXEC);
- fcntl(pipeout[0], F_SETFD, FD_CLOEXEC);
- fcntl(pipeerr[0], F_SETFD, FD_CLOEXEC);
-
- /* setup Environment varibles */
- do {
- static int environment_set = 0;
-
- if (environment_set)
- break;
-
- if (setup_session_env(ssh, s) != 0)
- goto cleanup;
-
- environment_set = 1;
- } while (0);
-
- if (!in_chroot)
- chdir(s->pw->pw_dir);
-
- if (s->is_subsystem >= SUBSYSTEM_INT_SFTP_ERROR) {
- command = "echo This service allows sftp connections only.";
- pty = 0;
- }
-
- exec_command = build_session_commandline(s->pw->pw_shell, shell_command_option, command);
- if (exec_command == NULL)
- goto cleanup;
-
- /* start the process */
- {
- PROCESS_INFORMATION pi;
- STARTUPINFOW si;
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info;
- HANDLE job_dup;
-
- memset(&si, 0, sizeof(STARTUPINFO));
- si.cb = sizeof(STARTUPINFO);
- si.dwXSize = 5;
- si.dwYSize = 5;
- si.dwXCountChars = s->col;
- si.dwYCountChars = s->row;
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE | STARTF_USECOUNTCHARS;
-
- si.hStdInput = (HANDLE)w32_fd_to_handle(pipein[0]);
- si.hStdOutput = (HANDLE)w32_fd_to_handle(pipeout[1]);
- si.hStdError = (HANDLE)w32_fd_to_handle(pipeerr[1]);
- si.lpDesktop = NULL;
-
- if ((exec_command_w = utf8_to_utf16(exec_command)) == NULL)
- goto cleanup;
-
- debug("Executing command: %s with%spty", exec_command, pty? " ":" no ");
-
- if (pty) {
- fcntl(s->ptyfd, F_SETFD, FD_CLOEXEC);
- if (exec_command_with_pty(exec_command_w, &si, &pi, s->ttyfd) == -1)
- goto cleanup;
- close(s->ttyfd);
- s->ttyfd = -1;
- } else if (!CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
- errno = EOTHER;
- error("ERROR. Cannot create process (%u).\n", GetLastError());
- goto cleanup;
- }
-
- CloseHandle(pi.hThread);
- memset(&job_info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
- job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK;
-
- /*
- * assign job object to control processes spawned by shell
- * 1. create job object
- * 2. assign child to job object
- * 3. duplicate job handle into child so it would be the last to close it
- */
- if ((job = CreateJobObjectW(NULL, NULL)) == NULL ||
- !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());
- errno = EOTHER;
- TerminateProcess(pi.hProcess, 255);
- CloseHandle(pi.hProcess);
- goto cleanup;
- }
-
- s->pid = pi.dwProcessId;
- register_child(pi.hProcess, pi.dwProcessId);
- }
-
- /* Close the child sides of the socket pairs. */
- close(pipein[0]);
- close(pipeout[1]);
- close(pipeerr[1]);
-
- /*
- * Enter the interactive session. Note: server_loop must be able to
- * handle the case that fdin and fdout are the same.
- */
- if (pty) {
- /* Set interactive/non-interactive mode */
- packet_set_interactive(1, options.ip_qos_interactive,
- options.ip_qos_bulk);
- session_set_fds(ssh, s, pipein[1], pipeout[0], -1, 1, 1);
- } else {
- /* Set interactive/non-interactive mode */
- packet_set_interactive(s->display != NULL, options.ip_qos_interactive,
- options.ip_qos_bulk);
- session_set_fds(ssh, s, pipein[1], pipeout[0], pipeerr[0], s->is_subsystem, 0);
- }
-
- ret = 0;
-
-cleanup:
- if (!exec_command)
- free(exec_command);
- if (!exec_command_w)
- free(exec_command_w);
- if (job)
- CloseHandle(job);
-
- return ret;
-}
-
-int
-do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) {
- return do_exec_windows(ssh, s, command, 0);
-}
-
-int
-do_exec_pty(struct ssh *ssh, Session *s, const char *command) {
- return do_exec_windows(ssh, s, command, 1);
-}
-
-#else /* !WINDOWS */
+#ifndef WINDOWS /* !WINDOWS */
/*
* This is called to fork and execute a command when we have no tty. This
* will call do_child from the child, and server_loop from the parent after
@@ -3081,4 +2740,23 @@ session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns)
remote = ssh_remote_ipaddr(ssh);
return remote;
}
+/*
+* Since in_chroot is static for now, create this function
+* to have unix code intact
+*/
+#ifdef WINDOWS
+int get_in_chroot()
+{
+ return in_chroot;
+}
+/*
+ * Since do_setup_env is static for now, create this function
+ * to have unix code intact
+*/
+char **
+do_setup_env_proxy(struct ssh *ssh, Session *s, const char *shell)
+{
+ return do_setup_env(ssh, s, shell);
+}
+#endif
\ No newline at end of file