diff --git a/src/internal.c b/src/internal.c index 11902b7b4..5db18ddb4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8496,16 +8496,35 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh, isCancel ? " cancel" : "", bindAddr, bindPort); } - if (ret == WS_SUCCESS && wantReply) { - ret = SendGlobalRequestFwdSuccess(ssh, 1, bindPort); - } - if (ret == WS_SUCCESS) { if (ssh->ctx->fwdCb) { ret = ssh->ctx->fwdCb(isCancel ? WOLFSSH_FWD_REMOTE_CLEANUP : WOLFSSH_FWD_REMOTE_SETUP, ssh->fwdCbCtx, bindAddr, bindPort); } + else { + WLOG(WS_LOG_WARN, "No forwarding callback set, rejecting request. " + "Set one with wolfSSH_CTX_SetFwdCb()."); + ret = WS_UNIMPLEMENTED_E; + } + } + + if (wantReply) { + if (ret == WS_SUCCESS) { + if (isCancel) { + ret = SendRequestSuccess(ssh, 1); + } + else { + ret = SendGlobalRequestFwdSuccess(ssh, 1, bindPort); + } + } + else { + ret = SendRequestSuccess(ssh, 0); + } + } + else if (ret == WS_UNIMPLEMENTED_E) { + /* No reply expected; silently reject without terminating connection. */ + ret = WS_SUCCESS; } if (bindAddr != NULL) diff --git a/tests/regress.c b/tests/regress.c index 5d069d7fc..bf37202d1 100644 --- a/tests/regress.c +++ b/tests/regress.c @@ -231,6 +231,21 @@ static word32 BuildDirectTcpipExtra(const char* host, word32 hostPort, return idx; } + +static word32 BuildGlobalRequestFwdPacket(const char* bindAddr, word32 bindPort, + int isCancel, byte wantReply, byte* out, word32 outSz) +{ + byte payload[256]; + word32 idx = 0; + const char* reqName = isCancel ? "cancel-tcpip-forward" : "tcpip-forward"; + + idx = AppendString(payload, sizeof(payload), idx, reqName); + idx = AppendByte (payload, sizeof(payload), idx, wantReply); + idx = AppendString(payload, sizeof(payload), idx, bindAddr); + idx = AppendUint32(payload, sizeof(payload), idx, bindPort); + + return WrapPacket(MSGID_GLOBAL_REQUEST, payload, idx, out, outSz); +} #endif /* Simple in-memory transport harness */ @@ -957,6 +972,94 @@ static void AssertChannelOpenFailResponse(const ChannelOpenHarness* harness, AssertTrue(harness->ssh->channelList == NULL); } +#ifdef WOLFSSH_FWD +static word32 ParsePayloadLen(const byte* packet, word32 packetSz) +{ + word32 packetLen; + byte padLen; + + AssertNotNull(packet); + AssertTrue(packetSz >= 6); + + WMEMCPY(&packetLen, packet, sizeof(packetLen)); + packetLen = ntohl(packetLen); + padLen = packet[4]; + + AssertTrue(packetLen >= (word32)padLen + 1); + AssertTrue(packetSz >= packetLen + 4); + + return packetLen - padLen - 1; +} + +static const byte* ParseGlobalRequestName(const byte* packet, word32 packetSz, + word32* nameSz) +{ + word32 packetLen; + word32 payloadLen; + word32 strSz; + const byte* payload; + + AssertNotNull(packet); + AssertNotNull(nameSz); + AssertTrue(packetSz >= 10); + + WMEMCPY(&packetLen, packet, sizeof(packetLen)); + packetLen = ntohl(packetLen); + AssertTrue(packetSz >= packetLen + 4); + + payloadLen = ParsePayloadLen(packet, packetSz); + payload = packet + 5; + + AssertTrue(payloadLen >= 1 + sizeof(word32)); + AssertIntEQ(payload[0], MSGID_GLOBAL_REQUEST); + + WMEMCPY(&strSz, payload + 1, sizeof(strSz)); + strSz = ntohl(strSz); + AssertTrue(payloadLen >= 1 + sizeof(word32) + strSz); + + *nameSz = strSz; + return payload + 1 + sizeof(word32); +} + +static void AssertGlobalRequestReply(const ChannelOpenHarness* harness, + byte expectedMsgId) +{ + byte msgId; + word32 payloadLen; + + AssertTrue(harness->io.outSz > 0); + msgId = ParseMsgId(harness->io.out, harness->io.outSz); + AssertIntEQ(msgId, expectedMsgId); + + payloadLen = ParsePayloadLen(harness->io.out, harness->io.outSz); + if (expectedMsgId == MSGID_REQUEST_FAILURE) { + AssertIntEQ(payloadLen, 1); + } + else if (expectedMsgId == MSGID_REQUEST_SUCCESS) { + const byte* reqName; + word32 reqNameSz; + + reqName = ParseGlobalRequestName(harness->io.in, harness->io.inSz, + &reqNameSz); + + if (reqNameSz == sizeof("tcpip-forward") - 1 && + WMEMCMP(reqName, "tcpip-forward", + sizeof("tcpip-forward") - 1) == 0) { + AssertIntEQ(payloadLen, 5); + } + else if (reqNameSz == sizeof("cancel-tcpip-forward") - 1 && + WMEMCMP(reqName, "cancel-tcpip-forward", + sizeof("cancel-tcpip-forward") - 1) == 0) { + AssertIntEQ(payloadLen, 1); + } + else { + Fail(("unexpected global request name"), + ("%.*s", (int)reqNameSz, reqName)); + } + } +} +#endif + static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx) { (void)channel; @@ -978,6 +1081,17 @@ static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx, return WS_SUCCESS; } + +static int AcceptFwdCb(WS_FwdCbAction action, void* ctx, + const char* host, word32 port) +{ + (void)action; + (void)ctx; + (void)host; + (void)port; + + return WS_SUCCESS; +} #endif @@ -1242,6 +1356,101 @@ static void TestDirectTcpipNoFwdCbSendsOpenFail(void) FreeChannelOpenHarness(&harness); } + +static void TestGlobalRequestFwdNoCbSendsFailure(void) +{ + ChannelOpenHarness harness; + byte in[256]; + word32 inSz; + int ret; + + inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 2222, 0, 1, in, sizeof(in)); + InitChannelOpenHarness(&harness, in, inSz); + /* no fwdCb registered */ + + ret = DoReceive(harness.ssh); + + AssertIntEQ(ret, WS_SUCCESS); + AssertGlobalRequestReply(&harness, MSGID_REQUEST_FAILURE); + + FreeChannelOpenHarness(&harness); +} + +static void TestGlobalRequestFwdNoCbNoReplyKeepsConnection(void) +{ + ChannelOpenHarness harness; + byte in[256]; + word32 inSz; + int ret; + + /* wantReply=0: no reply sent, connection must stay alive */ + inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 2222, 0, 0, in, sizeof(in)); + InitChannelOpenHarness(&harness, in, inSz); + /* no fwdCb registered */ + + ret = DoReceive(harness.ssh); + + AssertIntEQ(ret, WS_SUCCESS); + AssertIntEQ(harness.io.outSz, 0); /* no reply sent */ + + FreeChannelOpenHarness(&harness); +} + +static void TestGlobalRequestFwdWithCbSendsSuccess(void) +{ + ChannelOpenHarness harness; + byte in[256]; + word32 inSz; + int ret; + + inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 2222, 0, 1, in, sizeof(in)); + InitChannelOpenHarness(&harness, in, inSz); + AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, AcceptFwdCb, NULL), WS_SUCCESS); + + ret = DoReceive(harness.ssh); + + AssertIntEQ(ret, WS_SUCCESS); + AssertGlobalRequestReply(&harness, MSGID_REQUEST_SUCCESS); + + FreeChannelOpenHarness(&harness); +} + +static void TestGlobalRequestFwdCancelNoCbSendsFailure(void) +{ + ChannelOpenHarness harness; + byte in[256]; + word32 inSz; + int ret; + + inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 2222, 1, 1, in, sizeof(in)); + InitChannelOpenHarness(&harness, in, inSz); + + ret = DoReceive(harness.ssh); + + AssertIntEQ(ret, WS_SUCCESS); + AssertGlobalRequestReply(&harness, MSGID_REQUEST_FAILURE); + + FreeChannelOpenHarness(&harness); +} + +static void TestGlobalRequestFwdCancelWithCbSendsSuccess(void) +{ + ChannelOpenHarness harness; + byte in[256]; + word32 inSz; + int ret; + + inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 2222, 1, 1, in, sizeof(in)); + InitChannelOpenHarness(&harness, in, inSz); + AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, AcceptFwdCb, NULL), WS_SUCCESS); + + ret = DoReceive(harness.ssh); + + AssertIntEQ(ret, WS_SUCCESS); + AssertGlobalRequestReply(&harness, MSGID_REQUEST_SUCCESS); + + FreeChannelOpenHarness(&harness); +} #endif #ifdef WOLFSSH_AGENT @@ -1707,6 +1916,11 @@ int main(int argc, char** argv) #ifdef WOLFSSH_FWD TestDirectTcpipRejectSendsOpenFail(); TestDirectTcpipNoFwdCbSendsOpenFail(); + TestGlobalRequestFwdNoCbSendsFailure(); + TestGlobalRequestFwdNoCbNoReplyKeepsConnection(); + TestGlobalRequestFwdWithCbSendsSuccess(); + TestGlobalRequestFwdCancelNoCbSendsFailure(); + TestGlobalRequestFwdCancelWithCbSendsSuccess(); #endif #ifdef WOLFSSH_AGENT TestAgentChannelNullAgentSendsOpenFail();