diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index fe0f7419a..0d1785d08 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -831,6 +831,10 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, #endif byte shellBuffer[WOLFSSHD_SHELL_BUFFER_SZ]; int cnt_r, cnt_w; + int windowFull = 0; /* bytes left in shellBuffer that a prior send could + * not pass on to wolfSSH yet. This happens with window + * full, rekey, or want-write; resent before reading + * more so the buffered data is not overwritten. */ HANDLE ptyIn = NULL, ptyOut = NULL; HANDLE cnslIn = NULL, cnslOut = NULL; STARTUPINFOEX ext; @@ -1063,7 +1067,10 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, continue; } } - break; + /* keep looping while buffered data still needs to be + * flushed to the SSH channel */ + if (!windowFull) + break; } } if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) { @@ -1123,7 +1130,30 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } - if (readPending) { + /* if a previous send could not complete, resend the buffered data + * before reading more so shellBuffer is not overwritten */ + if (windowFull) { + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, windowFull); + if (cnt_w == WS_WINDOW_FULL || cnt_w == WS_REKEYING || + cnt_w == WS_WANT_WRITE) { + continue; + } + else if (cnt_w < 0) { + break; + } + else { + windowFull -= cnt_w; + if (windowFull > 0) { + WMEMMOVE(shellBuffer, shellBuffer + cnt_w, windowFull); + continue; + } + if (windowFull < 0) + windowFull = 0; + } + } + + if (readPending && !windowFull) { WMEMSET(shellBuffer, 0, WOLFSSHD_SHELL_BUFFER_SZ); if (ReadFile(ptyOut, shellBuffer, WOLFSSHD_SHELL_BUFFER_SZ, &cnt_r, @@ -1137,8 +1167,19 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, if (cnt_r > 0) { cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, shellBuffer, cnt_r); - if (cnt_w < 0) + if (cnt_w > 0 && cnt_w < cnt_r) { /* partial send */ + windowFull = cnt_r - cnt_w; + WMEMMOVE(shellBuffer, shellBuffer + cnt_w, + windowFull); + } + else if (cnt_w == WS_WINDOW_FULL || + cnt_w == WS_REKEYING || + cnt_w == WS_WANT_WRITE) { + windowFull = cnt_r; /* save amount to be sent */ + } + else if (cnt_w < 0) { break; + } } } } @@ -1234,6 +1275,10 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, int windowFull = 0; /* Contains size of bytes from shellBuffer that did * not get passed on to wolfSSH yet. This happens * with window full errors or when rekeying. */ + int windowFullExt = 0; /* Nonzero when the bytes held in shellBuffer + * belong to the extended (stderr) data stream + * and must be resent with + * wolfSSH_extended_data_send(). */ int wantWrite = 0; int peerConnected = 1; int stdoutEmpty = 0; @@ -1543,8 +1588,14 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } rc = select((int)maxFd + 1, &readFds, &writeFds, NULL, NULL); - if (rc == -1) + if (rc == -1) { + /* Signal (e.g. SIGCHLD from child exit) interrupted select. + * Re-evaluate the loop condition so any pending windowFull + * data and remaining pipe contents still get drained. */ + if (errno == EINTR) + continue; break; + } } else { pending = 1; /* found some pending SSH data */ @@ -1623,8 +1674,14 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, /* if the window was previously full, try resending the data */ if (windowFull) { - cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, - shellBuffer, windowFull); + if (windowFullExt) { + cnt_w = wolfSSH_extended_data_send(ssh, shellBuffer, + windowFull); + } + else { + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, windowFull); + } if (cnt_w == WS_WINDOW_FULL || cnt_w == WS_REKEYING) { continue; } @@ -1632,6 +1689,10 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, wantWrite = 1; continue; } + else if (cnt_w < 0) { + kill(childPid, SIGINT); + break; + } else { windowFull -= cnt_w; if (windowFull > 0) { @@ -1640,6 +1701,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } if (windowFull < 0) windowFull = 0; + windowFullExt = 0; } } @@ -1660,15 +1722,22 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, cnt_r); if (cnt_w > 0 && cnt_w < cnt_r) { /* partial send */ windowFull = cnt_r - cnt_w; + windowFullExt = 1; WMEMMOVE(shellBuffer, shellBuffer + cnt_w, windowFull); + /* don't let the stdout read below trample the + * buffered stderr remainder */ + continue; } else if (cnt_w == WS_WINDOW_FULL || cnt_w == WS_REKEYING) { windowFull = cnt_r; /* save amount to be sent */ + windowFullExt = 1; continue; } else if (cnt_w == WS_WANT_WRITE) { + windowFull = cnt_r; + windowFullExt = 1; wantWrite = 1; continue; } @@ -1698,15 +1767,19 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, shellBuffer, cnt_r); if (cnt_w > 0 && cnt_w < cnt_r) { /* partial send */ windowFull = cnt_r - cnt_w; + windowFullExt = 0; WMEMMOVE(shellBuffer, shellBuffer + cnt_w, windowFull); } else if (cnt_w == WS_WINDOW_FULL || cnt_w == WS_REKEYING) { windowFull = cnt_r; /* save amount to be sent */ + windowFullExt = 0; continue; } else if (cnt_w == WS_WANT_WRITE) { + windowFull = cnt_r; + windowFullExt = 0; wantWrite = 1; continue; } @@ -1721,28 +1794,35 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, else { if (FD_ISSET(childFd, &readFds)) { cnt_r = (int)read(childFd, shellBuffer, sizeof shellBuffer); - /* This read will return 0 on EOF */ - if (cnt_r <= 0) { + /* Treat a 0 return as EOF so the loop can shut down. */ + if (cnt_r < 0) { int err = errno; if (err != EAGAIN && err != 0) { break; } } + else if (cnt_r == 0) { + stdoutEmpty = 1; + } else { if (cnt_r > 0) { cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, shellBuffer, cnt_r); if (cnt_w > 0 && cnt_w < cnt_r) { /* partial send */ windowFull = cnt_r - cnt_w; + windowFullExt = 0; WMEMMOVE(shellBuffer, shellBuffer + cnt_w, windowFull); } else if (cnt_w == WS_WINDOW_FULL || cnt_w == WS_REKEYING) { windowFull = cnt_r; + windowFullExt = 0; continue; } else if (cnt_w == WS_WANT_WRITE) { + windowFull = cnt_r; + windowFullExt = 0; wantWrite = 1; continue; }