Skip to content

Commit 53b848f

Browse files
committed
Test SFTP forged handles and namespace isolation
- Reject forged/raw-fd handles in Write/Read/FSetSTAT/FSTAT/Close - Isolate file vs directory handle-ID namespaces - Cover positive and forged FSTAT
1 parent 0c1fe55 commit 53b848f

3 files changed

Lines changed: 539 additions & 0 deletions

File tree

src/wolfsftp.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6123,6 +6123,88 @@ int wolfSSH_SFTP_RecvFSetSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
61236123
}
61246124

61256125
#endif /* _WIN32_WCE */
6126+
6127+
#if defined(WOLFSSH_TEST_INTERNAL) && !defined(USE_WINDOWS_API) && \
6128+
!defined(NO_FILESYSTEM)
6129+
/* Test-only plumbing for the forged-handle regression test in tests/regress.c.
6130+
*
6131+
* The SFTP request handlers buffer their status/handle reply into ssh->recvState
6132+
* via wolfSSH_SFTP_RecvSetSend(). When a test drives the handlers directly that
6133+
* state is not otherwise allocated, so these accessors let the test own its
6134+
* lifetime (and inspect the buffered reply) without exposing the private
6135+
* WS_SFTP_RECV_STATE type. */
6136+
6137+
/* Allocate ssh->recvState so handler replies are captured instead of leaked. */
6138+
int wolfSSH_SFTP_TestRecvStateInit(WOLFSSH* ssh)
6139+
{
6140+
if (ssh == NULL) {
6141+
return WS_BAD_ARGUMENT;
6142+
}
6143+
if (ssh->recvState == NULL) {
6144+
ssh->recvState = (WS_SFTP_RECV_STATE*)WMALLOC(
6145+
sizeof(WS_SFTP_RECV_STATE), ssh->ctx->heap, DYNTYPE_SFTP_STATE);
6146+
if (ssh->recvState == NULL) {
6147+
return WS_MEMORY_E;
6148+
}
6149+
WMEMSET(ssh->recvState, 0, sizeof(WS_SFTP_RECV_STATE));
6150+
}
6151+
return WS_SUCCESS;
6152+
}
6153+
6154+
/* Return the most recent buffered reply (data + size) produced by a handler. */
6155+
const byte* wolfSSH_SFTP_TestRecvReply(WOLFSSH* ssh, word32* sz)
6156+
{
6157+
if (ssh == NULL || ssh->recvState == NULL) {
6158+
if (sz != NULL) {
6159+
*sz = 0;
6160+
}
6161+
return NULL;
6162+
}
6163+
if (sz != NULL) {
6164+
*sz = ssh->recvState->buffer.sz;
6165+
}
6166+
return ssh->recvState->buffer.data;
6167+
}
6168+
6169+
/* Free ssh->recvState and any buffered reply. */
6170+
void wolfSSH_SFTP_TestRecvStateFree(WOLFSSH* ssh)
6171+
{
6172+
if (ssh != NULL) {
6173+
wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL);
6174+
}
6175+
}
6176+
6177+
/* Return the number of open file handles tracked for the session. Lets the
6178+
* handle-cap and close-failure regression tests observe the tracking list
6179+
* without exposing the private WS_FILE_LIST type. */
6180+
int wolfSSH_SFTP_TestFileHandleCount(WOLFSSH* ssh)
6181+
{
6182+
WS_FILE_LIST* cur;
6183+
int count = 0;
6184+
6185+
if (ssh == NULL) {
6186+
return 0;
6187+
}
6188+
for (cur = ssh->fileList; cur != NULL; cur = cur->next) {
6189+
count++;
6190+
}
6191+
return count;
6192+
}
6193+
6194+
/* Close the underlying descriptor of the head tracked file handle out of band,
6195+
* leaving the node in the list with a now-stale fd. The next RecvClose on that
6196+
* handle will see its close() fail, exercising the path that must still drop
6197+
* the handle from the tracking list. Returns WS_SUCCESS if a node was found. */
6198+
int wolfSSH_SFTP_TestInvalidateHeadFd(WOLFSSH* ssh)
6199+
{
6200+
if (ssh == NULL || ssh->fileList == NULL) {
6201+
return WS_BAD_ARGUMENT;
6202+
}
6203+
WCLOSE(ssh->fs, ssh->fileList->fd);
6204+
return WS_SUCCESS;
6205+
}
6206+
#endif /* WOLFSSH_TEST_INTERNAL && !USE_WINDOWS_API && !NO_FILESYSTEM */
6207+
61266208
#endif /* !NO_WOLFSSH_SERVER */
61276209

61286210

0 commit comments

Comments
 (0)