Skip to content

Commit 37573ce

Browse files
committed
tests: cover the exec and subsystem req callbacks
Only the shell hook had coverage, so nothing held DoChannelRequest() to handing the command and session type to the exec and subsystem callbacks or to answering with what they return. - assert each callback sees the session type and the command string the request carried, and the ctx set on the session - assert an accepting callback draws CHANNEL_SUCCESS and a rejecting one CHANNEL_FAILURE, whether it rejects with a WS_ error or a bare nonzero
1 parent 5f9752f commit 37573ce

1 file changed

Lines changed: 123 additions & 0 deletions

File tree

tests/regress.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3350,6 +3350,127 @@ static void TestChannelCloseCallbackReturnIgnored(void)
33503350
FreeChannelOpenHarness(&harness);
33513351
}
33523352

3353+
/* Builds a plaintext SSH_MSG_CHANNEL_REQUEST whose type-specific tail is a
3354+
* single string, which is the shape of both "exec" and "subsystem". */
3355+
static word32 BuildChannelStringRequestPacket(word32 recipientChannelId,
3356+
const char* type, byte wantReply, const char* arg,
3357+
byte* out, word32 outSz)
3358+
{
3359+
byte payload[128];
3360+
word32 idx = 0;
3361+
3362+
idx = AppendUint32(payload, sizeof(payload), idx, recipientChannelId);
3363+
idx = AppendString(payload, sizeof(payload), idx, type);
3364+
idx = AppendByte(payload, sizeof(payload), idx, wantReply);
3365+
idx = AppendString(payload, sizeof(payload), idx, arg);
3366+
3367+
return WrapPacket(MSGID_CHANNEL_REQUEST, payload, idx, out, outSz);
3368+
}
3369+
3370+
/* What a session request callback saw. */
3371+
static int sessionReqCbCalls;
3372+
static WS_SessionType sessionReqCbType;
3373+
static char sessionReqCbCommand[32];
3374+
static void* sessionReqCbCtx;
3375+
static int sessionReqCbReturn;
3376+
3377+
static int RecordingSessionReqCb(WOLFSSH_CHANNEL* channel, void* ctx)
3378+
{
3379+
const char* command;
3380+
3381+
sessionReqCbCalls++;
3382+
sessionReqCbCtx = ctx;
3383+
sessionReqCbCommand[0] = 0;
3384+
3385+
if (channel != NULL) {
3386+
sessionReqCbType = wolfSSH_ChannelGetSessionType(channel);
3387+
command = wolfSSH_ChannelGetSessionCommand(channel);
3388+
if (command != NULL) {
3389+
WSTRNCPY(sessionReqCbCommand, command,
3390+
sizeof(sessionReqCbCommand) - 1);
3391+
sessionReqCbCommand[sizeof(sessionReqCbCommand) - 1] = 0;
3392+
}
3393+
}
3394+
3395+
return sessionReqCbReturn;
3396+
}
3397+
3398+
/* Drives one session request through a fresh harness, with the callback
3399+
* returning cbReturn, and returns the message id the server answered with. */
3400+
static byte RunSessionRequest(const char* type, const char* arg, int cbReturn,
3401+
WS_SessionType expectType)
3402+
{
3403+
ChannelOpenHarness harness;
3404+
WOLFSSH_CHANNEL* channel;
3405+
byte in[128];
3406+
word32 inSz;
3407+
int cbCtx = 0;
3408+
byte replyId;
3409+
3410+
sessionReqCbCalls = 0;
3411+
sessionReqCbType = WOLFSSH_SESSION_UNKNOWN;
3412+
sessionReqCbCommand[0] = 0;
3413+
sessionReqCbCtx = NULL;
3414+
sessionReqCbReturn = cbReturn;
3415+
3416+
InitChannelOpenHarness(&harness, NULL, 0);
3417+
if (WSTRCMP(type, "exec") == 0) {
3418+
AssertIntEQ(wolfSSH_CTX_SetChannelReqExecCb(harness.ctx,
3419+
RecordingSessionReqCb), WS_SUCCESS);
3420+
}
3421+
else {
3422+
AssertIntEQ(wolfSSH_CTX_SetChannelReqSubsysCb(harness.ctx,
3423+
RecordingSessionReqCb), WS_SUCCESS);
3424+
}
3425+
AssertIntEQ(wolfSSH_SetChannelReqCtx(harness.ssh, &cbCtx), WS_SUCCESS);
3426+
3427+
channel = SeedUnconfirmedChannel(&harness);
3428+
AssertIntEQ(ChannelUpdatePeer(channel, 5, 1024, 1024), WS_SUCCESS);
3429+
channel->openConfirmed = 1;
3430+
3431+
inSz = BuildChannelStringRequestPacket(channel->channel, type, 1, arg,
3432+
in, sizeof(in));
3433+
RepointHarnessInput(&harness, in, inSz);
3434+
3435+
AssertIntEQ(DoReceive(harness.ssh), WS_SUCCESS);
3436+
AssertIntEQ(harness.io.inOff, harness.io.inSz);
3437+
AssertIntEQ(sessionReqCbCalls, 1);
3438+
AssertTrue(sessionReqCbCtx == &cbCtx);
3439+
AssertIntEQ(sessionReqCbType, expectType);
3440+
AssertIntEQ(WSTRCMP(sessionReqCbCommand, arg), 0);
3441+
3442+
replyId = ParseMsgId(harness.io.out, harness.io.outSz);
3443+
FreeChannelOpenHarness(&harness);
3444+
3445+
return replyId;
3446+
}
3447+
3448+
/* The exec callback is the only place an application can vet a remote
3449+
* command, and its return is what decides the reply on the wire.
3450+
* DoChannelRequest() tests only for nonzero, so a bare 1 rejects the same
3451+
* as a WS_ error. */
3452+
static void TestChannelReqExecCallbackRuns(void)
3453+
{
3454+
AssertIntEQ(RunSessionRequest("exec", "ls", WS_SUCCESS,
3455+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_SUCCESS);
3456+
AssertIntEQ(RunSessionRequest("exec", "ls", WS_BAD_ARGUMENT,
3457+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_FAILURE);
3458+
AssertIntEQ(RunSessionRequest("exec", "ls", 1,
3459+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_FAILURE);
3460+
}
3461+
3462+
/* Same contract for the subsystem callback, which is how a server decides
3463+
* whether to serve SFTP on a channel. */
3464+
static void TestChannelReqSubsysCallbackRuns(void)
3465+
{
3466+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", WS_SUCCESS,
3467+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_SUCCESS);
3468+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", WS_BAD_ARGUMENT,
3469+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_FAILURE);
3470+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", 1,
3471+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_FAILURE);
3472+
}
3473+
33533474
/* A username change after the first userauth request must end the session. */
33543475
static void TestUsernameChangeDisconnects(void)
33553476
{
@@ -12554,6 +12675,8 @@ int main(int argc, char** argv)
1255412675
TestChannelOpenFailCallbackRejects();
1255512676
TestChannelCloseCallbackRuns();
1255612677
TestChannelCloseCallbackReturnIgnored();
12678+
TestChannelReqExecCallbackRuns();
12679+
TestChannelReqSubsysCallbackRuns();
1255712680
TestSecondSessionChannelRejected();
1255812681
TestUsernameChangeDisconnects();
1255912682
TestSameUserRetryAllowed();

0 commit comments

Comments
 (0)