Skip to content

Commit 561c9ab

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 56ff222 commit 561c9ab

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
@@ -3255,6 +3255,127 @@ static void TestChannelCloseCallbackReturnIgnored(void)
32553255
FreeChannelOpenHarness(&harness);
32563256
}
32573257

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

0 commit comments

Comments
 (0)