Skip to content

Commit 240c904

Browse files
committed
tests: cover a channel-open rejection on a forwarding channel
No regress case registered a channelOpenCb and a fwdCb at the same time, which is why the v1.5.0 clobber -- fwdCb(LOCAL_SETUP) overwriting the open callback's rejection -- shipped unnoticed. The fix is on master already (0317c40, 616eb68); this is the missing gate. - TestDirectTcpipOpenCbRejectBeatsFwdCb: both callbacks registered and the open callback rejects, so the peer must get a channel-open failure and the forwarding hook must not run at all - TestDirectTcpipFwdCbRejectAfterOpenCbAccept: the other half, where the open callback accepts and the fwdCb's rejection has to reach the peer - TestDirectTcpipFwdCbRejectsChannelId: DoChannelOpen() consults the fwdCb twice, and only the setup rejection was covered All three pin the recipient channel and the reason code, not just the message id. The call counter is file scope rather than reached through the callback ctx, so the zero the first test asserts cannot be a ctx that stopped being delivered; the third test reads 2 on the same counter as the positive control.
1 parent 9731cfe commit 240c904

1 file changed

Lines changed: 152 additions & 0 deletions

File tree

tests/regress.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,14 @@ static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
18391839
}
18401840

18411841
#ifdef WOLFSSH_FWD
1842+
static int AcceptChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
1843+
{
1844+
(void)channel;
1845+
(void)ctx;
1846+
1847+
return WS_SUCCESS;
1848+
}
1849+
18421850
static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx,
18431851
const char* host, word32 port)
18441852
{
@@ -1863,6 +1871,41 @@ static int AcceptFwdCb(WS_FwdCbAction action, void* ctx,
18631871
return WS_SUCCESS;
18641872
}
18651873

1874+
/* Counts every action the library asks for. File scope rather than reached
1875+
* through the callback ctx, so a zero reading means the hook did not run and
1876+
* cannot instead mean the ctx stopped being delivered. */
1877+
static word32 fwdCbCallCount;
1878+
1879+
static int CountingFwdCb(WS_FwdCbAction action, void* ctx,
1880+
const char* host, word32 port)
1881+
{
1882+
(void)action;
1883+
(void)ctx;
1884+
(void)host;
1885+
(void)port;
1886+
1887+
fwdCbCallCount++;
1888+
1889+
return WS_SUCCESS;
1890+
}
1891+
1892+
/* Counts, and rejects the channel-id handoff that follows a successful
1893+
* LOCAL_SETUP -- the second of DoChannelOpen()'s two fwdCb consultations. */
1894+
static int CountingRejectChannelIdFwdCb(WS_FwdCbAction action, void* ctx,
1895+
const char* host, word32 port)
1896+
{
1897+
(void)ctx;
1898+
(void)host;
1899+
(void)port;
1900+
1901+
fwdCbCallCount++;
1902+
1903+
if (action == WOLFSSH_FWD_CHANNEL_ID)
1904+
return WS_FWD_NOT_AVAILABLE;
1905+
1906+
return WS_SUCCESS;
1907+
}
1908+
18661909
#define REGRESS_FWD_ALLOC_PORT 49152
18671910

18681911
static int AllocatePortFwdCb(WS_FwdCbAction action, void* ctx,
@@ -2674,6 +2717,112 @@ static void TestDirectTcpipNoFwdCbSendsOpenFail(void)
26742717
FreeChannelOpenHarness(&harness);
26752718
}
26762719

2720+
/* Both a channelOpenCb and a fwdCb registered, open callback rejects. The
2721+
* rejection has to stand: the forwarding hook must not run, and must not
2722+
* overwrite the rejection with its own return. That clobber shipped in
2723+
* v1.5.0, where no test registered both callbacks at once. */
2724+
static void TestDirectTcpipOpenCbRejectBeatsFwdCb(void)
2725+
{
2726+
ChannelOpenHarness harness;
2727+
byte extra[128];
2728+
byte in[192];
2729+
word32 extraSz;
2730+
word32 inSz;
2731+
int ret;
2732+
2733+
fwdCbCallCount = 0;
2734+
2735+
extraSz = BuildDirectTcpipExtra("127.0.0.1", 8080, "127.0.0.1", 2222,
2736+
extra, sizeof(extra));
2737+
inSz = BuildChannelOpenPacket("direct-tcpip", 9, 0x4000, 0x8000,
2738+
extra, extraSz, in, sizeof(in));
2739+
2740+
InitChannelOpenHarness(&harness, in, inSz);
2741+
AssertIntEQ(wolfSSH_CTX_SetChannelOpenCb(harness.ctx, RejectChannelOpenCb),
2742+
WS_SUCCESS);
2743+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, CountingFwdCb, NULL),
2744+
WS_SUCCESS);
2745+
2746+
ret = DoReceive(harness.ssh);
2747+
AssertChannelOpenFailResponse(&harness, ret);
2748+
AssertIntEQ(ParseChannelOpenFailRecipient(harness.io.out, harness.io.outSz),
2749+
9); /* RFC 4254 5.1: the peer's channel ID comes back */
2750+
AssertIntEQ(ParseChannelOpenFailReason(harness.io.out, harness.io.outSz),
2751+
OPEN_ADMINISTRATIVELY_PROHIBITED);
2752+
AssertIntEQ(fwdCbCallCount, 0);
2753+
2754+
FreeChannelOpenHarness(&harness);
2755+
}
2756+
2757+
/* The other half of the pair: the open callback accepts, so the fwdCb decides,
2758+
* and its rejection must reach the peer. */
2759+
static void TestDirectTcpipFwdCbRejectAfterOpenCbAccept(void)
2760+
{
2761+
ChannelOpenHarness harness;
2762+
byte extra[128];
2763+
byte in[192];
2764+
word32 extraSz;
2765+
word32 inSz;
2766+
int ret;
2767+
2768+
extraSz = BuildDirectTcpipExtra("127.0.0.1", 8080, "127.0.0.1", 2222,
2769+
extra, sizeof(extra));
2770+
inSz = BuildChannelOpenPacket("direct-tcpip", 9, 0x4000, 0x8000,
2771+
extra, extraSz, in, sizeof(in));
2772+
2773+
InitChannelOpenHarness(&harness, in, inSz);
2774+
AssertIntEQ(wolfSSH_CTX_SetChannelOpenCb(harness.ctx, AcceptChannelOpenCb),
2775+
WS_SUCCESS);
2776+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, RejectDirectTcpipSetup, NULL),
2777+
WS_SUCCESS);
2778+
2779+
ret = DoReceive(harness.ssh);
2780+
AssertChannelOpenFailResponse(&harness, ret);
2781+
AssertIntEQ(ParseChannelOpenFailRecipient(harness.io.out, harness.io.outSz),
2782+
9);
2783+
AssertIntEQ(ParseChannelOpenFailReason(harness.io.out, harness.io.outSz),
2784+
OPEN_ADMINISTRATIVELY_PROHIBITED);
2785+
2786+
FreeChannelOpenHarness(&harness);
2787+
}
2788+
2789+
/* DoChannelOpen() consults the fwdCb twice. A rejection at the second
2790+
* consultation, the channel-id handoff, must fail the open the same way the
2791+
* setup rejection does. The count doubles as the positive control for the
2792+
* zero asserted above: the same counter reaches 2 here. */
2793+
static void TestDirectTcpipFwdCbRejectsChannelId(void)
2794+
{
2795+
ChannelOpenHarness harness;
2796+
byte extra[128];
2797+
byte in[192];
2798+
word32 extraSz;
2799+
word32 inSz;
2800+
int ret;
2801+
2802+
fwdCbCallCount = 0;
2803+
2804+
extraSz = BuildDirectTcpipExtra("127.0.0.1", 8080, "127.0.0.1", 2222,
2805+
extra, sizeof(extra));
2806+
inSz = BuildChannelOpenPacket("direct-tcpip", 9, 0x4000, 0x8000,
2807+
extra, extraSz, in, sizeof(in));
2808+
2809+
InitChannelOpenHarness(&harness, in, inSz);
2810+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx,
2811+
CountingRejectChannelIdFwdCb, NULL), WS_SUCCESS);
2812+
2813+
ret = DoReceive(harness.ssh);
2814+
AssertChannelOpenFailResponse(&harness, ret);
2815+
AssertIntEQ(ParseChannelOpenFailRecipient(harness.io.out, harness.io.outSz),
2816+
9);
2817+
/* This path leaves fail_reason at OPEN_OK and leans on the default,
2818+
* unlike the rejections that set the reason themselves. */
2819+
AssertIntEQ(ParseChannelOpenFailReason(harness.io.out, harness.io.outSz),
2820+
OPEN_ADMINISTRATIVELY_PROHIBITED);
2821+
AssertIntEQ(fwdCbCallCount, 2);
2822+
2823+
FreeChannelOpenHarness(&harness);
2824+
}
2825+
26772826
static void TestForwardedTcpipOnServerSendsOpenFail(void)
26782827
{
26792828
ChannelOpenHarness harness;
@@ -8025,6 +8174,9 @@ int main(int argc, char** argv)
80258174
#ifdef WOLFSSH_FWD
80268175
TestDirectTcpipRejectSendsOpenFail();
80278176
TestDirectTcpipNoFwdCbSendsOpenFail();
8177+
TestDirectTcpipOpenCbRejectBeatsFwdCb();
8178+
TestDirectTcpipFwdCbRejectAfterOpenCbAccept();
8179+
TestDirectTcpipFwdCbRejectsChannelId();
80288180
TestForwardedTcpipOnServerSendsOpenFail();
80298181
TestGlobalRequestFwdNoCbSendsFailure();
80308182
TestGlobalRequestFwdNoCbNoReplyKeepsConnection();

0 commit comments

Comments
 (0)