ssh: add a channel session-granted accessor

wolfSSH_ChannelGetSessionGranted() reports whether a shell, exec or
subsystem request on a channel has been answered CHANNEL_SUCCESS, so an
application can tell a second request from the first without reaching
into WOLFSSH_CHANNEL.

- the flag is still clear for the request a session callback is
  answering, so a set flag is an earlier request's grant
- wolfsshd's session callback reads the grant through it
pull/1258/head
John Safranek 2026-09-15 19:57:27 -07:00 committed by Paul Adelsbach
parent fd4d462510
commit dc03049a13
4 changed files with 50 additions and 1 deletions

View File

@ -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";
}

View File

@ -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;

View File

@ -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();

View File

@ -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);