Use a Console as input to drive a shell in sshd.exe

Fixes issues like cmd.exe shell not handling backspace, control-c.
Control-c is still a work in progress and will be fixed but backspace
processing works. This work when complete will make cmd.exe shell and
powershell work better for interactive users.
This commit is contained in:
quamrulmina 2015-10-23 19:10:06 -05:00
parent ce1d1340d9
commit 339912c24b
8 changed files with 299 additions and 33 deletions

View File

@ -2462,13 +2462,15 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
#else
if ( c->client_tty )
telProcessNetwork ( data, data_len ); // run it by ANSI engine if it is the ssh client
else
buffer_append(&c->output, data, data_len); // it is the sshd server, so pass it on
if ( c->isatty ) {
buffer_append(&c->input, data, data_len); // we echo the data if it is sshd server and pty interactive mode
if ( (data_len ==1) && (data[0] == '\b') )
buffer_append(&c->input, " \b", 2); // for backspace, we need to send space and another backspace for visual erase
else {
buffer_append(&c->output, data, data_len); // it is the sshd server, so pass it on
if ( c->isatty ) { // we echo the data if it is sshd server and pty interactive mode
buffer_append(&c->input, data, data_len);
if ( (data_len ==1) && (data[0] == '\b') )
buffer_append(&c->input, " \b", 2); // for backspace, we need to send space and another backspace for visual erase
}
}
#endif
}
packet_check_eom();

View File

@ -168,7 +168,10 @@ static int escape_pending1; /* Last character was an escape (proto1 only) */
static int last_was_cr; /* Last character was a newline. */
static int exit_status; /* Used to store the command exit status. */
static int stdin_eof; /* EOF has been encountered on stderr. */
static Buffer stdin_buffer; /* Buffer for stdin data. */
#ifndef WIN32_FIXME
static
#endif
Buffer stdin_buffer; /* Buffer for stdin data. */
static Buffer stdout_buffer; /* Buffer for stdout data. */
static Buffer stderr_buffer; /* Buffer for stderr data. */
static u_int buffer_high; /* Soft max buffer size. */

View File

@ -19,7 +19,7 @@ LDFLAGS=-L. @LDFLAGS@ -L/lib/win32api
WIN32COMPATFILES = daemon.o gettimeofday.o homedirhelp.o pwd.o sfds.o \
socket.o startupneeds.o strcasecmp.o syslog.o lsalogon.o lsastring.o \
stringhelp.o deskright.o win32auth.o kerberos.o cng_cipher.o ansiprsr.o console.o tnnet.o
stringhelp.o deskright.o win32auth.o kerberos.o cng_cipher.o ansiprsr.o console.o tnnet.o conio.o
WIN32COMPATLIB=@LIBWIN32COMPAT@

View File

@ -0,0 +1,164 @@
/* conio.c
* Author: Pragma Systems, Inc. <www.pragmasys.com>
* Contribution by Pragma Systems, Inc. for Microsoft openssh win32 port
* Copyright (c) 2011, 2015 Pragma Systems, Inc.
* All rights reserved
*
* Inserts data into Windows Console Input. WriteToConsole() API implemented.
*
* 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.
* 2. Binaries produced provide no direct or implied warranties or any
* guarantee of performance or suitability.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <console.h>
#include <conio.h>
COORD lastCursorLoc = { 0, 0 };
BYTE KeyboardState[256];
INPUT_RECORD srec;
DWORD dwGlobalConsoleMode ;
int WriteToConsole(HANDLE fd, unsigned char *buf, size_t len, size_t *dwWritten, void *flag)
{
static KEY_EVENT_RECORD *pkey;
static KEY_EVENT_RECORD *pkey2;
static INPUT_RECORD irec[2];
static BOOL bInitKeyboard = TRUE;
size_t ctr;
int rc;
DWORD dwRecords;
DWORD vkey;
BOOL bNeedToWait = TRUE;
CONSOLE_SCREEN_BUFFER_INFO csbi;
int scr_width = 80; /* screen horizontal width, e.g. 80 */
int scr_height = 25; /* screen vertical length, e.g. 25 */
char tmpbuf[2];
int local_echo = 0;
/*
* Need to set pkey and pkey2 which we use below. Initialize the keyboard state table.
*/
if (bInitKeyboard)
{
GetKeyboardState(KeyboardState);
bInitKeyboard = FALSE;
srec.EventType = KEY_EVENT;
srec.Event.KeyEvent.bKeyDown = TRUE;
srec.Event.KeyEvent.wRepeatCount = 1;
srec.Event.KeyEvent.wVirtualKeyCode = 0x10;
srec.Event.KeyEvent.wVirtualScanCode = 0x2a;
srec.Event.KeyEvent.uChar.AsciiChar = 0;
srec.Event.KeyEvent.uChar.UnicodeChar = 0;
srec.Event.KeyEvent.dwControlKeyState = 0x10;
irec[0].EventType = KEY_EVENT; /* init key down message */
pkey = &(irec[0].Event.KeyEvent);
pkey->wRepeatCount = 1;
pkey->bKeyDown = TRUE;
irec[1].EventType = KEY_EVENT; /* init key up message */
pkey2 = &(irec[1].Event.KeyEvent);
pkey2->wRepeatCount = 1;
pkey2->bKeyDown = FALSE;
}
// Stream mode processing
if (local_echo)
{
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
}
GetConsoleMode(fd, &dwGlobalConsoleMode);
ctr = 0;
while (ctr < len)
{
if (local_echo)
{
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
lastCursorLoc.Y = csbi.dwCursorPosition.Y;
lastCursorLoc.X = csbi.dwCursorPosition.X;
}
{
pkey->dwControlKeyState = 0x00000000;
pkey->uChar.AsciiChar = buf[ctr]; /* next char in ascii */
mbtowc(&(pkey->uChar.UnicodeChar), (const char *)&(buf[ctr]), 1);
vkey = VkKeyScan(pkey->uChar.AsciiChar);
if ((BYTE)(vkey >> 8) != 0xFF) // high order word
{
if (vkey & 0x0100 || (KeyboardState[VK_LSHIFT] & 0x80)) /* high word gives shift, ctrl, alt status */
pkey->dwControlKeyState |= SHIFT_PRESSED; /* shift key presssed*/
if (vkey & 0x0200 || (KeyboardState[VK_LCONTROL] & 0x80))
pkey->dwControlKeyState |= LEFT_CTRL_PRESSED; /* any ctrl really*/
if ((vkey & 0x0400) || (KeyboardState[VK_LMENU] & 0x80))
pkey->dwControlKeyState |= LEFT_ALT_PRESSED; /* any ALT really*/
}
if ((BYTE)vkey != 0xFF) // low order word
{
pkey->wVirtualKeyCode = (BYTE)vkey;
pkey->wVirtualScanCode = MapVirtualKey(pkey->wVirtualKeyCode, 0);
if (pkey->uChar.UnicodeChar == 0x1b) // stream mode fix for Admark ESC sequences
pkey->wVirtualKeyCode = 0x00db;
}
/* we need to mimic key up and key down */
if (pkey->dwControlKeyState & 0x0100)
{
srec.Event.KeyEvent.bKeyDown = TRUE;
srec.Event.KeyEvent.dwControlKeyState = 0x10;
WriteConsoleInput(fd, &srec, 1, &dwRecords); /* write shift down */
tmpbuf[0] = irec[0].Event.KeyEvent.uChar.AsciiChar;
tmpbuf[1] = '\0';
}
pkey->bKeyDown = TRUE; /*since pkey is mucked by others we do it again*/
/* dup these into key up message structure from key down message */
pkey2->wVirtualKeyCode = pkey->wVirtualKeyCode;
pkey2->wVirtualScanCode = pkey->wVirtualScanCode;
pkey2->uChar.AsciiChar = pkey->uChar.AsciiChar;
pkey2->uChar.UnicodeChar = pkey->uChar.UnicodeChar;
pkey2->dwControlKeyState = pkey->dwControlKeyState;
WriteConsoleInput(fd, irec, 2, &dwRecords); /* key down,up msgs */
tmpbuf[0] = irec[0].Event.KeyEvent.uChar.AsciiChar;
tmpbuf[1] = '\0';
if (pkey->dwControlKeyState & 0x0100)
{
srec.Event.KeyEvent.bKeyDown = FALSE;
srec.Event.KeyEvent.dwControlKeyState = 0x0;
WriteConsoleInput(fd, &srec, 1, &dwRecords); /* write shift up */
}
//if ((local_echo))
//{
// bNeedToWait = EchoInputCharacter(buf[ctr], &csbi.dwCursorPosition, dwGlobalConsoleMode);
//}
}
ctr++;
Sleep(0);
}
*dwWritten = len;
//netflush();
return 0;
}

View File

@ -2616,7 +2616,7 @@ int WSHELPwrite(int sfd, const char *buf, unsigned int max)
case SFD_TYPE_FD:
case SFD_TYPE_PIPE:
case SFD_TYPE_CONSOLE:
//case SFD_TYPE_CONSOLE:
{
ret = _write(sfd_to_fd(sfd), buf, max);
@ -2650,7 +2650,16 @@ int WSHELPwrite(int sfd, const char *buf, unsigned int max)
}
break;
}
}
case SFD_TYPE_CONSOLE:
{
//ret = _write(sfd_to_fd(sfd), buf, max);
DWORD dwWritten = 0 ;
ret = WriteToConsole(sfd_to_handle(sfd), buf, max, &dwWritten, 0) ;
ret = max ;
break;
}
}
DBG_MSG("<- WSHELPwrite(sfd = %d, ret = %d)...\n", sfd, ret);

View File

@ -549,6 +549,7 @@ do_exec_no_pty(Session *s, const char *command)
if (!command)
{
exec_command = s->pw->pw_shell;
//exec_command = "c:\\tools\\echoit.exe"; // temp
}
else
{
@ -560,8 +561,21 @@ do_exec_no_pty(Session *s, const char *command)
/*
* Create three socket pairs for stdin, stdout and stderr
*/
socketpair(sockin);
HANDLE wfdtocmd = -1;
if ( (!s -> is_subsystem) && (s ->ttyfd != -1))
{
wfdtocmd = GetStdHandle (STD_INPUT_HANDLE) ;
sockin[1] = allocate_sfd((int)wfdtocmd); // put the std input handle in our global general handle table
if (sockin[1] >= 0)
sfd_set_to_console(sockin[1]); // mark it as Console type
//allocate_standard_descriptor(STDIN_FILENO);
//allocate_standard_descriptor(wfdtocmd); // put the std input handle in our global general handle table
}
else
socketpair(sockin);
socketpair(sockout);
socketpair(sockerr);
@ -569,10 +583,14 @@ do_exec_no_pty(Session *s, const char *command)
debug3("sockout[0]: %d sockout[1]: %d", sockout[0], sockout[1]);
debug3("sockerr[0]: %d sockerr[1]: %d", sockerr[0], sockerr[1]);
crlf_sfd(sockin[1]);
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
crlf_sfd(sockin[1]);
crlf_sfd(sockout[1]);
SetHandleInformation(sfd_to_handle(sockin[1]), HANDLE_FLAG_INHERIT, 0);
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
SetHandleInformation(sfd_to_handle(sockin[1]), HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(sfd_to_handle(sockout[1]), HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(sfd_to_handle(sockerr[1]), HANDLE_FLAG_INHERIT, 0);
@ -583,13 +601,35 @@ do_exec_no_pty(Session *s, const char *command)
memset(&si, 0 , sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdInput = (HANDLE) sfd_to_handle(sockin[0]);
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.lpReserved = 0;
si.lpTitle = NULL; /* NULL means use exe name as title */
si.dwX = 0;
si.dwY = 0;
si.dwXSize = 80;
si.dwYSize = 25;
si.dwXCountChars = 80;
si.dwYCountChars = 25;
si.dwFillAttribute = 0;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE | STARTF_USECOUNTCHARS; // | STARTF_USESHOWWINDOW ;
si.wShowWindow = 0; // FALSE ;
si.cbReserved2 = 0;
si.lpReserved2 = 0;
if ( (!s -> is_subsystem) && (s ->ttyfd != -1) ) {
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE) ; // shell tty interactive session gets a console input for Win32
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
}
else {
si.hStdInput = (HANDLE) sfd_to_handle(sockin[0]);
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
}
//si.wShowWindow = SW_HIDE;
//si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.lpDesktop = L"winsta0\\default";
SetEnvironmentVariable("USER", s->pw->pw_name);
SetEnvironmentVariable("USERNAME", s->pw->pw_name);
SetEnvironmentVariable("LOGNAME", s->pw->pw_name);
@ -785,12 +825,12 @@ do_exec_no_pty(Session *s, const char *command)
GetUserName(name, &size);
//if (!(s -> is_subsystem)) {
if (!(s -> is_subsystem)) {
// Send to the remote client ANSI/VT Sequence so that they send us CRLF in place of LF
//Channel *c=channel_by_id ( s->chanid );
//buffer_append(&c->input, "\033[20h", 5);
//channel_output_poll();
//}
Channel *c=channel_by_id ( s->chanid );
buffer_append(&c->input, "\033[20h", 5);
channel_output_poll();
}
//if (s ->ttyfd != -1) {
// set the channel to tty interactive type
@ -876,8 +916,9 @@ do_exec_no_pty(Session *s, const char *command)
/*
* We are the parent. Close the child sides of the socket pairs.
*/
close(sockin[0]);
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
close(sockin[0]);
close(sockout[0]);
close(sockerr[0]);

27
ssh.c
View File

@ -296,12 +296,38 @@ static void CleanUpProxyProcess()
}
}
extern Buffer stdin_buffer; /* Buffer for stdin data. */
/*
* This function handles exit signal.
*/
BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
{
switch( dwCtrlType )
{
// Handle the CTRL-C signal.
case CTRL_C_EVENT:
// send CTRL_C code to the remote app via sshd server
//buffer_put_char(&stdin_buffer, 0x3); // control-c is decimal 3
//Beep( 750, 300 );
//return( TRUE ); // we have handled it. FALSE would be go to next handler
case CTRL_BREAK_EVENT:
// send CTRL_BREAK to the remote side ?
//return TRUE;
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
// send SHELL_CODE_TERMINATE to the remote side
//return FALSE ; // go to next handler
default:
break;
//return FALSE;
}
debug("Exit signal received...");
CleanUpProxyProcess();
@ -311,6 +337,7 @@ BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
cleanup_exit(0);
return TRUE;
}
#endif /* WIN32_FIXME */

34
sshd.c
View File

@ -1707,8 +1707,12 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
memset(&si, 0 , sizeof(STARTUPINFO));
char remotesoc[64];
snprintf ( remotesoc, sizeof(remotesoc), "%d", sfd_to_handle(*newsock));
SetEnvironmentVariable("SSHD_REMSOC", remotesoc);
si.cb = sizeof(STARTUPINFO);
si.hStdInput = (HANDLE) sfd_to_handle(*newsock);
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); //(HANDLE) sfd_to_handle(*newsock);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.wShowWindow = SW_HIDE;
@ -1886,7 +1890,20 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
{
debug("Exit signal received...");
switch( dwCtrlType )
{
case CTRL_C_EVENT:
return TRUE; // control C will be passed to shell but sshd wil not exit
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
break;
default:
break;
}
debug("Exit signal received...");
cleanup_exit(0);
@ -2776,19 +2793,22 @@ main(int ac, char **av)
}
else
{
STARTUPINFO si;
//STARTUPINFO si;
memset(&si, 0 , sizeof(STARTUPINFO));
//memset(&si, 0 , sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
//si.cb = sizeof(STARTUPINFO);
/*
* Get the stdin handle from process info to use for client
*/
GetStartupInfo(&si);
//GetStartupInfo(&si);
sock_in = sock_out = newsock = allocate_sfd(si.hStdInput);
int remotesochandle ;
remotesochandle = atoi( getenv("SSHD_REMSOC") );
sock_in = sock_out = newsock = allocate_sfd(remotesochandle) ; //si.hStdInput);
/*
* We don't have a startup_pipe