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
509 changes: 356 additions & 153 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
24 changes: 22 additions & 2 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 @@ -13080,7 +13081,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 Down Expand Up @@ -13130,6 +13131,10 @@ static int DoChannelRequest(WOLFSSH* ssh,
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")) {
Expand All @@ -13139,6 +13144,10 @@ static int DoChannelRequest(WOLFSSH* ssh,
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);
Expand All @@ -13150,6 +13159,10 @@ static int DoChannelRequest(WOLFSSH* ssh,
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);
Expand Down Expand Up @@ -13287,11 +13300,18 @@ 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. */
if (sessionReq && 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
54 changes: 51 additions & 3 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 @@ -4772,7 +4793,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 @@ -5766,6 +5788,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
21 changes: 20 additions & 1 deletion src/wolfsftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1383,8 +1383,27 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh)
if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE)
ssh->error = WS_SUCCESS;

if (ssh->appChannels
&& ssh->acceptState >= ACCEPT_SERVER_USERAUTH_SENT
&& ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) {
/* 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. */
const WOLFSSH_CHANNEL* channel = ssh->channelList;

if (channel == NULL || !channel->sessionGranted
|| channel->sessionType != WOLFSSH_SESSION_SUBSYSTEM
|| channel->command == NULL
|| WSTRNCMP(channel->command, "sftp", 4) != 0) {
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) {
byte name[] = "sftp";

WLOG(WS_LOG_SFTP, "Trying to do SSH accept first");
Expand Down
5 changes: 5 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -2611,6 +2611,8 @@ static void test_wolfSSH_SCP_CB(void)
AssertIntEQ(wolfSSH_SetScpErrorMsg(NULL, err), WS_BAD_ARGUMENT);
AssertIntEQ(wolfSSH_SetScpErrorMsg(ssh, NULL), WS_BAD_ARGUMENT);

AssertIntEQ(wolfSSH_SCP_accept(NULL), WS_BAD_ARGUMENT);

wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
}
Expand Down Expand Up @@ -7910,6 +7912,9 @@ static void test_wolfSSH_KeyboardInteractive(void)
argsCount = 0;
args[argsCount++] = ".";
args[argsCount++] = "-1";
/* Echo mode: "test" is not an account on the host, so the echoserver's
* shell callback would refuse the shell request this client sends. */
args[argsCount++] = "-f";
args[argsCount++] = "-i";
args[argsCount++] = "test:test";
args[argsCount++] = "-p";
Expand Down
Loading
Loading