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/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 5a98e5cf4..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) { @@ -1323,19 +1343,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..904f47efe 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7132,6 +7132,249 @@ 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)) + +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; +} + +#endif /* HAVE_SYS_IOCTL_H */ +#endif /* !NO_TERMIOS && WOLFSSH_TERM */ + + static int DoChannelRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { @@ -7189,24 +7432,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 +7522,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);