diff --git a/.github/workflows/sshd-test.yml b/.github/workflows/sshd-test.yml index 42287a66f..828344af8 100644 --- a/.github/workflows/sshd-test.yml +++ b/.github/workflows/sshd-test.yml @@ -28,5 +28,5 @@ jobs: - name: make check run: make check - name: run wolfSSHd tests - run: sudo ./run_all_sshd_tests.sh + run: sudo ./run_all_sshd_tests.sh root working-directory: ./apps/wolfsshd/test diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index 40649231d..1221e120d 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -66,10 +66,10 @@ jobs: - name: Install zephyr SDK run: | - wget -q https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${{ matrix.config.zephyr-sdk }}/zephyr-sdk-${{ matrix.config.zephyr-sdk }}_linux-x86_64.tar.xz - tar xf zephyr-sdk-${{ matrix.config.zephyr-sdk }}_linux-x86_64.tar.xz + wget -q https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${{ matrix.config.zephyr-sdk }}/zephyr-sdk-${{ matrix.config.zephyr-sdk }}_linux-x86_64_minimal.tar.xz + tar xf zephyr-sdk-${{ matrix.config.zephyr-sdk }}_linux-x86_64_minimal.tar.xz cd zephyr-sdk-${{ matrix.config.zephyr-sdk }} - ./setup.sh -h -c + ./setup.sh -h -c -t x86_64-zephyr-elf - name: Run wolfssh tests id: wolfssh-test diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 21242d7b2..5e216cbe7 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -162,12 +162,26 @@ static void modes_clear(void) { WOLFSSH_TERMIOS term = oldTerm; - term.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK - | ECHONL | ECHOPRT | NOFLSH | TOSTOP | FLUSHO - | PENDIN | EXTPROC); + term.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE + | ECHOK | ECHONL | NOFLSH | TOSTOP); - term.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF - | IXANY | IGNBRK | INPCK | PARMRK); + /* check macros set for some BSD dependent and missing on + * QNX flags */ +#ifdef ECHOPRT + term.c_lflag &= ~(ECHOPRT); +#endif +#ifdef FLUSHO + term.c_lflag &= ~(FLUSHO); +#endif +#ifdef PENDIN + term.c_lflag &= ~(PENDIN); +#endif +#ifdef EXTPROC + term.c_lflag &= ~(EXTPROC); +#endif + + term.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON + | IXOFF | IXANY | IGNBRK | INPCK | PARMRK); #ifdef IUCLC term.c_iflag &= ~IUCLC; #endif @@ -178,8 +192,10 @@ static void modes_clear(void) term.c_oflag &= ~OLCUC; #endif - term.c_cflag &= ~(CSTOPB | PARENB | PARODD | CLOCAL | CRTSCTS); - + term.c_cflag &= ~(CSTOPB | PARENB | PARODD | CLOCAL); +#ifdef CRTSCTS + term.c_cflag &= ~(CRTSCTS); +#endif tcsetattr(STDIN_FILENO, TCSANOW, &term); } diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index bcb8c6304..4be651626 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -380,6 +380,8 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFS if (pwInfo == NULL) { /* user name not found on system */ ret = WS_FATAL_ERROR; + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] User name not found on system"); } } @@ -412,6 +414,8 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFS if (ret == WS_SUCCESS) { storedHashCpy = WSTRDUP(storedHash, NULL, DYNTYPE_STRING); if (storedHash == NULL) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error getting stored hash copy"); ret = WS_MEMORY_E; } } diff --git a/apps/wolfsshd/test/create_sshd_config.sh b/apps/wolfsshd/test/create_sshd_config.sh index 3069a2eda..19be76acd 100755 --- a/apps/wolfsshd/test/create_sshd_config.sh +++ b/apps/wolfsshd/test/create_sshd_config.sh @@ -26,11 +26,15 @@ PermitEmptyPasswords no UsePrivilegeSeparation no UseDNS no -TrustedUserCAKeys $PWD/ca-cert-ecc.pem -HostKey $PWD/server-key.pem -HostCertificate $PWD/server-cert.pem +TrustedUserCAKeys $PWD/../../../keys/ca-cert-ecc.pem +HostKey $PWD/../../../keys/server-key.pem +HostCertificate $PWD/../../../keys/server-cert.pem EOF +cd ../../../keys/ +./renewcerts.sh $1 +cd ../apps/wolfsshd/test/ + exit 0 diff --git a/apps/wolfsshd/test/run_all_sshd_tests.sh b/apps/wolfsshd/test/run_all_sshd_tests.sh index 017609c5e..746762bb7 100755 --- a/apps/wolfsshd/test/run_all_sshd_tests.sh +++ b/apps/wolfsshd/test/run_all_sshd_tests.sh @@ -2,15 +2,22 @@ echo "Running all wolfSSHd tests" -TEST_HOST=$1 -TEST_PORT=$2 +if [ -z "$1" ]; then + USER=$USER +else + USER=$1 +fi + +TEST_HOST=$2 +TEST_PORT=$3 + TOTAL=0 SKIPPED=0 # setup set -e ./create_authorized_test_file.sh -./create_sshd_config.sh +./create_sshd_config.sh $USER set +e if [ ! -z "$TEST_HOST" ] && [ ! -z "$TEST_PORT" ]; then @@ -31,7 +38,7 @@ fi run_test() { printf "$1 ... " - ./"$1" "$TEST_HOST" "$TEST_PORT" &> stdout.txt + ./"$1" "$TEST_HOST" "$TEST_PORT" "$USER" &> stdout.txt RESULT=$? TOTAL=$((TOTAL+1)) if [ "$RESULT" == 77 ]; then @@ -73,6 +80,16 @@ else SKIPPED=$((SKIPPED+1)) fi +# these tests run with X509 sshd-config loaded +if [ "$USING_LOCAL_HOST" == 1 ]; then + start_wolfsshd "sshd_config_test_x509" +fi +run_test "sshd_x509_test.sh" +if [ "$USING_LOCAL_HOST" == 1 ]; then + printf "Shutting down test wolfSSHd\n" + stop_wolfsshd +fi + printf "All tests ran, $TOTAL passed, $SKIPPED skipped\n" exit 0 diff --git a/apps/wolfsshd/test/sshd_x509_test.sh b/apps/wolfsshd/test/sshd_x509_test.sh new file mode 100755 index 000000000..dd9005f53 --- /dev/null +++ b/apps/wolfsshd/test/sshd_x509_test.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# sshd local test + +PWD=`pwd` +cd ../../.. + +if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then + echo "expecting host, port and user as arguments" + echo "./sshd_x509_text.sh 127.0.0.1 22222 user" + exit -1 +fi + +TEST_CLIENT="./examples/client/client" +PRIVATE_KEY="./keys/$3-key.der" +PUBLIC_KEY="./keys/$3-cert.der" +CA_CERT="./keys/ca-cert-ecc.der" + +set -e +echo "$TEST_CLIENT -c 'pwd' -u $3 -i $PRIVATE_KEY -J $PUBLIC_KEY -A $CA_CERT -h \"$1\" -p \"$2\"" +$TEST_CLIENT -c 'pwd' -u $3 -i "$PRIVATE_KEY" -J "$PUBLIC_KEY" -A "$CA_CERT" -h "$1" -p "$2" +set +e + +rm -f error.txt +echo "$TEST_CLIENT -c 'ls error' -u $3 -i $PRIVATE_KEY -J $PUBLIC_KEY -A $CA_CERT -h \"$1\" -p \"$2\" 2> error.txt" +$TEST_CLIENT -c 'ls error' -u $3 -i "$PRIVATE_KEY" -J "$PUBLIC_KEY" -A "$CA_CERT" -h "$1" -p "$2" 2> error.txt + +# check stderr output was caught +if [ ! -s error.txt ]; then + echo "No stderr data was found when expected!!" + cd $PWD + exit 1 +fi +rm -f error.txt + +cd $PWD +exit 0 + + diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index bd3ad8ed4..01d17f37a 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -1167,15 +1167,20 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, WS_SOCKET_T sshFd = 0; int rc; WS_SOCKET_T childFd = 0; + int stdoutPipe[2], stderrPipe[2]; pid_t childPid; #ifndef EXAMPLE_BUFFER_SZ #define EXAMPLE_BUFFER_SZ 4096 +#endif +#ifndef MAX_IDLE_COUNT + #define MAX_IDLE_COUNT 2 #endif byte shellBuffer[EXAMPLE_BUFFER_SZ]; byte channelBuffer[EXAMPLE_BUFFER_SZ]; char* forcedCmd; int windowFull = 0; + int idle = 0; forcedCmd = wolfSSHD_ConfigGetForcedCmd(usrConf); @@ -1198,6 +1203,20 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, return WS_FATAL_ERROR; } + /* create pipes for stdout and stderr */ + if (forcedCmd) { + if (pipe(stdoutPipe) != 0) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue creating stdout pipe"); + return WS_FATAL_ERROR; + } + if (pipe(stderrPipe) != 0) { + close(stdoutPipe[0]); + close(stderrPipe[1]); + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue creating stderr pipe"); + return WS_FATAL_ERROR; + } + } + ChildRunning = 1; childPid = forkpty(&childFd, NULL, NULL, NULL); if (childPid < 0) { @@ -1216,6 +1235,29 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, signal(SIGINT, SIG_DFL); signal(SIGCHLD, SIG_DFL); + if (forcedCmd) { + close(stdoutPipe[0]); + close(stderrPipe[0]); + if (dup2(stdoutPipe[1], STDOUT_FILENO) == -1) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error redirecting stdout pipe"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + exit(1); + } + + return WS_FATAL_ERROR; + } + if (dup2(stderrPipe[1], STDERR_FILENO) == -1) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error redirecting stderr pipe"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + exit(1); + } + + return WS_FATAL_ERROR; + } + } + /* set additional groups if needed */ if (wolfSSHD_AuthSetGroups(conn->auth, wolfSSH_GetUsername(ssh), pPasswd->pw_gid) != WS_SUCCESS) { @@ -1306,6 +1348,8 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, args[1] = "-c"; args[2] = forcedCmd; ret = execv(cmd, (char**)args); + close(stdoutPipe[1]); + close(stderrPipe[1]); } else { ret = execv(cmd, (char**)args); @@ -1359,8 +1403,12 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, #endif wolfSSH_SetTerminalResizeCtx(ssh, (void*)&childFd); + if (forcedCmd) { + close(stdoutPipe[1]); + close(stderrPipe[1]); + } - while (ChildRunning) { + while (idle < MAX_IDLE_COUNT) { byte tmp[2]; fd_set readFds; WS_SOCKET_T maxFd; @@ -1368,13 +1416,27 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, int cnt_w; int pending = 0; + idle++; /* increment idle count, gets reset if not idle */ + FD_ZERO(&readFds); FD_SET(sshFd, &readFds); maxFd = sshFd; - FD_SET(childFd, &readFds); - if (childFd > maxFd) - maxFd = childFd; + /* select on stdout/stderr pipes with forced commands */ + if (forcedCmd) { + FD_SET(stdoutPipe[0], &readFds); + if (stdoutPipe[0] > maxFd) + maxFd = stdoutPipe[0]; + + FD_SET(stderrPipe[0], &readFds); + if (stderrPipe[0] > maxFd) + maxFd = stderrPipe[0]; + } + else { + FD_SET(childFd, &readFds); + if (childFd > maxFd) + maxFd = childFd; + } if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) { rc = select((int)maxFd + 1, &readFds, NULL, NULL, NULL); @@ -1383,6 +1445,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } else { pending = 1; /* found some pending SSH data */ + idle = 0; } if (windowFull || pending || FD_ISSET(sshFd, &readFds)) { @@ -1398,6 +1461,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, if (cnt_r < 0) { rc = wolfSSH_get_error(ssh); if (rc == WS_CHAN_RXD) { + idle = 0; if (lastChannel == shellChannelId) { cnt_r = wolfSSH_ChannelIdRead(ssh, shellChannelId, channelBuffer, @@ -1425,6 +1489,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, shellBuffer, cnt_r); if (cnt_w == WS_WINDOW_FULL) { windowFull = 1; + idle = 0; continue; } else { @@ -1432,28 +1497,87 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } - if (FD_ISSET(childFd, &readFds)) { - cnt_r = (int)read(childFd, shellBuffer, sizeof shellBuffer); - /* This read will return 0 on EOF */ - if (cnt_r <= 0) { - int err = errno; - if (err != EAGAIN) { - break; + if (forcedCmd) { + if (FD_ISSET(stderrPipe[0], &readFds)) { + cnt_r = (int)read(stderrPipe[0], shellBuffer, + sizeof shellBuffer); + /* This read will return 0 on EOF */ + if (cnt_r <= 0) { + int err = errno; + if (err != EAGAIN && err != 0) { + break; + } + } + else { + if (cnt_r > 0) { + idle = 0; + cnt_w = wolfSSH_extended_data_send(ssh, shellBuffer, + cnt_r); + if (cnt_w == WS_WINDOW_FULL) { + windowFull = 1; + continue; + } + else if (cnt_w < 0) + break; + } } } - else { - if (cnt_r > 0) { - cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, - shellBuffer, cnt_r); - if (cnt_w == WS_WINDOW_FULL) { - windowFull = 1; - continue; + + /* handle stdout */ + if (FD_ISSET(stdoutPipe[0], &readFds)) { + cnt_r = (int)read(stdoutPipe[0], shellBuffer, + sizeof shellBuffer); + /* This read will return 0 on EOF */ + if (cnt_r <= 0) { + int err = errno; + if (err != EAGAIN && err != 0) { + break; + } + } + else { + if (cnt_r > 0) { + idle = 0; + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, cnt_r); + if (cnt_w == WS_WINDOW_FULL) { + windowFull = 1; + continue; + } + else if (cnt_w < 0) + break; } - else if (cnt_w < 0) + } + } + } + else { + if (FD_ISSET(childFd, &readFds)) { + cnt_r = (int)read(childFd, shellBuffer, sizeof shellBuffer); + /* This read will return 0 on EOF */ + if (cnt_r <= 0) { + int err = errno; + if (err != EAGAIN && err != 0) { break; + } + } + else { + if (cnt_r > 0) { + idle = 0; + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, cnt_r); + if (cnt_w == WS_WINDOW_FULL) { + windowFull = 1; + continue; + } + else if (cnt_w < 0) + break; + } } } } + + if (ChildRunning && idle) { + idle = 0; /* waiting on child process */ + } } /* get return value of child process */ @@ -1477,6 +1601,24 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } } + + /* check for any left over data in pipes then close them */ + if (forcedCmd) { + int readSz; + + readSz = (int)read(stdoutPipe[0], shellBuffer, sizeof shellBuffer); + if (readSz > 0) { + wolfSSH_ChannelIdSend(ssh, shellChannelId, shellBuffer, readSz); + } + + readSz = (int)read(stderrPipe[0], shellBuffer, sizeof shellBuffer); + if (readSz > 0) { + wolfSSH_extended_data_send(ssh, shellBuffer, readSz); + } + close(stdoutPipe[0]); + close(stderrPipe[0]); + } + (void)conn; return WS_SUCCESS; } diff --git a/examples/client/common.c b/examples/client/common.c index 671532a4d..da2da1ea9 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -542,27 +542,62 @@ int ClientSetEcho(int type) } echoInit = 1; } - if (type == 1) { - if (tcsetattr(STDIN_FILENO, TCSANOW, &originalTerm) != 0) { - printf("Couldn't restore the terminal settings.\n"); - return -1; - } - } - else { - struct termios newTerm; - memcpy(&newTerm, &originalTerm, sizeof(struct termios)); - - newTerm.c_lflag &= ~ECHO; - if (type == 2) { - newTerm.c_lflag &= ~(ICANON | ECHOE | ECHOK | ECHONL | ISIG); + if (echoInit) { + if (type == 1) { + if (tcsetattr(STDIN_FILENO, TCSANOW, &originalTerm) != 0) { + printf("Couldn't restore the terminal settings.\n"); + return -1; + } } else { - newTerm.c_lflag |= (ICANON | ECHONL); - } + struct termios newTerm; + memcpy(&newTerm, &originalTerm, sizeof(struct termios)); + + newTerm.c_lflag &= ~ECHO; + if (type == 2) { + newTerm.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE + | ECHOK | ECHONL | NOFLSH | TOSTOP); + + /* check macros set for some BSD dependent and not missing on + * QNX flags */ + #ifdef ECHOPRT + newTerm.c_lflag &= ~(ECHOPRT); + #endif + #ifdef FLUSHO + newTerm.c_lflag &= ~(FLUSHO); + #endif + #ifdef PENDIN + newTerm.c_lflag &= ~(PENDIN); + #endif + #ifdef EXTPROC + newTerm.c_lflag &= ~(EXTPROC); + #endif - if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { - printf("Couldn't turn off echo.\n"); - return -1; + newTerm.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON + | IXOFF | IXANY | IGNBRK | INPCK | PARMRK); + #ifdef IUCLC + newTerm.c_iflag &= ~IUCLC; + #endif + newTerm.c_iflag |= IGNPAR; + + newTerm.c_oflag &= ~(OPOST | ONOCR | ONLRET); + #ifdef OUCLC + newTerm.c_oflag &= ~OLCUC; + #endif + + newTerm.c_cflag &= ~(CSTOPB | PARENB | PARODD | CLOCAL); + #ifdef CRTSCTS + newTerm.c_cflag &= ~(CRTSCTS); + #endif + } + else { + newTerm.c_lflag |= (ICANON | ECHONL); + } + + if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } } } #else diff --git a/keys/renewcerts.sh b/keys/renewcerts.sh index 2aadb7144..5c630bdfe 100755 --- a/keys/renewcerts.sh +++ b/keys/renewcerts.sh @@ -1,14 +1,23 @@ touch index.txt +if [ -z "$1" ]; then + USER_NAME="fred" +else + USER_NAME=$1 + cp fred-key.der $USER_NAME-key.der + cp fred-key.pem $USER_NAME-key.pem + sed -i "s/fred/$USER_NAME/g" renewcerts.cnf +fi + # renew CA openssl req -subj '/C=US/ST=Washington/L=Seattle/O=wolfSSL/OU=Development/CN=www.wolfssl.com/emailAddress=ca@example.com' -key ca-key-ecc.pem -text -out ca-cert-ecc.pem -config renewcerts.cnf -new -nodes -x509 -extensions v3_ca -days 3650 -set_serial 6 openssl x509 -in ca-cert-ecc.pem -outform DER -out ca-cert-ecc.der -# renew fred-cert -openssl req -subj '/C=US/ST=WA/L=Seattle/O=wolfSSL Inc/OU=Development/CN=Fred/emailAddress=fred@example.com' -key fred-key.pem -out fred-cert.csr -config renewcerts.cnf -new -nodes +# renew user cert +openssl req -subj "/C=US/ST=WA/L=Seattle/O=wolfSSL Inc/OU=Development/CN=$USER_NAME/emailAddress=fred@example.com" -key $USER_NAME-key.pem -out $USER_NAME-cert.csr -config renewcerts.cnf -new -nodes -openssl x509 -req -in fred-cert.csr -days 3650 -extfile renewcerts.cnf -extensions v3_fred -CA ca-cert-ecc.pem -CAkey ca-key-ecc.pem -text -out fred-cert.pem -set_serial 7 -openssl x509 -in fred-cert.pem -outform DER -out fred-cert.der +openssl x509 -req -in $USER_NAME-cert.csr -days 3650 -extfile renewcerts.cnf -extensions v3_$USER_NAME -CA ca-cert-ecc.pem -CAkey ca-key-ecc.pem -text -out $USER_NAME-cert.pem -set_serial 7 +openssl x509 -in $USER_NAME-cert.pem -outform DER -out $USER_NAME-cert.der # renew server-cert openssl req -subj '/C=US/ST=Washington/L=Seattle/O=Eliptic/OU=ECC/CN=www.wolfssl.com/emailAddress=server@example.com' -key server-key.pem -out server-cert.csr -config renewcerts.cnf -new -nodes diff --git a/src/internal.c b/src/internal.c index 4e69277fb..1da411cc5 100644 --- a/src/internal.c +++ b/src/internal.c @@ -13428,6 +13428,115 @@ int SendChannelData(WOLFSSH* ssh, word32 channelId, } +int SendChannelExtendedData(WOLFSSH* ssh, word32 channelId, + byte* data, word32 dataSz) +{ + byte* output; + word32 idx; + int ret = WS_SUCCESS; + WOLFSSH_CHANNEL* channel = NULL; + + WLOG(WS_LOG_DEBUG, "Entering SendChannelData()"); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) { + if (ssh->isKeying) + ret = WS_REKEYING; + } + + /* if already having data pending try to flush it first and do not continue + * to que more on fail */ + if (ret == WS_SUCCESS && ssh->outputBuffer.plainSz > 0) { + WLOG(WS_LOG_DEBUG, "Flushing out want write data"); + ret = wolfSSH_SendPacket(ssh); + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret); + return ret; + } + + } + + if (ret == WS_SUCCESS) { + if (ssh->outputBuffer.length != 0) + ret = wolfSSH_SendPacket(ssh); + } + + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF); + if (channel == NULL) { + WLOG(WS_LOG_DEBUG, "Invalid channel"); + ret = WS_INVALID_CHANID; + } + } + + if (ret == WS_SUCCESS) { + if (channel->peerWindowSz == 0) { + WLOG(WS_LOG_DEBUG, "channel window is full"); + ssh->error = WS_WINDOW_FULL; + ret = WS_WINDOW_FULL; + } + } + + if (ret == WS_SUCCESS) { + word32 bound = min(channel->peerWindowSz, channel->peerMaxPacketSz); + bound = min(bound, channel->maxPacketSz); + + if (dataSz > bound) { + WLOG(WS_LOG_DEBUG, + "Trying to send %u, client will only accept %u, limiting", + dataSz, bound); + dataSz = bound; + } + + ret = PreparePacket(ssh, + MSG_ID_SZ + UINT32_SZ + UINT32_SZ + LENGTH_SZ + dataSz); + } + + if (ret == WS_SUCCESS) { + output = ssh->outputBuffer.buffer; + idx = ssh->outputBuffer.length; + + + output[idx++] = MSGID_CHANNEL_EXTENDED_DATA; + c32toa(channel->peerChannel, output + idx); + idx += UINT32_SZ; + c32toa(CHANNEL_EXTENDED_DATA_STDERR, output + idx); + idx += UINT32_SZ; + c32toa(dataSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, data, dataSz); + idx += dataSz; + + ssh->outputBuffer.length = idx; + + ret = BundlePacket(ssh); + } + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, " dataSz = %u", dataSz); + WLOG(WS_LOG_INFO, " peerWindowSz = %u", channel->peerWindowSz); + channel->peerWindowSz -= dataSz; + WLOG(WS_LOG_INFO, " update peerWindowSz = %u", channel->peerWindowSz); + } + + /* at this point the data has been loaded into WOLFSSH structure and is + * considered consumed */ + if (ret == WS_SUCCESS) + ret = wolfSSH_SendPacket(ssh); + + if (ret == WS_SUCCESS || ret == WS_WANT_WRITE) + ret = dataSz; + + if (ssh && ssh->error == WS_WANT_WRITE) + ssh->outputBuffer.plainSz = dataSz; + + WLOG(WS_LOG_DEBUG, "Leaving SendChannelExtendedData(), ret = %d", ret); + return ret; +} + + int SendChannelWindowAdjust(WOLFSSH* ssh, word32 channelId, word32 bytesToAdd) { diff --git a/src/ssh.c b/src/ssh.c index c0b70ab66..73a48231a 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1222,6 +1222,27 @@ int wolfSSH_global_request(WOLFSSH *ssh, const unsigned char* data, word32 dataS } +int wolfSSH_extended_data_send(WOLFSSH* ssh, byte* buf, word32 bufSz) +{ + int bytesTxd = 0; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_extended_data_send()"); + + if (ssh == NULL || buf == NULL || ssh->channelList == NULL) + return WS_BAD_ARGUMENT; + + if (ssh->isKeying) { + ssh->error = WS_REKEYING; + return WS_REKEYING; + } + + bytesTxd = SendChannelExtendedData(ssh, ssh->channelList->channel, buf, bufSz); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_extended_data_send(), txd = %d", bytesTxd); + return bytesTxd; +} + + /* Reads pending data from extended data buffer. Currently can be used to get * STDERR information sent across the channel. * Returns the number of bytes read on success */ diff --git a/tests/api.c b/tests/api.c index b7f26063a..5ef8010aa 100644 --- a/tests/api.c +++ b/tests/api.c @@ -953,6 +953,7 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) func_args ser; tcp_ready ready; int argsCount; + int clientFd; const char* args[10]; WOLFSSH_CTX* ctx = NULL; @@ -1061,6 +1062,10 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) AssertIntEQ(argsCount, WS_SUCCESS); + /* close client socket down */ + clientFd = wolfSSH_get_fd(ssh); + close(clientFd); + wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); #ifdef WOLFSSH_ZEPHYR diff --git a/wolfssh/internal.h b/wolfssh/internal.h index fe435b33e..c16e2c3fe 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -717,7 +717,7 @@ struct WOLFSSH { word32 defaultPeerChannelId; word32 connectChannelId; byte channelName[WOLFSSH_MAX_CHN_NAMESZ]; - byte channelNameSz; + word32 channelNameSz; word32 lastRxId; WOLFSSH_BUFFER inputBuffer; @@ -949,6 +949,7 @@ WOLFSSH_LOCAL int SendChannelEow(WOLFSSH*, word32); WOLFSSH_LOCAL int SendChannelClose(WOLFSSH*, word32); WOLFSSH_LOCAL int SendChannelExit(WOLFSSH*, word32, int); WOLFSSH_LOCAL int SendChannelData(WOLFSSH*, word32, byte*, word32); +WOLFSSH_LOCAL int SendChannelExtendedData(WOLFSSH*, word32, byte*, word32); WOLFSSH_LOCAL int SendChannelWindowAdjust(WOLFSSH*, word32, word32); WOLFSSH_LOCAL int SendChannelRequest(WOLFSSH*, byte*, word32); WOLFSSH_LOCAL int SendChannelTerminalResize(WOLFSSH*, word32, word32, word32, diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 0e2d49f85..218a44484 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -256,6 +256,7 @@ WOLFSSH_API int wolfSSH_stream_peek(WOLFSSH*, byte*, word32); WOLFSSH_API int wolfSSH_stream_read(WOLFSSH*, byte*, word32); WOLFSSH_API int wolfSSH_stream_send(WOLFSSH*, byte*, word32); WOLFSSH_API int wolfSSH_stream_exit(WOLFSSH*, int); +WOLFSSH_API int wolfSSH_extended_data_send(WOLFSSH* ssh, byte* buf, word32 bufSz); WOLFSSH_API int wolfSSH_extended_data_read(WOLFSSH*, byte*, word32); WOLFSSH_API int wolfSSH_TriggerKeyExchange(WOLFSSH*); WOLFSSH_API int wolfSSH_SendIgnore(WOLFSSH*, const byte*, word32); diff --git a/zephyr/samples/tests/sample.yaml b/zephyr/samples/tests/sample.yaml index f216d6345..4f6f359cf 100644 --- a/zephyr/samples/tests/sample.yaml +++ b/zephyr/samples/tests/sample.yaml @@ -9,7 +9,7 @@ common: - "Zephyr wolfSSH tests passed" tests: sample.lib.wolfssh_tests: - timeout: 50 + timeout: 200 platform_allow: qemu_x86 integration_platforms: - qemu_x86