From c9b8d19ff10975b22f71122e8e1db4bb24e1ff81 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Wed, 15 Apr 2026 10:01:04 +0900 Subject: [PATCH 1/3] Fix DoKexDhReply to reject the pubkey without the callback --- src/internal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal.c b/src/internal.c index 1202d132e..3727c573f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -5818,8 +5818,8 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } } else { - WLOG(WS_LOG_DEBUG, "DKDR: no public key check callback, accepted"); - ret = WS_SUCCESS; + WLOG(WS_LOG_DEBUG, "DKDR: no public key check callback, rejected"); + ret = WS_PUBKEY_REJECTED_E; } } From 367dd5a8f28073a779af52337349fee4b06ae4d3 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Wed, 15 Apr 2026 10:43:15 +0900 Subject: [PATCH 2/3] Add regress test for DoKexDhReply --- tests/regress.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/tests/regress.c b/tests/regress.c index 321151b8d..f5747a389 100644 --- a/tests/regress.c +++ b/tests/regress.c @@ -749,8 +749,8 @@ static void FreeKexReplyHarness(KexReplyHarness* harness) } } -static void InitKexReplyHarness(KexReplyHarness* harness, - const char* keyAlgo, byte mutateReply) +static void InitKexReplyHarnessEx(KexReplyHarness* harness, + const char* keyAlgo, byte mutateReply, byte skipPublicKeyCheck) { byte keyBuf[2048]; word32 keySz; @@ -781,7 +781,9 @@ static void InitKexReplyHarness(KexReplyHarness* harness, wolfSSH_SetUserAuth(harness->clientCtx, RegressionClientUserAuth); wolfSSH_SetUserAuth(harness->serverCtx, RegressionServerUserAuth); - wolfSSH_CTX_SetPublicKeyCheck(harness->clientCtx, AcceptAnyServerHostKey); + if (!skipPublicKeyCheck) { + wolfSSH_CTX_SetPublicKeyCheck(harness->clientCtx, AcceptAnyServerHostKey); + } keySz = LoadFileBuffer(REGRESS_SERVER_KEY_PATH, keyBuf, sizeof(keyBuf)); AssertTrue(keySz > 0); @@ -802,6 +804,12 @@ static void InitKexReplyHarness(KexReplyHarness* harness, WS_SUCCESS); } +static void InitKexReplyHarness(KexReplyHarness* harness, + const char* keyAlgo, byte mutateReply) +{ + InitKexReplyHarnessEx(harness, keyAlgo, mutateReply, 0); +} + static int IsHandshakeRetryable(int err) { return err == WS_WANT_READ || err == WS_WANT_WRITE || @@ -903,6 +911,33 @@ static void TestKexDhReplyRejectsRsaSha2_512SigNameDowngrade(void) } #endif +static void AssertHandshakeRejectsWithNoPublicKeyCheck(const char* keyAlgo) +{ + KexReplyHarness harness; + KexReplyRunResult result; + + InitKexReplyHarnessEx(&harness, keyAlgo, 0, 1 /* skipPublicKeyCheck */); + RunKexReplyHandshake(&harness, &result); + + AssertFalse(result.clientSuccess); + AssertTrue(result.clientRet == WS_FATAL_ERROR); + AssertTrue(result.clientErr != WS_WANT_READ && result.clientErr != WS_WANT_WRITE); + AssertIntEQ(result.clientErr, WS_PUBKEY_REJECTED_E); + AssertFalse(harness.client->connectState >= CONNECT_KEYED); + + FreeKexReplyHarness(&harness); +} + +static void TestKexDhReplyRejectsNoPublicKeyCheck(void) +{ +#ifndef WOLFSSH_NO_RSA_SHA2_256 + AssertHandshakeRejectsWithNoPublicKeyCheck("rsa-sha2-256"); +#endif +#ifndef WOLFSSH_NO_RSA_SHA2_512 + AssertHandshakeRejectsWithNoPublicKeyCheck("rsa-sha2-512"); +#endif +} + #endif /* KEXDH_REPLY_REGRESS_KEX_ALGO */ static void AssertChannelOpenFailResponse(const ChannelOpenHarness* harness, @@ -1667,6 +1702,7 @@ int main(int argc, char** argv) #ifndef WOLFSSH_NO_RSA_SHA2_512 TestKexDhReplyRejectsRsaSha2_512SigNameDowngrade(); #endif + TestKexDhReplyRejectsNoPublicKeyCheck(); #endif #ifdef WOLFSSH_SFTP From 9e3eee127611a254a841014c2369c64a5f45cb80 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Wed, 15 Apr 2026 11:04:09 +0900 Subject: [PATCH 3/3] Fix existing tests --- tests/api.c | 11 +++++++++++ tests/auth.c | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/api.c b/tests/api.c index d4d6ec221..fed2bad59 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1158,6 +1158,15 @@ static int sftpUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) return ret; } +static int AcceptAnyServerHostKey(const byte* pubKey, word32 pubKeySz, + void* ctx) +{ + (void)pubKey; + (void)pubKeySz; + (void)ctx; + return 0; +} + /* performs connection to port, sets WOLFSSH_CTX and WOLFSSH on success * caller needs to free ctx and ssh when done */ @@ -1180,6 +1189,7 @@ static void sftp_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port) return; } + wolfSSH_CTX_SetPublicKeyCheck(*ctx, AcceptAnyServerHostKey); wolfSSH_SetUserAuth(*ctx, sftpUserAuth); *ssh = wolfSSH_new(*ctx); if (*ssh == NULL) { @@ -1888,6 +1898,7 @@ static void keyboard_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port) return; } + wolfSSH_CTX_SetPublicKeyCheck(*ctx, AcceptAnyServerHostKey); wolfSSH_SetUserAuth(*ctx, keyboardUserAuth); *ssh = wolfSSH_new(*ctx); if (*ssh == NULL) { diff --git a/tests/auth.c b/tests/auth.c index d9f7013fc..35d6390b4 100644 --- a/tests/auth.c +++ b/tests/auth.c @@ -549,6 +549,15 @@ static THREAD_RETURN WOLFSSH_THREAD pubkey_server_thread(void* args) WOLFSSL_RETURN_FROM_THREAD(0); } +static int AcceptAnyServerHostKey(const byte* pubKey, word32 pubKeySz, + void* ctx) +{ + (void)pubKey; + (void)pubKeySz; + (void)ctx; + return 0; +} + /* Run one pubkey auth attempt. * sCtx – server context (authorised key hash) * cCtx – client context (key material to present) @@ -578,6 +587,7 @@ static int run_pubkey_test(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx, clientCtx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); AssertNotNull(clientCtx); + wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey); wolfSSH_SetUserAuth(clientCtx, clientPubkeyUserAuth); clientSsh = wolfSSH_new(clientCtx); @@ -977,6 +987,7 @@ static int basic_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port) return WS_BAD_ARGUMENT; } + wolfSSH_CTX_SetPublicKeyCheck(*ctx, AcceptAnyServerHostKey); wolfSSH_SetUserAuth(*ctx, keyboardUserAuth); *ssh = wolfSSH_new(*ctx); if (*ssh == NULL) {