wolfSSH_ChannelGetSessionGranted() reports whether a shell, exec or
subsystem request on a channel has been answered CHANNEL_SUCCESS, so an
application can tell a second request from the first without reaching
into WOLFSSH_CHANNEL.
- the flag is still clear for the request a session callback is
answering, so a set flag is an earlier request's grant
- wolfsshd's session callback reads the grant through it
wolfSSH_ChannelCommandIsScp() reports whether a channel's session
command starts an SCP transfer, taking "scp" only as its own token. The
divert in wolfSSH_accept() and an application's exec callback both ask
it, so the two cannot disagree about what the SCP server is handed.
- an exec of "scpbackup foo" runs as an ordinary exec
- a command carrying a NUL within the recorded command size is not an
SCP command; ParseScpCommand() walks a C string, so a NUL would drop
whatever follows it
- wolfsshd's session callback asks through the same helper
DoChannelRequestSession() serves the shell, exec and subsystem arms
together, and its debug line labels the string it read. A subsystem
request labels that string "subsystem" rather than "command".
- unit.c: renumber the shell-after-exec failure to follow the codes
above it
The generic channel request callback may free the channel it was handed,
so DoChannelRequest() looks it up again before the type handling reads
it. Gone, the request ends there, and a reply the peer wanted fails on
the missing channel the way one after a typed session callback does.
- ssh.h says the callback may free its channel and what the request does
from there
- regress.c frees the channel from the callback on each of the three
answers, and on a pty-req, the type that wrote to the channel outside
a session request
wolfSSH_CTX_SetChannelReqAnyCb() and wolfSSH_CTX_SetGlobalReqAnyCb()
register a callback consulted first for a channel or global request,
with the name, the type-specific part, and whether a reply is wanted. A
tri-state answer grants, refuses, or leaves it to the handling already
there, so a policy reaches the types with no hook of their own.
- the name is the one that arrived, since a copy into a buffer truncates
a long name and ends it at an embedded NUL, and a policy has to answer
on what the peer sent
- a grant still parses and records what the library needs, so a granted
session request commits the session, the typed callbacks are not
consulted, and a granted unknown type is answered CHANNEL_SUCCESS
- a port-0 tcpip-forward skips the callback, since only the forward
callback can report the port bound and a policy that had bound a
listener would then have to be refused, per RFC 4254 7.1
- a client refuses tcpip-forward and cancel-tcpip-forward ahead of any
policy, matched on the name that arrived so it holds in a build with
no forwarding, where neither name is in the name table
- window-change, exit-status and exit-signal are cleared of a reply
before the handling runs, so a refusal leaves them unanswered too, per
RFC 4254 6.7 and 6.10
- regress.c covers the answers, the data delivered, the names that do
not fit a copy, and which callbacks each answer leaves out
The echoserver's -A mode runs an accepted scp command through
wolfSSH_SCP_accept(). Reaching that call's want retry path takes a
non-blocking server, which -N supplies.
- copy to and from an app-driven server in scp.test
- check the entry point's null-session argument
With -A the echoserver drives its own channels: accept() returns at
userauth and the callbacks below start the shell, SFTP or SCP session.
Off by default. The two modes are exclusive, since the callbacks answer
the session requests the accept state machine otherwise answers itself.
- wsShellStartCb() forks the pty, so it is registered in either mode,
and claims the channel only once there is a shell behind it; a second
request is refused rather than forking over the running shell
- wsExecStartCb() takes an "scp " command as a transfer and any other
command as a session, and is registered in either mode, since the
legacy path has always started a shell for an exec request too
- wsSubsysStartCb() is registered only with -A, as accept() serves sftp
itself, and guards a NULL command, which a truncated request leaves
behind
- ssh_worker() drives the session through shellCtx.appFd, claims the
channel itself when no callback did, and leaves an SFTP or SCP
handoff through its cleanup so the pty master still closes
- open the agent channel from the select loop, since the peer's
auth-agent-req lands after accept() has returned, and read the
listener from the context each pass because it appears mid-loop
- resume a subsystem accept that returns a want, waiting on the socket
between attempts rather than spinning
- close the accepted socket again, clear fwdFd on EOF or reset, and
stay in the loop on WS_REKEYING, which the read arm already handles
- key ChildRunning's sig_atomic_t on WOLFSSH_SHELL, the only build
with the SIGCHLD handler that writes it, so a target whose libc has
no signal.h still compiles
- ask for echo mode in the keyboard-interactive test, which has no
account on the host for the shell callback to fork a shell for
A shell, exec or subsystem request changes the channel only once the
callback accepts it. The session type and command are set for the
callback to read and put back if it refuses, and CLIENT_DONE follows
acceptance alone, so wolfSSH_accept() stays where it is rather than
reporting a session it answered CHANNEL_FAILURE as established.
- DoChannelRequestSession() carries the three arms, which differed only
in the type and the callback consulted
- a refusal puts the type and command back, so a grant an earlier
request won still stands
- FreeChannelCommand() wipes and releases a command line for both
ChannelDelete() and the refusal path
- unit.c drives a refused shell, exec and subsystem request through
DoChannelRequest() and checks nothing was committed, and that a
refusal after a grant puts the earlier command back whole
- regress.c checks accept() stays at ACCEPT_SERVER_CHANNEL_ACCEPT_SENT
on a refused shell, and that the sftp gate and both diverts ask for
the grant alone
Issue: F-8852
- wolfSSH_OutputPending() moves from wolfssh/internal.h to
wolfssh/ssh.h as a WOLFSSH_API taking a const WOLFSSH*, defined
in src/ssh.c beside the other public calls.
- wolfSSH_worker() puts the send's code in the return in place of
an event when the flush fails outright, and gates its flush on
wolfSSH_OutputPending(). wolfssh/ssh.h states both.
- Both wolfsshd shell loops, both echoservers and
ReceiveScpMessage() dispatch on wolfSSH_worker()'s return, and
call wolfSSH_get_error() only to tell a transient failure from a
terminal one. The POSIX wolfsshd loop sets wantWrite from
wolfSSH_OutputPending().
- Five worker tests expect the send's code where they expected the
event, and tests/testsuite.c calls wolfSSH_OutputPending().
- wolfSSH_worker() calls wolfSSH_SendPacket() whenever
ssh->outputBuffer holds bytes and the session is not
disconnected. ssh->error keeps the receive's code when the
receive failed, and the close's when a WS_CHANNEL_CLOSED pass
hard-failed its flush; WS_REKEYING is withheld on a failed
flush. Drops the second DoReceive(), the WOLFSSH_TEST_BLOCK
fork and the separate WS_CHANNEL_CLOSED flush.
- BundlePacket() resets ssh->outputBuffer.length to
ssh->packetStartIdx when the framing fails. wolfSSH_shutdown()
reports WS_WANT_WRITE when its close-read leaves output queued,
and the send's own error in place of it when that send failed.
SendPacketFlush() records its code in ssh->error on every
transport failure path, and wolfSSH_TriggerKeyExchange() writes
it only when SendKexInit() fails.
- portfwd, client and scpclient accept WS_WANT_WRITE from
wolfSSH_shutdown(); in scpclient the close-message drain runs
on it.
- wolfssh/ssh.h drops WS_WINDOW_FULL from wolfSSH_worker() and says
to read the return and wolfSSH_get_error() as independent channels
on every pass.
- Twenty unit tests and the extended TestWorkerReportsDisconnect
cover what ret and ssh->error hold after a receive, send, buffer,
callback or framing failure.
test_wolfSSH_set_fd() compares the NULL return of wolfSSH_get_fd()
against the platform invalid-socket sentinel, the value wolfSSH_new()
initializes rfd/wfd to. The old check only asserted the result was not
WS_SUCCESS, which the previous WS_BAD_ARGUMENT return also satisfied.
wolfSSH_accept() hands a session to the built-in SCP or SFTP server
only when the request naming it was answered CHANNEL_SUCCESS. A
callback that refuses one sends CHANNEL_FAILURE, yet sessionType and
command are recorded ahead of that answer and stay set, so the diverts
read a refused session as a served one.
- gate both diverts on channel->sessionGranted, as
wolfSSH_SFTP_accept() already gates the app-channels path
- the SCP divert tests the channel list itself, having no command
lookup ahead of it to do that
- cover each divert with a refused request and a granted control
The peer's exec or subsystem command line is wiped ahead of both frees
that release it, ChannelDelete() and the GetStringAlloc() that replaces
it on a repeat request, the way ChannelDelete() already wipes the
decrypted inputBuffer just above. A command line can carry a password
or a token among its arguments.
- ScrubChannelCommand() leaves the free to GetStringAlloc(), so a parse
that fails behind it holds no dangling pointer
- cover both wipes with the retain-on-free allocator, the replacement
through wolfSSH_TestDoChannelRequest()
- release the test's hand-built channel on a setup failure
Issue: F-8850
DoChannelRequest() records the session type and asks the exec and
subsystem callbacks whether to grant a session only when the command
string parsed. A failed parse is refused on ret alone, and
channel->command still holds an earlier request's value rather than the
one being answered.
- cover a command length header running past the end of the packet, on
exec and on subsystem
Issue: F-11674
An application vetting an exec or subsystem request in its channel
request callback is handed the command as a C string, which stops at an
embedded NUL. wolfSSH_ChannelGetSessionCommandSz() and
wolfSSH_GetSessionCommandSz() report the parsed wire length, so a
callback can match a name whole the way DoChannelRequest() does.
- both accessors report 0 for a NULL channel or session
- wolfSSH_GetSessionCommand() defers to the channel accessor
- the sftp divert in wolfSSH_accept() asks the accessor for the length
- correct the trace name in wolfSSH_ChannelGetSessionCommand()
- cover a callback seeing "sftp\0evil" through exec and subsystem
CheckSftpAcceptRefusesUngranted() drives the same refusal two ways, a
registered subsystem callback saying no and app channels standing in
for a missing one, and asserted nothing that told them apart. Assert
the call count each case expects.
The built-in SFTP server takes a session only when the subsystem name
is sftp, matched whole. DoChannelRequest() keeps the parsed length in
channel->commandSz, so neither wolfSSH_SFTP_accept()'s grant gate nor
wolfSSH_accept()'s divert serves "sftpx" or "sftp\0evil".
- cover a granted name longer than sftp, one of its length, and one
running past an embedded NUL
- cover the divert with those three names and a control that diverts
- exec keeps its command length too
Issue: F-11665
wolfSSH_SFTP_accept() serves an application-driven session only on a
channel whose subsystem request was answered CHANNEL_SUCCESS.
DoChannelRequest() records the session type and command before it
decides, and leaves both set on a refusal, so they cannot say by
themselves whether anything was granted.
- add channel->sessionGranted, set from the answer a shell, exec or
subsystem request gets rather than from the request arriving
- look the channel up again before recording it: a callback may close
its own channel, and wolfSSH_ChannelFree() frees it
- log a request's strings where they are known good: once the parse
has succeeded, and ahead of a callback that may free the channel
- gate the app-channels path on that flag alongside the session type
and the command
- cover a refusal from both sides, no callback registered and a
callback that rejects, and a callback that frees its channel
In application-driven mode wolfSSH_accept() parks at userauth, so the
sftp test its divert applies never runs. wolfSSH_SFTP_accept() applies
it itself: the session channel must be a subsystem the application's
callback granted sftp on, or the call returns WS_INVALID_STATE_E and
leaves the wire alone without recording an error.
- gate the app-channels branch on wolfSSH_GetSessionType() and
wolfSSH_GetSessionCommand(), the same test accept() makes
- ask for that grant in every accept state: below the user-auth stop
accept() returns with no channel open, and past the stop there is no
accept() left that could have checked anything
- say in ssh.h that the mode serves SFTP through that grant and never
reaches the SCP entry point
- regress.c refuses the call with no channel, ahead of accept(), on a
granted shell and on an established one, and serves an INIT on a
granted sftp subsystem
DoChannelRequest() reads ssh->appChannels when the request arrives, so
turning the mode on after accept() established the session still refuses
an uncallbacked shell, exec or subsystem request from then on. Only
accept()'s stopping point is pinned, by the guard around stopState.
- say the flag reaches the requests that follow, and that what it cannot
do is move where accept() returns
- drive a shell request over the wire in both modes from the late-enable
test, pinning the behaviour the header now describes
wolfSSH_SetAppChannels() changes where wolfSSH_accept() stops and what
becomes of a session request with no callback behind it, so both modes
are exercised.
- regress.c drives a server with the pivot on, one with a shell
callback and one without, and checks accept() stops at
ACCEPT_SERVER_USERAUTH_SENT
- regress.c pins the context setter, the session's inheritance of it,
and that turning it on after accept() established the session still
returns
- regress.c re-enters a parked accept() with output still queued, which
is the one path that flushes before reading the state, and pins that
it leaves the state on the stop
- unit.c checks DoChannelRequest() refuses a shell, exec and subsystem
request with no callback once the pivot is on
- The WOLFSSH_ZEPHYR branch of FindNextDirEntry() loops while the
entry name is "." or "..", matching the POSIX and Windows
branches, in place of while (1).
- tests/api.c gains test_wolfSSH_SCP_SendRecursiveEntry(), staging a
directory holding one file and driving three
WOLFSSH_SCP_RECURSIVE_REQUEST calls through wsScpSendCallback(),
checking the entry name, size, and bytes placed in buf. dirPath
is rooted at CONFIG_WOLFSSH_SFTP_DEFAULT_DIR under WOLFSSH_ZEPHYR
and at "./scp_recur_entry" otherwise.
- The test is gated on WOLFSSH_SCP, with WOLFSSH_SCP_USER_CALLBACKS,
NO_FILESYSTEM and NO_WOLFSSH_DIR unset, carries an empty stub
otherwise, and is called from wolfSSH_ApiTest().
- scpStageRecurFile() writes that fixture file.
Issue: F-13315
- DoChannelOpen refuses an auth-agent open on a server endpoint, and on
a client with the agent disabled or connectState below
CONNECT_CLIENT_CHANNEL_AGENT_REQUEST_SENT.
- Both answer OPEN_ADMINISTRATIVELY_PROHIBITED; the ssh->agent check
stays as the resource check behind them.
- Cover the refusals before the request, with the agent disabled, over
an accepting channelOpenCb, and on a server, plus the accepted open
past the request; each refusal asserts ssh->error stays clean.
- Move TestAgentChannelNullAgentSendsOpenFail to a client harness, and
have the agent tests set the agent flag explicitly.
Issue: F-13389
TEST_NULL_DEVICE, TEST_SETENV, TEST_UNSETENV, and TEST_MKDIR were
gated on USE_WINDOWS_API, while the arpa/inet.h/direct.h include
guard a few lines above keys on _WIN32. Whether mkdir(), setenv(),
and /dev/null work as expected is a libc availability question, not
a wolfSSH API-selection one, so _WIN32 is the more direct fit and
now both guards in this file agree.
No behavior change: USE_WINDOWS_API is only ever set when _WIN32 is
already defined, so this does not change which branch any current
build takes.
Verified with the MinGW cross compiler both with WOLFSSH_SFTP defined
and without, and confirmed a plain Linux build still passes
tests/regress.test and tests/unit.test.
A local run with ./configure --enable-smallstack (no --enable-sftp)
showed WMKDIR as an implicit, undeclared function under MinGW.
wolfssh/port.h only defines WMKDIR when WOLFSSH_SFTP, WOLFSSH_SCP, or
WOLFSSH_SSHD is enabled, but TestKnownHostsLastEntry itself is gated
on WOLFSSL_BASE64_ENCODE alone, so it compiles in configurations
where none of those three are on and WMKDIR does not exist.
Add a small TEST_MKDIR macro next to the existing TEST_SETENV and
TEST_UNSETENV ones, backed directly by _mkdir() under USE_WINDOWS_API
and mkdir() otherwise, and use it in place of WMKDIR. The Windows
branch needs direct.h for _mkdir's declaration; include it next to
the existing _WIN32 guard around arpa/inet.h at the top of the file.
Verified with the MinGW cross compiler both with WOLFSSH_SFTP defined
and without, and confirmed a plain Linux build configured with
--enable-smallstack (no --enable-sftp) still passes tests/regress.test
and tests/unit.test.
The next mingw-regress failure after the previous fix was
TestKnownHostsLastEntry failing its "ready" assertion. It calls
open("/dev/null", O_RDONLY) to point stdin at EOF while it drives the
known_hosts prompt, but Windows has no /dev/null; the call simply
fails there, so open() returns -1 and every check gated on ready is
skipped.
Add a TEST_NULL_DEVICE macro that resolves to "NUL" under
USE_WINDOWS_API and "/dev/null" otherwise, and use it at both call
sites in this file: TestKnownHostsLastEntry, and the same pattern in
TestPasswordEofNoCrash a bit earlier, which happens to be masked in
CI by its own isatty() guard but would hit the same bug if ever run
against a real terminal on Windows.
apps/wolfssh/common.c declared a CONSOLE_SCREEN_BUFFER_INFO local in
ClientSetEcho that nothing ever read; the Windows echo toggling never
grew the code that would have used it. Drop the unused declaration.
wolfssh/port.h defined WSTRSEP as strsep(), a BSD extension MSVCRT
and MinGW do not provide. Add a portable wstrsep() in src/port.c,
matching the wstrnstr/wstrncat/wstrdup pattern already used for other
missing string functions, and route WSTRSEP through it under
USE_WINDOWS_API.
tests/regress.c called the two argument POSIX mkdir(path, mode) and
setenv()/unsetenv() directly in TestKnownHostsLastEntry. Use the
existing WMKDIR macro for the directory creation, and add small
TEST_SETENV/TEST_UNSETENV macros backed by _putenv_s() on Windows so
the HOME juggling this test does still works there.
src/wolfsftp.c had three separate issues in code paths that had never
been compiled before this job existed. wolfSSH_SFTP_RecvOpen declared
a flagsAndAttrs DWORD that nothing read, since WS_CreateFileA is
called with a hardcoded FILE_ATTRIBUTE_NORMAL instead.
wolfSSH_SFTP_RecvOpenDir compared a signed loop counter against a
sizeof expression while building ssh->driveList, so make the counter
word32. wolfSSH_SFTP_Put passed &state->rSz, an int, to ReadFile()'s
DWORD* output parameter; read into a local DWORD and copy it into
state->rSz afterward, since that field is also assigned from
WFREAD() on non-Windows builds.
src/wolfterm.c's wolfSSH_DoOSC never used its handle parameter. Mark
it with WOLFSSH_UNUSED rather than removing it, since the parameter
matches the signature its two call sites already pass and future OSC
handling such as window titles is a natural use for it.
Verified against a real x86_64-w64-mingw32 cross compiler with a
config.h edited to match the sizes and header availability the
actual Windows CI run reported (SIZEOF_LONG 4, HAVE_SYS_IOCTL_H
undefined, and so on): every file this job compiles builds cleanly
under the same -Werror flag set. Also reconfirmed a clean, unmodified
Linux build still passes both tests/regress.test and tests/unit.test.
MinGW-w64 does not ship arpa/inet.h; guard tests/regress.c's include
the same way apps/wolfsshd/auth.c already does, since htonl/ntohl end
up declared via the winsock2.h wolfSSL's headers pull in later in the
same translation unit.
wolfssh/test.h guarded its MSVC #pragma warning(disable:4996) with
USE_WINDOWS_API alone, which is also true for MinGW's gcc; gcc treats
the unrecognized pragma as an error under -Werror. Require _MSC_VER
too, matching the existing ALIGN16 pragma guard in wolfssh/internal.h.
wolfSSH_CleanPath's Windows/Nucleus drive-letter cleanup re-declared
`i` in a nested scope, shadowing the function's own `i` used by every
other loop in the function. Hoist `j` to the function's declarations
(guarded by the same #if so non-Windows/Nucleus builds don't get an
unused-variable warning) and drop the now-unnecessary block so the
loop reuses the outer `i`.
Verified locally: autoreconf + configure + make tests/regress.test
tests/unit.test builds clean and both binaries pass on Linux.
Added more test vectors as well as locking in behavior of VerifyProtoId
Added round trip test for proto id
Comment fixes
New define for proto id min size
wolfSSH_CTX_SetSshProtoIdStr() now rejects a string that is not
CRLF-terminated, exceeds 255 bytes counting the terminator, or
carries a CR or LF in the body. DoKexInit() subtracts the two
terminator bytes from the length when hashing it, so an
unterminated string underflowed the hash length.
Issue: F-10571
partial progress
wolfSSH_AGENT_ChannelOpen() settles its channel and agent on whether
the open reached the peer, which SendPacketDelivered() answers from the
flush count. The highwater callback runs after the last byte is out, so
its failure arrives as this send's return, and the rollback read that
as a send that never left.
- give SendPacketDelivered() external linkage, contract in internal.h
- note in agent.h that an error raised once the open is on the wire
leaves the channel open
- test a failing highwater callback: the channel and the agent stand,
and the poll after it is the idempotent one
wolfSSH_AGENT_ChannelOpen() answers a poll on a session that is over
with WS_FATAL_ERROR and WS_DISCONNECT in ssh->error, the shape every
other public sender uses: no channel opened, nothing on the wire,
RFC 4253 section 11.1. wolfSSH_accept() gates the open it drives, so
the new public entry point is the only way in.
- promote SendAfterDisconnect() to WOLFSSH_LOCAL so agent.c uses the
same helper as every other public sender
- leave an open queued before the disconnect unflushed, the rule
wolfSSH_shutdown() applies to all but its own disconnect
- keep WS_DISCONNECT in ssh->error at the accept() call site, which
used to overwrite it with the status the open returns
The one server-side site that opens auth-agent@openssh.com sits inside
wolfSSH_accept(), so an application driving its own channels cannot
reach it: the session records the request and no channel follows.
- add wolfSSH_AGENT_ChannelOpen(), the same open lifted out of accept(),
which still calls it
- it reports WS_BAD_ARGUMENT until the peer asks and on a client
session, and is idempotent after, so an application can poll it
- publish the agent on a queued open too, so a retry after WS_WANT_WRITE
finds it rather than opening a second channel and leaking the first
- flush what is left of a queued open on the next call, rather than
reporting a success the peer never saw
- record ssh->error from the send alone, so neither a poll ahead of the
request nor a failed allocation stops accept() continuing
enable SHA1 with windows cert store test case
expand test cases, adjust to authorized key file, minor dead code adjustments
add more documentation, refactor duplicate code sections, clean up test cases, more adjustments to logging spamming protections
add Windows cert store test case
make windows cert feature default disabled and simplify macro guard
additional unit tests, advertise x509 and pubkey, use CN to match username, build check for WOLFSSL_SYS_CA_CERTS, fix for CM ref count
additional build test, uniform enum name, fail on unkown cert store ecc curve, tie in of loading whole cert store for sys CA's
WOLFSSH_FWD_LOCAL_CLEANUP now fires, and what must not fire matters as
much as what must: the gate is the channel's own setup record, not its
type.
- a peer-opened direct-tcpip channel reports SETUP, CHANNEL_ID, then
CLEANUP when it closes
- a forwarding channel this side opened reports no cleanup, and a
session channel no forwarding action at all
- an open that fails after a successful setup still reports the cleanup,
and a setup that reported failure draws none
- freeing the session, or the channel with wolfSSH_ChannelFree(),
reports it, and the session free after a channel free does not
report it again
WOLFSSH_FWD_LOCAL_CLEANUP is part of the public WS_FwdCbAction contract
and the library never sent it, so an application's handler never ran and
every peer-opened forward leaked what its setup callback allocated.
ChannelDelete() now sends it, so a peer close, an open refused after the
setup ran, wolfSSH_ChannelFree(), and freeing the session all report it.
- record the setup in a new fwdSetupTxd bit on WOLFSSH_CHANNEL: a
locally opened forward gets no LOCAL_SETUP, so the channel type alone
cannot say whether the application holds anything
- clear the bit when the cleanup goes out, so a channel reports it once
- pass the channel's id in the port parameter, the way
WOLFSSH_FWD_CHANNEL_ID does, so an application with two forwards can
tell which one ended
- TestDirectTcpipFwdCbRejectsChannelId now counts three callback calls
wolfSSH_shutdown() flushes whatever a short send left in the output
buffer, not just a queued disconnect. A rejected auth's
USERAUTH_FAILURE has no channel, and DoChannelClose() retires the
channel as soon as the close is bundled, so neither had anything
left to carry the retry.
- The gate still refuses a flush once the peer has disconnected,
unless our own disconnect is the thing queued.
- A flush that finishes on a live session clears the WS_WANT_WRITE
the short send latched, as the disconnected path already did.
- ssh.h documents the widened flush and the WS_WANT_WRITE a short
one leaves wolfSSH_shutdown() returning.
- Tests cover the rejected auth with no channel and the close whose
channel DoChannelClose() already retired.
- A flush that short-sends on a live channel leaves the teardown
queued behind it. The retry adds no second EOF or close, and skips
the close wait while its own flush is still short.
- A teardown send that drained the leftovers settles the flush; one
that failed outranks it, since a reset leaves the buffer intact.
Only the shell hook had coverage, so nothing held DoChannelRequest() to
handing the command and session type to the exec and subsystem callbacks
or to answering with what they return.
- assert each callback sees the session type and the command string the
request carried, and the ctx set on the session
- assert an accepting callback draws CHANNEL_SUCCESS and a rejecting one
CHANNEL_FAILURE, whether it rejects with a WS_ error or a bare nonzero
wolfSSH_CTX_SetChannelCloseCb() had no caller in tests/, examples/ or
apps/, so nothing held DoChannelClose() to running the hook before it
retires the channel.
- assert the callback runs with the closing channel's id and the ctx
set on the session
- assert the channel is still on the list inside the callback and gone
by the time the caller is told
- a rejecting callback changes nothing: the return is discarded and the
close completes
The confirmation and failure hooks reach an application only through
DoChannelOpenConf() and DoChannelOpenFail(), and nothing in tests/ or
apps/ registered either, so both arms shipped unexercised.
- assert the confirm callback runs with the peer's channel id, window
and packet size already recorded
- assert the failure callback runs while the channel is still findable,
before DoChannelOpenFail() removes it
- count each hook separately, so a test can tell which one ran
- a rejecting confirm hook fails the receive and leaves the open
unfinished; a rejecting failure hook fails it and leaves the channel
on the list
- seed the unconfirmed channel through ChannelNew() and ChannelAppend(),
the state an outstanding open leaves behind
RFC 4254 6.1 has a session open travelling client-to-server, so
DoChannelOpen refuses one on a client endpoint in the type switch,
ahead of ChannelNew and any channelOpenCb. OpenSSH and Dropbear
refuse it outright too, with no application opt-in.
- Answer OPEN_ADMINISTRATIVELY_PROHIBITED, as the forwarded-tcpip
direction gate below it does.
- Widen the client harness guard in regress.c so it builds without
WOLFSSH_FWD, and hoist AcceptChannelOpenCb out of the forwarding
block.
- Cover the refusal, and the gate outranking an accepting
channelOpenCb.
Issue: F-8836
TestClientOnlyKexMsgsBlocked now asserts that 31 and 33, the ids a
client does receive, stay allowed where the handshake expects them, so
widening the role check into a 30-34 range fails the suite. The
IsMessageAllowed() comment records the receive-only policy and the
per-KEX-method id namespace.
- assert id 33 is allowed once expectMsgId is MSGID_KEXDH_GEX_REPLY
- assert expectMsgId starts at MSGID_NONE, so the role check is what
rejects the blocked ids
- recheck all three blocked ids during a rekey on an established session
wolfSSH_CTX_SetSshProtoIdStr() now rejects a string that is not
CRLF-terminated, exceeds 255 bytes counting the terminator, or
carries a CR or LF in the body. DoKexInit() subtracts the two
terminator bytes from the length when hashing it, so an
unterminated string underflowed the hash length.
Issue: F-10571
Gates ML-DSA composites behind WOLFSSH_NO_MLDSA_COMPOSITES.
Deduplicates key handling and uses heap allocation for
composite buffers when compiling for small stacks.
FwdRemoteSettle() folds a port-0 forward the peer bound onto a
registration already standing at that port. A request still in its
send window resolved its forward before the send, so it names the
survivor from here on, the same as the queued replies do.
- Add FwdPendingRebind() and call it ahead of the unlink, so a cancel
whose send pumped in the answer commits naming a forward rather
than nothing.
- Regression test drives the port-0 answer in from the cancel's send
callback.
FwdPendingCommit() looks for a registration of the bind its entry names
before linking it. An answer pumped in from the send can settle a
port-pending forward onto that port, and the fold in FwdRemoteSettle()
runs before the committing entry joins the list.
- replies queued against the folded entry move to the one that stands
at the bind
- test drives that answer from the IO send callback; the bind takes two
registrations without the fold, and one cancel leaves one matching