Skip to content

Commit 98e3b63

Browse files
committed
fix DoChannelOpen failure response and add regression test
Send SSH_MSG_CHANNEL_OPEN_FAILURE for unclassified channel open errors instead of incorrectly falling back to SSH_MSG_REQUEST_FAILURE. Normalize OPEN_OK error cases to an administrative-prohibited channel open failure with a generic description, and add white-box regressions covering callback rejection plus optional direct-tcpip and agent-null paths. F-2076
1 parent 4ff4fac commit 98e3b63

2 files changed

Lines changed: 250 additions & 10 deletions

File tree

src/internal.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8710,19 +8710,19 @@ static int DoChannelOpen(WOLFSSH* ssh,
87108710
else {
87118711
const char *description = NULL;
87128712

8713-
if (fail_reason == OPEN_ADMINISTRATIVELY_PROHIBITED)
8713+
if (fail_reason == OPEN_OK) {
8714+
fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED;
8715+
description = "Channel open failed.";
8716+
}
8717+
else if (fail_reason == OPEN_ADMINISTRATIVELY_PROHIBITED)
87148718
description = "Administratively prohibited.";
87158719
else if (fail_reason == OPEN_UNKNOWN_CHANNEL_TYPE)
87168720
description = "Channel type not supported.";
87178721
else if (fail_reason == OPEN_RESOURCE_SHORTAGE)
87188722
description = "Not enough resources.";
87198723

8720-
if (description != NULL) {
8721-
ret = SendChannelOpenFail(ssh, peerChannelId,
8722-
fail_reason, description, "en");
8723-
}
8724-
else
8725-
ret = SendRequestSuccess(ssh, 0); /* XXX Is this right? */
8724+
ret = SendChannelOpenFail(ssh, peerChannelId,
8725+
fail_reason, description, "en");
87268726
}
87278727

87288728
#ifdef WOLFSSH_FWD

tests/regress.c

Lines changed: 243 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,103 @@ static byte ParseMsgId(const byte* pkt, word32 sz)
124124
return pkt[5];
125125
}
126126

127+
static word32 AppendByte(byte* buf, word32 bufSz, word32 idx, byte value)
128+
{
129+
AssertTrue(idx < bufSz);
130+
buf[idx++] = value;
131+
return idx;
132+
}
133+
134+
static word32 AppendUint32(byte* buf, word32 bufSz, word32 idx, word32 value)
135+
{
136+
word32 netValue = htonl(value);
137+
138+
AssertTrue(idx + UINT32_SZ <= bufSz);
139+
WMEMCPY(buf + idx, &netValue, UINT32_SZ);
140+
idx += UINT32_SZ;
141+
return idx;
142+
}
143+
144+
static word32 AppendData(byte* buf, word32 bufSz, word32 idx,
145+
const byte* data, word32 dataSz)
146+
{
147+
AssertTrue(idx + dataSz <= bufSz);
148+
if (dataSz > 0) {
149+
WMEMCPY(buf + idx, data, dataSz);
150+
idx += dataSz;
151+
}
152+
return idx;
153+
}
154+
155+
static word32 AppendString(byte* buf, word32 bufSz, word32 idx,
156+
const char* value)
157+
{
158+
word32 valueSz = (word32)WSTRLEN(value);
159+
160+
idx = AppendUint32(buf, bufSz, idx, valueSz);
161+
return AppendData(buf, bufSz, idx, (const byte*)value, valueSz);
162+
}
163+
164+
static word32 WrapPacket(byte msgId, const byte* payload, word32 payloadSz,
165+
byte* out, word32 outSz)
166+
{
167+
word32 idx = 0;
168+
word32 packetLen;
169+
word32 need;
170+
byte padLen = MIN_PAD_LENGTH;
171+
172+
while (((UINT32_SZ + PAD_LENGTH_SZ + MSG_ID_SZ + payloadSz + padLen) %
173+
MIN_BLOCK_SZ) != 0) {
174+
padLen++;
175+
}
176+
177+
packetLen = PAD_LENGTH_SZ + MSG_ID_SZ + payloadSz + padLen;
178+
need = UINT32_SZ + packetLen;
179+
180+
AssertTrue(outSz >= need);
181+
182+
idx = AppendUint32(out, outSz, idx, packetLen);
183+
idx = AppendByte(out, outSz, idx, padLen);
184+
idx = AppendByte(out, outSz, idx, msgId);
185+
idx = AppendData(out, outSz, idx, payload, payloadSz);
186+
AssertTrue(idx + padLen <= outSz);
187+
WMEMSET(out + idx, 0, padLen);
188+
idx += padLen;
189+
190+
return idx;
191+
}
192+
193+
static word32 BuildChannelOpenPacket(const char* type, word32 peerChannelId,
194+
word32 peerInitialWindowSz, word32 peerMaxPacketSz,
195+
const byte* extra, word32 extraSz, byte* out, word32 outSz)
196+
{
197+
byte payload[256];
198+
word32 idx = 0;
199+
200+
idx = AppendString(payload, sizeof(payload), idx, type);
201+
idx = AppendUint32(payload, sizeof(payload), idx, peerChannelId);
202+
idx = AppendUint32(payload, sizeof(payload), idx, peerInitialWindowSz);
203+
idx = AppendUint32(payload, sizeof(payload), idx, peerMaxPacketSz);
204+
idx = AppendData(payload, sizeof(payload), idx, extra, extraSz);
205+
206+
return WrapPacket(MSGID_CHANNEL_OPEN, payload, idx, out, outSz);
207+
}
208+
209+
#ifdef WOLFSSH_FWD
210+
static word32 BuildDirectTcpipExtra(const char* host, word32 hostPort,
211+
const char* origin, word32 originPort, byte* out, word32 outSz)
212+
{
213+
word32 idx = 0;
214+
215+
idx = AppendString(out, outSz, idx, host);
216+
idx = AppendUint32(out, outSz, idx, hostPort);
217+
idx = AppendString(out, outSz, idx, origin);
218+
idx = AppendUint32(out, outSz, idx, originPort);
219+
220+
return idx;
221+
}
222+
#endif
223+
127224
/* Simple in-memory transport harness */
128225
typedef struct {
129226
byte* in; /* data to feed into client */
@@ -134,9 +231,6 @@ typedef struct {
134231
word32 outCap;
135232
} MemIo;
136233

137-
/* Minimal send/recv helpers for future transport-level tests; keep them static
138-
* and unused for now to avoid warnings when Werror is on. */
139-
#ifdef WOLFSSH_TEST_MEMIO
140234
static int MemRecv(WOLFSSH* ssh, void* buf, word32 sz, void* ctx)
141235
{
142236
(void)ssh;
@@ -172,6 +266,78 @@ static void MemIoInit(MemIo* io, byte* in, word32 inSz, byte* out, word32 outCap
172266
io->outSz = 0;
173267
io->outCap = outCap;
174268
}
269+
270+
typedef struct {
271+
WOLFSSH_CTX* ctx;
272+
WOLFSSH* ssh;
273+
MemIo io;
274+
byte out[256];
275+
} ChannelOpenHarness;
276+
277+
static void InitChannelOpenHarness(ChannelOpenHarness* harness,
278+
byte* in, word32 inSz)
279+
{
280+
WMEMSET(harness, 0, sizeof(*harness));
281+
282+
harness->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
283+
AssertNotNull(harness->ctx);
284+
285+
wolfSSH_SetIORecv(harness->ctx, MemRecv);
286+
wolfSSH_SetIOSend(harness->ctx, MemSend);
287+
288+
harness->ssh = wolfSSH_new(harness->ctx);
289+
AssertNotNull(harness->ssh);
290+
291+
MemIoInit(&harness->io, in, inSz, harness->out, sizeof(harness->out));
292+
wolfSSH_SetIOReadCtx(harness->ssh, &harness->io);
293+
wolfSSH_SetIOWriteCtx(harness->ssh, &harness->io);
294+
harness->ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT;
295+
}
296+
297+
static void FreeChannelOpenHarness(ChannelOpenHarness* harness)
298+
{
299+
if (harness->ssh != NULL)
300+
wolfSSH_free(harness->ssh);
301+
if (harness->ctx != NULL)
302+
wolfSSH_CTX_free(harness->ctx);
303+
}
304+
305+
static void AssertChannelOpenFailResponse(const ChannelOpenHarness* harness,
306+
int ret)
307+
{
308+
byte msgId;
309+
310+
AssertIntEQ(ret, WS_SUCCESS);
311+
AssertIntEQ(harness->io.inOff, harness->io.inSz);
312+
AssertTrue(harness->io.outSz > 0);
313+
AssertTrue(harness->io.outSz <= harness->io.outCap);
314+
315+
msgId = ParseMsgId(harness->io.out, harness->io.outSz);
316+
AssertIntEQ(msgId, MSGID_CHANNEL_OPEN_FAIL);
317+
AssertFalse(msgId == MSGID_REQUEST_FAILURE);
318+
}
319+
320+
static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
321+
{
322+
(void)channel;
323+
(void)ctx;
324+
325+
return WS_BAD_ARGUMENT;
326+
}
327+
328+
#ifdef WOLFSSH_FWD
329+
static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx,
330+
const char* host, word32 port)
331+
{
332+
(void)ctx;
333+
(void)host;
334+
(void)port;
335+
336+
if (action == WOLFSSH_FWD_LOCAL_SETUP)
337+
return WS_FWD_SETUP_E;
338+
339+
return WS_SUCCESS;
340+
}
175341
#endif
176342

177343

@@ -369,6 +535,73 @@ static void TestChannelAllowedAfterAuth(WOLFSSH* ssh)
369535
AssertTrue(allowed);
370536
}
371537

538+
static void TestChannelOpenCallbackRejectSendsOpenFail(void)
539+
{
540+
ChannelOpenHarness harness;
541+
byte in[128];
542+
word32 inSz;
543+
int ret;
544+
545+
inSz = BuildChannelOpenPacket("session", 7, 0x4000, 0x8000,
546+
NULL, 0, in, sizeof(in));
547+
548+
InitChannelOpenHarness(&harness, in, inSz);
549+
AssertIntEQ(wolfSSH_CTX_SetChannelOpenCb(harness.ctx, RejectChannelOpenCb),
550+
WS_SUCCESS);
551+
552+
ret = DoReceive(harness.ssh);
553+
AssertChannelOpenFailResponse(&harness, ret);
554+
555+
FreeChannelOpenHarness(&harness);
556+
}
557+
558+
#ifdef WOLFSSH_FWD
559+
static void TestDirectTcpipRejectSendsOpenFail(void)
560+
{
561+
ChannelOpenHarness harness;
562+
byte extra[128];
563+
byte in[192];
564+
word32 extraSz;
565+
word32 inSz;
566+
int ret;
567+
568+
extraSz = BuildDirectTcpipExtra("127.0.0.1", 8080, "127.0.0.1", 2222,
569+
extra, sizeof(extra));
570+
inSz = BuildChannelOpenPacket("direct-tcpip", 9, 0x4000, 0x8000,
571+
extra, extraSz, in, sizeof(in));
572+
573+
InitChannelOpenHarness(&harness, in, inSz);
574+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, RejectDirectTcpipSetup, NULL),
575+
WS_SUCCESS);
576+
577+
ret = DoReceive(harness.ssh);
578+
AssertChannelOpenFailResponse(&harness, ret);
579+
580+
FreeChannelOpenHarness(&harness);
581+
}
582+
#endif
583+
584+
#ifdef WOLFSSH_AGENT
585+
static void TestAgentChannelNullAgentSendsOpenFail(void)
586+
{
587+
ChannelOpenHarness harness;
588+
byte in[128];
589+
word32 inSz;
590+
int ret;
591+
592+
inSz = BuildChannelOpenPacket("auth-agent@openssh.com", 11, 0x4000,
593+
0x8000, NULL, 0, in, sizeof(in));
594+
595+
InitChannelOpenHarness(&harness, in, inSz);
596+
AssertTrue(harness.ssh->agent == NULL);
597+
598+
ret = DoReceive(harness.ssh);
599+
AssertChannelOpenFailResponse(&harness, ret);
600+
601+
FreeChannelOpenHarness(&harness);
602+
}
603+
#endif
604+
372605

373606
/* Reject a peer KEXINIT once keying is in progress. */
374607
static void TestKexInitRejectedWhenKeying(WOLFSSH* ssh)
@@ -583,6 +816,13 @@ int main(int argc, char** argv)
583816
TestPublicKeyFailureAborts(ssh);
584817
TestChannelBlockedBeforeAuth(ssh);
585818
TestChannelAllowedAfterAuth(ssh);
819+
TestChannelOpenCallbackRejectSendsOpenFail();
820+
#ifdef WOLFSSH_FWD
821+
TestDirectTcpipRejectSendsOpenFail();
822+
#endif
823+
#ifdef WOLFSSH_AGENT
824+
TestAgentChannelNullAgentSendsOpenFail();
825+
#endif
586826
TestKexInitRejectedWhenKeying(ssh);
587827
TestClientBuffersIdempotent();
588828
TestPasswordEofNoCrash();

0 commit comments

Comments
 (0)