diff --git a/ChangeLog.md b/ChangeLog.md index 778a8607c..651c0f357 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,74 @@ +# wolfSSH v1.4.15 (December 22, 2023) + +## Vulnerabilities + +* Fixes a potential vulnerability described in the paper "Passive SSH Key + Compromise via Lattices". While the misbehavior described hasn't + been observed in wolfSSH, the fix is now implemented. The RSA signature + is verified before sending to the peer. + - Keegan Ryan, Kaiwen He, George Arnold Sullivan, and Nadia Heninger. 2023. + Passive SSH Key Compromise via Lattices. Cryptology ePrint Archive, + Report 2023/1711. https://eprint.iacr.org/2023/1711. + +## Notes + +* When building wolfSSL/wolfCrypt versions before v5.6.6 with CMake, + wolfSSH may have a problem with RSA keys. This is due to wolfSSH not + checking on the size of `___uint128_t`. wolfSSH sees the RSA structure + as the wrong size. You will have to define `HAVE___UINT128_T` if you + know you have it and are using it in wolfSSL. wolfSSL v5.6.6 exports that + define in options.h when using CMake. + +## New Features + +* Added wolfSSH client application. +* Added support for OpenSSH-style private keys, like those made by ssh-keygen. +* Added support for the Zephyr RTOS. +* Added support for multiple authentication schemes in the userauth callback + with the error response `WOLFSSH_USERAUTH_PARTIAL_SUCCESS`. + +## Improvements + +* Allow override of default sshd user name at build. +* Do not attempt to copy device files. The client won't ask, and the server + won't do it. +* More wolfSSHd testing. +* Portability updates. +* Terminal updates for shell connections to wolfSSHd, including window size + updates. +* QNX support updates. +* Windows file support updates for SFTP and SCP. +* Allow for longer command strings in wolfSSHd. +* Tweaked some select timeouts in the echoserver. +* Add some type size checks to configure. +* Update for changes in wolfSSL's threading wrappers. +* Updates for Espressif support and testing. +* Speed improvements for SFTP. (Fixed unnecessary waiting.) +* Windows wolfSSHd improvements. +* The functions `wolfSSH_ReadKey_file()` and `wolfSSH_ReadKey_buffer()` + handle more encodings. +* Add function to supply new protocol ID string. +* Support larger RSA keys. +* MinGW support updates. +* Update file use W-macro wrappers with a filesystem parameter. + +## Fixes + +* When setting the file permissions for a file in Zephyr, use the correct + permission constants. +* Fix buffer issue in `DoReceive()` on some edge failure conditions. +* Prevent wolfSSHd zombie processes. +* Fixed a few references to the heap variable for user supplied memory + allocation functions. +* Fixed an index update when verifying the server's RSA signature during KEX. +* Fixed some of the guards around optional code. +* Fixed some would-block cases when using non-blocking sockets in the + examples. +* Fixed some compile issues with liboqs. +* Fix for interop issue with OpenSSH when using AES-CTR. + +--- + # wolfSSH v1.4.14 (July 7, 2023) ## New Feature Additions and Improvements @@ -22,6 +93,8 @@ - Fix for support with secondary groups with wolfSSHd - Fixes for SFTP edge cases when used with LWiP +--- + # wolfSSH v1.4.13 (Apr 3, 2023) ## New Feature Additions and Improvements diff --git a/README.md b/README.md index 79b27ec38..828ac0ba5 100644 --- a/README.md +++ b/README.md @@ -497,3 +497,9 @@ john-cert.der would be: $ ./examples/client/client -u john -J ./keys/john-cert.der -i ./keys/john-key.der + +WOLFSSH APPLICATIONS +==================== + +wolfSSH comes with a server daemon and a command line shell tool. Check out +the apps directory for more information. diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index 53722ee20..bcb8c6304 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -39,6 +39,12 @@ #ifndef _WIN32 #include +#else +/* avoid macro redefinition warnings on STATUS values when include ntstatus.h */ +#undef UMDF_USING_NTSTATUS +#define UMDF_USING_NTSTATUS +#undef UNICODE +#define UNICODE #endif #include @@ -685,6 +691,21 @@ static int CheckPublicKeyUnix(const char* name, #include #include + +/* Pulled in from Advapi32.dll */ +extern BOOL WINAPI LogonUserExExW(LPTSTR usr, + LPTSTR dmn, + LPTSTR paswd, + DWORD logonType, + DWORD logonProv, + PTOKEN_GROUPS tokenGrp, + PHANDLE tokenPh, + PSID* loginSid, + PVOID* pBuffer, + LPDWORD pBufferLen , + PQUOTA_LIMITS quotaLimits +); + #define MAX_USERNAME 256 static int _GetHomeDirectory(WOLFSSHD_AUTH* auth, const char* usr, WCHAR* out, int outSz) @@ -705,7 +726,7 @@ static int _GetHomeDirectory(WOLFSSHD_AUTH* auth, const char* usr, WCHAR* out, i CoTaskMemFree(homeDir); } else { - PROFILEINFO pInfo = { sizeof(PROFILEINFO) }; + PROFILEINFO pInfo = { 0 }; /* failed with get known folder path, try with loading the user profile */ pInfo.dwFlags = PI_NOUI; diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index 86245ba58..76f6bef07 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -60,6 +60,9 @@ #ifdef WIN32 #include #endif +#ifdef HAVE_LIMITS_H + #include +#endif struct WOLFSSHD_CONFIG { void* heap; diff --git a/configure.ac b/configure.ac index 280451af5..9e7b1abcc 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # All right reserved. AC_COPYRIGHT([Copyright (C) 2014-2023 wolfSSL Inc.]) -AC_INIT([wolfssh],[1.4.14],[support@wolfssl.com],[wolfssh],[https://www.wolfssl.com]) +AC_INIT([wolfssh],[1.4.15],[support@wolfssl.com],[wolfssh],[https://www.wolfssl.com]) AC_PREREQ([2.63]) AC_CONFIG_AUX_DIR([build-aux]) @@ -18,7 +18,7 @@ AC_ARG_PROGRAM AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) -WOLFSSH_LIBRARY_VERSION=15:1:7 +WOLFSSH_LIBRARY_VERSION=15:2:7 # | | | # +------+ | +---+ # | | | @@ -55,7 +55,7 @@ AC_TYPE_UINT8_T AC_TYPE_UINTPTR_T # Check headers/libs -AC_CHECK_HEADERS([sys/select.h sys/time.h sys/ioctl.h pty.h util.h termios.h]) +AC_CHECK_HEADERS([limits.h sys/select.h sys/time.h sys/ioctl.h pty.h util.h termios.h]) AC_CHECK_LIB([network],[socket]) AC_CHECK_LIB([util],[forkpty]) diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c index 9e1a7d4d7..38b079823 100644 --- a/examples/sftpclient/sftpclient.c +++ b/examples/sftpclient/sftpclient.c @@ -714,37 +714,37 @@ static int doCmds(func_args* args) } if ((pt = WSTRNSTR(msg, "chmod", MAX_CMD_SZ)) != NULL) { - int sz; + word32 sz, idx; char* f = NULL; char mode[WOLFSSH_MAX_OCTET_LEN]; pt += sizeof("chmod"); - sz = (int)WSTRLEN(pt); + sz = (word32)WSTRLEN(pt); if (pt[sz - 1] == '\n') pt[sz - 1] = '\0'; /* advance pointer to first location of non space character */ - for (i = 0; i < sz && pt[0] == ' '; i++, pt++); - sz = (int)WSTRLEN(pt); + for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++); + sz = (word32)WSTRLEN(pt); /* get mode */ sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1)? sz : WOLFSSH_MAX_OCTET_LEN -1; WMEMCPY(mode, pt, sz); mode[WOLFSSH_MAX_OCTET_LEN - 1] = '\0'; - for (i = 0; i < sz; i++) { - if (mode[i] == ' ') { - mode[i] = '\0'; + for (idx = 0; idx < sz; idx++) { + if (mode[idx] == ' ') { + mode[idx] = '\0'; break; } } - if (i == 0) { + if (idx == 0) { printf("error with getting mode\r\n"); continue; } - pt += (int)WSTRLEN(mode); - sz = (int)WSTRLEN(pt); - for (i = 0; i < sz && pt[0] == ' '; i++, pt++); + pt += (word32)WSTRLEN(mode); + sz = (word32)WSTRLEN(pt); + for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++); if (pt[0] != '/') { int maxSz = (int)WSTRLEN(workingDir) + sz + 2; diff --git a/src/agent.c b/src/agent.c index f72b7185e..78fc73c06 100644 --- a/src/agent.c +++ b/src/agent.c @@ -701,6 +701,10 @@ static int SignHashRsa(WOLFSSH_AGENT_KEY_RSA* rawKey, enum wc_HashType hashType, WLOG(WS_LOG_DEBUG, "Bad RSA Sign"); ret = WS_RSA_E; } + else { + ret = wolfSSH_RsaVerify(sig, *sigSz, + encSig, encSigSz, &key, heap, "SignHashRsa"); + } } wc_FreeRsaKey(&key); diff --git a/src/internal.c b/src/internal.c index c68e136c0..7e9727cf6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9422,6 +9422,47 @@ static INLINE byte SigTypeForId(byte id) } +#ifndef WOLFSSH_NO_RSA +/* + * wolfSSH_RsaVerify + * sig - signature to verify + * sigSz - signature to verify size + * digest - encoded digest for verification + * digestSz - encoded digest size + * key - key used to sign and verify signature + * heap - allocation heap + * loc - calling function for logging + */ +int wolfSSH_RsaVerify(byte *sig, word32 sigSz, + const byte* digest, word32 digestSz, + RsaKey* key, void* heap, const char* loc) +{ + byte* checkSig; + int ret = WS_SUCCESS; + + checkSig = (byte*)WMALLOC(sigSz, heap, DYNTYPE_TEMP); + if (checkSig == NULL) { + ret = WS_MEMORY_E; + } + else { + int checkSz; + + checkSz = wc_RsaSSL_VerifyInline(sig, sigSz, &checkSig, key); + if (checkSz < 0 + || (word32)checkSz != digestSz + || WMEMCMP(digest, checkSig, digestSz) != 0) { + WLOG(WS_LOG_DEBUG, "%s: %s", loc, "Bad RSA Sign Verify"); + ret = WS_RSA_E; + } + ForceZero(checkSig, sigSz); + WFREE(checkSig, heap, DYNTYPE_TEMP); + } + + return ret; +} +#endif /* WOLFSSH_NO_RSA */ + + /* SendKexDhReply() * It is also the funciton used for MSGID_KEXECDH_REPLY. The parameters * are analogous between the two messages. Where MSGID_KEXDH_REPLY has @@ -9932,7 +9973,7 @@ int SendKexDhReply(WOLFSSH* ssh) encSigSz = wc_EncodeSignature(encSig, digest, wc_HashGetDigestSize(sigHashId), wc_HashGetOID(sigHashId)); - if (encSigSz <= 0) { + if (encSigSz == 0) { WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad Encode Sig"); ret = WS_CRYPTO_FAILED; } @@ -9946,6 +9987,12 @@ int SendKexDhReply(WOLFSSH* ssh) WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); ret = WS_RSA_E; } + else { + ret = wolfSSH_RsaVerify(sig_ptr, sigSz, + encSig, encSigSz, + &sigKeyBlock_ptr->sk.rsa.key, + heap, "SendKexDhReply"); + } } #ifdef WOLFSSH_SMALL_STACK WFREE(encSig, heap, DYNTYPE_TEMP); @@ -11175,6 +11222,11 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, WLOG(WS_LOG_DEBUG, "SUAR: Bad RSA Sign"); ret = WS_RSA_E; } + else { + ret = wolfSSH_RsaVerify(output + begin, keySig->sigSz, + encDigest, encDigestSz, &keySig->ks.rsa.key, + ssh->ctx->heap, "SUAR"); + } } } @@ -11324,21 +11376,23 @@ static int BuildUserAuthRequestRsaCert(WOLFSSH* ssh, if (ret == WS_SUCCESS) ret = wc_HashFinal(&hash, hashId, digest); - c32toa(keySig->sigSz + 7 + LENGTH_SZ * 2, output + begin); - begin += LENGTH_SZ; - c32toa(7, output + begin); - begin += LENGTH_SZ; - WMEMCPY(output + begin, "ssh-rsa", 7); - begin += 7; - c32toa(keySig->sigSz, output + begin); - begin += LENGTH_SZ; - encDigestSz = wc_EncodeSignature(encDigest, digest, digestSz, - wc_HashGetOID(hashId)); - if (encDigestSz <= 0) { - WLOG(WS_LOG_DEBUG, "SUAR: Bad Encode Sig"); - ret = WS_CRYPTO_FAILED; + if (ret == WS_SUCCESS) { + c32toa(keySig->sigSz + 7 + LENGTH_SZ * 2, output + begin); + begin += LENGTH_SZ; + c32toa(7, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, "ssh-rsa", 7); + begin += 7; + c32toa(keySig->sigSz, output + begin); + begin += LENGTH_SZ; + encDigestSz = wc_EncodeSignature(encDigest, digest, digestSz, + wc_HashGetOID(hashId)); + if (encDigestSz <= 0) { + WLOG(WS_LOG_DEBUG, "SUAR: Bad Encode Sig"); + ret = WS_CRYPTO_FAILED; + } } - else { + if (ret == WS_SUCCESS) { int sigSz; WLOG(WS_LOG_INFO, "Signing hash with RSA."); sigSz = wc_RsaSSL_Sign(encDigest, encDigestSz, @@ -11348,6 +11402,11 @@ static int BuildUserAuthRequestRsaCert(WOLFSSH* ssh, WLOG(WS_LOG_DEBUG, "SUAR: Bad RSA Sign"); ret = WS_RSA_E; } + else { + ret = wolfSSH_RsaVerify(output + begin, keySig->sigSz, + encDigest, encDigestSz, &keySig->ks.rsa.key, + ssh->ctx->heap, "SUAR"); + } } if (ret == WS_SUCCESS) diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 3bc55c09e..e32d09d04 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -2699,12 +2699,12 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char s[WOLFSSH_MAX_FILENAME]; if (!special) { /* do not add dir name in special case */ - if (WSTRLEN(dirName) + out->fSz + 2 > (sizeof r)) { + if (WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName) + >= (int)sizeof(r)) { WLOG(WS_LOG_SFTP, "Path length too large"); WFREE(out->fName, out->heap, DYNTYPE_SFTP); return WS_FATAL_ERROR; } - WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName); } else { if (out->fSz + 1 > (sizeof r)) { @@ -2789,12 +2789,12 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char r[WOLFSSH_MAX_FILENAME]; char s[WOLFSSH_MAX_FILENAME]; - if ((WSTRLEN(dirName) + WSTRLEN(out->fName) + 2) > sizeof(r)) { + if (WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName) + >= (int)sizeof(r)) { WLOG(WS_LOG_SFTP, "Path length too large"); WFREE(out->fName, out->heap, DYNTYPE_SFTP); return WS_FATAL_ERROR; } - WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName); if (wolfSSH_RealPath(ssh->sftpDefaultPath, r, s, sizeof(s)) < 0) { WLOG(WS_LOG_SFTP, "Error cleaning path to get attributes"); @@ -2954,12 +2954,12 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char r[WOLFSSH_MAX_FILENAME]; char s[WOLFSSH_MAX_FILENAME]; - if ((WSTRLEN(dirName) + WSTRLEN(out->fName) + 2) > sizeof(r)) { + if (WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName) + >= (int)sizeof(r)) { WLOG(WS_LOG_SFTP, "Path length too large"); WFREE(out->fName, out->heap, DYNTYPE_SFTP); return WS_FATAL_ERROR; } - WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName); if (wolfSSH_RealPath(ssh->sftpDefaultPath, r, s, sizeof(s)) < 0) { WLOG(WS_LOG_SFTP, "Error cleaning path to get attributes"); @@ -3020,12 +3020,12 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char r[WOLFSSH_MAX_FILENAME]; char s[WOLFSSH_MAX_FILENAME]; - if ((WSTRLEN(dirName) + WSTRLEN(out->fName) + 2) > sizeof(r)) { + if (WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName) + >= (int)sizeof(r)) { WLOG(WS_LOG_SFTP, "Path length too large"); WFREE(out->fName, out->heap, DYNTYPE_SFTP); return WS_FATAL_ERROR; } - WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName); if (wolfSSH_RealPath(ssh->sftpDefaultPath, r, s, sizeof(s)) < 0) { WFREE(out->fName, out->heap, DYNTYPE_SFTP); @@ -3087,12 +3087,12 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, char r[WOLFSSH_MAX_FILENAME]; char s[WOLFSSH_MAX_FILENAME]; - if ((WSTRLEN(dirName) + WSTRLEN(out->fName) + 2) > sizeof(r)) { + if (WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName) + >= (int)sizeof(r)) { WLOG(WS_LOG_SFTP, "Path length too large"); WFREE(out->fName, out->heap, DYNTYPE_SFTP); return WS_FATAL_ERROR; } - WSNPRINTF(r, sizeof(r), "%s/%s", dirName, out->fName); if (wolfSSH_RealPath(ssh->sftpDefaultPath, r, s, sizeof(s)) < 0) { WFREE(out->fName, out->heap, DYNTYPE_SFTP); diff --git a/wolfssh/internal.h b/wolfssh/internal.h index cb2a4dce3..3cd5b3776 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef WOLFSSH_SCP #include #endif @@ -1195,6 +1196,9 @@ WOLFSSH_LOCAL int wsScpSendCallback(WOLFSSH*, int, const char*, char*, word32, WOLFSSH_LOCAL int wolfSSH_CleanPath(WOLFSSH* ssh, char* in); +WOLFSSH_LOCAL int wolfSSH_RsaVerify(byte *sig, word32 sigSz, + const byte* digest, word32 digestSz, + RsaKey* key, void* heap, const char* loc); WOLFSSH_LOCAL void DumpOctetString(const byte*, word32); WOLFSSH_LOCAL int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz); WOLFSSH_LOCAL void AddAssign64(word32*, word32); diff --git a/wolfssh/version.h b/wolfssh/version.h index 2ceb7504d..3c3e9cfdc 100644 --- a/wolfssh/version.h +++ b/wolfssh/version.h @@ -35,8 +35,8 @@ extern "C" { #endif -#define LIBWOLFSSH_VERSION_STRING "1.4.14" -#define LIBWOLFSSH_VERSION_HEX 0x01004014 +#define LIBWOLFSSH_VERSION_STRING "1.4.15" +#define LIBWOLFSSH_VERSION_HEX 0x01004015 #ifdef __cplusplus }