From 7965c97ceb5b0cf9ed5c237b7ab08c9685844e10 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 5 Jun 2023 15:29:51 -0700 Subject: [PATCH 01/16] wolfSSH Client 1. Add scaffold to the build for the wolfSSH client. 2. A little whitespace change in configure.ac. --- .gitignore | 1 + Makefile.am | 2 +- apps/include.am | 6 ++++++ apps/wolfssh/include.am | 8 ++++++++ apps/wolfssh/wolfssh.c | 30 ++++++++++++++++++++++++++++++ configure.ac | 21 ++++++++++++++++----- 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 apps/include.am create mode 100644 apps/wolfssh/include.am create mode 100644 apps/wolfssh/wolfssh.c diff --git a/.gitignore b/.gitignore index c73676e9b..46dea9e79 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ examples/sftpclient/wolfsftp examples/scpclient/wolfscp # applications +apps/wolfssh/wolfssh apps/wolfsshd/wolfsshd apps/wolfsshd/test/test_configuration diff --git a/Makefile.am b/Makefile.am index a8759d4f4..f03df800c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,7 +33,7 @@ EXTRA_DIST+= LICENSING README.md ChangeLog.md include src/include.am include wolfssh/include.am -include apps/wolfsshd/include.am +include apps/include.am include examples/include.am include tests/include.am include keys/include.am diff --git a/apps/include.am b/apps/include.am new file mode 100644 index 000000000..f832780b8 --- /dev/null +++ b/apps/include.am @@ -0,0 +1,6 @@ +# vim:ft=automake +# included from Top Level Makefile.am +# All paths should be given relative to the root + +include apps/wolfssh/include.am +include apps/wolfsshd/include.am diff --git a/apps/wolfssh/include.am b/apps/wolfssh/include.am new file mode 100644 index 000000000..8e726c469 --- /dev/null +++ b/apps/wolfssh/include.am @@ -0,0 +1,8 @@ +if BUILD_SSHCLIENT + +bin_PROGRAMS += apps/wolfssh/wolfssh +apps_wolfssh_wolfssh_SOURCES = apps/wolfssh/wolfssh.c +apps_wolfssh_wolfssh_LDADD = src/libwolfssh.la +apps_wolfssh_wolfssh_DEPENDENCIES = src/libwolfssh.la + +endif BUILD_SSHCLIENT diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c new file mode 100644 index 000000000..a3faa9376 --- /dev/null +++ b/apps/wolfssh/wolfssh.c @@ -0,0 +1,30 @@ +/* wolfssh.c + * + * Copyright (C) 2014-2023 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +int main(void) +{ + printf("hello world\n"); +} diff --git a/configure.ac b/configure.ac index 026af7c41..3d29d3aa8 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,11 @@ AC_ARG_ENABLE([sshd], [AS_HELP_STRING([--enable-sshd],[Enable SSHD support (default: disabled)])], [ENABLED_SSHD=$enableval],[ENABLED_SSHD=no]) +# SSH Client +AC_ARG_ENABLE([sshclient], + [AS_HELP_STRING([--enable-sshclient],[Enable SSH client app (default: disabled)])], + [ENABLED_SSHCLIENT=$enableval],[ENABLED_SSHCLIENT=no]) + # TCP/IP Forwarding AC_ARG_ENABLE([fwd], [AS_HELP_STRING([--enable-fwd],[Enable TCP/IP Forwarding support (default: disabled)])], @@ -227,7 +232,7 @@ AC_ARG_ENABLE([distro], AS_IF([test "x$ENABLED_DISTRO" = "xyes"], [ENABLED_ALL=yes; enable_shared=yes; enable_static=yes]) AS_IF([test "x$ENABLED_ALL" = "xyes"], - [ENABLED_KEYGEN=yes; ENABLED_SCP=yes; ENABLED_SFTP=yes; ENABLED_FWD=yes; ENABLED_SHELL=yes; ENABLED_AGENT=yes; ENABLED_SSHD=yes; ENABLED_CERTS=yes]) + [ENABLED_KEYGEN=yes; ENABLED_SCP=yes; ENABLED_SFTP=yes; ENABLED_FWD=yes; ENABLED_SHELL=yes; ENABLED_AGENT=yes; ENABLED_SSHD=yes; ENABLED_SSHCLIENT=yes; ENABLED_CERTS=yes]) AS_IF([test "x$ENABLED_SSHD" = "xyes"], [ENABLED_SHELL=yes]) @@ -246,12 +251,16 @@ AS_IF([test "x$ENABLED_PTERM" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_TERM"]) AS_IF([test "x$ENABLED_SHELL" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SHELL"]) -AS_IF([test "x$ENABLED_AGENT" = "xyes"],[AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_AGENT"]) -AS_IF([test "x$ENABLED_CERTS" = "xyes"],[AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_CERTS"]) +AS_IF([test "x$ENABLED_AGENT" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_AGENT"]) +AS_IF([test "x$ENABLED_CERTS" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_CERTS"]) AS_IF([test "x$ENABLED_SMALLSTACK" = "xyes"], - [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SMALL_STACK"]) + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SMALL_STACK"]) AS_IF([test "x$ENABLED_SSHD" = "xyes"], - [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) +AS_IF([test "x$ENABLED_SSHCLIENT" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHCLIENT"]) if test "$ENABLED_SSHD" = "yes"; then if test -n "$PAM_LIB" @@ -304,6 +313,7 @@ AM_CONDITIONAL([BUILD_TERM],[test "x$ENABLED_TERM" = "xyes"]) AM_CONDITIONAL([BUILD_SHELL],[test "x$ENABLED_SHELL" = "xyes"]) AM_CONDITIONAL([BUILD_AGENT],[test "x$ENABLED_AGENT" = "xyes"]) AM_CONDITIONAL([BUILD_SSHD],[test "x$ENABLED_SSHD" = "xyes"]) +AM_CONDITIONAL([BUILD_SSHCLIENT],[test "x$ENABLED_SSHCLIENT" = "xyes"]) AM_CONDITIONAL([BUILD_CERTS],[test "x$ENABLED_CERTS" = "xyes"]) AX_HARDEN_CC_COMPILER_FLAGS @@ -346,6 +356,7 @@ AS_ECHO([" * echoserver shell support: $ENABLED_SHELL"]) AS_ECHO([" * scp: $ENABLED_SCP"]) AS_ECHO([" * sftp: $ENABLED_SFTP"]) AS_ECHO([" * sshd: $ENABLED_SSHD"]) +AS_ECHO([" * ssh client: $ENABLED_SSHCLIENT"]) AS_ECHO([" * agent: $ENABLED_AGENT"]) AS_ECHO([" * TCP/IP Forwarding: $ENABLED_FWD"]) AS_ECHO([" * X.509 Certs: $ENABLED_CERTS"]) From 325a89b7eb906e5cedf9ce09b781dc678c6b32dd Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Mon, 23 Jan 2023 06:36:38 -0800 Subject: [PATCH 02/16] initial wolfssh app --- apps/wolfssh/wolfssh.c | 1284 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 1281 insertions(+), 3 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index a3faa9376..4181366f3 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -22,9 +22,1287 @@ #include #endif -#include +#define WOLFSSH_TEST_CLIENT -int main(void) +#ifdef WOLFSSL_USER_SETTINGS + #include +#else + #include +#endif + +#include +#include +#include +#ifdef WOLFSSH_AGENT + #include +#endif +#include +#include "examples/client/client.h" +#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) + #include +#endif + +#ifdef WOLFSSH_SHELL + #ifdef HAVE_PTY_H + #include + #endif + #ifdef HAVE_UTIL_H + #include + #endif + #ifdef HAVE_TERMIOS_H + #include + #endif + #include +#endif /* WOLFSSH_SHELL */ + +#ifdef WOLFSSH_AGENT + #include + #include + #include + #include +#endif /* WOLFSSH_AGENT */ + +#ifdef HAVE_SYS_SELECT_H + #include +#endif + +#ifdef WOLFSSH_CERTS + #include +#endif + + +#ifndef NO_WOLFSSH_CLIENT + +/* type = 2 : shell / execute command settings + * type = 0 : password + * type = 1 : restore default + * return 0 on success */ +static int SetEcho(int type) +{ +#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) + static int echoInit = 0; + static struct termios originalTerm; + + if (!echoInit) { + if (tcgetattr(STDIN_FILENO, &originalTerm) != 0) { + printf("Couldn't get the original terminal settings.\n"); + return -1; + } + 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); + } + else { + newTerm.c_lflag |= (ICANON | ECHONL); + } + + if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } +#else + static int echoInit = 0; + static DWORD originalTerm; + static CONSOLE_SCREEN_BUFFER_INFO screenOrig; + HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); + if (!echoInit) { + if (GetConsoleMode(stdinHandle, &originalTerm) == 0) { + printf("Couldn't get the original terminal settings.\n"); + return -1; + } + echoInit = 1; + } + if (type == 1) { + if (SetConsoleMode(stdinHandle, originalTerm) == 0) { + printf("Couldn't restore the terminal settings.\n"); + return -1; + } + } + else if (type == 2) { + DWORD newTerm = originalTerm; + + newTerm &= ~ENABLE_PROCESSED_INPUT; + newTerm &= ~ENABLE_PROCESSED_OUTPUT; + newTerm &= ~ENABLE_LINE_INPUT; + newTerm &= ~ENABLE_ECHO_INPUT; + newTerm &= ~(ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE); + + if (SetConsoleMode(stdinHandle, newTerm) == 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } + else { + DWORD newTerm = originalTerm; + + newTerm &= ~ENABLE_ECHO_INPUT; + + if (SetConsoleMode(stdinHandle, newTerm) == 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } +#endif + + return 0; +} + + +static void ShowUsage(void) { - printf("hello world\n"); + printf("client %s\n", LIBWOLFSSH_VERSION_STRING); + printf(" -? display this help and exit\n"); + printf(" -h host to connect to, default %s\n", wolfSshIp); + printf(" -p port to connect on, default %d\n", wolfSshPort); + printf(" -u username to authenticate as (REQUIRED)\n"); + printf(" -P password for username, prompted if omitted\n"); + printf(" -e use sample ecc key for user\n"); + printf(" -i filename for the user's private key\n"); + printf(" -j filename for the user's public key\n"); + printf(" -x exit after successful connection without doing\n" + " read/write\n"); + printf(" -N use non-blocking sockets\n"); +#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) + printf(" -c executes remote command and pipe stdin/stdout\n"); +#ifdef USE_WINDOWS_API + printf(" -R raw untranslated output\n"); +#endif +#endif +#ifdef WOLFSSH_AGENT + printf(" -a Attempt to use SSH-AGENT\n"); +#endif +#ifdef WOLFSSH_CERTS + printf(" -J filename for DER certificate to use\n"); + printf(" Certificate example : client -u orange \\\n"); + printf(" -J orange-cert.der -i orange-key.der\n"); + printf(" -A filename for DER CA certificate to verify host\n"); +#endif } + + +static byte userPassword[256]; +static byte userPublicKeyBuf[512]; +static byte* userPublicKey = userPublicKeyBuf; +static const byte* userPublicKeyType = NULL; +static const char* pubKeyName = NULL; +static const char* certName = NULL; +static const char* caCert = NULL; +static byte userPrivateKeyBuf[1191]; /* Size equal to hanselPrivateRsaSz. */ +static byte* userPrivateKey = userPrivateKeyBuf; +static const byte* userPrivateKeyType = NULL; +static word32 userPublicKeySz = 0; +static word32 userPublicKeyTypeSz = 0; +static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf); +static word32 userPrivateKeyTypeSz = 0; +static byte isPrivate = 0; + + +#ifdef WOLFSSH_CERTS +#if 0 +/* compiled in for using RSA certificates instead of ECC certificate */ +static const byte publicKeyType[] = "x509v3-ssh-rsa"; +static const byte privateKeyType[] = "ssh-rsa"; +#else +static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; +#endif +#endif + +static int wsUserAuth(byte authType, + WS_UserAuthData* authData, + void* ctx) +{ + int ret = WOLFSSH_USERAUTH_SUCCESS; + +#ifdef DEBUG_WOLFSSH + /* inspect supported types from server */ + printf("Server supports:\n"); + if (authData->type & WOLFSSH_USERAUTH_PASSWORD) { + printf(" - password\n"); + } + if (authData->type & WOLFSSH_USERAUTH_PUBLICKEY) { + printf(" - publickey\n"); + } + printf("wolfSSH requesting to use type %d\n", authType); +#endif + + /* Wait for request of public key on names known to have one */ + if ((authData->type & WOLFSSH_USERAUTH_PUBLICKEY) && + authData->username != NULL && + authData->usernameSz > 0) { + + /* in the case that the name is hansel or in the case that the user + * passed in a public key file, use public key auth */ + if ((XSTRNCMP((char*)authData->username, "hansel", + authData->usernameSz) == 0) || + pubKeyName != NULL || certName != NULL) { + + if (authType == WOLFSSH_USERAUTH_PASSWORD) { + printf("rejecting password type with %s in favor of pub key\n", + (char*)authData->username); + return WOLFSSH_USERAUTH_FAILURE; + } + } + } + + if (authType == WOLFSSH_USERAUTH_PUBLICKEY) { + WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey; + + pk->publicKeyType = userPublicKeyType; + pk->publicKeyTypeSz = userPublicKeyTypeSz; + pk->publicKey = userPublicKey; + pk->publicKeySz = userPublicKeySz; + pk->privateKey = userPrivateKey; + pk->privateKeySz = userPrivateKeySz; + + ret = WOLFSSH_USERAUTH_SUCCESS; + } + else if (authType == WOLFSSH_USERAUTH_PASSWORD) { + const char* defaultPassword = (const char*)ctx; + word32 passwordSz = 0; + + if (defaultPassword != NULL) { + passwordSz = (word32)strlen(defaultPassword); + memcpy(userPassword, defaultPassword, passwordSz); + } + else { + printf("Password: "); + fflush(stdout); + SetEcho(0); + if (fgets((char*)userPassword, sizeof(userPassword), stdin) == NULL) { + printf("Getting password failed.\n"); + ret = WOLFSSH_USERAUTH_FAILURE; + } + else { + char* c = strpbrk((char*)userPassword, "\r\n"); + if (c != NULL) + *c = '\0'; + } + passwordSz = (word32)strlen((const char*)userPassword); + SetEcho(1); + #ifdef USE_WINDOWS_API + printf("\r\n"); + #endif + } + + if (ret == WOLFSSH_USERAUTH_SUCCESS) { + authData->sf.password.password = userPassword; + authData->sf.password.passwordSz = passwordSz; + } + } + + return ret; +} + + +#if defined(WOLFSSH_AGENT) || \ + (defined(WOLFSSH_CERTS) && \ + (defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME))) +static inline void ato32(const byte* c, word32* u32) +{ + *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; +} +#endif + + +#if defined(WOLFSSH_CERTS) && \ + (defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME)) +static int ParseRFC6187(const byte* in, word32 inSz, byte** leafOut, + word32* leafOutSz) +{ + int ret = WS_SUCCESS; + word32 l = 0, m = 0; + + if (inSz < sizeof(word32)) { + printf("inSz %d too small for holding cert name\n", inSz); + return WS_BUFFER_E; + } + + /* Skip the name */ + ato32(in, &l); + m += l + sizeof(word32); + + /* Get the cert count */ + if (ret == WS_SUCCESS) { + word32 count; + + if (inSz - m < sizeof(word32)) + return WS_BUFFER_E; + + ato32(in + m, &count); + m += sizeof(word32); + if (ret == WS_SUCCESS && count == 0) + ret = WS_FATAL_ERROR; /* need at least one cert */ + } + + if (ret == WS_SUCCESS) { + word32 certSz = 0; + + if (inSz - m < sizeof(word32)) + return WS_BUFFER_E; + + ato32(in + m, &certSz); + m += sizeof(word32); + if (ret == WS_SUCCESS) { + /* store leaf cert size to present to user callback */ + *leafOutSz = certSz; + *leafOut = (byte*)in + m; + } + + if (inSz - m < certSz) + return WS_BUFFER_E; + + } + + return ret; +} +#endif /* WOLFSSH_CERTS */ + + +static int wsPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx) +{ + int ret = 0; + + #ifdef DEBUG_WOLFSSH + printf("Sample public key check callback\n" + " public key = %p\n" + " public key size = %u\n" + " ctx = %s\n", pubKey, pubKeySz, (const char*)ctx); + #else + (void)pubKey; + (void)pubKeySz; + (void)ctx; + #endif + +#ifdef WOLFSSH_CERTS +#if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) + /* try to parse the certificate and check it's IP address */ + if (pubKeySz > 0) { + DecodedCert dCert; + byte* der = NULL; + word32 derSz = 0; + + if (ParseRFC6187(pubKey, pubKeySz, &der, &derSz) == WS_SUCCESS) { + wc_InitDecodedCert(&dCert, der, derSz, NULL); + if (wc_ParseCert(&dCert, CERT_TYPE, NO_VERIFY, NULL) != 0) { + printf("public key not a cert\n"); + } + else { + int ipMatch = 0; + DNS_entry* current = dCert.altNames; + + while (current != NULL) { + if (current->type == ASN_IP_TYPE) { + printf("host cert alt. name IP : %s\n", + current->ipString); + printf("\texpecting host IP : %s\n", (char*)ctx); + if (XSTRCMP(ctx, current->ipString) == 0) { + printf("\tmatched!\n"); + ipMatch = 1; + } + } + current = current->next; + } + + if (ipMatch == 0) { + printf("IP did not match expected IP\n"); + ret = -1; + } + } + FreeDecodedCert(&dCert); + } + } +#else + printf("wolfSSL not built with OPENSSL_ALL or WOLFSSL_IP_ALT_NAME\n"); + printf("\tnot checking IP address from peer's cert\n"); +#endif +#endif + + return ret; +} + + +static int NonBlockSSH_connect(WOLFSSH* ssh) +{ + int ret; + int error; + SOCKET_T sockfd; + int select_ret = 0; + + ret = wolfSSH_connect(ssh); + error = wolfSSH_get_error(ssh); + sockfd = (SOCKET_T)wolfSSH_get_fd(ssh); + + while (ret != WS_SUCCESS && + (error == WS_WANT_READ || error == WS_WANT_WRITE)) + { + if (error == WS_WANT_READ) + printf("... client would read block\n"); + else if (error == WS_WANT_WRITE) + printf("... client would write block\n"); + + select_ret = tcp_select(sockfd, 1); + if (select_ret == WS_SELECT_RECV_READY || + select_ret == WS_SELECT_ERROR_READY) + { + ret = wolfSSH_connect(ssh); + } + else if (select_ret == WS_SELECT_TIMEOUT) + error = WS_WANT_READ; + else + error = WS_FATAL_ERROR; + } + + return ret; +} + +#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) + +typedef struct thread_args { + WOLFSSH* ssh; + wolfSSL_Mutex lock; + byte rawMode; +} thread_args; + +#ifdef _POSIX_THREADS + #define THREAD_RET void* + #define THREAD_RET_SUCCESS NULL +#elif defined(_MSC_VER) + #define THREAD_RET DWORD WINAPI + #define THREAD_RET_SUCCESS 0 +#else + #define THREAD_RET int + #define THREAD_RET_SUCCESS 0 +#endif + +static THREAD_RET readInput(void* in) +{ + byte buf[256]; + int bufSz = sizeof(buf); + thread_args* args = (thread_args*)in; + int ret = 0; + word32 sz = 0; +#ifdef USE_WINDOWS_API + HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); +#endif + + while (ret >= 0) { + WMEMSET(buf, 0, bufSz); + #ifdef USE_WINDOWS_API + /* Using A version to avoid potential 2 byte chars */ + ret = ReadConsoleA(stdinHandle, (void*)buf, bufSz - 1, (DWORD*)&sz, + NULL); + #else + ret = (int)read(STDIN_FILENO, buf, bufSz -1); + sz = (word32)ret; + #endif + if (ret <= 0) { + fprintf(stderr, "Error reading stdin\n"); + return THREAD_RET_SUCCESS; + } + /* lock SSH structure access */ + wc_LockMutex(&args->lock); + ret = wolfSSH_stream_send(args->ssh, buf, sz); + wc_UnLockMutex(&args->lock); + if (ret <= 0) { + fprintf(stderr, "Couldn't send data\n"); + return THREAD_RET_SUCCESS; + } + } +#if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) + wc_ecc_fp_free(); /* free per thread cache */ +#endif + return THREAD_RET_SUCCESS; +} + + +static THREAD_RET readPeer(void* in) +{ + byte buf[80]; + int bufSz = sizeof(buf); + thread_args* args = (thread_args*)in; + int ret = 0; + int fd = wolfSSH_get_fd(args->ssh); + word32 bytes; +#ifdef USE_WINDOWS_API + HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + fd_set readSet; + fd_set errSet; + + FD_ZERO(&readSet); + FD_ZERO(&errSet); + FD_SET(fd, &readSet); + FD_SET(fd, &errSet); + + while (ret >= 0) { + bytes = select(fd + 1, &readSet, NULL, &errSet, NULL); + wc_LockMutex(&args->lock); + while (bytes > 0 && (FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet))) { + /* there is something to read off the wire */ + WMEMSET(buf, 0, bufSz); + ret = wolfSSH_stream_read(args->ssh, buf, bufSz - 1); + if (ret == WS_EXTDATA) { /* handle extended data */ + do { + WMEMSET(buf, 0, bufSz); + ret = wolfSSH_extended_data_read(args->ssh, buf, bufSz - 1); + if (ret < 0) + err_sys("Extended data read failed."); + buf[bufSz - 1] = '\0'; + fprintf(stderr, "%s", buf); + } while (ret > 0); + } + else if (ret <= 0) { + #ifdef WOLFSSH_AGENT + if (ret == WS_FATAL_ERROR) { + ret = wolfSSH_get_error(args->ssh); + if (ret == WS_CHAN_RXD) { + byte agentBuf[512]; + int rxd, txd; + word32 channel = 0; + + wolfSSH_GetLastRxId(args->ssh, &channel); + rxd = wolfSSH_ChannelIdRead(args->ssh, channel, + agentBuf, sizeof(agentBuf)); + if (rxd > 4) { + word32 msgSz = 0; + + ato32(agentBuf, &msgSz); + if (msgSz > (word32)rxd - 4) { + rxd += wolfSSH_ChannelIdRead(args->ssh, channel, + agentBuf + rxd, + sizeof(agentBuf) - rxd); + } + + txd = rxd; + rxd = sizeof(agentBuf); + ret = wolfSSH_AGENT_Relay(args->ssh, + agentBuf, (word32*)&txd, + agentBuf, (word32*)&rxd); + if (ret == WS_SUCCESS) { + ret = wolfSSH_ChannelIdSend(args->ssh, channel, + agentBuf, rxd); + } + } + WMEMSET(agentBuf, 0, sizeof(agentBuf)); + continue; + } + } + #endif + if (ret != WS_EOF) { + err_sys("Stream read failed."); + } + } + else { + buf[bufSz - 1] = '\0'; + + #ifdef USE_WINDOWS_API + if (args->rawMode == 0) { + ret = wolfSSH_ConvertConsole(args->ssh, stdoutHandle, buf, + ret); + if (ret != WS_SUCCESS && ret != WS_WANT_READ) { + err_sys("issue with print out"); + } + if (ret == WS_WANT_READ) { + ret = 0; + } + } + else { + printf("%s", buf); + fflush(stdout); + } + #else + printf("%s", buf); + fflush(stdout); + #endif + } + if (wolfSSH_stream_peek(args->ssh, buf, bufSz) <= 0) { + bytes = 0; /* read it all */ + } + } + wc_UnLockMutex(&args->lock); + } +#if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) + wc_ecc_fp_free(); /* free per thread cache */ +#endif + + return THREAD_RET_SUCCESS; +} +#endif /* !SINGLE_THREADED && !WOLFSSL_NUCLEUS */ + + +#ifdef WOLFSSH_CERTS + +static int load_der_file(const char* filename, byte** out, word32* outSz) +{ + WFILE* file; + byte* in; + word32 inSz; + int ret; + + if (filename == NULL || out == NULL || outSz == NULL) + return -1; + + ret = WFOPEN(&file, filename, "rb"); + if (ret != 0 || file == WBADFILE) + return -1; + + if (WFSEEK(file, 0, WSEEK_END) != 0) { + WFCLOSE(file); + return -1; + } + inSz = (word32)WFTELL(file); + WREWIND(file); + + if (inSz == 0) { + WFCLOSE(file); + return -1; + } + + in = (byte*)WMALLOC(inSz, NULL, 0); + if (in == NULL) { + WFCLOSE(file); + return -1; + } + + ret = (int)WFREAD(in, 1, inSz, file); + if (ret <= 0 || (word32)ret != inSz) { + ret = -1; + WFREE(in, NULL, 0); + in = 0; + inSz = 0; + } + else + ret = 0; + + *out = in; + *outSz = inSz; + + WFCLOSE(file); + + return ret; +} + +#endif /* WOLFSSH_CERTS */ + + +#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) + +static int callbackGlobalReq(WOLFSSH *ssh, void *buf, word32 sz, int reply, void *ctx) +{ + char reqStr[] = "SampleRequest"; + + if ((WOLFSSH *)ssh != *(WOLFSSH **)ctx) + { + printf("ssh(%x) != ctx(%x)\n", (unsigned int)ssh, (unsigned int)*(WOLFSSH **)ctx); + return WS_FATAL_ERROR; + } + + if (strlen(reqStr) == sz && (strncmp((char *)buf, reqStr, sz) == 0) + && reply == 1){ + printf("Global Request\n"); + return WS_SUCCESS; + } else { + return WS_FATAL_ERROR; + } + +} +#endif + + +#ifdef WOLFSSH_AGENT +typedef struct WS_AgentCbActionCtx { + struct sockaddr_un name; + int fd; + int state; +} WS_AgentCbActionCtx; + +static const char EnvNameAuthPort[] = "SSH_AUTH_SOCK"; + +static int wolfSSH_AGENT_DefaultActions(WS_AgentCbAction action, void* vCtx) +{ + WS_AgentCbActionCtx* ctx = (WS_AgentCbActionCtx*)vCtx; + int ret = WS_AGENT_SUCCESS; + + if (action == WOLFSSH_AGENT_LOCAL_SETUP) { + const char* sockName; + struct sockaddr_un* name = &ctx->name; + size_t size; + int err; + + sockName = getenv(EnvNameAuthPort); + if (sockName == NULL) + ret = WS_AGENT_NOT_AVAILABLE; + + if (ret == WS_AGENT_SUCCESS) { + memset(name, 0, sizeof(struct sockaddr_un)); + name->sun_family = AF_LOCAL; + strncpy(name->sun_path, sockName, sizeof(name->sun_path)); + name->sun_path[sizeof(name->sun_path) - 1] = '\0'; + size = strlen(sockName) + + offsetof(struct sockaddr_un, sun_path); + + ctx->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ctx->fd == -1) { + ret = WS_AGENT_SETUP_E; + err = errno; + fprintf(stderr, "socket() = %d\n", err); + } + } + + if (ret == WS_AGENT_SUCCESS) { + ret = connect(ctx->fd, + (struct sockaddr *)name, (socklen_t)size); + if (ret < 0) { + ret = WS_AGENT_SETUP_E; + err = errno; + fprintf(stderr, "connect() = %d", err); + } + } + + if (ret == WS_AGENT_SUCCESS) + ctx->state = AGENT_STATE_CONNECTED; + } + else if (action == WOLFSSH_AGENT_LOCAL_CLEANUP) { + int err; + + err = close(ctx->fd); + if (err != 0) { + err = errno; + fprintf(stderr, "close() = %d", err); + if (ret == 0) + ret = WS_AGENT_SETUP_E; + } + } + else + ret = WS_AGENT_INVALID_ACTION; + + return ret; +} + + +static int wolfSSH_AGENT_IO_Cb(WS_AgentIoCbAction action, + void* buf, word32 bufSz, void* vCtx) +{ + WS_AgentCbActionCtx* ctx = (WS_AgentCbActionCtx*)vCtx; + int ret = WS_AGENT_INVALID_ACTION; + + if (action == WOLFSSH_AGENT_IO_WRITE) { + const byte* wBuf = (const byte*)buf; + ret = (int)write(ctx->fd, wBuf, bufSz); + if (ret < 0) { + ret = WS_CBIO_ERR_GENERAL; + } + } + else if (action == WOLFSSH_AGENT_IO_READ) { + byte* rBuf = (byte*)buf; + ret = (int)read(ctx->fd, rBuf, bufSz); + if (ret < 0) { + ret = WS_CBIO_ERR_GENERAL; + } + } + + return ret; +} + + +#endif /* WOLFSSH_AGENT */ + + +#define MAX_USERNAME 120 +#define MAX_HOSTNAME 25 + +THREAD_RETURN WOLFSSH_THREAD client_test(void* args) +{ + WOLFSSH_CTX* ctx = NULL; + WOLFSSH* ssh = NULL; + SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID; + SOCKADDR_IN_T clientAddr; + socklen_t clientAddrSz = sizeof(clientAddr); + //char rxBuf[80]; + int ret = 0; + int ch; + //int userEcc = 0; + word16 port = wolfSshPort; + char* host = (char*)wolfSshIp; + const char* username = NULL; + const char* password = NULL; + const char* cmd = NULL; + const char* privKeyName = NULL; + //byte imExit = 0; + byte nonBlock = 0; + byte keepOpen = 1; +#ifdef USE_WINDOWS_API + byte rawMode = 0; +#endif +#ifdef WOLFSSH_AGENT + byte useAgent = 0; + WS_AgentCbActionCtx agentCbCtx; +#endif + char localUsername[MAX_USERNAME]; + char localHostname[MAX_HOSTNAME]; + char localCmd[120]; + + int argc = ((func_args*)args)->argc; + char** argv = ((func_args*)args)->argv; + ((func_args*)args)->return_code = 0; + + + /* test if command is set */ + if (argc > 0 && XSTRSTR(argv[argc-1], "@") == NULL && + argv[argc-1][0] != '-') { + int cmdSz; + + cmdSz = (int)XSTRLEN(argv[argc-1]); + XMEMCPY(localCmd, argv[argc-1], cmdSz); + localCmd[cmdSz] = '\0'; + cmd = &localCmd[0]; + argc -= 1; + } + + /* test if user@host is used */ + if (argc > 0 && XSTRSTR(argv[argc-1], "@")) { + int usernameSz; + int hostnameSz; + + username = argv[argc-1]; + host = XSTRSTR(username, "@"); + + usernameSz = (int)(host - username); + XMEMCPY(localUsername, username, usernameSz); + localUsername[usernameSz] = '\0'; + + hostnameSz = (int)XSTRLEN(host); + if (hostnameSz <= 1) { + + } + + XMEMCPY(localHostname, host+1, hostnameSz-1); + localHostname[hostnameSz-1] = '\0'; + + username = &localUsername[0]; + host = &localHostname[0]; + argc -= 1; + } + + while ((ch = mygetopt(argc, argv, "?ac:eh:i:j:p:tu:xzNP:RJ:A:")) != -1) { + switch (ch) { + case 'h': + host = myoptarg; + break; + + case 'z': + #ifdef WOLFSSH_SHOW_SIZES + wolfSSH_ShowSizes(); + exit(EXIT_SUCCESS); + #endif + break; + + case 'e': + //userEcc = 1; + break; + + case 'p': + port = (word16)atoi(myoptarg); + #if !defined(NO_MAIN_DRIVER) || defined(USE_WINDOWS_API) + if (port == 0) + err_sys("port number cannot be 0"); + #endif + break; + + case 'u': + username = myoptarg; + break; + + case 'P': + password = myoptarg; + break; + + case 'i': + privKeyName = myoptarg; + break; + + case 'j': + pubKeyName = myoptarg; + break; + + #ifdef WOLFSSH_CERTS + case 'J': + certName = myoptarg; + break; + + case 'A': + caCert = myoptarg; + break; + + #endif + + case 'x': + /* exit after successful connection without read/write */ + //imExit = 1; + break; + + case 'N': + nonBlock = 1; + break; + + #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) + case 'c': + cmd = myoptarg; + break; + #ifdef USE_WINDOWS_API + case 'R': + rawMode = 1; + break; + #endif /* USE_WINDOWS_API */ + #endif + + #ifdef WOLFSSH_AGENT + case 'a': + useAgent = 1; + break; + #endif + + case '?': + ShowUsage(); + exit(EXIT_SUCCESS); + + default: + ShowUsage(); + exit(MY_EX_USAGE); + } + } + myoptind = 0; /* reset for test cases */ + + if (username == NULL) + err_sys("client requires a username parameter."); + +#ifdef WOLFSSH_NO_RSA + userEcc = 1; +#endif + +#ifdef SINGLE_THREADED + if (keepOpen) + err_sys("Threading needed for terminal session\n"); +#endif + + if ((pubKeyName == NULL && certName == NULL) && privKeyName != NULL) { + err_sys("If setting priv key, need pub key."); + } + + if (privKeyName == NULL) { +// if (userEcc) { +// #ifndef WOLFSSH_NO_ECC +// ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz, +// WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, +// &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); +// #endif +// } +// else { +// #ifndef WOLFSSH_NO_RSA +// ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz, +// WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, +// &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); +// #endif +// } +// isPrivate = 1; +// if (ret != 0) err_sys("Couldn't load private key buffer."); + } + else { + #ifndef NO_FILESYSTEM + userPrivateKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(privKeyName, + (byte**)&userPrivateKey, &userPrivateKeySz, + (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, + &isPrivate, NULL); + #else + printf("file system not compiled in!\n"); + ret = -1; + #endif + if (ret != 0) err_sys("Couldn't load private key file."); + } + +#ifdef WOLFSSH_CERTS + /* passed in certificate to use */ + if (certName) { + ret = load_der_file(certName, &userPublicKey, &userPublicKeySz); + if (ret != 0) err_sys("Couldn't load certificate file."); + + userPublicKeyType = publicKeyType; + userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType); + } + else +#endif + if (pubKeyName == NULL) { +// byte* p = userPublicKey; +// userPublicKeySz = sizeof(userPublicKeyBuf); +// +// if (userEcc) { +// #ifndef WOLFSSH_NO_ECC +// ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc, +// (word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH, +// &p, &userPublicKeySz, +// &userPublicKeyType, &userPublicKeyTypeSz, NULL); +// #endif +// } +// else { +// #ifndef WOLFSSH_NO_RSA +// ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa, +// (word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH, +// &p, &userPublicKeySz, +// &userPublicKeyType, &userPublicKeyTypeSz, NULL); +// #endif +// } +// isPrivate = 1; +// if (ret != 0) err_sys("Couldn't load public key buffer."); + } + else { + #ifndef NO_FILESYSTEM + userPublicKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(pubKeyName, + &userPublicKey, &userPublicKeySz, + (const byte**)&userPublicKeyType, &userPublicKeyTypeSz, + &isPrivate, NULL); + #else + printf("file system not compiled in!\n"); + ret = -1; + #endif + if (ret != 0) err_sys("Couldn't load public key file."); + } + + ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); + if (ctx == NULL) + err_sys("Couldn't create wolfSSH client context."); + + if (((func_args*)args)->user_auth == NULL) + wolfSSH_SetUserAuth(ctx, wsUserAuth); + else + wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth); + +#ifdef WOLFSSH_AGENT + if (useAgent) { + wolfSSH_CTX_set_agent_cb(ctx, + wolfSSH_AGENT_DefaultActions, wolfSSH_AGENT_IO_Cb); + wolfSSH_CTX_AGENT_enable(ctx, 1); + } +#endif + + ssh = wolfSSH_new(ctx); + if (ssh == NULL) + err_sys("Couldn't create wolfSSH session."); + +#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) + wolfSSH_SetGlobalReq(ctx, callbackGlobalReq); + wolfSSH_SetGlobalReqCtx(ssh, &ssh); /* dummy ctx */ +#endif + + if (password != NULL) + wolfSSH_SetUserAuthCtx(ssh, (void*)password); + +#ifdef WOLFSSH_AGENT + if (useAgent) { + memset(&agentCbCtx, 0, sizeof(agentCbCtx)); + agentCbCtx.state = AGENT_STATE_INIT; + wolfSSH_set_agent_cb_ctx(ssh, &agentCbCtx); + } +#endif +#ifdef WOLFSSH_CERTS + /* CA certificate to verify host cert with */ + if (caCert) { + byte* der = NULL; + word32 derSz; + + ret = load_der_file(caCert, &der, &derSz); + if (ret != 0) err_sys("Couldn't load CA certificate file."); + if (wolfSSH_CTX_AddRootCert_buffer(ctx, der, derSz, + WOLFSSH_FORMAT_ASN1) != WS_SUCCESS) { + err_sys("Couldn't parse in CA certificate."); + } + WFREE(der, NULL, 0); + } + +#else + (void)caCert; +#endif /* WOLFSSH_CERTS */ + + wolfSSH_CTX_SetPublicKeyCheck(ctx, wsPublicKeyCheck); + wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)host); + + ret = wolfSSH_SetUsername(ssh, username); + if (ret != WS_SUCCESS) + err_sys("Couldn't set the username."); + + build_addr(&clientAddr, host, port); + tcp_socket(&sockFd); + ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz); + if (ret != 0) + err_sys("Couldn't connect to server."); + + if (nonBlock) + tcp_set_nonblocking(&sockFd); + + ret = wolfSSH_set_fd(ssh, (int)sockFd); + if (ret != WS_SUCCESS) + err_sys("Couldn't set the session's socket."); + + if (cmd != NULL) { + ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_EXEC, + (byte*)cmd, (word32)WSTRLEN((char*)cmd)); + if (ret != WS_SUCCESS) + err_sys("Couldn't set the channel type."); + } + +#ifdef WOLFSSH_TERM + if (keepOpen && cmd == NULL) { + ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_TERMINAL, NULL, 0); + if (ret != WS_SUCCESS) + err_sys("Couldn't set the terminal channel type."); + } +#endif + + if (!nonBlock) + ret = wolfSSH_connect(ssh); + else + ret = NonBlockSSH_connect(ssh); + if (ret != WS_SUCCESS) + err_sys("Couldn't connect SSH stream."); + +#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) + if (keepOpen) /* set up for psuedo-terminal */ + SetEcho(2); + + if (cmd != NULL || keepOpen == 1) { + #if defined(_POSIX_THREADS) + thread_args arg; + pthread_t thread[2]; + + arg.ssh = ssh; + wc_InitMutex(&arg.lock); + pthread_create(&thread[0], NULL, readInput, (void*)&arg); + pthread_create(&thread[1], NULL, readPeer, (void*)&arg); + pthread_join(thread[1], NULL); + pthread_cancel(thread[0]); + #elif defined(_MSC_VER) + thread_args arg; + HANDLE thread[2]; + + arg.ssh = ssh; + arg.rawMode = rawMode; + wc_InitMutex(&arg.lock); + thread[0] = CreateThread(NULL, 0, readInput, (void*)&arg, 0, 0); + thread[1] = CreateThread(NULL, 0, readPeer, (void*)&arg, 0, 0); + WaitForSingleObject(thread[1], INFINITE); + CloseHandle(thread[0]); + CloseHandle(thread[1]); + #else + err_sys("No threading to use"); + #endif + if (keepOpen) + SetEcho(1); + } +// else +#endif + +//#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) +// while (!imExit) { +//#else +// if (!imExit) { +//#endif +// ret = wolfSSH_stream_send(ssh, (byte*)testString, +// (word32)strlen(testString)); +// if (ret <= 0) +// err_sys("Couldn't send test string."); +// +// do { +// ret = wolfSSH_stream_read(ssh, (byte*)rxBuf, sizeof(rxBuf) - 1); +// if (ret <= 0) { +// ret = wolfSSH_get_error(ssh); +// if (ret != WS_WANT_READ && ret != WS_WANT_WRITE && +// ret != WS_CHAN_RXD) +// err_sys("Stream read failed."); +// } +// } while (ret == WS_WANT_READ || ret == WS_WANT_WRITE); +// +// rxBuf[ret] = '\0'; +// printf("Server said: %s\n", rxBuf); +// +//#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) +// sleep(10); +//#endif +// } + ret = wolfSSH_shutdown(ssh); + if (ret != WS_SUCCESS) { + err_sys("Sending the shutdown messages failed."); + } + ret = wolfSSH_worker(ssh, NULL); + if (ret != WS_SUCCESS) { + err_sys("Failed to listen for close messages from the peer."); + } + WCLOSESOCKET(sockFd); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); + if (ret != WS_SUCCESS) + err_sys("Closing client stream failed. Connection could have been closed by peer"); + + if (pubKeyName != NULL && userPublicKey != NULL) { + WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY); + } + + if (privKeyName != NULL && userPrivateKey != NULL) { + WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY); + } +#if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) + wc_ecc_fp_free(); /* free per thread cache */ +#endif + + return 0; +} + +#endif /* NO_WOLFSSH_CLIENT */ + + +#ifndef NO_MAIN_DRIVER + + int main(int argc, char** argv) + { + func_args args; + + args.argc = argc; + args.argv = argv; + args.return_code = 0; + args.user_auth = NULL; + + WSTARTTCP(); + + #ifdef DEBUG_WOLFSSH + wolfSSH_Debugging_ON(); + #endif + + wolfSSH_Init(); + + ChangeToWolfSshRoot(); +#ifndef NO_WOLFSSH_CLIENT + client_test(&args); +#endif + + wolfSSH_Cleanup(); + + return args.return_code; + } + + int myoptind = 0; + char* myoptarg = NULL; + +#endif /* NO_MAIN_DRIVER */ From 7d0e8aacda3f39cc1a9232f69b561c97d80f8575 Mon Sep 17 00:00:00 2001 From: Jacob Barthelmeh Date: Thu, 2 Mar 2023 08:27:21 -0700 Subject: [PATCH 03/16] fix warning and add more print outs to help menu --- apps/wolfssh/wolfssh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 4181366f3..556e07931 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -164,6 +164,8 @@ static int SetEcho(int type) static void ShowUsage(void) { printf("client %s\n", LIBWOLFSSH_VERSION_STRING); + printf("./wolfssh user@host\n"); + printf("./wolfssh -u user -h host\n"); printf(" -? display this help and exit\n"); printf(" -h host to connect to, default %s\n", wolfSshIp); printf(" -p port to connect on, default %d\n", wolfSshPort); @@ -880,7 +882,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) username = argv[argc-1]; host = XSTRSTR(username, "@"); - usernameSz = (int)(host - username); + usernameSz = (int)((uintptr_t)host - (uintptr_t)username); XMEMCPY(localUsername, username, usernameSz); localUsername[usernameSz] = '\0'; From 0346f68102a0d72eed1229ce80960fb2d14854d9 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 11 Sep 2023 16:30:25 -0700 Subject: [PATCH 04/16] wolfSSH Client 1. Since the base of the client is copied from the example client, remove the command line options used by the client. Start populating with a few of the options used by the other client, and decode the destination and optional command from the command line. 2. Move parsing the command line to its own function. It is a member of a set of functions for reading the command line options, the config file, and the environment to config the client. 3. Trim out many includes. Some will be added back later. Some don't need to be there. 4. The client will only use non-blocking sockets. Note/Todo: (a) The connection fails to start some of the time. (b) The user name has the potential to leak. --- apps/wolfssh/wolfssh.c | 438 +++++++++++++++++++++-------------------- 1 file changed, 221 insertions(+), 217 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 556e07931..8ff2c3d09 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -31,17 +31,19 @@ #endif #include -#include +#include +#include + #include -#ifdef WOLFSSH_AGENT - #include -#endif #include #include "examples/client/client.h" #if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) #include #endif +#include +#include + #ifdef WOLFSSH_SHELL #ifdef HAVE_PTY_H #include @@ -71,7 +73,9 @@ #endif -#ifndef NO_WOLFSSH_CLIENT +int myoptind = 0; +char* myoptarg = NULL; + /* type = 2 : shell / execute command settings * type = 0 : password @@ -161,37 +165,21 @@ static int SetEcho(int type) } -static void ShowUsage(void) +static void ShowUsage(const char* appPath) { - printf("client %s\n", LIBWOLFSSH_VERSION_STRING); - printf("./wolfssh user@host\n"); - printf("./wolfssh -u user -h host\n"); - printf(" -? display this help and exit\n"); - printf(" -h host to connect to, default %s\n", wolfSshIp); - printf(" -p port to connect on, default %d\n", wolfSshPort); - printf(" -u username to authenticate as (REQUIRED)\n"); - printf(" -P password for username, prompted if omitted\n"); - printf(" -e use sample ecc key for user\n"); - printf(" -i filename for the user's private key\n"); - printf(" -j filename for the user's public key\n"); - printf(" -x exit after successful connection without doing\n" - " read/write\n"); - printf(" -N use non-blocking sockets\n"); -#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) - printf(" -c executes remote command and pipe stdin/stdout\n"); -#ifdef USE_WINDOWS_API - printf(" -R raw untranslated output\n"); -#endif -#endif -#ifdef WOLFSSH_AGENT - printf(" -a Attempt to use SSH-AGENT\n"); -#endif -#ifdef WOLFSSH_CERTS - printf(" -J filename for DER certificate to use\n"); - printf(" Certificate example : client -u orange \\\n"); - printf(" -J orange-cert.der -i orange-key.der\n"); - printf(" -A filename for DER CA certificate to verify host\n"); -#endif + char appNameBuf[MAXPATHLEN]; + const char* appName; + + appName = basename_r(appPath, appNameBuf); + /* Attempt to use the actual program name from the caller. Otherwise, + * default to "wolfssh". */ + if (appName == NULL) { + appName = "wolfssh"; + } + + printf("%s v%s\n", appName, LIBWOLFSSH_VERSION_STRING); + printf("usage: %s [-E logfile] [-G] [-l login_name] [-N] [-p port] [-V]\n", + appName); } @@ -450,11 +438,6 @@ static int NonBlockSSH_connect(WOLFSSH* ssh) while (ret != WS_SUCCESS && (error == WS_WANT_READ || error == WS_WANT_WRITE)) { - if (error == WS_WANT_READ) - printf("... client would read block\n"); - else if (error == WS_WANT_WRITE) - printf("... client would write block\n"); - select_ret = tcp_select(sockfd, 1); if (select_ret == WS_SELECT_RECV_READY || select_ret == WS_SELECT_ERROR_READY) @@ -603,6 +586,9 @@ static THREAD_RET readPeer(void* in) } } #endif + if (ret == WS_ERROR) { + /* Sometimes, err is WANT_READ. Why? */ + } if (ret != WS_EOF) { err_sys("Stream read failed."); } @@ -823,172 +809,203 @@ static int wolfSSH_AGENT_IO_Cb(WS_AgentIoCbAction action, #endif /* WOLFSSH_AGENT */ -#define MAX_USERNAME 120 -#define MAX_HOSTNAME 25 +struct config { + const char* logFile; + const char* user; + const char* hostname; + const char* destination; + char* command; + char* user_host_buffer; + word32 printConfig:1; + word32 noCommand:1; + word16 port; +}; -THREAD_RETURN WOLFSSH_THREAD client_test(void* args) -{ - WOLFSSH_CTX* ctx = NULL; - WOLFSSH* ssh = NULL; - SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID; - SOCKADDR_IN_T clientAddr; - socklen_t clientAddrSz = sizeof(clientAddr); - //char rxBuf[80]; - int ret = 0; - int ch; - //int userEcc = 0; - word16 port = wolfSshPort; - char* host = (char*)wolfSshIp; - const char* username = NULL; - const char* password = NULL; - const char* cmd = NULL; - const char* privKeyName = NULL; - //byte imExit = 0; - byte nonBlock = 0; - byte keepOpen = 1; -#ifdef USE_WINDOWS_API - byte rawMode = 0; -#endif -#ifdef WOLFSSH_AGENT - byte useAgent = 0; - WS_AgentCbActionCtx agentCbCtx; -#endif - char localUsername[MAX_USERNAME]; - char localHostname[MAX_HOSTNAME]; - char localCmd[120]; - int argc = ((func_args*)args)->argc; - char** argv = ((func_args*)args)->argv; - ((func_args*)args)->return_code = 0; +static int config_init_default(struct config* config) +{ + memset(config, 0, sizeof(*config)); + config->port = 22; + return 0; +} - /* test if command is set */ - if (argc > 0 && XSTRSTR(argv[argc-1], "@") == NULL && - argv[argc-1][0] != '-') { - int cmdSz; - cmdSz = (int)XSTRLEN(argv[argc-1]); - XMEMCPY(localCmd, argv[argc-1], cmdSz); - localCmd[cmdSz] = '\0'; - cmd = &localCmd[0]; - argc -= 1; - } +static int config_parse_command_line(struct config* config, + int argc, char** argv) +{ + int ch; - /* test if user@host is used */ - if (argc > 0 && XSTRSTR(argv[argc-1], "@")) { - int usernameSz; - int hostnameSz; + while ((ch = mygetopt(argc, argv, "E:Gl:Np:V")) != -1) { + switch (ch) { + case 'E': + config->logFile = myoptarg; + break; - username = argv[argc-1]; - host = XSTRSTR(username, "@"); + case 'G': + config->printConfig = 1; + break; - usernameSz = (int)((uintptr_t)host - (uintptr_t)username); - XMEMCPY(localUsername, username, usernameSz); - localUsername[usernameSz] = '\0'; + case 'l': + config->user = myoptarg; + break; - hostnameSz = (int)XSTRLEN(host); - if (hostnameSz <= 1) { + case 'N': + config->noCommand = 1; + break; - } + case 'p': + config->port = (word16)atoi(myoptarg); + break; - XMEMCPY(localHostname, host+1, hostnameSz-1); - localHostname[hostnameSz-1] = '\0'; + case 'V': + fprintf(stderr, "wolfSSH v%s, wolfSSL v%s\n", + LIBWOLFSSH_VERSION_STRING, + LIBWOLFSSL_VERSION_STRING); + exit(EXIT_SUCCESS); - username = &localUsername[0]; - host = &localHostname[0]; - argc -= 1; + default: + ShowUsage(argv[0]); + exit(EXIT_FAILURE); + } } - while ((ch = mygetopt(argc, argv, "?ac:eh:i:j:p:tu:xzNP:RJ:A:")) != -1) { - switch (ch) { - case 'h': - host = myoptarg; - break; + /* Parse the destination. Either: + * - [user@]hostname + * - ssh://[user@]hostname[:port] */ + if (myoptind < argc) { + const char* uriPrefix = "ssh://"; + char* cursor; + char* found; + size_t sz; + int checkPort; + + myoptarg = argv[myoptind]; + + sz = strlen(myoptarg) + 1; + cursor = (char*)malloc(sz); + memcpy(cursor, myoptarg, sz); + config->user_host_buffer = cursor; + + if (strstr(cursor, uriPrefix)) { + checkPort = 1; + cursor += strlen(uriPrefix); + } + else { + checkPort = 0; + } - case 'z': - #ifdef WOLFSSH_SHOW_SIZES - wolfSSH_ShowSizes(); - exit(EXIT_SUCCESS); - #endif - break; + found = strchr(cursor, '@'); + if (found != NULL) { + *found = '\0'; + config->user = cursor; + cursor = found + 1; + } + else { + config->user = NULL; + } - case 'e': - //userEcc = 1; - break; + config->hostname = cursor; + if (checkPort) { + found = strchr(cursor, ':'); + if (found != NULL) { + *found = '\0'; + cursor = found + 1; + config->port = atoi(cursor); + } + } - case 'p': - port = (word16)atoi(myoptarg); - #if !defined(NO_MAIN_DRIVER) || defined(USE_WINDOWS_API) - if (port == 0) - err_sys("port number cannot be 0"); - #endif - break; + myoptind++; + } - case 'u': - username = myoptarg; - break; + if (myoptind < argc) { + int i; + size_t commandSz; + char* cursor; + char* command; + + /* Count the spaces needed. The following will calculate one extra + * space but that's for the nul termination. */ + commandSz = argc - myoptind; + for (i = myoptind; i < argc; i++) { + commandSz += strlen(argv[i]); + } - case 'P': - password = myoptarg; - break; + command = (char*)malloc(commandSz); + cursor = command; - case 'i': - privKeyName = myoptarg; - break; + for (i = myoptind; i < argc; i++) { + cursor = stpcpy(cursor, argv[i]); + *cursor = ' '; + cursor++; + } + *(--cursor) = '\0'; + myoptind++; + } - case 'j': - pubKeyName = myoptarg; - break; + return 0; +} - #ifdef WOLFSSH_CERTS - case 'J': - certName = myoptarg; - break; - case 'A': - caCert = myoptarg; - break; +static int config_print(struct config* config) +{ + if (config->printConfig) { + printf("user %s\n", config->user); + printf("hostname %s\n", config->hostname); + printf("port %u\n", config->port); + printf("noCommand %s\n", config->noCommand ? "true" : "false"); + printf("logfile %s\n", config->logFile); + printf("command %s\n", config->command); + } - #endif + return 0; +} - case 'x': - /* exit after successful connection without read/write */ - //imExit = 1; - break; - case 'N': - nonBlock = 1; - break; +static int config_cleanup(struct config* config) +{ + if (config->user_host_buffer) { + free(config->user_host_buffer); + } + if (config->command) { + free(config->command); + } - #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) - case 'c': - cmd = myoptarg; - break; - #ifdef USE_WINDOWS_API - case 'R': - rawMode = 1; - break; - #endif /* USE_WINDOWS_API */ - #endif + return 0; +} - #ifdef WOLFSSH_AGENT - case 'a': - useAgent = 1; - break; - #endif - case '?': - ShowUsage(); - exit(EXIT_SUCCESS); +THREAD_RETURN WOLFSSH_THREAD client_test(void* args) +{ + WOLFSSH_CTX* ctx = NULL; + WOLFSSH* ssh = NULL; + SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID; + SOCKADDR_IN_T clientAddr; + socklen_t clientAddrSz = sizeof(clientAddr); + //char rxBuf[80]; + int ret = 0; + //int userEcc = 0; + const char* password = NULL; + const char* privKeyName = NULL; + //byte imExit = 0; + byte keepOpen = 1; +#ifdef USE_WINDOWS_API + byte rawMode = 0; +#endif +#ifdef WOLFSSH_AGENT + byte useAgent = 0; + WS_AgentCbActionCtx agentCbCtx; +#endif + struct config config; - default: - ShowUsage(); - exit(MY_EX_USAGE); - } - } - myoptind = 0; /* reset for test cases */ + ((func_args*)args)->return_code = 0; - if (username == NULL) + config_init_default(&config); + config_parse_command_line(&config, + ((func_args*)args)->argc, ((func_args*)args)->argv); + config_print(&config); + + if (config.user == NULL) err_sys("client requires a username parameter."); #ifdef WOLFSSH_NO_RSA @@ -1140,44 +1157,41 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #endif /* WOLFSSH_CERTS */ wolfSSH_CTX_SetPublicKeyCheck(ctx, wsPublicKeyCheck); - wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)host); + wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)config.hostname); - ret = wolfSSH_SetUsername(ssh, username); + ret = wolfSSH_SetUsername(ssh, config.user); if (ret != WS_SUCCESS) err_sys("Couldn't set the username."); - build_addr(&clientAddr, host, port); + build_addr(&clientAddr, config.hostname, config.port); tcp_socket(&sockFd); ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz); if (ret != 0) - err_sys("Couldn't connect to server."); + err_sys("Couldn't connect to server!!!."); - if (nonBlock) - tcp_set_nonblocking(&sockFd); + tcp_set_nonblocking(&sockFd); ret = wolfSSH_set_fd(ssh, (int)sockFd); if (ret != WS_SUCCESS) err_sys("Couldn't set the session's socket."); - if (cmd != NULL) { + if (config.command != NULL) { ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_EXEC, - (byte*)cmd, (word32)WSTRLEN((char*)cmd)); + (byte*)config.command, + (word32)WSTRLEN((char*)config.command)); if (ret != WS_SUCCESS) err_sys("Couldn't set the channel type."); } #ifdef WOLFSSH_TERM - if (keepOpen && cmd == NULL) { + if (keepOpen && config.command == NULL) { ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_TERMINAL, NULL, 0); if (ret != WS_SUCCESS) err_sys("Couldn't set the terminal channel type."); } #endif - if (!nonBlock) - ret = wolfSSH_connect(ssh); - else - ret = NonBlockSSH_connect(ssh); + ret = NonBlockSSH_connect(ssh); if (ret != WS_SUCCESS) err_sys("Couldn't connect SSH stream."); @@ -1185,7 +1199,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (keepOpen) /* set up for psuedo-terminal */ SetEcho(2); - if (cmd != NULL || keepOpen == 1) { + if (config.command != NULL || keepOpen == 1) { #if defined(_POSIX_THREADS) thread_args arg; pthread_t thread[2]; @@ -1269,42 +1283,32 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) wc_ecc_fp_free(); /* free per thread cache */ #endif + config_cleanup(&config); + return 0; } -#endif /* NO_WOLFSSH_CLIENT */ - -#ifndef NO_MAIN_DRIVER - - int main(int argc, char** argv) - { - func_args args; - - args.argc = argc; - args.argv = argv; - args.return_code = 0; - args.user_auth = NULL; - - WSTARTTCP(); +int main(int argc, char** argv) +{ + func_args args; - #ifdef DEBUG_WOLFSSH - wolfSSH_Debugging_ON(); - #endif + args.argc = argc; + args.argv = argv; + args.return_code = 0; + args.user_auth = NULL; - wolfSSH_Init(); + WSTARTTCP(); - ChangeToWolfSshRoot(); -#ifndef NO_WOLFSSH_CLIENT - client_test(&args); -#endif + #ifdef DEBUG_WOLFSSH + wolfSSH_Debugging_ON(); + #endif - wolfSSH_Cleanup(); + wolfSSH_Init(); - return args.return_code; - } + client_test(&args); - int myoptind = 0; - char* myoptarg = NULL; + wolfSSH_Cleanup(); -#endif /* NO_MAIN_DRIVER */ + return args.return_code; +} From e19b60b7671bca73efcfd4fae38b94d3d6cd4b5e Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 13 Sep 2023 09:05:27 -0700 Subject: [PATCH 05/16] wolfSSH Client 1. Added a comment regarding the potential leak. It is confirmed NOT A LEAK. 2. Fixed the connection fail. Added a check for WANT_READ and treat that as a success. 3. Put back a couple things removed from the last commit regarding agent. 4. Change ShowUsage to use basename(). (Linux build didn't have basename_r().) 5. Add NULL for the fs context parameter of the file wrapper macros. 6. Printing config checks any pointers for null then uses a default value. 7. Minor cleanups. --- apps/wolfssh/wolfssh.c | 57 +++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 8ff2c3d09..76bfe1454 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -35,6 +35,9 @@ #include #include +#ifdef WOLFSSH_AGENT + #include +#endif #include #include "examples/client/client.h" #if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) @@ -165,12 +168,11 @@ static int SetEcho(int type) } -static void ShowUsage(const char* appPath) +static void ShowUsage(char* appPath) { - char appNameBuf[MAXPATHLEN]; const char* appName; - appName = basename_r(appPath, appNameBuf); + appName = basename(appPath); /* Attempt to use the actual program name from the caller. Otherwise, * default to "wolfssh". */ if (appName == NULL) { @@ -550,10 +552,13 @@ static THREAD_RET readPeer(void* in) } while (ret > 0); } else if (ret <= 0) { - #ifdef WOLFSSH_AGENT if (ret == WS_FATAL_ERROR) { ret = wolfSSH_get_error(args->ssh); - if (ret == WS_CHAN_RXD) { + if (ret == WS_WANT_READ) { + ret = WS_SUCCESS; + } + #ifdef WOLFSSH_AGENT + else if (ret == WS_CHAN_RXD) { byte agentBuf[512]; int rxd, txd; word32 channel = 0; @@ -584,12 +589,9 @@ static THREAD_RET readPeer(void* in) WMEMSET(agentBuf, 0, sizeof(agentBuf)); continue; } + #endif /* WOLFSSH_AGENT */ } - #endif - if (ret == WS_ERROR) { - /* Sometimes, err is WANT_READ. Why? */ - } - if (ret != WS_EOF) { + else if (ret != WS_EOF) { err_sys("Stream read failed."); } } @@ -616,7 +618,8 @@ static THREAD_RET readPeer(void* in) fflush(stdout); #endif } - if (wolfSSH_stream_peek(args->ssh, buf, bufSz) <= 0) { + ret = wolfSSH_stream_peek(args->ssh, buf, bufSz); + if (ret <= 0) { bytes = 0; /* read it all */ } } @@ -643,29 +646,29 @@ static int load_der_file(const char* filename, byte** out, word32* outSz) if (filename == NULL || out == NULL || outSz == NULL) return -1; - ret = WFOPEN(&file, filename, "rb"); + ret = WFOPEN(NULL, &file, filename, "rb"); if (ret != 0 || file == WBADFILE) return -1; - if (WFSEEK(file, 0, WSEEK_END) != 0) { - WFCLOSE(file); + if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { + WFCLOSE(NULL, file); return -1; } - inSz = (word32)WFTELL(file); - WREWIND(file); + inSz = (word32)WFTELL(NULL, file); + WREWIND(NULL, file); if (inSz == 0) { - WFCLOSE(file); + WFCLOSE(NULL, file); return -1; } in = (byte*)WMALLOC(inSz, NULL, 0); if (in == NULL) { - WFCLOSE(file); + WFCLOSE(NULL, file); return -1; } - ret = (int)WFREAD(in, 1, inSz, file); + ret = (int)WFREAD(NULL, in, 1, inSz, file); if (ret <= 0 || (word32)ret != inSz) { ret = -1; WFREE(in, NULL, 0); @@ -678,7 +681,7 @@ static int load_der_file(const char* filename, byte** out, word32* outSz) *out = in; *outSz = inSz; - WFCLOSE(file); + WFCLOSE(NULL, file); return ret; } @@ -813,13 +816,15 @@ struct config { const char* logFile; const char* user; const char* hostname; - const char* destination; char* command; char* user_host_buffer; word32 printConfig:1; word32 noCommand:1; word16 port; }; +/* Note: user is either pointing to an argv argument, the value of USER in + * the env, or to the username in the user_host_buffer. hostname will be + * pointing into user_host_buffer. */ static int config_init_default(struct config* config) @@ -950,12 +955,12 @@ static int config_parse_command_line(struct config* config, static int config_print(struct config* config) { if (config->printConfig) { - printf("user %s\n", config->user); - printf("hostname %s\n", config->hostname); + printf("user %s\n", config->user ? config->user : "none"); + printf("hostname %s\n", config->hostname ? config->hostname : "none"); printf("port %u\n", config->port); printf("noCommand %s\n", config->noCommand ? "true" : "false"); - printf("logfile %s\n", config->logFile); - printf("command %s\n", config->command); + printf("logfile %s\n", config->logFile ? config->logFile : "default"); + printf("command %s\n", config->command ? config->command : "none"); } return 0; @@ -1167,7 +1172,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) tcp_socket(&sockFd); ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz); if (ret != 0) - err_sys("Couldn't connect to server!!!."); + err_sys("Couldn't connect to server."); tcp_set_nonblocking(&sockFd); From f28d6b4a264691503ac83c23701ec853836cd008 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 13 Sep 2023 17:17:04 -0700 Subject: [PATCH 06/16] wolfSSH Client 1. Merge the current example client code into the wolfssh client. 2. Add the example client common files to the wolfssh client. --- apps/wolfssh/common.c | 759 ++++++++++++++++++++++++++++++++++++++++ apps/wolfssh/common.h | 36 ++ apps/wolfssh/include.am | 3 +- apps/wolfssh/wolfssh.c | 736 +++++++++++++------------------------- 4 files changed, 1037 insertions(+), 497 deletions(-) create mode 100644 apps/wolfssh/common.c create mode 100644 apps/wolfssh/common.h diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c new file mode 100644 index 000000000..64c5a19d2 --- /dev/null +++ b/apps/wolfssh/common.c @@ -0,0 +1,759 @@ +/* common.c + * + * Copyright (C) 2014-2023 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + + +#ifdef HAVE_CONFIG_H + #include +#endif + +#define WOLFSSH_TEST_CLIENT + +#include +#include +#include +#include +#include +#include +#include "examples/client/common.h" +#ifndef USE_WINDOWS_API + #include +#endif + +#ifdef WOLFSSH_CERTS + #include +#endif + +static byte userPublicKeyBuf[512]; +static byte* userPublicKey = userPublicKeyBuf; +static const byte* userPublicKeyType = NULL; +static byte userPassword[256]; +static const byte* userPrivateKeyType = NULL; +static word32 userPublicKeySz = 0; +static byte pubKeyLoaded = 0; /* was a public key loaded */ +static byte userPrivateKeyBuf[1191]; /* Size equal to hanselPrivateRsaSz. */ +static byte* userPrivateKey = userPrivateKeyBuf; +static word32 userPublicKeyTypeSz = 0; +static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf); +static word32 userPrivateKeyTypeSz = 0; +static byte isPrivate = 0; + + +#ifdef WOLFSSH_CERTS +#if 0 +/* compiled in for using RSA certificates instead of ECC certificate */ +static const byte publicKeyType[] = "x509v3-ssh-rsa"; +static const byte privateKeyType[] = "ssh-rsa"; +#else +static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; +#endif +#endif + + + + +#ifndef WOLFSSH_NO_RSA +static const char* hanselPublicRsa = + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" + "MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G" + "p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj" + "nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW" + "NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE" + "nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel"; +static const byte hanselPrivateRsa[] = { + 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xbd, 0x3f, 0x76, 0x45, 0xa3, 0x03, 0xac, 0x38, 0xd5, 0xc7, 0x0f, 0x93, + 0x30, 0x5a, 0x20, 0x9c, 0x89, 0x7c, 0xad, 0x05, 0x16, 0x46, 0x86, 0x83, + 0x0d, 0x8a, 0x2b, 0x16, 0x4a, 0x05, 0x2c, 0xe4, 0x77, 0x47, 0x70, 0x00, + 0xae, 0x1d, 0x83, 0xe2, 0xd9, 0x6e, 0x99, 0xd4, 0xf0, 0x45, 0x98, 0x15, + 0x93, 0xf6, 0x87, 0x4e, 0xac, 0x64, 0x63, 0xa1, 0x95, 0xc9, 0x7c, 0x30, + 0xe8, 0x3e, 0x2f, 0xa3, 0xf1, 0x24, 0x9f, 0x0c, 0x6b, 0x1c, 0xfe, 0x1b, + 0x02, 0x99, 0xcd, 0xc6, 0xa7, 0x6c, 0x84, 0x85, 0x46, 0x54, 0x12, 0x40, + 0xe1, 0xb4, 0xe5, 0xf2, 0xaa, 0x39, 0xec, 0xd6, 0x27, 0x24, 0x0b, 0xd1, + 0xa1, 0xe2, 0xef, 0x34, 0x69, 0x25, 0x6d, 0xc0, 0x74, 0x67, 0x25, 0x98, + 0x7d, 0xc4, 0xf8, 0x52, 0xab, 0x9b, 0x4b, 0x3a, 0x12, 0x1d, 0xe1, 0xe3, + 0xfa, 0xd6, 0xcf, 0x9a, 0xe6, 0x9c, 0x23, 0x4e, 0x39, 0xc4, 0x84, 0x16, + 0x88, 0x3d, 0x42, 0x4e, 0xd8, 0x2f, 0xcc, 0xd2, 0x91, 0x67, 0x9d, 0xb6, + 0x71, 0x2a, 0x02, 0x65, 0x5f, 0xbb, 0x75, 0x0e, 0x8c, 0xbb, 0x87, 0x97, + 0x97, 0xc6, 0xf8, 0xb2, 0x98, 0xe2, 0x2f, 0x68, 0x26, 0x4a, 0x53, 0xec, + 0x79, 0x3a, 0x8a, 0x5f, 0xcc, 0xcf, 0xf0, 0x16, 0x47, 0xb2, 0xd0, 0x43, + 0xd6, 0x36, 0x6c, 0xc8, 0xe7, 0x2f, 0xfe, 0xa7, 0x35, 0x39, 0x69, 0xfb, + 0x1d, 0x78, 0x45, 0x9d, 0x89, 0x00, 0xc8, 0x41, 0xcf, 0x34, 0x1f, 0xa3, + 0xf3, 0xf1, 0xfb, 0x28, 0x14, 0xfb, 0xd8, 0x48, 0x6f, 0xac, 0xe3, 0xfc, + 0x33, 0xd1, 0xdb, 0xae, 0xef, 0x27, 0x9e, 0x57, 0x56, 0x29, 0xa2, 0x1a, + 0x3a, 0xe5, 0x9a, 0xfe, 0xa4, 0x49, 0xc8, 0x7f, 0xb7, 0x4e, 0xd0, 0x1f, + 0x04, 0x6e, 0x58, 0x16, 0xb7, 0xeb, 0x9d, 0xf8, 0x92, 0x3c, 0xc2, 0xb0, + 0x21, 0x7c, 0x4e, 0x31, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x01, 0x00, 0x8d, 0xa4, 0x61, 0x06, 0x2f, 0xc3, 0x40, 0xf4, 0x6c, 0xf4, + 0x87, 0x30, 0xb8, 0x00, 0xcc, 0xe5, 0xbc, 0x75, 0x87, 0x1e, 0x06, 0x95, + 0x14, 0x7a, 0x23, 0xf9, 0x24, 0xd4, 0x92, 0xe4, 0x1a, 0xbc, 0x88, 0x95, + 0xfc, 0x3b, 0x56, 0x16, 0x1b, 0x2e, 0xff, 0x64, 0x2b, 0x58, 0xd7, 0xd8, + 0x8e, 0xc2, 0x9f, 0xb2, 0xe5, 0x84, 0xb9, 0xbc, 0x8d, 0x61, 0x54, 0x35, + 0xb0, 0x70, 0xfe, 0x72, 0x04, 0xc0, 0x24, 0x6d, 0x2f, 0x69, 0x61, 0x06, + 0x1b, 0x1d, 0xe6, 0x2d, 0x6d, 0x79, 0x60, 0xb7, 0xf4, 0xdb, 0xb7, 0x4e, + 0x97, 0x36, 0xde, 0x77, 0xc1, 0x9f, 0x85, 0x4e, 0xc3, 0x77, 0x69, 0x66, + 0x2e, 0x3e, 0x61, 0x76, 0xf3, 0x67, 0xfb, 0xc6, 0x9a, 0xc5, 0x6f, 0x99, + 0xff, 0xe6, 0x89, 0x43, 0x92, 0x44, 0x75, 0xd2, 0x4e, 0x54, 0x91, 0x58, + 0xb2, 0x48, 0x2a, 0xe6, 0xfa, 0x0d, 0x4a, 0xca, 0xd4, 0x14, 0x9e, 0xf6, + 0x27, 0x67, 0xb7, 0x25, 0x7a, 0x43, 0xbb, 0x2b, 0x67, 0xd1, 0xfe, 0xd1, + 0x68, 0x23, 0x06, 0x30, 0x7c, 0xbf, 0x60, 0x49, 0xde, 0xcc, 0x7e, 0x26, + 0x5a, 0x3b, 0xfe, 0xa6, 0xa6, 0xe7, 0xa8, 0xdd, 0xac, 0xb9, 0xaf, 0x82, + 0x9a, 0x3a, 0x41, 0x7e, 0x61, 0x21, 0x37, 0xa3, 0x08, 0xe4, 0xc4, 0xbc, + 0x11, 0xf5, 0x3b, 0x8e, 0x4d, 0x51, 0xf3, 0xbd, 0xda, 0xba, 0xb2, 0xc5, + 0xee, 0xfb, 0xcf, 0xdf, 0x83, 0xa1, 0x82, 0x01, 0xe1, 0x51, 0x9d, 0x07, + 0x5a, 0x5d, 0xd8, 0xc7, 0x5b, 0x3f, 0x97, 0x13, 0x6a, 0x4d, 0x1e, 0x8d, + 0x39, 0xac, 0x40, 0x95, 0x82, 0x6c, 0xa2, 0xa1, 0xcc, 0x8a, 0x9b, 0x21, + 0x32, 0x3a, 0x58, 0xcc, 0xe7, 0x2d, 0x1a, 0x79, 0xa4, 0x31, 0x50, 0xb1, + 0x4b, 0x76, 0x23, 0x1b, 0xb3, 0x40, 0x3d, 0x3d, 0x72, 0x72, 0x32, 0xec, + 0x5f, 0x38, 0xb5, 0x8d, 0xb2, 0x8d, 0x02, 0x81, 0x81, 0x00, 0xed, 0x5a, + 0x7e, 0x8e, 0xa1, 0x62, 0x7d, 0x26, 0x5c, 0x78, 0xc4, 0x87, 0x71, 0xc9, + 0x41, 0x57, 0x77, 0x94, 0x93, 0x93, 0x26, 0x78, 0xc8, 0xa3, 0x15, 0xbd, + 0x59, 0xcb, 0x1b, 0xb4, 0xb2, 0x6b, 0x0f, 0xe7, 0x80, 0xf2, 0xfa, 0xfc, + 0x8e, 0x32, 0xa9, 0x1b, 0x1e, 0x7f, 0xe1, 0x26, 0xef, 0x00, 0x25, 0xd8, + 0xdd, 0xc9, 0x1a, 0x23, 0x00, 0x26, 0x3b, 0x46, 0x23, 0xc0, 0x50, 0xe7, + 0xce, 0x62, 0xb2, 0x36, 0xb2, 0x98, 0x09, 0x16, 0x34, 0x18, 0x9e, 0x46, + 0xbc, 0xaf, 0x2c, 0x28, 0x94, 0x2f, 0xe0, 0x5d, 0xc9, 0xb2, 0xc8, 0xfb, + 0x5d, 0x13, 0xd5, 0x36, 0xaa, 0x15, 0x0f, 0x89, 0xa5, 0x16, 0x59, 0x5d, + 0x22, 0x74, 0xa4, 0x47, 0x5d, 0xfa, 0xfb, 0x0c, 0x5e, 0x80, 0xbf, 0x0f, + 0xc2, 0x9c, 0x95, 0x0f, 0xe7, 0xaa, 0x7f, 0x16, 0x1b, 0xd4, 0xdb, 0x38, + 0x7d, 0x58, 0x2e, 0x57, 0x78, 0x2f, 0x02, 0x81, 0x81, 0x00, 0xcc, 0x1d, + 0x7f, 0x74, 0x36, 0x6d, 0xb4, 0x92, 0x25, 0x62, 0xc5, 0x50, 0xb0, 0x5c, + 0xa1, 0xda, 0xf3, 0xb2, 0xfd, 0x1e, 0x98, 0x0d, 0x8b, 0x05, 0x69, 0x60, + 0x8e, 0x5e, 0xd2, 0x89, 0x90, 0x4a, 0x0d, 0x46, 0x7e, 0xe2, 0x54, 0x69, + 0xae, 0x16, 0xe6, 0xcb, 0xd5, 0xbd, 0x7b, 0x30, 0x2b, 0x7b, 0x5c, 0xee, + 0x93, 0x12, 0xcf, 0x63, 0x89, 0x9c, 0x3d, 0xc8, 0x2d, 0xe4, 0x7a, 0x61, + 0x09, 0x5e, 0x80, 0xfb, 0x3c, 0x03, 0xb3, 0x73, 0xd6, 0x98, 0xd0, 0x84, + 0x0c, 0x59, 0x9f, 0x4e, 0x80, 0xf3, 0x46, 0xed, 0x03, 0x9d, 0xd5, 0xdc, + 0x8b, 0xe7, 0xb1, 0xe8, 0xaa, 0x57, 0xdc, 0xd1, 0x41, 0x55, 0x07, 0xc7, + 0xdf, 0x67, 0x3c, 0x72, 0x78, 0xb0, 0x60, 0x8f, 0x85, 0xa1, 0x90, 0x99, + 0x0c, 0xa5, 0x67, 0xab, 0xf0, 0xb6, 0x74, 0x90, 0x03, 0x55, 0x7b, 0x5e, + 0xcc, 0xc5, 0xbf, 0xde, 0xa7, 0x9f, 0x02, 0x81, 0x80, 0x40, 0x81, 0x6e, + 0x91, 0xae, 0xd4, 0x88, 0x74, 0xab, 0x7e, 0xfa, 0xd2, 0x60, 0x9f, 0x34, + 0x8d, 0xe3, 0xe6, 0xd2, 0x30, 0x94, 0xad, 0x10, 0xc2, 0x19, 0xbf, 0x6b, + 0x2e, 0xe2, 0xe9, 0xb9, 0xef, 0x94, 0xd3, 0xf2, 0xdc, 0x96, 0x4f, 0x9b, + 0x09, 0xb3, 0xa1, 0xb6, 0x29, 0x44, 0xf4, 0x82, 0xd1, 0xc4, 0x77, 0x6a, + 0xd7, 0x23, 0xae, 0x4d, 0x75, 0x16, 0x78, 0xda, 0x70, 0x82, 0xcc, 0x6c, + 0xef, 0xaf, 0xc5, 0x63, 0xc6, 0x23, 0xfa, 0x0f, 0xd0, 0x7c, 0xfb, 0x76, + 0x7e, 0x18, 0xff, 0x32, 0x3e, 0xcc, 0xb8, 0x50, 0x7f, 0xb1, 0x55, 0x77, + 0x17, 0x53, 0xc3, 0xd6, 0x77, 0x80, 0xd0, 0x84, 0xb8, 0x4d, 0x33, 0x1d, + 0x91, 0x1b, 0xb0, 0x75, 0x9f, 0x27, 0x29, 0x56, 0x69, 0xa1, 0x03, 0x54, + 0x7d, 0x9f, 0x99, 0x41, 0xf9, 0xb9, 0x2e, 0x36, 0x04, 0x24, 0x4b, 0xf6, + 0xec, 0xc7, 0x33, 0x68, 0x6b, 0x02, 0x81, 0x80, 0x60, 0x35, 0xcb, 0x3c, + 0xd0, 0xe6, 0xf7, 0x05, 0x28, 0x20, 0x1d, 0x57, 0x82, 0x39, 0xb7, 0x85, + 0x07, 0xf7, 0xa7, 0x3d, 0xc3, 0x78, 0x26, 0xbe, 0x3f, 0x44, 0x66, 0xf7, + 0x25, 0x0f, 0xf8, 0x76, 0x1f, 0x39, 0xca, 0x57, 0x0e, 0x68, 0xdd, 0xc9, + 0x27, 0xb2, 0x8e, 0xa6, 0x08, 0xa9, 0xd4, 0xe5, 0x0a, 0x11, 0xde, 0x3b, + 0x30, 0x8b, 0xff, 0x72, 0x28, 0xe0, 0xf1, 0x58, 0xcf, 0xa2, 0x6b, 0x93, + 0x23, 0x02, 0xc8, 0xf0, 0x09, 0xa7, 0x21, 0x50, 0xd8, 0x80, 0x55, 0x7d, + 0xed, 0x0c, 0x48, 0xd5, 0xe2, 0xe9, 0x97, 0x19, 0xcf, 0x93, 0x6c, 0x52, + 0xa2, 0xd6, 0x43, 0x6c, 0xb4, 0xc5, 0xe1, 0xa0, 0x9d, 0xd1, 0x45, 0x69, + 0x58, 0xe1, 0xb0, 0x27, 0x9a, 0xec, 0x2b, 0x95, 0xd3, 0x1d, 0x81, 0x0b, + 0x7a, 0x09, 0x5e, 0xa5, 0xf1, 0xdd, 0x6b, 0xe4, 0xe0, 0x08, 0xf8, 0x46, + 0x81, 0xc1, 0x06, 0x8b, 0x02, 0x81, 0x80, 0x00, 0xf6, 0xf2, 0xeb, 0x25, + 0xba, 0x78, 0x04, 0xad, 0x0e, 0x0d, 0x2e, 0xa7, 0x69, 0xd6, 0x57, 0xe6, + 0x36, 0x32, 0x50, 0xd2, 0xf2, 0xeb, 0xad, 0x31, 0x46, 0x65, 0xc0, 0x07, + 0x97, 0x83, 0x6c, 0x66, 0x27, 0x3e, 0x94, 0x2c, 0x05, 0x01, 0x5f, 0x5c, + 0xe0, 0x31, 0x30, 0xec, 0x61, 0xd2, 0x74, 0x35, 0xb7, 0x9f, 0x38, 0xe7, + 0x8e, 0x67, 0xb1, 0x50, 0x08, 0x68, 0xce, 0xcf, 0xd8, 0xee, 0x88, 0xfd, + 0x5d, 0xc4, 0xcd, 0xe2, 0x86, 0x3d, 0x4a, 0x0e, 0x04, 0x7f, 0xee, 0x8a, + 0xe8, 0x9b, 0x16, 0xa1, 0xfc, 0x09, 0x82, 0xe2, 0x62, 0x03, 0x3c, 0xe8, + 0x25, 0x7f, 0x3c, 0x9a, 0xaa, 0x83, 0xf8, 0xd8, 0x93, 0xd1, 0x54, 0xf9, + 0xce, 0xb4, 0xfa, 0x35, 0x36, 0xcc, 0x18, 0x54, 0xaa, 0xf2, 0x90, 0xb7, + 0x7c, 0x97, 0x0b, 0x27, 0x2f, 0xae, 0xfc, 0xc3, 0x93, 0xaf, 0x1a, 0x75, + 0xec, 0x18, 0xdb +}; +static const unsigned int hanselPrivateRsaSz = 1191; +#endif + + +#ifndef WOLFSSH_NO_ECC +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 +static const char* hanselPublicEcc = + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA" + "BBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25" + "qUzgDtH7oyaQROUnNvk= hansel"; +static const byte hanselPrivateEcc[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x03, 0x6e, 0x17, 0xd3, 0xb9, + 0xb8, 0xab, 0xc8, 0xf9, 0x1f, 0xf1, 0x2d, 0x44, 0x4c, 0x3b, 0x12, 0xb1, + 0xa4, 0x77, 0xd8, 0xed, 0x0e, 0x6a, 0xbe, 0x60, 0xc2, 0xf6, 0x8b, 0xe7, + 0xd3, 0x87, 0x83, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xd9, 0x08, 0xe4, + 0x94, 0xcf, 0xe8, 0x3d, 0x25, 0x17, 0x8d, 0xad, 0x6f, 0x15, 0xf5, 0xf5, + 0xc1, 0x3c, 0xee, 0x1c, 0xed, 0x51, 0x2e, 0x85, 0x48, 0x3a, 0x06, 0xbc, + 0xf7, 0xe2, 0x53, 0x40, 0xa0, 0x78, 0xd4, 0x9b, 0x23, 0xe6, 0x85, 0x74, + 0xa2, 0x33, 0x4c, 0xfe, 0x42, 0x40, 0x42, 0x52, 0xbe, 0x6d, 0xb9, 0xa9, + 0x4c, 0xe0, 0x0e, 0xd1, 0xfb, 0xa3, 0x26, 0x90, 0x44, 0xe5, 0x27, 0x36, + 0xf9 +}; +static const unsigned int hanselPrivateEccSz = 121; +#elif !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) +static const char* hanselPublicEcc = + "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAA" + "CFBAET/BOzBb9Jx9b52VIHFP4g/uk5KceDpz2M+/Ln9WiDjsMfb4NgNCAB+EMNJUX/TNBL" + "FFmqr7c6+zUH+QAo2qstvQDsReyFkETRB2vZD//nCZfcAe0RMtKZmgtQLKXzSlimUjXBM4" + "/zE5lwE05aXADp88h8nuaT/X4bll9cWJlH0fUykA== hansel"; +static const byte hanselPrivateEcc[] = { + 0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42, 0x01, 0x79, 0x40, 0xb8, + 0x33, 0xe5, 0x53, 0x5b, 0x9e, 0xfd, 0xed, 0xbe, 0x7c, 0x68, 0xe4, 0xb6, + 0xc3, 0x50, 0x00, 0x0d, 0x39, 0x64, 0x05, 0xf6, 0x5a, 0x5d, 0x41, 0xab, + 0xb3, 0xd9, 0xa7, 0xcb, 0x1c, 0x7d, 0x34, 0x46, 0x5c, 0x2d, 0x56, 0x26, + 0xa0, 0x6a, 0xc7, 0x3d, 0x4f, 0x78, 0x58, 0x14, 0x66, 0x6c, 0xfc, 0x86, + 0x3c, 0x8b, 0x5b, 0x54, 0x29, 0x89, 0x93, 0x48, 0xd9, 0x54, 0x8b, 0xbe, + 0x9d, 0x91, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0xa1, + 0x81, 0x89, 0x03, 0x81, 0x86, 0x00, 0x04, 0x01, 0x13, 0xfc, 0x13, 0xb3, + 0x05, 0xbf, 0x49, 0xc7, 0xd6, 0xf9, 0xd9, 0x52, 0x07, 0x14, 0xfe, 0x20, + 0xfe, 0xe9, 0x39, 0x29, 0xc7, 0x83, 0xa7, 0x3d, 0x8c, 0xfb, 0xf2, 0xe7, + 0xf5, 0x68, 0x83, 0x8e, 0xc3, 0x1f, 0x6f, 0x83, 0x60, 0x34, 0x20, 0x01, + 0xf8, 0x43, 0x0d, 0x25, 0x45, 0xff, 0x4c, 0xd0, 0x4b, 0x14, 0x59, 0xaa, + 0xaf, 0xb7, 0x3a, 0xfb, 0x35, 0x07, 0xf9, 0x00, 0x28, 0xda, 0xab, 0x2d, + 0xbd, 0x00, 0xec, 0x45, 0xec, 0x85, 0x90, 0x44, 0xd1, 0x07, 0x6b, 0xd9, + 0x0f, 0xff, 0xe7, 0x09, 0x97, 0xdc, 0x01, 0xed, 0x11, 0x32, 0xd2, 0x99, + 0x9a, 0x0b, 0x50, 0x2c, 0xa5, 0xf3, 0x4a, 0x58, 0xa6, 0x52, 0x35, 0xc1, + 0x33, 0x8f, 0xf3, 0x13, 0x99, 0x70, 0x13, 0x4e, 0x5a, 0x5c, 0x00, 0xe9, + 0xf3, 0xc8, 0x7c, 0x9e, 0xe6, 0x93, 0xfd, 0x7e, 0x1b, 0x96, 0x5f, 0x5c, + 0x58, 0x99, 0x47, 0xd1, 0xf5, 0x32, 0x90 +}; +static const unsigned int hanselPrivateEccSz = 223; +#else + #error "Enable an ECC Curve or disable ECC." +#endif +#endif + + +#if defined(WOLFSSH_CERTS) + +static int load_der_file(const char* filename, byte** out, word32* outSz) +{ + WFILE* file; + byte* in; + word32 inSz; + int ret; + + if (filename == NULL || out == NULL || outSz == NULL) + return -1; + + ret = WFOPEN(NULL, &file, filename, "rb"); + if (ret != 0 || file == WBADFILE) + return -1; + + if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { + WFCLOSE(NULL, file); + return -1; + } + inSz = (word32)WFTELL(NULL, file); + WREWIND(NULL, file); + + if (inSz == 0) { + WFCLOSE(NULL, file); + return -1; + } + + in = (byte*)WMALLOC(inSz, NULL, 0); + if (in == NULL) { + WFCLOSE(NULL, file); + return -1; + } + + ret = (int)WFREAD(NULL, in, 1, inSz, file); + if (ret <= 0 || (word32)ret != inSz) { + ret = -1; + WFREE(in, NULL, 0); + in = 0; + inSz = 0; + } + else + ret = 0; + + *out = in; + *outSz = inSz; + + WFCLOSE(NULL, file); + + return ret; +} + + +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME)) +static inline void ato32(const byte* c, word32* u32) +{ + *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; +} + +/* when set as true then ignore miss matching IP addresses */ +static int IPOverride = 0; + +static int ParseRFC6187(const byte* in, word32 inSz, byte** leafOut, + word32* leafOutSz) +{ + int ret = WS_SUCCESS; + word32 l = 0, m = 0; + + if (inSz < sizeof(word32)) { + printf("inSz %d too small for holding cert name\n", inSz); + return WS_BUFFER_E; + } + + /* Skip the name */ + ato32(in, &l); + m += l + sizeof(word32); + + /* Get the cert count */ + if (ret == WS_SUCCESS) { + word32 count; + + if (inSz - m < sizeof(word32)) + return WS_BUFFER_E; + + ato32(in + m, &count); + m += sizeof(word32); + if (ret == WS_SUCCESS && count == 0) + ret = WS_FATAL_ERROR; /* need at least one cert */ + } + + if (ret == WS_SUCCESS) { + word32 certSz = 0; + + if (inSz - m < sizeof(word32)) + return WS_BUFFER_E; + + ato32(in + m, &certSz); + m += sizeof(word32); + if (ret == WS_SUCCESS) { + /* store leaf cert size to present to user callback */ + *leafOutSz = certSz; + *leafOut = (byte*)in + m; + } + + if (inSz - m < certSz) + return WS_BUFFER_E; + + } + + return ret; +} + +void ClientIPOverride(int flag) +{ + IPOverride = flag; +} +#endif /* OPENSSL_ALL || WOLFSSL_IP_ALT_NAME */ +#endif /* WOLFSSH_CERTS */ + + +int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx) +{ + int ret = 0; + + #ifdef DEBUG_WOLFSSH + printf("Sample public key check callback\n" + " public key = %p\n" + " public key size = %u\n" + " ctx = %s\n", pubKey, pubKeySz, (const char*)ctx); + #else + (void)pubKey; + (void)pubKeySz; + (void)ctx; + #endif + +#ifdef WOLFSSH_CERTS +#if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) + /* try to parse the certificate and check it's IP address */ + if (pubKeySz > 0) { + DecodedCert dCert; + byte* der = NULL; + word32 derSz = 0; + + if (ParseRFC6187(pubKey, pubKeySz, &der, &derSz) == WS_SUCCESS) { + wc_InitDecodedCert(&dCert, der, derSz, NULL); + if (wc_ParseCert(&dCert, CERT_TYPE, NO_VERIFY, NULL) != 0) { + WLOG(WS_LOG_DEBUG, "public key not a cert"); + } + else { + int ipMatch = 0; + DNS_entry* current = dCert.altNames; + + if (ctx == NULL) { + WLOG(WS_LOG_ERROR, "No host IP set to check against!"); + ret = -1; + } + + if (ret == 0) { + while (current != NULL) { + if (current->type == ASN_IP_TYPE) { + WLOG(WS_LOG_DEBUG, "host cert alt. name IP : %s", + current->ipString); + WLOG(WS_LOG_DEBUG, + "\texpecting host IP : %s", (char*)ctx); + if (XSTRCMP(ctx, current->ipString) == 0) { + WLOG(WS_LOG_DEBUG, "\tmatched!"); + ipMatch = 1; + } + } + current = current->next; + } + } + + if (ipMatch == 0) { + printf("IP did not match expected IP"); + if (!IPOverride) { + printf("\n"); + ret = -1; + } + else { + ret = 0; + printf("..overriding\n"); + } + } + } + FreeDecodedCert(&dCert); + } + } +#else + WLOG(WS_LOG_DEBUG, "wolfSSL not built with OPENSSL_ALL or WOLFSSL_IP_ALT_NAME"); + WLOG(WS_LOG_DEBUG, "\tnot checking IP address from peer's cert"); +#endif +#endif + + return ret; +} + + +int ClientUserAuth(byte authType, + WS_UserAuthData* authData, + void* ctx) +{ + int ret = WOLFSSH_USERAUTH_SUCCESS; + +#ifdef DEBUG_WOLFSSH + /* inspect supported types from server */ + printf("Server supports:\n"); + if (authData->type & WOLFSSH_USERAUTH_PASSWORD) { + printf(" - password\n"); + } + if (authData->type & WOLFSSH_USERAUTH_PUBLICKEY) { + printf(" - publickey\n"); + } + printf("wolfSSH requesting to use type %d\n", authType); +#endif + + /* Wait for request of public key on names known to have one */ + if ((authData->type & WOLFSSH_USERAUTH_PUBLICKEY) && + authData->username != NULL && + authData->usernameSz > 0) { + + /* in the case that the user passed in a public key file, + * use public key auth */ + if (pubKeyLoaded == 1) { + if (authType == WOLFSSH_USERAUTH_PASSWORD) { + printf("rejecting password type with %s in favor of pub key\n", + (char*)authData->username); + return WOLFSSH_USERAUTH_FAILURE; + } + } + } + + if (authType == WOLFSSH_USERAUTH_PUBLICKEY) { + WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey; + + pk->publicKeyType = userPublicKeyType; + pk->publicKeyTypeSz = userPublicKeyTypeSz; + pk->publicKey = userPublicKey; + pk->publicKeySz = userPublicKeySz; + pk->privateKey = userPrivateKey; + pk->privateKeySz = userPrivateKeySz; + + ret = WOLFSSH_USERAUTH_SUCCESS; + } + else if (authType == WOLFSSH_USERAUTH_PASSWORD) { + const char* defaultPassword = (const char*)ctx; + word32 passwordSz = 0; + + if (defaultPassword != NULL) { + passwordSz = (word32)strlen(defaultPassword); + memcpy(userPassword, defaultPassword, passwordSz); + } + else { + printf("Password: "); + fflush(stdout); + ClientSetEcho(0); + if (fgets((char*)userPassword, sizeof(userPassword), stdin) == NULL) { + fprintf(stderr, "Getting password failed.\n"); + ret = WOLFSSH_USERAUTH_FAILURE; + } + else { + char* c = strpbrk((char*)userPassword, "\r\n"); + if (c != NULL) + *c = '\0'; + } + passwordSz = (word32)strlen((const char*)userPassword); + ClientSetEcho(1); + #ifdef USE_WINDOWS_API + printf("\r\n"); + #endif + fflush(stdout); + } + + if (ret == WOLFSSH_USERAUTH_SUCCESS) { + authData->sf.password.password = userPassword; + authData->sf.password.passwordSz = passwordSz; + } + } + + return ret; +} + + +/* type = 2 : shell / execute command settings + * type = 0 : password + * type = 1 : restore default + * return 0 on success */ +int ClientSetEcho(int type) +{ +#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) + static int echoInit = 0; + static struct termios originalTerm; + + if (!echoInit) { + if (tcgetattr(STDIN_FILENO, &originalTerm) != 0) { + printf("Couldn't get the original terminal settings.\n"); + return -1; + } + 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); + } + else { + newTerm.c_lflag |= (ICANON | ECHONL); + } + + if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } +#else + static int echoInit = 0; + static DWORD originalTerm; + static CONSOLE_SCREEN_BUFFER_INFO screenOrig; + HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); + if (!echoInit) { + if (GetConsoleMode(stdinHandle, &originalTerm) == 0) { + printf("Couldn't get the original terminal settings.\n"); + return -1; + } + echoInit = 1; + } + if (type == 1) { + if (SetConsoleMode(stdinHandle, originalTerm) == 0) { + printf("Couldn't restore the terminal settings.\n"); + return -1; + } + } + else if (type == 2) { + DWORD newTerm = originalTerm; + + newTerm &= ~ENABLE_PROCESSED_INPUT; + newTerm &= ~ENABLE_PROCESSED_OUTPUT; + newTerm &= ~ENABLE_LINE_INPUT; + newTerm &= ~ENABLE_ECHO_INPUT; + newTerm &= ~(ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE); + + if (SetConsoleMode(stdinHandle, newTerm) == 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } + else { + DWORD newTerm = originalTerm; + + newTerm &= ~ENABLE_ECHO_INPUT; + + if (SetConsoleMode(stdinHandle, newTerm) == 0) { + printf("Couldn't turn off echo.\n"); + return -1; + } + } +#endif + + return 0; +} + + +/* Set certificate to use and public key. + * returns 0 on success */ +int ClientUseCert(const char* certName) +{ + int ret = 0; + + if (certName != NULL) { + #ifdef WOLFSSH_CERTS + ret = load_der_file(certName, &userPublicKey, &userPublicKeySz); + if (ret == 0) { + userPublicKeyType = publicKeyType; + userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType); + pubKeyLoaded = 1; + } + #else + fprintf(stderr, "Certificate support not compiled in"); + ret = WS_NOT_COMPILED; + #endif + } + + return ret; +} + + +/* Reads the private key to use from file name privKeyName. + * returns 0 on success */ +int ClientSetPrivateKey(const char* privKeyName, int userEcc) +{ + int ret; + + if (privKeyName == NULL) { + if (userEcc) { + #ifndef WOLFSSH_NO_ECC + ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz, + WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, + &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); + #endif + } + else { + #ifndef WOLFSSH_NO_RSA + ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz, + WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, + &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); + #endif + } + isPrivate = 1; + } + else { + #ifndef NO_FILESYSTEM + userPrivateKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(privKeyName, + (byte**)&userPrivateKey, &userPrivateKeySz, + (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, + &isPrivate, NULL); + #else + printf("file system not compiled in!\n"); + ret = WC_NOT_COMPILED; + #endif + } + + return ret; +} + + +/* Set public key to use + * returns 0 on success */ +int ClientUsePubKey(const char* pubKeyName, int userEcc) +{ + int ret; + + if (pubKeyName == NULL) { + byte* p = userPublicKey; + userPublicKeySz = sizeof(userPublicKeyBuf); + + if (userEcc) { + #ifndef WOLFSSH_NO_ECC + ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc, + (word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH, + &p, &userPublicKeySz, + &userPublicKeyType, &userPublicKeyTypeSz, NULL); + #endif + } + else { + #ifndef WOLFSSH_NO_RSA + ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa, + (word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH, + &p, &userPublicKeySz, + &userPublicKeyType, &userPublicKeyTypeSz, NULL); + #endif + } + isPrivate = 1; + } + else { + #ifndef NO_FILESYSTEM + userPublicKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(pubKeyName, + &userPublicKey, &userPublicKeySz, + (const byte**)&userPublicKeyType, &userPublicKeyTypeSz, + &isPrivate, NULL); + #else + printf("file system not compiled in!\n"); + ret = -1; + #endif + if (ret == 0) { + pubKeyLoaded = 1; + } + } + + return ret; +} + +int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert) +{ + int ret = 0; + + /* CA certificate to verify host cert with */ + if (caCert) { + #ifdef WOLFSSH_CERTS + byte* der = NULL; + word32 derSz; + + ret = load_der_file(caCert, &der, &derSz); + if (ret == 0) { + if (wolfSSH_CTX_AddRootCert_buffer(ctx, der, derSz, + WOLFSSH_FORMAT_ASN1) != WS_SUCCESS) { + fprintf(stderr, "Couldn't parse in CA certificate."); + ret = WS_PARSE_E; + } + WFREE(der, NULL, 0); + } + #else + (void)ctx; + fprintf(stderr, "Support for certificates not compiled in."); + ret = WS_NOT_COMPILED; + #endif + } + return ret; +} + + +void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName) +{ + if (pubKeyName != NULL && userPublicKey != NULL) { + WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY); + } + + if (privKeyName != NULL && userPrivateKey != NULL) { + WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY); + } +} diff --git a/apps/wolfssh/common.h b/apps/wolfssh/common.h new file mode 100644 index 000000000..ff49b4d05 --- /dev/null +++ b/apps/wolfssh/common.h @@ -0,0 +1,36 @@ +/* common.h + * + * Copyright (C) 2014-2023 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#ifndef WOLFSSH_COMMON_H +#define WOLFSSH_COMMON_H +int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert); +int ClientUsePubKey(const char* pubKeyName, int userEcc); +int ClientSetPrivateKey(const char* privKeyName, int userEcc); +int ClientUseCert(const char* certName); +int ClientSetEcho(int type); +int ClientUserAuth(byte authType, + WS_UserAuthData* authData, + void* ctx); +int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx); +void ClientIPOverride(int flag); +void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName); + +#endif /* WOLFSSH_COMMON_H */ + diff --git a/apps/wolfssh/include.am b/apps/wolfssh/include.am index 8e726c469..716619004 100644 --- a/apps/wolfssh/include.am +++ b/apps/wolfssh/include.am @@ -1,7 +1,8 @@ if BUILD_SSHCLIENT bin_PROGRAMS += apps/wolfssh/wolfssh -apps_wolfssh_wolfssh_SOURCES = apps/wolfssh/wolfssh.c +apps_wolfssh_wolfssh_SOURCES = apps/wolfssh/wolfssh.c \ + apps/wolfssh/common.c apps/wolfssh/common.h apps_wolfssh_wolfssh_LDADD = src/libwolfssh.la apps_wolfssh_wolfssh_DEPENDENCIES = src/libwolfssh.la diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 76bfe1454..31638cecd 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -33,13 +33,13 @@ #include #include #include - #include #ifdef WOLFSSH_AGENT #include #endif #include #include "examples/client/client.h" +#include "apps/wolfssh/common.h" #if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) #include #endif @@ -57,7 +57,9 @@ #ifdef HAVE_TERMIOS_H #include #endif - #include + #ifndef USE_WINDOWS_API + #include + #endif #endif /* WOLFSSH_SHELL */ #ifdef WOLFSSH_AGENT @@ -80,94 +82,6 @@ int myoptind = 0; char* myoptarg = NULL; -/* type = 2 : shell / execute command settings - * type = 0 : password - * type = 1 : restore default - * return 0 on success */ -static int SetEcho(int type) -{ -#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) - static int echoInit = 0; - static struct termios originalTerm; - - if (!echoInit) { - if (tcgetattr(STDIN_FILENO, &originalTerm) != 0) { - printf("Couldn't get the original terminal settings.\n"); - return -1; - } - 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); - } - else { - newTerm.c_lflag |= (ICANON | ECHONL); - } - - if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { - printf("Couldn't turn off echo.\n"); - return -1; - } - } -#else - static int echoInit = 0; - static DWORD originalTerm; - static CONSOLE_SCREEN_BUFFER_INFO screenOrig; - HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); - if (!echoInit) { - if (GetConsoleMode(stdinHandle, &originalTerm) == 0) { - printf("Couldn't get the original terminal settings.\n"); - return -1; - } - echoInit = 1; - } - if (type == 1) { - if (SetConsoleMode(stdinHandle, originalTerm) == 0) { - printf("Couldn't restore the terminal settings.\n"); - return -1; - } - } - else if (type == 2) { - DWORD newTerm = originalTerm; - - newTerm &= ~ENABLE_PROCESSED_INPUT; - newTerm &= ~ENABLE_PROCESSED_OUTPUT; - newTerm &= ~ENABLE_LINE_INPUT; - newTerm &= ~ENABLE_ECHO_INPUT; - newTerm &= ~(ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE); - - if (SetConsoleMode(stdinHandle, newTerm) == 0) { - printf("Couldn't turn off echo.\n"); - return -1; - } - } - else { - DWORD newTerm = originalTerm; - - newTerm &= ~ENABLE_ECHO_INPUT; - - if (SetConsoleMode(stdinHandle, newTerm) == 0) { - printf("Couldn't turn off echo.\n"); - return -1; - } - } -#endif - - return 0; -} - - static void ShowUsage(char* appPath) { const char* appName; @@ -185,123 +99,12 @@ static void ShowUsage(char* appPath) } -static byte userPassword[256]; -static byte userPublicKeyBuf[512]; -static byte* userPublicKey = userPublicKeyBuf; -static const byte* userPublicKeyType = NULL; static const char* pubKeyName = NULL; static const char* certName = NULL; static const char* caCert = NULL; -static byte userPrivateKeyBuf[1191]; /* Size equal to hanselPrivateRsaSz. */ -static byte* userPrivateKey = userPrivateKeyBuf; -static const byte* userPrivateKeyType = NULL; -static word32 userPublicKeySz = 0; -static word32 userPublicKeyTypeSz = 0; -static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf); -static word32 userPrivateKeyTypeSz = 0; -static byte isPrivate = 0; - - -#ifdef WOLFSSH_CERTS -#if 0 -/* compiled in for using RSA certificates instead of ECC certificate */ -static const byte publicKeyType[] = "x509v3-ssh-rsa"; -static const byte privateKeyType[] = "ssh-rsa"; -#else -static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; -#endif -#endif - -static int wsUserAuth(byte authType, - WS_UserAuthData* authData, - void* ctx) -{ - int ret = WOLFSSH_USERAUTH_SUCCESS; - -#ifdef DEBUG_WOLFSSH - /* inspect supported types from server */ - printf("Server supports:\n"); - if (authData->type & WOLFSSH_USERAUTH_PASSWORD) { - printf(" - password\n"); - } - if (authData->type & WOLFSSH_USERAUTH_PUBLICKEY) { - printf(" - publickey\n"); - } - printf("wolfSSH requesting to use type %d\n", authType); -#endif - - /* Wait for request of public key on names known to have one */ - if ((authData->type & WOLFSSH_USERAUTH_PUBLICKEY) && - authData->username != NULL && - authData->usernameSz > 0) { - - /* in the case that the name is hansel or in the case that the user - * passed in a public key file, use public key auth */ - if ((XSTRNCMP((char*)authData->username, "hansel", - authData->usernameSz) == 0) || - pubKeyName != NULL || certName != NULL) { - - if (authType == WOLFSSH_USERAUTH_PASSWORD) { - printf("rejecting password type with %s in favor of pub key\n", - (char*)authData->username); - return WOLFSSH_USERAUTH_FAILURE; - } - } - } - - if (authType == WOLFSSH_USERAUTH_PUBLICKEY) { - WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey; - - pk->publicKeyType = userPublicKeyType; - pk->publicKeyTypeSz = userPublicKeyTypeSz; - pk->publicKey = userPublicKey; - pk->publicKeySz = userPublicKeySz; - pk->privateKey = userPrivateKey; - pk->privateKeySz = userPrivateKeySz; - - ret = WOLFSSH_USERAUTH_SUCCESS; - } - else if (authType == WOLFSSH_USERAUTH_PASSWORD) { - const char* defaultPassword = (const char*)ctx; - word32 passwordSz = 0; - - if (defaultPassword != NULL) { - passwordSz = (word32)strlen(defaultPassword); - memcpy(userPassword, defaultPassword, passwordSz); - } - else { - printf("Password: "); - fflush(stdout); - SetEcho(0); - if (fgets((char*)userPassword, sizeof(userPassword), stdin) == NULL) { - printf("Getting password failed.\n"); - ret = WOLFSSH_USERAUTH_FAILURE; - } - else { - char* c = strpbrk((char*)userPassword, "\r\n"); - if (c != NULL) - *c = '\0'; - } - passwordSz = (word32)strlen((const char*)userPassword); - SetEcho(1); - #ifdef USE_WINDOWS_API - printf("\r\n"); - #endif - } - - if (ret == WOLFSSH_USERAUTH_SUCCESS) { - authData->sf.password.password = userPassword; - authData->sf.password.passwordSz = passwordSz; - } - } - - return ret; -} -#if defined(WOLFSSH_AGENT) || \ - (defined(WOLFSSH_CERTS) && \ - (defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME))) +#if defined(WOLFSSH_AGENT) static inline void ato32(const byte* c, word32* u32) { *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; @@ -309,123 +112,6 @@ static inline void ato32(const byte* c, word32* u32) #endif -#if defined(WOLFSSH_CERTS) && \ - (defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME)) -static int ParseRFC6187(const byte* in, word32 inSz, byte** leafOut, - word32* leafOutSz) -{ - int ret = WS_SUCCESS; - word32 l = 0, m = 0; - - if (inSz < sizeof(word32)) { - printf("inSz %d too small for holding cert name\n", inSz); - return WS_BUFFER_E; - } - - /* Skip the name */ - ato32(in, &l); - m += l + sizeof(word32); - - /* Get the cert count */ - if (ret == WS_SUCCESS) { - word32 count; - - if (inSz - m < sizeof(word32)) - return WS_BUFFER_E; - - ato32(in + m, &count); - m += sizeof(word32); - if (ret == WS_SUCCESS && count == 0) - ret = WS_FATAL_ERROR; /* need at least one cert */ - } - - if (ret == WS_SUCCESS) { - word32 certSz = 0; - - if (inSz - m < sizeof(word32)) - return WS_BUFFER_E; - - ato32(in + m, &certSz); - m += sizeof(word32); - if (ret == WS_SUCCESS) { - /* store leaf cert size to present to user callback */ - *leafOutSz = certSz; - *leafOut = (byte*)in + m; - } - - if (inSz - m < certSz) - return WS_BUFFER_E; - - } - - return ret; -} -#endif /* WOLFSSH_CERTS */ - - -static int wsPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx) -{ - int ret = 0; - - #ifdef DEBUG_WOLFSSH - printf("Sample public key check callback\n" - " public key = %p\n" - " public key size = %u\n" - " ctx = %s\n", pubKey, pubKeySz, (const char*)ctx); - #else - (void)pubKey; - (void)pubKeySz; - (void)ctx; - #endif - -#ifdef WOLFSSH_CERTS -#if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) - /* try to parse the certificate and check it's IP address */ - if (pubKeySz > 0) { - DecodedCert dCert; - byte* der = NULL; - word32 derSz = 0; - - if (ParseRFC6187(pubKey, pubKeySz, &der, &derSz) == WS_SUCCESS) { - wc_InitDecodedCert(&dCert, der, derSz, NULL); - if (wc_ParseCert(&dCert, CERT_TYPE, NO_VERIFY, NULL) != 0) { - printf("public key not a cert\n"); - } - else { - int ipMatch = 0; - DNS_entry* current = dCert.altNames; - - while (current != NULL) { - if (current->type == ASN_IP_TYPE) { - printf("host cert alt. name IP : %s\n", - current->ipString); - printf("\texpecting host IP : %s\n", (char*)ctx); - if (XSTRCMP(ctx, current->ipString) == 0) { - printf("\tmatched!\n"); - ipMatch = 1; - } - } - current = current->next; - } - - if (ipMatch == 0) { - printf("IP did not match expected IP\n"); - ret = -1; - } - } - FreeDecodedCert(&dCert); - } - } -#else - printf("wolfSSL not built with OPENSSL_ALL or WOLFSSL_IP_ALT_NAME\n"); - printf("\tnot checking IP address from peer's cert\n"); -#endif -#endif - - return ret; -} - - static int NonBlockSSH_connect(WOLFSSH* ssh) { int ret; @@ -441,10 +127,17 @@ static int NonBlockSSH_connect(WOLFSSH* ssh) (error == WS_WANT_READ || error == WS_WANT_WRITE)) { select_ret = tcp_select(sockfd, 1); - if (select_ret == WS_SELECT_RECV_READY || + + /* Continue in want write cases even if did not select on socket + * because there could be pending data to be written. Added continue + * on want write for test cases where a forced want read was introduced + * and the socket will not be receiving more data. */ + if (error == WS_WANT_WRITE || error == WS_WANT_READ || + select_ret == WS_SELECT_RECV_READY || select_ret == WS_SELECT_ERROR_READY) { ret = wolfSSH_connect(ssh); + error = wolfSSH_get_error(ssh); } else if (select_ret == WS_SELECT_TIMEOUT) error = WS_WANT_READ; @@ -474,6 +167,110 @@ typedef struct thread_args { #define THREAD_RET_SUCCESS 0 #endif + +static int sendCurrentWindowSize(thread_args* args) +{ + int ret; + word32 col = 80, row = 24, xpix = 0, ypix = 0; + + wc_LockMutex(&args->lock); +#if defined(_MSC_VER) + { + CONSOLE_SCREEN_BUFFER_INFO cs; + + if (GetConsoleScreenBufferInfo( + GetStdHandle(STD_OUTPUT_HANDLE), &cs) != 0) { + col = cs.srWindow.Right - cs.srWindow.Left + 1; + row = cs.srWindow.Bottom - cs.srWindow.Top + 1; + } + } +#else + { + struct winsize windowSize = { 0,0,0,0 }; + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize); + col = windowSize.ws_col; + row = windowSize.ws_row; + xpix = windowSize.ws_xpixel; + ypix = windowSize.ws_ypixel; + } +#endif + ret = wolfSSH_ChangeTerminalSize(args->ssh, col, row, xpix, ypix); + wc_UnLockMutex(&args->lock); + + return ret; +} + + +#ifndef _MSC_VER + +#if (defined(__OSX__) || defined(__APPLE__)) +#include +dispatch_semaphore_t windowSem; +#else +#include +static sem_t windowSem; +#endif + +/* capture window change signales */ +static void WindowChangeSignal(int sig) +{ +#if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_signal(windowSem); +#else + sem_post(&windowSem); +#endif + (void)sig; +} + +/* thread for handling window size adjustments */ +static THREAD_RET windowMonitor(void* in) +{ + thread_args* args; + int ret; + + args = (thread_args*)in; + do { + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_wait(windowSem, DISPATCH_TIME_FOREVER); + #else + sem_wait(&windowSem); + #endif + ret = sendCurrentWindowSize(args); + (void)ret; + } while (1); + + return THREAD_RET_SUCCESS; +} +#else +/* no SIGWINCH on Windows, poll current terminal size */ +static word32 prevCol, prevRow; + +static int windowMonitor(thread_args* args) +{ + word32 row, col; + int ret = WS_SUCCESS; + CONSOLE_SCREEN_BUFFER_INFO cs; + + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cs) != 0) { + col = cs.srWindow.Right - cs.srWindow.Left + 1; + row = cs.srWindow.Bottom - cs.srWindow.Top + 1; + + if (prevCol != col || prevRow != row) { + prevCol = col; + prevRow = row; + + wc_LockMutex(&args->lock); + ret = wolfSSH_ChangeTerminalSize(args->ssh, col, row, 0, 0); + wc_UnLockMutex(&args->lock); + } + } + + return ret; +} +#endif + + static THREAD_RET readInput(void* in) { byte buf[256]; @@ -491,9 +288,19 @@ static THREAD_RET readInput(void* in) /* Using A version to avoid potential 2 byte chars */ ret = ReadConsoleA(stdinHandle, (void*)buf, bufSz - 1, (DWORD*)&sz, NULL); + (void)windowMonitor(args); #else ret = (int)read(STDIN_FILENO, buf, bufSz -1); sz = (word32)ret; + + /* add carriage returns for interop with windows server */ + if (ret > 0) { + if (ret == 1 && buf[0] == '\n') { + buf[1] = '\r'; + ret++; + sz++; + } + } #endif if (ret <= 0) { fprintf(stderr, "Error reading stdin\n"); @@ -517,7 +324,7 @@ static THREAD_RET readInput(void* in) static THREAD_RET readPeer(void* in) { - byte buf[80]; + byte buf[256]; int bufSz = sizeof(buf); thread_args* args = (thread_args*)in; int ret = 0; @@ -534,7 +341,18 @@ static THREAD_RET readPeer(void* in) FD_SET(fd, &readSet); FD_SET(fd, &errSet); +#ifdef USE_WINDOWS_API + /* set handle to use for window resize */ + wc_LockMutex(&args->lock); + wolfSSH_SetTerminalResizeCtx(args->ssh, stdoutHandle); + wc_UnLockMutex(&args->lock); +#endif + while (ret >= 0) { + #ifdef USE_WINDOWS_API + (void)windowMonitor(args); + #endif + bytes = select(fd + 1, &readSet, NULL, &errSet, NULL); wc_LockMutex(&args->lock); while (bytes > 0 && (FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet))) { @@ -548,7 +366,13 @@ static THREAD_RET readPeer(void* in) if (ret < 0) err_sys("Extended data read failed."); buf[bufSz - 1] = '\0'; + #ifdef USE_WINDOWS_API fprintf(stderr, "%s", buf); + #else + if (write(STDERR_FILENO, buf, ret) < 0) { + perror("Issue with stderr write "); + } + #endif } while (ret > 0); } else if (ret <= 0) { @@ -614,7 +438,9 @@ static THREAD_RET readPeer(void* in) fflush(stdout); } #else - printf("%s", buf); + if (write(STDOUT_FILENO, buf, ret) < 0) { + perror("write to stdout error "); + } fflush(stdout); #endif } @@ -634,78 +460,25 @@ static THREAD_RET readPeer(void* in) #endif /* !SINGLE_THREADED && !WOLFSSL_NUCLEUS */ -#ifdef WOLFSSH_CERTS - -static int load_der_file(const char* filename, byte** out, word32* outSz) -{ - WFILE* file; - byte* in; - word32 inSz; - int ret; - - if (filename == NULL || out == NULL || outSz == NULL) - return -1; - - ret = WFOPEN(NULL, &file, filename, "rb"); - if (ret != 0 || file == WBADFILE) - return -1; - - if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { - WFCLOSE(NULL, file); - return -1; - } - inSz = (word32)WFTELL(NULL, file); - WREWIND(NULL, file); - - if (inSz == 0) { - WFCLOSE(NULL, file); - return -1; - } - - in = (byte*)WMALLOC(inSz, NULL, 0); - if (in == NULL) { - WFCLOSE(NULL, file); - return -1; - } - - ret = (int)WFREAD(NULL, in, 1, inSz, file); - if (ret <= 0 || (word32)ret != inSz) { - ret = -1; - WFREE(in, NULL, 0); - in = 0; - inSz = 0; - } - else - ret = 0; - - *out = in; - *outSz = inSz; - - WFCLOSE(NULL, file); - - return ret; -} - -#endif /* WOLFSSH_CERTS */ - - #if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) -static int callbackGlobalReq(WOLFSSH *ssh, void *buf, word32 sz, int reply, void *ctx) +static int callbackGlobalReq(WOLFSSH *ssh, void *buf, word32 sz, + int reply, void *ctx) { char reqStr[] = "SampleRequest"; - if ((WOLFSSH *)ssh != *(WOLFSSH **)ctx) - { - printf("ssh(%x) != ctx(%x)\n", (unsigned int)ssh, (unsigned int)*(WOLFSSH **)ctx); + if ((WOLFSSH *)ssh != *(WOLFSSH **)ctx) { + printf("ssh(%p) != ctx(%p)\n", ssh, *(WOLFSSH **)ctx); return WS_FATAL_ERROR; } - if (strlen(reqStr) == sz && (strncmp((char *)buf, reqStr, sz) == 0) - && reply == 1){ + if (strlen(reqStr) == sz + && (strncmp((char *)buf, reqStr, sz) == 0) + && reply == 1) { printf("Global Request\n"); return WS_SUCCESS; - } else { + } + else { return WS_FATAL_ERROR; } @@ -1014,7 +787,9 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("client requires a username parameter."); #ifdef WOLFSSH_NO_RSA - userEcc = 1; + /* XXX This needs to go. + * Client should tell the key type based on the key. */ + //userEcc = 1; #endif #ifdef SINGLE_THREADED @@ -1026,84 +801,27 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("If setting priv key, need pub key."); } - if (privKeyName == NULL) { -// if (userEcc) { -// #ifndef WOLFSSH_NO_ECC -// ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz, -// WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, -// &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); -// #endif -// } -// else { -// #ifndef WOLFSSH_NO_RSA -// ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz, -// WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, -// &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); -// #endif -// } -// isPrivate = 1; -// if (ret != 0) err_sys("Couldn't load private key buffer."); - } - else { - #ifndef NO_FILESYSTEM - userPrivateKey = NULL; /* create new buffer based on parsed input */ - ret = wolfSSH_ReadKey_file(privKeyName, - (byte**)&userPrivateKey, &userPrivateKeySz, - (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, - &isPrivate, NULL); - #else - printf("file system not compiled in!\n"); - ret = -1; - #endif - if (ret != 0) err_sys("Couldn't load private key file."); + /* XXX Setting to zero instead of userEcc. Should be able to + * tell key type from the key. */ + ret = ClientSetPrivateKey(privKeyName, 0); + if (ret != 0) { + err_sys("Error setting private key"); } #ifdef WOLFSSH_CERTS /* passed in certificate to use */ if (certName) { - ret = load_der_file(certName, &userPublicKey, &userPublicKeySz); - if (ret != 0) err_sys("Couldn't load certificate file."); - - userPublicKeyType = publicKeyType; - userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType); + ret = ClientUseCert(certName); } else #endif - if (pubKeyName == NULL) { -// byte* p = userPublicKey; -// userPublicKeySz = sizeof(userPublicKeyBuf); -// -// if (userEcc) { -// #ifndef WOLFSSH_NO_ECC -// ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc, -// (word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH, -// &p, &userPublicKeySz, -// &userPublicKeyType, &userPublicKeyTypeSz, NULL); -// #endif -// } -// else { -// #ifndef WOLFSSH_NO_RSA -// ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa, -// (word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH, -// &p, &userPublicKeySz, -// &userPublicKeyType, &userPublicKeyTypeSz, NULL); -// #endif -// } -// isPrivate = 1; -// if (ret != 0) err_sys("Couldn't load public key buffer."); + /* XXX Setting to zero instead of userEcc. Should be able to + * tell key type from the key. */ + if (pubKeyName) { + ret = ClientUsePubKey(pubKeyName, 0); } - else { - #ifndef NO_FILESYSTEM - userPublicKey = NULL; /* create new buffer based on parsed input */ - ret = wolfSSH_ReadKey_file(pubKeyName, - &userPublicKey, &userPublicKeySz, - (const byte**)&userPublicKeyType, &userPublicKeyTypeSz, - &isPrivate, NULL); - #else - printf("file system not compiled in!\n"); - ret = -1; - #endif - if (ret != 0) err_sys("Couldn't load public key file."); + if (ret != 0) { + err_sys("Error setting public key"); } ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); @@ -1111,7 +829,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("Couldn't create wolfSSH client context."); if (((func_args*)args)->user_auth == NULL) - wolfSSH_SetUserAuth(ctx, wsUserAuth); + wolfSSH_SetUserAuth(ctx, ClientUserAuth); else wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth); @@ -1123,6 +841,14 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) } #endif +#ifdef WOLFSSH_CERTS + ClientLoadCA(ctx, caCert); +#else + (void)caCert; +#endif /* WOLFSSH_CERTS */ + + wolfSSH_CTX_SetPublicKeyCheck(ctx, ClientPublicKeyCheck); + ssh = wolfSSH_new(ctx); if (ssh == NULL) err_sys("Couldn't create wolfSSH session."); @@ -1142,26 +868,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) wolfSSH_set_agent_cb_ctx(ssh, &agentCbCtx); } #endif -#ifdef WOLFSSH_CERTS - /* CA certificate to verify host cert with */ - if (caCert) { - byte* der = NULL; - word32 derSz; - - ret = load_der_file(caCert, &der, &derSz); - if (ret != 0) err_sys("Couldn't load CA certificate file."); - if (wolfSSH_CTX_AddRootCert_buffer(ctx, der, derSz, - WOLFSSH_FORMAT_ASN1) != WS_SUCCESS) { - err_sys("Couldn't parse in CA certificate."); - } - WFREE(der, NULL, 0); - } - -#else - (void)caCert; -#endif /* WOLFSSH_CERTS */ - wolfSSH_CTX_SetPublicKeyCheck(ctx, wsPublicKeyCheck); wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)config.hostname); ret = wolfSSH_SetUsername(ssh, config.user); @@ -1189,7 +896,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) } #ifdef WOLFSSH_TERM - if (keepOpen && config.command == NULL) { + if (keepOpen) { ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_TERMINAL, NULL, 0); if (ret != WS_SUCCESS) err_sys("Couldn't set the terminal channel type."); @@ -1202,19 +909,45 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) if (keepOpen) /* set up for psuedo-terminal */ - SetEcho(2); + ClientSetEcho(2); if (config.command != NULL || keepOpen == 1) { #if defined(_POSIX_THREADS) thread_args arg; - pthread_t thread[2]; + pthread_t thread[3]; arg.ssh = ssh; wc_InitMutex(&arg.lock); - pthread_create(&thread[0], NULL, readInput, (void*)&arg); - pthread_create(&thread[1], NULL, readPeer, (void*)&arg); - pthread_join(thread[1], NULL); + #if (defined(__OSX__) || defined(__APPLE__)) + windowSem = dispatch_semaphore_create(0); + #else + sem_init(&windowSem, 0, 0); + #endif + + if (config.command) { + int err; + + /* exec command does not contain initial terminal size, + * unlike pty-req. Send an inital terminal size for recieving + * the results of the command */ + err = sendCurrentWindowSize(&arg); + if (err != WS_SUCCESS) { + fprintf(stderr, "Issue sending exec initial terminal size\n\r"); + } + } + + signal(SIGWINCH, WindowChangeSignal); + pthread_create(&thread[0], NULL, windowMonitor, (void*)&arg); + pthread_create(&thread[1], NULL, readInput, (void*)&arg); + pthread_create(&thread[2], NULL, readPeer, (void*)&arg); + pthread_join(thread[2], NULL); pthread_cancel(thread[0]); + pthread_cancel(thread[1]); + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_release(windowSem); + #else + sem_destroy(&windowSem); + #endif #elif defined(_MSC_VER) thread_args arg; HANDLE thread[2]; @@ -1222,6 +955,19 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) arg.ssh = ssh; arg.rawMode = rawMode; wc_InitMutex(&arg.lock); + + if (config.command) { + int err; + + /* exec command does not contain initial terminal size, + * unlike pty-req. Send an inital terminal size for recieving + * the results of the command */ + err = sendCurrentWindowSize(&arg); + if (err != WS_SUCCESS) { + fprintf(stderr, "Issue sending exec initial terminal size\n\r"); + } + } + thread[0] = CreateThread(NULL, 0, readInput, (void*)&arg, 0, 0); thread[1] = CreateThread(NULL, 0, readPeer, (void*)&arg, 0, 0); WaitForSingleObject(thread[1], INFINITE); @@ -1231,7 +977,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("No threading to use"); #endif if (keepOpen) - SetEcho(1); + ClientSetEcho(1); } // else #endif @@ -1264,26 +1010,24 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) //#endif // } ret = wolfSSH_shutdown(ssh); - if (ret != WS_SUCCESS) { - err_sys("Sending the shutdown messages failed."); - } - ret = wolfSSH_worker(ssh, NULL); - if (ret != WS_SUCCESS) { - err_sys("Failed to listen for close messages from the peer."); + /* do not continue on with shutdown process if peer already disconnected */ + if (ret != WS_SOCKET_ERROR_E + && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) { + if (ret != WS_SUCCESS) { + err_sys("Sending the shutdown messages failed."); + } + ret = wolfSSH_worker(ssh, NULL); + if (ret != WS_SUCCESS) { + err_sys("Failed to listen for close messages from the peer."); + } } WCLOSESOCKET(sockFd); wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); - if (ret != WS_SUCCESS) - err_sys("Closing client stream failed. Connection could have been closed by peer"); + if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) + err_sys("Closing client stream failed"); - if (pubKeyName != NULL && userPublicKey != NULL) { - WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY); - } - - if (privKeyName != NULL && userPrivateKey != NULL) { - WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY); - } + ClientFreeBuffers(pubKeyName, privKeyName); #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) wc_ecc_fp_free(); /* free per thread cache */ #endif From cdef5f69ad0c6326126a607288632b5c1d7bf55b Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 14 Sep 2023 14:13:08 -0700 Subject: [PATCH 07/16] wolfSSH Client 1. Allocating the user and hostname separately, makes updates from default to set value easier. 2. Set a default user name based and private key file name based on the environment. 3. Changed the visibility of the client common functions to WOLFSSL_LOCAL. 4. Removed the userEcc varible from the main loop, and from the client utility functions. Key type should be figured from the key. 5. Removed the hardcoded default keys for fake user Hansel. The key loading functions will error out if a file name isn't used. --- apps/wolfssh/common.c | 256 +++-------------------------------------- apps/wolfssh/common.h | 29 ++--- apps/wolfssh/wolfssh.c | 101 +++++++++++----- 3 files changed, 102 insertions(+), 284 deletions(-) diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c index 64c5a19d2..de1296171 100644 --- a/apps/wolfssh/common.c +++ b/apps/wolfssh/common.c @@ -31,7 +31,7 @@ #include #include #include -#include "examples/client/common.h" +#include "apps/wolfssh/common.h" #ifndef USE_WINDOWS_API #include #endif @@ -47,7 +47,7 @@ static byte userPassword[256]; static const byte* userPrivateKeyType = NULL; static word32 userPublicKeySz = 0; static byte pubKeyLoaded = 0; /* was a public key loaded */ -static byte userPrivateKeyBuf[1191]; /* Size equal to hanselPrivateRsaSz. */ +static byte userPrivateKeyBuf[1191]; static byte* userPrivateKey = userPrivateKeyBuf; static word32 userPublicKeyTypeSz = 0; static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf); @@ -66,176 +66,6 @@ static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; #endif - - -#ifndef WOLFSSH_NO_RSA -static const char* hanselPublicRsa = - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" - "MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G" - "p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj" - "nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW" - "NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE" - "nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel"; -static const byte hanselPrivateRsa[] = { - 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, - 0xbd, 0x3f, 0x76, 0x45, 0xa3, 0x03, 0xac, 0x38, 0xd5, 0xc7, 0x0f, 0x93, - 0x30, 0x5a, 0x20, 0x9c, 0x89, 0x7c, 0xad, 0x05, 0x16, 0x46, 0x86, 0x83, - 0x0d, 0x8a, 0x2b, 0x16, 0x4a, 0x05, 0x2c, 0xe4, 0x77, 0x47, 0x70, 0x00, - 0xae, 0x1d, 0x83, 0xe2, 0xd9, 0x6e, 0x99, 0xd4, 0xf0, 0x45, 0x98, 0x15, - 0x93, 0xf6, 0x87, 0x4e, 0xac, 0x64, 0x63, 0xa1, 0x95, 0xc9, 0x7c, 0x30, - 0xe8, 0x3e, 0x2f, 0xa3, 0xf1, 0x24, 0x9f, 0x0c, 0x6b, 0x1c, 0xfe, 0x1b, - 0x02, 0x99, 0xcd, 0xc6, 0xa7, 0x6c, 0x84, 0x85, 0x46, 0x54, 0x12, 0x40, - 0xe1, 0xb4, 0xe5, 0xf2, 0xaa, 0x39, 0xec, 0xd6, 0x27, 0x24, 0x0b, 0xd1, - 0xa1, 0xe2, 0xef, 0x34, 0x69, 0x25, 0x6d, 0xc0, 0x74, 0x67, 0x25, 0x98, - 0x7d, 0xc4, 0xf8, 0x52, 0xab, 0x9b, 0x4b, 0x3a, 0x12, 0x1d, 0xe1, 0xe3, - 0xfa, 0xd6, 0xcf, 0x9a, 0xe6, 0x9c, 0x23, 0x4e, 0x39, 0xc4, 0x84, 0x16, - 0x88, 0x3d, 0x42, 0x4e, 0xd8, 0x2f, 0xcc, 0xd2, 0x91, 0x67, 0x9d, 0xb6, - 0x71, 0x2a, 0x02, 0x65, 0x5f, 0xbb, 0x75, 0x0e, 0x8c, 0xbb, 0x87, 0x97, - 0x97, 0xc6, 0xf8, 0xb2, 0x98, 0xe2, 0x2f, 0x68, 0x26, 0x4a, 0x53, 0xec, - 0x79, 0x3a, 0x8a, 0x5f, 0xcc, 0xcf, 0xf0, 0x16, 0x47, 0xb2, 0xd0, 0x43, - 0xd6, 0x36, 0x6c, 0xc8, 0xe7, 0x2f, 0xfe, 0xa7, 0x35, 0x39, 0x69, 0xfb, - 0x1d, 0x78, 0x45, 0x9d, 0x89, 0x00, 0xc8, 0x41, 0xcf, 0x34, 0x1f, 0xa3, - 0xf3, 0xf1, 0xfb, 0x28, 0x14, 0xfb, 0xd8, 0x48, 0x6f, 0xac, 0xe3, 0xfc, - 0x33, 0xd1, 0xdb, 0xae, 0xef, 0x27, 0x9e, 0x57, 0x56, 0x29, 0xa2, 0x1a, - 0x3a, 0xe5, 0x9a, 0xfe, 0xa4, 0x49, 0xc8, 0x7f, 0xb7, 0x4e, 0xd0, 0x1f, - 0x04, 0x6e, 0x58, 0x16, 0xb7, 0xeb, 0x9d, 0xf8, 0x92, 0x3c, 0xc2, 0xb0, - 0x21, 0x7c, 0x4e, 0x31, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, - 0x01, 0x00, 0x8d, 0xa4, 0x61, 0x06, 0x2f, 0xc3, 0x40, 0xf4, 0x6c, 0xf4, - 0x87, 0x30, 0xb8, 0x00, 0xcc, 0xe5, 0xbc, 0x75, 0x87, 0x1e, 0x06, 0x95, - 0x14, 0x7a, 0x23, 0xf9, 0x24, 0xd4, 0x92, 0xe4, 0x1a, 0xbc, 0x88, 0x95, - 0xfc, 0x3b, 0x56, 0x16, 0x1b, 0x2e, 0xff, 0x64, 0x2b, 0x58, 0xd7, 0xd8, - 0x8e, 0xc2, 0x9f, 0xb2, 0xe5, 0x84, 0xb9, 0xbc, 0x8d, 0x61, 0x54, 0x35, - 0xb0, 0x70, 0xfe, 0x72, 0x04, 0xc0, 0x24, 0x6d, 0x2f, 0x69, 0x61, 0x06, - 0x1b, 0x1d, 0xe6, 0x2d, 0x6d, 0x79, 0x60, 0xb7, 0xf4, 0xdb, 0xb7, 0x4e, - 0x97, 0x36, 0xde, 0x77, 0xc1, 0x9f, 0x85, 0x4e, 0xc3, 0x77, 0x69, 0x66, - 0x2e, 0x3e, 0x61, 0x76, 0xf3, 0x67, 0xfb, 0xc6, 0x9a, 0xc5, 0x6f, 0x99, - 0xff, 0xe6, 0x89, 0x43, 0x92, 0x44, 0x75, 0xd2, 0x4e, 0x54, 0x91, 0x58, - 0xb2, 0x48, 0x2a, 0xe6, 0xfa, 0x0d, 0x4a, 0xca, 0xd4, 0x14, 0x9e, 0xf6, - 0x27, 0x67, 0xb7, 0x25, 0x7a, 0x43, 0xbb, 0x2b, 0x67, 0xd1, 0xfe, 0xd1, - 0x68, 0x23, 0x06, 0x30, 0x7c, 0xbf, 0x60, 0x49, 0xde, 0xcc, 0x7e, 0x26, - 0x5a, 0x3b, 0xfe, 0xa6, 0xa6, 0xe7, 0xa8, 0xdd, 0xac, 0xb9, 0xaf, 0x82, - 0x9a, 0x3a, 0x41, 0x7e, 0x61, 0x21, 0x37, 0xa3, 0x08, 0xe4, 0xc4, 0xbc, - 0x11, 0xf5, 0x3b, 0x8e, 0x4d, 0x51, 0xf3, 0xbd, 0xda, 0xba, 0xb2, 0xc5, - 0xee, 0xfb, 0xcf, 0xdf, 0x83, 0xa1, 0x82, 0x01, 0xe1, 0x51, 0x9d, 0x07, - 0x5a, 0x5d, 0xd8, 0xc7, 0x5b, 0x3f, 0x97, 0x13, 0x6a, 0x4d, 0x1e, 0x8d, - 0x39, 0xac, 0x40, 0x95, 0x82, 0x6c, 0xa2, 0xa1, 0xcc, 0x8a, 0x9b, 0x21, - 0x32, 0x3a, 0x58, 0xcc, 0xe7, 0x2d, 0x1a, 0x79, 0xa4, 0x31, 0x50, 0xb1, - 0x4b, 0x76, 0x23, 0x1b, 0xb3, 0x40, 0x3d, 0x3d, 0x72, 0x72, 0x32, 0xec, - 0x5f, 0x38, 0xb5, 0x8d, 0xb2, 0x8d, 0x02, 0x81, 0x81, 0x00, 0xed, 0x5a, - 0x7e, 0x8e, 0xa1, 0x62, 0x7d, 0x26, 0x5c, 0x78, 0xc4, 0x87, 0x71, 0xc9, - 0x41, 0x57, 0x77, 0x94, 0x93, 0x93, 0x26, 0x78, 0xc8, 0xa3, 0x15, 0xbd, - 0x59, 0xcb, 0x1b, 0xb4, 0xb2, 0x6b, 0x0f, 0xe7, 0x80, 0xf2, 0xfa, 0xfc, - 0x8e, 0x32, 0xa9, 0x1b, 0x1e, 0x7f, 0xe1, 0x26, 0xef, 0x00, 0x25, 0xd8, - 0xdd, 0xc9, 0x1a, 0x23, 0x00, 0x26, 0x3b, 0x46, 0x23, 0xc0, 0x50, 0xe7, - 0xce, 0x62, 0xb2, 0x36, 0xb2, 0x98, 0x09, 0x16, 0x34, 0x18, 0x9e, 0x46, - 0xbc, 0xaf, 0x2c, 0x28, 0x94, 0x2f, 0xe0, 0x5d, 0xc9, 0xb2, 0xc8, 0xfb, - 0x5d, 0x13, 0xd5, 0x36, 0xaa, 0x15, 0x0f, 0x89, 0xa5, 0x16, 0x59, 0x5d, - 0x22, 0x74, 0xa4, 0x47, 0x5d, 0xfa, 0xfb, 0x0c, 0x5e, 0x80, 0xbf, 0x0f, - 0xc2, 0x9c, 0x95, 0x0f, 0xe7, 0xaa, 0x7f, 0x16, 0x1b, 0xd4, 0xdb, 0x38, - 0x7d, 0x58, 0x2e, 0x57, 0x78, 0x2f, 0x02, 0x81, 0x81, 0x00, 0xcc, 0x1d, - 0x7f, 0x74, 0x36, 0x6d, 0xb4, 0x92, 0x25, 0x62, 0xc5, 0x50, 0xb0, 0x5c, - 0xa1, 0xda, 0xf3, 0xb2, 0xfd, 0x1e, 0x98, 0x0d, 0x8b, 0x05, 0x69, 0x60, - 0x8e, 0x5e, 0xd2, 0x89, 0x90, 0x4a, 0x0d, 0x46, 0x7e, 0xe2, 0x54, 0x69, - 0xae, 0x16, 0xe6, 0xcb, 0xd5, 0xbd, 0x7b, 0x30, 0x2b, 0x7b, 0x5c, 0xee, - 0x93, 0x12, 0xcf, 0x63, 0x89, 0x9c, 0x3d, 0xc8, 0x2d, 0xe4, 0x7a, 0x61, - 0x09, 0x5e, 0x80, 0xfb, 0x3c, 0x03, 0xb3, 0x73, 0xd6, 0x98, 0xd0, 0x84, - 0x0c, 0x59, 0x9f, 0x4e, 0x80, 0xf3, 0x46, 0xed, 0x03, 0x9d, 0xd5, 0xdc, - 0x8b, 0xe7, 0xb1, 0xe8, 0xaa, 0x57, 0xdc, 0xd1, 0x41, 0x55, 0x07, 0xc7, - 0xdf, 0x67, 0x3c, 0x72, 0x78, 0xb0, 0x60, 0x8f, 0x85, 0xa1, 0x90, 0x99, - 0x0c, 0xa5, 0x67, 0xab, 0xf0, 0xb6, 0x74, 0x90, 0x03, 0x55, 0x7b, 0x5e, - 0xcc, 0xc5, 0xbf, 0xde, 0xa7, 0x9f, 0x02, 0x81, 0x80, 0x40, 0x81, 0x6e, - 0x91, 0xae, 0xd4, 0x88, 0x74, 0xab, 0x7e, 0xfa, 0xd2, 0x60, 0x9f, 0x34, - 0x8d, 0xe3, 0xe6, 0xd2, 0x30, 0x94, 0xad, 0x10, 0xc2, 0x19, 0xbf, 0x6b, - 0x2e, 0xe2, 0xe9, 0xb9, 0xef, 0x94, 0xd3, 0xf2, 0xdc, 0x96, 0x4f, 0x9b, - 0x09, 0xb3, 0xa1, 0xb6, 0x29, 0x44, 0xf4, 0x82, 0xd1, 0xc4, 0x77, 0x6a, - 0xd7, 0x23, 0xae, 0x4d, 0x75, 0x16, 0x78, 0xda, 0x70, 0x82, 0xcc, 0x6c, - 0xef, 0xaf, 0xc5, 0x63, 0xc6, 0x23, 0xfa, 0x0f, 0xd0, 0x7c, 0xfb, 0x76, - 0x7e, 0x18, 0xff, 0x32, 0x3e, 0xcc, 0xb8, 0x50, 0x7f, 0xb1, 0x55, 0x77, - 0x17, 0x53, 0xc3, 0xd6, 0x77, 0x80, 0xd0, 0x84, 0xb8, 0x4d, 0x33, 0x1d, - 0x91, 0x1b, 0xb0, 0x75, 0x9f, 0x27, 0x29, 0x56, 0x69, 0xa1, 0x03, 0x54, - 0x7d, 0x9f, 0x99, 0x41, 0xf9, 0xb9, 0x2e, 0x36, 0x04, 0x24, 0x4b, 0xf6, - 0xec, 0xc7, 0x33, 0x68, 0x6b, 0x02, 0x81, 0x80, 0x60, 0x35, 0xcb, 0x3c, - 0xd0, 0xe6, 0xf7, 0x05, 0x28, 0x20, 0x1d, 0x57, 0x82, 0x39, 0xb7, 0x85, - 0x07, 0xf7, 0xa7, 0x3d, 0xc3, 0x78, 0x26, 0xbe, 0x3f, 0x44, 0x66, 0xf7, - 0x25, 0x0f, 0xf8, 0x76, 0x1f, 0x39, 0xca, 0x57, 0x0e, 0x68, 0xdd, 0xc9, - 0x27, 0xb2, 0x8e, 0xa6, 0x08, 0xa9, 0xd4, 0xe5, 0x0a, 0x11, 0xde, 0x3b, - 0x30, 0x8b, 0xff, 0x72, 0x28, 0xe0, 0xf1, 0x58, 0xcf, 0xa2, 0x6b, 0x93, - 0x23, 0x02, 0xc8, 0xf0, 0x09, 0xa7, 0x21, 0x50, 0xd8, 0x80, 0x55, 0x7d, - 0xed, 0x0c, 0x48, 0xd5, 0xe2, 0xe9, 0x97, 0x19, 0xcf, 0x93, 0x6c, 0x52, - 0xa2, 0xd6, 0x43, 0x6c, 0xb4, 0xc5, 0xe1, 0xa0, 0x9d, 0xd1, 0x45, 0x69, - 0x58, 0xe1, 0xb0, 0x27, 0x9a, 0xec, 0x2b, 0x95, 0xd3, 0x1d, 0x81, 0x0b, - 0x7a, 0x09, 0x5e, 0xa5, 0xf1, 0xdd, 0x6b, 0xe4, 0xe0, 0x08, 0xf8, 0x46, - 0x81, 0xc1, 0x06, 0x8b, 0x02, 0x81, 0x80, 0x00, 0xf6, 0xf2, 0xeb, 0x25, - 0xba, 0x78, 0x04, 0xad, 0x0e, 0x0d, 0x2e, 0xa7, 0x69, 0xd6, 0x57, 0xe6, - 0x36, 0x32, 0x50, 0xd2, 0xf2, 0xeb, 0xad, 0x31, 0x46, 0x65, 0xc0, 0x07, - 0x97, 0x83, 0x6c, 0x66, 0x27, 0x3e, 0x94, 0x2c, 0x05, 0x01, 0x5f, 0x5c, - 0xe0, 0x31, 0x30, 0xec, 0x61, 0xd2, 0x74, 0x35, 0xb7, 0x9f, 0x38, 0xe7, - 0x8e, 0x67, 0xb1, 0x50, 0x08, 0x68, 0xce, 0xcf, 0xd8, 0xee, 0x88, 0xfd, - 0x5d, 0xc4, 0xcd, 0xe2, 0x86, 0x3d, 0x4a, 0x0e, 0x04, 0x7f, 0xee, 0x8a, - 0xe8, 0x9b, 0x16, 0xa1, 0xfc, 0x09, 0x82, 0xe2, 0x62, 0x03, 0x3c, 0xe8, - 0x25, 0x7f, 0x3c, 0x9a, 0xaa, 0x83, 0xf8, 0xd8, 0x93, 0xd1, 0x54, 0xf9, - 0xce, 0xb4, 0xfa, 0x35, 0x36, 0xcc, 0x18, 0x54, 0xaa, 0xf2, 0x90, 0xb7, - 0x7c, 0x97, 0x0b, 0x27, 0x2f, 0xae, 0xfc, 0xc3, 0x93, 0xaf, 0x1a, 0x75, - 0xec, 0x18, 0xdb -}; -static const unsigned int hanselPrivateRsaSz = 1191; -#endif - - -#ifndef WOLFSSH_NO_ECC -#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 -static const char* hanselPublicEcc = - "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA" - "BBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25" - "qUzgDtH7oyaQROUnNvk= hansel"; -static const byte hanselPrivateEcc[] = { - 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x03, 0x6e, 0x17, 0xd3, 0xb9, - 0xb8, 0xab, 0xc8, 0xf9, 0x1f, 0xf1, 0x2d, 0x44, 0x4c, 0x3b, 0x12, 0xb1, - 0xa4, 0x77, 0xd8, 0xed, 0x0e, 0x6a, 0xbe, 0x60, 0xc2, 0xf6, 0x8b, 0xe7, - 0xd3, 0x87, 0x83, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, - 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xd9, 0x08, 0xe4, - 0x94, 0xcf, 0xe8, 0x3d, 0x25, 0x17, 0x8d, 0xad, 0x6f, 0x15, 0xf5, 0xf5, - 0xc1, 0x3c, 0xee, 0x1c, 0xed, 0x51, 0x2e, 0x85, 0x48, 0x3a, 0x06, 0xbc, - 0xf7, 0xe2, 0x53, 0x40, 0xa0, 0x78, 0xd4, 0x9b, 0x23, 0xe6, 0x85, 0x74, - 0xa2, 0x33, 0x4c, 0xfe, 0x42, 0x40, 0x42, 0x52, 0xbe, 0x6d, 0xb9, 0xa9, - 0x4c, 0xe0, 0x0e, 0xd1, 0xfb, 0xa3, 0x26, 0x90, 0x44, 0xe5, 0x27, 0x36, - 0xf9 -}; -static const unsigned int hanselPrivateEccSz = 121; -#elif !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) -static const char* hanselPublicEcc = - "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAA" - "CFBAET/BOzBb9Jx9b52VIHFP4g/uk5KceDpz2M+/Ln9WiDjsMfb4NgNCAB+EMNJUX/TNBL" - "FFmqr7c6+zUH+QAo2qstvQDsReyFkETRB2vZD//nCZfcAe0RMtKZmgtQLKXzSlimUjXBM4" - "/zE5lwE05aXADp88h8nuaT/X4bll9cWJlH0fUykA== hansel"; -static const byte hanselPrivateEcc[] = { - 0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42, 0x01, 0x79, 0x40, 0xb8, - 0x33, 0xe5, 0x53, 0x5b, 0x9e, 0xfd, 0xed, 0xbe, 0x7c, 0x68, 0xe4, 0xb6, - 0xc3, 0x50, 0x00, 0x0d, 0x39, 0x64, 0x05, 0xf6, 0x5a, 0x5d, 0x41, 0xab, - 0xb3, 0xd9, 0xa7, 0xcb, 0x1c, 0x7d, 0x34, 0x46, 0x5c, 0x2d, 0x56, 0x26, - 0xa0, 0x6a, 0xc7, 0x3d, 0x4f, 0x78, 0x58, 0x14, 0x66, 0x6c, 0xfc, 0x86, - 0x3c, 0x8b, 0x5b, 0x54, 0x29, 0x89, 0x93, 0x48, 0xd9, 0x54, 0x8b, 0xbe, - 0x9d, 0x91, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0xa1, - 0x81, 0x89, 0x03, 0x81, 0x86, 0x00, 0x04, 0x01, 0x13, 0xfc, 0x13, 0xb3, - 0x05, 0xbf, 0x49, 0xc7, 0xd6, 0xf9, 0xd9, 0x52, 0x07, 0x14, 0xfe, 0x20, - 0xfe, 0xe9, 0x39, 0x29, 0xc7, 0x83, 0xa7, 0x3d, 0x8c, 0xfb, 0xf2, 0xe7, - 0xf5, 0x68, 0x83, 0x8e, 0xc3, 0x1f, 0x6f, 0x83, 0x60, 0x34, 0x20, 0x01, - 0xf8, 0x43, 0x0d, 0x25, 0x45, 0xff, 0x4c, 0xd0, 0x4b, 0x14, 0x59, 0xaa, - 0xaf, 0xb7, 0x3a, 0xfb, 0x35, 0x07, 0xf9, 0x00, 0x28, 0xda, 0xab, 0x2d, - 0xbd, 0x00, 0xec, 0x45, 0xec, 0x85, 0x90, 0x44, 0xd1, 0x07, 0x6b, 0xd9, - 0x0f, 0xff, 0xe7, 0x09, 0x97, 0xdc, 0x01, 0xed, 0x11, 0x32, 0xd2, 0x99, - 0x9a, 0x0b, 0x50, 0x2c, 0xa5, 0xf3, 0x4a, 0x58, 0xa6, 0x52, 0x35, 0xc1, - 0x33, 0x8f, 0xf3, 0x13, 0x99, 0x70, 0x13, 0x4e, 0x5a, 0x5c, 0x00, 0xe9, - 0xf3, 0xc8, 0x7c, 0x9e, 0xe6, 0x93, 0xfd, 0x7e, 0x1b, 0x96, 0x5f, 0x5c, - 0x58, 0x99, 0x47, 0xd1, 0xf5, 0x32, 0x90 -}; -static const unsigned int hanselPrivateEccSz = 223; -#else - #error "Enable an ECC Curve or disable ECC." -#endif -#endif - - #if defined(WOLFSSH_CERTS) static int load_der_file(const char* filename, byte** out, word32* outSz) @@ -633,39 +463,15 @@ int ClientUseCert(const char* certName) /* Reads the private key to use from file name privKeyName. * returns 0 on success */ -int ClientSetPrivateKey(const char* privKeyName, int userEcc) +int ClientSetPrivateKey(const char* privKeyName) { int ret; - if (privKeyName == NULL) { - if (userEcc) { - #ifndef WOLFSSH_NO_ECC - ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz, - WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, - &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); - #endif - } - else { - #ifndef WOLFSSH_NO_RSA - ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz, - WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, - &userPrivateKeyType, &userPrivateKeyTypeSz, NULL); - #endif - } - isPrivate = 1; - } - else { - #ifndef NO_FILESYSTEM - userPrivateKey = NULL; /* create new buffer based on parsed input */ - ret = wolfSSH_ReadKey_file(privKeyName, - (byte**)&userPrivateKey, &userPrivateKeySz, - (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, - &isPrivate, NULL); - #else - printf("file system not compiled in!\n"); - ret = WC_NOT_COMPILED; - #endif - } + userPrivateKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(privKeyName, + (byte**)&userPrivateKey, &userPrivateKeySz, + (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, + &isPrivate, NULL); return ret; } @@ -673,46 +479,18 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc) /* Set public key to use * returns 0 on success */ -int ClientUsePubKey(const char* pubKeyName, int userEcc) +int ClientUsePubKey(const char* pubKeyName) { int ret; - if (pubKeyName == NULL) { - byte* p = userPublicKey; - userPublicKeySz = sizeof(userPublicKeyBuf); - - if (userEcc) { - #ifndef WOLFSSH_NO_ECC - ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc, - (word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH, - &p, &userPublicKeySz, - &userPublicKeyType, &userPublicKeyTypeSz, NULL); - #endif - } - else { - #ifndef WOLFSSH_NO_RSA - ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa, - (word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH, - &p, &userPublicKeySz, - &userPublicKeyType, &userPublicKeyTypeSz, NULL); - #endif - } - isPrivate = 1; - } - else { - #ifndef NO_FILESYSTEM - userPublicKey = NULL; /* create new buffer based on parsed input */ - ret = wolfSSH_ReadKey_file(pubKeyName, - &userPublicKey, &userPublicKeySz, - (const byte**)&userPublicKeyType, &userPublicKeyTypeSz, - &isPrivate, NULL); - #else - printf("file system not compiled in!\n"); - ret = -1; - #endif - if (ret == 0) { - pubKeyLoaded = 1; - } + userPublicKey = NULL; /* create new buffer based on parsed input */ + ret = wolfSSH_ReadKey_file(pubKeyName, + &userPublicKey, &userPublicKeySz, + (const byte**)&userPublicKeyType, &userPublicKeyTypeSz, + &isPrivate, NULL); + + if (ret == 0) { + pubKeyLoaded = 1; } return ret; diff --git a/apps/wolfssh/common.h b/apps/wolfssh/common.h index ff49b4d05..5eb510cce 100644 --- a/apps/wolfssh/common.h +++ b/apps/wolfssh/common.h @@ -18,19 +18,20 @@ * along with wolfSSH. If not, see . */ -#ifndef WOLFSSH_COMMON_H -#define WOLFSSH_COMMON_H -int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert); -int ClientUsePubKey(const char* pubKeyName, int userEcc); -int ClientSetPrivateKey(const char* privKeyName, int userEcc); -int ClientUseCert(const char* certName); -int ClientSetEcho(int type); -int ClientUserAuth(byte authType, - WS_UserAuthData* authData, - void* ctx); -int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx); -void ClientIPOverride(int flag); -void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName); +#ifndef APPS_WOLFSSH_COMMON_H +#define APPS_WOLFSSH_COMMON_H -#endif /* WOLFSSH_COMMON_H */ +WOLFSSH_LOCAL int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert); +WOLFSSH_LOCAL int ClientUsePubKey(const char* pubKeyName); +WOLFSSH_LOCAL int ClientSetPrivateKey(const char* privKeyName); +WOLFSSH_LOCAL int ClientUseCert(const char* certName); +WOLFSSH_LOCAL int ClientSetEcho(int type); +WOLFSSH_LOCAL int ClientUserAuth(byte authType, WS_UserAuthData* authData, + void* ctx); +WOLFSSH_LOCAL int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, + void* ctx); +WOLFSSH_LOCAL void ClientIPOverride(int flag); +WOLFSSH_LOCAL void ClientFreeBuffers(const char* pubKeyName, + const char* privKeyName); +#endif /* APPS_WOLFSSH_COMMON_H */ diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 31638cecd..9c280a1f7 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -586,25 +586,51 @@ static int wolfSSH_AGENT_IO_Cb(WS_AgentIoCbAction action, struct config { - const char* logFile; - const char* user; - const char* hostname; + char* logFile; + char* user; + char* hostname; + char* keyFile; char* command; - char* user_host_buffer; word32 printConfig:1; word32 noCommand:1; word16 port; }; -/* Note: user is either pointing to an argv argument, the value of USER in - * the env, or to the username in the user_host_buffer. hostname will be - * pointing into user_host_buffer. */ static int config_init_default(struct config* config) { + char* env; + size_t sz; + memset(config, 0, sizeof(*config)); config->port = 22; + env = getenv("USER"); + if (env != NULL) { + char* user; + + sz = strlen(env + 1); + user = (char*)malloc(sz); + if (user != NULL) { + strcpy(user, env); + config->user = user; + } + } + + env = getenv("HOME"); + if (env != NULL) { + const char* defaultName = "/.ssh/id_ecdsa"; + char* keyFile; + + sz = strlen(env) + strlen(defaultName) + 1; + keyFile = (char*)malloc(sz); + if (keyFile != NULL) { + strcpy(keyFile, env); + strcat(keyFile, defaultName); + config->keyFile = keyFile; + } + } + return 0; } @@ -653,6 +679,7 @@ static int config_parse_command_line(struct config* config, * - ssh://[user@]hostname[:port] */ if (myoptind < argc) { const char* uriPrefix = "ssh://"; + char* dest; char* cursor; char* found; size_t sz; @@ -661,9 +688,9 @@ static int config_parse_command_line(struct config* config, myoptarg = argv[myoptind]; sz = strlen(myoptarg) + 1; - cursor = (char*)malloc(sz); - memcpy(cursor, myoptarg, sz); - config->user_host_buffer = cursor; + dest = (char*)malloc(sz); + memcpy(dest, myoptarg, sz); + cursor = dest; if (strstr(cursor, uriPrefix)) { checkPort = 1; @@ -674,25 +701,40 @@ static int config_parse_command_line(struct config* config, } found = strchr(cursor, '@'); + if (found == cursor) { + fprintf(stderr, "can't start destination with just an @\n"); + } if (found != NULL) { *found = '\0'; - config->user = cursor; + if (config->user) { + free(config->user); + } + sz = strlen(cursor); + config->user = malloc(sz + 1); + strcpy(config->user, cursor); cursor = found + 1; } - else { - config->user = NULL; - } - config->hostname = cursor; if (checkPort) { found = strchr(cursor, ':'); if (found != NULL) { *found = '\0'; + sz = strlen(cursor); + config->hostname = (char*)malloc(sz); + strcpy(config->hostname, cursor); cursor = found + 1; - config->port = atoi(cursor); + if (*cursor != 0) { + config->port = atoi(cursor); + } } } + else { + sz = strlen(cursor); + config->hostname = (char*)malloc(sz); + strcpy(config->hostname, cursor); + } + free(dest); myoptind++; } @@ -731,6 +773,7 @@ static int config_print(struct config* config) printf("user %s\n", config->user ? config->user : "none"); printf("hostname %s\n", config->hostname ? config->hostname : "none"); printf("port %u\n", config->port); + printf("keyFile %s\n", config->keyFile ? config->keyFile : "none"); printf("noCommand %s\n", config->noCommand ? "true" : "false"); printf("logfile %s\n", config->logFile ? config->logFile : "default"); printf("command %s\n", config->command ? config->command : "none"); @@ -742,8 +785,14 @@ static int config_print(struct config* config) static int config_cleanup(struct config* config) { - if (config->user_host_buffer) { - free(config->user_host_buffer); + if (config->user) { + free(config->user); + } + if (config->hostname) { + free(config->hostname); + } + if (config->keyFile) { + free(config->keyFile); } if (config->command) { free(config->command); @@ -762,7 +811,6 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) socklen_t clientAddrSz = sizeof(clientAddr); //char rxBuf[80]; int ret = 0; - //int userEcc = 0; const char* password = NULL; const char* privKeyName = NULL; //byte imExit = 0; @@ -786,12 +834,6 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (config.user == NULL) err_sys("client requires a username parameter."); -#ifdef WOLFSSH_NO_RSA - /* XXX This needs to go. - * Client should tell the key type based on the key. */ - //userEcc = 1; -#endif - #ifdef SINGLE_THREADED if (keepOpen) err_sys("Threading needed for terminal session\n"); @@ -801,9 +843,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("If setting priv key, need pub key."); } - /* XXX Setting to zero instead of userEcc. Should be able to - * tell key type from the key. */ - ret = ClientSetPrivateKey(privKeyName, 0); + ret = ClientSetPrivateKey(privKeyName); if (ret != 0) { err_sys("Error setting private key"); } @@ -815,10 +855,9 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) } else #endif - /* XXX Setting to zero instead of userEcc. Should be able to - * tell key type from the key. */ + if (pubKeyName) { - ret = ClientUsePubKey(pubKeyName, 0); + ret = ClientUsePubKey(pubKeyName); } if (ret != 0) { err_sys("Error setting public key"); From 4d5c4e13063a66d115fef01094a07ba05990b880 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 14 Sep 2023 16:53:25 -0700 Subject: [PATCH 08/16] Semaphore Fix 1. Add quit parameter to the example client's thread_args structure. This will be used to let the windowMonitor thread know the application is quitting. 2. If the windowMonitor's quit flag is set, it will exit and not call wait again. 3. Join the windowMonitor thread rather than cancel it. --- apps/wolfssh/wolfssh.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 9c280a1f7..e2ec80348 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -154,6 +154,7 @@ typedef struct thread_args { WOLFSSH* ssh; wolfSSL_Mutex lock; byte rawMode; + byte quit; } thread_args; #ifdef _POSIX_THREADS @@ -233,6 +234,9 @@ static THREAD_RET windowMonitor(void* in) do { #if (defined(__OSX__) || defined(__APPLE__)) dispatch_semaphore_wait(windowSem, DISPATCH_TIME_FOREVER); + if (args->quit) { + break; + } #else sem_wait(&windowSem); #endif @@ -956,6 +960,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) pthread_t thread[3]; arg.ssh = ssh; + arg.quit = 0; wc_InitMutex(&arg.lock); #if (defined(__OSX__) || defined(__APPLE__)) windowSem = dispatch_semaphore_create(0); @@ -980,7 +985,14 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) pthread_create(&thread[1], NULL, readInput, (void*)&arg); pthread_create(&thread[2], NULL, readPeer, (void*)&arg); pthread_join(thread[2], NULL); - pthread_cancel(thread[0]); + /* Wake the windowMonitor thread so it can exit. */ + arg.quit = 1; + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_signal(windowSem); + #else + sem_post(&windowSem); + #endif + pthread_join(thread[0], NULL); pthread_cancel(thread[1]); #if (defined(__OSX__) || defined(__APPLE__)) dispatch_release(windowSem); From 93a54401413f9dd73ddc3c974452921e1d266515 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 18 Sep 2023 13:31:14 -0700 Subject: [PATCH 09/16] wolfSSH Client 1. Update to use a default public key based on the name of the private key. 2. Stash the public key name in the config structure, removing it from the list of globals. --- apps/wolfssh/wolfssh.c | 48 +++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index e2ec80348..b0c20960f 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -99,9 +99,10 @@ static void ShowUsage(char* appPath) } -static const char* pubKeyName = NULL; +#ifdef WOLFSSH_CERTS static const char* certName = NULL; static const char* caCert = NULL; +#endif #if defined(WOLFSSH_AGENT) @@ -594,6 +595,7 @@ struct config { char* user; char* hostname; char* keyFile; + char* pubKeyFile; char* command; word32 printConfig:1; word32 noCommand:1; @@ -624,6 +626,7 @@ static int config_init_default(struct config* config) env = getenv("HOME"); if (env != NULL) { const char* defaultName = "/.ssh/id_ecdsa"; + const char* pubSuffix = ".pub"; char* keyFile; sz = strlen(env) + strlen(defaultName) + 1; @@ -633,6 +636,15 @@ static int config_init_default(struct config* config) strcat(keyFile, defaultName); config->keyFile = keyFile; } + + sz += strlen(pubSuffix); + keyFile = (char*)malloc(sz); + if (keyFile != NULL) { + strcpy(keyFile, env); + strcat(keyFile, defaultName); + strcat(keyFile, pubSuffix); + config->pubKeyFile = keyFile; + } } return 0; @@ -778,6 +790,8 @@ static int config_print(struct config* config) printf("hostname %s\n", config->hostname ? config->hostname : "none"); printf("port %u\n", config->port); printf("keyFile %s\n", config->keyFile ? config->keyFile : "none"); + printf("pubKeyFile %s\n", + config->keyFile ? config->keyFile : "none"); printf("noCommand %s\n", config->noCommand ? "true" : "false"); printf("logfile %s\n", config->logFile ? config->logFile : "default"); printf("command %s\n", config->command ? config->command : "none"); @@ -798,6 +812,9 @@ static int config_cleanup(struct config* config) if (config->keyFile) { free(config->keyFile); } + if (config->pubKeyFile) { + free(config->pubKeyFile); + } if (config->command) { free(config->command); } @@ -816,7 +833,6 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) //char rxBuf[80]; int ret = 0; const char* password = NULL; - const char* privKeyName = NULL; //byte imExit = 0; byte keepOpen = 1; #ifdef USE_WINDOWS_API @@ -842,14 +858,17 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (keepOpen) err_sys("Threading needed for terminal session\n"); #endif - - if ((pubKeyName == NULL && certName == NULL) && privKeyName != NULL) { +#if 0 + if ((config.pubKeyFilae== NULL && certName == NULL) + && config.keyFile != NULL) { err_sys("If setting priv key, need pub key."); } - - ret = ClientSetPrivateKey(privKeyName); - if (ret != 0) { - err_sys("Error setting private key"); +#endif + if (config.keyFile) { + ret = ClientSetPrivateKey(config.keyFile); + if (ret != 0) { + err_sys("Error setting private key"); + } } #ifdef WOLFSSH_CERTS @@ -860,8 +879,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) else #endif - if (pubKeyName) { - ret = ClientUsePubKey(pubKeyName); + if (config.pubKeyFile) { + ret = ClientUsePubKey(config.pubKeyFile); } if (ret != 0) { err_sys("Error setting public key"); @@ -871,10 +890,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ctx == NULL) err_sys("Couldn't create wolfSSH client context."); - if (((func_args*)args)->user_auth == NULL) - wolfSSH_SetUserAuth(ctx, ClientUserAuth); - else - wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth); + wolfSSH_SetUserAuth(ctx, ClientUserAuth); #ifdef WOLFSSH_AGENT if (useAgent) { @@ -886,8 +902,6 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #ifdef WOLFSSH_CERTS ClientLoadCA(ctx, caCert); -#else - (void)caCert; #endif /* WOLFSSH_CERTS */ wolfSSH_CTX_SetPublicKeyCheck(ctx, ClientPublicKeyCheck); @@ -1078,7 +1092,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) err_sys("Closing client stream failed"); - ClientFreeBuffers(pubKeyName, privKeyName); + ClientFreeBuffers(config.pubKeyFile, config.keyFile); #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) wc_ecc_fp_free(); /* free per thread cache */ #endif From 35b5c40969b96edc2f3905e149aa15df790aacef Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 18 Sep 2023 17:10:57 -0700 Subject: [PATCH 10/16] wolfSSH Client 1. Remove stray print when the client isn't going to ask for a password. 2. Remove adding the CR to the end of the string sent to the server. The appropriate solution is to fix the issue in the Windows build of the server. For POSIX, the LF is converted to CR/LF, so we get a CR/CR/LF in the end, which looks like the enter key double-striking. --- apps/wolfssh/common.c | 2 -- apps/wolfssh/wolfssh.c | 9 --------- 2 files changed, 11 deletions(-) diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c index de1296171..0264f28fe 100644 --- a/apps/wolfssh/common.c +++ b/apps/wolfssh/common.c @@ -291,8 +291,6 @@ int ClientUserAuth(byte authType, * use public key auth */ if (pubKeyLoaded == 1) { if (authType == WOLFSSH_USERAUTH_PASSWORD) { - printf("rejecting password type with %s in favor of pub key\n", - (char*)authData->username); return WOLFSSH_USERAUTH_FAILURE; } } diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index b0c20960f..061af1467 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -297,15 +297,6 @@ static THREAD_RET readInput(void* in) #else ret = (int)read(STDIN_FILENO, buf, bufSz -1); sz = (word32)ret; - - /* add carriage returns for interop with windows server */ - if (ret > 0) { - if (ret == 1 && buf[0] == '\n') { - buf[1] = '\r'; - ret++; - sz++; - } - } #endif if (ret <= 0) { fprintf(stderr, "Error reading stdin\n"); From a577c119fc2e8a8b4e6c0f207179ed25a781155c Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 19 Sep 2023 09:43:55 -0700 Subject: [PATCH 11/16] wolfSSH Client 1. Removed redundant fflush() after a write(). 2. Treat WS_CHANNEL_CLOSED during shutdown as successful. --- apps/wolfssh/wolfssh.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 061af1467..adbf01283 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -437,7 +437,6 @@ static THREAD_RET readPeer(void* in) if (write(STDOUT_FILENO, buf, ret) < 0) { perror("write to stdout error "); } - fflush(stdout); #endif } ret = wolfSSH_stream_peek(args->ssh, buf, bufSz); @@ -1073,7 +1072,11 @@ 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_CHANNEL_CLOSED) { + /* Shutting down, channel closing isn't a fail. */ + ret = WS_SUCCESS; + } + else if (ret != WS_SUCCESS) { err_sys("Failed to listen for close messages from the peer."); } } From 0ded1c83e8b851c5b0c3933281387d9ca225f49c Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 19 Sep 2023 18:31:46 -0700 Subject: [PATCH 12/16] wolfSSH Client 1. In the windowMonitor(), move the check for quit outside the conditional build so it works for both macOS and Linux. 2. Removed a dead store in readPeer(). If the socket wants read, that's fine. Don't need to override it. 3. When allocating memory for the hostname, don't forget the nul. 4. When parsing the command line and storing a copy of it to send to the peer, stash it in the config. 5. When allocating memory for the username, add 1 for the nul outside the strlen(). 6. When canceling a thread, be sure to join it afterwards. 7. Added a read me document for wolfSSH client. Just a quick note. To be replaced later with something more formal. --- apps/wolfssh/README.md | 13 +++++++++++++ apps/wolfssh/include.am | 2 ++ apps/wolfssh/wolfssh.c | 16 +++++++++------- 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 apps/wolfssh/README.md diff --git a/apps/wolfssh/README.md b/apps/wolfssh/README.md new file mode 100644 index 000000000..f402b3b0f --- /dev/null +++ b/apps/wolfssh/README.md @@ -0,0 +1,13 @@ +WOLFSSH CLIENT +============== + +The wolfSSH client will connect to a server and try to open a terminal. It'll +default the username to your current username, and it will try to use your +ecdsa private key to authenticate. The key file path is hard coded to +`$HOME/.ssh/id_ecdsa`. It is currently far enough along I can use it. The +private keys are the ones produced by the OpenSSL command line tool, not the +ssh-keygen tool. + +Phase 2 is going to bring reading the config files `/etc/ssh/ssh_config` and +`$HOME/.ssh/config`. It will handle OpenSSH style modern keys. It will also +have support for SSH-AGENT and forwarding. diff --git a/apps/wolfssh/include.am b/apps/wolfssh/include.am index 716619004..bfb90663a 100644 --- a/apps/wolfssh/include.am +++ b/apps/wolfssh/include.am @@ -7,3 +7,5 @@ apps_wolfssh_wolfssh_LDADD = src/libwolfssh.la apps_wolfssh_wolfssh_DEPENDENCIES = src/libwolfssh.la endif BUILD_SSHCLIENT + +EXTRA_DIST+= apps/wolfssh/README.md diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index adbf01283..0aacb6cfe 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -235,12 +235,12 @@ static THREAD_RET windowMonitor(void* in) do { #if (defined(__OSX__) || defined(__APPLE__)) dispatch_semaphore_wait(windowSem, DISPATCH_TIME_FOREVER); - if (args->quit) { - break; - } #else sem_wait(&windowSem); #endif + if (args->quit) { + break; + } ret = sendCurrentWindowSize(args); (void)ret; } while (1); @@ -375,7 +375,7 @@ static THREAD_RET readPeer(void* in) if (ret == WS_FATAL_ERROR) { ret = wolfSSH_get_error(args->ssh); if (ret == WS_WANT_READ) { - ret = WS_SUCCESS; + continue; } #ifdef WOLFSSH_AGENT else if (ret == WS_CHAN_RXD) { @@ -605,7 +605,7 @@ static int config_init_default(struct config* config) if (env != NULL) { char* user; - sz = strlen(env + 1); + sz = strlen(env) + 1; user = (char*)malloc(sz); if (user != NULL) { strcpy(user, env); @@ -726,7 +726,7 @@ static int config_parse_command_line(struct config* config, if (found != NULL) { *found = '\0'; sz = strlen(cursor); - config->hostname = (char*)malloc(sz); + config->hostname = (char*)malloc(sz + 1); strcpy(config->hostname, cursor); cursor = found + 1; if (*cursor != 0) { @@ -736,7 +736,7 @@ static int config_parse_command_line(struct config* config, } else { sz = strlen(cursor); - config->hostname = (char*)malloc(sz); + config->hostname = (char*)malloc(sz + 1); strcpy(config->hostname, cursor); } @@ -758,6 +758,7 @@ static int config_parse_command_line(struct config* config, } command = (char*)malloc(commandSz); + config->command = command; cursor = command; for (i = myoptind; i < argc; i++) { @@ -998,6 +999,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #endif pthread_join(thread[0], NULL); pthread_cancel(thread[1]); + pthread_join(thread[1], NULL); #if (defined(__OSX__) || defined(__APPLE__)) dispatch_release(windowSem); #else From 653722f481de805c8f20e7225c57ecc14dca1e90 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 20 Sep 2023 08:56:16 -0700 Subject: [PATCH 13/16] wolfSSH Client 1. Remove the global req test code. 2. Remove the check for having both both private key and public key specified. It currently derives a public key name from the private key name. --- apps/wolfssh/wolfssh.c | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 0aacb6cfe..1d2777851 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -821,10 +821,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID; SOCKADDR_IN_T clientAddr; socklen_t clientAddrSz = sizeof(clientAddr); - //char rxBuf[80]; int ret = 0; const char* password = NULL; - //byte imExit = 0; byte keepOpen = 1; #ifdef USE_WINDOWS_API byte rawMode = 0; @@ -849,12 +847,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (keepOpen) err_sys("Threading needed for terminal session\n"); #endif -#if 0 - if ((config.pubKeyFilae== NULL && certName == NULL) - && config.keyFile != NULL) { - err_sys("If setting priv key, need pub key."); - } -#endif + if (config.keyFile) { ret = ClientSetPrivateKey(config.keyFile); if (ret != 0) { @@ -1036,36 +1029,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (keepOpen) ClientSetEcho(1); } -// else #endif -//#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) -// while (!imExit) { -//#else -// if (!imExit) { -//#endif -// ret = wolfSSH_stream_send(ssh, (byte*)testString, -// (word32)strlen(testString)); -// if (ret <= 0) -// err_sys("Couldn't send test string."); -// -// do { -// ret = wolfSSH_stream_read(ssh, (byte*)rxBuf, sizeof(rxBuf) - 1); -// if (ret <= 0) { -// ret = wolfSSH_get_error(ssh); -// if (ret != WS_WANT_READ && ret != WS_WANT_WRITE && -// ret != WS_CHAN_RXD) -// err_sys("Stream read failed."); -// } -// } while (ret == WS_WANT_READ || ret == WS_WANT_WRITE); -// -// rxBuf[ret] = '\0'; -// printf("Server said: %s\n", rxBuf); -// -//#if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) -// sleep(10); -//#endif -// } ret = wolfSSH_shutdown(ssh); /* do not continue on with shutdown process if peer already disconnected */ if (ret != WS_SOCKET_ERROR_E From fd67c8b0c8d3d42470dbfc38947610f5591ce22c Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 22 Sep 2023 15:58:17 -0700 Subject: [PATCH 14/16] wolfSSH Client 1. Changed the function client_test()'s name to wolfSSH_Client(). 2. Added "destination" to the usage text. 3. Improved the handling of the termios settings. 4. Remove the file names from the ClientFreeBuffers() function. 5. Changed ClientFreeBuffers() to free if the pointers aren't pointing at the static buffers. They may not load because the file is bad, but it will still have a file name. 6. Only try to load the public key or cert if loading the private key was successful. Do not fail out if key cannot load, password may still be possible. --- apps/wolfssh/common.c | 6 +-- apps/wolfssh/common.h | 3 +- apps/wolfssh/wolfssh.c | 91 ++++++++++++++++++++++++++++++++---------- 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c index 0264f28fe..3945866c2 100644 --- a/apps/wolfssh/common.c +++ b/apps/wolfssh/common.c @@ -523,13 +523,13 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert) } -void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName) +void ClientFreeBuffers(void) { - if (pubKeyName != NULL && userPublicKey != NULL) { + if (userPublicKey != userPublicKeyBuf) { WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY); } - if (privKeyName != NULL && userPrivateKey != NULL) { + if (userPrivateKey != userPrivateKeyBuf) { WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY); } } diff --git a/apps/wolfssh/common.h b/apps/wolfssh/common.h index 5eb510cce..14d45dcba 100644 --- a/apps/wolfssh/common.h +++ b/apps/wolfssh/common.h @@ -31,7 +31,6 @@ WOLFSSH_LOCAL int ClientUserAuth(byte authType, WS_UserAuthData* authData, WOLFSSH_LOCAL int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx); WOLFSSH_LOCAL void ClientIPOverride(int flag); -WOLFSSH_LOCAL void ClientFreeBuffers(const char* pubKeyName, - const char* privKeyName); +WOLFSSH_LOCAL void ClientFreeBuffers(void); #endif /* APPS_WOLFSSH_COMMON_H */ diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 1d2777851..61d6c6e7e 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -94,7 +94,8 @@ static void ShowUsage(char* appPath) } printf("%s v%s\n", appName, LIBWOLFSSH_VERSION_STRING); - printf("usage: %s [-E logfile] [-G] [-l login_name] [-N] [-p port] [-V]\n", + printf("usage: %s [-E logfile] [-G] [-l login_name] [-N] [-p port] " + "[-V] destination\n", appName); } @@ -149,6 +150,53 @@ static int NonBlockSSH_connect(WOLFSSH* ssh) return ret; } +#ifdef HAVE_TERMIOS_H +WOLFSSH_TERMIOS oldTerm; + +static void modes_store(void) +{ + tcgetattr(STDIN_FILENO, &oldTerm); +} + +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_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF + | IXANY | IGNBRK | INPCK | PARMRK); +#ifdef IUCLC + term.c_iflag &= ~IUCLC; +#endif + term.c_iflag |= IGNPAR; + + term.c_oflag &= ~(OPOST | ONOCR | ONLRET); +#ifdef OUCLC + term.c_oflag &= ~OLCUC; +#endif + + term.c_cflag &= ~(CSTOPB | PARENB | PARODD | CLOCAL | CRTSCTS); + + tcsetattr(STDIN_FILENO, TCSANOW, &term); +} + +static void modes_reset(void) +{ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &oldTerm); +} + +#define MODES_STORE() modes_store() +#define MODES_CLEAR() modes_clear() +#define MODES_RESET() modes_reset() +#else +#define MODES_STORE() do {} while(0) +#define MODES_CLEAR() do {} while(0) +#define MODES_RESET() do {} while(0) +#endif + #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) typedef struct thread_args { @@ -814,7 +862,7 @@ static int config_cleanup(struct config* config) } -THREAD_RETURN WOLFSSH_THREAD client_test(void* args) +static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) { WOLFSSH_CTX* ctx = NULL; WOLFSSH* ssh = NULL; @@ -833,6 +881,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #endif struct config config; + MODES_STORE(); + ((func_args*)args)->return_code = 0; config_init_default(&config); @@ -850,26 +900,20 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (config.keyFile) { ret = ClientSetPrivateKey(config.keyFile); - if (ret != 0) { - err_sys("Error setting private key"); + if (ret == 0) { + #ifdef WOLFSSH_CERTS + /* passed in certificate to use */ + if (certName) { + (void)ClientUseCert(certName); + } + else + #endif + if (config.pubKeyFile) { + (void)ClientUsePubKey(config.pubKeyFile); + } } } -#ifdef WOLFSSH_CERTS - /* passed in certificate to use */ - if (certName) { - ret = ClientUseCert(certName); - } - else -#endif - - if (config.pubKeyFile) { - ret = ClientUsePubKey(config.pubKeyFile); - } - if (ret != 0) { - err_sys("Error setting public key"); - } - ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); if (ctx == NULL) err_sys("Couldn't create wolfSSH client context."); @@ -949,8 +993,12 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("Couldn't connect SSH stream."); #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) +#if 0 if (keepOpen) /* set up for psuedo-terminal */ ClientSetEcho(2); +#endif + + MODES_CLEAR(); if (config.command != NULL || keepOpen == 1) { #if defined(_POSIX_THREADS) @@ -1053,12 +1101,13 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) err_sys("Closing client stream failed"); - ClientFreeBuffers(config.pubKeyFile, config.keyFile); + ClientFreeBuffers(); #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) wc_ecc_fp_free(); /* free per thread cache */ #endif config_cleanup(&config); + MODES_RESET(); return 0; } @@ -1081,7 +1130,7 @@ int main(int argc, char** argv) wolfSSH_Init(); - client_test(&args); + wolfSSH_Client(&args); wolfSSH_Cleanup(); From 2250f149b56498d31a324aa4f4ef557f3919de02 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Sun, 24 Sep 2023 14:16:02 -0700 Subject: [PATCH 15/16] wolfSSH Client 1. Relabel many of the string functions with their compatibility wrappers. 2. Add command line options to the readme file. --- apps/wolfssh/README.md | 19 ++++++++++++ apps/wolfssh/wolfssh.c | 66 +++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/apps/wolfssh/README.md b/apps/wolfssh/README.md index f402b3b0f..96e12789f 100644 --- a/apps/wolfssh/README.md +++ b/apps/wolfssh/README.md @@ -11,3 +11,22 @@ ssh-keygen tool. Phase 2 is going to bring reading the config files `/etc/ssh/ssh_config` and `$HOME/.ssh/config`. It will handle OpenSSH style modern keys. It will also have support for SSH-AGENT and forwarding. + +Command Line Options +-------------------- + + -E logfile : Specify a different log file. + -G : Print out the configuration as used. + -l login_name : Overrides the login name specified in the destination. + -N : Do not execute remote command. + -p port : Overrides the destination port number. + -V : Print out the version. + +The destination option is the only required option. It can be in the two +following formats: + + [user@]hostname + ssh://[user@]hostname[:port] + +The default value for _user_ is the current user's login name. The default +value for _port_ is 22. diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 61d6c6e7e..5562d733e 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -515,8 +515,8 @@ static int callbackGlobalReq(WOLFSSH *ssh, void *buf, word32 sz, return WS_FATAL_ERROR; } - if (strlen(reqStr) == sz - && (strncmp((char *)buf, reqStr, sz) == 0) + if (WSTRLEN(reqStr) == sz + && (WSTRNCMP((char *)buf, reqStr, sz) == 0) && reply == 1) { printf("Global Request\n"); return WS_SUCCESS; @@ -554,11 +554,11 @@ static int wolfSSH_AGENT_DefaultActions(WS_AgentCbAction action, void* vCtx) ret = WS_AGENT_NOT_AVAILABLE; if (ret == WS_AGENT_SUCCESS) { - memset(name, 0, sizeof(struct sockaddr_un)); + WMEMSET(name, 0, sizeof(struct sockaddr_un)); name->sun_family = AF_LOCAL; - strncpy(name->sun_path, sockName, sizeof(name->sun_path)); + WSTRNCPY(name->sun_path, sockName, sizeof(name->sun_path)); name->sun_path[sizeof(name->sun_path) - 1] = '\0'; - size = strlen(sockName) + + size = WSTRLEN(sockName) + offsetof(struct sockaddr_un, sun_path); ctx->fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -646,15 +646,15 @@ static int config_init_default(struct config* config) char* env; size_t sz; - memset(config, 0, sizeof(*config)); + WMEMSET(config, 0, sizeof(*config)); config->port = 22; env = getenv("USER"); if (env != NULL) { char* user; - sz = strlen(env) + 1; - user = (char*)malloc(sz); + sz = WSTRLEN(env) + 1; + user = (char*)WMALLOC(sz, NULL, 0); if (user != NULL) { strcpy(user, env); config->user = user; @@ -667,16 +667,16 @@ static int config_init_default(struct config* config) const char* pubSuffix = ".pub"; char* keyFile; - sz = strlen(env) + strlen(defaultName) + 1; - keyFile = (char*)malloc(sz); + sz = WSTRLEN(env) + WSTRLEN(defaultName) + 1; + keyFile = (char*)WMALLOC(sz, NULL, 0); if (keyFile != NULL) { strcpy(keyFile, env); strcat(keyFile, defaultName); config->keyFile = keyFile; } - sz += strlen(pubSuffix); - keyFile = (char*)malloc(sz); + sz += WSTRLEN(pubSuffix); + keyFile = (char*)WMALLOC(sz, NULL, 0); if (keyFile != NULL) { strcpy(keyFile, env); strcat(keyFile, defaultName); @@ -741,20 +741,20 @@ static int config_parse_command_line(struct config* config, myoptarg = argv[myoptind]; - sz = strlen(myoptarg) + 1; - dest = (char*)malloc(sz); - memcpy(dest, myoptarg, sz); + sz = WSTRLEN(myoptarg) + 1; + dest = (char*)WMALLOC(sz, NULL, 0); + WMEMCPY(dest, myoptarg, sz); cursor = dest; - if (strstr(cursor, uriPrefix)) { + if (WSTRSTR(cursor, uriPrefix)) { checkPort = 1; - cursor += strlen(uriPrefix); + cursor += WSTRLEN(uriPrefix); } else { checkPort = 0; } - found = strchr(cursor, '@'); + found = WSTRCHR(cursor, '@'); if (found == cursor) { fprintf(stderr, "can't start destination with just an @\n"); } @@ -763,18 +763,18 @@ static int config_parse_command_line(struct config* config, if (config->user) { free(config->user); } - sz = strlen(cursor); - config->user = malloc(sz + 1); + sz = WSTRLEN(cursor); + config->user = WMALLOC(sz + 1, NULL, 0); strcpy(config->user, cursor); cursor = found + 1; } if (checkPort) { - found = strchr(cursor, ':'); + found = WSTRCHR(cursor, ':'); if (found != NULL) { *found = '\0'; - sz = strlen(cursor); - config->hostname = (char*)malloc(sz + 1); + sz = WSTRLEN(cursor); + config->hostname = (char*)WMALLOC(sz + 1, NULL, 0); strcpy(config->hostname, cursor); cursor = found + 1; if (*cursor != 0) { @@ -783,8 +783,8 @@ static int config_parse_command_line(struct config* config, } } else { - sz = strlen(cursor); - config->hostname = (char*)malloc(sz + 1); + sz = WSTRLEN(cursor); + config->hostname = (char*)WMALLOC(sz + 1, NULL, 0); strcpy(config->hostname, cursor); } @@ -802,10 +802,10 @@ static int config_parse_command_line(struct config* config, * space but that's for the nul termination. */ commandSz = argc - myoptind; for (i = myoptind; i < argc; i++) { - commandSz += strlen(argv[i]); + commandSz += WSTRLEN(argv[i]); } - command = (char*)malloc(commandSz); + command = (char*)WMALLOC(commandSz, NULL, 0); config->command = command; cursor = command; @@ -843,19 +843,19 @@ static int config_print(struct config* config) static int config_cleanup(struct config* config) { if (config->user) { - free(config->user); + WFREE(config->user, NULL, 0); } if (config->hostname) { - free(config->hostname); + WFREE(config->hostname, NULL, 0); } if (config->keyFile) { - free(config->keyFile); + WFREE(config->keyFile, NULL, 0); } if (config->pubKeyFile) { - free(config->pubKeyFile); + WFREE(config->pubKeyFile, NULL, 0); } if (config->command) { - free(config->command); + WFREE(config->command, NULL, 0); } return 0; @@ -948,7 +948,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) #ifdef WOLFSSH_AGENT if (useAgent) { - memset(&agentCbCtx, 0, sizeof(agentCbCtx)); + WMEMSET(&agentCbCtx, 0, sizeof(agentCbCtx)); agentCbCtx.state = AGENT_STATE_INIT; wolfSSH_set_agent_cb_ctx(ssh, &agentCbCtx); } From 53a4dea81a1e5eaed83f6ef92c4f96cb7b57deee Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 25 Sep 2023 09:41:50 -0700 Subject: [PATCH 16/16] wolfSSH Client 1. Fix build with enabling client and disabling terminal support. --- apps/wolfssh/wolfssh.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c index 5562d733e..9448e3f34 100644 --- a/apps/wolfssh/wolfssh.c +++ b/apps/wolfssh/wolfssh.c @@ -150,7 +150,7 @@ static int NonBlockSSH_connect(WOLFSSH* ssh) return ret; } -#ifdef HAVE_TERMIOS_H +#if defined(HAVE_TERMIOS_H) && defined(WOLFSSH_TERM) WOLFSSH_TERMIOS oldTerm; static void modes_store(void) @@ -191,11 +191,11 @@ static void modes_reset(void) #define MODES_STORE() modes_store() #define MODES_CLEAR() modes_clear() #define MODES_RESET() modes_reset() -#else +#else /* HAVE_TERMIOS_H && WOLFSSH_TERM */ #define MODES_STORE() do {} while(0) #define MODES_CLEAR() do {} while(0) #define MODES_RESET() do {} while(0) -#endif +#endif /* HAVE_TERMIOS_H && WOLFSSH_TERM */ #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) @@ -218,6 +218,7 @@ typedef struct thread_args { #endif +#ifdef WOLFSSH_TERM static int sendCurrentWindowSize(thread_args* args) { int ret; @@ -295,7 +296,7 @@ static THREAD_RET windowMonitor(void* in) return THREAD_RET_SUCCESS; } -#else +#else /* _MSC_VER */ /* no SIGWINCH on Windows, poll current terminal size */ static word32 prevCol, prevRow; @@ -321,7 +322,8 @@ static int windowMonitor(thread_args* args) return ret; } -#endif +#endif /* _MSC_VER */ +#endif /* WOLFSSH_TERM */ static THREAD_RET readInput(void* in) @@ -393,7 +395,7 @@ static THREAD_RET readPeer(void* in) #endif while (ret >= 0) { - #ifdef USE_WINDOWS_API + #if defined(WOLFSSH_TERM) && defined(USE_WINDOWS_API) (void)windowMonitor(args); #endif @@ -1005,9 +1007,10 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) thread_args arg; pthread_t thread[3]; + wc_InitMutex(&arg.lock); arg.ssh = ssh; +#ifdef WOLFSSH_TERM arg.quit = 0; - wc_InitMutex(&arg.lock); #if (defined(__OSX__) || defined(__APPLE__)) windowSem = dispatch_semaphore_create(0); #else @@ -1028,9 +1031,11 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) signal(SIGWINCH, WindowChangeSignal); pthread_create(&thread[0], NULL, windowMonitor, (void*)&arg); +#endif /* WOLFSSH_TERM */ pthread_create(&thread[1], NULL, readInput, (void*)&arg); pthread_create(&thread[2], NULL, readPeer, (void*)&arg); pthread_join(thread[2], NULL); +#ifdef WOLFSSH_TERM /* Wake the windowMonitor thread so it can exit. */ arg.quit = 1; #if (defined(__OSX__) || defined(__APPLE__)) @@ -1039,13 +1044,16 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args) sem_post(&windowSem); #endif pthread_join(thread[0], NULL); +#endif /* WOLFSSH_TERM */ pthread_cancel(thread[1]); pthread_join(thread[1], NULL); +#ifdef WOLFSSH_TERM #if (defined(__OSX__) || defined(__APPLE__)) dispatch_release(windowSem); #else sem_destroy(&windowSem); #endif +#endif /* WOLFSSH_TERM */ #elif defined(_MSC_VER) thread_args arg; HANDLE thread[2];