mirror of https://github.com/wolfSSL/wolfssh.git
Accept exact-fit ScpBuffer in no-filesystem SCP send callback
parent
497da8a130
commit
1644bb773d
|
|
@ -97,3 +97,48 @@ jobs:
|
|||
path: wolfssh
|
||||
configure: ${{ matrix.config }} LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include"
|
||||
check: true
|
||||
|
||||
build_wolfssh_no_filesystem:
|
||||
needs:
|
||||
- build_wolfssl
|
||||
- create_matrix
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
wolfssl: ${{ fromJson(needs.create_matrix.outputs['versions']) }}
|
||||
name: Build and test wolfssh with NO_FILESYSTEM
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 6
|
||||
steps:
|
||||
- name: Checking cache for wolfssl
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: build-dir/
|
||||
key: wolfssh-os-check-wolfssl-${{ matrix.wolfssl }}-ubuntu-latest
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: wolfssh/
|
||||
|
||||
- name: autogen
|
||||
working-directory: ./wolfssh/
|
||||
run: ./autogen.sh
|
||||
|
||||
- name: configure
|
||||
working-directory: ./wolfssh/
|
||||
run: |
|
||||
./configure --enable-scp LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include -DNO_FILESYSTEM"
|
||||
|
||||
# The examples do not build without a filesystem, so build the
|
||||
# library and the unit test directly instead of running make all
|
||||
- name: make unit test
|
||||
working-directory: ./wolfssh/
|
||||
run: make tests/unit.test
|
||||
|
||||
- name: run unit test
|
||||
working-directory: ./wolfssh/
|
||||
run: |
|
||||
set -o pipefail
|
||||
LD_LIBRARY_PATH=${{ github.workspace }}/build-dir/lib ./tests/unit.test | tee unit-test.log
|
||||
grep "ScpSendCallback_ExactFitBuffer: SUCCESS" unit-test.log
|
||||
|
|
|
|||
|
|
@ -3716,10 +3716,12 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
|
|||
*aTime = sendBuffer->mTime;
|
||||
*fileMode = sendBuffer->mode;
|
||||
|
||||
/* copy over buffer info */
|
||||
/* copy over buffer info, idx past fileSz wraps the unsigned
|
||||
* math and is caught by the guard */
|
||||
ret = (bufSz < (sendBuffer->fileSz - sendBuffer->idx))?
|
||||
bufSz : sendBuffer->fileSz - sendBuffer->idx;
|
||||
if (sendBuffer->idx + ret >= sendBuffer->bufferSz) {
|
||||
if (sendBuffer->idx > sendBuffer->fileSz ||
|
||||
sendBuffer->idx + ret > sendBuffer->bufferSz) {
|
||||
WLOG(WS_LOG_ERROR, scpState,
|
||||
"potential buffer overflow caught, abort");
|
||||
ret = WS_SCP_ABORT;
|
||||
|
|
@ -3739,17 +3741,19 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
|
|||
break;
|
||||
|
||||
case WOLFSSH_SCP_CONTINUE_FILE_TRANSFER:
|
||||
/* copy over buffer info */
|
||||
if (sendBuffer->idx >= sendBuffer->bufferSz) {
|
||||
/* copy over buffer info, idx past fileSz would underflow the
|
||||
* size math */
|
||||
if (sendBuffer->idx > sendBuffer->bufferSz ||
|
||||
sendBuffer->idx > sendBuffer->fileSz) {
|
||||
WLOG(WS_LOG_ERROR, scpState,
|
||||
"sendbuffer idx greater than buffer size, abort");
|
||||
"sendbuffer idx out of range, abort");
|
||||
ret = WS_SCP_ABORT;
|
||||
break;
|
||||
}
|
||||
ret = (bufSz < (sendBuffer->fileSz - sendBuffer->idx))?
|
||||
bufSz : sendBuffer->fileSz - sendBuffer->idx;
|
||||
if (ret > 0) {
|
||||
if (sendBuffer->idx + ret >= sendBuffer->bufferSz) {
|
||||
if (sendBuffer->idx + ret > sendBuffer->bufferSz) {
|
||||
ret = WS_SCP_ABORT;
|
||||
WLOG(WS_LOG_ERROR, scpState, "buffer size issue, abort");
|
||||
break;
|
||||
|
|
@ -3761,7 +3765,8 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
|
|||
ret = WS_EOF;
|
||||
}
|
||||
|
||||
if (sendBuffer->status(ssh, sendBuffer->name,
|
||||
if (sendBuffer->status != NULL &&
|
||||
sendBuffer->status(ssh, sendBuffer->name,
|
||||
WOLFSSH_SCP_CONTINUE_FILE_TRANSFER, sendBuffer)
|
||||
!= WS_SUCCESS) {
|
||||
WLOG(WS_LOG_DEBUG, scpState, "continue status fail, abort");
|
||||
|
|
|
|||
229
tests/unit.c
229
tests/unit.c
|
|
@ -14256,6 +14256,227 @@ cleanup:
|
|||
#endif /* WOLFSSH_SCP recv callback depth guard test */
|
||||
|
||||
|
||||
#if defined(WOLFSSH_SCP) && !defined(WOLFSSH_SCP_USER_CALLBACKS) && \
|
||||
defined(NO_FILESYSTEM)
|
||||
|
||||
static int scpSendTestStatus(WOLFSSH* ssh, const char* fileName,
|
||||
enum WS_ScpFileStates state, ScpBuffer* file)
|
||||
{
|
||||
WOLFSSH_UNUSED(ssh);
|
||||
WOLFSSH_UNUSED(fileName);
|
||||
WOLFSSH_UNUSED(state);
|
||||
WOLFSSH_UNUSED(file);
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
static int scpSendTestStatusFail(WOLFSSH* ssh, const char* fileName,
|
||||
enum WS_ScpFileStates state, ScpBuffer* file)
|
||||
{
|
||||
WOLFSSH_UNUSED(ssh);
|
||||
WOLFSSH_UNUSED(fileName);
|
||||
WOLFSSH_UNUSED(state);
|
||||
WOLFSSH_UNUSED(file);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* The default no-filesystem send callback must accept an ScpBuffer whose
|
||||
* bufferSz exactly equals fileSz, in both single- and multi-chunk transfers,
|
||||
* and must still refuse a fileSz larger than the buffer. */
|
||||
static int test_ScpSendCallback_ExactFitBuffer(void)
|
||||
{
|
||||
WOLFSSH_CTX* ctx = NULL;
|
||||
WOLFSSH* ssh = NULL;
|
||||
ScpBuffer sendBuf;
|
||||
byte data[16];
|
||||
byte out[64];
|
||||
char fileName[DEFAULT_SCP_FILE_NAME_SZ];
|
||||
word64 mTime = 0;
|
||||
word64 aTime = 0;
|
||||
word32 totalSz = 0;
|
||||
int fileMode = 0;
|
||||
int result = 0;
|
||||
int ret;
|
||||
word32 i;
|
||||
|
||||
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
|
||||
if (ctx == NULL)
|
||||
return -1500;
|
||||
ssh = wolfSSH_new(ctx);
|
||||
if (ssh == NULL) {
|
||||
wolfSSH_CTX_free(ctx);
|
||||
return -1501;
|
||||
}
|
||||
|
||||
for (i = 0; i < (word32)sizeof(data); i++)
|
||||
data[i] = (byte)i;
|
||||
|
||||
WMEMSET(&sendBuf, 0, sizeof(sendBuf));
|
||||
WMEMCPY(sendBuf.name, "file.txt", WSTRLEN("file.txt") + 1);
|
||||
sendBuf.nameSz = (word32)WSTRLEN(sendBuf.name);
|
||||
sendBuf.buffer = data;
|
||||
sendBuf.bufferSz = (word32)sizeof(data);
|
||||
sendBuf.fileSz = (word32)sizeof(data);
|
||||
sendBuf.status = scpSendTestStatus;
|
||||
WMEMSET(fileName, 0, sizeof(fileName));
|
||||
|
||||
/* exact fit, single chunk: the whole file is returned, not aborted */
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST, "file.txt",
|
||||
fileName, (word32)sizeof(fileName), &mTime, &aTime, &fileMode, 0,
|
||||
&totalSz, out, (word32)sizeof(out), &sendBuf);
|
||||
if (ret != (int)sendBuf.fileSz)
|
||||
result = -1502;
|
||||
if (result == 0 && sendBuf.idx != sendBuf.fileSz)
|
||||
result = -1503;
|
||||
if (result == 0 && totalSz != sendBuf.fileSz)
|
||||
result = -1504;
|
||||
if (result == 0 && WMEMCMP(out, data, sizeof(data)) != 0)
|
||||
result = -1505;
|
||||
|
||||
/* exact fit, multi-chunk: final chunk lands on the buffer boundary */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 0;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != 10)
|
||||
result = -1506;
|
||||
}
|
||||
if (result == 0) {
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 10, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != 6)
|
||||
result = -1507;
|
||||
if (result == 0 && WMEMCMP(out, data + 10, 6) != 0)
|
||||
result = -1508;
|
||||
}
|
||||
if (result == 0) {
|
||||
/* transfer complete: a further continue reports EOF, not abort */
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 16, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_EOF)
|
||||
result = -1509;
|
||||
}
|
||||
|
||||
/* fileSz larger than the buffer is still refused before any copy */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = sendBuf.bufferSz + 1;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, (word32)sizeof(out), &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1510;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* file smaller than the buffer keeps working */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = 8;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, (word32)sizeof(out), &sendBuf);
|
||||
if (ret != 8)
|
||||
result = -1511;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* a NULL status callback must not crash the continue state */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.status = NULL;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != 10)
|
||||
result = -1512;
|
||||
if (result == 0) {
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime,
|
||||
&aTime, &fileMode, 10, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != 6)
|
||||
result = -1513;
|
||||
}
|
||||
sendBuf.status = scpSendTestStatus;
|
||||
}
|
||||
|
||||
/* idx one past the buffer end must abort, not report EOF; idx == fileSz
|
||||
* keeps the inner guard out of play so only the top guard can reject */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = sendBuf.bufferSz + 1;
|
||||
sendBuf.fileSz = sendBuf.bufferSz + 1;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1514;
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* a chunk that would run past the buffer end mid-continue must abort */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 10;
|
||||
sendBuf.fileSz = sendBuf.bufferSz + 4;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 10, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1515;
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* idx past fileSz mid-continue must abort; idx + bufSz stays inside
|
||||
* bufferSz here, so only the underflow guard can reject */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 5;
|
||||
sendBuf.fileSz = 4;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1516;
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* same underflow shape in the single file request state */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 5;
|
||||
sendBuf.fileSz = 4;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_SINGLE_FILE_REQUEST,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1517;
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.fileSz = sendBuf.bufferSz;
|
||||
}
|
||||
|
||||
/* a failing status callback aborts the continue state even after a
|
||||
* successful copy */
|
||||
if (result == 0) {
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.status = scpSendTestStatusFail;
|
||||
ret = wsScpSendCallback(ssh, WOLFSSH_SCP_CONTINUE_FILE_TRANSFER,
|
||||
"file.txt", fileName, (word32)sizeof(fileName), &mTime, &aTime,
|
||||
&fileMode, 0, &totalSz, out, 10, &sendBuf);
|
||||
if (ret != WS_SCP_ABORT)
|
||||
result = -1518;
|
||||
sendBuf.idx = 0;
|
||||
sendBuf.status = scpSendTestStatus;
|
||||
}
|
||||
|
||||
wolfSSH_free(ssh);
|
||||
wolfSSH_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
#endif /* WOLFSSH_SCP && !WOLFSSH_SCP_USER_CALLBACKS && NO_FILESYSTEM */
|
||||
|
||||
|
||||
/* ParseECCPubKey() Unit Test */
|
||||
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
|
|
@ -16254,6 +16475,14 @@ int wolfSSH_UnitTest(int argc, char** argv)
|
|||
testResult = testResult || unitResult;
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSH_SCP) && !defined(WOLFSSH_SCP_USER_CALLBACKS) && \
|
||||
defined(NO_FILESYSTEM)
|
||||
unitResult = test_ScpSendCallback_ExactFitBuffer();
|
||||
printf("ScpSendCallback_ExactFitBuffer: %s\n",
|
||||
(unitResult == 0 ? "SUCCESS" : "FAILED"));
|
||||
testResult = testResult || unitResult;
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_TEST_CAPTURING_ALLOCATOR
|
||||
unitResult = test_SshResourceFree_zeroesSecrets();
|
||||
printf("SshResourceFree_zeroesSecrets: %s\n",
|
||||
|
|
|
|||
Loading…
Reference in New Issue