mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-09-26 11:29:04 +02:00
Fix of 1211 and 1082 (#349)
PowerShell/Win32-OpenSSH#1211 PowerShell/Win32-OpenSSH#1082 Added support for posix_spawnp that executes the command directly instead of appending path. (SH_ASKPASS and proxy command use this). Refactored posix spawn commandline building logic to automatically account for Windows CRT escaping rules on all arguments.
This commit is contained in:
parent
cc16f80123
commit
a75116b6f8
@ -463,6 +463,7 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c" />
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\w32-doexec.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="version.rc" />
|
<ResourceCompile Include="version.rc" />
|
||||||
|
@ -150,6 +150,12 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c">
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_groupaccess.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\w32-doexec.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="version.rc">
|
<ResourceCompile Include="version.rc">
|
||||||
|
@ -910,7 +910,7 @@ realpath(const char *inputpath, char resolved[PATH_MAX])
|
|||||||
|
|
||||||
if (is_win_path) {
|
if (is_win_path) {
|
||||||
if (_strnicmp(inputpath, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0) {
|
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)]);
|
strcat_s(path, PATH_MAX, &inputpath[strlen(PROGRAM_DATA)]);
|
||||||
} else {
|
} else {
|
||||||
memcpy_s(path, PATH_MAX, inputpath, strlen(inputpath));
|
memcpy_s(path, PATH_MAX, inputpath, strlen(inputpath));
|
||||||
@ -1318,7 +1318,7 @@ int
|
|||||||
is_absolute_path(const char *path)
|
is_absolute_path(const char *path)
|
||||||
{
|
{
|
||||||
int retVal = 0;
|
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++;
|
path++;
|
||||||
|
|
||||||
if (*path == '/' || *path == '\\' || (*path != '\0' && isalpha(*path) && path[1] == ':') ||
|
if (*path == '/' || *path == '\\' || (*path != '\0' && isalpha(*path) && path[1] == ':') ||
|
||||||
@ -1583,75 +1583,25 @@ cleanup:
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/* Interpret scp and sftp executables*/
|
||||||
/* builds session commandline. returns NULL with errno set on failure, caller should free returned string */
|
|
||||||
char *
|
char *
|
||||||
build_session_commandline(const char *shell, const char* shell_arg, const char *command)
|
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;
|
enum cmd_type { CMD_OTHER, CMD_SFTP, CMD_SCP } command_type = CMD_OTHER;
|
||||||
char *progdir = __progdir, *cmd_sp = NULL, *cmdline = NULL, *ret = NULL, *p;
|
char *cmd_sp = NULL;
|
||||||
int len, progdir_len = (int)strlen(progdir);
|
int len = 0, command_len;
|
||||||
|
|
||||||
#define CMDLINE_APPEND(P, S) \
|
|
||||||
do { \
|
|
||||||
int _S_len = (int)strlen(S); \
|
|
||||||
memcpy((P), (S), _S_len); \
|
|
||||||
(P) += _S_len; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
const char *command_args = NULL;
|
||||||
|
|
||||||
if (!command)
|
if (!command)
|
||||||
break;
|
return NULL;
|
||||||
|
|
||||||
command_len = (int)strlen(command);
|
command_len = (int)strlen(command);
|
||||||
/*TODO - replace numbers below with readable compile time operators*/
|
/*TODO - replace numbers below with readable compile time operators*/
|
||||||
if (command_len >= 13 && _memicmp(command, "internal-sftp", 13) == 0) {
|
if (command_len >= 13 && _memicmp(command, "internal-sftp", 13) == 0) {
|
||||||
command_type = CMD_SFTP;
|
command_type = CMD_SFTP;
|
||||||
command_args = command + 13;
|
command_args = command + 13;
|
||||||
} else if (command_len >= 11 && _memicmp(command, "sftp-server", 11) == 0) {
|
}
|
||||||
|
else if (command_len >= 11 && _memicmp(command, "sftp-server", 11) == 0) {
|
||||||
command_type = CMD_SFTP;
|
command_type = CMD_SFTP;
|
||||||
|
|
||||||
/* account for possible .exe extension */
|
/* account for possible .exe extension */
|
||||||
@ -1659,7 +1609,8 @@ do { \
|
|||||||
command_args = command + 15;
|
command_args = command + 15;
|
||||||
else
|
else
|
||||||
command_args = command + 11;
|
command_args = command + 11;
|
||||||
} else if (command_len >= 3 && _memicmp(command, "scp", 3) == 0) {
|
}
|
||||||
|
else if (command_len >= 3 && _memicmp(command, "scp", 3) == 0) {
|
||||||
command_type = CMD_SCP;
|
command_type = CMD_SCP;
|
||||||
|
|
||||||
/* account for possible .exe extension */
|
/* account for possible .exe extension */
|
||||||
@ -1669,115 +1620,185 @@ do { \
|
|||||||
command_args = command + 3;
|
command_args = command + 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command_type == CMD_OTHER)
|
len = command_len + 5; /* account for possible .exe addition and null term */
|
||||||
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) {
|
if ((cmd_sp = malloc(len)) == NULL) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
goto done;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
else
|
||||||
CMDLINE_APPEND(p, "\\sftp-server.exe\"");
|
strcpy_s(cmd_sp, len, command);
|
||||||
} else {
|
return cmd_sp;
|
||||||
if (command_type == CMD_SCP)
|
|
||||||
CMDLINE_APPEND(p, "scp.exe");
|
|
||||||
else
|
|
||||||
CMDLINE_APPEND(p, "sftp-server.exe");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shell_type == SH_CYGWIN) {
|
/*
|
||||||
*p = '\0';
|
* cmd is internally decoarated with a set of '"'
|
||||||
convertToForwardslash(cmd_sp);
|
* 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
|
||||||
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 ");
|
|
||||||
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++) {
|
char *
|
||||||
if (command[i] == '\\') {
|
build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path)
|
||||||
CMDLINE_APPEND(p, "\\");
|
{
|
||||||
CMDLINE_APPEND(p, "\\"); // For every backslash add another backslash.
|
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;
|
||||||
}
|
}
|
||||||
else if (command[i] == '\"') {
|
|
||||||
CMDLINE_APPEND(p, "\\"); // Add backslash for every double quote.
|
if (!(path = _strdup(cmd))) {
|
||||||
CMDLINE_APPEND(p, "\"");
|
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 {
|
else {
|
||||||
*p++ = command[i];
|
/*path already contains "*/
|
||||||
}
|
memcpy(t, path + 1, path_len - 1);
|
||||||
}
|
t += path_len - 1;
|
||||||
} else {
|
|
||||||
CMDLINE_APPEND(p, command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CMDLINE_APPEND(p, "\"");
|
*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;
|
||||||
}
|
}
|
||||||
*p = '\0';
|
}
|
||||||
|
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;
|
ret = cmdline;
|
||||||
cmdline = NULL;
|
cmdline = NULL;
|
||||||
done:
|
cleanup:
|
||||||
if (cmd_sp)
|
if (path)
|
||||||
free(cmd_sp);
|
free(path);
|
||||||
if (cmdline)
|
if (cmdline)
|
||||||
free(cmdline);
|
free(cmdline);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
is_bash_test_env()
|
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;
|
int retVal = 0;
|
||||||
const size_t cygwin_path_prefix_len = strlen(CYGWIN_PATH_PREFIX);
|
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);
|
memset(out, 0, out_len);
|
||||||
|
if (_strnicmp(in, CYGWIN_PATH_PREFIX, cygwin_path_prefix_len) == 0) {
|
||||||
out[0] = in[cygwin_path_prefix_len];
|
out[0] = in[cygwin_path_prefix_len];
|
||||||
out[1] = ':';
|
out[1] = ':';
|
||||||
strcat_s(out, out_len, &in[cygwin_path_prefix_len + 1]);
|
strcat_s(out, out_len, &in[cygwin_path_prefix_len + 1]);
|
||||||
retVal = 1;
|
retVal = 1;
|
||||||
} else {
|
} else
|
||||||
strcat_s(out, out_len, in);
|
strcpy_s(out, out_len, in);
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,10 @@ int is_absolute_path(const char *);
|
|||||||
int file_in_chroot_jail(HANDLE);
|
int file_in_chroot_jail(HANDLE);
|
||||||
PSID get_sid(const char*);
|
PSID get_sid(const char*);
|
||||||
int am_system();
|
int am_system();
|
||||||
char* build_session_commandline(const char *, const char *, const char *);
|
|
||||||
int is_conpty_supported();
|
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();
|
char* get_custom_lsa_package();
|
||||||
wchar_t* get_final_path_by_handle(HANDLE h);
|
wchar_t* get_final_path_by_handle(HANDLE h);
|
||||||
int lookup_principal_name(const wchar_t * sam_account_name, wchar_t * user_principal_name);
|
int lookup_principal_name(const wchar_t * sam_account_name, wchar_t * user_principal_name);
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
static struct passwd pw;
|
static struct passwd pw;
|
||||||
static char* pw_shellpath = NULL;
|
static char* pw_shellpath = NULL;
|
||||||
char* shell_command_option = NULL;
|
char* shell_command_option = NULL;
|
||||||
|
BOOLEAN arg_escape = TRUE;
|
||||||
|
|
||||||
/* returns 0 on success, and -1 with errno set on failure */
|
/* returns 0 on success, and -1 with errno set on failure */
|
||||||
static int
|
static int
|
||||||
@ -76,8 +77,12 @@ set_defaultshell()
|
|||||||
(path_buf[0] != L'\0')) {
|
(path_buf[0] != L'\0')) {
|
||||||
/* fetched default shell path from registry */
|
/* fetched default shell path from registry */
|
||||||
tmp_len = _countof(option_buf);
|
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)
|
if (RegQueryValueExW(reg_key, L"DefaultShellCommandOption", 0, NULL, (LPBYTE)option_buf, &tmp_len) != ERROR_SUCCESS)
|
||||||
option_buf[0] = L'\0';
|
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 {
|
} else {
|
||||||
if (!GetSystemDirectoryW(path_buf, _countof(path_buf))) {
|
if (!GetSystemDirectoryW(path_buf, _countof(path_buf))) {
|
||||||
errno = GetLastError();
|
errno = GetLastError();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "inc\unistd.h"
|
#include "inc\unistd.h"
|
||||||
#include "debug.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
|
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)
|
__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"))
|
if (strcmp(user, "sshd"))
|
||||||
load_user_profile(user_token, user);
|
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);
|
CloseHandle(user_token);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
447
contrib/win32/win32compat/w32-doexec.c
Normal file
447
contrib/win32/win32compat/w32-doexec.c
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* Author: Yanbing Wang <yawang@microsoft.com>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#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);
|
||||||
|
}
|
@ -1034,115 +1034,28 @@ int fork()
|
|||||||
verbose("fork is not supported");
|
verbose("fork is not supported");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
char * build_commandline_string(const char* cmd, char *const argv[], BOOLEAN prepend_module_path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spawn a child process
|
* spawn a child process
|
||||||
* - specified by cmd with agruments argv
|
* - specified by cmd with agruments argv
|
||||||
* - with std handles set to in, out, err
|
* - with std handles set to in, out, err
|
||||||
* - flags are passed to CreateProcess call
|
* - 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
|
* spawned child will run as as_user if its not NULL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
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)
|
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;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFOW si;
|
STARTUPINFOW si;
|
||||||
BOOL b;
|
BOOL b;
|
||||||
char *cmdline, *t;
|
char *cmdline;
|
||||||
char * const *t1;
|
|
||||||
DWORD cmdline_len = 0;
|
|
||||||
wchar_t * cmdline_utf16 = NULL;
|
wchar_t * cmdline_utf16 = NULL;
|
||||||
int add_module_path = 0, ret = -1;
|
int ret = -1;
|
||||||
char *path = NULL;
|
if ((cmdline = build_commandline_string(cmd, argv, prepend_module_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) {
|
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
goto cleanup;
|
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 "<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);
|
|
||||||
|
|
||||||
*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) {
|
if ((cmdline_utf16 = utf8_to_utf16(cmdline)) == NULL) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1155,12 +1068,18 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL
|
|||||||
si.hStdError = err;
|
si.hStdError = err;
|
||||||
si.dwFlags = STARTF_USESTDHANDLES;
|
si.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
debug3("spawning %ls", cmdline_utf16);
|
wchar_t * t = cmdline_utf16;
|
||||||
|
do {
|
||||||
|
debug3("spawning %ls", t);
|
||||||
if (as_user)
|
if (as_user)
|
||||||
b = CreateProcessAsUserW(as_user, NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
|
b = CreateProcessAsUserW(as_user, NULL, t, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
|
||||||
else
|
else
|
||||||
b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
|
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 (b) {
|
||||||
if (register_child(pi.hProcess, pi.dwProcessId) == -1) {
|
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;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
|
ret = pi.dwProcessId;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
errno = GetLastError();
|
errno = GetLastError();
|
||||||
error("%s failed error:%d", (as_user ? "CreateProcessAsUserW" : "CreateProcessW"), GetLastError());
|
error("%s failed error:%d", (as_user ? "CreateProcessAsUserW" : "CreateProcessW"), GetLastError());
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pi.dwProcessId;
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (cmdline)
|
if (cmdline)
|
||||||
free(cmdline);
|
free(cmdline);
|
||||||
if (cmdline_utf16)
|
if (cmdline_utf16)
|
||||||
free(cmdline_utf16);
|
free(cmdline_utf16);
|
||||||
if (path)
|
|
||||||
free(path);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "inc\fcntl.h"
|
#include "inc\fcntl.h"
|
||||||
|
#include "inc\utf.h"
|
||||||
#include "misc_internal.h"
|
#include "misc_internal.h"
|
||||||
|
#include "signal_internal.h"
|
||||||
|
|
||||||
// Return Value: 0 for success, -1 for failure
|
// Return Value: 0 for success, -1 for failure
|
||||||
int
|
int
|
||||||
@ -96,33 +98,60 @@ int is_conpty_supported()
|
|||||||
return 0;
|
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, };
|
wchar_t pty_cmdline[MAX_CMD_LEN] = { 0, };
|
||||||
int ret = -1;
|
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())
|
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*/
|
/* 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
|
* In PTY mode, ssh-shellhost takes stderr as control channel
|
||||||
* TODO - fix this and pass control channel pipe as a command line parameter
|
* 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);
|
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());
|
debug("%s - failed to execute %ls, error:%d", __func__, pty_cmdline, GetLastError());
|
||||||
errno = EOTHER;
|
errno = EOTHER;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
*pid = pi.dwProcessId;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
if (cmd_w)
|
||||||
|
free(cmd_w);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
@ -224,7 +224,9 @@ int argc_original = 0;
|
|||||||
wchar_t **wargv_original = NULL;
|
wchar_t **wargv_original = NULL;
|
||||||
|
|
||||||
int wmain(int argc, wchar_t **wargv) {
|
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;
|
argc_original = argc;
|
||||||
wargv_original = wargv;
|
wargv_original = wargv;
|
||||||
|
|
||||||
@ -232,6 +234,38 @@ int wmain(int argc, wchar_t **wargv) {
|
|||||||
/* change current directory to sshd.exe root */
|
/* change current directory to sshd.exe root */
|
||||||
_wchdir(__wprogdir);
|
_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 (!StartServiceCtrlDispatcherW(dispatch_table)) {
|
||||||
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
|
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
|
||||||
return sshd_main(argc, wargv); /* sshd running NOT as service*/
|
return sshd_main(argc, wargv); /* sshd running NOT as service*/
|
||||||
|
8
pal_doexec.h
Normal file
8
pal_doexec.h
Normal file
@ -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 */
|
@ -51,7 +51,7 @@ Describe "Tests for scp command" -Tags "CI" {
|
|||||||
Title = 'Simple copy local file to remote file'
|
Title = 'Simple copy local file to remote file'
|
||||||
Source = $SourceFilePath
|
Source = $SourceFilePath
|
||||||
Destination = "test_target:$DestinationFilePath"
|
Destination = "test_target:$DestinationFilePath"
|
||||||
Options = "-S '$sshcmd'"
|
Options = "-S `"$sshcmd`""
|
||||||
},
|
},
|
||||||
@{
|
@{
|
||||||
Title = 'Simple copy remote file to local file'
|
Title = 'Simple copy remote file to local file'
|
||||||
|
@ -76,6 +76,8 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
|||||||
$dfltShellRegPath = "HKLM:\Software\OpenSSH"
|
$dfltShellRegPath = "HKLM:\Software\OpenSSH"
|
||||||
$dfltShellRegKeyName = "DefaultShell"
|
$dfltShellRegKeyName = "DefaultShell"
|
||||||
$dfltShellCmdOptionRegKeyName = "DefaultShellCommandOption"
|
$dfltShellCmdOptionRegKeyName = "DefaultShellCommandOption"
|
||||||
|
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
||||||
|
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
function ConfigureDefaultShell {
|
function ConfigureDefaultShell {
|
||||||
param
|
param
|
||||||
@ -192,45 +194,105 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context "$tC - configure default shell Scenarios" {
|
Context "$tC - configure powershell default shell Scenarios" {
|
||||||
BeforeAll {$tI=1}
|
BeforeAll {
|
||||||
AfterAll{$tC++}
|
$tI=1
|
||||||
AfterEach {
|
$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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AfterAll{
|
||||||
|
$tC++
|
||||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
|
||||||
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
It "$tC.$tI - default shell as powershell" -skip:$skip {
|
It "$tC.$tI - basic powershell" -skip:$skip {
|
||||||
$shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path
|
|
||||||
if($shell_path -ne $null) {
|
|
||||||
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
|
|
||||||
|
|
||||||
$o = ssh test_target Write-Output 1234
|
$o = ssh test_target Write-Output 1234
|
||||||
$o | Should Be "1234"
|
$o | Should Be "1234"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
It "$tC.$tI - default shell as cmd" -skip:$skip {
|
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
|
$shell_path = (Get-Command cmd.exe -ErrorAction SilentlyContinue).path
|
||||||
if($shell_path -ne $null) {
|
if($shell_path -ne $null) {
|
||||||
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
|
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 {
|
||||||
$o = ssh test_target where cmd
|
$o = ssh test_target where cmd
|
||||||
$o | Should Contain "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 {
|
||||||
It "$tC.$tI - shellhost as default shell and multiple double quotes in cmdline" {
|
# actual command line ssh target echo '$env:computername'
|
||||||
# actual command line ssh target \"cmd\" /c \"echo hello\"
|
$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
|
$shell_path = (Get-Command ssh-shellhost -ErrorAction SilentlyContinue).path
|
||||||
ConfigureDefaultShell -default_shell_path $shell_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}
|
BeforeAll {$tI=1}
|
||||||
AfterAll{$tC++}
|
AfterAll{$tC++}
|
||||||
|
|
||||||
@ -298,16 +360,17 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
It "ProxyCommand with file name only" {
|
It "ProxyCommand with file name only" {
|
||||||
& cmd /c "ssh -o ProxyCommand=`"cmd.exe /c echo Invalid proxy 1>&2`" abc 2>$stderrFile"
|
& cmd /c "ssh -o ProxyCommand=`"cmd.exe /c echo test string for invalid proxy 1>&2`" abc 2>$stderrFile"
|
||||||
$stderrFile | Should Contain "Invalid proxy"
|
$stderrFile | Should Contain "test string for invalid proxy"
|
||||||
|
write-host (Get-Content $stderrFile)
|
||||||
$stderrFile | Should Contain "Connection closed by remote host"
|
$stderrFile | Should Contain "Connection closed by remote host"
|
||||||
}
|
}
|
||||||
|
|
||||||
It "ProxyCommand with absolute path to the file" {
|
It "ProxyCommand with absolute path to the file" {
|
||||||
& cmd /c "ssh -o ProxyCommand=`"$($env:ComSpec) /c echo Invalid proxy 1>&2`" abc 2>$stderrFile"
|
& cmd /c "ssh -o ProxyCommand=`"$($env:ComSpec) /c echo test string for invalid proxy 1>&2`" abc 2>$stderrFile"
|
||||||
$stderrFile | Should Contain "Invalid proxy"
|
$stderrFile | Should Contain "test string for invalid proxy"
|
||||||
|
write-host (Get-Content $stderrFile)
|
||||||
$stderrFile | Should Contain "Connection closed by remote host"
|
$stderrFile | Should Contain "Connection closed by remote host"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -318,98 +318,99 @@ test_chroot()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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");
|
TEST_START("arg is null");
|
||||||
out = build_session_commandline("c:\\system32\\cmd.exe", NULL, NULL);
|
out = build_exec_command(NULL);
|
||||||
ASSERT_STRING_EQ(out, "\"c:\\system32\\cmd.exe\"");
|
ASSERT_PTR_EQ(out, NULL);
|
||||||
TEST_DONE();
|
TEST_DONE();
|
||||||
|
|
||||||
TEST_START("cmd shell tests");
|
TEST_START("scp tests");
|
||||||
buf[0] = '\0';
|
out = build_exec_command("sCp -arg");
|
||||||
strcat(buf, "\"c:\\system32\\cmd.exe\" /c \"\"");
|
ASSERT_STRING_EQ(out, "scp.exe -arg");
|
||||||
strcat(buf, progdir);
|
free(out);
|
||||||
int len_pg = strlen(buf);
|
out = build_exec_command("sCp.exe -arg1 -arg2");
|
||||||
out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "internal-sftp -arg");
|
ASSERT_STRING_EQ(out, "scp.exe -arg1 -arg2");
|
||||||
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);
|
|
||||||
free(out);
|
free(out);
|
||||||
TEST_DONE();
|
TEST_DONE();
|
||||||
|
|
||||||
TEST_START("bash shell tests");
|
TEST_START("sftp tests");
|
||||||
out = build_session_commandline("c:\\system32\\bash.exe", NULL, "internal-sftp -arg");
|
out = build_exec_command("internal-sftp \"arg1 arg2\"");
|
||||||
ASSERT_STRING_EQ(out, "\"c:\\system32\\bash.exe\" -c \"sftp-server.exe -arg\"");
|
ASSERT_STRING_EQ(out, "sftp-server.exe \"arg1 arg2\"");
|
||||||
free(out);
|
free(out);
|
||||||
out = build_session_commandline("c:\\system32\\bash", NULL, "internal-sftp -arg");
|
out = build_exec_command("SFTP-server.exe -arg");
|
||||||
ASSERT_STRING_EQ(out, "\"c:\\system32\\bash\" -c \"sftp-server.exe -arg\"");
|
ASSERT_STRING_EQ(out, "sftp-server.exe -arg");
|
||||||
free(out);
|
free(out);
|
||||||
out = build_session_commandline("c:\\system32\\bash", NULL, "sFTP-server -arg");
|
out = build_exec_command("sftp-SERVER -arg");
|
||||||
ASSERT_STRING_EQ(out, "\"c:\\system32\\bash\" -c \"sftp-server.exe -arg\"");
|
ASSERT_STRING_EQ(out, "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\"");
|
|
||||||
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\"");
|
|
||||||
free(out);
|
|
||||||
out = build_session_commandline("c:\\powershell", NULL, "sftp-server -arg");
|
|
||||||
ASSERT_STRING_EQ(out, "\"c:\\powershell\" -c \"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\"");
|
|
||||||
free(out);
|
free(out);
|
||||||
TEST_DONE();
|
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
|
void
|
||||||
miscellaneous_tests()
|
miscellaneous_tests()
|
||||||
@ -421,5 +422,6 @@ miscellaneous_tests()
|
|||||||
test_realpath();
|
test_realpath();
|
||||||
test_statvfs();
|
test_statvfs();
|
||||||
test_chroot();
|
test_chroot();
|
||||||
test_build_session_commandline();
|
test_build_exec_command();
|
||||||
|
test_build_commandline_string();
|
||||||
}
|
}
|
||||||
|
368
session.c
368
session.c
@ -96,6 +96,7 @@
|
|||||||
#include "monitor_wrap.h"
|
#include "monitor_wrap.h"
|
||||||
#include "sftp.h"
|
#include "sftp.h"
|
||||||
#include "atomicio.h"
|
#include "atomicio.h"
|
||||||
|
#include "pal_doexec.h"
|
||||||
|
|
||||||
#if defined(KRB5) && defined(USE_AFS)
|
#if defined(KRB5) && defined(USE_AFS)
|
||||||
#include <kafs.h>
|
#include <kafs.h>
|
||||||
@ -118,8 +119,10 @@ void session_set_fds(struct ssh *, Session *, int, int, int, int, int);
|
|||||||
void session_pty_cleanup(Session *);
|
void session_pty_cleanup(Session *);
|
||||||
void session_proctitle(Session *);
|
void session_proctitle(Session *);
|
||||||
int session_setup_x11fwd(struct ssh *, Session *);
|
int session_setup_x11fwd(struct ssh *, Session *);
|
||||||
|
#ifndef WINDOWS /* !WINDOWS */
|
||||||
int do_exec_pty(struct ssh *, Session *, const char *);
|
int do_exec_pty(struct ssh *, Session *, const char *);
|
||||||
int do_exec_no_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 *);
|
int do_exec(struct ssh *, Session *, const char *);
|
||||||
void do_login(struct ssh *, Session *, const char *);
|
void do_login(struct ssh *, Session *, const char *);
|
||||||
void do_child(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
|
#define USE_PIPES 1
|
||||||
|
|
||||||
#ifdef WINDOWS
|
#ifndef WINDOWS /* !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 */
|
|
||||||
/*
|
/*
|
||||||
* This is called to fork and execute a command when we have no tty. This
|
* 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
|
* 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);
|
remote = ssh_remote_ipaddr(ssh);
|
||||||
return remote;
|
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
|
Loading…
x
Reference in New Issue
Block a user