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:
parent
ce1d1340d9
commit
339912c24b
14
channels.c
14
channels.c
|
@ -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();
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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@
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
73
session.c
73
session.c
|
@ -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
27
ssh.c
|
@ -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
34
sshd.c
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue