diff --git a/contrib/win32/openssh/vstsbuild.ps1 b/contrib/win32/openssh/vstsbuild.ps1
index 64a75b32c..6c771a3a1 100644
--- a/contrib/win32/openssh/vstsbuild.ps1
+++ b/contrib/win32/openssh/vstsbuild.ps1
@@ -18,7 +18,7 @@ if (-not $gitBinFullPath)
function Get-RepoFork
{
[CmdletBinding()]
- param([string]$AccountURL, [string]$RepoFork, [string]$repoLocalPath, [string]$BranchName)
+ param([string]$AccountURL="https://github.com/powershell", [string]$RepoFork, [string]$repoLocalPath, [string]$BranchName)
if (Test-Path -Path $repoLocalPath -PathType Container)
{
Remove-Item -Path $repoLocalPath -Recurse -Force
@@ -132,4 +132,4 @@ catch
finally{
Write-VstsTaskState
exit 0
-}
\ No newline at end of file
+}
diff --git a/contrib/win32/openssh/win32iocompat.vcxproj b/contrib/win32/openssh/win32iocompat.vcxproj
index d4b351321..86b4688bd 100644
--- a/contrib/win32/openssh/win32iocompat.vcxproj
+++ b/contrib/win32/openssh/win32iocompat.vcxproj
@@ -304,6 +304,7 @@
+
diff --git a/contrib/win32/win32compat/console.c b/contrib/win32/win32compat/console.c
index a21609e40..e25bf9983 100644
--- a/contrib/win32/win32compat/console.c
+++ b/contrib/win32/win32compat/console.c
@@ -40,11 +40,14 @@
#include "debug.h"
#include "console.h"
#include "ansiprsr.h"
+#include "misc_internal.h"
HANDLE hOutputConsole = NULL;
DWORD stdin_dwSavedAttributes = 0;
DWORD stdout_dwSavedAttributes = 0;
WORD wStartingAttributes = 0;
+unsigned int console_out_cp_saved = 0;
+unsigned int console_in_cp_saved = 0;
int ScreenX;
int ScreenY;
@@ -142,14 +145,28 @@ ConEnterRawMode()
SavedViewRect = csbi.srWindow;
debug("console doesn't support the ansi parsing");
} else {
- ConSaveViewRect();
debug("console supports the ansi parsing");
- }
+ if (is_conpty_supported()) {
+ console_out_cp_saved = GetConsoleOutputCP();
+ console_in_cp_saved = GetConsoleCP();
+ if (SetConsoleOutputCP(CP_UTF8))
+ debug3("Successfully set console output code page from:%d to %d", console_out_cp_saved, CP_UTF8);
+ else
+ error("Failed to set console output code page from:%d to %d error:%d", console_out_cp_saved, CP_UTF8, GetLastError());
+
+ if (SetConsoleCP(CP_UTF8))
+ debug3("Successfully set console input code page from:%d to %d", console_in_cp_saved, CP_UTF8);
+ else
+ error("Failed to set console input code page from:%d to %d error:%d", console_in_cp_saved, CP_UTF8, GetLastError());
+ } else {
+ ConSaveViewRect();
+ }
+ }
ConSetScreenX();
ConSetScreenY();
ScrollTop = 0;
- ScrollBottom = ConVisibleWindowHeight();
+ ScrollBottom = ConVisibleWindowHeight();
in_raw_mode = 1;
}
@@ -160,6 +177,22 @@ ConExitRawMode()
{
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), stdin_dwSavedAttributes);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), stdout_dwSavedAttributes);
+
+ if (FALSE == isAnsiParsingRequired && is_conpty_supported()) {
+ if (console_out_cp_saved) {
+ if(SetConsoleOutputCP(console_out_cp_saved))
+ debug3("Successfully set console output code page from %d to %d", CP_UTF8, console_out_cp_saved);
+ else
+ error("Failed to set console output code page from %d to %d error:%d", CP_UTF8, console_out_cp_saved, GetLastError());
+ }
+
+ if (console_in_cp_saved) {
+ if (SetConsoleCP(console_in_cp_saved))
+ debug3("Successfully set console input code page from %d to %d", CP_UTF8, console_in_cp_saved);
+ else
+ error("Failed to set console input code page from %d to %d error:%d", CP_UTF8, console_in_cp_saved, GetLastError());
+ }
+ }
}
/* Used to exit the raw mode */
@@ -223,7 +256,7 @@ ConSetScreenRect(int xSize, int ySize)
bSuccess = SetConsoleScreenBufferSize(hOutputConsole, coordScreen);
}
- if (bSuccess)
+ if (bSuccess && !is_conpty_supported())
ConSaveViewRect();
/* if the current buffer *is* the size we want, don't do anything! */
@@ -270,7 +303,7 @@ ConSetScreenSize(int xSize, int ySize)
bSuccess = SetConsoleWindowInfo(hOutputConsole, TRUE, &srWindowRect);
}
- if (bSuccess)
+ if (bSuccess && !is_conpty_supported())
ConSaveViewRect();
/* if the current buffer *is* the size we want, don't do anything! */
@@ -1490,7 +1523,6 @@ void
ConSaveViewRect()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
-
if (GetConsoleScreenBufferInfo(hOutputConsole, &csbi))
SavedViewRect = csbi.srWindow;
}
@@ -1590,7 +1622,8 @@ ConMoveCursorTopOfVisibleWindow()
offset = csbi.dwCursorPosition.Y - csbi.srWindow.Top;
ConMoveVisibleWindow(offset);
- ConSaveViewRect();
+ if(!is_conpty_supported())
+ ConSaveViewRect();
}
}
diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c
index ec5548236..8c5b58f49 100644
--- a/contrib/win32/win32compat/fileio.c
+++ b/contrib/win32/win32compat/fileio.c
@@ -207,7 +207,7 @@ fileio_pipe(struct w32_io* pio[2], int duplex)
sec_attributes.bInheritHandle = TRUE;
sec_attributes.lpSecurityDescriptor = NULL;
- sec_attributes.nLength = 0;
+ sec_attributes.nLength = sizeof(sec_attributes);
/* create named pipe */
write_handle = CreateNamedPipeA(pipe_name,
@@ -415,18 +415,18 @@ cleanup:
/* returns 1 if true, 0 otherwise */
int
-file_in_chroot_jail(HANDLE handle, const char* path_utf8) {
+file_in_chroot_jail(HANDLE handle) {
/* ensure final path is within chroot */
- wchar_t path_buf[MAX_PATH], *final_path;
- if (GetFinalPathNameByHandleW(handle, path_buf, MAX_PATH, 0) == 0) {
- debug3("failed to get final path of file:%s error:%d", path_utf8, GetLastError());
+ wchar_t *final_path;
+
+ final_path = get_final_path_by_handle(handle);
+ if (!final_path)
return 0;
- }
- final_path = path_buf + 4;
+
to_wlower_case(final_path);
if ((wcslen(final_path) < wcslen(chroot_pathw)) ||
- memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
- final_path[wcslen(chroot_pathw)] != '\\') {
+ memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
+ final_path[wcslen(chroot_pathw)] != '\\') {
debug3("access denied due to attempt to escape chroot jail");
return 0;
}
@@ -478,7 +478,8 @@ fileio_open(const char *path_utf8, int flags, mode_t mode)
goto cleanup;
}
- if (chroot_pathw && !nonfs_dev && !file_in_chroot_jail(handle, path_utf8)) {
+ if (chroot_pathw && !nonfs_dev && !file_in_chroot_jail(handle)) {
+ debug3("%s is not in chroot jail", path_utf8);
errno = EACCES;
goto cleanup;
}
@@ -776,7 +777,7 @@ fileio_fstat(struct w32_io* pio, struct _stat64 *buf)
return -1;
}
- int fd = _open_osfhandle(dup_handle, 0);
+ int fd = _open_osfhandle((intptr_t)dup_handle, 0);
debug4("fstat - pio:%p", pio);
if (fd == -1) {
CloseHandle(dup_handle);
@@ -902,44 +903,41 @@ fileio_lseek(struct w32_io* pio, unsigned __int64 offset, int origin)
return 0;
}
-/* fdopen implementation */
+/*
+ * fdopen implementation - use with caution
+ * this implementation deviates from POSIX spec the following way
+ * - the underlying file descriptor is closed automatically
+ * hence no further POSIX io operations (read, write, close, etc) on the
+ * underlying file descriptor are supported
+ */
FILE*
fileio_fdopen(struct w32_io* pio, const char *mode)
{
- int fd_flags = 0;
+ wchar_t *file_path, *wmode = NULL;
+ FILE* ret = NULL;
+
debug4("fdopen - io:%p", pio);
- /* logic below doesn't work with overlapped file HANDLES */
- if (mode[1] == '\0') {
- switch (*mode) {
- case 'r':
- fd_flags = _O_RDONLY;
- break;
- case 'w':
- break;
- case 'a':
- fd_flags = _O_APPEND;
- break;
- default:
- errno = ENOTSUP;
- debug3("fdopen - ERROR unsupported mode %s", mode);
- return NULL;
- }
- } else {
- errno = ENOTSUP;
- debug3("fdopen - ERROR unsupported mode %s", mode);
- return NULL;
- }
+ if ((wmode = utf8_to_utf16(mode)) == NULL)
+ goto cleanup;
- int fd = _open_osfhandle((intptr_t)pio->handle, fd_flags);
+ file_path = get_final_path_by_handle(pio->handle);
+ if (!file_path)
+ goto cleanup;
+
+ /*
+ * close the win32 handle right away and remove entry from table
+ * otherwise, wfopen will get an access denied due to sharing violation
+ */
+ int w32_close(int);
+ w32_close(pio->table_index);
+ errno = _wfopen_s(&ret, file_path, wmode);
- if (fd == -1) {
- errno = EOTHER;
- debug3("fdopen - ERROR:%d _open_osfhandle()", errno);
- return NULL;
- }
+cleanup:
+ if (wmode)
+ free(wmode);
- return _fdopen(fd, mode);
+ return ret;
}
void
diff --git a/contrib/win32/win32compat/inc/sys/stat.h b/contrib/win32/win32compat/inc/sys/stat.h
index e8e960fb5..5e654fa70 100644
--- a/contrib/win32/win32compat/inc/sys/stat.h
+++ b/contrib/win32/win32compat/inc/sys/stat.h
@@ -38,6 +38,10 @@ int w32_mkdir(const char *pathname, unsigned short mode);
int w32_chmod(const char *, mode_t);
#define chmod w32_chmod
+int w32_fchmod(int fd, mode_t mode);
+#define fchmod w32_fchmod
+
+
struct w32_stat {
dev_t st_dev; /* ID of device containing file */
unsigned short st_ino; /* inode number */
diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c
index e1da03ad6..76e79a003 100644
--- a/contrib/win32/win32compat/misc.c
+++ b/contrib/win32/win32compat/misc.c
@@ -286,7 +286,8 @@ w32_fopen_utf8(const char *input_path, const char *mode)
if (chroot_pathw && !nonfs_dev) {
/* ensure final path is within chroot */
HANDLE h = (HANDLE)_get_osfhandle(_fileno(f));
- if (!file_in_chroot_jail(h, input_path)) {
+ if (!file_in_chroot_jail(h)) {
+ debug3("%s is not in chroot jail", input_path);
fclose(f);
f = NULL;
errno = EACCES;
@@ -419,34 +420,6 @@ w32_setvbuf(FILE *stream, char *buffer, int mode, size_t size) {
return setvbuf(stream, buffer, mode, size);
}
-/* TODO - deprecate this. This is not a POSIX API, used internally only */
-char *
-w32_programdir()
-{
- wchar_t* wpgmptr;
-
- if (s_programdir != NULL)
- return s_programdir;
-
- if (_get_wpgmptr(&wpgmptr) != 0)
- return NULL;
-
- if ((s_programdir = utf16_to_utf8(wpgmptr)) == NULL)
- return NULL;
-
- /* null terminate after directory path */
- char* tail = s_programdir + strlen(s_programdir);
- while (tail > s_programdir && *tail != '\\' && *tail != '/')
- tail--;
-
- if (tail > s_programdir)
- *tail = '\0';
- else
- *tail = '.'; /* current directory */
-
- return s_programdir;
-}
-
int
daemon(int nochdir, int noclose)
{
@@ -1592,11 +1565,11 @@ cleanup:
/* 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, int pty)
+build_session_commandline(const char *shell, const char* shell_arg, const char *command)
{
enum sh_type { SH_OTHER, SH_CMD, SH_PS, SH_BASH } shell_type = SH_OTHER;
enum cmd_type { CMD_OTHER, CMD_SFTP, CMD_SCP } command_type = CMD_OTHER;
- char *progdir = w32_programdir(), *cmd_sp = NULL, *cmdline = NULL, *ret = NULL, *p;
+ char *progdir = __progdir, *cmd_sp = NULL, *cmdline = NULL, *ret = NULL, *p;
int len, progdir_len = (int)strlen(progdir);
#define CMDLINE_APPEND(P, S) \
@@ -1650,7 +1623,7 @@ do { \
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;
@@ -1687,7 +1660,6 @@ do { \
p = cmd_sp;
if (shell_type == SH_CMD) {
-
CMDLINE_APPEND(p, "\"");
CMDLINE_APPEND(p, progdir);
@@ -1709,8 +1681,6 @@ do { \
} while (0);
len = 0;
- if (pty)
- len += progdir_len + (int)strlen("ssh-shellhost.exe") + 5;
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 */
@@ -1723,11 +1693,6 @@ do { \
}
p = cmdline;
- if (pty) {
- CMDLINE_APPEND(p, "\"");
- CMDLINE_APPEND(p, progdir);
- CMDLINE_APPEND(p, "\\ssh-shellhost.exe\" ");
- }
CMDLINE_APPEND(p, "\"");
CMDLINE_APPEND(p, shell);
CMDLINE_APPEND(p, "\"");
@@ -1742,14 +1707,10 @@ do { \
else
CMDLINE_APPEND(p, " -c ");
- /* bash type shells require " decoration around command*/
- if (shell_type == SH_BASH)
- CMDLINE_APPEND(p, "\"");
-
+ /* Add double quotes around command */
+ CMDLINE_APPEND(p, "\"");
CMDLINE_APPEND(p, command);
-
- if (shell_type == SH_BASH)
- CMDLINE_APPEND(p, "\"");
+ CMDLINE_APPEND(p, "\"");
}
*p = '\0';
ret = cmdline;
diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h
index 426727c24..966271359 100644
--- a/contrib/win32/win32compat/misc_internal.h
+++ b/contrib/win32/win32compat/misc_internal.h
@@ -25,6 +25,17 @@
/* maximum size for user principal name as defined in ad schema */
#define MAX_UPN_LEN 1024
+/* PTY windows size event type (for conhost and ssh-shellhost) */
+#define PTY_SIGNAL_RESIZE_WINDOW 8u
+
+/* maximum command line length */
+#define MAX_CMD_LEN 8191
+
+/* prog paths */
+extern char* __progname;
+extern char* __progdir;
+extern wchar_t* __wprogdir;
+
static char *machine_domain_name;
extern char* chroot_path;
@@ -35,7 +46,7 @@ extern wchar_t* chroot_pathw;
wchar_t * resolved_path_utf16(const char *);
void w32posix_initialize();
void w32posix_done();
-char* w32_programdir();
+void init_prog_paths();
void convertToBackslash(char *str);
void convertToBackslashW(wchar_t *str);
void convertToForwardslash(char *str);
@@ -51,9 +62,12 @@ HANDLE get_user_token(const char* user, int impersonation);
int load_user_profile(HANDLE user_token, char* user);
int create_directory_withsddl(wchar_t *path, wchar_t *sddl);
int is_absolute_path(const char *);
-int file_in_chroot_jail(HANDLE, 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 );
+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);
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/shell-host.c b/contrib/win32/win32compat/shell-host.c
index 02a85e8a6..d8fdc3182 100644
--- a/contrib/win32/win32compat/shell-host.c
+++ b/contrib/win32/win32compat/shell-host.c
@@ -44,7 +44,6 @@
#define MAX_CONSOLE_COLUMNS 9999
#define MAX_CONSOLE_ROWS 9999
-#define MAX_CMD_LEN 8191 // msdn
#define WM_APPEXIT WM_USER+1
#define MAX_EXPECTED_BUFFER_SIZE 1024
/* 4KB is the largest size for which writes are guaranteed to be atomic */
@@ -274,13 +273,14 @@ HANDLE child_in = INVALID_HANDLE_VALUE;
HANDLE child_err = INVALID_HANDLE_VALUE;
HANDLE pipe_in = INVALID_HANDLE_VALUE;
HANDLE pipe_out = INVALID_HANDLE_VALUE;
-HANDLE pipe_err = INVALID_HANDLE_VALUE;
+HANDLE pipe_ctrl = INVALID_HANDLE_VALUE;
HANDLE child = INVALID_HANDLE_VALUE;
HANDLE job = NULL;
HANDLE hConsoleBuffer = INVALID_HANDLE_VALUE;
HANDLE monitor_thread = INVALID_HANDLE_VALUE;
HANDLE io_thread = INVALID_HANDLE_VALUE;
HANDLE ux_thread = INVALID_HANDLE_VALUE;
+HANDLE ctrl_thread = INVALID_HANDLE_VALUE;
DWORD child_exit_code = 0;
DWORD hostProcessId = 0;
@@ -800,6 +800,48 @@ MonitorChild(_In_ LPVOID lpParameter)
return 0;
}
+unsigned __stdcall
+ControlThread(LPVOID p)
+{
+ short type, row, col;
+ DWORD len;
+ COORD coord;
+ SMALL_RECT rect;
+ while (1) {
+ if (!ReadFile(pipe_ctrl, &type, 2, &len, NULL))
+ break;
+ if (type != PTY_SIGNAL_RESIZE_WINDOW)
+ break;
+ if (!ReadFile(pipe_ctrl, &col, 2, &len, NULL))
+ break;
+ if (!ReadFile(pipe_ctrl, &row, 2, &len, NULL))
+ break;
+
+ /*
+ * when reducing width, console seemed to retain prior width
+ * while increasing width, however, it behaves right
+ *
+ * hence setting it less by 1 and setting it again to the right
+ * count
+ */
+
+ coord.X = col - 1;
+ coord.Y = row;
+ rect.Top = 0;
+ rect.Left = 0;
+ rect.Bottom = row - 1;
+ rect.Right = col - 2;
+ SetConsoleScreenBufferSize(child_out, coord);
+ SetConsoleWindowInfo(child_out, TRUE, &rect);
+
+ coord.X = col;
+ rect.Right = col - 1;
+ SetConsoleScreenBufferSize(child_out, coord);
+ SetConsoleWindowInfo(child_out, TRUE, &rect);
+ }
+ return 0;
+}
+
DWORD
ProcessEvent(void *p)
{
@@ -1231,10 +1273,10 @@ start_with_pty(wchar_t *command)
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
- pipe_err = GetStdHandle(STD_ERROR_HANDLE);
+ pipe_ctrl = GetStdHandle(STD_ERROR_HANDLE);
/* copy pipe handles passed through std io*/
- if ((pipe_in == INVALID_HANDLE_VALUE) || (pipe_out == INVALID_HANDLE_VALUE) || (pipe_err == INVALID_HANDLE_VALUE))
+ if ((pipe_in == INVALID_HANDLE_VALUE) || (pipe_out == INVALID_HANDLE_VALUE) || (pipe_ctrl == INVALID_HANDLE_VALUE))
return -1;
cp = GetConsoleCP();
@@ -1272,11 +1314,8 @@ start_with_pty(wchar_t *command)
/*
* Launch via cmd.exe /c, otherwise known issues exist with color rendering in powershell
*/
- cmd[0] = L'\0';
- GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, system32_path));
- GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L"\\cmd.exe /c "));
- GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, command));
-
+ _snwprintf_s(cmd, MAX_CMD_LEN, MAX_CMD_LEN, L"\"%ls\\cmd.exe\" /c \"%ls\"", system32_path, command);
+
SetConsoleCtrlHandler(NULL, FALSE);
GOTO_CLEANUP_ON_FALSE(CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi));
@@ -1311,6 +1350,10 @@ start_with_pty(wchar_t *command)
if (IS_INVALID_HANDLE(ux_thread))
goto cleanup;
+ ctrl_thread = (HANDLE)_beginthreadex(NULL, 0, ControlThread, NULL, 0, NULL);
+ if (IS_INVALID_HANDLE(ctrl_thread))
+ goto cleanup;
+
ProcessMessages(NULL);
cleanup:
dwStatus = GetLastError();
@@ -1330,6 +1373,11 @@ cleanup:
CloseHandle(io_thread);
}
+ if (!IS_INVALID_HANDLE(ctrl_thread)) {
+ TerminateThread(ctrl_thread, 0);
+ CloseHandle(ctrl_thread);
+ }
+
if (hEventHook)
__UnhookWinEvent(hEventHook);
@@ -1349,20 +1397,99 @@ cleanup:
return child_exit_code;
}
-int
-wmain(int ac, wchar_t **av)
+/* implements a basic shell - launches given cmd using CreateProcess */
+int start_as_shell(wchar_t* cmd)
{
- wchar_t *exec_command;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
- _set_invalid_parameter_handler(my_invalid_parameter_handler);
+ memset(&si, 0, sizeof(STARTUPINFOW));
+ memset(&pi, 0, sizeof(PROCESS_INFORMATION));
+ si.cb = sizeof(STARTUPINFOW);
- if (ac == 1) {
- printf("usage: shellhost.exe \n");
+ if (CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == FALSE) {
+ printf("ssh-shellhost cannot run '%ls', error: %d", cmd, GetLastError());
exit(255);
}
- /* get past shellhost.exe in commandline */
- exec_command = wcsstr(GetCommandLineW(), L"shellhost.exe") + wcslen(L"shellhost.exe") + 1;
+ CloseHandle(pi.hThread);
+ /* close std io handles */
+ CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
+ CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
+ CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
+ child_exit_code = 255;
- return start_with_pty(exec_command);
+ /* wait for child to exit */
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ if (!GetExitCodeProcess(pi.hProcess, &child_exit_code))
+ printf("ssh-shellhost unable to track child process, error: %d", GetLastError());
+
+ CloseHandle(pi.hProcess);
+ return child_exit_code;
+}
+
+/*
+ * Usage:
+ * Execute commandline with PTY
+ * ssh-shellhost.exe ---pty commandline
+ * Note that in PTY mode, stderr is taken as the control channel
+ * to receive Windows size change events
+ *
+ * Execute commandline like shell (plain IO redirection)
+ * Syntax mimics cmd.exe -c usage. Note the explicit double quotes
+ * around actual commandline to execute.
+ * ssh-shellhost.exe -c "commandline"
+ * Ex. ssh-shellhost.exe -c "notepad.exe file.txt"
+ * ssh-shellhost.exe -c ""my program.exe" "arg 1" "arg 2""
+ */
+int
+wmain(int ac, wchar_t **av)
+{
+ wchar_t *exec_command, *option, *cmdline;
+ int with_pty, len;
+
+ _set_invalid_parameter_handler(my_invalid_parameter_handler);
+
+ if (ac == 1)
+ goto usage;
+
+ if ((cmdline = _wcsdup(GetCommandLineW())) == NULL) {
+ printf("ssh-shellhost.exe ran out of memory");
+ exit(255);
+ }
+
+ if (option = wcsstr(cmdline, L" ---pty "))
+ with_pty = 1;
+ else if (option = wcsstr(cmdline, L" -c "))
+ with_pty = 0;
+ else
+ goto usage;
+
+ if (with_pty)
+ exec_command = option + wcslen(L" ---pty ");
+ else
+ exec_command = option + wcslen(L" -c ");
+
+ /* strip preceding white spaces */
+ while (*exec_command != L'\0' && *exec_command == L' ')
+ exec_command++;
+
+ if (exec_command == L'\0')
+ goto usage;
+
+ if (with_pty)
+ return start_with_pty(exec_command);
+ else {
+ /* if commandline is enclosed in double quotes, remove them */
+ len = (int)wcslen(exec_command);
+ if (len > 2 && *exec_command == L'\"' && *(exec_command + len - 1) == L'\"') {
+ *(exec_command + len - 1) = L'\0';
+ exec_command++;
+ }
+ return start_as_shell(exec_command);
+ }
+usage:
+ printf("ssh-shellhost does not support command line: %ls", cmdline);
+ exit(255);
}
diff --git a/contrib/win32/win32compat/signal.c b/contrib/win32/win32compat/signal.c
index 0776f2350..5fbbd7e19 100644
--- a/contrib/win32/win32compat/signal.c
+++ b/contrib/win32/win32compat/signal.c
@@ -107,7 +107,7 @@ static VOID CALLBACK
sigwinch_APCProc(_In_ ULONG_PTR dwParam)
{
debug5("SIGTERM APCProc()");
- sigaddset(&pending_signals, W32_SIGWINCH);
+ sigaddset(&pending_signals, SIGWINCH);
}
void
diff --git a/contrib/win32/win32compat/tncon.h b/contrib/win32/win32compat/tncon.h
index d58f596db..6b625d73d 100644
--- a/contrib/win32/win32compat/tncon.h
+++ b/contrib/win32/win32compat/tncon.h
@@ -56,7 +56,7 @@
#define SHIFT_TAB_KEY "\x1b[~"
#define SHIFT_ALT_Q "\x1b?"
#define ESCAPE_KEY "\x1b"
-#define BACKSPACE_KEY "\b"
+#define BACKSPACE_KEY "\x7f"
// VT100 Function Key's
#define VT100_PF1_KEY "\x1bO2"
diff --git a/contrib/win32/win32compat/tnnet.c b/contrib/win32/win32compat/tnnet.c
index 244e72667..8c3cc8b00 100644
--- a/contrib/win32/win32compat/tnnet.c
+++ b/contrib/win32/win32compat/tnnet.c
@@ -37,6 +37,7 @@
#include "ansiprsr.h"
#include "inc\utf.h"
#include "console.h"
+#include "misc_internal.h"
#define dwBuffer 4096
@@ -51,16 +52,17 @@ BOOL isFirstPacket = TRUE;
* are hardcoded in the server and will be transformed to Windows Console commands.
*/
void
-processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, size_t *resplen)
+processBuffer(HANDLE handle, char *buf, DWORD len, unsigned char **respbuf, size_t *resplen)
{
unsigned char *pszNewHead = NULL;
unsigned char *pszHead = NULL;
unsigned char *pszTail = NULL;
const char *applicationModeSeq = "\x1b[?1h";
- const int applicationModeSeqLen = (int)strlen(applicationModeSeq);
+ const DWORD applicationModeSeqLen = (DWORD)strlen(applicationModeSeq);
const char *normalModeSeq = "\x1b[?1l";
- const int normalModeSeqLen = (int)strlen(normalModeSeq);
+ const DWORD normalModeSeqLen = (DWORD)strlen(normalModeSeq);
const char *clsSeq = "\x1b[2J";
+ static int track_view_port = 1;
if (len == 0)
return;
@@ -68,6 +70,10 @@ processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, siz
if (false == isAnsiParsingRequired) {
if(isFirstPacket) {
isFirstPacket = FALSE;
+
+ if (is_conpty_supported())
+ track_view_port = 0;
+
/* Windows server at first sends the "cls" after the connection is established.
* There is a bug in the conhost which causes the visible window data to loose so to
* mitigate that issue we need to first move the visible window so that the cursor is at the top of the visible window.
@@ -81,15 +87,18 @@ processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, siz
else if(len >= normalModeSeqLen && strstr(buf, normalModeSeq))
gbVTAppMode = false;
+ /* WriteFile() gets messy when user does scroll up/down so we need to restore the visible window.
+ * It's a conhost bug but we need to live with it as they are not going to back port the fix.
+ */
+ if(track_view_port)
+ ConRestoreViewRect();
+
/* Console has the capability to parse so pass the raw buffer to console directly */
- ConRestoreViewRect(); /* Restore the visible window, otherwise WriteConsoleW() gets messy */
- wchar_t* t = utf8_to_utf16(buf);
- if (t) {
- WriteConsoleW(handle, t, (DWORD)wcslen(t), 0, 0);
- free(t);
- }
-
- ConSaveViewRect();
+ WriteFile(handle, buf, len, 0, 0);
+
+ if (track_view_port)
+ ConSaveViewRect();
+
return;
}
diff --git a/contrib/win32/win32compat/tnnet.h b/contrib/win32/win32compat/tnnet.h
index c3f5c312d..610236ffd 100644
--- a/contrib/win32/win32compat/tnnet.h
+++ b/contrib/win32/win32compat/tnnet.h
@@ -35,6 +35,6 @@
#ifndef __TNNET_H
#define __TNNET_H
-void processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, size_t *resplen);
+void processBuffer(HANDLE handle, char *buf, DWORD len, unsigned char **respbuf, size_t *resplen);
#endif
\ No newline at end of file
diff --git a/contrib/win32/win32compat/w32api_proxies.c b/contrib/win32/win32compat/w32api_proxies.c
index b20d988c0..20fa9fa42 100644
--- a/contrib/win32/win32compat/w32api_proxies.c
+++ b/contrib/win32/win32compat/w32api_proxies.c
@@ -51,8 +51,8 @@ system32_dir()
static HMODULE
load_module(wchar_t* name)
{
- HMODULE hm;
-
+ HMODULE hm = NULL;
+
/*system uses a standard search strategy to find the module */
if ((hm = LoadLibraryW(name)) == NULL)
debug3("unable to load module %ls at run time, error: %d", name, GetLastError());
@@ -82,6 +82,17 @@ load_advapi32()
return s_hm_advapi32;
}
+static HMODULE
+load_api_security_lsapolicy()
+{
+ static HMODULE s_hm_api_security_lsapolicy = NULL;
+
+ if (!s_hm_api_security_lsapolicy)
+ s_hm_api_security_lsapolicy = load_module(L"api-ms-win-security-lsapolicy-l1-1-0.dll");
+
+ return s_hm_api_security_lsapolicy;
+}
+
static HMODULE
load_secur32()
{
@@ -97,7 +108,7 @@ static HMODULE
load_ntdll()
{
static HMODULE s_hm_ntdll = NULL;
-
+
if (!s_hm_ntdll)
s_hm_ntdll = load_module(L"ntdll.dll");
@@ -106,6 +117,9 @@ load_ntdll()
FARPROC get_proc_address(HMODULE hm, char* fn)
{
+ if (hm == NULL) {
+ debug3("GetProcAddress of %s failed with error %d.", fn, GetLastError());
+ }
FARPROC ret = GetProcAddress(hm, fn);
if (!ret)
debug3("GetProcAddress of %s failed with error %d.", fn, GetLastError());
@@ -119,6 +133,7 @@ pLogonUserExExW(wchar_t *user_name, wchar_t *domain, wchar_t *password, DWORD lo
PVOID *profile_buffer, LPDWORD profile_length, PQUOTA_LIMITS quota_limits)
{
HMODULE hm = NULL;
+
typedef BOOL(WINAPI *LogonUserExExWType)(wchar_t*, wchar_t*, wchar_t*, DWORD, DWORD, PTOKEN_GROUPS, PHANDLE, PSID, PVOID, LPDWORD, PQUOTA_LIMITS);
static LogonUserExExWType s_pLogonUserExExW = NULL;
@@ -153,15 +168,84 @@ BOOLEAN pTranslateNameW(LPCWSTR name,
if ((s_pTranslateNameW = (TranslateNameWType)get_proc_address(hm, "TranslateNameW")) == NULL)
return FALSE;
- }
-
+ }
return s_pTranslateNameW(name, account_format, desired_name_format, translated_name, psize);
}
-ULONG pRtlNtStatusToDosError(NTSTATUS status)
+NTSTATUS pLsaOpenPolicy(PLSA_UNICODE_STRING system_name,
+ PLSA_OBJECT_ATTRIBUTES attrib,
+ ACCESS_MASK access,
+ PLSA_HANDLE handle)
{
HMODULE hm = NULL;
- typedef ULONG(NTAPI *RtlNtStatusToDosErrorType)(NTSTATUS);
+ typedef NTSTATUS(NTAPI *LsaOpenPolicyType)(PLSA_UNICODE_STRING, PLSA_OBJECT_ATTRIBUTES, ACCESS_MASK, PLSA_HANDLE);
+ static LsaOpenPolicyType s_pLsaOpenPolicy = NULL;
+ if (!s_pLsaOpenPolicy) {
+ if ((hm = load_api_security_lsapolicy()) == NULL &&
+ ((hm = load_advapi32()) == NULL))
+ return STATUS_ASSERTION_FAILURE;
+ if ((s_pLsaOpenPolicy = (LsaOpenPolicyType)get_proc_address(hm, "LsaOpenPolicy")) == NULL)
+ return STATUS_ASSERTION_FAILURE;
+ }
+ return s_pLsaOpenPolicy(system_name, attrib, access, handle);
+}
+NTSTATUS pLsaFreeMemory(PVOID buffer)
+{
+ HMODULE hm = NULL;
+ typedef NTSTATUS(NTAPI *LsaFreeMemoryType)(PVOID);
+ static LsaFreeMemoryType s_pLsaFreeMemory = NULL;
+ if (!s_pLsaFreeMemory) {
+ if ((hm = load_api_security_lsapolicy()) == NULL &&
+ ((hm = load_advapi32()) == NULL))
+ return STATUS_ASSERTION_FAILURE;
+ if ((s_pLsaFreeMemory = (LsaFreeMemoryType)get_proc_address(hm, "LsaFreeMemory")) == NULL)
+ return STATUS_ASSERTION_FAILURE;
+ }
+ return s_pLsaFreeMemory(buffer);
+}
+NTSTATUS pLsaAddAccountRights(LSA_HANDLE lsa_h,
+ PSID psid,
+ PLSA_UNICODE_STRING rights,
+ ULONG num_rights)
+{
+ HMODULE hm = NULL;
+ typedef NTSTATUS(NTAPI *LsaAddAccountRightsType)(LSA_HANDLE, PSID, PLSA_UNICODE_STRING, ULONG);
+ static LsaAddAccountRightsType s_pLsaAddAccountRights = NULL;
+ if (!s_pLsaAddAccountRights) {
+ if ((hm = load_api_security_lsapolicy()) == NULL &&
+ ((hm = load_advapi32()) == NULL))
+ return STATUS_ASSERTION_FAILURE;
+ if ((s_pLsaAddAccountRights = (LsaAddAccountRightsType)get_proc_address(hm, "LsaAddAccountRights")) == NULL)
+ return STATUS_ASSERTION_FAILURE;
+ }
+
+ return s_pLsaAddAccountRights(lsa_h, psid, rights, num_rights);
+}
+
+NTSTATUS pLsaRemoveAccountRights(LSA_HANDLE lsa_h,
+ PSID psid,
+ BOOLEAN all_rights,
+ PLSA_UNICODE_STRING rights,
+ ULONG num_rights)
+{
+ HMODULE hm = NULL;
+ typedef NTSTATUS(NTAPI *LsaRemoveAccountRightsType)(LSA_HANDLE, PSID, BOOLEAN, PLSA_UNICODE_STRING, ULONG);
+ static LsaRemoveAccountRightsType s_pLsaRemoveAccountRights = NULL;
+ if (!s_pLsaRemoveAccountRights) {
+ if ((hm = load_api_security_lsapolicy()) == NULL &&
+ ((hm = load_advapi32()) == NULL))
+ return STATUS_ASSERTION_FAILURE;
+ if ((s_pLsaRemoveAccountRights = (LsaRemoveAccountRightsType)get_proc_address(hm, "LsaRemoveAccountRights")) == NULL)
+ return STATUS_ASSERTION_FAILURE;
+ }
+
+ return s_pLsaRemoveAccountRights(lsa_h, psid, all_rights, rights, num_rights);
+}
+
+ULONG pRtlNtStatusToDosError(NTSTATUS status)
+{
+ HMODULE hm = NULL;
+ typedef ULONG(NTAPI *RtlNtStatusToDosErrorType)(NTSTATUS);
static RtlNtStatusToDosErrorType s_pRtlNtStatusToDosError = NULL;
if (!s_pRtlNtStatusToDosError) {
@@ -170,6 +254,24 @@ ULONG pRtlNtStatusToDosError(NTSTATUS status)
if ((s_pRtlNtStatusToDosError = (RtlNtStatusToDosErrorType)get_proc_address(hm, "RtlNtStatusToDosError")) == NULL)
return STATUS_ASSERTION_FAILURE;
- }
+ }
return pRtlNtStatusToDosError(status);
}
+
+NTSTATUS pLsaClose(LSA_HANDLE lsa_h)
+{
+ HMODULE hm = NULL;
+ typedef NTSTATUS(NTAPI *LsaCloseType)(LSA_HANDLE);
+ static LsaCloseType s_pLsaClose = NULL;
+
+ if (!s_pLsaClose) {
+ if ((hm = load_api_security_lsapolicy()) == NULL &&
+ ((hm = load_advapi32()) == NULL))
+ return STATUS_ASSERTION_FAILURE;
+
+ if ((s_pLsaClose = (LsaCloseType)get_proc_address(hm, "LsaClose")) == NULL)
+ return STATUS_ASSERTION_FAILURE;
+ }
+
+ return s_pLsaClose(lsa_h);
+}
diff --git a/contrib/win32/win32compat/w32api_proxies.h b/contrib/win32/win32compat/w32api_proxies.h
index 11073435c..a47d83b9c 100644
--- a/contrib/win32/win32compat/w32api_proxies.h
+++ b/contrib/win32/win32compat/w32api_proxies.h
@@ -15,8 +15,10 @@
BOOL pLogonUserExExW(wchar_t *, wchar_t *, wchar_t *, DWORD, DWORD, PTOKEN_GROUPS, PHANDLE, PSID *, PVOID *, LPDWORD, PQUOTA_LIMITS);
BOOLEAN pTranslateNameW(LPCWSTR, EXTENDED_NAME_FORMAT, EXTENDED_NAME_FORMAT, LPWSTR, PULONG);
NTSTATUS pLsaOpenPolicy(PLSA_UNICODE_STRING, PLSA_OBJECT_ATTRIBUTES, ACCESS_MASK, PLSA_HANDLE);
+NTSTATUS pLsaFreeMemory(PVOID);
NTSTATUS pLsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING, ULONG);
ULONG pRtlNtStatusToDosError(NTSTATUS);
-
+NTSTATUS pLsaClose(LSA_HANDLE);
+NTSTATUS pLsaRemoveAccountRights(LSA_HANDLE, PSID, BOOLEAN, PLSA_UNICODE_STRING, ULONG);
diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c
index 863061ff5..dc612a71c 100644
--- a/contrib/win32/win32compat/w32fd.c
+++ b/contrib/win32/win32compat/w32fd.c
@@ -33,6 +33,7 @@
#include "inc\sys\select.h"
#include "inc\sys\uio.h"
#include "inc\sys\types.h"
+#include "inc\sys\stat.h"
#include "inc\unistd.h"
#include "inc\fcntl.h"
#include "inc\sys\un.h"
@@ -73,6 +74,11 @@ void fd_decode_state(char*);
/* __progname */
char* __progname = "";
+/* __progdir */
+char* __progdir = "";
+wchar_t* __wprogdir = L"";
+
+
/* initializes mapping table*/
static int
fd_table_initialize()
@@ -176,41 +182,42 @@ fd_table_clear(int index)
FD_CLR(index, &(fd_table.occupied));
}
-/* TODO - consolidate w32_programdir logic in here */
-static int
+void
init_prog_paths()
{
wchar_t* wpgmptr;
- char* pgmptr;
+ static int processed = 0;
- if (_get_wpgmptr(&wpgmptr) != 0) {
- errno = EOTHER;
- return -1;
- }
+ if (processed)
+ return;
- if ((pgmptr = utf16_to_utf8(wpgmptr)) == NULL) {
- errno = ENOMEM;
- return -1;
- }
+ if (_get_wpgmptr(&wpgmptr) != 0)
+ fatal("unable to retrieve wpgmptr");
- __progname = strrchr(pgmptr, '\\') + 1;
- *(__progname - 1) = '\0';
+ if ((__wprogdir = _wcsdup(wpgmptr)) == NULL ||
+ (__progdir = utf16_to_utf8(__wprogdir)) == NULL)
+ fatal("out of memory");
+
+ __progname = strrchr(__progdir, '\\') + 1;
+ /* TODO: retain trailing \ at the end of progdir* variants ? */
+ *(strrchr(__progdir, '\\')) = '\0';
+ *(wcsrchr(__wprogdir, L'\\')) = L'\0';
/* strip .exe off __progname */
*(__progname + strlen(__progname) - 4) = '\0';
- return 0;
+ processed = 1;
}
void
w32posix_initialize()
{
+ init_prog_paths();
if ((fd_table_initialize() != 0) || (socketio_initialize() != 0))
DebugBreak();
main_thread = OpenThread(THREAD_SET_CONTEXT | SYNCHRONIZE, FALSE, GetCurrentThreadId());
if (main_thread == NULL ||
- sw_initialize() != 0 ||
- init_prog_paths() != 0 ) {
+ sw_initialize() != 0 ) {
DebugBreak();
fatal("failed to initialize w32posix wrapper");
}
@@ -962,6 +969,28 @@ w32_ftruncate(int fd, off_t length)
return 0;
}
+int w32_fchmod(int fd, mode_t mode)
+{
+ wchar_t *file_path;
+ char *file_path_utf8 = NULL;
+ int ret = -1;
+ CHECK_FD(fd);
+
+ file_path = get_final_path_by_handle(fd_table.w32_ios[fd]->handle);
+ if (!file_path)
+ goto cleanup;
+
+ if ((file_path_utf8 = utf16_to_utf8(file_path)) == NULL)
+ goto cleanup;
+
+ ret = w32_chmod(file_path_utf8, mode);
+cleanup:
+ if (file_path_utf8)
+ free(file_path_utf8);
+
+ return ret;
+}
+
int
w32_fsync(int fd)
{
@@ -1012,7 +1041,7 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL
/* compute total cmdline len*/
if (add_module_path)
- cmdline_len += (DWORD)strlen(w32_programdir()) + 1 + (DWORD)strlen(cmd) + 1 + 2;
+ cmdline_len += (DWORD)strlen(__progdir) + 1 + (DWORD)strlen(cmd) + 1 + 2;
else
cmdline_len += (DWORD)strlen(cmd) + 1 + 2;
@@ -1032,8 +1061,8 @@ spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDL
if (argv && argv[0])
*t++ = '\"';
if (add_module_path) {
- memcpy(t, w32_programdir(), strlen(w32_programdir()));
- t += strlen(w32_programdir());
+ memcpy(t, __progdir, strlen(__progdir));
+ t += strlen(__progdir);
*t++ = '\\';
}
diff --git a/contrib/win32/win32compat/win32_pty.c b/contrib/win32/win32compat/win32_pty.c
new file mode 100644
index 000000000..3d725065c
--- /dev/null
+++ b/contrib/win32/win32compat/win32_pty.c
@@ -0,0 +1,120 @@
+/*
+* Author: Balu G
+*
+* This file contains the conpty related functions.
+*
+* 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
+#include
+#include
+#include
+#include "Debug.h"
+#include "inc\fcntl.h"
+#include "misc_internal.h"
+
+// Return Value: 0 for success, -1 for failure
+int
+CreateConPty(const wchar_t *cmdline,
+ const unsigned short width,
+ const unsigned short height,
+ HANDLE const hInput,
+ HANDLE const hOutput,
+ HANDLE const tty_sighandle,
+ PROCESS_INFORMATION* const piPty)
+{
+ wchar_t system32_path[PATH_MAX] = { 0, };
+
+ SetHandleInformation(tty_sighandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+
+ wchar_t conhostCmdline[8191] = { 0, }; // msdn
+ wchar_t *cmd_fmt = L"%ls\\conhost.exe --headless --width %d --height %d --signal 0x%x -- %ls";
+
+ if (!GetSystemDirectoryW(system32_path, PATH_MAX))
+ fatal("unable to retrieve system32 path");
+
+ _snwprintf_s(conhostCmdline,
+ _countof(conhostCmdline),
+ _countof(conhostCmdline),
+ cmd_fmt,
+ system32_path,
+ width,
+ height,
+ tty_sighandle,
+ cmdline);
+
+ STARTUPINFOW si;
+ memset(&si, 0, sizeof(STARTUPINFOW));
+ si.cb = sizeof(STARTUPINFOW);
+ si.hStdInput = hInput;
+ si.hStdOutput = hOutput;
+ si.hStdError = hOutput;
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE | STARTF_USECOUNTCHARS;
+
+ debug3("pty commandline: %ls", conhostCmdline);
+
+ if (0 == CreateProcessW(NULL, conhostCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, piPty)) {
+ debug("%s - failed to execute %ls, error:%d", __func__, conhostCmdline, GetLastError());
+ errno = EOTHER;
+ return -1;
+ }
+
+ return 0;
+}
+
+int is_conpty_supported()
+{
+ /* TODO - enable this once conpty changes are validated */
+ return 0;
+}
+
+int exec_command_with_pty(wchar_t* cmd, STARTUPINFOW* si, PROCESS_INFORMATION* pi, int ttyfd)
+{
+ HANDLE ttyh = (HANDLE)w32_fd_to_handle(ttyfd);
+ wchar_t pty_cmdline[MAX_CMD_LEN] = { 0, };
+ int ret = -1;
+
+ if (is_conpty_supported())
+ return CreateConPty(cmd, (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);
+ /*
+ * 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;
+ debug3("pty commandline: %ls", pty_cmdline);
+
+ if (!CreateProcessW(NULL, pty_cmdline, NULL, NULL, TRUE, 0, NULL, NULL, si, pi)) {
+ debug("%s - failed to execute %ls, error:%d", __func__, pty_cmdline, GetLastError());
+ errno = EOTHER;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ return ret;
+}
\ No newline at end of file
diff --git a/contrib/win32/win32compat/win32_sshpty.c b/contrib/win32/win32compat/win32_sshpty.c
index f4f4e459a..8e1e6c461 100644
--- a/contrib/win32/win32compat/win32_sshpty.c
+++ b/contrib/win32/win32compat/win32_sshpty.c
@@ -6,23 +6,34 @@
#include
#include "..\..\..\sshpty.h"
-
+#include "inc\unistd.h"
+#include "misc_internal.h"
/*
* Windows versions of pty_*. Some of them are NO-OPs and should go
* away when pty logic is refactored and abstracted out
*
*/
+
+ /*
+ * allocates a control channel for Windows PTY
+ * ptyfd can be used to deliver Window size change events
+ */
int
pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
{
- /*
- * Simple console screen implementation in Win32 to give a
- * Unix like pty for interactive sessions
- */
+ int p[2];
*ttyfd = 0;
*ptyfd = 0;
- strlcpy(namebuf, "console", namebuflen);
+ if (w32_pipe(p) < 0)
+ return 0;
+
+ /* enable blocking mode io*/
+ unset_nonblock(p[0]);
+ unset_nonblock(p[1]);
+ *ttyfd = p[0];
+ *ptyfd = p[1];
+ strlcpy(namebuf, "windows-pty", namebuflen);
return 1;
}
@@ -38,8 +49,15 @@ pty_make_controlling_tty(int *ttyfd, const char *tty) {
void
pty_change_window_size(int ptyfd, u_int row, u_int col,
- u_int xpixel, u_int ypixel) {
- /* TODO - Need to implement*/
+ u_int xpixel, u_int ypixel)
+{
+ unsigned short signalPacket[3];
+ signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW;
+ signalPacket[1] = col;
+ signalPacket[2] = row;
+ // TODO - xpixel, ypixel
+
+ w32_write(ptyfd, signalPacket, sizeof(signalPacket));
}
diff --git a/contrib/win32/win32compat/win32_usertoken_utils.c b/contrib/win32/win32compat/win32_usertoken_utils.c
index 7598cb9d0..f9838efd5 100644
--- a/contrib/win32/win32compat/win32_usertoken_utils.c
+++ b/contrib/win32/win32compat/win32_usertoken_utils.c
@@ -135,6 +135,7 @@ generate_s4u_user_token(wchar_t* user_cpn, int impersonation) {
/* lookup the user principal name for the account */
WCHAR domain_upn[MAX_UPN_LEN + 1];
+
if (lookup_principal_name(user_cpn, domain_upn) != 0) {
/* failure - fallback to NetBiosDomain\SamAccountName */
wcscpy_s(domain_upn, ARRAYSIZE(domain_upn), user_cpn);
@@ -488,7 +489,7 @@ add_sid_mapping_to_lsa(PUNICODE_STRING domain_name,
}
if (p_output) {
- status = LsaFreeMemory(p_output);
+ status = pLsaFreeMemory(p_output);
if (status != STATUS_SUCCESS)
debug3("LsaFreeMemory failed with ntstatus: %d", status);
}
@@ -517,7 +518,7 @@ int remove_virtual_account_lsa_mapping(PUNICODE_STRING domain_name,
ret = -1;
if (p_output) {
- status = LsaFreeMemory(p_output);
+ status = pLsaFreeMemory(p_output);
if (status != STATUS_SUCCESS)
debug3("LsaFreeMemory failed with ntstatus: %d", status);
}
@@ -623,7 +624,7 @@ HANDLE generate_sshd_virtual_token()
/* assign service logon privilege to virtual account */
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
- if ((lsa_ret = LsaOpenPolicy(NULL, &ObjectAttributes,
+ if ((lsa_ret = pLsaOpenPolicy(NULL, &ObjectAttributes,
POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
&lsa_policy)) != STATUS_SUCCESS) {
error("%s: unable to open policy handle, error: %d",
@@ -632,7 +633,7 @@ HANDLE generate_sshd_virtual_token()
}
/* alter security to allow policy to account to logon as a service */
- if ((lsa_add_ret = LsaAddAccountRights(lsa_policy, sid_user, &svcLogonRight, 1)) != STATUS_SUCCESS) {
+ if ((lsa_add_ret = pLsaAddAccountRights(lsa_policy, sid_user, &svcLogonRight, 1)) != STATUS_SUCCESS) {
error("%s: unable to assign SE_SERVICE_LOGON_NAME privilege, error: %d",
__FUNCTION__, (ULONG)pRtlNtStatusToDosError(lsa_add_ret));
goto cleanup;
@@ -656,7 +657,7 @@ cleanup:
/* attempt to remove virtual account permissions if previous add succeeded */
if (lsa_add_ret == STATUS_SUCCESS)
- if ((lsa_ret = LsaRemoveAccountRights(lsa_policy, sid_user, FALSE, &svcLogonRight, 1)) != STATUS_SUCCESS)
+ if ((lsa_ret = pLsaRemoveAccountRights(lsa_policy, sid_user, FALSE, &svcLogonRight, 1)) != STATUS_SUCCESS)
debug("%s: unable to remove SE_SERVICE_LOGON_NAME privilege, error: %d", __FUNCTION__, pRtlNtStatusToDosError(lsa_ret));
if (sid_domain)
@@ -666,7 +667,7 @@ cleanup:
if (sid_group)
FreeSid(sid_group);
if (lsa_policy)
- LsaClose(lsa_policy);
+ pLsaClose(lsa_policy);
return va_token_restricted;
}
@@ -709,6 +710,24 @@ get_custom_lsa_package()
return s_lsa_auth_pkg;
}
+/*
+ * Not thread safe
+ * returned value is pointer from static buffer
+ * dont free()
+ */
+wchar_t* get_final_path_by_handle(HANDLE h)
+{
+ static wchar_t path_buf[PATH_MAX];
+
+ if (GetFinalPathNameByHandleW(h, path_buf, PATH_MAX, 0) == 0) {
+ errno = EOTHER;
+ debug3("failed to get final path of file with handle:%d error:%d", h, GetLastError());
+ return NULL;
+ }
+
+ return (path_buf + 4);
+}
+
/* using the netbiosname\samaccountname as an input, lookup the upn for the user.
* if no explicit upn is defined, implicit upn is returned (samaccountname@fqdn) */
int lookup_principal_name(const wchar_t * sam_account_name, wchar_t * user_principal_name)
diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c
index 6177a495f..1e40a8eda 100644
--- a/contrib/win32/win32compat/wmain_sshd.c
+++ b/contrib/win32/win32compat/wmain_sshd.c
@@ -189,7 +189,7 @@ create_prgdata_ssh_folder()
wcscat_s(sshd_config_path, _countof(sshd_config_path), L"\\sshd_config");
if (GetFileAttributesW(sshd_config_path) == INVALID_FILE_ATTRIBUTES) {
wchar_t sshd_config_default_path[PATH_MAX] = { 0, };
- swprintf_s(sshd_config_default_path, PATH_MAX, L"%S\\%s", w32_programdir(), L"sshd_config_default");
+ swprintf_s(sshd_config_default_path, PATH_MAX, L"%S\\%s", __progdir, L"sshd_config_default");
if (CopyFileW(sshd_config_default_path, sshd_config_path, TRUE) == 0) {
printf("Failed to copy %s to %s, error:%d", sshd_config_default_path, sshd_config_path, GetLastError());
@@ -264,11 +264,9 @@ int wmain(int argc, wchar_t **wargv) {
argc_original = argc;
wargv_original = wargv;
+ init_prog_paths();
/* change current directory to sshd.exe root */
- if ( (path_utf16 = utf8_to_utf16(w32_programdir())) == NULL)
- return -1;
- _wchdir(path_utf16);
- free(path_utf16);
+ _wchdir(__wprogdir);
if (!StartServiceCtrlDispatcherW(dispatch_table)) {
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
diff --git a/hostfile.c b/hostfile.c
index 6a485d1f7..12f174ff9 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -524,11 +524,6 @@ int
hostfile_replace_entries(const char *filename, const char *host, const char *ip,
struct sshkey **keys, size_t nkeys, int store_hash, int quiet, int hash_alg)
{
-#ifdef WINDOWS
- error("replacing host file entries is not supported in Windows yet");
- errno = ENOTSUP;
- return -1;
-#else /* !WINDOWS */
int r, fd, oerrno = 0;
int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE;
struct host_delete_ctx ctx;
@@ -643,7 +638,6 @@ hostfile_replace_entries(const char *filename, const char *host, const char *ip,
if (r == SSH_ERR_SYSTEM_ERROR)
errno = oerrno;
return r;
-#endif /* !WINDOWS */
}
static int
diff --git a/regress/pesterTests/KeyUtils.Tests.ps1 b/regress/pesterTests/KeyUtils.Tests.ps1
index da680851d..9de1ff851 100644
--- a/regress/pesterTests/KeyUtils.Tests.ps1
+++ b/regress/pesterTests/KeyUtils.Tests.ps1
@@ -295,6 +295,30 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" {
}
}
+ Context "$tC ssh-keygen known_hosts operations" {
+
+ BeforeAll {$tI=1}
+ AfterAll{$tC++}
+
+ It "$tC.$tI - list and delete host key thumbprints" {
+ $kh = Join-Path $testDir "$tC.$tI.known_hosts"
+ $entry = "[localhost]:47002 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMtJMxwn+iJU0X4+EC7PSj/cfcMbdP6ahhodtXx+6RHv sshtest_hostkey_ed25519"
+ $entry | Set-Content $kh
+ $o = ssh-keygen -F [localhost]:47002 -f $kh
+ $o.Count | Should Be 2
+ $o[1] | Should Be $entry
+
+ $o = ssh-keygen -H -F [localhost]:47002 -f $kh
+ $o.StartsWith("|1|") | Should Be $true
+
+ $o = ssh-keygen -R [localhost]:47002 -f $kh
+ $o.count | Should Be 3
+ $o[0] | Should Be "# Host [localhost]:47002 found: line 1"
+ (dir $kh).Length | Should Be 0
+ }
+
+ }
+
Context "$tC-ssh-add key files with different file perms" {
BeforeAll {
$keyFileName = "sshadd_userPermTestkey_ed25519"
diff --git a/regress/pesterTests/SSH.Tests.ps1 b/regress/pesterTests/SSH.Tests.ps1
index 81f764326..b1d2aaa64 100644
--- a/regress/pesterTests/SSH.Tests.ps1
+++ b/regress/pesterTests/SSH.Tests.ps1
@@ -81,14 +81,16 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
param
(
[string] $default_shell_path,
- [string] $default_shell_cmd_option_val
+ [string] $default_shell_cmd_option_val = $null
)
if (!(Test-Path $dfltShellRegPath)) {
New-Item -Path $dfltShellRegPath -Force | Out-Null
}
New-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -Value $default_shell_path -PropertyType String -Force
- New-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -Value $default_shell_cmd_option_val -PropertyType String -Force
+ if ($default_shell_cmd_option_val -ne $null) {
+ New-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -Value $default_shell_cmd_option_val -PropertyType String -Force
+ }
}
}
@@ -148,6 +150,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$o | Should Be "1234"
}
+ It "$tC.$tI - 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"
+ }
+
It "$tC.$tI - stdin from PS object" -skip:$skip {
# execute this script that dumps the length of input data, on the remote end
$str = "begin {} process { Write-Output `$input.Length} end { }"
@@ -211,6 +219,14 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$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\"
+ $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"
+ }
}
Context "$tC - cmdline parameters" {
@@ -271,105 +287,15 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$o | Should Be "1234"
$logFile | Should Contain "[::1]"
}
- }
-
-
- <#Context "Key is not secured in ssh-agent on server" {
- BeforeAll {
- $identifyFile = $client.clientPrivateKeyPaths[0]
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- AfterEach {
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- It '' -TestCases:$testData1 {
- param([string]$Title, $LogonStr, $Options, $SkipVerification = $false)
-
- $str = $ExecutionContext.InvokeCommand.ExpandString(".\ssh $($Options) $($LogonStr) hostname > $filePath")
- $client.RunCmd($str)
- #validate file content.
- Get-Content $filePath | Should be $server.MachineName
+ It "$tC.$tI - auto populate known hosts" {
+
+ $kh = Join-Path $testDir "$tC.$tI.known_hosts"
+ $nul | Set-Content $kh
+ # doing via cmd to intercept and drain stderr output
+ iex "cmd /c `"ssh -o UserKnownHostsFile=`"$kh`" -o StrictHostKeyChecking=no test_target hostname 2>&1`""
+ (Get-Content $kh).Count | Should Be 1
}
}
- Context "Key is secured in ssh-agent" {
- BeforeAll {
- $server.SecureHostKeys($server.PrivateHostKeyPaths)
- $identifyFile = $client.clientPrivateKeyPaths[0]
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- AfterAll {
- $Server.CleanupHostKeys()
- }
-
- AfterEach {
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- It '' -TestCases:$testData1 {
- param([string]$Title, $LogonStr, $Options, $SkipVerification = $false)
-
- $str = $ExecutionContext.InvokeCommand.ExpandString(".\ssh $Options $LogonStr hostname > $filePath")
- $client.RunCmd($str)
- #validate file content.
- Get-Content $filePath | Should be $server.MachineName
- }
- }
-
- Context "Single signon on client and keys secured in ssh-agent on server" {
- BeforeAll {
- $Server.SecureHostKeys($server.PrivateHostKeyPaths)
- $identifyFile = $client.clientPrivateKeyPaths[0]
- #setup single signon
- .\ssh-add.exe $identifyFile
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- AfterAll {
- $Server.CleanupHostKeys()
-
- #cleanup single signon
- .\ssh-add.exe -D
- }
-
- AfterEach {
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- It '' -TestCases:$testData {
- param([string]$Title, $LogonStr, $Options)
-
- $str = ".\ssh $($Options) $($LogonStr) hostname > $filePath"
- $client.RunCmd($str)
- #validate file content.
- Get-Content $filePath | Should be $server.MachineName
- }
- }
- Context "password authentication" {
- BeforeAll {
- $client.AddPasswordSetting($server.localAdminPassword)
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- AfterAll {
- $client.CleanupPasswordSetting()
- }
-
- AfterEach {
- Remove-Item -Path $filePath -Force -ea silentlycontinue
- }
-
- It '' -TestCases:$testData {
- param([string]$Title, $LogonStr, $Options)
-
- $str = ".\ssh $($Options) $($LogonStr) hostname > $filePath"
- $client.RunCmd($str)
- #validate file content.
- Get-Content $filePath | Should be $server.MachineName
- }
- }#>
}
diff --git a/regress/pesterTests/ShellHost.Tests.ps1 b/regress/pesterTests/ShellHost.Tests.ps1
new file mode 100644
index 000000000..a85edf22f
--- /dev/null
+++ b/regress/pesterTests/ShellHost.Tests.ps1
@@ -0,0 +1,42 @@
+$tC = 1
+$tI = 0
+$suite = "shellhost"
+
+Describe "E2E scenarios for ssh-shellhost" -Tags "CI" {
+ BeforeAll {
+ }
+
+ BeforeEach {
+ }
+
+ AfterEach {$tI++;}
+
+ Context "$tC - shellhost commandline scenarios" {
+ BeforeAll {$tI=1}
+ AfterAll{$tC++}
+
+ It "$tC.$tI - exit code tests" -skip:$skip {
+ foreach ($i in (0,1,4,5,44)) {
+ ssh-shellhost -c cmd /c exit $i
+ $LASTEXITCODE | Should Be $i
+ }
+ }
+
+ It "$tC.$tI - various quote tests" -skip:$skip {
+ $o = ssh-shellhost -c cmd /c echo hello
+ $o | Should Be "hello"
+ $o = ssh-shellhost -c `"cmd /c echo hello`"
+ $o | Should Be "hello"
+ $o = ssh-shellhost -c cmd /c echo `"hello`"
+ $o | Should Be "`"hello`""
+ $o = ssh-shellhost -c `"cmd /c echo `"hello`"`"
+ $o | Should Be "`"hello`""
+ $o = ssh-shellhost -c `"cmd /c echo `"hello`"
+ $o | Should Be "`"hello"
+ $o = ssh-shellhost -c `"`"cmd`" /c echo `"hello`"`"
+ $o | Should Be "`"hello`""
+
+ }
+
+ }
+}
diff --git a/regress/unittests/win32compat/miscellaneous_tests.c b/regress/unittests/win32compat/miscellaneous_tests.c
index f82ad5d88..7dd438253 100644
--- a/regress/unittests/win32compat/miscellaneous_tests.c
+++ b/regress/unittests/win32compat/miscellaneous_tests.c
@@ -64,7 +64,7 @@ test_sanitizedpath()
{
TEST_START("win32 program dir");
- char *win32prgdir_utf8 = w32_programdir();
+ char *win32prgdir_utf8 = __progdir;
ASSERT_PTR_NE(win32prgdir_utf8, NULL);
ASSERT_PTR_EQ(resolved_path_utf16(NULL), NULL);
@@ -314,126 +314,92 @@ test_chroot()
void
test_build_session_commandline()
{
- char *progdir = w32_programdir(), *out, buf[PATH_MAX*2], shellhost_path[PATH_MAX];
- shellhost_path[0] = '\0';
- strcat(shellhost_path, "\"");
- strcat(shellhost_path, progdir);
- strcat(shellhost_path, "\\ssh-shellhost.exe\"");
- int shellhost_path_len = (int)strlen(shellhost_path);
+ char *progdir = __progdir, *out, buf[PATH_MAX * 2];
TEST_START("default interactive session tests");
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, NULL, 0);
+ out = build_session_commandline("c:\\system32\\cmd.exe", NULL, NULL);
ASSERT_STRING_EQ(out, "\"c:\\system32\\cmd.exe\"");
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, NULL, 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\system32\\cmd.exe\"");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
TEST_DONE();
TEST_START("cmd shell tests");
buf[0] = '\0';
- strcat(buf, "\"c:\\system32\\cmd.exe\" /c \"");
+ 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", 0);
+ out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "internal-sftp -arg");
buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg");
+ strcat(buf, "\\sftp-server.exe\" -arg\"");
ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "SFTP-server.exe -arg", 0);
+ out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "SFTP-server.exe -arg");
buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg");
+ strcat(buf, "\\sftp-server.exe\" -arg\"");
ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sftp-SERVER -arg", 0);
+ out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sftp-SERVER -arg");
buf[len_pg] = '\0';
- strcat(buf, "\\sftp-server.exe\" -arg");
+ strcat(buf, "\\sftp-server.exe\" -arg\"");
ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sCp -arg", 0);
+ out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "sCp -arg");
buf[len_pg] = '\0';
- strcat(buf, "\\scp.exe\" -arg");
+ strcat(buf, "\\scp.exe\" -arg\"");
ASSERT_STRING_EQ(out, buf);
- out = build_session_commandline("c:\\system32\\cmd.exe", NULL, "mycommand -arg", 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\system32\\cmd.exe\" /c mycommand -arg");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
free(out);
-
TEST_DONE();
TEST_START("bash shell tests");
- out = build_session_commandline("c:\\system32\\bash.exe", NULL, "internal-sftp -arg", 0);
+ 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", 0);
+ 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", 0);
+ 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", 0);
+ 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:\\bash", "-custom", "mycommand -arg", 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\bash\" -custom \"mycommand -arg\"");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
- free(out);
- out = build_session_commandline("c:\\cygwin\\bash.exe", NULL, "internal-sftp -arg", 0);
+ 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", 0);
+ 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", 0);
+ 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", 0);
+ out = build_session_commandline("c:\\cygwin\\bash", NULL, "sCp -arg");
ASSERT_STRING_EQ(out, "\"c:\\cygwin\\bash\" -c \"scp.exe -arg\"");
free(out);
- out = build_session_commandline("c:\\cygwin\\bash", "-custom", "mycommand -arg", 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\cygwin\\bash\" -custom \"mycommand -arg\"");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
- free(out);
TEST_DONE();
TEST_START("powershell shell tests");
- out = build_session_commandline("c:\\powershell.exe", NULL, "internal-sftp -arg", 0);
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\powershell\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\powershell.exe\" -c scp.exe -arg");
- free(out);
- out = build_session_commandline("c:\\powershell.exe", "-custom", "mycommand -arg", 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\powershell.exe\" -custom mycommand -arg");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\myshell.exe\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c sftp-server.exe -arg");
+ 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", 0);
- ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c scp.exe -arg");
- free(out);
- out = build_session_commandline("c:\\myshell", "-custom", "mycommand -arg", 1);
- ASSERT_STRING_EQ(out + shellhost_path_len + 1, "\"c:\\myshell\" -custom mycommand -arg");
- out[shellhost_path_len] = '\0';
- ASSERT_STRING_EQ(out, shellhost_path);
+ out = build_session_commandline("c:\\myshell", NULL, "sCp -arg");
+ ASSERT_STRING_EQ(out, "\"c:\\myshell\" -c \"scp.exe -arg\"");
free(out);
TEST_DONE();
}
diff --git a/session.c b/session.c
index 14fa16214..b608dcb84 100644
--- a/session.c
+++ b/session.c
@@ -537,7 +537,7 @@ cleanup:
}
int register_child(void* child, unsigned long pid);
-char* build_session_commandline(const char *, const char *, const char *, int);
+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;
@@ -582,7 +582,7 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
pty = 0;
}
- exec_command = build_session_commandline(s->pw->pw_shell, shell_command_option, command, pty);
+ exec_command = build_session_commandline(s->pw->pw_shell, shell_command_option, command);
if (exec_command == NULL)
goto cleanup;
@@ -606,11 +606,18 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
si.hStdError = (HANDLE)w32_fd_to_handle(pipeerr[1]);
si.lpDesktop = NULL;
- debug("Executing command: %s", exec_command);
if ((exec_command_w = utf8_to_utf16(exec_command)) == NULL)
goto cleanup;
- if (!CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ 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;
@@ -641,12 +648,6 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
register_child(pi.hProcess, pi.dwProcessId);
}
- /*
- * Set interactive/non-interactive mode.
- */
- packet_set_interactive(s->display != NULL, options.ip_qos_interactive,
- options.ip_qos_bulk);
-
/* Close the child sides of the socket pairs. */
close(pipein[0]);
close(pipeout[1]);
@@ -656,10 +657,17 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) {
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
*/
- if (s->ttyfd == -1)
+ 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);
- else
- session_set_fds(ssh, s, pipein[1], pipeout[0], pipeerr[0], s->is_subsystem, 1); /* tty interactive session */
+ }
ret = 0;
diff --git a/ssh-keygen.c b/ssh-keygen.c
index b0f749dc9..e0f436219 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1053,19 +1053,12 @@ do_gen_all_hostkeys(struct passwd *pw)
pub_tmp, strerror(errno));
goto failnext;
}
-#ifdef WINDOWS
- /* Windows POSIX adpater does not support fdopen() on open(file)*/
- close(fd);
- chmod(pub_tmp, 0644);
- if ((f = fopen(pub_tmp, "w")) == NULL) {
- error("fopen %s failed: %s", pub_tmp, strerror(errno));
-#else /* !WINDOWS */
+
(void)fchmod(fd, 0644);
f = fdopen(fd, "w");
if (f == NULL) {
error("fdopen %s failed: %s", pub_tmp, strerror(errno));
close(fd);
-#endif /* !WINDOWS */
sshkey_free(public);
first = 0;
continue;
@@ -1232,10 +1225,6 @@ known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
static void
do_known_hosts(struct passwd *pw, const char *name)
{
-#ifdef WINDOWS
- fatal("Updating known_hosts is not supported in Windows yet.");
-#else /* !WINDOWS */
-
char *cp, tmp[PATH_MAX], old[PATH_MAX];
int r, fd, oerrno, inplace = 0;
struct known_hosts_ctx ctx;
@@ -1327,7 +1316,6 @@ do_known_hosts(struct passwd *pw, const char *name)
}
exit (find_host && !ctx.found_key);
-#endif /* !WINDOWS */
}
/*
@@ -1530,16 +1518,9 @@ do_change_comment(struct passwd *pw)
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
fatal("Could not save your public key in %s", identity_file);
-#ifdef WINDOWS
- /* Windows POSIX adpater does not support fdopen() on open(file)*/
- close(fd);
- if ((f = fopen(identity_file, "w")) == NULL)
- fatal("fopen %s failed: %s", identity_file, strerror(errno));
-#else /* !WINDOWS */
f = fdopen(fd, "w");
if (f == NULL)
fatal("fdopen %s failed: %s", identity_file, strerror(errno));
-#endif /* !WINDOWS */
if ((r = sshkey_write(public, f)) != 0)
fatal("write key failed: %s", ssh_err(r));
sshkey_free(public);
@@ -1784,15 +1765,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
fatal("Could not open \"%s\" for writing: %s", out,
strerror(errno));
-#ifdef WINDOWS
- /* Windows POSIX adpater does not support fdopen() on open(file)*/
- close(fd);
- if ((f = fopen(out, "w")) == NULL)
- fatal("fopen %s failed: %s", identity_file, strerror(errno));
-#else /* !WINDOWS */
if ((f = fdopen(fd, "w")) == NULL)
fatal("%s: fdopen: %s", __func__, strerror(errno));
-#endif /* !WINDOWS */
if ((r = sshkey_write(public, f)) != 0)
fatal("Could not write certified key to %s: %s",
out, ssh_err(r));
@@ -2851,15 +2825,8 @@ passphrase_again:
if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
fatal("Unable to save public key to %s: %s",
identity_file, strerror(errno));
-#ifdef WINDOWS
- /* Windows POSIX adpater does not support fdopen() on open(file)*/
- close(fd);
- if ((f = fopen(identity_file, "w")) == NULL)
- fatal("fopen %s failed: %s", identity_file, strerror(errno));
-#else /* !WINDOWS */
if ((f = fdopen(fd, "w")) == NULL)
fatal("fdopen %s failed: %s", identity_file, strerror(errno));
-#endif /* !WINDOWS */
if ((r = sshkey_write(public, f)) != 0)
error("write key failed: %s", ssh_err(r));
fprintf(f, " %s\n", comment);
diff --git a/sshd.c b/sshd.c
index 9d5b3235a..c042b6d07 100644
--- a/sshd.c
+++ b/sshd.c
@@ -786,6 +786,9 @@ privsep_preauth(Authctxt *authctxt)
pmonitor->m_recvfd = PRIVSEP_MONITOR_FD;
pmonitor->m_log_sendfd = PRIVSEP_LOG_FD;
+
+ fcntl(pmonitor->m_recvfd, F_SETFD, FD_CLOEXEC);
+ fcntl(pmonitor->m_log_sendfd, F_SETFD, FD_CLOEXEC);
/* Arrange for logging to be sent to the monitor */
set_log_handler(mm_log_handler, pmonitor);
@@ -948,6 +951,7 @@ privsep_postauth(Authctxt *authctxt)
close(pmonitor->m_recvfd);
pmonitor->m_recvfd = PRIVSEP_MONITOR_FD;
+ fcntl(pmonitor->m_recvfd, F_SETFD, FD_CLOEXEC);
monitor_recv_keystate(pmonitor);
do_setusercontext(authctxt->pw);