Add OpenSSH certificate user authentication - #1060
Conversation
2576e03 to
1c40cc1
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1060
Scan targets checked: wolfssh-bugs, wolfssh-src
No new issues found in the changed files. ✅
1c40cc1 to
c00d111
Compare
aidangarske
left a comment
There was a problem hiding this comment.
🐺 Skoll Code Review
Overall recommendation: REQUEST_CHANGES
Findings: 4 total — 4 posted, 0 skipped
Posted findings
- [High] RSA certificate guards assume rsa-sha2-256 is always available —
src/internal.c:15861-15876 - [Medium] OpenSSH RSA certificate CA signatures bypass SHA-1 soft-disable policy —
src/ossh.c:1013-1025 - [Medium] OSSH certificate API test assumes RSA and ECDSA are enabled —
tests/api.c:1579-1598 - [Medium] New sshd OSSH cert test cannot be selected or excluded —
apps/wolfsshd/test/run_all_sshd_tests.sh:5-14
Review generated by Skoll.
c00d111 to
e52b9ae
Compare
e52b9ae to
0565b64
Compare
0565b64 to
20d4cc9
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1060
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 5
3 finding(s) posted as inline comments (see file-level comments below)
Medium (1)
Unknown key type in keys file aborts scan instead of skipping the line
File: apps/wolfsshd/auth.c:276-278
Function: CheckAuthKeysLine
Category: Error handling defect
CheckAuthKeysLine returns WS_FATAL_ERROR (-1001) whenever the key-type token on a line is not in its internal allowedTypes[] list. SearchKeysFile treats any negative return code as a hard scan error and breaks out of the scan loop immediately (lines 865-868), returning the error code to the caller. A TrustedUserCAKeys file — the new use-case introduced by this PR — may legitimately contain entries whose type strings wolfSSH does not yet support (e.g., sk-ssh-ed25519@openssh.com FIDO2 CA keys, or future algorithm identifiers). If any such line appears before the matching CA key entry, SearchKeysFile returns WS_FATAL_ERROR and the entire CA trust check fails, denying certificate authentication even when the correct CA is present later in the file.
Recommendation: In CheckAuthKeysLine, change the !typeOk branch from returning WS_FATAL_ERROR to returning WSSHD_AUTH_FAILURE so that lines with unsupported key types are silently skipped rather than treated as a parse error that aborts the containing scan loop in SearchKeysFile.
Referenced code: apps/wolfsshd/auth.c:276-278 (3 lines)
Low (1)
Integration test uses a hardcoded port with no availability guard
Category: Hardcoded paths, ports, or environment dependencies
PORT=22226 is hardcoded at the top of the script with no check that the port is free before starting the test server. Parallel CI runs of this test, or a pre-existing process on port 22226, will cause the sshd startup to fail or produce spurious test failures. This is a test-quality issue, not a production defect.
Recommendation: Use a port chosen dynamically (e.g., via a helper that probes an available ephemeral port) or guard startup with a retry/wait loop, consistent with the pattern used by other integration tests in the repository.
This review was generated automatically by Fenrir. Findings are non-blocking.
d268d3a to
9e43f35
Compare
|
Hello @aidangarske , @ejohnstown , |
|
This is something @ejohnstown should review when he has time |
9e43f35 to
8ac6536
Compare
|
Hello @ejohnstown , |
8ac6536 to
3c08c93
Compare
ejohnstown
left a comment
There was a problem hiding this comment.
Review comments provided directly.
3c08c93 to
22131aa
Compare
22131aa to
81918a6
Compare
Add OpenSSH certificate user authentication
Adds OpenSSH certificate (
*-cert-v01@openssh.com) user auth to wolfSSHd behind--enable-ossh-certs, which is opt-in and deliberately not part of--enable-all— existing--enable-alldeployments are unaffected by this PR.Operators can authorize users by trusting a single CA via
TrustedUserCAKeysinstead of distributing every user's public key into
authorized_keys.What's implemented (Unix)
Library (
src/ossh.c,src/internal.c) — portable, no platform deps:embedded CA key (RSA incl.
rsa-sha2-256/512, ECDSA P-256/384/521, Ed25519).or unknown names rejected. Only
force-commandandsource-addressaresupported, and an empty or malformed value is rejected rather than treated
as unrestricted.
accepted) but not enforced — see Known limitations.
against it, using the on-the-wire signed length.
caKey, principals, validity,source-address,force-command) are handed to the user-auth callback.Daemon (
apps/wolfsshd/auth.c,wolfsshd.c) —CheckPublicKeyUnixenforcesfail-closed, in order: CA trust → principal binding (a principal-less cert is
rejected, matching
sshd) → validity window → source-address → force-command.Security model
The library proves only that the certificate is self-consistent (signed by its
embedded CA) and that the client holds the certified private key. It does not
decide trust — the callback checks
caKeyagainst a trust store and enforcespolicy, mirroring how public-key auth delegates the
authorized_keyscheck. Thelibrary fails closed when no user-auth callback is set.
ForceCommand and certificate force-command
A certificate
force-commandrestricts what the session may do — it deniesfile transfer unless the command is exactly
internal-sftp. A configuredForceCommandreplaces what is run for shell and exec, and does not byitself deny SFTP or SCP.
ForceCommandforce-command/bin/wrapper/bin/wrapper/bin/backup/bin/backupinternal-sftpinternal-sftp/bin/wrapper/bin/backup/bin/wrapperRow 1 is unchanged from master. Row 4 deliberately diverges from OpenSSH, which
checks
adm_forced_commandfirst and never reaches the certificate's command, soa configured
ForceCommandlifts the CA's restriction entirely; wolfSSHd honorsboth. wolfSSHd also denies where
sshdsubstitutes the forced command and letsthe client fail against the wrong program — the observable outcome matches in
every row.
Configuration
./configure --enable-ossh-certs --enable-sshd # or --enable-all --enable-ossh-certsPlatform support
the threaded path. Follow-up in two PRs (per-connection cert-state relocation,
then enforcement plus a Windows CI cert-auth job).
Testing
tests/api.c— parse, CA-signature verify, critical-option handling andextension tolerance,
OsshCertBaseIdmapping,OsshRsaCertSigIdselection, andrejection of a CA blob whose curve name and key type disagree.
tests/unit.c— Ed25519 public-key parse, the user-signature wire-lengthpath, and the
wolfSSH_RsaVerifyNULL-signature guard.test_configuration.c— principal binding (incl. empty-list rejection),validity window, source-address CIDR matching and enforcement through
CheckPublicKeyUnix, and force-command precedence.sshd_ossh_cert_test.sh— end-to-end via the wolfSSH and OpenSSH clients:valid certs (Ed25519/RSA/ECDSA CAs and user keys), untrusted CA, wrong and empty
principal, expired, unknown critical option, source-address match/deny,
force-command,
internal-sftp, SFTP/SCP gating, and a configuredForceCommandalongside a certificate.
--enable-allno longer implies the feature:sshd-test.ymlbuilds--enable-all --enable-ossh-certs(somake checkandthe
run_all_sshd_tests.she2e suite cover it), andos-check.ymladds an--enable-all --enable-ossh-certsmatrix entry on Ubuntu and macOS. The plain--enable-allentries stay, so both states are built.sshd_ossh_cert_test.shself-skips (exit 77) when the feature is off.
Known limitations
permit-pty,permit-port-forwarding,permit-agent-forwarding,permit-X11-forwardingand
permit-user-rcare recognized and validated, then ignored. In OpenSSHthese are opt-in permissions where absence means deny, so wolfSSH is the
permissive direction here. The live gap is
permit-pty: assh-keygen -O clear -O force-command=...certificate still gets aninteractive pty, because
SHELL_Subsystem()decides onSHELL_IsPty(ssh)alone. The four forwarding/user-rc extensions have no corresponding wolfSSHd
feature to gate today. Enforcement is a follow-up.
!entry) is not implemented; a negated entry failsthe whole list closed and logs the offending entry.
for them — the loader arrives with the feature. With
--enable-ossh-certsandX.509 disabled, a configured
HostCertificatenow fails daemon startup withWS_UNIMPLEMENTED_E— the previous guard was a never-defined macro, so thedirective was read and silently discarded. Remove
HostCertificateor enableX.509. This only affects builds that opt into
--enable-ossh-certs;--enable-allkeeps its current behavior, including theTrustedUserCAKeysX.509 load failure.
DoUserAuthRequestPublicKey; it is covered end-to-end only. Follow-up.Notes for reviewers
WS_UserAuthData_PublicKey: the cert fields are appended after every existingmember, and
isOsshCertsits afterdataToSignSzrather than packing withisCert, so every member a user-auth callback can reach keeps the same offsetwhether or not
WOLFSSH_OSSH_CERTSis enabled (verified withoffsetofin bothbuilds). A flag mismatch therefore truncates rather than misaligns. The struct
is a stack local in
DoUserAuthRequest(), so the padding is not aper-connection cost.
cannedKeyAlgoNamesHostKey, which omitsthe
*-cert-v01@openssh.comnames (host-certificate verification isunimplemented) while keeping the plain, X.509, and ML-DSA names in sync.
src/ossh.cis in the Windows MSVC project because the OpenSSH key decoders itdefines are referenced unconditionally.