From f8077a9f8e1bad756de5c1f990ab9df03e388cf0 Mon Sep 17 00:00:00 2001 From: bagajjal Date: Thu, 8 Jun 2017 11:48:54 -0700 Subject: [PATCH] Added support for SFTP only server for Windows https://github.com/PowerShell/Win32-OpenSSH/issues/757 --- session.c | 81 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/session.c b/session.c index 5904d203e..9a7ae956d 100644 --- a/session.c +++ b/session.c @@ -367,11 +367,18 @@ int register_child(void* child, unsigned long pid); int do_exec_windows(Session *s, const char *command, int pty) { int pipein[2], pipeout[2], pipeerr[2], r; - char *exec_command = NULL, *progdir = w32_programdir(); + char *exec_command = NULL, *progdir = w32_programdir(), *cmd = NULL, *shell_host = NULL, *command_b64 = NULL, *argp = NULL; wchar_t *exec_command_w = NULL, *pw_dir_w; + const char *sftp_exe = "sftp-server.exe"; + size_t command_b64_len = 0; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + BOOL create_process_ret_val; + HANDLE hToken = INVALID_HANDLE_VALUE; + extern int debug_flag; if (s->is_subsystem >= SUBSYSTEM_INT_SFTP_ERROR) { - error("sub system not supported, exiting"); + error("This service allows sftp connections only.\n"); fflush(NULL); exit(1); } @@ -383,7 +390,6 @@ int do_exec_windows(Session *s, const char *command, int pty) { if ((pw_dir_w = utf8_to_utf16(s->pw->pw_dir)) == NULL) fatal("%s: out of memory", __func__); - set_nonblock(pipein[0]); set_nonblock(pipein[1]); set_nonblock(pipeout[0]); @@ -407,12 +413,39 @@ int do_exec_windows(Session *s, const char *command, int pty) { if (command[1] == ':') /* absolute */ exec_command = xstrdup(command); else {/*relative*/ - exec_command = malloc(strlen(progdir) + 1 + strlen(command)); + const int command_len = strlen(progdir) + 1 + strlen(command) + (strlen(sftp_exe) - strlen(INTERNAL_SFTP_NAME)); + exec_command = malloc(command_len); if (exec_command == NULL) fatal("%s, out of memory", __func__); - memcpy(exec_command, progdir, strlen(progdir)); - exec_command[strlen(progdir)] = '\\'; - memcpy(exec_command + strlen(progdir) + 1, command, strlen(command) + 1); + + cmd = exec_command; + memcpy(cmd, progdir, strlen(progdir)); + cmd += strlen(progdir); + *cmd++ = '\\'; + + /* In windows, INTERNAL_SFTP is supported via sftp-server.exe. + * This is a deviation from the UNIX implementation that hosts sftp-server within sshd. + * If sftp-server were to be hosted within sshd for Windows, following would be needed + * - Impersonate client user + * - call sftp-server-main + * + * SSHD service account would need impersonate privilege to impersonate client user, + * thereby needing elevation of SSHD account privileges + * Apart from slight performance gain (by hosting sftp in process), there isn't a clear + * gain with this option over using and spawning sftp-server.exe. + * Hence going with the later option. + */ + if(IS_INTERNAL_SFTP(command)) { + memcpy(cmd, sftp_exe, strlen(sftp_exe) + 1); + cmd += strlen(sftp_exe); + + // copy the arguments (if any). + if(strlen(command) > strlen(INTERNAL_SFTP_NAME)) { + argp = command + strlen(INTERNAL_SFTP_NAME); + memcpy(cmd, argp, strlen(argp)+1); + } + } else + memcpy(cmd, command, strlen(command) + 1); } } else { /* @@ -420,9 +453,7 @@ int do_exec_windows(Session *s, const char *command, int pty) { * command is base64 encoded to preserve original special charecters like '"' * else they will get lost in CreateProcess translation */ - char *shell_host = pty ? "ssh-shellhost.exe " : "ssh-shellhost.exe -nopty ", *c; - char *command_b64 = NULL; - size_t command_b64_len = 0; + shell_host = pty ? "ssh-shellhost.exe " : "ssh-shellhost.exe -nopty "; if (command) { /* accomodate bas64 encoding bloat and null terminator */ command_b64_len = ((strlen(command) + 2) / 3) * 4 + 1; @@ -433,27 +464,21 @@ int do_exec_windows(Session *s, const char *command, int pty) { exec_command = malloc(strlen(progdir) + 1 + strlen(shell_host) + (command_b64 ? strlen(command_b64): 0) + 1); if (exec_command == NULL) fatal("%s, out of memory", __func__); - c = exec_command; - memcpy(c, progdir, strlen(progdir)); - c += strlen(progdir); - *c++ = '\\'; - memcpy(c, shell_host, strlen(shell_host)); - c += strlen(shell_host); + cmd = exec_command; + memcpy(cmd, progdir, strlen(progdir)); + cmd += strlen(progdir); + *cmd++ = '\\'; + memcpy(cmd, shell_host, strlen(shell_host)); + cmd += strlen(shell_host); if (command_b64) { - memcpy(c, command_b64, strlen(command_b64)); - c += strlen(command_b64); + memcpy(cmd, command_b64, strlen(command_b64)); + cmd += strlen(command_b64); } - *c = '\0'; + *cmd = '\0'; } /* start the process */ { - PROCESS_INFORMATION pi; - STARTUPINFOW si; - BOOL b; - HANDLE hToken = INVALID_HANDLE_VALUE; - extern int debug_flag; - memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwXSize = 5; @@ -478,11 +503,11 @@ int do_exec_windows(Session *s, const char *command, int pty) { /* in debug mode launch using sshd.exe user context */ if (debug_flag) - b = CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, + create_process_ret_val = CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, pw_dir_w, &si, &pi); else /* launch as client user context */ - b = CreateProcessAsUserW(hToken, NULL, exec_command_w, NULL, NULL, TRUE, + create_process_ret_val = CreateProcessAsUserW(hToken, NULL, exec_command_w, NULL, NULL, TRUE, DETACHED_PROCESS , NULL, pw_dir_w, &si, &pi); @@ -490,7 +515,7 @@ int do_exec_windows(Session *s, const char *command, int pty) { _putenv_s("SSH_ASYNC_STDOUT", ""); _putenv_s("SSH_ASYNC_STDERR", ""); - if (!b) + if (!create_process_ret_val) fatal("ERROR. Cannot create process (%u).\n", GetLastError()); CloseHandle(pi.hThread);