Commit Graph

341 Commits (c71202ffdb92202cc53bd520ee45f438d2edded4)

Author SHA1 Message Date
John Safranek 7b17f65b67 Tighten the stdin-stall bound
A correct loop measures zero and a spinning one saturates a core, so half a
core left room for a partial spin to pass. The shorter window costs the
suite nothing, since the child's sleep set the runtime.

- Bound the reading at a tenth of a core over three seconds
- Name the settle, window, sleep and limit rather than spelling each out
2026-08-31 11:53:29 -05:00
John Safranek 7c52cbe86f Wait on a child that is not taking its stdin
Stdin is non-blocking now, so a full pipe leaves an unwritten tail for the
next pass. Nothing can come off the channel until it drains, so the channel
data that is left unread kept pending set, and pending forced a zero timeout
on select(). The loop then polled instead of waiting on the child's stdin,
which is already in the write set, and burned a core until the child read.

- Take the zero timeout only when the child has no tail owed to it
- Add sshd_stdin_stall_test.sh, which fails without this
2026-08-31 11:53:29 -05:00
John Safranek 93f390092b Never wait on the child in the shell loop
SHELL_Subsystem() is the only reader of the child's output, so it must never be
the thing the child is waiting for. It was: the pass that writes the peer's
input to the child's stdin ran ahead of the pass that reads its stdout, and on
a pass with buffered channel data the output descriptors were left out of the
select() altogether. A child that fills its stdout pipe stops reading stdin,
the write blocks, and nothing is left to empty the pipe that would release it.
sshd_stdin_eof_test.sh case 2 is the shape that reaches it: a half-close with
the send window full leaves the whole window buffered, and the burst that
follows is up to four 32K writes with no read in between.

- The child's output is watched on every pass. A pass with work already in
  hand polls with a zero timeout instead of skipping select(), so it still
  sees the child's output.
- The descriptor written to is non-blocking, and what a short write leaves is
  carried in channelBuffer to the next pass, which waits for the child in
  select() rather than inside write(). Only EAGAIN keeps the remainder; any
  other short write still ends the session.
- The child's stdin closes on the peer's EOF once that remainder is gone too,
  not just once the channel is drained.
- A channel retired under us drops the remainder with the descriptor.
2026-08-31 11:53:29 -05:00
John Safranek ff59c723ec Handle the EOF status in apps and examples
Every in-tree caller of wolfSSH_worker() now recognises a peer half-close.
wolfsshd's shell loop and both echoservers need it: all three ladders end in
"else if (rc != WS_WANT_READ) break", and wolfsshd's reaches
kill(childPid, SIGKILL), so without it a client half-close kills the command
it just finished feeding.

- wolfsshd closes the child's stdin off the channel's own EOF state instead of
  off a worker return of zero, which no longer happens on a half-close.
- The echoservers answer the half-close off wolfSSH_ChannelGetEof() rather
  than the WS_EOF status: the flush inside wolfSSH_worker() can supersede that
  status, and it is raised once. They hand back the backlog first, finish a
  short send, and only send the EOF once the channel is empty. Answering is
  not conditional on the shell build, where an echo session is the default.
- The SFTP loops peek before leaving, so a half-close with requests still
  buffered is served rather than dropped, and they report an ordinary session
  end as success.
- The clients -- examples/client, scpclient, sftpclient, apps/wolfssh -- treat
  it as the graceful case instead of an error. apps/wolfssh counts it as a
  finished flush as well, since one worker pass can drain the queue and
  consume the peer's EOF together.
- portfwd relays it to the local socket with shutdown(SHUT_WR) so a local
  reader waiting on end-of-input returns, once the backlog has genuinely been
  handed over: a read cut short by a rekey leaves the half-close for a later
  pass.
- The Windows half of wolfsshd does not answer with an EOF of its own. That
  latches eofTxd and the child's remaining output would be refused, which is
  the defect this series removes from the library.
- The mplabx port drains before tearing down, the way its SFTP read path
  already did; its worker arm was unreachable for a half-close until now.
2026-08-31 11:53:29 -05:00
John Safranek 231772ceaa wolfsshd: stop polling when the SFTP channel has nothing buffered
wolfSSH_stream_peek() returns 0 for a live channel with an empty buffer,
which is the ordinary idle case. None of the arms after the peek match
that, so the loop falls through with the timeout still at
TEST_SFTP_TIMEOUT_NONE and tcp_select() returns on its 100 us floor. An
idle SFTP session keeps a core busy for as long as it stays connected.

- take the peek's zero return as "nothing to do" and let the next select
  wait a second, the same value the want-read paths already use
- sshd_sftp_idle_cpu_test.sh parks an idle SFTP session on the daemon and
  reads the connection process's CPU time out of /proc, failing if it
  spends 5 ticks or more over ten seconds

The measurement the test automates: 21 ticks per 10 seconds before, 0
after. It skips where there is no /proc or no local daemon to measure.
2026-08-28 21:36:07 -05:00
John Safranek d3d3e7ec31 Fix the cppcheck findings
Four sites re-tested an unchanged ret after a WOLFSSH_SMALL_STACK
allocation, which cppcheck reports as identicalInnerCondition because the
allocation that can change ret is compiled out in the default build. Test
the allocated pointer instead; that is what the check is guarding.

- certman.c, keygen.c and wolfsshd/auth.c: check the DecodedCert, MlDsaKey
  and DecodedCert pointers.
- internal.c CompositeEccSign: give the fixed-buffer build pointer aliases
  so both builds have the same shape, then check the r/s pointers.
- port.c: initialize fileHandle, which is only assigned when mbstowcs_s
  succeeds.
- wolfsftp.c: zero localTime before WLOCALTIME, which is a per-port macro.
2026-08-28 18:12:59 -05:00
John Safranek 689fec4b01 Drain the shell channel before closing stdin
SHELL_Subsystem() hands the child whatever the peer sent, whichever pass it
arrived on, and closes the write end of its stdin only once that buffer is
dry. It works off the shell channel's own inputBuffer, so data held back while
the window was full is still handed over; the old read ran only on the
worker's WS_CHAN_RXD and was skipped outright while windowFull.

- The channel id comes from the head of the channel list at entry, the only
  channel open there, rather than from DEFAULT_NEXT_CHANNEL, which a build
  can override.
- A lookup that finds nothing is not an EOF: only a channel that is present
  and drained closes the pipe.
- Data arriving behind the peer's EOF, which RFC 4254 section 5.3 forbids, is
  dropped rather than written to a stdin that is already closed. The write
  would fail with EBADF and end the session mid-stream.
- The short-write retry tests for a -1 return before reading errno, which
  nothing else sets.
2026-08-28 13:30:12 -05:00
John Safranek f494688e3a wolfsshd: let QNX own the host key's permissions
QNX system images fix the host key's owner and modes, and the daemon cannot
change either, so the secure gate refuses to load a key the integrator has no
way to correct. Add a hand-defined WOLFSSH_NO_HOSTKEY_PERMS, further
conditional on QNX, that hands only that policy to the platform.

- Fold the macro and the QNX test into the internal
  WOLFSSHD_HOSTKEY_RELAX_PERMS in wolfsshd.c.
- Add a relaxPerms argument to wolfSSHD_OpenSecureFile() that skips the owner,
  mode and ancestor-directory checks.
- Keep the structural checks: lstat, O_NOFOLLOW, S_ISREG and the dev/ino
  recheck, so a symlink, a non-regular file or a swap during the open is still
  refused.
- Set it only on the host key load, leaving the host cert, UserCAKeysFile,
  authorized_keys and shadow gates unchanged.
- Log at startup when the guard is built in.
- Add six test_OpenSecureFile scenarios for the relaxed path, unreachable at
  runtime off QNX and so otherwise uncovered.

Issue: ZD-22308
2026-08-25 14:33:52 -06:00
John Safranek 44bd4a06f4 Compose sshd_config Match blocks per keyword
wolfSSHD_GetUserConf returned the first matching Match block whole, so a
keyword named only in a later matching block was dropped and the outcome
depended on the order the blocks were written.

- track in a new setMask which keywords a node set itself, so a value
  inherited from the globals can be told from one the block named, with a
  compile time check that no option tag shifts out of the mask
- resolve into a fresh config seeded from the globals, letting every
  matching block contribute the keywords no earlier block claimed
- the resolved config now belongs to the caller, so wolfsshd and the auth
  paths free it and the tests compare values rather than node identity
- put sshd_match_overlap_test.sh back in the suite

Issue: ZD-22324
2026-08-25 14:30:39 -06:00
John Safranek d9f596b8c5 Test overlapping sshd_config Match blocks
OpenSSH resolves sshd_config one keyword at a time, scanning every Match
block that applies. wolfSSHD_GetUserConf returns the first matching block
whole, so a setting made only in a later matching block is dropped.

- add test_GetUserConfMatchOverlapCompose, covering a user matched by both
  a Match User and a Match Group block, in either order
- add sshd_match_overlap_test.sh, the same case against a live daemon,
  where the group block's ForceCommand is the setting that goes missing
- both fail until per keyword composition lands, so the unit test runs
  last and the script stays commented out of run_all_sshd_tests.sh
2026-08-25 14:30:39 -06:00
Yosuke Shimizu ad28e21221 wolfsshd: enforce shadow password and account aging
- IsShadowExpired() in auth.c returns 1 when a shadow entry's
  sp_expire date has arrived, its sp_lstchg is 0, or the day is at or
  past sp_lstchg + sp_max. Negative fields leave the matching check
  off; a negative day count, standing for an unavailable clock,
  denies the entries that carry aging. WSSHD_SECS_PER_DAY converts
  WTIME() into the unit those fields use. The helper is compiled
  under HAVE_SHADOW and !WOLFSSH_USE_PAM, as its caller is.
- CheckPasswordUnix() runs the shadow entry it looked up through the
  helper and, after an otherwise successful hash compare, logs the
  denial and returns WSSHD_AUTH_FAILURE.
- auth.h declares IsShadowExpired() for the unit test build.
- test_configuration.c adds test_IsShadowExpired() over a table of
  aging fields and day counts, and test_CheckPasswordUnix_expired()
  for the denial of a correct password.
- The CheckPasswordUnix() tests share one driver,
  wsshd_test_CheckPasswordUnixCase(), with the crypt() setup in
  wsshd_test_LoadShadowHash() and the three fail-closed shadow
  lookups gathered into test_CheckPasswordUnix_failClosed().

Issue: F-10577
2026-08-25 10:42:15 -07:00
JacobBarthelmeh 9777bc5ce8 add Windows sanity close of token before acquiring a new one 2026-08-24 14:49:09 -07:00
JacobBarthelmeh eb3fd6bcf0 add clean up in failure case and use WFREE instead of XFREE 2026-08-24 14:49:09 -07:00
JacobBarthelmeh b6bd975ccf Fixes for Windows wolfSSHd, f-8853 and f-8819 2026-08-24 14:49:09 -07:00
Paul Adelsbach 581053bcf6 CI: add code coverage workflow, misc script updates 2026-08-21 11:30:40 -07:00
John Safranek e7c8dc2c2c tests: guard stop_wolfsshd so it cannot fail its caller
stop_wolfsshd killed $PID unconditionally. With the daemon already gone the
kill failed, and under "set -e" that aborted the caller -- in
sshd_forcedcmd_test.sh before PID was cleared, so the ForceCommand-SFTP
scenario was silently skipped, the EXIT trap killed the dead pid a second
time, and the script exited 1.

- Guard on a non-empty PID, ignore a failed kill and return 0, so the
  function is safe to call from an EXIT trap.
- Clear PID after stopping, so a second call cannot kill a recycled pid.
- Remove the temp key dir even when no daemon was recorded, so a daemon that
  failed to start does not leak it.
- Collapse sshd_forcedcmd_test.sh's cleanup() wrapper to a bare
  trap stop_wolfsshd EXIT now that the function guards itself.
- Check the cd back to the test directory in sshd_x509_upn_fail.sh; the log
  it counts after the client run is the one there.
2026-08-20 10:28:20 -07:00
John Safranek 8496451357 tests: fix privdrop test's client key paths
sshd_privdrop_fail_test.sh runs from apps/wolfsshd/test and handed the
example clients relative key paths, but the clients call
ChangeToWolfSshRoot() before parsing arguments. Every client died at
"Error setting private key" and the test blamed the privilege drop.

- Anchor the key, payload and client paths at the script's own directory.
- Rename the saved directory to TESTDIR so a later cd cannot clobber it.
- Report "no fork at all" separately in the timeout diagnostic.
- Print the client's own output at every failure exit, so a client that
  never connects cannot be read as a daemon fault.
- Keep the client logs like log.txt, gitignored and removed on success.
2026-08-20 10:28:20 -07:00
John Safranek f52c3f7e22 tests: save sshd test scripts' dir in TESTDIR
Eight scripts saved their starting directory in PWD, which the shell
rewrites on every cd, so the "cd $PWD" restore landed in the repository
root. sshd_forcedcmd_test.sh's second scenario and the log count in
sshd_x509_upn_fail.sh silently never did what they cover.

- Save the starting directory in TESTDIR, as sshd_pubkey_reject_test.sh does.
- Quote "$TESTDIR" at every cd, now that the saved value is really used.
- Stop the daemon from a trap in sshd_forcedcmd_test.sh, so its now reachable
  second scenario cannot leave one on the shared port when set -e aborts.
- Take start_wolfsshd's before and after daemon PID snapshots from pgrep -x
  instead of scraping every digit run out of "ps -e", which mixed the TIME
  field's clock digits in with the PID and, with a leftover daemon running,
  stopped the wrong process.
- Let both snapshot pipelines fail, so a set -e caller survives no daemon
  being up and a daemon that dies after sudo returns is reported by the
  caller's own empty-PID check.
2026-08-20 10:28:20 -07:00
John Safranek 1d5199bb94 tests: fix StrictModes count aborting sshd suite
grep -c prints 0 and exits 1 when nothing matches, so the "|| echo 0"
fallback fired too and the count became "0\n0". The arithmetic error
unwound bash out of the test block, skipping the last eleven tests while
the summary still printed a pass and exited 0.

- Use the bare grep -c result and default only the empty case.
- Add a RUN_COMPLETE sentinel at the end of each branch that runs tests.
- Check the sentinel before the summary so an abort exits non-zero.
- Kill lingering daemons by process name, so the teardown does not kill
  the run itself before that check when invoked by a path holding
  "wolfsshd".
2026-08-20 10:28:20 -07:00
John Safranek 754317b6bf Use wolfssh-options in the test scripts
The scripts now read the build options from the probe instead of grepping
usage text, config.log and daemon logs. Drops the usage lines only tests read.
2026-08-19 16:55:00 -07:00
Paul Adelsbach 2b2e86fb4f Move path to end of log line to avoid early truncation of the log 2026-08-18 14:28:40 -07:00
Yosuke Shimizu 255dd926d9 Adopt the certificate loading APIs in the examples and apps 2026-08-12 16:06:39 -07:00
Yosuke Shimizu 6579f59236 wolfsshd: base Match blocks on the global config and keep included ones 2026-08-11 18:13:34 -07:00
Paul Adelsbach 0c355c31ac Enforce null termination in sshd unit tests 2026-08-10 15:07:59 -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
Ruby Martin 97bbe42263 Add WFSEEK return value checks where previously discarded 2026-08-10 10:19:45 -07:00
Emma Stensland 492e8cd65d Mitigate user enumeration timing oracle using dummy hash cache 2026-08-05 12:26:46 -07:00
John Safranek 109e787759 wolfsshd: drop dead dCert NULL check
Without WOLFSSH_SMALL_STACK dCert is the address of a stack variable, so
the NULL check could never fire. Keep the check under the small stack
build where the WMALLOC can actually fail.

Issue: CID-573006
2026-08-05 13:57:57 -05:00
John Safranek 960282ae47 wolfsshd: retry the final shell output flush
The drain after waitpid ignored the send return, so on a non-blocking
socket the tail of a command's output was dropped on a full window, a
rekey or a would block. Retry a bounded number of times and log when
the data still can not be sent.

Issue: CID-572907
2026-08-05 13:57:57 -05:00
John Safranek f4659eac86 wolfsshd: check fcntl results in the pipe drain
The leftover-data drain after waitpid ignored both fcntl calls. Check
the get and the set, and skip the drain read for a pipe that could not
be made non-blocking so the read cannot hang the connection process.

Issue: CID-572931
2026-08-05 13:57:57 -05:00
Mark Atwood 8e71812cf7 fix: restore privileges on SHELL_Subsystem error paths
SHELL_Subsystem() raises privileges to look up user information, but the
three pipe() failures and the forkpty() failure return WS_FATAL_ERROR
without dropping them again. HandleConnection() then runs its teardown --
wolfSSH_shutdown(), up to ten wolfSSH_worker() iterations, and the socket
drain -- still elevated when UsePrivilegeSeparation is yes or sandbox.
Drop permissions before each of the four returns, reusing the wording the
daemon already logs for a failed drop.

The forkpty() path also returned with all six pipe descriptors still
open. Close them there, as the pipe() failure paths already do, and reset
the slots to -1 to keep the child-branch invariant that an entry is
either a live descriptor or -1.

Issue: F-6980
2026-08-04 12:06:42 -04:00
John Safranek 773febc60c wolfsshd: compare the terminated copy in GetConfigInt
- The zero check ran WSTRCMP() on the caller's buffer, which is a
  length-bounded slice of the config line and not NUL terminated, so it
  read past inSz and rejected valid "0" values whose slice had trailing
  text.
- Compare num, the NUL-terminated copy that atol() was given.

Issue: F-7213
2026-08-02 22:10:24 -05:00
John Safranek bdc61a3200 wolfsshd: zero the crypt() hash after comparing
- crypt() returns a pointer into a static buffer that keeps the hashed
  password after CheckPasswordHashUnix() returns; wipe it once the
  comparison is done.

Issue: F-7220
2026-08-02 22:10:24 -05:00
Emma Stensland 22063423cd F-6700: Add PermitRootLogin prohibit-password and forced-commands-only modes 2026-07-31 15:44:47 -07:00
John Safranek c39d555d09 Fix TOCTOU defect in test_ConfigSavePID()
- Scenarios 1 and 5 fopen() once and fstat() that handle instead of
  stat()ing the path a second time.
- A failed open is now a logged error rather than an indirect rd == 0.
- The FIFO lstat() and failPath existence test stay as they are: neither
  has a descriptor to stat, and mkdtemp()'s 0700 directory makes them
  unraceable.

Issues: CID-651701
2026-07-31 11:10:57 -05:00
Emma Stensland 024a9a21a0 Fix memory-safety and error-handling edge cases 2026-07-29 14:35:14 -07:00
Emma Stensland ee1609da36 wolfsshd tests: mock getpwnam for test_AuthSetGroups_* to avoid depending on a real 'sshd' user 2026-07-29 11:56:01 -07:00
JacobBarthelmeh 419c7f7d16 force zero on password buffer after use 2026-07-29 09:46:06 -07:00
Mark Atwood 345be4c8eb test: close wolfSSH test_gap coverage hole
Add mutation-killing unit coverage flagged by Fenrir static analysis.
No production logic changes.

* F-6702 test_configuration.c: extend test_CheckPasswordHashUnix with
  empty/locked-password cases pinning the empty-branch guard.

Co-authored-by: John Safranek <john@wolfssl.com>
2026-07-28 16:51:10 -06:00
Yosuke Shimizu 029d412e1f Add OpenSSH certificate user authentication 2026-07-27 23:30:20 -07:00
shaunchokshi 65fdad2ffb wolfsshd: load PKCS#8 PEM host keys (e.g. ML-DSA)
wolfsshd could not load a host key stored as a PKCS#8 "-----BEGIN PRIVATE KEY-----"
PEM file (the form emitted for ML-DSA keys, and by `openssl genpkey`).

SetupCTX() pre-converted the file with wc_PemToDer(..., PRIVATEKEY_TYPE, ...),
which only recognizes the classic "RSA/EC PRIVATE KEY" headers. On a PKCS#8 body
that call returns success but produces a malformed DER (leading 0x04 rather than a
0x30 SEQUENCE); the bytes were then passed to wolfSSH_CTX_UsePrivateKey_buffer()
as WOLFSSH_FORMAT_ASN1, which rejected them with WS_BAD_FILETYPE_E.

Detect PEM vs DER by content and decode PEM with wc_KeyPemToDer(), which handles
PKCS#1, SEC1 and PKCS#8 private-key bodies. DER keys are unchanged.

Tested (master + wolfSSL master): an ML-DSA-65 PEM host key now loads
(wolfSSH_CTX_UsePrivateKey_buffer ret = 0, was WS_BAD_FILETYPE_E); ML-DSA-65 DER
and ECDSA SEC1 PEM host keys continue to load (no regression).

Also harden the host-key load path per review: reject an empty (0-byte)
key file explicitly instead of taking the PEM branch into WMALLOC(0), and
zeroize the raw file buffer (not just the decoded DER) before freeing so
private-key material does not linger in the heap.
2026-07-27 15:44:54 -07:00
Mark Atwood 19c73fb2ac fix: guard sshd chroot steps after a failure
SetupChroot() in wolfsshd ran chdir(chrootPath), chroot(chrootPath) and
chdir("/") in three independent if-blocks with a single return at the end.
Each ran unconditionally, so chroot() executed even when the preceding
chdir() into the target failed. That can leave the process chrooted with a
working directory still outside the new root.

Short-circuit the later steps on the ret>0 (no-failure) state so chroot()
runs only after the chdir() into the target succeeds, and chdir("/") only
after chroot() succeeds. WS_FATAL_ERROR is negative, so ret>0 is true only
while no step has failed.
2026-07-24 14:58:52 -06:00
Mark Atwood 216c1a056e fix: force-zero secret buffers before free
Zeroize key/digest/handshake buffers before they are freed or go out
of scope, and fix a private-key leak on the CTX-full reject path.
Build-verified with ./configure --enable-all (make exit 0).

- #1278 internal.c KeyAgreeEcdhMlKem_client: WS_FORCEZERO handshake->x
  ML-KEM private key before wc_MlKemKey_Free, matching the DH path.
- #2082 agent.c wolfSSH_AGENT_ID_free: WMEMSET -> WS_FORCEZERO for the
  key buffer and the id struct so scrubs are not optimized away.
- #2496 internal.c SignHRsa/SignHEcdsa: WS_FORCEZERO digest (and encSig
  in SignHRsa) before return.
- #2497 internal.c BuildUserAuthRequest Rsa/RsaCert/Ecc/EccCert:
  WS_FORCEZERO digest (and encDigest for the RSA paths).
- #2498 internal.c DoUserAuthRequestPublicKey: WS_FORCEZERO digest before
  it leaves scope on both success and failure exits.
- #2500 internal.c DoUserAuthRequestRsa: WS_FORCEZERO encDigest before
  free (both SMALL_STACK and stack-array paths).
- #2501 internal.c DoUserAuthRequestRsaCert: same encDigest scrub.
- #2886 internal.c SshResourceFree: WS_FORCEZERO ssh->h and ssh->sessionId
  (and reset sizes) alongside the already-zeroed KDF inputs.
- #3453 internal.c SetHostPrivateKey: on CTX-full reject, take ownership
  and WS_FORCEZERO+WFREE der instead of leaking the private key.
- #3680 internal.c ChannelDelete: WS_FORCEZERO inputBuffer before free.
- #6277 wolfsftp.c ClearState/GET/PUT cleanup: WS_FORCEZERO SFTP get/put
  state structs before free (all four sites).
- #6278 wolfsshd.c SHELL_Subsystem: WS_FORCEZERO channelBuffer/shellBuffer
  on the data-carrying exit.
2026-07-23 14:52:13 -06:00
John Safranek 4f0d2d1f21 Fix CHECKED_RETURN defects in test_configuration.c
Coverity flagged unchecked return values in the wolfsshd config tests:

- Check the return code and the NULL check on the file handle returned
  by WFOPEN() in test_IncludeRecursionBound().
- Cast the return on WREMOVE() to void in test_IncludeRecursionBound()
  and CleanupWildcardTest().
- Check WFCLOSE() so a deferred flush failure fails the fixture setup,
  and void the remaining WRMDIR()/WCLOSEDIR()/WFCLOSE() returns.
- Report a short write and a failed close separately, testing the write
  first since a short write is what provokes the flush failure.
- Reset the WFILE* to WBADFILE after each close so the guard on it means
  the same thing on every loop iteration.
- Pass NULL rather than 0 for the WMKDIR() filesystem handle, and
  initialize the WFILE* to WBADFILE to match its later comparisons.

Issues: CID-646430, CID-646431
2026-07-21 16:09:29 -06:00
Paul Adelsbach 9f63fac8ac Fix test compile issue in auth.c 2026-07-21 15:51:07 -05:00
Yosuke Shimizu 37e852b95e Test SearchForPubKey authorized_keys no-match rejection 2026-07-21 13:04:42 -05:00
Yosuke Shimizu 9702235f91 wolfsshd: make the supplementary-group drop reliable and tested 2026-07-21 12:27:36 -05:00
Yosuke Shimizu a72b9448ad Gate wolfsshd PermitRootLogin on resolved UID 0 2026-07-21 12:26:48 -05:00
Yosuke Shimizu cc5311a829 wolfsshd: open PID file with O_NOFOLLOW and a fixed mode 2026-07-20 20:02:53 -05:00