Commit Graph

316 Commits (8f5d31a2c6952a34ca17df1885eb299136bfeb80)

Author SHA1 Message Date
Paul Adelsbach b92e57ab4c PR feedback 2026-08-19 16:53:24 -07:00
Paul Adelsbach b1cd689ecc F-8824: enable config files without trailing newline 2026-08-19 16:53:24 -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
Yosuke Shimizu 5ae0cc4c93 port: resolve the full SFTP offset in the Harmony and Zephyr ports
- Assemble the split offset with wResolveOffset() in the Harmony wPread
  and wPwrite, and seek with the resolved value.
- Assemble the split offset with wResolveOffset() in the Zephyr wPread
  and wPwrite, and seek with the resolved value.
- Define WOLFSSH_MAX_FILE_OFFSET as 0x7FFFFFFF in the Harmony block, so
  the ceiling comes from SYS_FS_FileSeek's int32_t offset rather than
  from off_t.
- Add test_PreadPwriteOffsetCeiling() covering ports whose seek type
  cannot reach 4 GiB, including a read back at an in-range offset.
- Add test_ResolveOffset() covering offset assembly, both sides of the
  ceiling, and the NULL guards.

Issue: F-8823
2026-08-18 15:01:39 -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
Yosuke Shimizu 1644bb773d Accept exact-fit ScpBuffer in no-filesystem SCP send callback 2026-08-14 14:34:16 -07:00
Yosuke Shimizu 2142821bc6 Stop wPread and wPwrite dropping the high offset word 2026-08-14 14:25:55 -07:00
Paul Adelsbach 326d181389 PR feedback: fix test issue with --disable-server --enable-sshclient 2026-08-13 14:36:28 -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
Emma Stensland 244c82c86d Add remaining ML-DSA composite signature algorithms 2026-08-10 14:51:37 -07:00
Emma Stensland 866f7392cd Add ML-DSA44-Ed25519 composite signature support 2026-08-10 14:51:37 -07:00
John Safranek b2b32cca51 tests: add test_ByteHighwater
- The byte-count branch of HighwaterCheck() had no coverage. Exercise
  the boundary at the mark, the once-per-epoch flag that keeps the
  callback from firing a second time, the receive side, and a mark of
  0 disabling the check.

Issue: F-6978
2026-08-10 13:53:08 -06:00
John Safranek 6793c3e273 internal: validate the ECC curve name in user auth
- DoUserAuthRequestEcc() skipped the curve name in the public key blob
  and imported the point with wc_ecc_import_x963(), which picks the
  curve from the point length, so the key did not have to be on the
  curve the declared algorithm names.
- Derive the curve from pk->publicKeyType with NameToId() and
  wcPrimeForId(), require the blob's curve name to equal
  PrimeNameForId() for that id, and import with
  wc_ecc_import_x963_ex() pinned to that curve.
- The import's error check sat outside the success guard, so it
  rewrote any earlier error as WS_CRYPTO_FAILED. Scope it to the
  import itself so the parse and algorithm-match errors keep their
  own codes.
- Add test_EccUserAuthCurveMismatch, which offers a blob naming one
  curve under another algorithm and expects the request to fail.

Issue: F-6979
2026-08-10 13:53:08 -06:00
John Safranek 4f827ca4a9 Reject client conn messages before key exchange
- IsMessageAllowedClient() gated the post-userauth rejection on
  connectState >= CONNECT_KEYED, so every state below it allowed
  msgid 80 and above.
- A client in the pre-key-exchange DoReceive() loops accepted an
  unencrypted CHANNEL_OPEN, created the channel and replied with
  CHANNEL_OPEN_CONF.
- Add a state sweep over the connection-protocol range and a full
  DoReceive() of a plaintext CHANNEL_OPEN from a pre-auth state.

Issue: F-7628
2026-08-10 12:49:49 -07:00
Ruby Martin ab8058d7cd check return value of WFTELL() 2026-08-10 10:19:45 -07:00
Ruby Martin 2a30f48772 Update WFSEEK call sites that may misinterpret return value in Harmony/Nucleus builds 2026-08-10 10:19:45 -07:00
John Safranek 7e56f10b81 auth: free OpenSSH-certificate user keys
- cover the ID_OSSH_CERT_* key IDs in wolfSSH_KEY_clean
- zeroize the key signature struct before freeing it
- add a unit test for the RSA OpenSSH-certificate ID

Issue: F-7519
2026-08-10 10:31:07 -06:00
John Safranek c61c7a04dd client: fix keyboard-interactive responses
- zero the response arrays and stop filling after the first
  failed read, tracking only the slots populated
- release the previous round's responses on each INFO_REQUEST
- add auth.test coverage for EOF stdin and a second round

Issue: F-7515, F-7516
2026-08-10 10:31:07 -06:00
John Safranek 457ac40255 certs: reject an empty RFC 6187 cert chain
- fail ParseCertChain when the peer sends a zero cert count
- only set isCert when a leaf was actually parsed
- add a ParseLeafCert test hook and zero-count unit test

Issue: F-7517
2026-08-10 10:31:07 -06:00
John Safranek 319180beff ssh: match channel request types exactly
- add ChannelRequestIs() comparing the type length as well
- use it for all nine handled request types, so truncated,
  empty and NUL-padded types are rejected
- cover "sh", "sub", empty and "shell\0AAAA" in unit tests

Issue: F-7217
2026-08-10 10:31:07 -06:00
John Safranek 7c4b1d3549 kex: keep GenerateKey hash error in loop
- gate the multi-block loop on ret so the first wc_HashFinal
  result is not discarded
- add a SHA-256 two-block key expansion known-answer test

Issue: F-7507
2026-08-10 10:31:07 -06: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
Yosuke Shimizu 8d1d67a7db Dispatch DoAsn1Key public keys on key type 2026-08-05 10:03:00 -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
Mark Atwood 82dd5814f8 fix: raise wolfSSH RSA user-authentication key minimum
Raise a cryptographic minimum to current guidance. It rejects peers below
the new floor by design.

- Reject RSA user-authentication keys smaller than 2048 bits with
  WS_CERT_KEY_SIZE_E, per NIST SP 800-131A. The floor is configurable via
  a new WOLFSSH_RSA_MIN_KEY_BITS define (default 2048).

Issue: F-6517
2026-08-04 11:34:17 -04:00
Yosuke Shimizu 6f0cbe3f13 Buffer client SFTP VERSION header across partial reads 2026-08-03 12:07:45 -07: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
Emma Stensland 47ddc70b40 added check for private and public keys decoded 2026-07-31 16:14:52 -07:00
John Safranek 70eef07db1 Cap open SFTP directory handles per session
Directory handles are tracked on ssh->dirList, a separate list from the
file handles, so the SFTP_AddFileHandle cap does not reach them. A peer
could loop on OPENDIR and grow that list until allocation failed.

- Add SFTP_DirHandleCapped() and check it in both RecvOpenDir branches
  before the directory is opened or the name buffer allocated, so the
  rejection has nothing to unwind. Refusal sends an SFTP status like the
  existing permission-denied path rather than dropping the request.
- Reuse WOLFSSH_MAX_SFTP_HANDLES, so each list is bounded separately.
- Add wolfSSH_SFTP_TestDirHandleCount() and a regress test.
2026-07-31 15:20:04 -05:00
John Safranek 00fd3bac0e Cap open SFTP file handles per session
An authenticated peer could open SFTP file handles without limit,
growing ssh->fileList until allocation failed and making the linear
handle lookup progressively more expensive.

- Limit tracked file handles per session to WOLFSSH_MAX_SFTP_HANDLES
  (default 64), bounding memory and the linear-lookup DoS surface.
- Check the cap in SFTP_FileHandleCapped() from both RecvOpen branches
  after the path is resolved and before the file is opened. The open
  flags derived from the request may carry O_CREAT and O_TRUNC, so
  refusing after the open would create or truncate the target on a
  request the peer is told failed. SFTP_AddFileHandle() keeps the check
  as a backstop.
- Refuse with FTP_FAILURE "Too Many Open File Handles" so the peer can
  tell a resource limit from a server malfunction.
- Add a regress test via wolfSSH_SFTP_TestFileHandleCount() covering the
  cap, the refusal status reply, and that a refused open leaves the
  target file untouched.
2026-07-31 15:20:04 -05:00
Yosuke Shimizu be5331aa78 Fix pty-req terminal mode size handling when stdin is not a tty 2026-07-31 09:11:30 -07:00
Emma Stensland 024a9a21a0 Fix memory-safety and error-handling edge cases 2026-07-29 14:35:14 -07:00
John Safranek daa3d88c3c Match OpenSSH on empty name-list elements
- GetNameListRaw stops at the first zero-length name and drops the
  rest, matching OpenSSH's match_list() on a peer proposal.
- Its comma test lagged the index by one, folding a doubled comma into
  the preceding name.
- An empty name-list is a list of zero names, not WS_BAD_ARGUMENT.
- Drop CountNameList. DoExtInfoServerSigAlgs parses into an array
  capped at WOLFSSH_MAX_NAMELIST_CNT and allocates only on a match. A
  list naming nothing it can sign with is advisory, RFC 8308 section
  3.1, so it clears any recorded list instead of failing the
  connection. The latest server-sig-algs wins, as in OpenSSH.
- Guard the list[0] reads in DoKexInit on a non-empty list. No change
  today, an empty list fails to match first.
- Add wolfSSH_TestDoExtInfo and tests for both name-list paths, the
  caps, lists of more than one algorithm, and a second EXT_INFO
  superseding the recorded list, empty and unusable values included.

Issue: F-6817
2026-07-29 09:52:35 -06:00
Yosuke Shimizu 06b0499c59 fix: keep wc_RsaSSL_Sign()'s result signed in SignHashRsa 2026-07-28 16:17:11 -07:00
John Safranek 51a7eca3af Clean up two source comments
- src/internal.c DoKexDhReply: drop the stale fuzz note on the signature
  size. The size checks it pointed at stay.
- tests/unit.c: replace an em-dash with a comma, for ASCII-only sources.
2026-07-28 16:49:59 -06:00
John Safranek dd7b6066f4 tests: cover the zero bound, empty name list, and short sig blob
None of the three paths this branch changed had coverage, which is why
make check passed both before and after the SendChannelData regression.

- unit: SendChannelData and SendChannelExtendedData with a peer maximum
  packet size of 0 report WS_WINDOW_FULL, queue nothing, and leave
  peerWindowSz alone. A zero-length send still succeeds.
- unit: BuildNameList terminates buf for an empty id list. The buffer is
  poisoned first, so a missing terminator shows up as a wrong length
  instead of depending on what the allocator handed back. Reaches the
  static function through a new wolfSSH_TestBuildNameList hook.
- regress: a KEXDH_REPLY whose signature blob holds nothing but a name
  length prefix is rejected with WS_BUFFER_E, pinning the bounded read
  that replaced the hand-rolled name parse.

Each test was confirmed to fail with its fix reverted.

Issue: F-4586, F-4587, F-6525
2026-07-28 16:49:59 -06:00
Yosuke Shimizu c5d9195b63 keyboard-interactive: send USERAUTH_FAILURE on response-count mismatch 2026-07-27 23:42:15 -07:00
Yosuke Shimizu 029d412e1f Add OpenSSH certificate user authentication 2026-07-27 23:30:20 -07:00
Mark Atwood e393bcb5d7 fix: harden peer-supplied numeric field parsing
Replace atoi() and strtoull() on peer-controlled numeric fields with
bounded, error-reporting parsers. atoi() has undefined behavior on
overflow and cannot signal malformed input. strtoull() is C99, so it is
unavailable on some ports, and it accepts a leading sign and leading
whitespace that the SCP header does not permit.

- #2881 src/wolfscp.c: add ScpParseUInt64 helper; parse SCP header
  file size / mtime / atime with digit validation and overflow
  rejection against a caller-supplied max, replacing the 3 strtoull
  call sites from de23a694. Also rejects "+1", " 1" and "\t1" on all
  three fields, and "-1" on the unbounded scpMTime and scpATime, which
  had taken the wrapped 0xFFFFFFFFFFFFFFFF. The helper takes the field
  length, dropping the temporary '\n' written over the separating space
  at each call site, and with it the <stdint.h> include de23a694 had
  added for UINT32_MAX.
- #2882 src/wolfterm.c: add parseArg helper using strtol; clamp
  invalid/out-of-range VT100 params to 0, replacing 2 atoi call
  sites (blocks peer "-1" wrapping to 0xFFFFFFFF). A param with bytes
  left over after its digits, such as "12x", clamps to 0 rather than
  parsing as a short value. strtol is C89 and needs no replacement.
  Inside USE_WINDOWS_API, so not built on Linux or macOS. Also drop
  the unused maxIdx from getArgs().

Extend test_ScpGetFileSize and test_ScpGetTimestamp in tests/unit.c for
the fields the new parser rejects but strtoull() accepted: a signed or
whitespace-padded field, an empty field, a field holding a NUL, and the
64-bit maximum and one past it on the timestamps. Drop the strtoull()
and ERANGE references from those test comments.

Issue: F-2881, F-2882
2026-07-27 10:29:05 -06:00
John Safranek 50ee1b61e8 Per-channel stderr buffering and flow control
Move extDataBuffer from WOLFSSH to WOLFSSH_CHANNEL and give stderr real
back-pressure: charge the window on receipt, credit on read, never lose
the credit.

- Buffer stderr per channel; accumulate across packets instead of
  overwriting unread data.
- Charge the window on receipt, replenish on read; reject data larger
  than the advertised window.
- Route all credit through ChannelCreditWindow(): parks credit that
  cannot go out (mid-rekey, failed send), suppresses zero-byte adjusts.
- Propagate hard credit-send failures on unknown extended-data types;
  WS_WANT_WRITE and WS_OVERFLOW_E stay success.
- Add wolfSSH_Channel{Id,}ReadExt / SendExt; the stream_/extended_data_
  pair stays first-channel-only.
- Drain SCP stderr in a loop; document the mandatory-drain contract.

Issue: F-864
2026-07-23 16:58:04 -06:00