Merge pull request #509 from ejohnstown/sftp-update

Updates for SFTP Support
pull/513/head
JacobBarthelmeh 2023-04-24 16:27:52 -06:00 committed by GitHub
commit 54df2e99b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 194 additions and 108 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);