diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 9448e3f34..21242d7b2 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -1104,6 +1104,11 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) } } WCLOSESOCKET(sockFd); + +#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) + ((func_args*)args)->return_code = wolfSSH_GetExitStatus(ssh); +#endif + wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) diff --git a/apps/wolfsshd/test/error_return.sh b/apps/wolfsshd/test/error_return.sh new file mode 100755 index 000000000..0c00336e5 --- /dev/null +++ b/apps/wolfsshd/test/error_return.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# sshd local test + +PWD=`pwd` +cd ../../.. + +TEST_CLIENT="./examples/client/client" +USER=`whoami` +PRIVATE_KEY="./keys/hansel-key-ecc.der" +PUBLIC_KEY="./keys/hansel-key-ecc.pub" + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "expecting host and port as arguments" + echo "./error_return.sh 127.0.0.1 22222" + exit -1 +fi + +echo "$TEST_CLIENT -c 'bash -c \"(exit 2)\"' -u $USER -i $PRIVATE_KEY -j $PUBLIC_KEY -h \"$1\" -p \"$2\"" +$TEST_CLIENT -c 'bash -c "(exit 2)"' -u $USER -i $PRIVATE_KEY -j $PUBLIC_KEY -h "$1" -p "$2" +RESULT=$? +if [ "$RESULT" != 2 ]; then + echo "Expecting error return value of 2 for failed ls command, found $RESULT" + cd $PWD + exit 1 +fi + +cd $PWD +exit 0 + + diff --git a/apps/wolfsshd/test/run_all_sshd_tests.sh b/apps/wolfsshd/test/run_all_sshd_tests.sh index 4c082d2cd..017609c5e 100755 --- a/apps/wolfsshd/test/run_all_sshd_tests.sh +++ b/apps/wolfsshd/test/run_all_sshd_tests.sh @@ -53,6 +53,10 @@ run_test() { run_test "sshd_exec_test.sh" run_test "sshd_term_size_test.sh" +#Github actions needs resolved for these test cases +#run_test "error_return.sh" +#run_test "sshd_login_grace_test.sh" + # add aditional tests here, check on var USING_LOCAL_HOST if can make sshd # server start/restart with changes diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 896723e57..bd3ad8ed4 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -1140,6 +1140,12 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, HeapFree(GetProcessHeap(), 0, ext.lpAttributeList); } + if (wolfSSH_SetExitStatus(ssh, processState) != + WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue sending childs exit " + "status"); + } + ClosePseudoConsole(pCon); CloseHandle(processInfo.hThread); CloseHandle(wolfSSHD_GetAuthToken(conn->auth)); @@ -1308,13 +1314,9 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue opening shell"); exit(1); } - exit(0); /* exit child process and close down SSH connection */ + exit(ret); /* exit child process and close down SSH connection */ } - /* do not wait for status of child process, and signal that the child can - * be reaped to avoid zombie processes when running in the foreground */ - signal(SIGCHLD, SIG_IGN); - if (wolfSSHD_AuthReducePermissionsUser(conn->auth, pPasswd->pw_uid, pPasswd->pw_gid) != WS_SUCCESS) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user ID"); @@ -1454,6 +1456,27 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } + /* get return value of child process */ + { + int waitStatus; + + do { + rc = waitpid(childPid, &waitStatus, 0); + /* if the waitpid experinced an interupt then try again */ + } while (rc < 0 && errno == EINTR); + + if (rc < 0) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue waiting for childs exit " + "status"); + } + else { + if (wolfSSH_SetExitStatus(ssh, (word32)WEXITSTATUS(waitStatus)) != + WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue setting childs exit " + "status"); + } + } + } (void)conn; return WS_SUCCESS; } @@ -1657,7 +1680,7 @@ static void* HandleConnection(void* arg) SHELL_Subsystem(conn, ssh, pPasswd, usrConf, wolfSSH_GetSessionCommand(ssh)); } - #endif /* WOLFSH_SHELL */ + #endif /* WOLFSSH_SHELL */ /* SCP can be an exec type */ if (ret == WS_SCP_INIT) { @@ -1712,9 +1735,16 @@ static void* HandleConnection(void* arg) break; } - if (error != WS_WANT_READ && error != WS_WANT_WRITE) { + if (ret == WS_FATAL_ERROR && + (error != WS_WANT_READ && + error != WS_WANT_WRITE)) { break; } + #ifdef _WIN32 + Sleep(1); + #else + usleep(1); + #endif } if (attempt == maxAttempt) { @@ -1724,6 +1754,7 @@ static void* HandleConnection(void* arg) } } + /* check if there is a response to the shutdown */ wolfSSH_free(ssh); if (conn != NULL) { WCLOSESOCKET(conn->fd); diff --git a/examples/client/client.c b/examples/client/client.c index b1f523881..4d56575f6 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -982,15 +982,23 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("Sending the shutdown messages failed."); } ret = wolfSSH_worker(ssh, NULL); - if (ret != WS_SUCCESS) { + if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E && + ret != WS_CHANNEL_CLOSED) { err_sys("Failed to listen for close messages from the peer."); } } WCLOSESOCKET(sockFd); + +#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) + ((func_args*)args)->return_code = wolfSSH_GetExitStatus(ssh); +#endif + wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); - if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) + if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E && + ret != WS_CHANNEL_CLOSED) { err_sys("Closing client stream failed"); + } ClientFreeBuffers(pubKeyName, privKeyName); #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) diff --git a/src/internal.c b/src/internal.c index 5d708f7b3..4e69277fb 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7542,6 +7542,40 @@ static int DoChannelRequest(WOLFSSH* ssh, } } #endif /* WOLFSSH_SHELL && WOLFSSH_TERM */ +#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) + else if (WSTRNCMP(type, "exit-status", typeSz) == 0) { + ret = GetUint32(&ssh->exitStatus, buf, len, &begin); + WLOG(WS_LOG_AGENT, "Got exit status %u.", ssh->exitStatus); + } + else if (WSTRNCMP(type, "exit-signal", typeSz) == 0) { + char sig[WOLFSSH_MAX_NAMESZ]; + word32 sigSz; + byte coreDumped; + + WLOG(WS_LOG_AGENT, "Got exit signal, remote command terminated"); + + sigSz = WOLFSSH_MAX_NAMESZ; + ret = GetString(sig, &sigSz, buf, len, &begin); + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_AGENT, "SIGNAL : %s", sig); + ret = GetBoolean(&coreDumped, buf, len, &begin); + } + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_AGENT, "Core Dumped?: %d", coreDumped); + sigSz = WOLFSSH_MAX_NAMESZ; + ret = GetString(sig, &sigSz, buf, len, &begin); + } + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_AGENT, "Error Msg : %s", sig); + sigSz = WOLFSSH_MAX_NAMESZ; + + /* getting language tag */ + ret = GetString(sig, &sigSz, buf, len, &begin); + } + } +#endif } if (ret == WS_SUCCESS) diff --git a/src/ssh.c b/src/ssh.c index 4597f8a03..c0b70ab66 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -954,24 +954,47 @@ int wolfSSH_connect(WOLFSSH* ssh) int wolfSSH_shutdown(WOLFSSH* ssh) { int ret = WS_SUCCESS; + WOLFSSH_CHANNEL* channel = NULL; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_shutdown()"); if (ssh == NULL || ssh->channelList == NULL) ret = WS_BAD_ARGUMENT; - if (ret == WS_SUCCESS) - ret = SendChannelEof(ssh, ssh->channelList->peerChannel); + /* look up the channel if it still exists */ + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, ssh->channelList->peerChannel, WS_CHANNEL_ID_SELF); + } - /* continue on success and in case where queing up send packets */ - if (ret == WS_SUCCESS || - (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) - ret = SendChannelExit(ssh, ssh->channelList->peerChannel, 0); + /* if channel close was not already sent then send it */ + if (channel != NULL && !channel->closeTxd) { + if (ret == WS_SUCCESS) { + ret = SendChannelEof(ssh, ssh->channelList->peerChannel); + } - /* continue on success and in case where queing up send packets */ - if (ret == WS_SUCCESS || - (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) - ret = SendChannelClose(ssh, ssh->channelList->peerChannel); + /* continue on success and in case where queing up send packets */ + if (ret == WS_SUCCESS || + (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) { + ret = SendChannelExit(ssh, ssh->channelList->peerChannel, + #if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) + ssh->exitStatus); + #else + 0); + #endif + } + + /* continue on success and in case where queing up send packets */ + if (ret == WS_SUCCESS || + (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) + ret = SendChannelClose(ssh, ssh->channelList->peerChannel); + } + + + /* if the channel was not yet removed then read to get + * response to SendChannelClose */ + if (channel != NULL && ret == WS_SUCCESS) { + ret = wolfSSH_worker(ssh, NULL); + } if (ssh != NULL && ssh->channelList == NULL) { WLOG(WS_LOG_DEBUG, "channel list was already removed"); @@ -1365,6 +1388,34 @@ void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx) #endif +#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) +/* returns the exit status captured from the connection if any */ +int wolfSSH_GetExitStatus(WOLFSSH* ssh) +{ + if (ssh == NULL) { + WLOG(WS_LOG_DEBUG, "wolfSSH_GetExitStatus WOLFSSH struct was NULL"); + return WS_BAD_ARGUMENT; + } + return ssh->exitStatus; +} + + +/* Sets the exit status to send on shutdown + * returns WS_SUCCESS on success */ +int wolfSSH_SetExitStatus(WOLFSSH* ssh, word32 exitStatus) +{ + if (ssh == NULL) { + WLOG(WS_LOG_DEBUG, "wolfSSH_SetExitStatus WOLFSSH struct was NULL"); + return WS_BAD_ARGUMENT; + } + WLOG(WS_LOG_DEBUG, "wolfSSH_SetExitStatus sending exit status %u", + exitStatus); + ssh->exitStatus = exitStatus; + return WS_SUCCESS; +} +#endif + + /* Used to set the channel request type sent in wolfSSH connect. The default * type set is shell if this function is not called. * diff --git a/tests/api.c b/tests/api.c index c417a1c56..b7f26063a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1051,6 +1051,14 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) /* If the socket is closed on shutdown, peer is gone, this is OK. */ argsCount = WS_SUCCESS; } + +#if DEFAULT_HIGHWATER_MARK < 8000 + if (argsCount == WS_REKEYING) { + /* in cases where highwater mark is really small a re-key could happen */ + argsCount = WS_SUCCESS; + } +#endif + AssertIntEQ(argsCount, WS_SUCCESS); wolfSSH_free(ssh); diff --git a/wolfssh/internal.h b/wolfssh/internal.h index c31b229e7..fe435b33e 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -822,6 +822,9 @@ struct WOLFSSH { byte* modes; word32 modesSz; #endif +#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) + word32 exitStatus; +#endif }; @@ -953,6 +956,8 @@ WOLFSSH_LOCAL int SendChannelTerminalResize(WOLFSSH*, word32, word32, word32, WOLFSSH_LOCAL int SendChannelTerminalRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelAgentRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int); +WOLFSSH_LOCAL int SendChannelExitStatus(WOLFSSH* ssh, word32 channelId, + word32 exitStatus); WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32, const byte*, word32, const byte*, word32, byte doKeyPad); diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 10da1f1c7..0e2d49f85 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -293,6 +293,8 @@ typedef int (*WS_CallbackTerminalSize)(WOLFSSH*, word32, word32, word32, WOLFSSH_API void wolfSSH_SetTerminalResizeCb(WOLFSSH* ssh, WS_CallbackTerminalSize cb); WOLFSSH_API void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx); +WOLFSSH_API int wolfSSH_GetExitStatus(WOLFSSH* ssh); +WOLFSSH_API int wolfSSH_SetExitStatus(WOLFSSH* ssh, word32 exitStatus); enum WS_HighwaterSide {