Merge pull request #793 from JacobBarthelmeh/rekey

sanity checks on message types during rekey
pull/841/head
John Safranek 2025-10-06 13:54:03 -07:00 committed by GitHub
commit 50aa10d717
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 210 additions and 49 deletions

View File

@ -342,6 +342,9 @@ static THREAD_RET readInput(void* in)
ret = wolfSSH_stream_send(args->ssh, buf, sz);
wc_UnLockMutex(&args->lock);
if (ret <= 0) {
if (ret == WS_REKEYING) {
continue;
}
fprintf(stderr, "Couldn't send data\n");
return THREAD_RET_SUCCESS;
}
@ -472,8 +475,16 @@ static THREAD_RET readPeer(void* in)
continue;
}
#endif /* WOLFSSH_AGENT */
else if (ret == WS_REKEYING) {
wolfSSH_worker(args->ssh, NULL);
ret = 0;
}
}
else if (ret != WS_EOF) {
if (ret == 0) {
bytes = 0;
continue;
}
err_sys("Stream read failed.");
}
}

View File

@ -1416,8 +1416,11 @@ static int sftp_worker(thread_ctx_t* threadCtx)
}
else if (ret < 0) {
error = wolfSSH_get_error(ssh);
if (error == WS_EOF)
if (error == WS_EOF) {
/* shutdown is happening, clear peek error */
ret = 0;
break;
}
}
if (ret == WS_FATAL_ERROR && error == 0) {

View File

@ -747,6 +747,13 @@ static int doCmds(func_args* args)
/* check directory is valid */
do {
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);
}
}
ret = wolfSSH_SFTP_STAT(ssh, pt, &atrb);
err = wolfSSH_get_error(ssh);
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE)
@ -828,6 +835,13 @@ static int doCmds(func_args* args)
/* update permissions */
do {
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);
}
}
ret = wolfSSH_SFTP_CHMOD(ssh, pt, mode);
err = wolfSSH_get_error(ssh);
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE)
@ -878,6 +892,13 @@ static int doCmds(func_args* args)
}
do {
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);
}
}
ret = wolfSSH_SFTP_RMDIR(ssh, pt);
err = wolfSSH_get_error(ssh);
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE)
@ -924,6 +945,13 @@ static int doCmds(func_args* args)
}
do {
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);
}
}
ret = wolfSSH_SFTP_Remove(ssh, pt);
err = wolfSSH_get_error(ssh);
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE)
@ -1119,7 +1147,7 @@ static int doCmds(func_args* args)
/* alternate main loop for the autopilot get/receive */
static int doAutopilot(int cmd, char* local, char* remote)
{
int err;
int err = 0;
int ret = WS_SUCCESS;
char fullpath[128] = ".";
WS_SFTPNAME* name = NULL;
@ -1156,6 +1184,12 @@ static int doAutopilot(int cmd, char* local, char* remote)
}
do {
if (err == WS_REKEYING || err == WS_WINDOW_FULL) { /* handle rekeying state */
do {
ret = wolfSSH_worker(ssh, NULL);
} while (ret == WS_REKEYING);
}
if (cmd == AUTOPILOT_PUT) {
ret = wolfSSH_SFTP_Put(ssh, local, fullpath, 0, NULL);
}
@ -1164,7 +1198,8 @@ static int doAutopilot(int cmd, char* local, char* remote)
}
err = wolfSSH_get_error(ssh);
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
err == WS_CHAN_RXD || err == WS_REKEYING) &&
err == WS_CHAN_RXD || err == WS_REKEYING ||
err == WS_WINDOW_FULL) &&
ret == WS_FATAL_ERROR);
if (ret != WS_SUCCESS) {
@ -1452,14 +1487,52 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
WFREE(workingDir, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (ret == WS_SUCCESS) {
if (wolfSSH_shutdown(ssh) != WS_SUCCESS) {
int rc;
rc = wolfSSH_get_error(ssh);
int err;
ret = wolfSSH_shutdown(ssh);
if (rc != WS_SOCKET_ERROR_E && rc != WS_EOF)
printf("error with wolfSSH_shutdown()\n");
/* peer hung up, stop trying to shutdown */
if (ret == WS_SOCKET_ERROR_E) {
ret = 0;
}
err = wolfSSH_get_error(ssh);
if (err != WS_SOCKET_ERROR_E &&
(err == WS_WANT_READ || err == WS_WANT_WRITE)) {
int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;
for (attempt = 0; attempt < maxAttempt; attempt++) {
ret = wolfSSH_worker(ssh, NULL);
err = wolfSSH_get_error(ssh);
/* peer successfully closed down gracefully */
if (ret == WS_CHANNEL_CLOSED) {
ret = 0;
break;
}
/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
ret = 0;
break;
}
if (err == WS_WANT_READ || err == WS_WANT_WRITE) {
/* Wanting read or wanting write. Clear ret. */
ret = 0;
}
else {
break;
}
}
if (attempt == maxAttempt) {
printf("SFTP client gave up on gracefull shutdown,"
"closing the socket\n");
}
}
}
WCLOSESOCKET(sockFd);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);

View File

@ -595,6 +595,40 @@ static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
}
/* RFC 4253 section 7.1, Once having sent SSH_MSG_KEXINIT the only messages
* that can be sent are 1-19 (except SSH_MSG_SERVICE_REQUEST and
* SSH_MSG_SERVICE_ACCEPT), 20-29 (except SSH_MSG_KEXINIT again), and 30-49
*/
INLINE static int IsMessageAllowedKeying(WOLFSSH *ssh, byte msg)
{
if (ssh->isKeying == 0) {
return 1;
}
/* case of service request or accept in 1-19 */
if (msg == MSGID_SERVICE_REQUEST || msg == MSGID_SERVICE_ACCEPT) {
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by during rekeying", msg);
ssh->error = WS_REKEYING;
return 0;
}
/* case of resending SSH_MSG_KEXINIT */
if (msg == MSGID_KEXINIT) {
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by during rekeying", msg);
ssh->error = WS_REKEYING;
return 0;
}
/* case where message id greater than 49 */
if (msg >= MSGID_USERAUTH_REQUEST) {
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by during rekeying", msg);
ssh->error = WS_REKEYING;
return 0;
}
return 1;
}
#ifndef NO_WOLFSSH_SERVER
INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
{
@ -673,8 +707,14 @@ INLINE static int IsMessageAllowedClient(WOLFSSH *ssh, byte msg)
#endif /* NO_WOLFSSH_CLIENT */
INLINE static int IsMessageAllowed(WOLFSSH *ssh, byte msg)
/* 'state' argument is for if trying to send a message or receive one.
* Returns 1 if allowed 0 if not allowed. */
INLINE static int IsMessageAllowed(WOLFSSH *ssh, byte msg, byte state)
{
if (state == WS_MSG_SEND && !IsMessageAllowedKeying(ssh, msg)) {
return 0;
}
#ifndef NO_WOLFSSH_SERVER
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
return IsMessageAllowedServer(ssh, msg);
@ -5905,7 +5945,6 @@ static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
HandshakeInfoFree(ssh->handshake, ssh->ctx->heap);
ssh->handshake = NULL;
WLOG(WS_LOG_DEBUG, "Keying completed");
if (ssh->ctx->keyingCompletionCb)
ssh->ctx->keyingCompletionCb(ssh->keyingCompletionCtx);
}
@ -9322,7 +9361,7 @@ static int DoPacket(WOLFSSH* ssh, byte* bufferConsumed)
return WS_OVERFLOW_E;
}
if (!IsMessageAllowed(ssh, msg)) {
if (!IsMessageAllowed(ssh, msg, WS_MSG_RECV)) {
return WS_MSGID_NOT_ALLOWED_E;
}
@ -15662,6 +15701,12 @@ int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId)
if (ssh == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS) {
if (!IsMessageAllowed(ssh, MSGID_CHANNEL_EOF, WS_MSG_SEND)) {
ret = WS_MSGID_NOT_ALLOWED_E;
}
}
if (ret == WS_SUCCESS) {
channel = ChannelFind(ssh, peerChannelId, WS_CHANNEL_ID_PEER);
if (channel == NULL)
@ -16090,6 +16135,12 @@ int SendChannelWindowAdjust(WOLFSSH* ssh, word32 channelId,
if (ssh == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS) {
if (!IsMessageAllowed(ssh, MSGID_CHANNEL_WINDOW_ADJUST, WS_MSG_SEND)) {
ret = WS_MSGID_NOT_ALLOWED_E;
}
}
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
if (channel == NULL) {
WLOG(WS_LOG_DEBUG, "Invalid channel");

View File

@ -1135,6 +1135,11 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz)
return WS_ERROR;
}
if (ssh->isKeying) {
ssh->error = WS_REKEYING;
return WS_FATAL_ERROR;
}
inputBuffer = &ssh->channelList->inputBuffer;
ssh->error = WS_SUCCESS;
@ -1164,7 +1169,7 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz)
}
/* update internal input buffer based on data read */
if (ret == WS_SUCCESS) {
if (ret == WS_SUCCESS && !ssh->isKeying) {
int n;
n = min(bufSz, inputBuffer->length - inputBuffer->idx);
@ -1196,7 +1201,7 @@ int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz)
if (ssh->isKeying) {
ssh->error = WS_REKEYING;
return WS_REKEYING;
return WS_FATAL_ERROR;
}
bytesTxd = SendChannelData(ssh, ssh->channelList->channel, buf, bufSz);
@ -2901,6 +2906,11 @@ int wolfSSH_ChannelRead(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz)
if (channel == NULL || buf == NULL || bufSz == 0)
return WS_BAD_ARGUMENT;
if (channel->ssh->isKeying) {
channel->ssh->error = WS_REKEYING;
return WS_REKEYING;
}
bufSz = _ChannelRead(channel, buf, bufSz);
WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelRead(), bytesRxd = %d",

View File

@ -416,6 +416,7 @@ 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_WINDOW_FULL ||
ssh->error == WS_REKEYING);
}
@ -1170,8 +1171,9 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh)
case SFTP_EXT:
ret = SFTP_ServerRecvInit(ssh);
if (ret != WS_SUCCESS) {
if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL);
}
return ret;
}
ssh->sftpState = SFTP_RECV;
@ -1418,7 +1420,11 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer,
state->buffer.sz);
if (ret < 0) {
if (!NoticeError(ssh)) {
if (NoticeError(ssh)) {
/* keep state for returning to */
ret = WS_FATAL_ERROR;
}
else {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
}
return ret;
@ -1569,8 +1575,9 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
/* break out if encountering an error with nothing stored to send */
if (ret < 0 && !state->toSend) {
if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
}
return ret;
}
state->buffer.idx = 0;
@ -7468,8 +7475,7 @@ int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
/* send header and type specific data */
ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer);
if (ret < 0) {
if (ssh->error == WS_WANT_READ ||
ssh->error == WS_WANT_WRITE) {
if (NoticeError(ssh)) {
return WS_FATAL_ERROR;
}
state->state = STATE_SEND_WRITE_CLEANUP;
@ -7481,16 +7487,15 @@ int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
case STATE_SEND_WRITE_SEND_BODY:
WLOG(WS_LOG_SFTP, "SFTP SEND_WRITE STATE: SEND_BODY");
state->sentSz = wolfSSH_stream_send(ssh, in, inSz);
if (state->sentSz == WS_WINDOW_FULL ||
state->sentSz == WS_REKEYING ||
state->sentSz == WS_WANT_READ ||
state->sentSz == WS_WANT_WRITE) {
ret = wolfSSH_worker(ssh, NULL);
continue; /* skip past rest and send more */
}
if (state->sentSz <= 0) {
ssh->error = state->sentSz;
ret = WS_FATAL_ERROR;
if (NoticeError(ssh)) {
ret = wolfSSH_worker(ssh,NULL);
continue;
}
/* if it was not a notice error then clean up the state and
* exit out */
state->state = STATE_SEND_WRITE_CLEANUP;
continue;
}
@ -7512,8 +7517,7 @@ int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
state->maxSz = SFTP_GetHeader(ssh, &state->reqId, &type,
&state->buffer);
if (state->maxSz <= 0) {
if (ssh->error == WS_WANT_READ ||
ssh->error == WS_WANT_WRITE) {
if (NoticeError(ssh)) {
return WS_FATAL_ERROR;
}
ssh->error = WS_SFTP_BAD_HEADER;
@ -7692,8 +7696,8 @@ 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 (NoticeError(ssh)) {
return WS_FATAL_ERROR;
}
if (ssh->error != WS_WANT_READ &&
ssh->error != WS_WANT_WRITE) {
@ -7711,14 +7715,12 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
/* Get response */
if ((ret = SFTP_GetHeader(ssh, &state->reqId, &state->type,
&state->buffer)) <= 0) {
if (ssh->error != WS_WANT_READ &&
ssh->error != WS_WANT_WRITE) {
if (!NoticeError(ssh)) {
state->state = STATE_SEND_READ_CLEANUP;
continue;
}
return WS_FATAL_ERROR;
}
ret = wolfSSH_SFTP_buffer_create(ssh, &state->buffer, ret);
if (ret != WS_SUCCESS) {
state->state = STATE_SEND_READ_CLEANUP;
@ -7736,8 +7738,9 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
state->state = STATE_SEND_READ_CLEANUP;
continue;
}
else
else {
ssh->reqId++;
}
if (state->type == WOLFSSH_FTP_DATA)
state->state = STATE_SEND_READ_FTP_DATA;
@ -7755,8 +7758,7 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
/* get size of string and place it into out buffer */
ret = wolfSSH_stream_read(ssh, szFlat, UINT32_SZ);
if (ret < 0) {
if (ssh->error != WS_WANT_READ &&
ssh->error != WS_WANT_WRITE) {
if (!NoticeError(ssh)) {
state->state = STATE_SEND_READ_CLEANUP;
continue;
}
@ -7935,8 +7937,9 @@ int wolfSSH_SFTP_MKDIR(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
/* send header and type specific data */
ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer);
if (ret < 0) {
if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_MKDIR);
}
return ret;
}
@ -7949,8 +7952,9 @@ int wolfSSH_SFTP_MKDIR(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
/* Get response */
if ((ret = SFTP_GetHeader(ssh, &state->reqId, &type,
&state->buffer)) <= 0) {
if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_MKDIR);
}
return WS_FATAL_ERROR;
}
@ -7981,8 +7985,9 @@ int wolfSSH_SFTP_MKDIR(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer,
wolfSSH_SFTP_buffer_size(&state->buffer));
if (ret < 0) {
if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
wolfSSH_SFTP_ClearState(ssh, STATE_ID_MKDIR);
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_MKDIR);
}
return WS_FATAL_ERROR;
}
@ -8049,8 +8054,7 @@ WS_SFTPNAME* wolfSSH_SFTP_ReadDir(WOLFSSH* ssh, byte* handle,
case STATE_READDIR_NAME:
name = wolfSSH_SFTP_DoName(ssh);
if (name == NULL) {
if (ssh->error != WS_WANT_READ
&& ssh->error != WS_WANT_WRITE) {
if (!NoticeError(ssh)) {
wolfSSH_SFTP_ClearState(ssh, STATE_ID_READDIR);
}
return NULL;
@ -9183,10 +9187,9 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
state->handle, state->handleSz, state->pOfst,
state->r, state->rSz);
if (sz <= 0) {
if (ssh->error == WS_WANT_READ ||
ssh->error == WS_WANT_WRITE ||
ssh->error == WS_WINDOW_FULL)
if (NoticeError(ssh)) {
return WS_FATAL_ERROR;
}
}
else {
AddAssign64(state->pOfst, sz);

View File

@ -1057,14 +1057,16 @@ static void test_wolfSSH_SFTP_SendReadPacket(void)
outSz = WOLFSSH_MAX_SFTP_RW / 2;
rxSz = wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz,
ofst, out, outSz);
AssertIntGT(rxSz, 0);
AssertIntLE(rxSz, outSz);
if (wolfSSH_get_error(ssh) != WS_REKEYING) {
AssertIntGT(rxSz, 0);
AssertIntLE(rxSz, outSz);
}
/* read all */
outSz = WOLFSSH_MAX_SFTP_RW;
rxSz = wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz,
ofst, out, outSz);
if (rxSz != WS_REKEYING) {
if (wolfSSH_get_error(ssh) != WS_REKEYING) {
AssertIntGT(rxSz, 0);
AssertIntLE(rxSz, outSz);
}
@ -1075,6 +1077,11 @@ static void test_wolfSSH_SFTP_SendReadPacket(void)
}
}
/* take care of re-keying state before shutdown call */
while (wolfSSH_get_error(ssh) == WS_REKEYING) {
wolfSSH_worker(ssh, NULL);
}
argsCount = wolfSSH_shutdown(ssh);
if (argsCount == WS_SOCKET_ERROR_E) {
/* If the socket is closed on shutdown, peer is gone, this is OK. */

View File

@ -1250,6 +1250,10 @@ enum WS_MessageIds {
#define CHANNEL_EXTENDED_DATA_STDERR WOLFSSH_EXT_DATA_STDERR
/* Used when checking IsMessageAllowed() to determine if creating and sending
* the message or receiving the message is allowed */
#define WS_MSG_SEND 1
#define WS_MSG_RECV 2
/* dynamic memory types */
enum WS_DynamicTypes {
@ -1443,4 +1447,3 @@ enum TerminalModes {
#endif
#endif /* _WOLFSSH_INTERNAL_H_ */