From a35d1e35435b27de6c757c252648bba8b11dd6c3 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 14 May 2026 13:47:18 -0700 Subject: [PATCH 1/2] SendKexInit: advertise ext-info-s Per RFC 8308 Section 2.1. 1. The server will now advertize that it accepts an ext-info message. 2. The client and server will send the ext-info message if requested and and allowed. The per-side send functions are guarded based on configuration. The wolfSSH client currently doesn't have anything to say in an ext-info message. Issue: F-3448 --- src/internal.c | 70 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/src/internal.c b/src/internal.c index b50a45721..a66226917 100644 --- a/src/internal.c +++ b/src/internal.c @@ -4423,13 +4423,15 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } /* Extension Info Flag */ if (ret == WS_SUCCESS) { - /* Only checking for this is we are server. Our client does - * not have anything to say to a server, yet. */ - if (side == WOLFSSH_ENDPOINT_SERVER && !ssh->extInfoSent) { + /* Determine whether we should send EXT_INFO after NEWKEYS based on + * whether the peer advertised ext-info-c (server) or ext-info-s + * (client). */ + if (!ssh->extInfoSent) { byte extInfo; - /* Match the client accepts extInfo. */ - algoId = ID_EXTINFO_C; + /* Match the peer accepts extInfo. */ + algoId = (side == WOLFSSH_ENDPOINT_SERVER) + ? ID_EXTINFO_C : ID_EXTINFO_S; extInfo = MatchIdLists(side, list, listSz, &algoId, 1); ssh->sendExtInfo = extInfo == algoId; } @@ -11314,6 +11316,10 @@ int SendKexInit(WOLFSSH* ssh) kexAlgoNamesPlus = ",ext-info-c"; kexAlgoNamesPlusSz = (word32)WSTRLEN(kexAlgoNamesPlus); } + else { + kexAlgoNamesPlus = ",ext-info-s"; + kexAlgoNamesPlusSz = (word32)WSTRLEN(kexAlgoNamesPlus); + } kexAlgoNamesSz = AlgoListSz(ssh->algoListKex); encAlgoNamesSz = AlgoListSz(ssh->algoListCipher); @@ -14207,7 +14213,8 @@ int SendServiceAccept(WOLFSSH* ssh, byte serviceId) static const char serverSigAlgsName[] = "server-sig-algs"; -int SendExtInfo(WOLFSSH* ssh) +#ifndef NO_WOLFSSH_SERVER +static int SendExtInfoServer(WOLFSSH* ssh) { byte* output; word32 idx; @@ -14215,11 +14222,7 @@ int SendExtInfo(WOLFSSH* ssh) word32 serverSigAlgsNameSz = 0; int ret = WS_SUCCESS; - WLOG(WS_LOG_DEBUG, "Entering SendExtInfo()"); - - if (ssh == NULL) { - ret = WS_BAD_ARGUMENT; - } + WLOG(WS_LOG_DEBUG, "Entering SendExtInfoServer()"); if (ret == WS_SUCCESS) { keyAlgoNamesSz = AlgoListSz(ssh->algoListKeyAccepted); @@ -14259,6 +14262,51 @@ int SendExtInfo(WOLFSSH* ssh) ret = wolfSSH_SendPacket(ssh); } + WLOG(WS_LOG_DEBUG, "Leaving SendExtInfoServer(), ret = %d", ret); + return ret; +} +#endif /* NO_WOLFSSH_SERVER */ + + +#ifndef NO_WOLFSSH_CLIENT +static int SendExtInfoClient(WOLFSSH* ssh) +{ + int ret = WS_SUCCESS; + + WOLFSSH_UNUSED(ssh); + WLOG(WS_LOG_DEBUG, "Entering SendExtInfoClient()"); + /* This is currently a stub. Our client doesn't have anything to say. */ + WLOG(WS_LOG_DEBUG, "Leaving SendExtInfoClient(), ret = %d", ret); + + return ret; +} +#endif /* NO_WOLFSSH_CLIENT */ + + +int SendExtInfo(WOLFSSH* ssh) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering SendExtInfo()"); + + if (ssh == NULL || ssh->ctx == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + /* Disabling server and client is checked at compile time. */ + #ifndef NO_WOLFSSH_SERVER + if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) { + ret = SendExtInfoServer(ssh); + } + #endif + #ifndef NO_WOLFSSH_CLIENT + if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) { + ret = SendExtInfoClient(ssh); + } + #endif + } + WLOG(WS_LOG_DEBUG, "Leaving SendExtInfo(), ret = %d", ret); return ret; } From caec20f1f8791e6493349f2541b0d6e00d19d562 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 2 Jun 2026 14:28:41 -0700 Subject: [PATCH 2/2] SFTP: retry short writes, fail if stuck 1. Loop WPWRITE until the full chunk is written, advancing the split 64-bit offset with carry. Bail out on error or a zero return and report WOLFSSH_FTP_FAILURE so clients see the truncation instead of a silent success. 2. Update the backup WPWRITE() implemented with fwrite() to parallel the behavior of the backup WPREAD(). Changed to write sz number of 1 byte objects. Issue: F-3880 --- src/wolfsftp.c | 52 +++++++++++++++++++++++++++++++++++++------------- wolfssh/port.h | 4 ++-- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 0dd7e415e..923664aeb 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -3689,20 +3689,46 @@ int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_BUFFER_E; } - ret = WPWRITE(ssh->fs, fd, (byte*)str, strSz, ofst); - if (ret < 0) { - #if defined(WOLFSSL_NUCLEUS) && defined(DEBUG_WOLFSSH) - if (ret == NUF_NOSPC) { - WLOG(WS_LOG_SFTP, "Ran out of memory"); + { + word32 written = 0; + + /* Retry while WPWRITE makes forward progress; bail on error + * or zero return to avoid spinning on a stuck backend. */ + while (written < strSz) { + ret = WPWRITE(ssh->fs, fd, (byte*)str + written, + strSz - written, ofst); + if (ret <= 0) { + break; + } + written += (word32)ret; + /* Advance the split 64-bit offset, propagating carry. */ + ofst[0] += (word32)ret; + if (ofst[0] < (word32)ret) { + ofst[1] += 1; + } + } + + if (ret < 0) { + #if defined(WOLFSSL_NUCLEUS) && defined(DEBUG_WOLFSSH) + if (ret == NUF_NOSPC) { + WLOG(WS_LOG_SFTP, "Ran out of memory"); + } + #endif + WLOG(WS_LOG_SFTP, "Error writing to file"); + res = err; + type = WOLFSSH_FTP_FAILURE; + ret = WS_INVALID_STATE_E; + } + else if (written != strSz) { + WLOG(WS_LOG_SFTP, "Short write: %u of %u bytes", + written, strSz); + res = err; + type = WOLFSSH_FTP_FAILURE; + ret = WS_INVALID_STATE_E; + } + else { + ret = WS_SUCCESS; } - #endif - WLOG(WS_LOG_SFTP, "Error writing to file"); - res = err; - type = WOLFSSH_FTP_FAILURE; - ret = WS_INVALID_STATE_E; - } - else { - ret = WS_SUCCESS; } } diff --git a/wolfssh/port.h b/wolfssh/port.h index fee7ab39b..ce2683079 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -1127,7 +1127,7 @@ extern "C" { fseek(fd, ofst, 0); } - return fwrite(buf, sz, 1, fd); + return fwrite(buf, sizeof(unsigned char), sz, fd); } #define WPWRITE(fs,fd,b,s,o) wPwrite((fd),(b),(s),(o)) #endif @@ -1143,7 +1143,7 @@ extern "C" { fseek(fd, ofst, 0); } - return fread(buf, 1, sz, fd); + return fread(buf, sizeof(unsigned char), sz, fd); } #define WPREAD(fs,fd,b,s,o) wPread((fd),(b),(s),(o)) #endif