Commit Graph

140 Commits (10d97e151fbab0c8d06be441cdc8c2e121bca65d)

Author SHA1 Message Date
John Safranek 6b954da7f7 tests: cover the application-driven SCP start
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
2026-09-15 09:31:16 -07:00
John Safranek 971f2f01e8 echoserver: answer session requests in callbacks
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
2026-09-15 09:31:16 -07:00
John Safranek 28bf47d2a0 tests: pin the get_fd invalid-socket sentinel
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.
2026-09-10 18:03:59 -07:00
Yosuke Shimizu c039486e52 scp: return directory entries from the Zephyr entry walk
- 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
2026-09-10 15:28:17 -07:00
JacobBarthelmeh 4baba585d5 fix for flags handling, ocsp case, macro guards, unused variable, changed default from MY to required to be set
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
2026-09-05 01:32:02 -07:00
Yosuke Shimizu f1b6a99d56 wolfsftp, port: apply the attributes SETSTAT and FSETSTAT acknowledge
- SFTP_SetFileAttributes() and SFTP_SetFileAttributesHandle() carry
  out the size, ownership, permission and timestamp requests while ret
  is WS_SUCCESS, bound the size with wResolveOffset() against
  WOLFSSH_MAX_FILE_OFFSET, and set WS_UNIMPLEMENTED_E where the port
  defines no wrapper.
- wolfSSH_SFTP_RecvSetSTAT() and wolfSSH_SFTP_RecvFSetSTAT() answer
  WOLFSSH_FTP_UNSUPPORTED for WS_UNIMPLEMENTED_E; wolfSSH_SFTP_CHMOD()
  sets the attribute flags to WOLFSSH_FILEATRB_PERM before sending.
- port.h adds WTRUNCATE, WFTRUNCATE, WCHOWN and WFCHOWN for the POSIX
  port, and defines WSETTIME and WFSETTIME over the existing WUTIMES
  and WFUTIMES helpers in place of their (0) definitions.
- SFTP_SetMode() guards on _WIN32_WCE in place of USE_WINDOWS_API, and
  port.c adds WS_ChmodA(), which trims the SFTP leading root and calls
  _wchmod(); it and the WCHMOD mapping to it are left out under
  _WIN32_WCE, which keeps the _chmod mapping.
- tests/regress.c adds TestSftpSetStatAttributes(); tests/sftp.c adds
  a chmod of a directory and installs SFTP_TEST_UMASK in place of the
  sftpTestUmask static; the STATE_SET_ATR_SEND case in tests/api.c
  sets atr.flags to WOLFSSH_FILEATRB_PERM.

Issue: F-11658
2026-09-02 08:42:36 -07:00
Yosuke Shimizu 5151732d6b wolfsftp: keep the local file when resuming a get
- The Windows local open in wolfSSH_SFTP_Get() passes OPEN_EXISTING
  when the write offset is nonzero and CREATE_ALWAYS otherwise, and
  drops FILE_APPEND_DATA from the desired access. A new
  DWORD creationDisp replaces the block-scoped desiredAccess.
- That open reports INVALID_HANDLE_VALUE as WS_BAD_FILE_E and moves to
  STATE_GET_CLEANUP; the OVERLAPPED offset is set from gOfst on every
  open rather than only when resuming.
- STATE_GET_LOOKUP_OFFSET clears a saved offset when the remote size
  STATE_GET_LSTAT stored in state->attrib is no larger than it, and
  again when the local destination does not hold exactly that many
  bytes. The destination stat overwrites state->attrib.
- tests/api.c adds test_wolfSSH_SFTP_GetResume() and its
  sftpGetToCompletion() helper, six cases over the resume paths, built
  where the hosted file wrappers are available.
- test_wolfSSH_SFTP_PutResume() and test_wolfSSH_SFTP_GetResume() drop
  the WOLFSSH_ZEPHYR k_sleep() block their bodies exclude.
- .gitignore covers every wolfssh_*.tmp the api tests leave behind on
  an aborted run, replacing the known_hosts-only entry.

Issue: F-12547
2026-09-01 09:19:10 -07:00
John Safranek e396a0a4b6 Match forwarded-tcpip to registered forwards
RFC 4254 7.2 says a forwarded-tcpip open answers a forward the client
asked for, so refuse an open naming anything else. Enforcement starts at
the first wolfSSH_FwdRemoteSetup(), leaving a client that frames
tcpip-forward itself unaffected.

- Register each setup per session. A wildcard bind matches on port alone.
- Port 0 now requires want-reply, since only the reply names the port.
- Repeat setups of one bind share a registration, so one cancel undoes
  it.
- A cancel stops matching as it goes out, but a want-reply cancel stays
  registered until the peer answers: a refusal leaves the listener up.
- Replies carry no request id, so a per-session queue pairs them in send
  order. A want-reply wolfSSH_global_request() takes a slot as well.
- Registration is split around the send: allocate first, link once the
  request reached the wire.
- A request resolves its registration on commit, since sending runs
  application callbacks that can reenter the library.
- Tests cover matching, cancel, overlapping requests, send-order
  pairing, port 0, registration around the send, and reentrancy from a
  callback.
- They drive a client session, so they sit outside the server-only block
  in regress.c and run in a --disable-server build. The harness struct,
  its teardown, the channel-open-failure helpers and the forwarding
  callback moved out with them, shared with the server-side tests.

Contracts for wolfSSH_FwdRemoteSetup(), wolfSSH_FwdRemoteCancel() and
wolfSSH_global_request() are in wolfssh/ssh.h.

Issue: ZD-22195
2026-08-31 20:35:56 -05:00
John Safranek 5fb7be429b fix: validate keyboard-interactive prompts
RFC 4256 section 3.3 forbids an empty prompt, and RFC 4251 section 5
allows only 0 or 1 in a boolean field. Validation runs before the
payload is sized, so the sizing and building passes agree.

- reject a zero-length prompt or one with a NULL buffer
- reject a prompt count with the arrays unset
- reject a prompt over WOLFSSH_MAX_PROMPT_SZ, which the sizing pass
  would otherwise sum into a wrapped payload size
- normalize the outgoing echo byte to 0 or 1
- cover the rejections in tests/api.c, the echo byte in tests/auth.c

Issue: F-10583, F-10584
2026-08-28 17:45:20 -05:00
John Safranek 245baf01f0 Document the wolfSSH_SetChannelType contract
The refusals added for names the peer cannot use changed the return
contract of a public API whose block comment still promised only
WS_SUCCESS. There is no dox_comments entry, so that comment is all an
embedder has.

- Spell out each WS_BAD_ARGUMENT case, the keep-the-stored-name rule,
  and that a refused call leaves the selected type alone.
- api.c asserts connectChannelId across the refusals. It is the field
  SendChannelRequest() switches on, so moving the checks back below the
  assignment would otherwise pass.
2026-08-26 12:11:15 -05:00
John Safranek 4d9f6a9721 Report a channel name the peer cannot use
wolfSSH_SetChannelType() discarded an exec or subsystem name it could
not use and still returned WS_SUCCESS. SendChannelRequest() then omits
the name field entirely, which the peer rejects as malformed, dropping
the connection. Both an oversized name and an empty one reach it; the
empty case is reachable from the command line as "wolfssh -c ''".

- Return WS_BAD_ARGUMENT for a name at or above WOLFSSH_MAX_CHN_NAMESZ,
  matching how the function already reports a bad type or side.
- Return WS_BAD_ARGUMENT when no name is given and none was stored by
  an earlier call, and when a size arrives with no name behind it.
- Keep returning WS_SUCCESS when an earlier call stored a name, which
  is what the SFTP and SCP retry loops depend on.
- Return before setting connectChannelId so a rejected call leaves
  no state behind, as the server-side exec rejection does.
- Keep the stored name intact when a later call is refused.
- api.c asserts each refusal, and the largest name still admitted.
2026-08-26 12:11:15 -05:00
John Safranek 050dee0fbf Bound channel maxPacketSz below the wire limit
MAX_PACKET_SZ caps the whole SSH binary packet, but the channel
maxPacketSz it was compared against counts only channel payload. A
peer honoring the advertised 35000 overruns the receiver's own check.

- Derive MAX_CHANNEL_PACKET_SZ in internal.h: MAX_PACKET_SZ less the
  transport framing, the CHANNEL_EXTENDED_DATA header, the worst-case
  padding BundlePacket() picks, and MAX_HMAC_SZ. 34899 by default.
- Name that overhead twice, once for the compiler and once as a
  literal for the preprocessor, which reads the wolfCrypt enum
  constants in the first form as zero. The #error guarding
  DEFAULT_MAX_PACKET_SZ uses the second rather than its own copy.
- MAX_CHANNEL_PACKET_SZ is derived rather than a tunable, so it is
  not overridable; an override defeated the bound it enforces.
- wolfSSH_CTX_SetWindowPacketSize() bounds maxPacketSz against that
  instead of MAX_PACKET_SZ; DEFAULT_MAX_PACKET_SZ is unaffected.
- api.c tests the new edge and that MAX_PACKET_SZ is now rejected.

Issue: F-8835
2026-08-26 12:11:15 -05:00
Yosuke Shimizu f60b00cbf8 ossh: check the key-type parse before the ID lookup
- GetOpenSshPublicKey() calls NameToId() and enters the key-type
  switch only when GetStringRef() returns WS_SUCCESS, and returns
  that result otherwise.
- publicKeyType starts NULL and keyId starts ID_UNKNOWN.
- tests/api.c adds test_GetOpenSshPublicKey_type(), gated on
  WOLFSSH_TPM and WOLFSSH_TEST_INTERNAL, covering a truncated type
  string, a truncated length prefix, an empty type, an unsupported
  type and a well-formed ssh-rsa blob.
- Each case asserts idx alongside the return code: UINT32_SZ for a
  truncated type string, 0 for a truncated length prefix, and the
  full blob size for the empty type, the unsupported type and the
  ssh-rsa key.

Issue: F-11650
2026-08-26 08:50:23 -07:00
Yosuke Shimizu faca3bc265 wolfsftp: keep the remote file when resuming a put
- wolfSSH_SFTP_Put() adds WOLFSSH_FXF_TRUNC to the destination open
  only when the write offset is zero.
- STATE_PUT_LOOKUP_OFFSET clears a saved offset when the local file
  is no larger than it.
- A new STATE_PUT_STAT_REMOTE stats the destination when the saved
  offset is nonzero, and clears the offset unless the reported size
  matches it exactly, or the stat returns WS_SFTP_STATUS_NOT_OK or
  WS_PERMISSIONS. Other stat failures re-save the offset and move to
  STATE_PUT_CLEANUP; a want-read or want-write keeps the state.
  WS_SFTP_PUT_STATE carries the attributes both states read.
- The Windows server open maps WOLFSSH_FXF_CREAT to OPEN_ALWAYS and
  reserves CREATE_ALWAYS for an open that also asked for
  WOLFSSH_FXF_TRUNC; the disabled TRUNCATE_EXISTING mapping is
  dropped.
- tests/api.c adds test_wolfSSH_SFTP_PutResume(), five cases over the
  resume paths, built where the hosted file wrappers are available.

Issue: F-11659
2026-08-26 08:49:48 -07:00
John Safranek 3b43e485dd Split SFTP confinement from the start path
- Add wolfSSH_SFTP_SetConfinePath and a separate sftpConfinePath, so
  the start path only says where a session begins, and only an
  explicit confinement root rejects out-of-tree requests.
- Have GetAndCleanPath take the WOLFSSH and enforce the confinement
  root, resolving relative requests against the start path.
- Factor the shared canonicalize-and-store work out of
  wolfSSH_SFTP_SetDefaultPath into CanonicalizePath and StorePath.
- Give the echoserver -D to opt a session into confinement; without
  it the -d home directory only says where SFTP starts.
- Document both settings, and the symlink and TOCTOU caveats, once in
  wolfsftp.h, noting the confinement root itself is trusted.
- Cover the split in api.c, and in regress.c start a session in a
  subdirectory of the confinement root: a sibling of the start
  directory is reachable, anything above the root is not.

Issue: ZD-22308
2026-08-20 09:56:48 -06:00
Yosuke Shimizu 091399cc67 agent: size the signature buffer from the identity
- PostSignRequest() allocates the signature buffer from agent->heap,
  sized from the identity's modulus mpint for RSA and from
  ECDSA_ASN_SIG_SZ for ECDSA, and frees it before returning. A modulus
  longer than RSA_MAX_SIZE, or a key type that sets no size, returns
  WS_BUFFER_E.
- wolfSSH_AGENT_SignRequest() reads the agent's reply into a heap
  buffer of WOLFSSH_AGENT_MAX_RSP_SZ, a new overridable define in
  agent.c, freed after the last use of agent->msg.
- tests/api.c carries a 3072-bit RSA key and a P-521 key as hex string
  components, and build_string() and build_mpint() helpers that write
  the message fields. AgentTestCtx.response sizes from
  AGENT_TEST_BUF_SZ.
- test_wolfSSH_agent_signrequest_rsa_3072() and
  test_wolfSSH_agent_signrequest_ecc_p521() add their identity through
  the agent callbacks and sign with it, then clear the stored private
  exponent or point and sign again.
  test_wolfSSH_agent_signrequest_rsa_too_large() adds an identity whose
  modulus exceeds RSA_MAX_SIZE and expects WS_BUFFER_E.
- The comment on test_wolfSSH_agent_signrequest_oversize_rsa_key()
  describes the identity that test uses.

Issue: F-10541
2026-08-19 21:33:15 -07:00
Yosuke Shimizu f530d32548 Load every PEM block in a root CA buffer
- LoadRootCaPemBuffer() loads every block a PEM CA buffer holds as a
  root CA, skipping the ones that fail. It returns WS_SUCCESS when
  any loaded, WS_PARSE_E when all failed, and WS_BAD_FILE_E when the
  buffer holds no block.
- A block runs header to footer with the next header capping the
  footer search, so wc_PemToDer() gets the block rather than the rest
  of the buffer. A header that nothing closes is skipped and the walk
  resumes at its end; each form's header is re-sought only from
  behind the one just read.
- A block takes the plain or the trusted form, whichever header leads
  picking the type. FindInBuffer() searches a length-delimited
  buffer, so an embedded NUL does not end the search.
- wolfSSH_ProcessBuffer() routes a PEM BUFTYPE_CA there and, like
  DoPemCert(), gives WS_BAD_FILETYPE_E for the trusted form as a
  certificate; SniffCertForm() reads its header as X.509 PEM.
- internal.h defines WOLFSSH_HAVE_TRUSTED_CERT_PEM under
  WOLFSSH_CERTS with wolfSSL 5.8.0 or newer and declares
  IsTrustedCertPem(); ssh.h documents the cert buffer calls.
- tests/api.c adds catBuffers(), makeTrustedPem() and
  assertCaInstalled(), with tests for the bundle, trusted file and
  trusted ReadCert paths.
2026-08-19 14:54:06 -07:00
John Safranek dac55852e3 Use wolfCrypt's Base16_Decode in test.h when it is available
- test.h's static Base16_Decode collides with wolfSSL's public one
    when coding.h lands first, breaking --enable-tpm builds.
  - Include coding.h in test.h, keeping the local copy only when
    WOLFSSL_BASE16 is absent; --enable-wolfssh alone does not set it.
  - api.c includes coding.h too, dropping its hand-declared
    Base64_Encode_NoNl, which would now be a duplicate.
2026-08-16 12:30:15 -07:00
Paul Adelsbach aeb3c4e3d2 Fix build with --disable-server 2026-08-13 14:36:28 -07:00
Yosuke Shimizu e91ff50def Fix a one byte heap overflow in LoadTpmSshKey 2026-08-13 11:59:57 -07:00
Yosuke Shimizu ca6a036edc Replace the matching certificate slot instead of appending a duplicate 2026-08-12 15:43:01 -07:00
John Safranek ddd9c1a761 Guard IdentifyAsn1Key test on TEST_INTERNAL
- IdentifyAsn1Key is WOLFSSH_LOCAL, so the check and its DER blob
  only build when linking the test library.
2026-08-11 11:55:02 -05:00
Yosuke Shimizu 53538e8261 Report a failed PEM decode as WS_PARSE_E, not WS_BAD_FILE_E 2026-08-10 15:11:39 -07:00
John Safranek 28185e4d90 scp: reject NULL message in SetScpErrorMsg
- check message for NULL alongside ssh
- drop the always-true valueSz guard
- cover both NULL arguments in test_wolfSSH_SCP_CB

Issue: F-7510
2026-08-10 10:31:07 -06:00
Yosuke Shimizu c2d169872e Add certificate loading APIs with content-based format detection 2026-08-05 23:02:51 -07:00
John Safranek e4cccaf02b tests: retry stateful SFTP calls in read staging
- add sftp_retry_remove()/sftp_retry_close() so Close and Remove are
  driven to completion instead of abandoned mid-operation
- split the retry predicate: Open and SendWritePacket keep state only on
  WS_WANT_READ/WS_WANT_WRITE, LS adds WS_REKEYING, Close and Remove gate
  on NoticeError()
- assert the staging close; keep the post-read cleanup best effort, as
  the reads are skipped rather than completed on WS_REKEYING
- route the WOLFSSH_TEST_INTERNAL write cleanup through the helpers
- shrink the staging chunk to 512 bytes
2026-08-05 10:54:31 -06:00
John Safranek 36da2e1b5a tests: harden SFTP read target staging
- Clamp the staging write chunk to the bytes remaining and derive the
  try cap from the chunk count, so the loop lands exactly on
  WOLFSSH_MAX_SFTP_RW for any value of the macro.
- Retry the staging Open and the LS on WS_WANT_READ, WS_WANT_WRITE,
  and WS_REKEYING, matching the rekey tolerance used elsewhere in the
  function.
- Assert the staging Open, the LS, and the listing match rather than
  keying the read block on them, so neither a failed create nor a
  connection stuck in WANT/REKEYING until the try cap can drop the
  read coverage without failing the test.
- Remove any stale file before the staging Open, so the reads never
  target a file staged by a prior aborted run.
- Size the read buffer from WOLFSSH_MAX_SFTP_RW, the amount staged and
  the largest amount read, instead of the listed file size, and drop
  the listed-size guard on the 18 byte read.
- Guard the listing name compare against a NULL fName.
- Verify every read returns the staged fill byte, not just a valid
  length; the check is a macro so a failure reports the calling
  read's line.
2026-08-05 10:54:31 -06:00
John Safranek 153c220a11 tests: fix api.test SFTP read target race
- Opening the listing's first entry raced with tests/testsuite.test,
  which creates and removes files in the same directory under
  "make -j check"; the entry could be gone before the open.
- Stage the read target over SFTP, select it from the listing by name,
  and remove it afterward.
- Check the listing with AssertNotNull; a missing entry used to skip
  the whole test body.
2026-08-05 10:54:31 -06:00
John Safranek 719cc31b60 scp: reuse GetScpFileName buffer instead of realloc
- Track the scpFileName allocation size in a new scpFileNameCap field,
  so GetScpFileName() and ScpCheckForRename() reuse the buffer whenever
  the name plus its terminator fits, instead of testing against the
  previous name length
- Pass scpFileNameCap to the send callback, which writes into
  scpFileName and needs the capacity; scpFileNameSz is now the name
  length on every path, including the source path
- Free and reallocate the transfer buffer in ScpSourceInit(), clearing
  the size fields alongside the pointers they describe
- Wrap a long line in ScpProcessEntry()
- Add test_ScpGetFileName covering the reuse-vs-realloc branch, the
  exact-fit boundary, a grow-by-one that catches an off-by-one in the
  reuse condition, and a source-path buffer holding no name yet
- Add test_wolfSSH_SCP_RecursiveTwoFiles, a real "scp -r" transfer of
  two files, covering the scpFileHeaderSent reset on the recursive
  path added in the duplicate-header fix; it clears leftovers from an
  aborted run up front and bounds its recv so a regression fails
  instead of hanging. Not built on Windows, where a recursive transfer
  through the default callbacks does not reproduce the sent file
2026-08-04 12:20:49 -04:00
John Safranek c2da5989ec scp: fix duplicate file header on send
- Gate header on new scpFileHeaderSent flag, not scpFileOffset==0
- A send callback returning 0 bytes first no longer re-sends header
- Skip zero-length SCP_SEND_FILE send to avoid empty CHANNEL_DATA
- Abort on a second consecutive 0-byte send callback return with file
  data outstanding; skipping the send would otherwise spin
  SCP_SEND_FILE -> SCP_TRANSFER with no socket I/O
- Reset offset/bufferedSz/flags in ScpSourceInit for connection reuse
- Document the WS_CallbackScpSend contract, including when a 0 return
  is valid, next to the typedef
- Add test_wolfSSH_SCP_SendZeroFirst regression (func_args scp_send hook)
- Zero func_args in kex.c/testsuite.c so the new scp_send field is not read uninitialized

Issue: ZD-22176
2026-08-04 12:20:49 -04:00
John Safranek 50da369585 DoPemKey: pass isPrivate through to key identification
- IdentifyAsn1Key() was called with a literal 1, so a public PEM decoded
  by wc_PubKeyPemToDer() was run through the private-key decoders and
  never identified.
- Add test_wolfSSH_ReadPublicKey_pem() covering a public RSA PEM read
  through wolfSSH_ReadPublicKey_buffer(). The read itself only compiles
  with WOLFSSH_TPM, so the test also asserts the isPrivate 0 vs 1
  difference in IdentifyAsn1Key() directly, which every build runs.

Issue: F-7209
2026-08-02 22:10:24 -05:00
John Safranek 5133a1ea39 DoSshPubKey: fix off-by-one null terminator
c[inSz-1] = 0 clobbered the last byte of the copied key data instead
of terminating the string after it, truncating public keys by one
character.

Issue: F-7205
2026-08-02 22:10:24 -05:00
Yosuke Shimizu 06b0499c59 fix: keep wc_RsaSSL_Sign()'s result signed in SignHashRsa 2026-07-28 16:17:11 -07:00
Yosuke Shimizu 029d412e1f Add OpenSSH certificate user authentication 2026-07-27 23:30:20 -07:00
John Safranek 9382811225 Add client-side remote port forwarding
- Add wolfSSH_FwdRemoteSetup/Cancel to request tcpip-forward
- Add SendGlobalRequestFwd to frame the request per RFC 4254
- Add portfwd -r reverse mode, driven by the fwd and req-success cbs
- Wait for the peer's reply before -R reports ready, and take the
  bound port from it; with -f 0 that reply is the only source
- Accept -f 0 in reverse mode, where it asks the peer to pick the port
- Cancel names the port the peer bound, not the one requested, so a
  peer keyed on its listener's port can still find it
- Add a req-failure cb so a refused forward is reported, not waited on

Issue: ZD-21867
2026-07-23 15:54:30 -06:00
John Safranek 35ef7b2cd5 userauth: fix auth-cap counting gaps
- Charge abandoned keyboard INFO_REQUEST before method dispatch so a
  peer can't dodge the cap; drop unused "counted" param.
- Exempt the opening "none" probe in DoUserAuthRequestNone() under
  WOLFSSH_ALLOW_USERAUTH_NONE, matching ssh.h contract.
- unit.c: add keyboard restart, setup-rejection, and none-exemption
  MaxAuthAttempts scenarios.
- api.c: assert "none" cipher/MAC accepted under WOLFSSH_ALLOW_NONE_CIPHER.
2026-07-23 14:51:31 -06:00
John Safranek 928bcd1ce2 Validate algo-list setters, reject none MAC/enc
- Setters stored caller string verbatim; "none" cipher/MAC risked cleartext
- Add CheckAlgoList()/NameToIdType() to validate each token by category
- Set* now returns WS_INVALID_ALGO_ID on bad/NULL input, list unchanged
- "none" is rejected in the key list, and allowed for cipher/MAC only under
  WOLFSSH_ALLOW_NONE_CIPHER
- Names unknown to the build are skipped rather than rejected, so a portable
  superset list still works; a known name in the wrong category fails
- Reject empty list elements apart from one trailing comma, which would
  otherwise reach KEXINIT as a zero-length name
- Give ID_NONE a block size so the none cipher no longer divides by zero in
  BundlePacket(), and skip the zero-length key derivations it implies
- Treat an empty peer server-sig-algs as no extension rather than a fatal
  parse error
- The "at least one X" guards checked the implementation macros but not the
  soft-disable macros that gate joining the canned default list, so a build
  whose only survivors are soft-disabled got an empty default list: KEXINIT
  advertised nothing and CheckAlgoList() rejected it. Reachable as
  -DWOLFSSH_NO_HMAC_SHA2_256 -DWOLFSSH_NO_HMAC_SHA2_512 and as
  -DWOLFSSH_NO_AES_GCM -DWOLFSSH_NO_AES_CTR; both now fail at compile time
  naming the soft-disable macro to define
- Zero MAC algorithms is already a hard #error, so assert the queried MAC
  list in test_wolfSSH_SetAlgoList() rather than returning early, which was
  hiding the KEX/KEY/CIPHER coverage behind an unreachable guard

Issue: 2474
2026-07-23 14:09:19 -06:00
John Safranek 212e976c2b Add server-side max failed userauth attempt limit
- accept() userauth loop had no cap on failed attempts
- Add maxAuthAttempts (CTX, default 6) and per-session authFailures
- CountUserAuthFailure() counts only genuine pw/pubkey/kbd-int failures
- On limit: send SSH_MSG_DISCONNECT, fail accept; SetMaxAuthAttempts(<=0)=default

Issue: 2473
2026-07-23 14:09:19 -06:00
Yosuke Shimizu 5acbeaeaee Run threaded api-test SFTP/SCP tests on Windows 2026-07-22 17:29:27 -05:00
Yosuke Shimizu 0b819b0b48 wolfterm: bound escBuf saves and clear esc state on OSC resume
Harden the partial-sequence reassembly in wolfSSH_DoControlSeq and fix an
escape-state leak in the wolfSSH_ConvertConsole OSC resume path
(USE_WINDOWS_API console handling).

- Tighten both escBuf partial-save branches to reject bufSz - *idx >=
  WOLFSSL_MAX_ESCBUF before the WMEMCPY and log via WLOG. escBuf is
  WOLFSSL_MAX_ESCBUF bytes, so a save of exactly that length fills it with
  no room for later state; rejecting the boundary keeps the two branches
  consistent and matches the resume check.

- Add the capacity guard to the initial-parse CSI save branch, which
  previously copied into escBuf with no bound. In the default build getArgs
  caps the advance below WOLFSSL_MAX_ESCBUF so the copy was already safe;
  the guard prevents a custom WOLFSSH_MAX_CONSOLE_ARGS > WOLFSSL_MAX_ESCBUF
  build from overflowing escBuf.

- Clear escState to WC_ESC_NONE when a resumed OSC sequence completes, not
  just escBufSz. Without this escState stayed WS_ESC_OSC and the next call
  re-entered OSC parsing on plain bytes instead of printing them.

Extend test_wolfSSH_ConvertConsole with a CSI sequence split across three
calls (ESC[ | args | command char), a trailing plain byte that must be
printed once state is cleared, and a single buffer whose CSI args run to
the end and complete on the next byte.
2026-07-21 12:57:31 -05:00
Yosuke Shimizu 1f70747236 certman: enforce leaf is end-entity in VerifyCerts_buffer 2026-07-20 19:50:11 -05:00
aidan garske 1b276273e9 Allocate channelName dynamically to shrink WOLFSSH struct by ~4KB 2026-07-20 17:43:42 -05:00
aidan garske 091310a1eb F-3881 - Reserve separator and NUL in RealPath segment bound check 2026-07-20 17:24:02 -05:00
Yosuke Shimizu 6dd249beb5 Reject SFTP resume names that fill the whole name field 2026-07-17 14:06:19 -06:00
Eric Blankenhorn 28701904c1 Harden DoOpenSshKey parsing 2026-07-06 16:55:54 -05:00
Yosuke Shimizu c8a347de85 Reject symlinks in default SCP send callback 2026-06-23 14:37:57 -07:00
Yosuke Shimizu f2b4f43c20 Sanitize control bytes in formatted log messages 2026-06-18 13:52:00 -07:00
Yosuke Shimizu 2425f7c594 Bound OSC index before reads in wolfSSH_DoOSC 2026-06-18 11:00:42 -07:00
Yosuke Shimizu 1efd64743c wolfssh/scp: complete rekey that starts mid-transfer 2026-06-16 15:56:50 -07:00
Yosuke Shimizu cb9bb7a2a7 Fix SFTP client states dropping unsent bytes after partial channel send 2026-06-16 11:21:19 -07:00