diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index dc3363d5..6e5cd797 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -438,6 +438,7 @@ static int SetupChroot(WOLFSSHD_CONFIG* usrConf) #ifdef WOLFSSH_SFTP #define TEST_SFTP_TIMEOUT 1 +#define TEST_SFTP_TIMEOUT_NONE 0 /* handle SFTP operations * returns WS_SUCCESS on success @@ -445,11 +446,12 @@ static int SetupChroot(WOLFSSHD_CONFIG* usrConf) static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, WPASSWD* pPasswd, WOLFSSHD_CONFIG* usrConf) { - byte tmp[1]; int ret = WS_SUCCESS; int error = WS_SUCCESS; WS_SOCKET_T sockfd; int select_ret = 0; + int timeout = TEST_SFTP_TIMEOUT_NONE; + byte peek_buf[1]; /* temporarily elevate permissions to get users information */ if (wolfSSHD_AuthRaisePermissions(conn->auth) != WS_SUCCESS) { @@ -501,29 +503,67 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, sockfd = (WS_SOCKET_T)wolfSSH_get_fd(ssh); do { - if (wolfSSH_stream_peek(ssh, tmp, 1) > 0) { - select_ret = WS_SELECT_RECV_READY; - } - else { - select_ret = tcp_select(sockfd, TEST_SFTP_TIMEOUT); + select_ret = tcp_select(sockfd, timeout); + if (select_ret == WS_SELECT_ERROR_READY) { + break; } - if (select_ret == WS_SELECT_RECV_READY || - select_ret == WS_SELECT_ERROR_READY || - error == WS_WANT_WRITE) - { + if (select_ret == WS_SELECT_RECV_READY) { + ret = wolfSSH_worker(ssh, NULL); + error = wolfSSH_get_error(ssh); + if (ret == WS_REKEYING) { + /* In a rekey, keeping turning the crank. */ + timeout = TEST_SFTP_TIMEOUT; + continue; + } + if (error == WS_EOF) { + break; + } + if (ret != WS_SUCCESS && ret != WS_CHAN_RXD) { + /* If not successful and no channel data, leave. */ + break; + } + } + + if (wolfSSH_SFTP_PendingSend(ssh)) { + /* Yes, process the SFTP data. */ ret = wolfSSH_SFTP_read(ssh); error = wolfSSH_get_error(ssh); + timeout = (ret == WS_REKEYING) ? + TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; + if (error == WS_WANT_READ || error == WS_WANT_WRITE || + error == WS_CHAN_RXD || error == WS_REKEYING || + error == WS_WINDOW_FULL) + ret = error; + if (error == WS_EOF) + break; + continue; } - else if (select_ret == WS_SELECT_TIMEOUT) - error = WS_WANT_READ; - else - error = WS_FATAL_ERROR; - if (error == WS_WANT_READ || error == WS_WANT_WRITE || - error == WS_CHAN_RXD || error == WS_REKEYING || - error == WS_WINDOW_FULL) - ret = error; + ret = wolfSSH_stream_peek(ssh, peek_buf, sizeof(peek_buf)); + if (ret > 0) { + /* Yes, process the SFTP data. */ + ret = wolfSSH_SFTP_read(ssh); + error = wolfSSH_get_error(ssh); + timeout = (ret == WS_REKEYING) ? + TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; + if (error == WS_WANT_READ || error == WS_WANT_WRITE || + error == WS_CHAN_RXD || error == WS_REKEYING || + error == WS_WINDOW_FULL) + ret = error; + if (error == WS_EOF) + break; + continue; + } + else if (ret == WS_REKEYING) { + timeout = TEST_SFTP_TIMEOUT; + continue; + } + else if (ret < 0) { + error = wolfSSH_get_error(ssh); + if (error == WS_EOF) + break; + } if (ret == WS_FATAL_ERROR && error == 0) { WOLFSSH_CHANNEL* channel = @@ -533,6 +573,8 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, break; } } + + timeout = TEST_SFTP_TIMEOUT; } while (ret != WS_FATAL_ERROR); (void)conn; diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 459139b7..a7a16228 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -107,8 +107,6 @@ #ifndef NO_WOLFSSH_SERVER -#define TEST_SFTP_TIMEOUT 1 - static const char echoserverBanner[] = "wolfSSH Example Echo Server\n"; static int quit = 0; @@ -1133,84 +1131,81 @@ static int ssh_worker(thread_ctx_t* threadCtx) #ifdef WOLFSSH_SFTP + +#define TEST_SFTP_TIMEOUT_NONE 0 +#define TEST_SFTP_TIMEOUT 1 + /* handle SFTP operations * returns 0 on success */ static int sftp_worker(thread_ctx_t* threadCtx) { - byte tmp[1]; - int ret = WS_SUCCESS; - int error = WS_SUCCESS; - WS_SOCKET_T sockfd; - int select_ret = 0; + WOLFSSH* ssh = threadCtx->ssh; + WS_SOCKET_T s; + int ret = WS_SUCCESS; + int error; + int selected; + unsigned char peek_buf[1]; + int timeout = TEST_SFTP_TIMEOUT; + + s = (WS_SOCKET_T)wolfSSH_get_fd(ssh); - error = wolfSSH_get_error(threadCtx->ssh); - sockfd = (WS_SOCKET_T)wolfSSH_get_fd(threadCtx->ssh); do { - if (threadCtx->nonBlock) { - if (error == WS_WANT_READ) { - WOLFSSH_CHANNEL* c; - printf("... sftp server would read block\n"); + selected = tcp_select(s, timeout); + if (selected == WS_SELECT_ERROR_READY) { + break; + } - /* if all channels are closed then close connection */ - c = wolfSSH_ChannelNext(threadCtx->ssh, NULL); - if (c && wolfSSH_ChannelGetEof(c)) { - ret = 0; - break; - } - } - else if (error == WS_WANT_WRITE) { - word32 c; - printf("... sftp server would write block\n"); - - /* handle backlog of send packets */ - wolfSSH_worker(threadCtx->ssh, &c); - ret = error = wolfSSH_get_error(threadCtx->ssh); + if (selected == WS_SELECT_RECV_READY) { + ret = wolfSSH_worker(ssh, NULL); + error = wolfSSH_get_error(ssh); + if (ret == WS_REKEYING) { + /* In a rekey, keeping turning the crank. */ + timeout = TEST_SFTP_TIMEOUT; continue; } - } - - /* if there is a current send in progress then continue to process it */ - if (wolfSSH_SFTP_PendingSend(threadCtx->ssh)) { - ret = wolfSSH_SFTP_read(threadCtx->ssh); - error = wolfSSH_get_error(threadCtx->ssh); - } - else { - if (wolfSSH_stream_peek(threadCtx->ssh, tmp, 1) > 0) { - select_ret = WS_SELECT_RECV_READY; + if (error == WS_EOF) { + break; } - else { - select_ret = tcp_select(sockfd, TEST_SFTP_TIMEOUT); - } - - if (select_ret == WS_SELECT_RECV_READY || - select_ret == WS_SELECT_ERROR_READY || - error == WS_WANT_WRITE) - { - ret = wolfSSH_SFTP_read(threadCtx->ssh); - error = wolfSSH_get_error(threadCtx->ssh); - } - else if (select_ret == WS_SELECT_TIMEOUT) - error = WS_WANT_READ; - else - error = WS_FATAL_ERROR; - } - - if (error == WS_WANT_READ || error == WS_WANT_WRITE || - error == WS_CHAN_RXD || error == WS_REKEYING || - error == WS_WINDOW_FULL) - ret = error; - - if (ret == WS_FATAL_ERROR && error == 0) { - WOLFSSH_CHANNEL* channel = - wolfSSH_ChannelNext(threadCtx->ssh, NULL); - if (channel && wolfSSH_ChannelGetEof(channel)) { - ret = 0; + if (ret != WS_SUCCESS && ret != WS_CHAN_RXD) { + /* If not successful and no channel data, leave. */ break; } } - } while (ret != WS_FATAL_ERROR && ret != WS_SOCKET_ERROR_E); + if (wolfSSH_SFTP_PendingSend(ssh)) { + /* Yes, process the SFTP data. */ + ret = wolfSSH_SFTP_read(ssh); + timeout = (ret == WS_REKEYING) ? + TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; + continue; + } + + ret = wolfSSH_stream_peek(ssh, peek_buf, sizeof(peek_buf)); + if (ret > 0) { + /* Yes, process the SFTP data. */ + ret = wolfSSH_SFTP_read(ssh); + timeout = (ret == WS_REKEYING) ? + TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; + continue; + } + else if (ret == WS_REKEYING) { + timeout = TEST_SFTP_TIMEOUT; + continue; + } + + /* Old check for EOF here */ + { + WOLFSSH_CHANNEL* channel = + wolfSSH_ChannelNext(ssh, NULL); + if (channel && wolfSSH_ChannelGetEof(channel)) { + ret = WS_EOF; + break; + } + } + + timeout = TEST_SFTP_TIMEOUT; + } while (1); return ret; } @@ -1335,7 +1330,11 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs) break; } - if (error != WS_WANT_READ && error != WS_WANT_WRITE) { + if (error == WS_WANT_READ || error == WS_WANT_WRITE) { + /* Wanting read or wanting write. Clear ret. */ + ret = 0; + } + else { break; } } diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c index 31292476..142f8d11 100644 --- a/examples/sftpclient/sftpclient.c +++ b/examples/sftpclient/sftpclient.c @@ -501,8 +501,14 @@ static int doCmds(func_args* args) if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) { ret = wolfSSH_get_error(ssh); } + while (ret == WS_REKEYING || ssh->error == WS_REKEYING) { + ret = wolfSSH_worker(ssh, NULL); + if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) { + ret = wolfSSH_get_error(ssh); + } + } } while (ret == WS_WANT_READ || ret == WS_WANT_WRITE || - ret == WS_CHAN_RXD || ret == WS_REKEYING); + ret == WS_CHAN_RXD); #ifndef WOLFSSH_NO_TIMESTAMP WMEMSET(currentFile, 0, WOLFSSH_MAX_FILENAME); diff --git a/src/internal.c b/src/internal.c index 15822c97..8df184f1 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7411,7 +7411,8 @@ int DoReceive(WOLFSSH* ssh) ret = DoPacket(ssh); ssh->error = ret; if (ret < 0 && !(ret == WS_CHAN_RXD || ret == WS_EXTDATA || - ret == WS_CHANNEL_CLOSED || ret == WS_WANT_WRITE)) { + ret == WS_CHANNEL_CLOSED || ret == WS_WANT_WRITE || + ret == WS_REKEYING)) { return WS_FATAL_ERROR; } WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", peerMacSz); @@ -11896,6 +11897,7 @@ int SendChannelData(WOLFSSH* ssh, word32 channelId, if (ret == WS_SUCCESS) { word32 bound = min(channel->peerWindowSz, channel->peerMaxPacketSz); + bound = min(bound, channel->maxPacketSz); if (dataSz > bound) { WLOG(WS_LOG_DEBUG, diff --git a/src/ssh.c b/src/ssh.c index e6674a1c..104dbf4d 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -998,6 +998,15 @@ int wolfSSH_stream_peek(WOLFSSH* ssh, byte* buf, word32 bufSz) if (ssh == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; + if (ssh->isKeying) { + ssh->error = WS_REKEYING; + return WS_REKEYING; + } + if (ssh->channelList->eofRxd) { + ssh->error = WS_EOF; + return WS_ERROR; + } + inputBuffer = &ssh->channelList->inputBuffer; bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx); if (buf != NULL) { @@ -1030,6 +1039,11 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz) if (ssh == NULL || buf == NULL || bufSz == 0 || ssh->channelList == NULL) return WS_BAD_ARGUMENT; + if (ssh->channelList->eofRxd) { + ssh->error = WS_EOF; + return WS_ERROR; + } + inputBuffer = &ssh->channelList->inputBuffer; ssh->error = WS_SUCCESS; @@ -1089,6 +1103,11 @@ int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz) if (ssh == NULL || buf == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; + if (ssh->isKeying) { + ssh->error = WS_REKEYING; + return WS_REKEYING; + } + bytesTxd = SendChannelData(ssh, ssh->channelList->channel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd); @@ -1764,15 +1783,24 @@ int wolfSSH_worker(WOLFSSH* ssh, word32* channelId) ret = DoReceive(ssh); } - if (channelId != NULL && ssh != NULL) { - *channelId = ssh->lastRxId; + if (ret == WS_SUCCESS) { + if (channelId != NULL) { + *channelId = ssh->lastRxId; + } + + if (ssh->isKeying) { + ssh->error = WS_REKEYING; + return WS_REKEYING; + } } - if (ret == WS_CHAN_RXD) + if (ret == WS_CHAN_RXD) { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_worker(), " "data received on channel %u", ssh->lastRxId); - else + } + else { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_worker(), ret = %d", ret); + } return ret; } diff --git a/src/wolfsftp.c b/src/wolfsftp.c index d915a370..47a2ac43 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -404,7 +404,8 @@ static INLINE int NoticeError(WOLFSSH* ssh) { return (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE || - ssh->error == WS_CHAN_RXD); + ssh->error == WS_CHAN_RXD || + ssh->error == WS_REKEYING); } @@ -512,7 +513,7 @@ static int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer) return WS_BUFFER_E; } - while (buffer->idx < buffer->sz && (ret > 0 || ret == WS_SUCCESS)) { + if (buffer->idx < buffer->sz) { ret = wolfSSH_stream_send(ssh, buffer->data + buffer->idx, buffer->sz - buffer->idx); if (ret > 0) { @@ -520,11 +521,6 @@ static int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer) } WLOG(WS_LOG_SFTP, "SFTP buffer sent %d / %d bytes", buffer->idx, buffer->sz); - - /* interupt sending for a rekey or full window */ - if (ret == WS_WINDOW_FULL || ret == WS_REKEYING) { - ret = wolfSSH_worker(ssh, NULL); - } } return ret; @@ -1488,12 +1484,26 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh) case STATE_RECV_SEND: if (state->toSend) { ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer); - if (ret == WS_SUCCESS || ret > 0) { - ret = WS_SUCCESS; - state->toSend = 0; - wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); + if (ret < 0) { + if (ret == WS_REKEYING || ssh->error == WS_REKEYING) { + return WS_REKEYING; + } + if (ssh->error != WS_WANT_READ && + ssh->error != WS_WANT_WRITE && + ssh->error != WS_WINDOW_FULL) { + wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); + } + return WS_FATAL_ERROR; } + if (wolfSSH_SFTP_buffer_idx(&state->buffer) + < wolfSSH_SFTP_buffer_size(&state->buffer)) { + ssh->error = WS_WANT_WRITE; + return WS_FATAL_ERROR; + } + ret = WS_SUCCESS; + state->toSend = 0; } + wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); return ret; default: @@ -3407,10 +3417,6 @@ int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_BUFFER_E; } - if (sz > WOLFSSH_MAX_SFTP_RW) { - sz = WOLFSSH_MAX_SFTP_RW; - } - /* read from handle and send data back to client */ out = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER + UINT32_SZ, ssh->ctx->heap, DYNTYPE_BUFFER); @@ -6922,6 +6928,9 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, /* send header and type specific data */ ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer); if (ret < 0) { + if (ret == WS_REKEYING) { + return ret; + } if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) { state->state = STATE_SEND_READ_CLEANUP; diff --git a/tests/api.c b/tests/api.c index f0dea551..eaccd418 100644 --- a/tests/api.c +++ b/tests/api.c @@ -816,14 +816,14 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) } /* partial read */ - outSz = tmp->atrb.sz[0] / 2; + outSz = WOLFSSH_MAX_SFTP_RW / 2; rxSz = wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, ofst, out, outSz); AssertIntGT(rxSz, 0); AssertIntLE(rxSz, outSz); /* read all */ - outSz = tmp->atrb.sz[0]; + outSz = WOLFSSH_MAX_SFTP_RW; rxSz = wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, ofst, out, outSz); AssertIntGT(rxSz, 0); diff --git a/wolfssh/test.h b/wolfssh/test.h index 34d650e6..52062e9f 100644 --- a/wolfssh/test.h +++ b/wolfssh/test.h @@ -681,7 +681,7 @@ static INLINE int tcp_select(SOCKET_T socketfd, int to_sec) { WFD_SET_TYPE recvfds, errfds; int nfds = (int)socketfd + 1; - struct timeval timeout = {(to_sec > 0) ? to_sec : 0, 0}; + struct timeval timeout = {(to_sec > 0) ? to_sec : 0, 100}; int result; WFD_ZERO(&recvfds);