mirror of https://github.com/wolfSSL/wolfssh.git
echoserver: answer session requests in callbacks
With -A the echoserver drives its own channels: accept() returns at userauth and the callbacks below start the shell, SFTP or SCP session. Off by default. The two modes are exclusive, since the callbacks answer the session requests the accept state machine otherwise answers itself. - wsShellStartCb() forks the pty, so it is registered in either mode, and claims the channel only once there is a shell behind it; a second request is refused rather than forking over the running shell - wsExecStartCb() takes an "scp " command as a transfer and any other command as a session, and is registered in either mode, since the legacy path has always started a shell for an exec request too - wsSubsysStartCb() is registered only with -A, as accept() serves sftp itself, and guards a NULL command, which a truncated request leaves behind - ssh_worker() drives the session through shellCtx.appFd, claims the channel itself when no callback did, and leaves an SFTP or SCP handoff through its cleanup so the pty master still closes - open the agent channel from the select loop, since the peer's auth-agent-req lands after accept() has returned, and read the listener from the context each pass because it appears mid-loop - resume a subsystem accept that returns a want, waiting on the socket between attempts rather than spinning - close the accepted socket again, clear fwdFd on EOF or reset, and stay in the loop on WS_REKEYING, which the read arm already handles - key ChildRunning's sig_atomic_t on WOLFSSH_SHELL, the only build with the SIGCHLD handler that writes it, so a target whose libc has no signal.h still compiles - ask for echo mode in the keyboard-interactive test, which has no account on the host for the shell callback to fork a shell forpull/1254/head
parent
399c83b1b6
commit
971f2f01e8
|
|
@ -139,6 +139,15 @@ static int quit = 0;
|
|||
wolfSSL_Mutex doneLock;
|
||||
#define MAX_PASSWD_RETRY 3
|
||||
static int passwdRetry = MAX_PASSWD_RETRY;
|
||||
/* ssh_worker() reads ChildRunning whether or not a shell is compiled in.
|
||||
* With a shell, ChildSig() writes it from a SIGCHLD handler, so it has to
|
||||
* be sig_atomic_t; without one there is no handler, and no signal.h to
|
||||
* declare that type. Zephyr's libc has neither. */
|
||||
#ifdef WOLFSSH_SHELL
|
||||
static volatile sig_atomic_t ChildRunning = 0;
|
||||
#else
|
||||
static volatile int ChildRunning = 0;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef EXAMPLE_HIGHWATER_MARK
|
||||
|
|
@ -197,7 +206,7 @@ typedef struct WS_FwdCbActionCtx {
|
|||
typedef struct {
|
||||
WOLFSSH* ssh;
|
||||
WS_SOCKET_T fd;
|
||||
word32 id;
|
||||
word32 tid;
|
||||
int echo;
|
||||
char nonBlock;
|
||||
#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ)
|
||||
|
|
@ -212,6 +221,12 @@ typedef struct {
|
|||
WS_FwdCbActionCtx fwdCbCtx;
|
||||
#endif
|
||||
WS_AppCtx shellCtx;
|
||||
#ifdef WOLFSSH_SFTP
|
||||
int doSftp;
|
||||
#endif
|
||||
#ifdef WOLFSSH_SCP
|
||||
int doScp;
|
||||
#endif
|
||||
byte channelBuffer[EXAMPLE_BUFFER_SZ];
|
||||
/* The EOF drain holds an unsent tail across worker passes,
|
||||
* so it cannot share channelBuffer with the read path. */
|
||||
|
|
@ -250,7 +265,7 @@ static int dump_stats(thread_ctx_t* ctx)
|
|||
"Statistics for Thread #%u:\r\n"
|
||||
" txCount = %u\r\n rxCount = %u\r\n"
|
||||
" seq = %u\r\n peerSeq = %u\r\n",
|
||||
ctx->id, txCount, rxCount, seq, peerSeq);
|
||||
ctx->tid, txCount, rxCount, seq, peerSeq);
|
||||
statsSz = (word32)WSTRLEN(ctx->statsBuffer);
|
||||
|
||||
fprintf(stderr, "%s", ctx->statsBuffer);
|
||||
|
|
@ -659,8 +674,9 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
|
|||
else if (action == WOLFSSH_FWD_CHANNEL_ID) {
|
||||
appCtx->channelId = port;
|
||||
}
|
||||
else
|
||||
else {
|
||||
ret = WS_FWD_INVALID_ACTION;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -668,6 +684,222 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
|
|||
#endif /* WOLFSSH_FWD */
|
||||
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
static void ChildSig(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
ChildRunning = 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
static int termios_show(int fd)
|
||||
{
|
||||
struct termios tios;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
WMEMSET((void *) &tios, 0, sizeof(tios));
|
||||
rc = tcgetattr(fd, &tios);
|
||||
printf("tcgetattr returns=%x\n", rc);
|
||||
|
||||
printf("iflag/oflag/cflag/lflag = %x/%x/%x/%x\n",
|
||||
(unsigned int)tios.c_iflag, (unsigned int)tios.c_oflag,
|
||||
(unsigned int)tios.c_cflag, (unsigned int)tios.c_lflag);
|
||||
printf("c_ispeed/c_ospeed = %x/%x\n",
|
||||
(unsigned int)tios.c_ispeed, (unsigned int)tios.c_ospeed);
|
||||
for (i = 0; i < NCCS; i++) {
|
||||
printf("c_cc[%d] = %hhx\n", i, tios.c_cc[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* WOLFSSH_SHELL */
|
||||
|
||||
|
||||
/* Registered in every build, in both modes: with no shell the echoserver
|
||||
* still has to take the channel to mark it connected, so ssh_worker() will
|
||||
* echo on it. Returns WS_SUCCESS to accept the request, 1 to reject it. */
|
||||
static int wsShellStartCb(WOLFSSH_CHANNEL* channel, void* ctx)
|
||||
{
|
||||
thread_ctx_t* threadCtx = (thread_ctx_t*)ctx;
|
||||
word32 channelId = 0;
|
||||
|
||||
if (threadCtx == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* One session per connection. Nothing stops a peer asking a second
|
||||
* time, and taking it would fork a second shell over the first and
|
||||
* lose the fd of the one already running. */
|
||||
if (threadCtx->shellCtx.state == APP_STATE_CONNECTED
|
||||
|| threadCtx->shellCtx.appFd >= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Our own id: it is what wolfSSH_worker() reports and what the read,
|
||||
* send, and find calls below take. */
|
||||
if (wolfSSH_ChannelGetId(channel, &channelId, WS_CHANNEL_ID_SELF)
|
||||
!= WS_SUCCESS) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
/* Echo mode has no shell to start, ssh_worker() echoes the channel data
|
||||
* back through the SSH stream. */
|
||||
if (!threadCtx->echo) {
|
||||
WOLFSSH* ssh;
|
||||
const char *userName;
|
||||
struct passwd *p_passwd;
|
||||
struct termios tios;
|
||||
pid_t childPid;
|
||||
int rc;
|
||||
|
||||
ssh = threadCtx->ssh;
|
||||
userName = wolfSSH_GetUsername(ssh);
|
||||
p_passwd = getpwnam((const char *)userName);
|
||||
if (p_passwd == NULL) {
|
||||
/* Not actually a user on the system. */
|
||||
#ifdef SHELL_DEBUG
|
||||
fprintf(stderr, "user %s does not exist\n", userName);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
childPid = forkpty(&threadCtx->shellCtx.appFd, NULL, NULL, NULL);
|
||||
|
||||
if (childPid < 0) {
|
||||
/* forkpty failed, so return */
|
||||
ChildRunning = 0;
|
||||
return 1;
|
||||
}
|
||||
else if (childPid == 0) {
|
||||
/* Child process */
|
||||
const char *args[] = {"-sh", NULL};
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
printf("userName is %s\n", userName);
|
||||
system("env");
|
||||
#endif
|
||||
|
||||
setenv("HOME", p_passwd->pw_dir, 1);
|
||||
setenv("LOGNAME", p_passwd->pw_name, 1);
|
||||
rc = chdir(p_passwd->pw_dir);
|
||||
if (rc != 0) {
|
||||
/* Never return: the child would run on inside the library
|
||||
* and write to the parent's socket. */
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execv("/bin/sh", (char **)args);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef SHELL_DEBUG
|
||||
printf("In childPid > 0; getpid=%d\n", (int)getpid());
|
||||
#endif
|
||||
signal(SIGCHLD, ChildSig);
|
||||
|
||||
rc = tcgetattr(threadCtx->shellCtx.appFd, &tios);
|
||||
if (rc != 0) {
|
||||
printf("tcgetattr failed: rc =%d,errno=%x\n", rc, errno);
|
||||
return 1;
|
||||
}
|
||||
rc = tcsetattr(threadCtx->shellCtx.appFd, TCSAFLUSH, &tios);
|
||||
if (rc != 0) {
|
||||
printf("tcsetattr failed: rc =%d,errno=%x\n", rc, errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
termios_show(threadCtx->shellCtx.appFd);
|
||||
#endif
|
||||
|
||||
/* set initial size of terminal based on saved size */
|
||||
#if !defined(NO_TERMIOS) && defined(WOLFSSH_TERM)
|
||||
#if defined(HAVE_SYS_IOCTL_H)
|
||||
wolfSSH_DoModes(ssh->modes, ssh->modesSz, threadCtx->shellCtx.appFd);
|
||||
{
|
||||
struct winsize s = {0};
|
||||
|
||||
s.ws_col = ssh->widthChar;
|
||||
s.ws_row = ssh->heightRows;
|
||||
s.ws_xpixel = ssh->widthPixels;
|
||||
s.ws_ypixel = ssh->heightPixels;
|
||||
|
||||
ioctl(threadCtx->shellCtx.appFd, TIOCSWINSZ, &s);
|
||||
}
|
||||
#endif /* HAVE_SYS_IOCTL_H */
|
||||
|
||||
wolfSSH_SetTerminalResizeCtx(ssh, (void*)&threadCtx->shellCtx.appFd);
|
||||
#endif /* !NO_TERMIOS && WOLFSSH_TERM */
|
||||
}
|
||||
#endif /* WOLFSSH_SHELL */
|
||||
|
||||
/* Claim the channel only once it can be served. Claiming it up front
|
||||
* would leave the worker driving a connected shell that never started. */
|
||||
threadCtx->shellCtx.channelId = channelId;
|
||||
threadCtx->shellCtx.state = APP_STATE_CONNECTED;
|
||||
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WOLFSSH_SFTP
|
||||
static int wsSubsysStartCb(WOLFSSH_CHANNEL* channel, void* vCtx)
|
||||
{
|
||||
int rej = 1;
|
||||
|
||||
if (vCtx && channel) {
|
||||
thread_ctx_t* threadCtx;
|
||||
const char* cmd;
|
||||
WS_SessionType type;
|
||||
|
||||
threadCtx = (thread_ctx_t*)vCtx;
|
||||
cmd = wolfSSH_ChannelGetSessionCommand(channel);
|
||||
type = wolfSSH_ChannelGetSessionType(channel);
|
||||
|
||||
/* A truncated subsystem string leaves the command NULL, and this
|
||||
* runs before anything else has looked at it. */
|
||||
if (type == WOLFSSH_SESSION_SUBSYSTEM && cmd != NULL
|
||||
&& WSTRCMP(cmd, "sftp") == 0) {
|
||||
threadCtx->doSftp = 1;
|
||||
rej = WS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return rej;
|
||||
}
|
||||
#endif /* WOLFSSH_SFTP */
|
||||
|
||||
|
||||
/* An "scp ..." command starts a transfer, anything else runs as a session,
|
||||
* the same as a shell request: the echoserver never runs the command. */
|
||||
static int wsExecStartCb(WOLFSSH_CHANNEL* channel, void* vCtx)
|
||||
{
|
||||
int rej = 1;
|
||||
|
||||
if (vCtx && channel) {
|
||||
const char* cmd = wolfSSH_ChannelGetSessionCommand(channel);
|
||||
|
||||
#ifdef WOLFSSH_SCP
|
||||
if (cmd != NULL && WSTRNCMP(cmd, "scp ", 4) == 0) {
|
||||
((thread_ctx_t*)vCtx)->doScp = 1;
|
||||
rej = WS_SUCCESS;
|
||||
}
|
||||
else
|
||||
#endif /* WOLFSSH_SCP */
|
||||
{
|
||||
rej = wsShellStartCb(channel, vCtx);
|
||||
}
|
||||
(void)cmd;
|
||||
}
|
||||
|
||||
return rej;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
|
||||
static void display_ascii(char *p_buf,
|
||||
|
|
@ -709,30 +941,6 @@ static void buf_dump(unsigned char *buf, int len)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
static int termios_show(int fd)
|
||||
{
|
||||
struct termios tios;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
WMEMSET((void *) &tios, 0, sizeof(tios));
|
||||
rc = tcgetattr(fd, &tios);
|
||||
printf("tcgetattr returns=%x\n", rc);
|
||||
|
||||
printf("iflag/oflag/cflag/lflag = %x/%x/%x/%x\n",
|
||||
(unsigned int)tios.c_iflag, (unsigned int)tios.c_oflag,
|
||||
(unsigned int)tios.c_cflag, (unsigned int)tios.c_lflag);
|
||||
printf("c_ispeed/c_ospeed = %x/%x\n",
|
||||
(unsigned int)tios.c_ispeed, (unsigned int)tios.c_ospeed);
|
||||
for (i = 0; i < NCCS; i++) {
|
||||
printf("c_cc[%d] = %hhx\n", i, tios.c_cc[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* WOLFSSH_SHELL */
|
||||
|
||||
#endif /* SHELL_DEBUG */
|
||||
|
||||
|
||||
|
|
@ -817,21 +1025,14 @@ static int termios_show(int fd)
|
|||
#endif
|
||||
|
||||
|
||||
int ChildRunning = 0;
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
static void ChildSig(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
ChildRunning = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ssh_worker(thread_ctx_t* threadCtx)
|
||||
{
|
||||
WOLFSSH* ssh;
|
||||
WS_SOCKET_T sshFd;
|
||||
int rc = 0;
|
||||
/* What the loop hands back, so a transfer taking over the session
|
||||
* still leaves through the cleanup below it. */
|
||||
int workerRet = 0;
|
||||
int eofAnswered = 0;
|
||||
/* Held across passes with 0 <= eofOff <= eofRead. */
|
||||
int eofRead = 0;
|
||||
|
|
@ -839,11 +1040,8 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
/* Without a shell there is no child to outlive the peer's EOF, and the
|
||||
* read path echoes unconditionally. */
|
||||
int echoOnly = 1;
|
||||
#ifdef WOLFSSH_SHELL
|
||||
const char *userName;
|
||||
struct passwd *p_passwd;
|
||||
WS_SOCKET_T childFd = 0;
|
||||
pid_t childPid;
|
||||
#ifdef WOLFSSH_AGENT
|
||||
int agentOpened = 0;
|
||||
#endif
|
||||
#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ)
|
||||
pthread_t globalReq_th;
|
||||
|
|
@ -862,6 +1060,20 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
|
||||
sshFd = wolfSSH_get_fd(ssh);
|
||||
|
||||
if (threadCtx->shellCtx.state != APP_STATE_CONNECTED) {
|
||||
/* The legacy path: wolfSSH_accept() answered the session request
|
||||
* itself, so no channel-request callback ran to claim the channel.
|
||||
* Claim it here, on the session accept() established. */
|
||||
WOLFSSH_CHANNEL* sessionChannel;
|
||||
|
||||
sessionChannel = wolfSSH_ChannelNext(ssh, NULL);
|
||||
if (sessionChannel != NULL) {
|
||||
threadCtx->shellCtx.state = APP_STATE_CONNECTED;
|
||||
wolfSSH_ChannelGetId(sessionChannel,
|
||||
&threadCtx->shellCtx.channelId, WS_CHANNEL_ID_SELF);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ)
|
||||
/* submit Global Request for keep-alive */
|
||||
rc = pthread_create(&globalReq_th, NULL, global_req, threadCtx);
|
||||
|
|
@ -869,57 +1081,10 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
printf("pthread_create() failed.\n");
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo) {
|
||||
|
||||
userName = wolfSSH_GetUsername(ssh);
|
||||
p_passwd = getpwnam((const char *)userName);
|
||||
if (p_passwd == NULL) {
|
||||
/* Not actually a user on the system. */
|
||||
#ifdef SHELL_DEBUG
|
||||
fprintf(stderr, "user %s does not exist\n", userName);
|
||||
#endif
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
ChildRunning = 1;
|
||||
childPid = forkpty(&childFd, NULL, NULL, NULL);
|
||||
|
||||
if (childPid < 0) {
|
||||
/* forkpty failed, so return */
|
||||
ChildRunning = 0;
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
else if (childPid == 0) {
|
||||
/* Child process */
|
||||
const char *args[] = {"-sh", NULL};
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
printf("userName is %s\n", userName);
|
||||
system("env");
|
||||
#endif
|
||||
|
||||
setenv("HOME", p_passwd->pw_dir, 1);
|
||||
setenv("LOGNAME", p_passwd->pw_name, 1);
|
||||
rc = chdir(p_passwd->pw_dir);
|
||||
if (rc != 0) {
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
execv("/bin/sh", (char **)args);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
{
|
||||
/* Parent process */
|
||||
#ifdef WOLFSSH_SHELL
|
||||
struct termios tios;
|
||||
#endif
|
||||
#ifdef WOLFSSH_AGENT
|
||||
WS_SOCKET_T agentFd = -1;
|
||||
WS_SOCKET_T agentListenFd = threadCtx->agentCtx.listenFd;
|
||||
word32 agentChannelId = -1;
|
||||
#endif
|
||||
#ifdef WOLFSSH_FWD
|
||||
|
|
@ -927,52 +1092,7 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
word32 fwdBufferIdx = 0;
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo) {
|
||||
#ifdef SHELL_DEBUG
|
||||
printf("In childPid > 0; getpid=%d\n", (int)getpid());
|
||||
#endif
|
||||
signal(SIGCHLD, ChildSig);
|
||||
|
||||
rc = tcgetattr(childFd, &tios);
|
||||
if (rc != 0) {
|
||||
printf("tcgetattr failed: rc =%d,errno=%x\n", rc, errno);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
rc = tcsetattr(childFd, TCSAFLUSH, &tios);
|
||||
if (rc != 0) {
|
||||
printf("tcsetattr failed: rc =%d,errno=%x\n", rc, errno);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
#ifdef SHELL_DEBUG
|
||||
termios_show(childFd);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
ChildRunning = 1;
|
||||
#else
|
||||
ChildRunning = 1;
|
||||
#endif
|
||||
|
||||
#if !defined(NO_TERMIOS) && defined(WOLFSSH_TERM) && defined(WOLFSSH_SHELL)
|
||||
#if defined(HAVE_SYS_IOCTL_H)
|
||||
/* if not echoing, set initial size of terminal based on saved size */
|
||||
if (!threadCtx->echo) {
|
||||
struct winsize s = {0,0,0,0};
|
||||
|
||||
wolfSSH_DoModes(ssh->modes, ssh->modesSz, childFd);
|
||||
s.ws_col = ssh->widthChar;
|
||||
s.ws_row = ssh->heightRows;
|
||||
s.ws_xpixel = ssh->widthPixels;
|
||||
s.ws_ypixel = ssh->heightPixels;
|
||||
|
||||
ioctl(childFd, TIOCSWINSZ, &s);
|
||||
|
||||
wolfSSH_SetTerminalResizeCtx(ssh, (void*)&childFd);
|
||||
}
|
||||
#endif /* HAVE_SYS_IOCTL_H */
|
||||
#endif /* !NO_TERMIOS && WOLFSSH_TERM && WOLFSSH_SHELL */
|
||||
|
||||
while (ChildRunning) {
|
||||
fd_set readFds;
|
||||
|
|
@ -984,18 +1104,33 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
FD_SET(sshFd, &readFds);
|
||||
maxFd = sshFd;
|
||||
|
||||
#ifdef WOLFSSH_AGENT
|
||||
/* The peer's auth-agent-req lands after wolfSSH_accept() has
|
||||
* already returned in application-driven mode, so the channel
|
||||
* answering it is opened here rather than inside accept(). The
|
||||
* call reports WS_BAD_ARGUMENT until the request arrives. */
|
||||
if (!agentOpened
|
||||
&& wolfSSH_AGENT_ChannelOpen(ssh) == WS_SUCCESS) {
|
||||
agentOpened = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo) {
|
||||
FD_SET(childFd, &readFds);
|
||||
if (childFd > maxFd)
|
||||
maxFd = childFd;
|
||||
if (threadCtx->shellCtx.state == APP_STATE_CONNECTED
|
||||
&& threadCtx->shellCtx.appFd >= 0) {
|
||||
FD_SET(threadCtx->shellCtx.appFd, &readFds);
|
||||
if (threadCtx->shellCtx.appFd > maxFd)
|
||||
maxFd = threadCtx->shellCtx.appFd;
|
||||
}
|
||||
#endif /* WOLFSSH_SHELL */
|
||||
#ifdef WOLFSSH_AGENT
|
||||
if (threadCtx->agentCtx.state == APP_STATE_LISTEN) {
|
||||
FD_SET(agentListenFd, &readFds);
|
||||
if (agentListenFd > maxFd)
|
||||
maxFd = agentListenFd;
|
||||
/* The poll above creates this listener mid-loop; re-read it
|
||||
* each pass rather than caching it. */
|
||||
if (threadCtx->agentCtx.state == APP_STATE_LISTEN
|
||||
&& threadCtx->agentCtx.listenFd >= 0) {
|
||||
FD_SET(threadCtx->agentCtx.listenFd, &readFds);
|
||||
if (threadCtx->agentCtx.listenFd > maxFd)
|
||||
maxFd = threadCtx->agentCtx.listenFd;
|
||||
}
|
||||
if (agentFd >= 0
|
||||
&& threadCtx->agentCtx.state == APP_STATE_CONNECTED) {
|
||||
|
|
@ -1020,6 +1155,7 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
maxFd = fwdFd;
|
||||
}
|
||||
#endif /* WOLFSSH_FWD */
|
||||
|
||||
rc = select((int)maxFd + 1, &readFds, NULL, NULL, NULL);
|
||||
if (rc == -1) {
|
||||
break;
|
||||
|
|
@ -1035,6 +1171,18 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
channel. The additional channel is only used with the
|
||||
agent. */
|
||||
cnt_r = wolfSSH_worker(ssh, &lastChannel);
|
||||
#ifdef WOLFSSH_SFTP
|
||||
if (threadCtx->doSftp) {
|
||||
workerRet = WS_SFTP_COMPLETE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFSSH_SCP
|
||||
if (threadCtx->doScp) {
|
||||
workerRet = WS_SCP_INIT;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* The channel reads below overwrite cnt_r with a byte
|
||||
* count, so keep the worker's status. */
|
||||
rc = cnt_r;
|
||||
|
|
@ -1110,7 +1258,8 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
* wolfSSH_ChannelIdRead() has no isKeying gate; the window
|
||||
* credit it owes is parked until the rekey finishes. */
|
||||
if (rc == WS_CHAN_RXD || rc == WS_REKEYING) {
|
||||
if (lastChannel == threadCtx->shellCtx.channelId) {
|
||||
if (threadCtx->shellCtx.state == APP_STATE_CONNECTED &&
|
||||
lastChannel == threadCtx->shellCtx.channelId) {
|
||||
cnt_r = wolfSSH_ChannelIdRead(ssh,
|
||||
threadCtx->shellCtx.channelId,
|
||||
threadCtx->channelBuffer,
|
||||
|
|
@ -1127,7 +1276,8 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
#endif
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo) {
|
||||
cnt_w = (int)write(childFd,
|
||||
cnt_w = (int)write(
|
||||
threadCtx->shellCtx.appFd,
|
||||
threadCtx->channelBuffer, cnt_r);
|
||||
}
|
||||
else {
|
||||
|
|
@ -1254,11 +1404,11 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo) {
|
||||
if (FD_ISSET(childFd, &readFds)) {
|
||||
cnt_r = (int)read(childFd,
|
||||
if (threadCtx->shellCtx.state == APP_STATE_CONNECTED
|
||||
&& threadCtx->shellCtx.appFd >= 0) {
|
||||
if (FD_ISSET(threadCtx->shellCtx.appFd, &readFds)) {
|
||||
cnt_r = (int)read(threadCtx->shellCtx.appFd,
|
||||
threadCtx->shellCtx.buffer,
|
||||
sizeof threadCtx->shellCtx.buffer);
|
||||
/* This read will return 0 on EOF */
|
||||
|
|
@ -1337,12 +1487,13 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (threadCtx->agentCtx.state == APP_STATE_LISTEN) {
|
||||
if (FD_ISSET(agentListenFd, &readFds)) {
|
||||
if (threadCtx->agentCtx.state == APP_STATE_LISTEN
|
||||
&& threadCtx->agentCtx.listenFd >= 0) {
|
||||
if (FD_ISSET(threadCtx->agentCtx.listenFd, &readFds)) {
|
||||
#ifdef SHELL_DEBUG
|
||||
printf("accepting agent connection\n");
|
||||
#endif
|
||||
agentFd = accept(agentListenFd, NULL, NULL);
|
||||
agentFd = accept(threadCtx->agentCtx.listenFd, NULL, NULL);
|
||||
if (agentFd == -1) {
|
||||
rc = errno;
|
||||
if (rc != SOCKET_EWOULDBLOCK) {
|
||||
|
|
@ -1373,8 +1524,7 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
fwdFd = -1;
|
||||
threadCtx->fwdCtx.appFd = -1;
|
||||
if (threadCtx->fwdCbCtx.hostName != NULL) {
|
||||
WFREE(threadCtx->fwdCbCtx.hostName,
|
||||
NULL, 0);
|
||||
WFREE(threadCtx->fwdCbCtx.hostName, NULL, 0);
|
||||
threadCtx->fwdCbCtx.hostName = NULL;
|
||||
}
|
||||
threadCtx->fwdCtx.state = APP_STATE_LISTEN;
|
||||
|
|
@ -1499,8 +1649,10 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
#endif /* WOLFSSH_FWD */
|
||||
}
|
||||
#ifdef WOLFSSH_SHELL
|
||||
if (!threadCtx->echo)
|
||||
WCLOSESOCKET(childFd);
|
||||
if (threadCtx->shellCtx.appFd >= 0) {
|
||||
WCLOSESOCKET(threadCtx->shellCtx.appFd);
|
||||
threadCtx->shellCtx.appFd = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -1508,10 +1660,13 @@ static int ssh_worker(thread_ctx_t* threadCtx)
|
|||
pthread_join(globalReq_th, NULL);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return workerRet;
|
||||
}
|
||||
|
||||
|
||||
/* Seconds to wait on the socket between subsystem-accept attempts. */
|
||||
#define ES_ACCEPT_TIMEOUT 1
|
||||
|
||||
#ifdef WOLFSSH_SFTP
|
||||
|
||||
#define TEST_SFTP_TIMEOUT_SHORT 0
|
||||
|
|
@ -1762,8 +1917,10 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
|
|||
else {
|
||||
ret = NonBlockSSH_accept(threadCtx->ssh);
|
||||
}
|
||||
|
||||
#ifdef WOLFSSH_SCP
|
||||
/* finish off SCP operation */
|
||||
/* The legacy path: accept() reports the scp command and does the
|
||||
* transfer on re-entry. */
|
||||
if (ret == WS_SCP_INIT) {
|
||||
if (!threadCtx->nonBlock)
|
||||
ret = wolfSSH_accept(threadCtx->ssh);
|
||||
|
|
@ -1779,6 +1936,8 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
|
|||
break;
|
||||
|
||||
#ifdef WOLFSSH_SFTP
|
||||
/* The legacy path: wolfSSH_accept() ran the subsystem request
|
||||
* itself and handed back a session ready to serve. */
|
||||
case WS_SFTP_COMPLETE:
|
||||
ret = sftp_worker(threadCtx);
|
||||
break;
|
||||
|
|
@ -1786,6 +1945,48 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
|
|||
|
||||
case WS_SUCCESS:
|
||||
ret = ssh_worker(threadCtx);
|
||||
#ifdef WOLFSSH_SCP
|
||||
if (ret == WS_SCP_INIT) {
|
||||
/* On a non-blocking socket the transfer comes back part
|
||||
* done; resume it rather than tearing the session down
|
||||
* mid-file. */
|
||||
do {
|
||||
ret = wolfSSH_SCP_accept(threadCtx->ssh);
|
||||
error = wolfSSH_get_error(threadCtx->ssh);
|
||||
if (ret != WS_SCP_COMPLETE
|
||||
&& (error == WS_WANT_READ
|
||||
|| error == WS_WANT_WRITE)) {
|
||||
tcp_select(wolfSSH_get_fd(threadCtx->ssh),
|
||||
ES_ACCEPT_TIMEOUT);
|
||||
}
|
||||
} while (ret != WS_SCP_COMPLETE
|
||||
&& (error == WS_WANT_READ || error == WS_WANT_WRITE));
|
||||
if (ret == WS_SCP_COMPLETE) {
|
||||
printf("scp file transfer completed\n");
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFSSH_SFTP
|
||||
if (ret == WS_SFTP_COMPLETE) {
|
||||
do {
|
||||
ret = wolfSSH_SFTP_accept(threadCtx->ssh);
|
||||
error = wolfSSH_get_error(threadCtx->ssh);
|
||||
/* Wait on the socket between attempts; without this the
|
||||
* gap before the client's SFTP INIT is a busy spin. */
|
||||
if (ret != WS_SFTP_COMPLETE
|
||||
&& (error == WS_WANT_READ
|
||||
|| error == WS_WANT_WRITE)) {
|
||||
tcp_select(wolfSSH_get_fd(threadCtx->ssh),
|
||||
ES_ACCEPT_TIMEOUT);
|
||||
}
|
||||
} while (ret != WS_SFTP_COMPLETE
|
||||
&& (error == WS_WANT_READ || error == WS_WANT_WRITE));
|
||||
}
|
||||
if (ret == WS_SFTP_COMPLETE) {
|
||||
ret = sftp_worker(threadCtx);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -3145,6 +3346,7 @@ static void ShowUsage(void)
|
|||
#ifdef WOLFSSH_SHELL
|
||||
printf(" -f echo input\n");
|
||||
#endif
|
||||
printf(" -A drive channels from the application callbacks\n");
|
||||
printf(" -p <num> port to connect on, default %d\n", wolfSshPort);
|
||||
printf(" -N use non-blocking sockets\n");
|
||||
#ifdef WOLFSSH_SFTP
|
||||
|
|
@ -3198,7 +3400,7 @@ static void ShowUsage(void)
|
|||
}
|
||||
|
||||
|
||||
#define ECHOSERVER_OPTLIST "?1a:d:DefEp:R:Ni:j:i:I:J:K:P:k:b:x:m:c:s:G:HW:"
|
||||
#define ECHOSERVER_OPTLIST "?1a:Ad:DefEp:R:Ni:j:i:I:J:K:P:k:b:x:m:c:s:G:HW:"
|
||||
|
||||
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
||||
/* Detects whether argv or the environment requests a host key from the
|
||||
|
|
@ -3329,6 +3531,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
int userEcc = 0;
|
||||
int peerEcc = 0;
|
||||
int echo = 0;
|
||||
int appChannels = 0;
|
||||
int ch;
|
||||
word16 port = wolfSshPort;
|
||||
char* readyFile = NULL;
|
||||
|
|
@ -3390,6 +3593,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
#endif
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
appChannels = 1;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (myoptarg == NULL) {
|
||||
ES_ERROR("NULL port value\n");
|
||||
|
|
@ -3631,6 +3838,25 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
#ifdef WOLFSSH_FWD
|
||||
wolfSSH_CTX_SetFwdCb(ctx, wolfSSH_FwdDefaultActions, NULL);
|
||||
#endif
|
||||
/* With -A the echoserver drives its own channels: accept() stops at
|
||||
* userauth and these callbacks start the shell, subsystem or transfer.
|
||||
* Off by default, so the path this example has always taken keeps an
|
||||
* in-tree demo. The two are exclusive: the callbacks answer the session
|
||||
* requests the accept state machine would otherwise answer itself. */
|
||||
/* The shell callback is the only place the pty is forked, and an exec
|
||||
* request that is not a transfer runs as a session, so both are
|
||||
* registered in both modes. accept() honours a registered callback with
|
||||
* application-driven channels off, so the legacy path keeps the shell it
|
||||
* has always started for either request. The subsystem callback is not
|
||||
* registered there: accept() serves sftp itself. */
|
||||
wolfSSH_CTX_SetChannelReqShellCb(ctx, wsShellStartCb);
|
||||
wolfSSH_CTX_SetChannelReqExecCb(ctx, wsExecStartCb);
|
||||
if (appChannels) {
|
||||
wolfSSH_CTX_SetAppChannels(ctx, 1);
|
||||
#ifdef WOLFSSH_SFTP
|
||||
wolfSSH_CTX_SetChannelReqSubsysCb(ctx, wsSubsysStartCb);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NO_FILESYSTEM
|
||||
if (sshPubKeyList) {
|
||||
|
|
@ -4050,6 +4276,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
#endif
|
||||
wolfSSH_SetUserAuthCtx(ssh, &pwMapList);
|
||||
wolfSSH_SetKeyingCompletionCbCtx(ssh, (void*)ssh);
|
||||
wolfSSH_SetChannelReqCtx(ssh, (void*)threadCtx);
|
||||
|
||||
/* Use the session object for its own highwater callback ctx */
|
||||
if (defaultHighwater > 0) {
|
||||
|
|
@ -4109,13 +4336,13 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
tcp_set_nonblocking(&clientFd);
|
||||
|
||||
wolfSSH_set_fd(ssh, (int)clientFd);
|
||||
threadCtx->fd = clientFd;
|
||||
|
||||
#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ)
|
||||
threadCtx->ctx = ctx;
|
||||
#endif
|
||||
threadCtx->ssh = ssh;
|
||||
threadCtx->fd = clientFd;
|
||||
threadCtx->id = threadCount++;
|
||||
threadCtx->tid = threadCount++;
|
||||
threadCtx->nonBlock = nonBlock;
|
||||
threadCtx->echo = echo;
|
||||
threadCtx->shellCtx.privateData = NULL;
|
||||
|
|
|
|||
|
|
@ -8008,6 +8008,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";
|
||||
|
|
|
|||
Loading…
Reference in New Issue