Skip to content

Commit 339912c

Browse files
committed
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.
1 parent ce1d134 commit 339912c

File tree

8 files changed

+299
-33
lines changed

8 files changed

+299
-33
lines changed

channels.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,13 +2462,15 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
24622462
#else
24632463
if ( c->client_tty )
24642464
telProcessNetwork ( data, data_len ); // run it by ANSI engine if it is the ssh client
2465-
else
2466-
buffer_append(&c->output, data, data_len); // it is the sshd server, so pass it on
2467-
if ( c->isatty ) {
2468-
buffer_append(&c->input, data, data_len); // we echo the data if it is sshd server and pty interactive mode
2469-
if ( (data_len ==1) && (data[0] == '\b') )
2470-
buffer_append(&c->input, " \b", 2); // for backspace, we need to send space and another backspace for visual erase
2465+
else {
2466+
buffer_append(&c->output, data, data_len); // it is the sshd server, so pass it on
2467+
if ( c->isatty ) { // we echo the data if it is sshd server and pty interactive mode
2468+
buffer_append(&c->input, data, data_len);
2469+
if ( (data_len ==1) && (data[0] == '\b') )
2470+
buffer_append(&c->input, " \b", 2); // for backspace, we need to send space and another backspace for visual erase
2471+
}
24712472
}
2473+
24722474
#endif
24732475
}
24742476
packet_check_eom();

clientloop.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,10 @@ static int escape_pending1; /* Last character was an escape (proto1 only) */
168168
static int last_was_cr; /* Last character was a newline. */
169169
static int exit_status; /* Used to store the command exit status. */
170170
static int stdin_eof; /* EOF has been encountered on stderr. */
171-
static Buffer stdin_buffer; /* Buffer for stdin data. */
171+
#ifndef WIN32_FIXME
172+
static
173+
#endif
174+
Buffer stdin_buffer; /* Buffer for stdin data. */
172175
static Buffer stdout_buffer; /* Buffer for stdout data. */
173176
static Buffer stderr_buffer; /* Buffer for stderr data. */
174177
static u_int buffer_high; /* Soft max buffer size. */

contrib/win32/win32compat/Makefile.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ LDFLAGS=-L. @LDFLAGS@ -L/lib/win32api
1919

2020
WIN32COMPATFILES = daemon.o gettimeofday.o homedirhelp.o pwd.o sfds.o \
2121
socket.o startupneeds.o strcasecmp.o syslog.o lsalogon.o lsastring.o \
22-
stringhelp.o deskright.o win32auth.o kerberos.o cng_cipher.o ansiprsr.o console.o tnnet.o
22+
stringhelp.o deskright.o win32auth.o kerberos.o cng_cipher.o ansiprsr.o console.o tnnet.o conio.o
2323

2424
WIN32COMPATLIB=@LIBWIN32COMPAT@
2525

contrib/win32/win32compat/conio.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/* conio.c
2+
* Author: Pragma Systems, Inc. <www.pragmasys.com>
3+
* Contribution by Pragma Systems, Inc. for Microsoft openssh win32 port
4+
* Copyright (c) 2011, 2015 Pragma Systems, Inc.
5+
* All rights reserved
6+
*
7+
* Inserts data into Windows Console Input. WriteToConsole() API implemented.
8+
*
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice.
13+
* 2. Binaries produced provide no direct or implied warranties or any
14+
* guarantee of performance or suitability.
15+
*/
16+
17+
#include <stdlib.h>
18+
#include <stdio.h>
19+
#include <string.h>
20+
21+
#include <windows.h>
22+
23+
#include <console.h>
24+
#include <conio.h>
25+
26+
COORD lastCursorLoc = { 0, 0 };
27+
BYTE KeyboardState[256];
28+
INPUT_RECORD srec;
29+
DWORD dwGlobalConsoleMode ;
30+
31+
int WriteToConsole(HANDLE fd, unsigned char *buf, size_t len, size_t *dwWritten, void *flag)
32+
{
33+
static KEY_EVENT_RECORD *pkey;
34+
static KEY_EVENT_RECORD *pkey2;
35+
static INPUT_RECORD irec[2];
36+
static BOOL bInitKeyboard = TRUE;
37+
size_t ctr;
38+
int rc;
39+
DWORD dwRecords;
40+
DWORD vkey;
41+
BOOL bNeedToWait = TRUE;
42+
CONSOLE_SCREEN_BUFFER_INFO csbi;
43+
44+
int scr_width = 80; /* screen horizontal width, e.g. 80 */
45+
int scr_height = 25; /* screen vertical length, e.g. 25 */
46+
char tmpbuf[2];
47+
int local_echo = 0;
48+
49+
/*
50+
* Need to set pkey and pkey2 which we use below. Initialize the keyboard state table.
51+
*/
52+
if (bInitKeyboard)
53+
{
54+
GetKeyboardState(KeyboardState);
55+
bInitKeyboard = FALSE;
56+
srec.EventType = KEY_EVENT;
57+
srec.Event.KeyEvent.bKeyDown = TRUE;
58+
srec.Event.KeyEvent.wRepeatCount = 1;
59+
srec.Event.KeyEvent.wVirtualKeyCode = 0x10;
60+
srec.Event.KeyEvent.wVirtualScanCode = 0x2a;
61+
srec.Event.KeyEvent.uChar.AsciiChar = 0;
62+
srec.Event.KeyEvent.uChar.UnicodeChar = 0;
63+
srec.Event.KeyEvent.dwControlKeyState = 0x10;
64+
65+
irec[0].EventType = KEY_EVENT; /* init key down message */
66+
pkey = &(irec[0].Event.KeyEvent);
67+
pkey->wRepeatCount = 1;
68+
pkey->bKeyDown = TRUE;
69+
70+
irec[1].EventType = KEY_EVENT; /* init key up message */
71+
pkey2 = &(irec[1].Event.KeyEvent);
72+
pkey2->wRepeatCount = 1;
73+
pkey2->bKeyDown = FALSE;
74+
}
75+
76+
// Stream mode processing
77+
if (local_echo)
78+
{
79+
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
80+
}
81+
82+
GetConsoleMode(fd, &dwGlobalConsoleMode);
83+
84+
ctr = 0;
85+
while (ctr < len)
86+
{
87+
if (local_echo)
88+
{
89+
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
90+
lastCursorLoc.Y = csbi.dwCursorPosition.Y;
91+
lastCursorLoc.X = csbi.dwCursorPosition.X;
92+
}
93+
{
94+
pkey->dwControlKeyState = 0x00000000;
95+
pkey->uChar.AsciiChar = buf[ctr]; /* next char in ascii */
96+
97+
mbtowc(&(pkey->uChar.UnicodeChar), (const char *)&(buf[ctr]), 1);
98+
vkey = VkKeyScan(pkey->uChar.AsciiChar);
99+
100+
if ((BYTE)(vkey >> 8) != 0xFF) // high order word
101+
{
102+
if (vkey & 0x0100 || (KeyboardState[VK_LSHIFT] & 0x80)) /* high word gives shift, ctrl, alt status */
103+
pkey->dwControlKeyState |= SHIFT_PRESSED; /* shift key presssed*/
104+
if (vkey & 0x0200 || (KeyboardState[VK_LCONTROL] & 0x80))
105+
pkey->dwControlKeyState |= LEFT_CTRL_PRESSED; /* any ctrl really*/
106+
if ((vkey & 0x0400) || (KeyboardState[VK_LMENU] & 0x80))
107+
pkey->dwControlKeyState |= LEFT_ALT_PRESSED; /* any ALT really*/
108+
}
109+
if ((BYTE)vkey != 0xFF) // low order word
110+
{
111+
pkey->wVirtualKeyCode = (BYTE)vkey;
112+
pkey->wVirtualScanCode = MapVirtualKey(pkey->wVirtualKeyCode, 0);
113+
if (pkey->uChar.UnicodeChar == 0x1b) // stream mode fix for Admark ESC sequences
114+
pkey->wVirtualKeyCode = 0x00db;
115+
116+
117+
}
118+
119+
/* we need to mimic key up and key down */
120+
if (pkey->dwControlKeyState & 0x0100)
121+
{
122+
srec.Event.KeyEvent.bKeyDown = TRUE;
123+
srec.Event.KeyEvent.dwControlKeyState = 0x10;
124+
WriteConsoleInput(fd, &srec, 1, &dwRecords); /* write shift down */
125+
tmpbuf[0] = irec[0].Event.KeyEvent.uChar.AsciiChar;
126+
tmpbuf[1] = '\0';
127+
}
128+
129+
pkey->bKeyDown = TRUE; /*since pkey is mucked by others we do it again*/
130+
131+
/* dup these into key up message structure from key down message */
132+
pkey2->wVirtualKeyCode = pkey->wVirtualKeyCode;
133+
pkey2->wVirtualScanCode = pkey->wVirtualScanCode;
134+
pkey2->uChar.AsciiChar = pkey->uChar.AsciiChar;
135+
pkey2->uChar.UnicodeChar = pkey->uChar.UnicodeChar;
136+
pkey2->dwControlKeyState = pkey->dwControlKeyState;
137+
138+
WriteConsoleInput(fd, irec, 2, &dwRecords); /* key down,up msgs */
139+
tmpbuf[0] = irec[0].Event.KeyEvent.uChar.AsciiChar;
140+
tmpbuf[1] = '\0';
141+
if (pkey->dwControlKeyState & 0x0100)
142+
{
143+
srec.Event.KeyEvent.bKeyDown = FALSE;
144+
srec.Event.KeyEvent.dwControlKeyState = 0x0;
145+
WriteConsoleInput(fd, &srec, 1, &dwRecords); /* write shift up */
146+
147+
}
148+
//if ((local_echo))
149+
//{
150+
// bNeedToWait = EchoInputCharacter(buf[ctr], &csbi.dwCursorPosition, dwGlobalConsoleMode);
151+
//}
152+
}
153+
ctr++;
154+
Sleep(0);
155+
}
156+
157+
*dwWritten = len;
158+
159+
//netflush();
160+
161+
return 0;
162+
}
163+
164+

contrib/win32/win32compat/socket.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,7 +2616,7 @@ int WSHELPwrite(int sfd, const char *buf, unsigned int max)
26162616

26172617
case SFD_TYPE_FD:
26182618
case SFD_TYPE_PIPE:
2619-
case SFD_TYPE_CONSOLE:
2619+
//case SFD_TYPE_CONSOLE:
26202620
{
26212621
ret = _write(sfd_to_fd(sfd), buf, max);
26222622

@@ -2650,7 +2650,16 @@ int WSHELPwrite(int sfd, const char *buf, unsigned int max)
26502650
}
26512651

26522652
break;
2653-
}
2653+
}
2654+
case SFD_TYPE_CONSOLE:
2655+
{
2656+
//ret = _write(sfd_to_fd(sfd), buf, max);
2657+
DWORD dwWritten = 0 ;
2658+
ret = WriteToConsole(sfd_to_handle(sfd), buf, max, &dwWritten, 0) ;
2659+
ret = max ;
2660+
2661+
break;
2662+
}
26542663
}
26552664

26562665
DBG_MSG("<- WSHELPwrite(sfd = %d, ret = %d)...\n", sfd, ret);

session.c

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ do_exec_no_pty(Session *s, const char *command)
549549
if (!command)
550550
{
551551
exec_command = s->pw->pw_shell;
552+
//exec_command = "c:\\tools\\echoit.exe"; // temp
552553
}
553554
else
554555
{
@@ -560,19 +561,36 @@ do_exec_no_pty(Session *s, const char *command)
560561
/*
561562
* Create three socket pairs for stdin, stdout and stderr
562563
*/
563-
564-
socketpair(sockin);
564+
565+
HANDLE wfdtocmd = -1;
566+
if ( (!s -> is_subsystem) && (s ->ttyfd != -1))
567+
{
568+
wfdtocmd = GetStdHandle (STD_INPUT_HANDLE) ;
569+
sockin[1] = allocate_sfd((int)wfdtocmd); // put the std input handle in our global general handle table
570+
if (sockin[1] >= 0)
571+
sfd_set_to_console(sockin[1]); // mark it as Console type
572+
573+
//allocate_standard_descriptor(STDIN_FILENO);
574+
//allocate_standard_descriptor(wfdtocmd); // put the std input handle in our global general handle table
575+
}
576+
else
577+
socketpair(sockin);
578+
565579
socketpair(sockout);
566580
socketpair(sockerr);
567581

568582
debug3("sockin[0]: %d sockin[1]: %d", sockin[0], sockin[1]);
569583
debug3("sockout[0]: %d sockout[1]: %d", sockout[0], sockout[1]);
570584
debug3("sockerr[0]: %d sockerr[1]: %d", sockerr[0], sockerr[1]);
571585

572-
crlf_sfd(sockin[1]);
586+
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
587+
crlf_sfd(sockin[1]);
588+
573589
crlf_sfd(sockout[1]);
574590

575-
SetHandleInformation(sfd_to_handle(sockin[1]), HANDLE_FLAG_INHERIT, 0);
591+
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
592+
SetHandleInformation(sfd_to_handle(sockin[1]), HANDLE_FLAG_INHERIT, 0);
593+
576594
SetHandleInformation(sfd_to_handle(sockout[1]), HANDLE_FLAG_INHERIT, 0);
577595
SetHandleInformation(sfd_to_handle(sockerr[1]), HANDLE_FLAG_INHERIT, 0);
578596

@@ -583,13 +601,35 @@ do_exec_no_pty(Session *s, const char *command)
583601
memset(&si, 0 , sizeof(STARTUPINFO));
584602

585603
si.cb = sizeof(STARTUPINFO);
586-
si.hStdInput = (HANDLE) sfd_to_handle(sockin[0]);
587-
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
588-
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
589-
si.wShowWindow = SW_HIDE;
590-
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
604+
si.lpReserved = 0;
605+
si.lpTitle = NULL; /* NULL means use exe name as title */
606+
si.dwX = 0;
607+
si.dwY = 0;
608+
si.dwXSize = 80;
609+
si.dwYSize = 25;
610+
si.dwXCountChars = 80;
611+
si.dwYCountChars = 25;
612+
si.dwFillAttribute = 0;
613+
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE | STARTF_USECOUNTCHARS; // | STARTF_USESHOWWINDOW ;
614+
si.wShowWindow = 0; // FALSE ;
615+
si.cbReserved2 = 0;
616+
si.lpReserved2 = 0;
617+
618+
if ( (!s -> is_subsystem) && (s ->ttyfd != -1) ) {
619+
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE) ; // shell tty interactive session gets a console input for Win32
620+
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
621+
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
622+
}
623+
else {
624+
si.hStdInput = (HANDLE) sfd_to_handle(sockin[0]);
625+
si.hStdOutput = (HANDLE) sfd_to_handle(sockout[0]);
626+
si.hStdError = (HANDLE) sfd_to_handle(sockerr[0]);
627+
}
628+
//si.wShowWindow = SW_HIDE;
629+
//si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
591630
si.lpDesktop = L"winsta0\\default";
592631

632+
593633
SetEnvironmentVariable("USER", s->pw->pw_name);
594634
SetEnvironmentVariable("USERNAME", s->pw->pw_name);
595635
SetEnvironmentVariable("LOGNAME", s->pw->pw_name);
@@ -785,12 +825,12 @@ do_exec_no_pty(Session *s, const char *command)
785825

786826
GetUserName(name, &size);
787827

788-
//if (!(s -> is_subsystem)) {
828+
if (!(s -> is_subsystem)) {
789829
// Send to the remote client ANSI/VT Sequence so that they send us CRLF in place of LF
790-
//Channel *c=channel_by_id ( s->chanid );
791-
//buffer_append(&c->input, "\033[20h", 5);
792-
//channel_output_poll();
793-
//}
830+
Channel *c=channel_by_id ( s->chanid );
831+
buffer_append(&c->input, "\033[20h", 5);
832+
channel_output_poll();
833+
}
794834

795835
//if (s ->ttyfd != -1) {
796836
// set the channel to tty interactive type
@@ -876,8 +916,9 @@ do_exec_no_pty(Session *s, const char *command)
876916
/*
877917
* We are the parent. Close the child sides of the socket pairs.
878918
*/
879-
880-
close(sockin[0]);
919+
if ( (s -> is_subsystem) || (s ->ttyfd == -1))
920+
close(sockin[0]);
921+
881922
close(sockout[0]);
882923
close(sockerr[0]);
883924

0 commit comments

Comments
 (0)