diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index a46a3ae2..045ac5ea 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -544,7 +544,7 @@ static int SessionRequestCb(WOLFSSH_CHANNEL* channel, void* vCtx) /* One program start per channel, as RFC 4254 section 6.5 allows. This * request's grant is recorded once the callback returns, so a flag * already set is an earlier request's. */ - if (!rej && channel->sessionGranted) { + if (!rej && wolfSSH_ChannelGetSessionGranted(channel) == 1) { rej = 1; reason = "a session is already running on the channel"; } diff --git a/src/ssh.c b/src/ssh.c index dee524dd..2afd0cab 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -5774,6 +5774,17 @@ word32 wolfSSH_ChannelGetSessionCommandSz(const WOLFSSH_CHANNEL* channel) } +/* returns 1 if a session was granted on the channel, 0 if not, and + * negative on failure */ +int wolfSSH_ChannelGetSessionGranted(const WOLFSSH_CHANNEL* channel) +{ + if (channel == NULL) { + return WS_BAD_ARGUMENT; + } + return channel->sessionGranted; +} + + int wolfSSH_CTX_SetChannelOpenCb(WOLFSSH_CTX* ctx, WS_CallbackChannelOpen cb) { int ret = WS_SSH_CTX_NULL_E; diff --git a/tests/regress.c b/tests/regress.c index 2edf881e..3c638fe8 100644 --- a/tests/regress.c +++ b/tests/regress.c @@ -5862,6 +5862,37 @@ static void TestAcceptDivertMatchesScpCommandToken(void) #endif /* WOLFSSH_SCP */ +/* The grant an application reads through the public accessor is the one the + * channel records: clear while the request that would set it is still being + * answered, set once it has been. */ +static void TestChannelGetSessionGrantedAccessor(void) +{ + ChannelOpenHarness harness; + WOLFSSH_CHANNEL* channel; + byte in[128]; + word32 inSz; + + AssertIntEQ(wolfSSH_ChannelGetSessionGranted(NULL), WS_BAD_ARGUMENT); + + InitChannelOpenHarness(&harness, NULL, 0); + channel = SeedUnconfirmedChannel(&harness); + AssertIntEQ(ChannelUpdatePeer(channel, 5, 1024, 1024), WS_SUCCESS); + channel->openConfirmed = 1; + AssertIntEQ(wolfSSH_ChannelGetSessionGranted(channel), 0); + + inSz = BuildChannelStringRequestPacket(channel->channel, "exec", 1, + "ls", in, sizeof(in)); + RepointHarnessInput(&harness, in, inSz); + + AssertIntEQ(DoReceive(harness.ssh), WS_SUCCESS); + AssertIntEQ(wolfSSH_ChannelGetSessionGranted(channel), 1); + AssertIntEQ(wolfSSH_ChannelGetSessionGranted(channel), + channel->sessionGranted); + + FreeChannelOpenHarness(&harness); +} + + /* A username change after the first userauth request must end the session. */ static void TestUsernameChangeDisconnects(void) { @@ -16006,6 +16037,7 @@ int main(int argc, char** argv) TestChannelReqExecCallbackRuns(); TestChannelReqSubsysCallbackRuns(); TestSessionReqCallbackSeesCommandSz(); + TestChannelGetSessionGrantedAccessor(); TestMalformedSessionRequestSkipsCallback(); TestSessionReqCallbackMayFreeChannel(); TestAppChannelsAcceptKeepsStopWithPendingOutput(); diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 4a5405b6..374ecc0d 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -451,6 +451,12 @@ WOLFSSH_API const char* wolfSSH_ChannelGetSessionCommand( WOLFSSH_API word32 wolfSSH_ChannelGetSessionCommandSz( const WOLFSSH_CHANNEL* channel); WOLFSSH_API int wolfSSH_ChannelIsPty(const WOLFSSH_CHANNEL* channel); +/* Returns 1 once a shell, exec or subsystem request on the channel has been + * answered CHANNEL_SUCCESS, 0 while none has been, and WS_BAD_ARGUMENT when + * channel is NULL. A session-request callback sees this still clear for the + * request it is answering, so a set flag is an earlier request's grant. */ +WOLFSSH_API int wolfSSH_ChannelGetSessionGranted( + const WOLFSSH_CHANNEL* channel); /* Channel callbacks */ typedef int (*WS_CallbackChannelOpen)(WOLFSSH_CHANNEL* channel, void* ctx);