From 711fee25f2f80c9e2fad0530f9747e774a6b192c Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 28 Dec 2023 14:29:02 -0800 Subject: [PATCH 1/3] wolfSSHd Terminal 1. Rename the stashed window size values. 2. Set the terminal modes after the child process is running. 3. Decode the modes list from the pty-request message. 4. Store the modes list for later use. --- apps/wolfsshd/wolfsshd.c | 14 +- src/internal.c | 271 +++++++++++++++++++++++++++++++++++++-- sshd_config | 0 wolfssh/internal.h | 14 +- wolfssh/ssh.h | 1 + 5 files changed, 275 insertions(+), 25 deletions(-) create mode 100644 sshd_config diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 5a98e5cf4..bee94cda8 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -1323,19 +1323,21 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, /* set initial size of terminal based on saved size */ #if defined(HAVE_SYS_IOCTL_H) + wolfSSH_DoModes(ssh->modes, ssh->modesSz, childFd); { - struct winsize s; + struct winsize s = {0}; + + s.ws_col = ssh->widthChar; + s.ws_row = ssh->heightRows; + s.ws_xpixel = ssh->widthPixels; + s.ws_ypixel = ssh->heightPixels; - WMEMSET(&s, 0, sizeof s); - s.ws_col = ssh->curX; - s.ws_row = ssh->curY; - s.ws_xpixel = ssh->curXP; - s.ws_ypixel = ssh->curYP; ioctl(childFd, TIOCSWINSZ, &s); } #endif wolfSSH_SetTerminalResizeCtx(ssh, (void*)&childFd); + while (ChildRunning) { byte tmp[2]; fd_set readFds; diff --git a/src/internal.c b/src/internal.c index 7e9727cf6..7f7b54884 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7132,6 +7132,243 @@ static int DoChannelClose(WOLFSSH* ssh, } +#define TTY_SET_CHAR(x,y,z) (x)[(y)] = (byte)(z) +#define TTY_SET_FLAG(x,y,z) (x) = (z) ? ((x) | (y)) : ((x) & ~(y)) + +int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd) +{ + WOLFSSH_TERMIOS term; + word32 idx = 0, arg; + + if (!modes || !modesSz || (modesSz % TERMINAL_MODE_SZ > 1)) + return -1; + /* + * Modes is a list of opcode-argument pairs. The opcodes are + * bytes and the arguments are uint32s. TTY_OP_END is an opcode + * that terminates the list. Of course, it isn't clear if + * TTY_OP_END has an arguement or note. The RFC doesn't say, + * but in operation it usually doesn't. Allow for an odd single + * byte left over. + */ + + tcgetattr(fd, &term); + + while (idx < modesSz && modes[idx] != WOLFSSH_TTY_OP_END + && modes[idx] < WOLFSSH_TTY_INVALID) { + + ato32(modes + idx + 1, &arg); + + switch (modes[idx]) { + /* Special Control Characters (c_cc) */ + case WOLFSSH_VINTR: + TTY_SET_CHAR(term.c_cc, VINTR, arg); + break; + case WOLFSSH_VQUIT: + TTY_SET_CHAR(term.c_cc, VQUIT, arg); + break; + case WOLFSSH_VERASE: + TTY_SET_CHAR(term.c_cc, VERASE, arg); + break; + case WOLFSSH_VKILL: + TTY_SET_CHAR(term.c_cc, VKILL, arg); + break; + case WOLFSSH_VEOF: + TTY_SET_CHAR(term.c_cc, VEOF, arg); + break; + case WOLFSSH_VEOL: + TTY_SET_CHAR(term.c_cc, VEOL, arg); + break; + case WOLFSSH_VEOL2: + TTY_SET_CHAR(term.c_cc, VEOL2, arg); + break; + case WOLFSSH_VSTART: + TTY_SET_CHAR(term.c_cc, VSTART, arg); + break; + case WOLFSSH_VSTOP: + TTY_SET_CHAR(term.c_cc, VSTOP, arg); + break; + case WOLFSSH_VSUSP: + TTY_SET_CHAR(term.c_cc, VSUSP, arg); + break; + case WOLFSSH_VDSUSP: + #ifdef VDSUSP + TTY_SET_CHAR(term.c_cc, VDSUSP, arg); + #endif + break; + case WOLFSSH_VREPRINT: + TTY_SET_CHAR(term.c_cc, VREPRINT, arg); + break; + case WOLFSSH_VWERASE: + TTY_SET_CHAR(term.c_cc, VWERASE, arg); + break; + case WOLFSSH_VLNEXT: + TTY_SET_CHAR(term.c_cc, VLNEXT, arg); + break; + case WOLFSSH_VFLUSH: + #ifdef VFLUSH + TTY_SET_CHAR(term.c_cc, VFLUSH, arg); + #endif + break; + case WOLFSSH_VSWTCH: + #ifdef VSWTCH + TTY_SET_CHAR(term.c_cc, VSWTCH, arg); + #endif + break; + case WOLFSSH_VSTATUS: + #ifdef VSTATUS + TTY_SET_CHAR(term.c_cc, VSTATUS, arg); + #endif + break; + case WOLFSSH_VDISCARD: + TTY_SET_CHAR(term.c_cc, VDISCARD, arg); + break; + + /* Input Modes (c_iflag) */ + case WOLFSSH_IGNPAR: + TTY_SET_FLAG(term.c_iflag, IGNPAR, arg); + break; + case WOLFSSH_PARMRK: + TTY_SET_FLAG(term.c_iflag, PARMRK, arg); + break; + case WOLFSSH_INPCK: + TTY_SET_FLAG(term.c_iflag, INPCK, arg); + break; + case WOLFSSH_ISTRIP: + TTY_SET_FLAG(term.c_iflag, ISTRIP, arg); + break; + case WOLFSSH_INLCR: + TTY_SET_FLAG(term.c_iflag, INLCR, arg); + break; + case WOLFSSH_IGNCR: + TTY_SET_FLAG(term.c_iflag, IGNCR, arg); + break; + case WOLFSSH_ICRNL: + TTY_SET_FLAG(term.c_iflag, ICRNL, arg); + break; + case WOLFSSH_IUCLC: + #ifdef IUCLC + TTY_SET_FLAG(term.c_iflag, IUCLC, arg); + #endif + break; + case WOLFSSH_IXON: + TTY_SET_FLAG(term.c_iflag, IXON, arg); + break; + case WOLFSSH_IXANY: + TTY_SET_FLAG(term.c_iflag, IXANY, arg); + break; + case WOLFSSH_IXOFF: + TTY_SET_FLAG(term.c_iflag, IXOFF, arg); + break; + case WOLFSSH_IMAXBEL: + TTY_SET_FLAG(term.c_iflag, IMAXBEL, arg); + break; + case WOLFSSH_IUTF8: + #ifdef IUTF8 + TTY_SET_FLAG(term.c_iflag, IUTF8, arg); + #endif + break; + + /* Local Modes (c_lflag) */ + case WOLFSSH_ISIG: + TTY_SET_FLAG(term.c_lflag, ISIG, arg); + break; + case WOLFSSH_ICANON: + TTY_SET_FLAG(term.c_lflag, ICANON, arg); + break; + case WOLFSSH_XCASE: + #ifdef XCASE + TTY_SET_FLAG(term.c_lflag, XCASE, arg); + #endif + break; + case WOLFSSH_ECHO: + TTY_SET_FLAG(term.c_lflag, ECHO, arg); + break; + case WOLFSSH_ECHOE: + TTY_SET_FLAG(term.c_lflag, ECHOE, arg); + break; + case WOLFSSH_ECHOK: + TTY_SET_FLAG(term.c_lflag, ECHOK, arg); + break; + case WOLFSSH_ECHONL: + TTY_SET_FLAG(term.c_lflag, ECHONL, arg); + break; + case WOLFSSH_NOFLSH: + TTY_SET_FLAG(term.c_lflag, NOFLSH, arg); + break; + case WOLFSSH_TOSTOP: + TTY_SET_FLAG(term.c_lflag, TOSTOP, arg); + break; + case WOLFSSH_IEXTEN: + TTY_SET_FLAG(term.c_lflag, IEXTEN, arg); + break; + case WOLFSSH_ECHOCTL: + TTY_SET_FLAG(term.c_lflag, ECHOCTL, arg); + break; + case WOLFSSH_ECHOKE: + TTY_SET_FLAG(term.c_lflag, ECHOKE, arg); + break; + case WOLFSSH_PENDIN: + #ifdef PENDIN + TTY_SET_FLAG(term.c_lflag, PENDIN, arg); + #endif + break; + + /* Output Modes (c_oflag) */ + case WOLFSSH_OPOST: + TTY_SET_FLAG(term.c_lflag, OPOST, arg); + break; + case WOLFSSH_OLCUC: + #ifdef OLCUC + TTY_SET_FLAG(term.c_lflag, OLCUC, arg); + #endif + break; + case WOLFSSH_ONLCR: + TTY_SET_FLAG(term.c_lflag, ONLCR, arg); + break; + case WOLFSSH_OCRNL: + TTY_SET_FLAG(term.c_lflag, OCRNL, arg); + break; + case WOLFSSH_ONOCR: + TTY_SET_FLAG(term.c_lflag, ONOCR, arg); + break; + case WOLFSSH_ONLRET: + TTY_SET_FLAG(term.c_lflag, ONLRET, arg); + break; + + /* Control Modes (c_cflag) */ + case WOLFSSH_CS7: + TTY_SET_FLAG(term.c_cflag, CS7, arg); + break; + case WOLFSSH_CS8: + TTY_SET_FLAG(term.c_cflag, CS8, arg); + break; + case WOLFSSH_PARENB: + TTY_SET_FLAG(term.c_cflag, PARENB, arg); + break; + case WOLFSSH_PARODD: + TTY_SET_FLAG(term.c_cflag, PARODD, arg); + break; + + /* Baud Rates */ + case WOLFSSH_TTY_OP_ISPEED: + cfsetispeed(&term, (speed_t)arg); + break; + case WOLFSSH_TTY_OP_OSPEED: + cfsetospeed(&term, (speed_t)arg); + break; + + default: + break; + } + idx += TERMINAL_MODE_SZ; + } + + tcsetattr(fd, TCSANOW, &term); + + return 0; +} + + static int DoChannelRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { @@ -7189,24 +7426,27 @@ static int DoChannelRequest(WOLFSSH* ssh, ret = GetUint32(&heightPixels, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetStringRef(&modesSz, &modes, buf, len, &begin); - - WOLFSSH_UNUSED(modes); - WOLFSSH_UNUSED(modesSz); - if (ret == WS_SUCCESS) { + ssh->modes = (byte*)WMALLOC(modesSz, ssh->ctx->heap, 0); + if (ssh->modes == NULL) + ret = WS_MEMORY_E; + } + if (ret == WS_SUCCESS) { + ssh->modesSz = modesSz; + WMEMCPY(ssh->modes, modes, modesSz); WLOG(WS_LOG_DEBUG, " term = %s", term); WLOG(WS_LOG_DEBUG, " widthChar = %u", widthChar); WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows); WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels); WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels); - ssh->curX = widthChar; - ssh->curY = heightRows; - ssh->curXP = widthPixels; - ssh->curYP = heightPixels; + ssh->widthChar = widthChar; + ssh->heightRows = heightRows; + ssh->widthPixels = widthPixels; + ssh->heightPixels = heightPixels; if (ssh->termResizeCb) { if (ssh->termResizeCb(ssh, widthChar, heightRows, - widthPixels, heightPixels, ssh->termCtx) - != WS_SUCCESS) { + widthPixels, heightPixels, + ssh->termCtx) != WS_SUCCESS) { ret = WS_FATAL_ERROR; } } @@ -7276,11 +7516,14 @@ static int DoChannelRequest(WOLFSSH* ssh, WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows); WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels); WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels); - ssh->curX = widthChar; - ssh->curY = heightRows; + ssh->widthChar = widthChar; + ssh->heightRows = heightRows; + ssh->widthPixels = widthPixels; + ssh->heightPixels = heightPixels; if (ssh->termResizeCb) { - if (ssh->termResizeCb(ssh, widthChar, heightRows, widthPixels, - heightPixels, ssh->termCtx) != WS_SUCCESS) { + if (ssh->termResizeCb(ssh, widthChar, heightRows, + widthPixels, heightPixels, + ssh->termCtx) != WS_SUCCESS) { ret = WS_FATAL_ERROR; } } diff --git a/sshd_config b/sshd_config new file mode 100644 index 000000000..e69de29bb diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 3cd5b3776..c31b229e7 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -378,6 +378,7 @@ enum { #define UINT32_SZ 4 #define LENGTH_SZ UINT32_SZ #define SSH_PROTO_SZ 7 /* "SSH-2.0" */ +#define TERMINAL_MODE_SZ 5 /* opcode byte + argument uint32 */ #define AEAD_IMP_IV_SZ 4 #define AEAD_EXP_IV_SZ 8 #define AEAD_NONCE_SZ (AEAD_IMP_IV_SZ+AEAD_EXP_IV_SZ) @@ -814,10 +815,12 @@ struct WOLFSSH { #ifdef WOLFSSH_TERM WS_CallbackTerminalSize termResizeCb; void* termCtx; - word32 curX; /* current terminal width */ - word32 curY; /* current terminal height */ - word32 curXP; /* pixel width */ - word32 curYP; /* pixel height */ + word32 widthChar; /* current terminal width */ + word32 heightRows; /* current terminal height */ + word32 widthPixels; /* pixel width */ + word32 heightPixels; /* pixel height */ + byte* modes; + word32 modesSz; #endif }; @@ -1262,7 +1265,8 @@ enum TerminalModes { WOLFSSH_PARENB, WOLFSSH_PARODD, WOLFSSH_TTY_OP_ISPEED = 128, - WOLFSSH_TTY_OP_OSPEED + WOLFSSH_TTY_OP_OSPEED, + WOLFSSH_TTY_INVALID = 160 }; #endif /* WOLFSSH_TERM */ diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index ab315daad..10da1f1c7 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -282,6 +282,7 @@ typedef enum { WOLFSSH_SESSION_TERMINAL, } WS_SessionType; +WOLFSSH_API int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd); WOLFSSH_API WS_SessionType wolfSSH_GetSessionType(const WOLFSSH*); WOLFSSH_API const char* wolfSSH_GetSessionCommand(const WOLFSSH*); WOLFSSH_API int wolfSSH_SetChannelType(WOLFSSH*, byte, byte*, word32); From 92c4242b1ff092f8b47cadbbad18777f7f4a069f Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 28 Dec 2023 15:20:11 -0800 Subject: [PATCH 2/3] wolfSSHd Terminal 1. Prep the SHELL variable inherited by the new shell to be equal to the user's shell. 2. Prep the new shell's $0 variable to be equal to the shell name prefixed with a '-', ie "/bin/bash" becomes "-bash". --- apps/wolfsshd/wolfsshd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index bee94cda8..b8b3673f7 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -1204,6 +1204,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, /* Child process */ const char *args[] = {"-sh", NULL, NULL, NULL}; char cmd[MAX_COMMAND_SZ]; + char shell[MAX_COMMAND_SZ]; int ret; signal(SIGINT, SIG_DFL); @@ -1258,6 +1259,25 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, setenv("HOME", pPasswd->pw_dir, 1); setenv("LOGNAME", pPasswd->pw_name, 1); + setenv("SHELL", pPasswd->pw_shell, 1); + + if (pPasswd->pw_shell) { + word32 shellSz = (word32)WSTRLEN(pPasswd->pw_shell); + + if (shellSz < sizeof(shell)) { + char* cursor; + char* start; + + WSTRNCPY(shell, pPasswd->pw_shell, sizeof(shell)); + cursor = shell; + do { + start = cursor; + *cursor = '-'; + cursor = WSTRCHR(start, '/'); + } while (cursor && *cursor != '\0'); + args[0] = start; + } + } rc = chdir(pPasswd->pw_dir); if (rc != 0) { From 72d083db405b401d7fb270cab9959e60ed4d7725 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 29 Dec 2023 16:38:34 -0800 Subject: [PATCH 3/3] wolfSSHd Terminal 1. Modified the ssh terminal size test to be agnostic to the version of sed used. 2. Add some guards around the mode setting code for ioctl() availability so it would build for Windows. --- apps/wolfsshd/test/sshd_term_size_test.sh | 18 ++++++++---------- src/internal.c | 6 ++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/wolfsshd/test/sshd_term_size_test.sh b/apps/wolfsshd/test/sshd_term_size_test.sh index 22d2f3a33..0a25fad54 100755 --- a/apps/wolfsshd/test/sshd_term_size_test.sh +++ b/apps/wolfsshd/test/sshd_term_size_test.sh @@ -26,16 +26,16 @@ COL=`tmux display -p -t test '#{pane_width}'` ROW=`tmux display -p -t test '#{pane_height}'` # get the terminals columns and lines -tmux send-keys -t test 'echo col=$COLUMNS row=$LINES' +tmux send-keys -t test 'echo;echo $COLUMNS $LINES;echo' tmux send-keys -t test 'ENTER' tmux capture-pane -t test -RESULT=`tmux show-buffer | grep -v echo | grep -v rejecting | grep "col="` +RESULT=$(tmux show-buffer | grep '^[0-9]* [0-9]*$') echo "$RESULT" echo "" echo "" -ROW_FOUND=`echo "$RESULT" | sed -e 's/.*[^0-9]\([0-9]\+\)[^0-9]*$/\1/'` -COL_FOUND=`echo "$RESULT" | sed -r 's/^[^0-9]*([0-9]+).*$/\1/'` +ROW_FOUND=$(echo "$RESULT" | sed -e 's/[0-9]* \([0-9]*\)/\1/') +COL_FOUND=$(echo "$RESULT" | sed -e 's/\([0-9]*\) [0-9]*/\1/') if [ "$COL" != "$COL_FOUND" ]; then echo "Col found was $COL_FOUND which does not match expected $COL" @@ -67,15 +67,13 @@ tmux new-session -d -x 50 -y 10 -s test "$TEST_CLIENT -t -u $USER -i $PRIVATE_KE # give the command a second to establish SSH connection sleep 0.5 -echo "New COL=$COL ROW=$ROW" - -tmux send-keys -t test 'echo col=$COLUMNS row=$LINES' +tmux send-keys -t test 'echo;echo $COLUMNS $LINES;echo' tmux send-keys -t test 'ENTER' tmux capture-pane -t test -RESULT=`tmux show-buffer | grep -v echo | grep -v rejecting | grep "col="` +RESULT=$(tmux show-buffer | grep '^[0-9]* [0-9]*$') -ROW_FOUND=`echo "$RESULT" | sed -e 's/.*[^0-9]\([0-9]\+\)[^0-9]*$/\1/'` -COL_FOUND=`echo "$RESULT" | sed -r 's/^[^0-9]*([0-9]+).*$/\1/'` +ROW_FOUND=$(echo "$RESULT" | sed -e 's/[0-9]* \([0-9]*\)/\1/') +COL_FOUND=$(echo "$RESULT" | sed -e 's/\([0-9]*\) [0-9]*/\1/') if [ "50" != "$COL_FOUND" ]; then echo "Col found was $COL_FOUND which does not match expected 50" diff --git a/src/internal.c b/src/internal.c index 7f7b54884..904f47efe 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7132,6 +7132,9 @@ static int DoChannelClose(WOLFSSH* ssh, } +#if !defined(NO_TERMIOS) && defined(WOLFSSH_TERM) +#if defined(HAVE_SYS_IOCTL_H) + #define TTY_SET_CHAR(x,y,z) (x)[(y)] = (byte)(z) #define TTY_SET_FLAG(x,y,z) (x) = (z) ? ((x) | (y)) : ((x) & ~(y)) @@ -7368,6 +7371,9 @@ int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd) return 0; } +#endif /* HAVE_SYS_IOCTL_H */ +#endif /* !NO_TERMIOS && WOLFSSH_TERM */ + static int DoChannelRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)