Client: check first_packet_follows guess in DoKexDhReply

When a server sends first_kex_packet_follows=TRUE with an incorrect KEX
algorithm guess, the client now silently discards the server's speculative
KEXDH_REPLY message by checking ignoreNextKexMsg at the top of DoKexDhReply,
matching the existing server-side handling in DoKexDhInit. Add regression
test covering the client-side skip path.

Affected functions: DoKexDhReply.
Issue: F-2863
pull/960/head
John Safranek 2026-04-24 14:45:27 -07:00 committed by Paul Adelsbach
parent b610dd75c4
commit f0c17282bc
3 changed files with 35 additions and 8 deletions

View File

@ -5823,6 +5823,17 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
return ret;
}
if (ret == WS_SUCCESS) {
if (ssh->handshake->ignoreNextKexMsg) {
/* skip this message. */
WLOG(WS_LOG_DEBUG, "Skipping server's KEXDH_REPLY message due to "
"first_packet_follows guess mismatch.");
ssh->handshake->ignoreNextKexMsg = 0;
*idx += len;
return WS_SUCCESS;
}
}
if (ret == WS_SUCCESS && len < LENGTH_SZ*2 + *idx) {
ret = WS_BUFFER_E;
}
@ -17901,6 +17912,11 @@ int wolfSSH_TestDoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
return DoKexDhInit(ssh, buf, len, idx);
}
int wolfSSH_TestDoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
{
return DoKexDhReply(ssh, buf, len, idx);
}
int wolfSSH_TestChannelPutData(WOLFSSH_CHANNEL* channel, byte* data,
word32 dataSz)
{

View File

@ -2072,9 +2072,9 @@ typedef int (*FirstPacketFollowsSkipFn)(WOLFSSH* ssh, byte* buf, word32 len,
word32* idx);
/* With ignoreNextKexMsg set, the target Do* handler must consume the packet,
* clear the flag, and not advance clientState past CLIENT_KEXINIT_DONE. */
* clear the flag, and not advance the peer's state past KEXINIT_DONE. */
static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
const char* label)
const char* label, byte endpointType, byte initState)
{
WOLFSSH_CTX* ctx;
WOLFSSH* ssh;
@ -2082,7 +2082,7 @@ static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
word32 idx = 0;
int ret;
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
ctx = wolfSSH_CTX_new(endpointType, NULL);
AssertNotNull(ctx);
ssh = wolfSSH_new(ctx);
@ -2090,9 +2090,12 @@ static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
AssertNotNull(ssh->handshake);
ssh->handshake->ignoreNextKexMsg = 1;
ssh->clientState = CLIENT_KEXINIT_DONE;
if (endpointType == WOLFSSH_ENDPOINT_SERVER)
ssh->clientState = initState;
else
ssh->serverState = initState;
/* Garbage payload — must never be parsed when skipped. */
/* Garbage payload that must never be parsed when skipped. */
WMEMSET(payload, 0xAB, sizeof(payload));
ret = fn(ssh, payload, sizeof(payload), &idx);
@ -2101,7 +2104,10 @@ static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
}
AssertIntEQ(idx, sizeof(payload));
AssertIntEQ(ssh->handshake->ignoreNextKexMsg, 0);
AssertIntEQ(ssh->clientState, CLIENT_KEXINIT_DONE);
if (endpointType == WOLFSSH_ENDPOINT_SERVER)
AssertIntEQ(ssh->clientState, initState);
else
AssertIntEQ(ssh->serverState, initState);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
@ -2109,11 +2115,14 @@ static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
static void TestFirstPacketFollowsSkipped(void)
{
RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhInit, "DoKexDhInit");
RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhInit,
"DoKexDhInit", WOLFSSH_ENDPOINT_SERVER, CLIENT_KEXINIT_DONE);
#ifndef WOLFSSH_NO_DH_GEX_SHA256
RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhGexRequest,
"DoKexDhGexRequest");
"DoKexDhGexRequest", WOLFSSH_ENDPOINT_SERVER, CLIENT_KEXINIT_DONE);
#endif
RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhReply,
"DoKexDhReply", WOLFSSH_ENDPOINT_CLIENT, SERVER_KEXINIT_DONE);
}
static void TestFirstPacketFollows(void)

View File

@ -1337,6 +1337,8 @@ enum WS_MessageIdLimits {
word32 len, word32* idx);
WOLFSSH_API int wolfSSH_TestDoKexDhInit(WOLFSSH* ssh, byte* buf,
word32 len, word32* idx);
WOLFSSH_API int wolfSSH_TestDoKexDhReply(WOLFSSH* ssh, byte* buf,
word32 len, word32* idx);
WOLFSSH_API int wolfSSH_TestChannelPutData(WOLFSSH_CHANNEL*, byte*, word32);
#ifndef WOLFSSH_NO_DH_GEX_SHA256
WOLFSSH_API int wolfSSH_TestDoKexDhGexRequest(WOLFSSH* ssh, byte* buf,