openssh-portable/contrib/win32/win32compat/shell-host.c

1431 lines
38 KiB
C
Raw Normal View History

2016-12-19 23:46:28 +01:00
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
* Primitive shell-host to support parsing of cmd.exe input and async IO redirection
*
* Author: Ray Heyes <ray.hayes@microsoft.com>
* PTY with ANSI emulation wrapper
*
* Copyright (c) 2017 Microsoft Corp.
* All rights reserved
*
* Shell-host is responsible for handling all the interactive and non-interactive cmds.
*
* 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.
*/
2016-12-19 23:46:28 +01:00
#include <Windows.h>
#include <Strsafe.h>
#include <stdio.h>
#include <io.h>
#include "misc_internal.h"
#include "inc\utf.h"
2016-12-19 23:46:28 +01:00
#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
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
#endif
typedef BOOL(WINAPI *__t_SetCurrentConsoleFontEx)(
_In_ HANDLE hConsoleOutput,
_In_ BOOL bMaximumWindow,
_In_ PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx
);
2016-12-19 23:46:28 +01:00
__t_SetCurrentConsoleFontEx __SetCurrentConsoleFontEx;
typedef BOOL(WINAPI *__t_UnhookWinEvent)(
_In_ HWINEVENTHOOK hWinEventHook
);
2016-12-19 23:46:28 +01:00
__t_UnhookWinEvent __UnhookWinEvent;
typedef HWINEVENTHOOK(WINAPI *__t_SetWinEventHook)(
_In_ UINT eventMin,
_In_ UINT eventMax,
_In_ HMODULE hmodWinEventProc,
_In_ WINEVENTPROC lpfnWinEventProc,
_In_ DWORD idProcess,
_In_ DWORD idThread,
_In_ UINT dwflags
);
2016-12-19 23:46:28 +01:00
__t_SetWinEventHook __SetWinEventHook;
typedef struct consoleEvent {
DWORD event;
HWND hwnd;
LONG idObject;
LONG idChild;
void* prior;
void* next;
2016-12-19 23:46:28 +01:00
} consoleEvent;
struct key_translation {
char incoming[6];
int vk;
char outgoing;
2016-12-19 23:46:28 +01:00
} key_translation;
struct key_translation keys[] = {
{ "\x1b", VK_ESCAPE, '\x1b' },
{ "\r", VK_RETURN, '\r' },
{ "\b", VK_BACK, '\b' },
{ "\x7f", VK_BACK, '\b' },
{ "\t", VK_TAB, '\t' },
2016-12-19 23:46:28 +01:00
{ "\x1b[A", VK_UP, 0 },
{ "\x1b[B", VK_DOWN, 0 },
{ "\x1b[C", VK_RIGHT, 0 },
{ "\x1b[D", VK_LEFT, 0 },
{ "\x1b[1~", VK_HOME, 0 },
{ "\x1b[2~", VK_INSERT, 0 },
{ "\x1b[3~", VK_DELETE, 0 },
{ "\x1b[4~", VK_END, 0 },
{ "\x1b[5~", VK_PRIOR, 0 },
{ "\x1b[6~", VK_NEXT, 0 },
{ "\x1b[11~", VK_F1, 0 },
{ "\x1b[12~", VK_F2, 0 },
{ "\x1b[13~", VK_F3, 0 },
{ "\x1b[14~", VK_F4, 0 },
{ "\x1b[15~", VK_F5, 0 },
{ "\x1b[17~", VK_F6, 0 },
{ "\x1b[18~", VK_F7, 0 },
{ "\x1b[19~", VK_F8, 0 },
{ "\x1b[20~", VK_F9, 0 },
{ "\x1b[21~", VK_F10, 0 },
{ "\x1b[23~", VK_F11, 0 },
{ "\x1b[24~", VK_F12, 0 }
};
static SHORT lastX = 0;
static SHORT lastY = 0;
2016-12-19 23:46:28 +01:00
consoleEvent* head = NULL;
consoleEvent* tail = NULL;
BOOL bRet = FALSE;
BOOL bNoScrollRegion = FALSE;
BOOL bStartup = TRUE;
BOOL bAnsi = FALSE;
BOOL bHookEvents = FALSE;
HANDLE child_out = INVALID_HANDLE_VALUE;
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 child = INVALID_HANDLE_VALUE;
DWORD child_exit_code = 0;
2016-12-19 23:46:28 +01:00
HANDLE hConsoleBuffer = INVALID_HANDLE_VALUE;
HANDLE monitor_thread = INVALID_HANDLE_VALUE;
HANDLE io_thread = INVALID_HANDLE_VALUE;
HANDLE ux_thread = INVALID_HANDLE_VALUE;
DWORD hostProcessId = 0;
DWORD hostThreadId = 0;
DWORD childProcessId = 0;
DWORD dwStatus = 0;
DWORD currentLine = 0;
DWORD lastLineLength = 0;
UINT cp = 0;
UINT ViewPortY = 0;
UINT lastViewPortY = 0;
BOOL bFullScreen = FALSE;
BOOL bUseAnsiEmulation = TRUE;
UINT savedViewPortY = 0;
UINT savedLastViewPortY = 0;
DWORD in_cmd_len = 0;
char in_cmd[MAX_CMD_LEN];
CRITICAL_SECTION criticalSection;
CONSOLE_SCREEN_BUFFER_INFOEX consoleInfo;
CONSOLE_SCREEN_BUFFER_INFOEX nextConsoleInfo;
STARTUPINFO inputSi;
#define GOTO_CLEANUP_ON_FALSE(exp) do { \
ret = (exp); \
if (ret == FALSE) \
goto cleanup; \
} while(0)
2016-12-19 23:46:28 +01:00
#define GOTO_CLEANUP_ON_ERR(exp) do { \
if ((exp) != 0) \
goto cleanup; \
} while(0)
2016-12-19 23:46:28 +01:00
int
ConSRWidth()
{
CONSOLE_SCREEN_BUFFER_INFOEX consoleBufferInfo;
ZeroMemory(&consoleBufferInfo, sizeof(consoleBufferInfo));
consoleBufferInfo.cbSize = sizeof(consoleBufferInfo);
GetConsoleScreenBufferInfoEx(child_out, &consoleBufferInfo);
return consoleBufferInfo.srWindow.Right;
}
/*
* This function will handle the console keystrokes.
*/
void
SendKeyStroke(HANDLE hInput, int keyStroke, char character)
2016-12-19 23:46:28 +01:00
{
DWORD wr = 0;
INPUT_RECORD ir;
ir.EventType = KEY_EVENT;
ir.Event.KeyEvent.bKeyDown = TRUE;
ir.Event.KeyEvent.wRepeatCount = 1;
ir.Event.KeyEvent.wVirtualKeyCode = keyStroke;
ir.Event.KeyEvent.wVirtualScanCode = 0;
ir.Event.KeyEvent.dwControlKeyState = 0;
ir.Event.KeyEvent.uChar.UnicodeChar = 0;
ir.Event.KeyEvent.uChar.AsciiChar = character;
WriteConsoleInputA(hInput, &ir, 1, &wr);
ir.Event.KeyEvent.bKeyDown = FALSE;
WriteConsoleInputA(hInput, &ir, 1, &wr);
2016-12-19 23:46:28 +01:00
}
void
ProcessIncomingKeys(char * ansikey)
{
int keylen = (int)strlen(ansikey);
if (!keylen)
return;
for (int nKey=0; nKey < ARRAYSIZE(keys); nKey++) {
if (strcmp(ansikey, keys[nKey].incoming) == 0) {
SendKeyStroke(child_in, keys[nKey].vk, keys[nKey].outgoing);
return;
}
}
2016-12-19 23:46:28 +01:00
for (int i=0; i < keylen; i++)
SendKeyStroke(child_in, 0, ansikey[i]);
2016-12-19 23:46:28 +01:00
}
/*
* VT output routines
*/
void
SendLF(HANDLE hInput)
{
DWORD wr = 0;
if (bUseAnsiEmulation)
WriteFile(hInput, "\n", 1, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendClearScreen(HANDLE hInput)
{
DWORD wr = 0;
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, "\033[2J", 4, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendClearScreenFromCursor(HANDLE hInput)
{
DWORD wr = 0;
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, "\033[1J", 4, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendHideCursor(HANDLE hInput)
{
DWORD wr = 0;
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, "\033[?25l", 6, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendShowCursor(HANDLE hInput)
{
DWORD wr = 0;
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, "\033[?25h", 6, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendCursorPositionRequest(HANDLE hInput)
{
DWORD wr = 0;
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, "\033[6n", 4, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendSetCursor(HANDLE hInput, int X, int Y)
{
DWORD wr = 0;
DWORD out = 0;
char formatted_output[255];
2016-12-19 23:46:28 +01:00
out = _snprintf_s(formatted_output, sizeof(formatted_output), _TRUNCATE, "\033[%d;%dH", Y, X);
if (bUseAnsiEmulation)
WriteFile(hInput, formatted_output, out, &wr, NULL);
2016-12-19 23:46:28 +01:00
}
void
SendVerticalScroll(HANDLE hInput, int lines)
{
DWORD wr = 0;
DWORD out = 0;
char formatted_output[255];
2016-12-19 23:46:28 +01:00
LONG vn = abs(lines);
/* Not supporting the [S at the moment. */
if (lines > 0) {
out = snprintf(formatted_output, sizeof(formatted_output), "\033[%dT", vn);
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, formatted_output, out, &wr, NULL);
}
2016-12-19 23:46:28 +01:00
}
void
SendHorizontalScroll(HANDLE hInput, int cells)
{
DWORD wr = 0;
DWORD out = 0;
char formatted_output[255];
2016-12-19 23:46:28 +01:00
out = snprintf(formatted_output, sizeof(formatted_output), "\033[%dG", cells);
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation)
WriteFile(hInput, formatted_output, out, &wr, NULL);
}
2016-12-19 23:46:28 +01:00
void
SendCharacter(HANDLE hInput, WORD attributes, wchar_t character)
{
DWORD wr = 0;
DWORD out = 0;
DWORD current = 0;
char formatted_output[2048];
static WORD pattributes = 0;
USHORT Color = 0;
2016-12-19 23:46:28 +01:00
ULONG Status = 0;
PSTR Next;
size_t SizeLeft;
2016-12-19 23:46:28 +01:00
if (!character)
return;
2016-12-19 23:46:28 +01:00
Next = formatted_output;
SizeLeft = sizeof formatted_output;
2016-12-19 23:46:28 +01:00
/* Handle the foreground intensity */
if ((attributes & FOREGROUND_INTENSITY) != 0)
2016-12-19 23:46:28 +01:00
Color = 1;
else
2016-12-19 23:46:28 +01:00
Color = 0;
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, "\033[%u", Color);
/* Handle the background intensity */
if ((attributes & BACKGROUND_INTENSITY) != 0)
Color = 1;
else
Color = 39;
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color);
/* Handle the underline */
if ((attributes & COMMON_LVB_UNDERSCORE) != 0)
Color = 4;
else
Color = 24;
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color);
/* Handle reverse video */
if ((attributes & COMMON_LVB_REVERSE_VIDEO) != 0)
Color = 7;
else
Color = 27;
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color);
2016-12-19 23:46:28 +01:00
/* Add background and foreground colors to buffer. */
Color = 30 +
4 * ((attributes & FOREGROUND_BLUE) != 0) +
2 * ((attributes & FOREGROUND_GREEN) != 0) +
1 * ((attributes & FOREGROUND_RED) != 0);
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color);
2016-12-19 23:46:28 +01:00
Color = 40 +
4 * ((attributes & BACKGROUND_BLUE) != 0) +
2 * ((attributes & BACKGROUND_GREEN) != 0) +
1 * ((attributes & BACKGROUND_RED) != 0);
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color);
2016-12-19 23:46:28 +01:00
StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, "m", Color);
2016-12-19 23:46:28 +01:00
if (bUseAnsiEmulation && attributes != pattributes)
WriteFile(hInput, formatted_output, (DWORD)(Next - formatted_output), &wr, NULL);
/* East asian languages have 2 bytes for each character, only use the first */
if (!(attributes & COMMON_LVB_TRAILING_BYTE)) {
int nSize = WideCharToMultiByte(CP_UTF8,
0,
&character,
1,
Next,
10,
NULL,
NULL);
if (nSize > 0)
WriteFile(hInput, Next, nSize, &wr, NULL);
}
2016-12-19 23:46:28 +01:00
pattributes = attributes;
2016-12-19 23:46:28 +01:00
}
void
SendBuffer(HANDLE hInput, CHAR_INFO *buffer, DWORD bufferSize)
{
if (bufferSize <= 0)
return;
2016-12-19 23:46:28 +01:00
for (DWORD i = 0; i < bufferSize; i++)
SendCharacter(hInput, buffer[i].Attributes, buffer[i].Char.UnicodeChar);
2016-12-19 23:46:28 +01:00
}
void
CalculateAndSetCursor(HANDLE hInput, UINT x, UINT y)
{
2016-12-19 23:46:28 +01:00
SendSetCursor(pipe_out, x + 1, y + 1);
currentLine = y;
2016-12-19 23:46:28 +01:00
}
void
SizeWindow(HANDLE hInput)
{
SMALL_RECT srWindowRect;
COORD coordScreen;
BOOL bSuccess = FALSE;
/* The input window does not scroll currently to ease calculations on the paint/draw */
bNoScrollRegion = TRUE;
/* Set the default font to Consolas */
CONSOLE_FONT_INFOEX matchingFont;
matchingFont.cbSize = sizeof(matchingFont);
matchingFont.nFont = 0;
matchingFont.dwFontSize.X = 0;
matchingFont.dwFontSize.Y = 16;
matchingFont.FontFamily = FF_DONTCARE;
matchingFont.FontWeight = FW_NORMAL;
wcscpy(matchingFont.FaceName, L"Consolas");
bSuccess = __SetCurrentConsoleFontEx(child_out, FALSE, &matchingFont);
/* This information is the live screen */
ZeroMemory(&consoleInfo, sizeof(consoleInfo));
consoleInfo.cbSize = sizeof(consoleInfo);
bSuccess = GetConsoleScreenBufferInfoEx(child_out, &consoleInfo);
/* Get the largest size we can size the console window to */
coordScreen = GetLargestConsoleWindowSize(child_out);
/* Define the new console window size and scroll position */
if (inputSi.dwXCountChars == 0 || inputSi.dwYCountChars == 0) {
inputSi.dwXCountChars = 80;
inputSi.dwYCountChars = 25;
}
2016-12-19 23:46:28 +01:00
srWindowRect.Right = (SHORT)(min(inputSi.dwXCountChars, (DWORD)coordScreen.X) - 1);
srWindowRect.Bottom = (SHORT)(min(inputSi.dwYCountChars, (DWORD)coordScreen.Y) - 1);
srWindowRect.Left = srWindowRect.Top = (SHORT)0;
2016-12-19 23:46:28 +01:00
/* Define the new console buffer size to be the maximum possible */
coordScreen.X = 100;
coordScreen.Y = 9999;
2016-12-19 23:46:28 +01:00
if (SetConsoleWindowInfo(child_out, TRUE, &srWindowRect))
bSuccess = SetConsoleScreenBufferSize(child_out, coordScreen);
else {
if (SetConsoleScreenBufferSize(child_out, coordScreen))
bSuccess = SetConsoleWindowInfo(child_out, TRUE, &srWindowRect);
}
2016-12-19 23:46:28 +01:00
bSuccess = GetConsoleScreenBufferInfoEx(child_out, &consoleInfo);
2016-12-19 23:46:28 +01:00
}
DWORD WINAPI
MonitorChild(_In_ LPVOID lpParameter)
{
WaitForSingleObject(child, INFINITE);
GetExitCodeProcess(child, &child_exit_code);
PostThreadMessage(hostThreadId, WM_APPEXIT, 0, 0);
return 0;
}
2016-12-19 23:46:28 +01:00
DWORD
ProcessEvent(void *p)
{
wchar_t chUpdate;
WORD wAttributes;
WORD wX;
WORD wY;
DWORD dwProcessId;
DWORD wr = 0;
DWORD event;
HWND hwnd;
LONG idObject;
LONG idChild;
if (!p)
return ERROR_INVALID_PARAMETER;
consoleEvent* current = (consoleEvent *)p;
if (current) {
event = current->event;
hwnd = current->hwnd;
idObject = current->idObject;
idChild = current->idChild;
} else
return ERROR_INVALID_PARAMETER;
if (event < EVENT_CONSOLE_CARET || event > EVENT_CONSOLE_LAYOUT)
return ERROR_INVALID_PARAMETER;
if (child_out == INVALID_HANDLE_VALUE || child_out == NULL)
return ERROR_INVALID_PARAMETER;
GetWindowThreadProcessId(hwnd, &dwProcessId);
if (childProcessId != dwProcessId)
return ERROR_SUCCESS;
ZeroMemory(&consoleInfo, sizeof(consoleInfo));
consoleInfo.cbSize = sizeof(consoleInfo);
GetConsoleScreenBufferInfoEx(child_out, &consoleInfo);
UINT viewPortHeight = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1;
UINT viewPortWidth = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1;
switch (event) {
case EVENT_CONSOLE_CARET:
{
COORD co;
co.X = LOWORD(idChild);
co.Y = HIWORD(idChild);
lastX = co.X;
lastY = co.Y;
SendSetCursor(pipe_out, lastX + 1, lastY + 1);
break;
}
case EVENT_CONSOLE_UPDATE_REGION:
{
SMALL_RECT readRect;
readRect.Top = HIWORD(idObject);
readRect.Left = LOWORD(idObject);
readRect.Bottom = HIWORD(idChild);
readRect.Right = LOWORD(idChild);
readRect.Right = max(readRect.Right, ConSRWidth());
/* Detect a "cls" (Windows) */
if (!bStartup &&
(readRect.Top == consoleInfo.srWindow.Top || readRect.Top == nextConsoleInfo.srWindow.Top)) {
BOOL isClearCommand = FALSE;
isClearCommand = (consoleInfo.dwSize.X == readRect.Right + 1) && (consoleInfo.dwSize.Y == readRect.Bottom + 1);
/* If cls then inform app to clear its buffers and return */
if (isClearCommand) {
SendClearScreen(pipe_out);
ViewPortY = 0;
lastViewPortY = 0;
return ERROR_SUCCESS;
}
}
/* Figure out the buffer size */
COORD coordBufSize;
coordBufSize.Y = readRect.Bottom - readRect.Top + 1;
coordBufSize.X = readRect.Right - readRect.Left + 1;
/*
* Security check: the maximum screen buffer size is 9999 columns x 9999 lines so check
* the computed buffer size for overflow. since the X and Y in the COORD structure
* are shorts they could be negative.
*/
if (coordBufSize.X < 0 || coordBufSize.X > MAX_CONSOLE_COLUMNS ||
coordBufSize.Y < 0 || coordBufSize.Y > MAX_CONSOLE_ROWS)
return ERROR_INVALID_PARAMETER;
/* Compute buffer size */
DWORD bufferSize = coordBufSize.X * coordBufSize.Y;
if (bufferSize > MAX_EXPECTED_BUFFER_SIZE) {
if (!bStartup) {
SendClearScreen(pipe_out);
ViewPortY = 0;
lastViewPortY = 0;
}
return ERROR_SUCCESS;
}
/* Create the screen scrape buffer */
CHAR_INFO *pBuffer = (PCHAR_INFO)malloc(sizeof(CHAR_INFO) * bufferSize);
if (!pBuffer)
return ERROR_INSUFFICIENT_BUFFER;
/* The top left destination cell of the temporary buffer is row 0, col 0 */
COORD coordBufCoord;
coordBufCoord.X = 0;
coordBufCoord.Y = 0;
/* Copy the block from the screen buffer to the temp. buffer */
if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) {
DWORD dwError = GetLastError();
free(pBuffer);
return dwError;
}
if ((DWORD)readRect.Top > currentLine)
for (DWORD n = currentLine; n < (DWORD)readRect.Top; n++)
SendLF(pipe_out);
/* Set cursor location based on the reported location from the message */
CalculateAndSetCursor(pipe_out, readRect.Left, readRect.Top);
/* Send the entire block */
SendBuffer(pipe_out, pBuffer, bufferSize);
lastViewPortY = ViewPortY;
lastLineLength = readRect.Left;
free(pBuffer);
break;
}
case EVENT_CONSOLE_UPDATE_SIMPLE:
{
chUpdate = LOWORD(idChild);
wAttributes = HIWORD(idChild);
wX = LOWORD(idObject);
wY = HIWORD(idObject);
SMALL_RECT readRect;
readRect.Top = wY;
readRect.Bottom = wY;
readRect.Left = wX;
readRect.Right = ConSRWidth();
/* Set cursor location based on the reported location from the message */
CalculateAndSetCursor(pipe_out, wX, wY);
COORD coordBufSize;
coordBufSize.Y = readRect.Bottom - readRect.Top + 1;
coordBufSize.X = readRect.Right - readRect.Left + 1;
2016-12-19 23:46:28 +01:00
/* The top left destination cell of the temporary buffer is row 0, col 0 */
COORD coordBufCoord;
coordBufCoord.X = 0;
coordBufCoord.Y = 0;
int pBufferSize = coordBufSize.X * coordBufSize.Y;
/* Send the one character. Note that a CR doesn't end up here */
CHAR_INFO *pBuffer = (PCHAR_INFO)malloc(sizeof(CHAR_INFO) * pBufferSize);
/* Copy the block from the screen buffer to the temp. buffer */
if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) {
DWORD dwError = GetLastError();
free(pBuffer);
return dwError;
}
SendBuffer(pipe_out, pBuffer, pBufferSize);
free(pBuffer);
2016-12-19 23:46:28 +01:00
break;
}
case EVENT_CONSOLE_UPDATE_SCROLL:
{
DWORD out = 0;
LONG vd = idChild;
LONG hd = idObject;
LONG vn = abs(vd);
if (vd > 0) {
if (ViewPortY > 0)
ViewPortY -= vn;
} else {
ViewPortY += vn;
}
break;
}
case EVENT_CONSOLE_LAYOUT:
{
if (consoleInfo.dwMaximumWindowSize.X == consoleInfo.dwSize.X &&
consoleInfo.dwMaximumWindowSize.Y == consoleInfo.dwSize.Y &&
(consoleInfo.dwCursorPosition.X == 0 && consoleInfo.dwCursorPosition.Y == 0)) {
/* Screen has switched to fullscreen mode */
SendClearScreen(pipe_out);
savedViewPortY = ViewPortY;
savedLastViewPortY = lastViewPortY;
ViewPortY = 0;
lastViewPortY = 0;;
bFullScreen = TRUE;
} else {
/* Leave full screen mode if applicable */
if (bFullScreen) {
SendClearScreen(pipe_out);
ViewPortY = savedViewPortY;
lastViewPortY = savedLastViewPortY;
bFullScreen = FALSE;
}
}
break;
}
}
2016-12-19 23:46:28 +01:00
return ERROR_SUCCESS;
}
2016-12-19 23:46:28 +01:00
DWORD WINAPI
ProcessEventQueue(LPVOID p)
{
if (child_in != INVALID_HANDLE_VALUE && child_in != NULL &&
child_out != INVALID_HANDLE_VALUE && child_out != NULL) {
DWORD dwInputMode;
DWORD dwOutputMode;
if (GetConsoleMode(child_in, &dwInputMode) && GetConsoleMode(child_out, &dwOutputMode))
if (((dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING) &&
((dwInputMode & ENABLE_VIRTUAL_TERMINAL_INPUT) == ENABLE_VIRTUAL_TERMINAL_INPUT))
bAnsi = TRUE;
else
bAnsi = FALSE;
}
while (1) {
while (head) {
EnterCriticalSection(&criticalSection);
consoleEvent* current = head;
if (current) {
if (current->next) {
head = current->next;
head->prior = NULL;
} else {
head = NULL;
tail = NULL;
}
}
LeaveCriticalSection(&criticalSection);
if (current) {
ProcessEvent(current);
free(current);
}
}
if (child_in != INVALID_HANDLE_VALUE && child_in != NULL &&
child_out != INVALID_HANDLE_VALUE && child_out != NULL) {
ZeroMemory(&consoleInfo, sizeof(consoleInfo));
consoleInfo.cbSize = sizeof(consoleInfo);
/* This information is the live buffer that's currently in use */
GetConsoleScreenBufferInfoEx(child_out, &consoleInfo);
/* Set the cursor to the last known good location according to the live buffer */
if (lastX != consoleInfo.dwCursorPosition.X ||
lastY != consoleInfo.dwCursorPosition.Y)
SendSetCursor(pipe_out, consoleInfo.dwCursorPosition.X + 1, consoleInfo.dwCursorPosition.Y + 1);
lastX = consoleInfo.dwCursorPosition.X;
lastY = consoleInfo.dwCursorPosition.Y;
}
Sleep(100);
}
return 0;
}
2016-12-19 23:46:28 +01:00
void
QueueEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild)
{
consoleEvent* current = NULL;
EnterCriticalSection(&criticalSection);
current = malloc(sizeof(consoleEvent));
if (current) {
if (!head) {
current->event = event;
current->hwnd = hwnd;
current->idChild = idChild;
current->idObject = idObject;
/* No links head == tail */
current->next = NULL;
current->prior = NULL;
head = current;
tail = current;
} else {
current->event = event;
current->hwnd = hwnd;
current->idChild = idChild;
current->idObject = idObject;
/* Current tail points to new tail */
tail->next = current;
/* New tail points to old tail */
current->prior = tail;
current->next = NULL;
/* Update the tail pointer to the new last event */
tail = current;
}
}
LeaveCriticalSection(&criticalSection);
}
2016-12-19 23:46:28 +01:00
void FreeQueueEvent()
{
EnterCriticalSection(&criticalSection);
while (head) {
consoleEvent* current = head;
head = current->next;
free(current);
}
head = NULL;
tail = NULL;
LeaveCriticalSection(&criticalSection);
}
DWORD WINAPI
ProcessPipes(LPVOID p)
{
BOOL ret;
DWORD dwStatus;
char buf[128];
/* process data from pipe_in and route appropriately */
while (1) {
DWORD rd = 0;
ZeroMemory(buf, sizeof(buf));
GOTO_CLEANUP_ON_FALSE(ReadFile(pipe_in, buf, sizeof(buf)-1, &rd, NULL)); /* read bufsize-1 */
bStartup = FALSE;
for (DWORD i=0; i < rd; i++) {
if (buf[i] == 0)
break;
if (buf[i] == 3) { /*Ctrl+C - Raise Ctrl+C*/
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
continue;
}
if (bAnsi) {
SendKeyStroke(child_in, 0, buf[i]);
} else {
ProcessIncomingKeys(buf);
break;
}
}
}
2016-12-19 23:46:28 +01:00
cleanup:
/* pipe_in has ended */
PostThreadMessage(hostThreadId, WM_APPEXIT, 0, 0);
dwStatus = GetLastError();
return 0;
2016-12-19 23:46:28 +01:00
}
void CALLBACK
ConsoleEventProc(HWINEVENTHOOK hWinEventHook,
2016-12-19 23:46:28 +01:00
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime)
{
QueueEvent(event, hwnd, idObject, idChild);
2016-12-19 23:46:28 +01:00
}
DWORD
ProcessMessages(void* p)
{
DWORD dwStatus;
SECURITY_ATTRIBUTES sa;
MSG msg;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
while (child_in == (HANDLE)-1) {
child_in = CreateFile(TEXT("CONIN$"), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa, OPEN_EXISTING, 0, NULL);
}
if (child_in == (HANDLE)-1)
goto cleanup;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
while (child_out == (HANDLE)-1) {
child_out = CreateFile(TEXT("CONOUT$"), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa, OPEN_EXISTING, 0, NULL);
}
if (child_out == (HANDLE)-1)
goto cleanup;
child_err = child_out;
SizeWindow(child_out);
/* Get the current buffer information after all the adjustments */
GetConsoleScreenBufferInfoEx(child_out, &consoleInfo);
/* Loop for the console output events */
while (GetMessage(&msg, NULL, 0, 0)) {
if (msg.message == WM_APPEXIT)
break;
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
2016-12-19 23:46:28 +01:00
cleanup:
dwStatus = GetLastError();
if (child_in != INVALID_HANDLE_VALUE)
CloseHandle(child_in);
if (child_out != INVALID_HANDLE_VALUE)
CloseHandle(child_out);
return 0;
2016-12-19 23:46:28 +01:00
}
int
start_with_pty(wchar_t *command)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
wchar_t cmd[MAX_CMD_LEN];
SECURITY_ATTRIBUTES sa;
BOOL ret;
DWORD dwStatus;
HANDLE hEventHook = NULL;
HMODULE hm_kernel32 = NULL, hm_user32 = NULL;
if ((hm_kernel32 = LoadLibraryW(L"kernel32.dll")) == NULL ||
(hm_user32 = LoadLibraryW(L"user32.dll")) == NULL ||
(__SetCurrentConsoleFontEx = (__t_SetCurrentConsoleFontEx)GetProcAddress(hm_kernel32, "SetCurrentConsoleFontEx")) == NULL ||
(__UnhookWinEvent = (__t_UnhookWinEvent)GetProcAddress(hm_user32, "UnhookWinEvent")) == NULL ||
(__SetWinEventHook = (__t_SetWinEventHook)GetProcAddress(hm_user32, "SetWinEventHook")) == NULL) {
printf("cannot support a pseudo terminal. \n");
return -1;
}
2016-12-19 23:46:28 +01:00
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
pipe_err = 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))
return -1;
cp = GetConsoleCP();
/*
* Windows PTY sends cursor positions in absolute coordinates starting from <0,0>
* We send a clear screen upfront to simplify client
*/
SendClearScreen(pipe_out);
ZeroMemory(&inputSi, sizeof(STARTUPINFO));
GetStartupInfo(&inputSi);
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
sa.bInheritHandle = TRUE;
/* WM_APPEXIT */
hostThreadId = GetCurrentThreadId();
hostProcessId = GetCurrentProcessId();
InitializeCriticalSection(&criticalSection);
hEventHook = __SetWinEventHook(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, NULL,
ConsoleEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
memset(&si, 0, sizeof(STARTUPINFO));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
/* Copy our parent buffer sizes */
si.cb = sizeof(STARTUPINFO);
si.dwFlags = 0;
/* disable inheritance on pipe_in*/
GOTO_CLEANUP_ON_FALSE(SetHandleInformation(pipe_in, HANDLE_FLAG_INHERIT, 0));
/*TODO - pick this up from system32*/
cmd[0] = L'\0';
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L"cmd.exe"));
if (command) {
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, command));
}
2016-12-19 23:46:28 +01:00
SetConsoleCtrlHandler(NULL, FALSE);
GOTO_CLEANUP_ON_FALSE(CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi));
childProcessId = pi.dwProcessId;
FreeConsole();
Sleep(20);
while (!AttachConsole(pi.dwProcessId)) {
if (GetExitCodeProcess(pi.hProcess, &child_exit_code) && child_exit_code != STILL_ACTIVE)
break;
Sleep(100);
}
/* monitor child exist */
child = pi.hProcess;
monitor_thread = CreateThread(NULL, 0, MonitorChild, NULL, 0, NULL);
if (IS_INVALID_HANDLE(monitor_thread))
goto cleanup;
2016-12-19 23:46:28 +01:00
/* disable Ctrl+C hander in this process*/
SetConsoleCtrlHandler(NULL, TRUE);
2016-12-19 23:46:28 +01:00
io_thread = CreateThread(NULL, 0, ProcessPipes, NULL, 0, NULL);
if (IS_INVALID_HANDLE(io_thread))
goto cleanup;
2016-12-19 23:46:28 +01:00
ux_thread = CreateThread(NULL, 0, ProcessEventQueue, NULL, 0, NULL);
if (IS_INVALID_HANDLE(ux_thread))
goto cleanup;
2016-12-19 23:46:28 +01:00
ProcessMessages(NULL);
cleanup:
dwStatus = GetLastError();
if (child != INVALID_HANDLE_VALUE)
TerminateProcess(child, 0);
if (!IS_INVALID_HANDLE(monitor_thread)) {
WaitForSingleObject(monitor_thread, INFINITE);
CloseHandle(monitor_thread);
}
if (!IS_INVALID_HANDLE(ux_thread)) {
TerminateThread(ux_thread, S_OK);
CloseHandle(ux_thread);
}
if (!IS_INVALID_HANDLE(io_thread)) {
TerminateThread(io_thread, 0);
CloseHandle(io_thread);
}
if (hEventHook)
__UnhookWinEvent(hEventHook);
FreeConsole();
if (child != INVALID_HANDLE_VALUE) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
FreeQueueEvent();
DeleteCriticalSection(&criticalSection);
return child_exit_code;
2016-12-19 23:46:28 +01:00
}
HANDLE child_pipe_read;
HANDLE child_pipe_write;
DWORD WINAPI
MonitorChild_nopty( _In_ LPVOID lpParameter)
{
WaitForSingleObject(child, INFINITE);
GetExitCodeProcess(child, &child_exit_code);
CloseHandle(pipe_in);
return 0;
2016-12-19 23:46:28 +01:00
}
int
start_withno_pty(wchar_t *command)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
wchar_t cmd[MAX_CMD_LEN];
SECURITY_ATTRIBUTES sa;
BOOL ret, process_input = FALSE, run_under_cmd = FALSE;
size_t command_len;
char buf[128];
DWORD rd = 0, wr = 0, i = 0;
pipe_in = GetStdHandle(STD_INPUT_HANDLE);
pipe_out = GetStdHandle(STD_OUTPUT_HANDLE);
pipe_err = 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))
return -1;
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
sa.bInheritHandle = TRUE;
if (!CreatePipe(&child_pipe_read, &child_pipe_write, &sa, 128))
return -1;
memset(&si, 0, sizeof(STARTUPINFO));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = child_pipe_read;
si.hStdOutput = pipe_out;
si.hStdError = pipe_err;
/* disable inheritance on child_pipe_write and pipe_in*/
GOTO_CLEANUP_ON_FALSE(SetHandleInformation(pipe_in, HANDLE_FLAG_INHERIT, 0));
GOTO_CLEANUP_ON_FALSE(SetHandleInformation(child_pipe_write, HANDLE_FLAG_INHERIT, 0));
/*
* check if the input needs to be processed (ex for CRLF translation)
* input stream needs to be processed when running the command
* within shell processor. This is needed when
* - launching a interactive shell (-nopty)
* ssh -T user@target
* - launching cmd explicity
* ssh user@target cmd
* - executing a cmd command
* ssh user@target dir
* - executing a cmd command within a cmd
* ssh user@target cmd /c dir
*/
if (!command)
process_input = TRUE;
else {
command_len = wcsnlen_s(command, MAX_CMD_LEN);
if ((command_len >= 3 && wcsncmp(command, L"cmd", 4) == 0) ||
(command_len >= 7 && wcsncmp(command, L"cmd.exe", 8) == 0) ||
(command_len >= 4 && wcsncmp(command, L"cmd ", 4) == 0) ||
(command_len >= 8 && wcsncmp(command, L"cmd.exe ", 8) == 0))
process_input = TRUE;
}
/* Try launching command as is first */
if (command) {
ret = CreateProcessW(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (ret == FALSE) {
/* it was probably this case - ssh user@target dir */
if (GetLastError() == ERROR_FILE_NOT_FOUND)
run_under_cmd = TRUE;
else
goto cleanup;
}
}
else
run_under_cmd = TRUE;
/* if above failed with FILE_NOT_FOUND, try running the provided command under cmd*/
if (run_under_cmd) {
/*TODO - pick this up from system32*/
cmd[0] = L'\0';
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L"cmd.exe"));
if (command) {
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" /c"));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, L" "));
GOTO_CLEANUP_ON_ERR(wcscat_s(cmd, MAX_CMD_LEN, command));
}
GOTO_CLEANUP_ON_FALSE(CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi));
/* Create process succeeded when running under cmd. input stream needs to be processed */
process_input = TRUE;
}
/* close unwanted handles*/
CloseHandle(child_pipe_read);
child_pipe_read = INVALID_HANDLE_VALUE;
child = pi.hProcess;
/* monitor child exist */
monitor_thread = CreateThread(NULL, 0, MonitorChild_nopty, NULL, 0, NULL);
if (IS_INVALID_HANDLE(monitor_thread))
goto cleanup;
/* disable Ctrl+C hander in this process*/
SetConsoleCtrlHandler(NULL, TRUE);
/* process data from pipe_in and route appropriately */
while (1) {
rd = wr = i = 0;
GOTO_CLEANUP_ON_FALSE(ReadFile(pipe_in, buf, sizeof(buf)-1, &rd, NULL));
if (process_input == FALSE) {
/* write stream directly to child stdin */
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, buf, rd, &wr, NULL));
continue;
}
/* else - process input before routing it to child */
while (i < rd) {
/* skip arrow keys */
if ((rd - i >= 3) && (buf[i] == '\033') && (buf[i + 1] == '[') &&
(buf[i + 2] >= 'A') && (buf[i + 2] <= 'D')) {
i += 3;
continue;
}
/* skip tab */
if (buf[i] == '\t') {
i++;
continue;
}
/* Ctrl +C */
if (buf[i] == '\003') {
GOTO_CLEANUP_ON_FALSE(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0));
in_cmd_len = 0;
i++;
continue;
}
/* for backspace, we need to send space and another backspace for visual erase */
if (buf[i] == '\b') {
if (in_cmd_len > 0) {
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, "\b \b", 3, &wr, NULL));
in_cmd_len--;
}
i++;
continue;
}
/* For CR and LF */
if ((buf[i] == '\r') || (buf[i] == '\n')) {
/* TODO - do a much accurate mapping */
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, buf + i, 1, &wr, NULL));
if ((buf[i] == '\r') && ((i == rd - 1) || (buf[i + 1] != '\n'))) {
buf[i] = '\n';
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, buf + i, 1, &wr, NULL));
}
in_cmd[in_cmd_len] = buf[i];
in_cmd_len++;
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, in_cmd, in_cmd_len, &wr, NULL));
in_cmd_len = 0;
i++;
continue;
}
GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, buf + i, 1, &wr, NULL));
in_cmd[in_cmd_len] = buf[i];
in_cmd_len++;
if (in_cmd_len == MAX_CMD_LEN - 1) {
GOTO_CLEANUP_ON_FALSE(WriteFile(child_pipe_write, in_cmd, in_cmd_len, &wr, NULL));
in_cmd_len = 0;
}
i++;
}
}
2016-12-19 23:46:28 +01:00
cleanup:
/* close child's stdin first */
if(!IS_INVALID_HANDLE(child_pipe_write))
CloseHandle(child_pipe_write);
if (!IS_INVALID_HANDLE(monitor_thread)) {
WaitForSingleObject(monitor_thread, INFINITE);
CloseHandle(monitor_thread);
}
if (!IS_INVALID_HANDLE(child))
TerminateProcess(child, 0);
return child_exit_code;
2016-12-19 23:46:28 +01:00
}
#include <Shlobj.h>
#include <Sddl.h>
static void* xmalloc(size_t size) {
void* ptr;
if ((ptr = malloc(size)) == NULL) {
printf("out of memory");
exit(EXIT_FAILURE);
}
return ptr;
}
#define SET_USER_ENV(folder_id, evn_variable) do { \
if (SHGetKnownFolderPath(&folder_id,0,NULL,&path) == S_OK) \
{ \
SetEnvironmentVariableW(evn_variable, path); \
CoTaskMemFree(path); \
} \
} while (0)
/* set user environment variables from user profile */
static void setup_session_user_vars()
{
/* retrieve and set env variables. */
HKEY reg_key = 0;
wchar_t *path;
wchar_t name[256];
wchar_t *data = NULL, *data_expanded = NULL, *path_value = NULL, *to_apply;
DWORD type, name_chars = 256, data_chars = 0, data_expanded_chars = 0, required, i = 0;
LONG ret;
SET_USER_ENV(FOLDERID_LocalAppData, L"LOCALAPPDATA");
SET_USER_ENV(FOLDERID_Profile, L"USERPROFILE");
SET_USER_ENV(FOLDERID_RoamingAppData, L"APPDATA");
ret = RegOpenKeyExW(HKEY_CURRENT_USER, L"Environment", 0, KEY_QUERY_VALUE, &reg_key);
if (ret != ERROR_SUCCESS)
//error("Error retrieving user environment variables. RegOpenKeyExW returned %d", ret);
return;
else while (1) {
to_apply = NULL;
required = data_chars * 2;
name_chars = 256;
ret = RegEnumValueW(reg_key, i++, name, &name_chars, 0, &type, (LPBYTE)data, &required);
if (ret == ERROR_NO_MORE_ITEMS)
break;
else if (ret == ERROR_MORE_DATA || required > data_chars * 2) {
if (data != NULL)
free(data);
data = xmalloc(required);
data_chars = required / 2;
i--;
continue;
}
else if (ret != ERROR_SUCCESS)
break;
if (type == REG_SZ)
to_apply = data;
else if (type == REG_EXPAND_SZ) {
required = ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
if (required > data_expanded_chars) {
if (data_expanded)
free(data_expanded);
data_expanded = xmalloc(required * 2);
data_expanded_chars = required;
ExpandEnvironmentStringsW(data, data_expanded, data_expanded_chars);
}
to_apply = data_expanded;
}
if (wcsicmp(name, L"PATH") == 0) {
if ((required = GetEnvironmentVariableW(L"PATH", NULL, 0)) != 0) {
/* "required" includes null term */
path_value = xmalloc((wcslen(to_apply) + 1 + required) * 2);
GetEnvironmentVariableW(L"PATH", path_value, required);
path_value[required - 1] = L';';
memcpy(path_value + required, to_apply, (wcslen(to_apply) + 1) * 2);
to_apply = path_value;
}
}
if (to_apply)
SetEnvironmentVariableW(name, to_apply);
}
if (reg_key)
RegCloseKey(reg_key);
if (data)
free(data);
if (data_expanded)
free(data_expanded);
if (path_value)
free(path_value);
RevertToSelf();
}
int b64_pton(char const *src, u_char *target, size_t targsize);
int
wmain(int ac, wchar_t **av)
{
int pty_requested = 0;
wchar_t *cmd = NULL, *cmd_b64 = NULL;
{
/* create job to hold all child processes */
HANDLE job = CreateJobObject(NULL, NULL);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info;
memset(&job_info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)))
return -1;
CloseHandle(job);
}
if ((ac == 1) || (ac == 2 && wcscmp(av[1], L"-nopty"))) {
pty_requested = 1;
cmd_b64 = ac == 2? av[1] : NULL;
} else if (ac <= 3 && wcscmp(av[1], L"-nopty") == 0)
cmd_b64 = ac == 3? av[2] : NULL;
else {
printf("ssh-shellhost received unexpected input arguments");
return -1;
}
setup_session_user_vars();
/* decode cmd_b64*/
if (cmd_b64) {
char *cmd_b64_utf8, *cmd_utf8;
if ((cmd_b64_utf8 = utf16_to_utf8(cmd_b64)) == NULL ||
/* strlen(b64) should be sufficient for decoded length */
(cmd_utf8 = malloc(strlen(cmd_b64_utf8))) == NULL) {
printf("ssh-shellhost - out of memory");
return -1;
}
memset(cmd_utf8, 0, strlen(cmd_b64_utf8));
if (b64_pton(cmd_b64_utf8, cmd_utf8, strlen(cmd_b64_utf8)) == -1 ||
(cmd = utf8_to_utf16(cmd_utf8)) == NULL) {
printf("ssh-shellhost encountered an internal error while decoding base64 cmdline");
return -1;
}
free(cmd_b64_utf8);
free(cmd_utf8);
}
if (pty_requested)
return start_with_pty(cmd);
else
return start_withno_pty(cmd);
}