Skip to content

Commit dbb834b

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 4eab6bb commit dbb834b

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
@@ -3272,6 +3272,127 @@ static void TestChannelCloseCallbackReturnIgnored(void)
32723272
FreeChannelOpenHarness(&harness);
32733273
}
32743274

3275+
/* Builds a plaintext SSH_MSG_CHANNEL_REQUEST whose type-specific tail is a
3276+
* single string, which is the shape of both "exec" and "subsystem". */
3277+
static word32 BuildChannelStringRequestPacket(word32 recipientChannelId,
3278+
const char* type, byte wantReply, const char* arg,
3279+
byte* out, word32 outSz)
3280+
{
3281+
byte payload[128];
3282+
word32 idx = 0;
3283+
3284+
idx = AppendUint32(payload, sizeof(payload), idx, recipientChannelId);
3285+
idx = AppendString(payload, sizeof(payload), idx, type);
3286+
idx = AppendByte(payload, sizeof(payload), idx, wantReply);
3287+
idx = AppendString(payload, sizeof(payload), idx, arg);
3288+
3289+
return WrapPacket(MSGID_CHANNEL_REQUEST, payload, idx, out, outSz);
3290+
}
3291+
3292+
/* What a session request callback saw. */
3293+
static int sessionReqCbCalls;
3294+
static WS_SessionType sessionReqCbType;
3295+
static char sessionReqCbCommand[32];
3296+
static void* sessionReqCbCtx;
3297+
static int sessionReqCbReturn;
3298+
3299+
static int RecordingSessionReqCb(WOLFSSH_CHANNEL* channel, void* ctx)
3300+
{
3301+
const char* command;
3302+
3303+
sessionReqCbCalls++;
3304+
sessionReqCbCtx = ctx;
3305+
sessionReqCbCommand[0] = 0;
3306+
3307+
if (channel != NULL) {
3308+
sessionReqCbType = wolfSSH_ChannelGetSessionType(channel);
3309+
command = wolfSSH_ChannelGetSessionCommand(channel);
3310+
if (command != NULL) {
3311+
WSTRNCPY(sessionReqCbCommand, command,
3312+
sizeof(sessionReqCbCommand) - 1);
3313+
sessionReqCbCommand[sizeof(sessionReqCbCommand) - 1] = 0;
3314+
}
3315+
}
3316+
3317+
return sessionReqCbReturn;
3318+
}
3319+
3320+
/* Drives one session request through a fresh harness, with the callback
3321+
* returning cbReturn, and returns the message id the server answered with. */
3322+
static byte RunSessionRequest(const char* type, const char* arg, int cbReturn,
3323+
WS_SessionType expectType)
3324+
{
3325+
ChannelOpenHarness harness;
3326+
WOLFSSH_CHANNEL* channel;
3327+
byte in[128];
3328+
word32 inSz;
3329+
int cbCtx = 0;
3330+
byte replyId;
3331+
3332+
sessionReqCbCalls = 0;
3333+
sessionReqCbType = WOLFSSH_SESSION_UNKNOWN;
3334+
sessionReqCbCommand[0] = 0;
3335+
sessionReqCbCtx = NULL;
3336+
sessionReqCbReturn = cbReturn;
3337+
3338+
InitChannelOpenHarness(&harness, NULL, 0);
3339+
if (WSTRCMP(type, "exec") == 0) {
3340+
AssertIntEQ(wolfSSH_CTX_SetChannelReqExecCb(harness.ctx,
3341+
RecordingSessionReqCb), WS_SUCCESS);
3342+
}
3343+
else {
3344+
AssertIntEQ(wolfSSH_CTX_SetChannelReqSubsysCb(harness.ctx,
3345+
RecordingSessionReqCb), WS_SUCCESS);
3346+
}
3347+
AssertIntEQ(wolfSSH_SetChannelReqCtx(harness.ssh, &cbCtx), WS_SUCCESS);
3348+
3349+
channel = SeedUnconfirmedChannel(&harness);
3350+
AssertIntEQ(ChannelUpdatePeer(channel, 5, 1024, 1024), WS_SUCCESS);
3351+
channel->openConfirmed = 1;
3352+
3353+
inSz = BuildChannelStringRequestPacket(channel->channel, type, 1, arg,
3354+
in, sizeof(in));
3355+
RepointHarnessInput(&harness, in, inSz);
3356+
3357+
AssertIntEQ(DoReceive(harness.ssh), WS_SUCCESS);
3358+
AssertIntEQ(harness.io.inOff, harness.io.inSz);
3359+
AssertIntEQ(sessionReqCbCalls, 1);
3360+
AssertTrue(sessionReqCbCtx == &cbCtx);
3361+
AssertIntEQ(sessionReqCbType, expectType);
3362+
AssertIntEQ(WSTRCMP(sessionReqCbCommand, arg), 0);
3363+
3364+
replyId = ParseMsgId(harness.io.out, harness.io.outSz);
3365+
FreeChannelOpenHarness(&harness);
3366+
3367+
return replyId;
3368+
}
3369+
3370+
/* The exec callback is the only place an application can vet a remote
3371+
* command, and its return is what decides the reply on the wire.
3372+
* DoChannelRequest() tests only for nonzero, so a bare 1 rejects the same
3373+
* as a WS_ error. */
3374+
static void TestChannelReqExecCallbackRuns(void)
3375+
{
3376+
AssertIntEQ(RunSessionRequest("exec", "ls", WS_SUCCESS,
3377+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_SUCCESS);
3378+
AssertIntEQ(RunSessionRequest("exec", "ls", WS_BAD_ARGUMENT,
3379+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_FAILURE);
3380+
AssertIntEQ(RunSessionRequest("exec", "ls", 1,
3381+
WOLFSSH_SESSION_EXEC), MSGID_CHANNEL_FAILURE);
3382+
}
3383+
3384+
/* Same contract for the subsystem callback, which is how a server decides
3385+
* whether to serve SFTP on a channel. */
3386+
static void TestChannelReqSubsysCallbackRuns(void)
3387+
{
3388+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", WS_SUCCESS,
3389+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_SUCCESS);
3390+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", WS_BAD_ARGUMENT,
3391+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_FAILURE);
3392+
AssertIntEQ(RunSessionRequest("subsystem", "sftp", 1,
3393+
WOLFSSH_SESSION_SUBSYSTEM), MSGID_CHANNEL_FAILURE);
3394+
}
3395+
32753396
/* A username change after the first userauth request must end the session. */
32763397
static void TestUsernameChangeDisconnects(void)
32773398
{
@@ -12472,6 +12593,8 @@ int main(int argc, char** argv)
1247212593
TestChannelOpenFailCallbackRejects();
1247312594
TestChannelCloseCallbackRuns();
1247412595
TestChannelCloseCallbackReturnIgnored();
12596+
TestChannelReqExecCallbackRuns();
12597+
TestChannelReqSubsysCallbackRuns();
1247512598
TestSecondSessionChannelRejected();
1247612599
TestUsernameChangeDisconnects();
1247712600
TestSameUserRetryAllowed();

0 commit comments

Comments
 (0)