Skip to content

Add OpenSSH certificate user authentication#1060

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:feat/osshCert
Open

Add OpenSSH certificate user authentication#1060
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:feat/osshCert

Conversation

@yosuke-wolfssl

Copy link
Copy Markdown
Contributor

Add OpenSSH certificate user authentication

Adds support for authenticating users with OpenSSH certificates
(*-cert-v01@openssh.com) in wolfSSHd, behind a new --enable-ossh-certs
configure option (also enabled by --enable-all).

Motivation

OpenSSH certificates let an operator authorize users by trusting a single CA
(via TrustedUserCAKeys) instead of distributing every user's public key into
authorized_keys. This brings wolfSSHd in line with stock OpenSSH sshd for
certificate-based user auth.

What's implemented (Unix)

Library (src/ossh.c, src/internal.c) — portable, no platform deps:

  • Parse the OpenSSH certificate wire format and validate type.
  • Verify the certificate's CA signature against the CA key embedded in the
    certificate (RSA incl. rsa-sha2-256/512, ECDSA P-256/P-384/P-521, Ed25519).
  • Reject unknown critical options; extract force-command and
    source-address; tolerate unknown extensions.
  • Reconstruct the certified base user key and verify the user's signature
    against it (proving possession of the certified private key), using the
    on-the-wire signed length so it works with the reconstructed key.
  • Parsed policy fields (caKey, principals, validity, source-address,
    force-command) are handed to the user-auth callback for enforcement.

Daemon (apps/wolfsshd/auth.c, wolfsshd.c)CheckPublicKeyUnix enforces,
fail-closed and in order:

  1. CA trust — the signing CA must be in TrustedUserCAKeys.
  2. Principal binding — the login user must be a listed principal (a
    principal-less certificate is rejected, matching OpenSSH sshd).
  3. Validity window — valid_after/valid_before.
  4. Source-address — peer IP must match the certificate's CIDR list.
  5. Force-command — a verified certificate's force-command overrides the
    requested command for shell, exec, SFTP, and SCP sessions
    (internal-sftp still permits SFTP).

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 — that is the application callback's responsibility
    (check caKey against a trust store, bind the principal, enforce
    validity/source-address). This mirrors how regular public-key auth delegates
    the authorized_keys check.
  • The library fails closed when no user-auth callback is set.

Configuration

# wolfsshd config
TrustedUserCAKeys /etc/wolfssh/trusted-user-ca-keys.pub
./configure --enable-ossh-certs --enable-sshd   # or --enable-all

Platform support

  • Unix: fully supported and enforced.
  • Windows: intentionally fails closed — a certificate is never accepted
    on the Windows threaded path. Follow-up work is planned in two PRs
    (per-connection cert-state relocation, then Windows enforcement + a Windows CI
    cert-auth job).

Testing

  • tests/api.c — certificate parse, CA-signature verify, and
    critical-option/extension handling (self-contained binary vectors).
  • tests/unit.c — Ed25519 public-key parse and the user-signature
    wire-length path (wire-derived length equals the legacy field-sum).
  • apps/wolfsshd/test/test_configuration.c — direct unit tests for the
    enforcement helpers (principal binding incl. empty-list rejection, validity
    window, source-address CIDR/prefix matching).
  • apps/wolfsshd/test/sshd_ossh_cert_test.sh — end-to-end against both the
    wolfSSH example client and the system OpenSSH client: valid cert (Ed25519/RSA/
    ECDSA CAs and user keys), untrusted CA, wrong principal, empty principal,
    expired cert, unknown critical option, source-address match/deny,
    force-command, internal-sftp, and SFTP/SCP force-command gating.
  • Wired into CI (--enable-all compiles the option in; the e2e runs via
    run_all_sshd_tests.sh).

Known limitations

  • Windows: fail-closed only (see above).
  • Source-address negation (!entry) is not implemented; a negated entry
    fails the whole list closed (denies) and logs the offending entry.
  • Host certificates are not implemented; wolfSSH_CTX_UseOsshCert_buffer
    returns WS_UNIMPLEMENTED_E.

Notes for reviewers

  • The OSSH cert fields in WS_UserAuthData_PublicKey are appended after the
    existing members so the default (WOLFSSH_OSSH_CERTS disabled) struct layout
    is unchanged; enabling the option changes the layout, so the app and library
    must be built with the same setting.
  • src/ossh.c is added to the Windows MSVC project (wolfssh.vcxproj) because
    the OpenSSH key decoders it defines are referenced unconditionally.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Jun 25, 2026
Copilot AI review requested due to automatic review settings June 25, 2026 01:59
@yosuke-wolfssl yosuke-wolfssl marked this pull request as draft June 25, 2026 01:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@yosuke-wolfssl yosuke-wolfssl force-pushed the feat/osshCert branch 2 times, most recently from 2576e03 to 1c40cc1 Compare June 25, 2026 02:53

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #1060

Scan targets checked: wolfssh-bugs, wolfssh-src

No new issues found in the changed files. ✅

@yosuke-wolfssl yosuke-wolfssl marked this pull request as ready for review June 25, 2026 04:11

@aidangarske aidangarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 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 availablesrc/internal.c:15861-15876
  • [Medium] OpenSSH RSA certificate CA signatures bypass SHA-1 soft-disable policysrc/ossh.c:1013-1025
  • [Medium] OSSH certificate API test assumes RSA and ECDSA are enabledtests/api.c:1579-1598
  • [Medium] New sshd OSSH cert test cannot be selected or excludedapps/wolfsshd/test/run_all_sshd_tests.sh:5-14

Review generated by Skoll.

Comment thread src/internal.c
keySig->sigId = matchId;
keySig->sigName = IdToName(matchId);
keySig->sigNameSz = (word32)WSTRLEN(keySig->sigName);
#ifdef WOLFSSH_OSSH_CERTS

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] RSA certificate guards assume rsa-sha2-256 is always available
🚫 BLOCK bug

The new OpenSSH RSA certificate path can reference cannedKeyAlgoOsshRsaSha2_256CertName even when WOLFSSH_NO_RSA_SHA2_256 is defined. internal.h allows WOLFSSH_NO_RSA_SHA2_256 to be set by NO_SHA256 while rsa-sha2-512 remains available, so an --enable-ossh-certs build with SHA-256 disabled and SHA-512 enabled fails to compile on this fallback. The same guard mismatch also advertises/maps rsa-sha2-512-cert-v01@openssh.com while parts of the cert handling gate ID_OSSH_CERT_RSA on SHA2-256, leaving SHA512-only builds inconsistent.

Recommendation: Use one consistent RSA-cert availability guard based on RSA plus at least one RSA SHA2 signature algorithm. Only reference the SHA2-256 cert name under #ifndef WOLFSSH_NO_RSA_SHA2_256, choose SHA2-512 when SHA2-256 is disabled, and apply the same guard to cannedKeyAlgoNames, NameIdMap, cannedKeyAlgoClient, OsshCertBaseId, and OsshRsaCertSigId. Add a compile configuration covering WOLFSSH_OSSH_CERTS with WOLFSSH_NO_RSA_SHA2_256 and SHA2-512 still enabled.

Comment thread src/ossh.c
&idx);
}

/* select hash from the signature algorithm name */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] OpenSSH RSA certificate CA signatures bypass SHA-1 soft-disable policy
💡 SUGGEST

The PR introduces OpenSSH certificate verification and accepts an RSA CA certificate signature whose signature algorithm string is ssh-rsa, mapping it to WC_HASH_TYPE_SHA. Data Flow: remote SSH userauth public-key packet -> certificate blob parsed by DoUserAuthRequestPublicKey -> OsshCertParse extracts the attacker-supplied signature algorithm from the certificate signature field -> OsshCertVerifySignature dispatches to OsshVerifyRsa -> SHA-1 RSA verification is accepted -> the daemon later trusts the embedded CA key via TrustedUserCAKeys. This is inconsistent with the existing user-auth algorithm list, where plain ID_SSH_RSA is only advertised when WOLFSSH_NO_SHA1_SOFT_DISABLE is defined. As a result, builds that otherwise soft-disable SHA-1 RSA can still authenticate OpenSSH certificates signed by a trusted RSA CA using SHA-1. A malicious or compromised certificate issuer, or an attacker able to exploit SHA-1 collision weaknesses in a CA-signing workflow, gets a weaker authentication path than the build policy intends. This issue is introduced by the PR's new RSA OpenSSH certificate verifier; no existing test asserts that SHA-1 OSSH CA signatures are rejected under the soft-disable configuration.

Recommendation: Apply the same SHA-1 soft-disable policy used by normal public-key authentication, or reject ssh-rsa CA signatures entirely by default. For example gate the ssh-rsa branch under #ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE. Also add a negative OpenSSH certificate test that signs an RSA-CA certificate with ssh-rsa and verifies rejection when SHA-1 is soft-disabled.

Comment thread tests/api.c
/* Parse, verify the CA signature, and validate the options of each committed
* certificate vector; then flip the final signature byte and confirm the
* verification fails while the parse still succeeds. */
static void test_wolfSSH_OsshCert_valid(void)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] OSSH certificate API test assumes RSA and ECDSA are enabled
💡 SUGGEST test

The new certificate verification test is guarded only by WOLFSSH_OSSH_CERTS and !WOLFSSH_NO_ED25519, but it always verifies vectors signed by Ed25519, RSA, and ECDSA CAs. Builds that enable OSSH certs and Ed25519 while disabling RSA or ECDSA will compile out the corresponding verifier and then fail this test, even though that algorithm-disabled configuration is otherwise valid.

Recommendation: Build the vector list conditionally: include the Ed25519 CA vector under Ed25519, the RSA CA vector under RSA support, and the ECDSA CA vector under ECDSA plus the needed curve support. Apply the same per-algorithm guards to the malformed verifier caTypes list.

@@ -176,6 +176,14 @@ else
printf "Shutting down test wolfSSHd\n"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] New sshd OSSH cert test cannot be selected or excluded
💡 SUGGEST test

sshd_ossh_cert_test.sh is invoked manually at the end of the local-host path, but it is not added to the test_cases array used by --match validation and --exclude. As a result, --match sshd_ossh_cert_test.sh is rejected as unknown, and --exclude sshd_ossh_cert_test.sh does not suppress the new test.

Recommendation: Add sshd_ossh_cert_test.sh to the selectable test list, or add a separate extra-test list that participates in --match and --exclude validation before running the self-contained local-host test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants