diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index 2f62fa855478..755ac9532da0 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -148,7 +148,8 @@ fileio_connect(struct w32_io* pio, char* name) break; debug4("waiting for agent connection, retrying after 1 sec"); - if ((ret = wait_for_any_event(NULL, 0, 1000) != 0) != 0) + ret = wait_for_any_event(NULL, 0, 1000); + if (ret != 0) goto cleanup; } while(1); diff --git a/contrib/win32/win32compat/socketio.c b/contrib/win32/win32compat/socketio.c index c4eceaea8f71..ad389d02aeb6 100644 --- a/contrib/win32/win32compat/socketio.c +++ b/contrib/win32/win32compat/socketio.c @@ -106,7 +106,7 @@ socketio_acceptEx(struct w32_io* pio) { struct acceptEx_context *context; struct sockaddr_storage addr; int addrlen = sizeof addr; - + debug5("acceptEx - io:%p", pio); context = (struct acceptEx_context *)pio->internal.context; ResetEvent(pio->read_overlapped.hEvent); @@ -114,7 +114,7 @@ socketio_acceptEx(struct w32_io* pio) if (getsockname(pio->sock, (struct sockaddr*)&addr, &addrlen) == SOCKET_ERROR) { errno = errno_from_WSALastError(); debug("acceptEx - getsockname() ERROR:%d, io:%p", WSAGetLastError(), pio); - return -1; + return -1; } /* create accepting socket */ @@ -165,6 +165,8 @@ CALLBACK WSARecvCompletionRoutine(IN DWORD dwError, pio->read_details.remaining = cbTransferred; pio->read_details.completed = 0; pio->read_details.pending = FALSE; + if (pio->read_overlapped.hEvent) + SetEvent(pio->read_overlapped.hEvent); } /* initiates async receive operation*/ @@ -198,6 +200,16 @@ socketio_WSARecv(struct w32_io* pio, BOOL* completed, int len) if (len) wsabuf.len = min((ULONG)len, wsabuf.len); + if (pio->read_overlapped.hEvent == NULL) { + pio->read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (pio->read_overlapped.hEvent == NULL) { + errno = ENOMEM; + debug3("WSARecv - CreateEvent() ERROR:%d, io:%p", errno, pio); + return -1; + } + } + ResetEvent(pio->read_overlapped.hEvent); + ret = WSARecv(pio->sock, &wsabuf, 1, NULL, &recv_flags, &pio->read_overlapped, &WSARecvCompletionRoutine); if (ret == 0) { pio->read_details.pending = TRUE; @@ -251,7 +263,7 @@ socketio_socket(int domain, int type, int protocol) debug3("%s - ERROR:%d", __FUNCTION__, WSAGetLastError()); \ } \ return ret; \ -} while (0) +} while (0) /* implements setsockopt() */ int @@ -363,7 +375,7 @@ int socketio_recv(struct w32_io* pio, void *buf, size_t len, int flags) { BOOL completed = FALSE; - errno_t r = 0; + errno_t r; debug5("recv - io:%p state:%d", pio, pio->internal.state); if ((buf == NULL) || (len == 0)) { @@ -393,7 +405,7 @@ socketio_recv(struct w32_io* pio, void *buf, size_t len, int flags) debug4("recv - io is already pending, io:%p", pio); return -1; } - } + } /* if we have some buffer copy it and return #bytes copied */ if (pio->read_details.remaining) { @@ -511,6 +523,12 @@ CALLBACK WSASendCompletionRoutine(IN DWORD dwError, pio->write_details.pending = FALSE; } +static BOOL +socketio_write_is_pending(struct w32_io* pio) +{ + return pio->write_details.pending; +} + /* implementation of send() */ int socketio_send(struct w32_io* pio, const void *buf, size_t len, int flags) @@ -518,7 +536,7 @@ socketio_send(struct w32_io* pio, const void *buf, size_t len, int flags) int ret = 0; WSABUF wsabuf; errno_t r = 0; - + debug5("send - io:%p state:%d", pio, pio->internal.state); if ((buf == NULL) || (len == 0)) { @@ -550,7 +568,11 @@ socketio_send(struct w32_io* pio, const void *buf, size_t len, int flags) if (pio->write_details.error) { errno = errno_from_WSAError(pio->write_details.error); - debug3("ERROR:%d, io:%p", pio->write_details.error, pio); + /* + * Logging can write through the Win32 fd table. A socket + * write-error path must return before logging so the log writer + * cannot re-enter socketio_send on the failed writer. + */ return -1; } @@ -600,10 +622,10 @@ socketio_send(struct w32_io* pio, const void *buf, size_t len, int flags) if (w32_io_is_blocking(pio)) { /* wait until io is done */ debug5("send - waiting as socket is in blocking mode, io:%p", pio); - while (pio->write_details.pending) + while (socketio_write_is_pending(pio)) if (wait_for_any_event(NULL, 0, INFINITE) == -1) { /* if interrupted but send has completed, we are good*/ - if ((errno != EINTR) || (pio->write_details.pending)) + if ((errno != EINTR) || socketio_write_is_pending(pio)) return -1; errno = 0; } @@ -653,6 +675,10 @@ socketio_close(struct w32_io* pio) if (pio->write_overlapped.hEvent) CloseHandle(pio->write_overlapped.hEvent); } else { + if (pio->read_overlapped.hEvent) + CloseHandle(pio->read_overlapped.hEvent); + if (pio->write_overlapped.hEvent) + CloseHandle(pio->write_overlapped.hEvent); if (pio->read_details.buf) free(pio->read_details.buf); @@ -669,7 +695,6 @@ struct w32_io* socketio_accept(struct w32_io* pio, struct sockaddr* addr, int* addrlen) { struct w32_io *accept_io = NULL; - int iResult = 0; struct acceptEx_context* context; struct sockaddr *local_address, *remote_address; int local_address_len, remote_address_len; @@ -786,7 +811,7 @@ socketio_connectex(struct w32_io* pio, const struct sockaddr* name, int namelen) if (SOCKET_ERROR == bind(pio->sock, tmp_addr, (int)tmp_addr_len)) { errno = errno_from_WSALastError(); - /* + /* * When use bind_address or bind_interface, this bind will return WSAEINVAL. But it doesn't matter. * https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind */ @@ -975,8 +1000,6 @@ socketio_on_select(struct w32_io* pio, BOOL rd) int w32_gethostname(char *name_utf8, size_t len) { - char* tmp_name_utf8 = NULL; - if (IsWindows8OrGreater()) { /* TODO - GetHostNameW not present in Win7, do GetProcAddr on Win8+*/ /* @@ -1003,9 +1026,8 @@ w32_gethostname(char *name_utf8, size_t len) void w32_freeaddrinfo(struct addrinfo *ai) { - struct addrinfo *cur; while (ai) { - cur = ai; + struct addrinfo *cur = ai; ai = ai->ai_next; if (cur->ai_addr) free(cur->ai_addr); diff --git a/contrib/win32/win32compat/w32-doexec.c b/contrib/win32/win32compat/w32-doexec.c index f996f7ebe5e0..ab26b714619e 100644 --- a/contrib/win32/win32compat/w32-doexec.c +++ b/contrib/win32/win32compat/w32-doexec.c @@ -53,6 +53,10 @@ #define SUBSYSTEM_INT_SFTP_ERROR 3 #endif +#ifndef SSHD_ENABLE_JOB_OBJECTS +#define SSHD_ENABLE_JOB_OBJECTS 1 +#endif + /* import */ extern ServerOptions options; extern struct sshauthopt *auth_opts; @@ -78,6 +82,10 @@ do_setup_env_proxy(struct ssh *, Session *, const char *); goto cleanup; \ } while(0) +#define SSHD_CHILD_PROCESS_ACCESS \ + (PROCESS_TERMINATE | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA | \ + PROCESS_QUERY_INFORMATION | SYNCHRONIZE) + static char* get_registry_operation_error_message(const LONG error_code) @@ -161,8 +169,8 @@ setup_session_user_vars(wchar_t* pw_dir_w) wchar_t* user_path = NULL; wchar_t* hklm_path = get_registry_key_value(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", L"PATH", &hklm_path_sz); wchar_t* hkcu_path = get_registry_key_value(HKEY_CURRENT_USER, L"Environment", L"PATH", &hkcu_path_sz); - DWORD user_path_sz = hklm_path_sz + hkcu_path_sz; if (hklm_path && hkcu_path) { + DWORD user_path_sz = hklm_path_sz + hkcu_path_sz; user_path = malloc(user_path_sz); if (user_path) { memcpy_s(user_path, user_path_sz, hklm_path, hklm_path_sz); @@ -194,8 +202,8 @@ setup_session_env(struct ssh *ssh, Session* s) _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 (size_t i = 0, j = 0; i < wcslen(tmp) && j < ARRAYSIZE(wbuf) - 1; i++) { - wbuf[j] = tmp[i]; + for (size_t tmp_index = 0, j = 0; tmp_index < wcslen(tmp) && j < ARRAYSIZE(wbuf) - 1; tmp_index++) { + wbuf[j] = tmp[tmp_index]; if (wbuf[j++] == L'$') wbuf[j++] = L'$'; } @@ -367,7 +375,12 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { exec_command = build_exec_command(command); debug3("exec_command: %s", exec_command); - if (shell_type == SH_PS || shell_type == SH_BASH || + if (s->is_subsystem && exec_command && + strstr(exec_command, "sftp-server.exe")) { + spawn_argv[0] = exec_command; + debug3("spawning sftp subsystem directly"); + } + else if (shell_type == SH_PS || shell_type == SH_BASH || shell_type == SH_CYGWIN || (shell_type == SH_OTHER) && arg_escape) { spawn_argv[0] = shell; @@ -439,12 +452,13 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { 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) { + if ((process_handle = OpenProcess(SSHD_CHILD_PROCESS_ACCESS, FALSE, pid)) == NULL) { errno = EOTHER; error("cannot get process handle: %d", GetLastError()); goto cleanup; } +#if SSHD_ENABLE_JOB_OBJECTS /* * assign job object to control processes spawned * 1. create job object @@ -461,6 +475,10 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { CloseHandle(process_handle); goto cleanup; } +#else + CloseHandle(process_handle); + process_handle = NULL; +#endif s->pid = pid; /* Close the child sides of the socket pairs. */ @@ -510,4 +528,4 @@ do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) { int do_exec_pty(struct ssh *ssh, Session *s, const char *command) { return do_exec_windows(ssh, s, command, 1); -} \ No newline at end of file +} diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index b4c436864dc4..8d4f9dd0cc54 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -73,6 +73,58 @@ void fd_decode_state(char*); #define POSIX_FD_STATE "c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_FD_STATE" #define POSIX_CHROOTW L"c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_CHROOT" +static const char * +w32_io_type_name(enum w32_io_type type) +{ + switch (type) { + case UNKNOWN_FD: + return "UNKNOWN_FD"; + case SOCK_FD: + return "SOCK_FD"; + case NONSOCK_FD: + return "NONSOCK_FD"; + case NONSOCK_SYNC_FD: + return "NONSOCK_SYNC_FD"; + default: + return "invalid"; + } +} + +static const char * +w32_sock_state_name(enum w32_io_sock_state state) +{ + switch (state) { + case SOCK_INITIALIZED: + return "SOCK_INITIALIZED"; + case SOCK_LISTENING: + return "SOCK_LISTENING"; + case SOCK_CONNECTING: + return "SOCK_CONNECTING"; + case SOCK_READY: + return "SOCK_READY"; + default: + return "invalid"; + } +} + +static void +debug3_fd_entry(const char *label, int fd) +{ + struct w32_io *pio; + + if (fd < 0 || fd >= MAX_FDS || fd_table.w32_ios[fd] == NULL) { + debug3("w32fd %s fd:%d empty", label, fd); + return; + } + + pio = fd_table.w32_ios[fd]; + debug3("w32fd %s fd:%d handle:%p type:%s state:%s pending:%d remaining:%lu error:%lu event:%p", + label, fd, pio->handle, w32_io_type_name(pio->type), + w32_sock_state_name(pio->internal.state), pio->read_details.pending, + (unsigned long)pio->read_details.remaining, + (unsigned long)pio->read_details.error, pio->read_overlapped.hEvent); +} + /* __progname */ char* __progname = ""; @@ -88,8 +140,6 @@ wchar_t* __wprogdata = L""; static int fd_table_initialize() { - struct w32_io *pio; - HANDLE wh; char *stdio_mode_env = NULL; int stdio_mode = NONSOCK_SYNC_FD; size_t len = 0; @@ -102,8 +152,12 @@ fd_table_initialize() stdio_mode = NONSOCK_FD; else if (strcmp(stdio_mode_env, "nonsock_sync") == 0) stdio_mode = NONSOCK_SYNC_FD; + debug3("w32fd stdio mode env:%s type:%s", + stdio_mode_env, w32_io_type_name(stdio_mode)); free(stdio_mode_env); - } + } else + debug3("w32fd stdio mode env: type:%s", + w32_io_type_name(stdio_mode)); /* table entries representing std in, out and error*/ DWORD wh_index[] = { STD_INPUT_HANDLE , STD_OUTPUT_HANDLE , STD_ERROR_HANDLE }; @@ -113,19 +167,20 @@ fd_table_initialize() /* prepare std io fds */ for (fd_num = STDIN_FILENO; fd_num <= STDERR_FILENO; fd_num++) { - wh = GetStdHandle(wh_index[fd_num]); + HANDLE wh = GetStdHandle(wh_index[fd_num]); if (wh != NULL && wh != INVALID_HANDLE_VALUE) { - pio = malloc(sizeof(struct w32_io)); + struct w32_io *pio = malloc(sizeof(struct w32_io)); if (!pio) { errno = ENOMEM; return -1; } memset(pio, 0, sizeof(struct w32_io)); - pio->type = stdio_mode; - pio->handle = wh; - fd_table_set(pio, fd_num); + pio->type = stdio_mode; + pio->handle = wh; + fd_table_set(pio, fd_num); + debug3_fd_entry("init std", fd_num); + } } - } /* decode fd state if any */ @@ -138,10 +193,16 @@ fd_table_initialize() */ if ((_dupenv_s(&posix_fd_state, NULL, POSIX_FD_STATE) == 0) && (NULL != posix_fd_state)) { + debug3("w32fd POSIX_FD_STATE present len:%llu", + (unsigned long long)strlen(posix_fd_state)); fd_decode_state(posix_fd_state); + debug3_fd_entry("after decode std", STDIN_FILENO); + debug3_fd_entry("after decode std", STDOUT_FILENO); + debug3_fd_entry("after decode std", STDERR_FILENO); free(posix_fd_state); _putenv_s(POSIX_FD_STATE, ""); - } + } else + debug3("w32fd POSIX_FD_STATE absent"); } /* decode chroot if any */ @@ -694,11 +755,11 @@ w32_io_process_fd_flags(struct w32_io* pio, int flags) int w32_fcntl(int fd, int cmd, ... /* arg */) { - va_list valist; - va_start(valist, cmd); int ret = 0; + va_list valist; CHECK_FD(fd); + va_start(valist, cmd); switch (cmd) { case F_GETFL: @@ -732,7 +793,7 @@ w32_fcntl(int fd, int cmd, ... /* arg */) int w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* exceptfds, const struct timeval *timeout) { - ULONGLONG ticks_start = GetTickCount64(), ticks_spent, timeout_ms = 0, time_rem = 0; + ULONGLONG ticks_start = GetTickCount64(), timeout_ms = 0; w32_fd_set read_ready_fds, write_ready_fds; HANDLE events[SELECT_EVENT_LIMIT]; int num_events = 0; @@ -745,6 +806,8 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep if (timeout) timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + debug3("select enter fds:%d timeout:%llu readfds:%p writefds:%p", + fds, (unsigned long long)timeout_ms, readfds, writefds); if (fds > MAX_FDS) { errno = EINVAL; @@ -769,6 +832,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep if (FD_ISSET(i, readfds)) { CHECK_FD(i); in_set_fds++; + debug3_fd_entry("select read input", i); } } @@ -777,6 +841,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep if (FD_ISSET(i, writefds)) { CHECK_FD(i); in_set_fds++; + debug3_fd_entry("select write input", i); } } @@ -792,7 +857,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep * start async io on selected fds if needed and pick up any events * that select needs to listen on */ - for (int i = 0; i < fds; i++) { + for (i = 0; i < fds; i++) { if (readfds && FD_ISSET(i, readfds)) { w32_io_on_select(fd_table.w32_ios[i], TRUE); if ((fd_table.w32_ios[i]->type == SOCK_FD) && @@ -803,6 +868,16 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep return -1; } events[num_events++] = fd_table.w32_ios[i]->read_overlapped.hEvent; + } else if ((fd_table.w32_ios[i]->type == SOCK_FD) && + (fd_table.w32_ios[i]->internal.state == SOCK_READY) && + fd_table.w32_ios[i]->read_details.pending && + fd_table.w32_ios[i]->read_overlapped.hEvent != NULL) { + if (num_events == SELECT_EVENT_LIMIT) { + debug3("select - ERROR: max #events breach"); + errno = ENOMEM; + return -1; + } + events[num_events++] = fd_table.w32_ios[i]->read_overlapped.hEvent; } } @@ -816,9 +891,10 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep return -1; } events[num_events++] = fd_table.w32_ios[i]->write_overlapped.hEvent; + } } } - } + debug3("select armed input:%d events:%d", in_set_fds, num_events); /* excute any scheduled APCs */ if (0 != wait_for_any_event(NULL, 0, 0)) @@ -826,28 +902,30 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep /* see if any io is ready */ for (i = 0; i < fds; i++) { - if (readfds && FD_ISSET(i, readfds)) { - if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { - FD_SET(i, &read_ready_fds); - out_ready_fds++; + if (readfds && FD_ISSET(i, readfds)) { + if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { + FD_SET(i, &read_ready_fds); + out_ready_fds++; + debug3_fd_entry("select read ready initial", i); + } } - } - if (writefds && FD_ISSET(i, writefds)) { - if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { - FD_SET(i, &write_ready_fds); - out_ready_fds++; + if (writefds && FD_ISSET(i, writefds)) { + if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { + FD_SET(i, &write_ready_fds); + out_ready_fds++; + debug3_fd_entry("select write ready initial", i); + } } } - } /* timeout specified and both fields are 0 - polling mode*/ /* proceed with further wait if not in polling mode*/ if ((timeout == NULL) || (timeout_ms != 0)) /* wait for io until any is ready */ - while (out_ready_fds == 0) { - ticks_spent = GetTickCount64() - ticks_start; - time_rem = 0; + while (out_ready_fds == 0) { + ULONGLONG ticks_spent = GetTickCount64() - ticks_start; + ULONGLONG time_rem = 0; if (timeout != NULL) { if (timeout_ms < ticks_spent) { @@ -864,21 +942,23 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep /* check on fd status */ out_ready_fds = 0; - for (int i = 0; i < fds; i++) { - if (readfds && FD_ISSET(i, readfds)) { - if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { - FD_SET(i, &read_ready_fds); - out_ready_fds++; + for (i = 0; i < fds; i++) { + if (readfds && FD_ISSET(i, readfds)) { + if (w32_io_is_io_available(fd_table.w32_ios[i], TRUE)) { + FD_SET(i, &read_ready_fds); + out_ready_fds++; + debug3_fd_entry("select read ready wait", i); + } } - } - if (writefds && FD_ISSET(i, writefds)) { - if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { - FD_SET(i, &write_ready_fds); - out_ready_fds++; + if (writefds && FD_ISSET(i, writefds)) { + if (w32_io_is_io_available(fd_table.w32_ios[i], FALSE)) { + FD_SET(i, &write_ready_fds); + out_ready_fds++; + debug3_fd_entry("select write ready wait", i); + } } } - } if (out_ready_fds == 0) debug5("select - wait ended without any IO completion, looping again"); @@ -915,7 +995,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep FD_CLR(i, writefds); } - debug5("select - returning %d", out_ready_fds); + debug3("select returning %d", out_ready_fds); return out_ready_fds; } @@ -924,6 +1004,7 @@ dup_handle(int fd) { HANDLE h = fd_table.w32_ios[fd]->handle; int is_sock = fd_table.w32_ios[fd]->type == SOCK_FD; + debug3_fd_entry("dup_handle source", fd); if (is_sock) { SOCKET dup_sock; @@ -940,15 +1021,19 @@ dup_handle(int fd) error("WSASocketW failed, WSALastError: %d", WSAGetLastError()); return NULL; } + debug3("w32fd dup_handle socket fd:%d source:%p duplicate:%p", + fd, h, (HANDLE)dup_sock); return (HANDLE)dup_sock; } else { - HANDLE dup_handle; - if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &dup_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + HANDLE duplicated_handle = NULL; + if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &duplicated_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { errno = EOTHER; error("dup - ERROR: DuplicatedHandle() :%d", GetLastError()); } - return dup_handle; + debug3("w32fd dup_handle nonsocket fd:%d source:%p duplicate:%p", + fd, h, duplicated_handle); + return duplicated_handle; } } @@ -978,6 +1063,8 @@ w32_dup2(int oldfd, int newfd) pio->internal.state = SOCK_READY; fd_table_set(pio, newfd); + debug3("w32fd dup2 oldfd:%d newfd:%d", oldfd, newfd); + debug3_fd_entry("dup2 new", newfd); return 0; } @@ -1145,14 +1232,16 @@ spawn_child_internal(const char* cmd, char *const argv[], HANDLE in, HANDLE out, do { if (as_user) { debug3("spawning %ls as user", t); - LPVOID lpEnvironment = NULL; - wchar_t* as_user_name = get_username_from_token(as_user); - if (as_user_name) { - if (wcsncmp(L"sshd", as_user_name, wcslen(L"sshd")) != 0) { /* Ignore any names that begin with the service name `sshd`. */ - b = CreateEnvironmentBlock(&lpEnvironment, as_user, TRUE); /* Load a user environment block inheriting the current context, thereby passing session state. */ + LPVOID lpEnvironment = NULL; + wchar_t* as_user_name = get_username_from_token(as_user); + if (as_user_name) { + if (wcsncmp(L"sshd", as_user_name, wcslen(L"sshd")) != 0) { /* Ignore any names that begin with the service name `sshd`. */ + /* Load a user environment block inheriting the current context, thereby passing session state. */ + if (!CreateEnvironmentBlock(&lpEnvironment, as_user, TRUE)) + debug3("CreateEnvironmentBlock failed error:%d", GetLastError()); + } + free(as_user_name); } - free(as_user_name); - } if (lpEnvironment) { /* Pass the user environment block to the new process. */ b = CreateProcessAsUserW(as_user, NULL, t, NULL, NULL, TRUE, flags | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, NULL, &si, &pi); DestroyEnvironmentBlock(lpEnvironment); @@ -1221,7 +1310,6 @@ fd_encode_state(const posix_spawn_file_actions_t *file_actions, HANDLE aux_h[]) struct std_fd_state *std_fd_state; struct inh_fd_state *c; DWORD len_req = 0; - BOOL b; int i; int fd_in = file_actions->stdio_redirect[STDIN_FILENO]; int fd_out = file_actions->stdio_redirect[STDOUT_FILENO]; @@ -1250,14 +1338,23 @@ fd_encode_state(const posix_spawn_file_actions_t *file_actions, HANDLE aux_h[]) c++; } - b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &len_req); + if (!CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &len_req)) { + free(buf); + errno = EOTHER; + return NULL; + } encoded = malloc(len_req); if (!encoded) { free(buf); errno = ENOMEM; return NULL; } - b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encoded, &len_req); + if (!CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encoded, &len_req)) { + free(buf); + free(encoded); + errno = EOTHER; + return NULL; + } free(buf); return encoded; @@ -1281,6 +1378,11 @@ fd_decode_state(char* enc_buf) CryptStringToBinary(enc_buf, 0, CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT, buf, &req, &skipped, &out_flags); std_fd_state = (struct std_fd_state *)buf; + debug3("w32fd decode std types in:%s out:%s err:%s inherited:%d", + w32_io_type_name(std_fd_state->in_type), + w32_io_type_name(std_fd_state->out_type), + w32_io_type_name(std_fd_state->err_type), + std_fd_state->num_inherited); fd_table.w32_ios[0]->type = std_fd_state->in_type; if (fd_table.w32_ios[0]->type == SOCK_FD) fd_table.w32_ios[0]->internal.state = SOCK_READY; @@ -1303,6 +1405,9 @@ fd_decode_state(char* enc_buf) if (pio->type == SOCK_FD) pio->internal.state = SOCK_READY; fd_table_set(pio, c->index); + debug3("w32fd decode inherited fd:%d handle:%p type:%s state:%s", + c->index, pio->handle, w32_io_type_name(pio->type), + w32_sock_state_name(pio->internal.state)); c++; } diff --git a/serverloop.c b/serverloop.c index 40ddfb042b49..068c06e64875 100644 --- a/serverloop.c +++ b/serverloop.c @@ -337,6 +337,8 @@ server_loop2(struct ssh *ssh, Authctxt *authctxt) for (;;) { process_buffered_input_packets(ssh); + if (ssh_packet_have_data_to_write(ssh)) + process_output(ssh, connection_out); if (!ssh_packet_is_rekeying(ssh) && ssh_packet_not_very_much_data_to_write(ssh)) diff --git a/sshd.c b/sshd.c index 3e6c34276543..25598741ca88 100644 --- a/sshd.c +++ b/sshd.c @@ -1023,12 +1023,22 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s, if (children[i].config != NULL || children[i].keys != NULL) pfd[npfd].events |= POLLOUT; + debug3("startup child poll slot %d pollfd %d " + "fd %d events 0x%x config_len %zu " + "keys_len %zu", i, npfd, children[i].pipefd, + (u_int)pfd[npfd].events, + children[i].config == NULL ? 0 : + sshbuf_len(children[i].config), + children[i].keys == NULL ? 0 : + sshbuf_len(children[i].keys)); startup_pollfd[i] = npfd++; } } /* Wait until a connection arrives or a child exits. */ ret = ppoll(pfd, npfd, NULL, &osigset); + debug3("startup child ppoll ret %d npfd %d errno %d", + ret, npfd, ret == -1 ? errno : 0); if (ret == -1 && errno != EINTR) { error("ppoll: %.100s", strerror(errno)); if (errno == EINVAL) @@ -1053,7 +1063,16 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s, } ptr = sshbuf_ptr(buf); len = sshbuf_len(buf); + debug3("startup child pipe write slot %d pollfd %d " + "fd %d revents 0x%x kind %s len %zd", i, + startup_pollfd[i], children[i].pipefd, + (u_int)pfd[startup_pollfd[i]].revents, + children[i].config == buf ? "config" : "keys", + len); ret = write(children[i].pipefd, ptr, len); + debug3("startup child pipe write slot %d fd %d " + "ret %d errno %d", i, children[i].pipefd, ret, + ret == -1 ? errno : 0); if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue; if (ret <= 0) { @@ -1070,9 +1089,17 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s, /* sent config, now send keys */ children[i].config = NULL; children[i].keys = pack_hostkeys(); + debug3("startup child pipe queued " + "hostkeys slot %d fd %d len %zu", + i, children[i].pipefd, + children[i].keys == NULL ? 0 : + sshbuf_len(children[i].keys)); } else if (children[i].keys == buf) { /* sent both config and keys */ children[i].keys = NULL; + debug3("startup child pipe finished " + "hostkeys slot %d fd %d", i, + children[i].pipefd); } else { fatal("config buf not set"); } @@ -1209,10 +1236,21 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s, error("posix_spawn initialization failed"); else { child = child_register(config_s[0], *newsock); - if (posix_spawn(&child->pid, rexec_argv[0], &actions, &attributes, rexec_argv, NULL) != 0) - error("%s, posix_spawn failed", __func__); - posix_spawn_file_actions_destroy(&actions); - posix_spawnattr_destroy(&attributes); + debug3("posix_spawn reexec child " + "registered pipefd %d sockfd %d " + "config_len %zu", child->pipefd, + *newsock, + child->config == NULL ? 0 : + sshbuf_len(child->config)); + if (posix_spawn(&child->pid, rexec_argv[0], + &actions, &attributes, rexec_argv, + NULL) != 0) + error("%s, posix_spawn failed", __func__); + else + debug("Spawned child %ld.", + (long)child->pid); + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attributes); } } #else