Skip to content
Open
86 changes: 66 additions & 20 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,7 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
ssh->highwaterMark = ctx->highwaterMark;
ssh->msgHighwaterMark = ctx->msgHighwaterMark;
ssh->maxAuthAttempts = ctx->maxAuthAttempts;
ssh->appChannels = ctx->appChannels;
ssh->highwaterCtx = (void*)ssh;
ssh->reqSuccessCtx = (void*)ssh;
ssh->fs = NULL;
Expand Down Expand Up @@ -4198,8 +4199,11 @@ void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap)
channel->channel);
}
ShrinkBuffer(&channel->extDataBuffer, 1);
if (channel->command)
/* Scrub the peer's command line, which can carry credentials. */
if (channel->command != NULL) {
WS_FORCEZERO(channel->command, channel->commandSz);
WFREE(channel->command, heap, DYNTYPE_STRING);
}
WFREE(channel, heap, DYNTYPE_CHANNEL);
}
}
Expand Down Expand Up @@ -13080,7 +13084,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
word32 typeSz;
char type[32];
byte wantReply;
int ret, rej = 0;
int ret, rej = 0, sessionReq = 0;

WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");

Expand All @@ -13097,8 +13101,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret);
return ret;
}

if (ret == WS_SUCCESS) {
else {
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
if (channel == NULL)
ret = WS_INVALID_CHANID;
Expand All @@ -13120,39 +13123,68 @@ static int DoChannelRequest(WOLFSSH* ssh,
nameSz = (word32)sizeof(name);
valueSz = (word32)sizeof(value);
ret = GetString(name, &nameSz, buf, len, &begin);
if (ret == WS_SUCCESS)
if (ret != WS_SUCCESS)
WLOG(WS_LOG_DEBUG, " name = %s", "<bad name>");
else {
ret = GetString(value, &valueSz, buf, len, &begin);

WLOG(WS_LOG_DEBUG, " %s = %s", name, value);
if (ret != WS_SUCCESS)
WLOG(WS_LOG_DEBUG, " %s = %s", name, "<bad value>");
else
WLOG(WS_LOG_DEBUG, " %s = %s", name, value);
}
}
else if (ChannelRequestIs(type, typeSz, "shell")) {
channel->sessionType = WOLFSSH_SESSION_SHELL;
if (ssh->ctx->channelReqShellCb) {
rej = ssh->ctx->channelReqShellCb(channel, ssh->channelReqCtx);
}
else {
rej = ssh->appChannels;
}
sessionReq = 1;
ssh->clientState = CLIENT_DONE;
}
else if (ChannelRequestIs(type, typeSz, "exec")) {
ret = GetStringAlloc(ssh->ctx->heap, &channel->command, NULL,
ret = GetStringAlloc(ssh->ctx->heap,
&channel->command, &channel->commandSz,
buf, len, &begin);
channel->sessionType = WOLFSSH_SESSION_EXEC;
if (ssh->ctx->channelReqExecCb) {
rej = ssh->ctx->channelReqExecCb(channel, ssh->channelReqCtx);
if (ret == WS_SUCCESS)
WLOG(WS_LOG_DEBUG, " command = %s", channel->command);
else
WLOG(WS_LOG_DEBUG, " command = %s", "<bad value>");
if (ret == WS_SUCCESS) {
channel->sessionType = WOLFSSH_SESSION_EXEC;
if (ssh->ctx->channelReqExecCb) {
rej = ssh->ctx->channelReqExecCb(channel,
ssh->channelReqCtx);
}
else {
rej = ssh->appChannels;
}
}
sessionReq = 1;
ssh->clientState = CLIENT_DONE;

WLOG(WS_LOG_DEBUG, " command = %s", channel->command);
}
else if (ChannelRequestIs(type, typeSz, "subsystem")) {
ret = GetStringAlloc(ssh->ctx->heap, &channel->command, NULL,
ret = GetStringAlloc(ssh->ctx->heap,
&channel->command, &channel->commandSz,
buf, len, &begin);
channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
if (ssh->ctx->channelReqSubsysCb) {
rej = ssh->ctx->channelReqSubsysCb(channel, ssh->channelReqCtx);
if (ret == WS_SUCCESS)
WLOG(WS_LOG_DEBUG, " subsystem = %s", channel->command);
else
WLOG(WS_LOG_DEBUG, " subsystem = %s", "<bad value>");
if (ret == WS_SUCCESS) {
channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
if (ssh->ctx->channelReqSubsysCb) {
rej = ssh->ctx->channelReqSubsysCb(channel,
ssh->channelReqCtx);
}
else {
rej = ssh->appChannels;
}
}
sessionReq = 1;
ssh->clientState = CLIENT_DONE;

WLOG(WS_LOG_DEBUG, " subsystem = %s", channel->command);
}
#ifdef WOLFSSH_TERM
else if (ChannelRequestIs(type, typeSz, "pty-req")) {
Expand Down Expand Up @@ -13287,11 +13319,25 @@ static int DoChannelRequest(WOLFSSH* ssh,
*idx = len;
}

/* Record the answer, not the ask: sessionType and command are set before
* the reject decision and stay set on a refusal, so they cannot say
* whether the session was granted. Set even without a wantReply, which
* changes only whether the peer is told.
*
* Look the channel up again rather than reusing the pointer from
* before the callback. A callback may close its own channel, and
* wolfSSH_ChannelFree() frees it, so the old pointer can be dead. */
if (sessionReq) {
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
if (channel != NULL)
channel->sessionGranted = (ret == WS_SUCCESS && !rej);
}

if (wantReply) {
int replyRet;

if (rej) {
WLOG(WS_LOG_DEBUG, "Callback rejecting channel request.");
WLOG(WS_LOG_DEBUG, "Rejecting channel request.");
}
replyRet = SendChannelSuccess(ssh, channelId,
(ret == WS_SUCCESS && !rej));
Expand Down
97 changes: 89 additions & 8 deletions src/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ const char acceptState[] = "accept state: %s";

int wolfSSH_accept(WOLFSSH* ssh)
{
byte stopState;

WLOG(WS_LOG_DEBUG, "Entering wolfSSH_accept()");

if (ssh == NULL)
Expand All @@ -643,6 +645,15 @@ int wolfSSH_accept(WOLFSSH* ssh)
return WS_INVALID_STATE_E;
}

/* In application-driven mode the state machine stops as soon as the
* user is authenticated; everything past that is the application's.
* Only stop there if the session has not already gone by: the loop
* below tests the stop state exactly, so a state it has stepped over
* would never terminate it. */
stopState = (ssh->appChannels
&& ssh->acceptState <= ACCEPT_SERVER_USERAUTH_SENT) ?
ACCEPT_SERVER_USERAUTH_SENT : ACCEPT_CLIENT_SESSION_ESTABLISHED;

/* check if data pending to be sent */
if (ssh->outputBuffer.length > 0 &&
ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) {
Expand All @@ -654,7 +665,11 @@ int wolfSSH_accept(WOLFSSH* ssh)
ssh->acceptState != ACCEPT_SERVER_USERAUTH_ACCEPT_SENT &&
ssh->acceptState != ACCEPT_SERVER_KEXINIT_SENT &&
ssh->acceptState != ACCEPT_KEYED &&
ssh->acceptState != ACCEPT_SERVER_CHANNEL_ACCEPT_SENT) {
ssh->acceptState != ACCEPT_SERVER_CHANNEL_ACCEPT_SENT &&
/* Never step over where this call is meant to stop. The
* loop below tests for that state exactly, and the SCP and
* SFTP re-entry states sort after it. */
ssh->acceptState != stopState) {
WLOG(WS_LOG_DEBUG, "Advancing accept state");
ssh->acceptState++;
}
Expand All @@ -676,7 +691,7 @@ int wolfSSH_accept(WOLFSSH* ssh)
}
}

while (ssh->acceptState != ACCEPT_CLIENT_SESSION_ESTABLISHED) {
while (ssh->acceptState != stopState) {
switch (ssh->acceptState) {

case ACCEPT_BEGIN:
Expand Down Expand Up @@ -766,6 +781,12 @@ int wolfSSH_accept(WOLFSSH* ssh)
}
ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT;
WLOG(WS_LOG_DEBUG, acceptState, "SERVER_USERAUTH_SENT");
if (stopState == ACCEPT_SERVER_USERAUTH_SENT) {
/* The application takes it from here. Tested through
* stopState so a callback that changed the flag during
* this call cannot half-apply it. */
break;
}
FALL_THROUGH;

case ACCEPT_SERVER_USERAUTH_SENT:
Expand Down Expand Up @@ -801,7 +822,9 @@ int wolfSSH_accept(WOLFSSH* ssh)
const char* cmd = wolfSSH_GetSessionCommand(ssh);
if (cmd != NULL &&
WOLFSSH_SESSION_SUBSYSTEM == wolfSSH_GetSessionType(ssh)
&& (WSTRNCMP(cmd, "sftp", 4) == 0)) {
&& ssh->channelList->commandSz ==
(word32)WSTRLEN("sftp")
&& (WSTRCMP(cmd, "sftp") == 0)) {
ssh->acceptState = ACCEPT_INIT_SFTP;
return wolfSSH_SFTP_accept(ssh);
}
Expand Down Expand Up @@ -4528,12 +4551,29 @@ WS_SessionType wolfSSH_GetSessionType(const WOLFSSH* ssh)

const char* wolfSSH_GetSessionCommand(const WOLFSSH* ssh)
{
const char* cmd = NULL;

WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetSessionCommand()");

if (ssh && ssh->channelList)
return ssh->channelList->command;
if (ssh) {
cmd = wolfSSH_ChannelGetSessionCommand(ssh->channelList);
}

return NULL;
return cmd;
}


word32 wolfSSH_GetSessionCommandSz(const WOLFSSH* ssh)
{
word32 commandSz = 0;

WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetSessionCommandSz()");

if (ssh) {
commandSz = wolfSSH_ChannelGetSessionCommandSz(ssh->channelList);
}

return commandSz;
}


Expand Down Expand Up @@ -4772,7 +4812,8 @@ WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNewRemote(WOLFSSH* ssh,
if (newChannel != NULL)
ChannelAppend(ssh, newChannel);

WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelFwdNewRemote(), newChannel = %p, ret = %d",
WLOG(WS_LOG_DEBUG,
"Leaving wolfSSH_ChannelFwdNewRemote(), newChannel = %p, ret = %d",
newChannel, ret);
return newChannel;
}
Expand Down Expand Up @@ -5686,7 +5727,7 @@ const char* wolfSSH_ChannelGetSessionCommand(const WOLFSSH_CHANNEL* channel)
{
const char* cmd = NULL;

WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetCommand()");
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetSessionCommand()");

if (channel) {
cmd = channel->command;
Expand All @@ -5696,6 +5737,20 @@ const char* wolfSSH_ChannelGetSessionCommand(const WOLFSSH_CHANNEL* channel)
}


word32 wolfSSH_ChannelGetSessionCommandSz(const WOLFSSH_CHANNEL* channel)
{
word32 commandSz = 0;

WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetSessionCommandSz()");

if (channel) {
commandSz = channel->commandSz;
}

return commandSz;
}


int wolfSSH_CTX_SetChannelOpenCb(WOLFSSH_CTX* ctx, WS_CallbackChannelOpen cb)
{
int ret = WS_SSH_CTX_NULL_E;
Expand Down Expand Up @@ -5766,6 +5821,32 @@ int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
}


int wolfSSH_CTX_SetAppChannels(WOLFSSH_CTX* ctx, byte enable)
{
int ret = WS_SSH_CTX_NULL_E;

if (ctx != NULL) {
ctx->appChannels = (enable != 0);
ret = WS_SUCCESS;
}

return ret;
}


int wolfSSH_SetAppChannels(WOLFSSH* ssh, byte enable)
{
int ret = WS_SSH_NULL_E;

if (ssh != NULL) {
ssh->appChannels = (enable != 0);
ret = WS_SUCCESS;
}

return ret;
}


int wolfSSH_SetChannelOpenCtx(WOLFSSH* ssh, void* ctx)
{
int ret = WS_SSH_NULL_E;
Expand Down
27 changes: 26 additions & 1 deletion src/wolfsftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1383,8 +1383,33 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh)
if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE)
ssh->error = WS_SUCCESS;

/* The grant is what says this session may be served, so it is asked
* for in every accept state. Below the user-auth stop the legacy
* branch would run the handshake itself, which in this mode returns
* with no channel open at all; at the stop or past it there is no
* accept() left that could have checked anything. */
if (ssh->appChannels) {
/* Application-driven mode parks accept() here for good, so the
* sftp grant it would have checked is the application's subsystem
* callback: serve only a session channel it granted sftp on. The
* request having named sftp is not enough, so this asks for the
* grant as well -- unlike wolfSSH_accept()'s divert, which reads
* only the type and command. The name matches whole, length
* and bytes: sftpx, or sftp with an embedded NUL, is some
* other subsystem. */
const WOLFSSH_CHANNEL* channel = ssh->channelList;

if (channel == NULL || !channel->sessionGranted
|| channel->sessionType != WOLFSSH_SESSION_SUBSYSTEM
|| channel->command == NULL
|| channel->commandSz != (word32)WSTRLEN("sftp")
|| WSTRCMP(channel->command, "sftp") != 0) {
Comment thread
ejohnstown marked this conversation as resolved.
WLOG(WS_LOG_SFTP, "No sftp subsystem granted on the session");
return WS_INVALID_STATE_E;
}
}
/* check accept is done, if not call wolfSSH accept */
if (ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) {
else if (ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) {
Comment thread
ejohnstown marked this conversation as resolved.
byte name[] = "sftp";

WLOG(WS_LOG_SFTP, "Trying to do SSH accept first");
Expand Down
Loading
Loading