From 616eb681e70759c30c3ba158f2b18d7bd954def8 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 24 Jun 2026 13:57:01 -0700 Subject: [PATCH] Gate forwarded-tcpip opens like direct-tcpip - forwarded-tcpip was never gated; fell through to default-accept channelOpenCb. - Require fwdCb for both forwarding channel types, failing closed without it. - Reject server-side forwarded-tcpip opens before any policy hook runs. - Add regress coverage for both rejections. Issue: F-6275 --- src/internal.c | 59 +++++++++++++++++++++++++++++++++++-------------- tests/regress.c | 28 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/internal.c b/src/internal.c index 1afcaba7..96650a4f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9266,23 +9266,45 @@ static int DoChannelOpen(WOLFSSH* ssh, else { ChannelUpdatePeer(newChannel, peerChannelId, peerInitialWindowSz, peerMaxPacketSz); - if (ssh->ctx->channelOpenCb) { - ret = ssh->ctx->channelOpenCb(newChannel, ssh->channelOpenCtx); - } - else { - WLOG(WS_LOG_WARN, "No channel open callback set " - "(call wolfSSH_CTX_SetChannelOpenCb()), accepting " - "channel open by default; typeId=%u, peerChannelId=%u", - (word32)typeId, peerChannelId); - } - if (ssh->channelListSz == 0) - ssh->defaultPeerChannelId = peerChannelId; #ifdef WOLFSSH_FWD - if (typeId == ID_CHANTYPE_TCPIP_DIRECT) { - ChannelUpdateForward(newChannel, - host, hostPort, origin, originPort, isDirect); - + /* A forwarded-tcpip open is a server-to-client message sent in + * response to a tcpip-forward request, so only a client should + * ever receive one. Reject opens arriving in the wrong direction + * up front, before any policy callback runs or channel state is + * updated. direct-tcpip is intentionally not direction-checked: + * either forwarding side may legitimately request a direct + * forward. */ + if (typeId == ID_CHANTYPE_TCPIP_FORWARD && + ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) { + WLOG(WS_LOG_WARN, "Rejecting forwarded-tcpip channel open " + "received by a server (wrong direction)"); + fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED; + ret = WS_ERROR; + } + #endif /* WOLFSSH_FWD */ + if (ret == WS_SUCCESS) { + if (ssh->ctx->channelOpenCb) { + ret = ssh->ctx->channelOpenCb(newChannel, + ssh->channelOpenCtx); + } + else { + WLOG(WS_LOG_WARN, "No channel open callback set " + "(call wolfSSH_CTX_SetChannelOpenCb()), accepting " + "channel open by default; typeId=%u, " + "peerChannelId=%u", + (word32)typeId, peerChannelId); + } + if (ssh->channelListSz == 0) + ssh->defaultPeerChannelId = peerChannelId; + } + #ifdef WOLFSSH_FWD + if (ret == WS_SUCCESS && + (typeId == ID_CHANTYPE_TCPIP_DIRECT || + typeId == ID_CHANTYPE_TCPIP_FORWARD)) { if (ssh->ctx->fwdCb) { + ChannelUpdateForward(newChannel, + host, hostPort, origin, originPort, isDirect); + ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_SETUP, ssh->fwdCbCtx, host, hostPort); if (ret == WS_SUCCESS) { @@ -9291,8 +9313,11 @@ static int DoChannelOpen(WOLFSSH* ssh, } } else { - WLOG(WS_LOG_WARN, "No forward callback set for direct-tcpip channel," - " failing channel open"); + /* Both forwarding channel types require an explicit policy + * callback; without one, fail closed rather than letting + * the default-accept channelOpenCb path admit them. */ + WLOG(WS_LOG_WARN, "No forward callback set for forwarding " + "channel, failing channel open"); fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED; ret = WS_ERROR; } diff --git a/tests/regress.c b/tests/regress.c index 86e19500..5d9e46cd 100644 --- a/tests/regress.c +++ b/tests/regress.c @@ -1525,6 +1525,33 @@ static void TestDirectTcpipNoFwdCbSendsOpenFail(void) FreeChannelOpenHarness(&harness); } +static void TestForwardedTcpipOnServerSendsOpenFail(void) +{ + ChannelOpenHarness harness; + byte extra[128]; + byte in[192]; + word32 extraSz; + word32 inSz; + int ret; + + /* forwarded-tcpip is only ever sent server-to-client. A server receiving + * one is the wrong direction and must be rejected even with a fwdCb set, + * before the forwarding policy hook runs. */ + extraSz = BuildDirectTcpipExtra("127.0.0.1", 8080, "127.0.0.1", 2222, + extra, sizeof(extra)); + inSz = BuildChannelOpenPacket("forwarded-tcpip", 9, 0x4000, 0x8000, + extra, extraSz, in, sizeof(in)); + + InitChannelOpenHarness(&harness, in, inSz); + AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, AcceptFwdCb, NULL), + WS_SUCCESS); + + ret = DoReceive(harness.ssh); + AssertChannelOpenFailResponse(&harness, ret); + + FreeChannelOpenHarness(&harness); +} + static void TestGlobalRequestFwdNoCbSendsFailure(void) { ChannelOpenHarness harness; @@ -4133,6 +4160,7 @@ int main(int argc, char** argv) #ifdef WOLFSSH_FWD TestDirectTcpipRejectSendsOpenFail(); TestDirectTcpipNoFwdCbSendsOpenFail(); + TestForwardedTcpipOnServerSendsOpenFail(); TestGlobalRequestFwdNoCbSendsFailure(); TestGlobalRequestFwdNoCbNoReplyKeepsConnection(); TestGlobalRequestFwdWithCbSendsSuccess();