Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
549 changes: 388 additions & 161 deletions examples/echoserver/echoserver.c

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions scripts/scp.test
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,47 @@ else
exit 1
fi

# With -A the echoserver binds the scp command itself and runs the transfer
# through wolfSSH_SCP_accept(); the cases above take the accept() re-entry
# instead. -N reaches that call's want-read/want-write retry path, which a
# blocking server completes in one call. The client stays blocking, which the
# server does not care about.
echo "Test basic copy from server to local, app-driven server"
./examples/echoserver/echoserver -A -N -1 -R $ready_file &
server_pid=$!
create_port
$run_client ./examples/scpclient/wolfscp -u jill -P upthehill -p $port -S $PWD/scripts/scp.test:$PWD/scp.test
RESULT=$?
remove_ready_file
stop_server
check_timeout $RESULT "basic copy from server to local, app-driven server"

if test -e $PWD/scp.test; then
rm $PWD/scp.test
else
echo -e "\n\nfailed to get file from app-driven server"
do_cleanup
exit 1
fi

echo "Test basic copy from local to server, app-driven server"
./examples/echoserver/echoserver -A -N -1 -R $ready_file &
server_pid=$!
create_port
$run_client ./examples/scpclient/wolfscp -u jill -P upthehill -p $port -L $PWD/scripts/scp.test:$PWD/scp.test
RESULT=$?
remove_ready_file
stop_server
check_timeout $RESULT "basic copy from local to server, app-driven server"

if test -e $PWD/scp.test; then
rm $PWD/scp.test
else
echo -e "\n\nfailed to send file to app-driven server"
do_cleanup
exit 1
fi

echo "Test of getting empty file"
touch $PWD/scripts/empty
./examples/echoserver/echoserver -1 -R $ready_file &
Expand Down
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
Loading
Loading