Use of Windows certificate store for authentication - #900
Use of Windows certificate store for authentication#900JacobBarthelmeh wants to merge 9 commits into
Conversation
bd1396d to
ca912b0
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds Windows Certificate Store integration to wolfSSH so host keys and client authentication keys can be sourced from the Windows cert store (including CI coverage on Windows).
Changes:
- Add a Windows-only API to load a private key by locating a certificate in the Windows Certificate Store, and use CNG to sign during SSH handshakes/auth.
- Extend cert manager plumbing and wolfsshd configuration to support system/user CA loading and cert-store-based host keys.
- Update Windows build projects and add a GitHub Actions workflow to exercise file-vs-store interop permutations.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| wolfssh/test.h | Prefer wolfCrypt Base16 when available; otherwise keep local Base16 decode helper. |
| wolfssh/ssh.h | Add wolfSSH_CTX_UsePrivateKey_fromStore() Windows-only public API. |
| wolfssh/internal.h | Add CTX private-key metadata for cert-store backed keys and internal helper prototypes. |
| wolfssh/certman.h | Expose cert-manager setter and Windows cert-store spec parser API. |
| src/ssh.c | Implement loading a CTX private key from the Windows Certificate Store. |
| src/internal.c | Add cert-store signing path (CNG) and cert-derived RSA public-key extraction for KEX/auth flows. |
| src/certman.c | Implement wolfSSH_SetCertManager() and wolfSSH_ParseCertStoreSpec(). |
| ide/winvs/wolfsshd/wolfsshd.vcxproj | Link against crypt32/ncrypt for cert-store features. |
| ide/winvs/wolfssh/wolfssh.vcxproj | Link against crypt32/ncrypt for cert-store features. |
| ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj | Link against crypt32/ncrypt for cert-store features. |
| ide/winvs/unit-test/unit-test.vcxproj | Link against crypt32/ncrypt for cert-store features; normalize XML header. |
| ide/winvs/echoserver/echoserver.vcxproj | Link against crypt32/ncrypt for cert-store features. |
| ide/winvs/client/client.vcxproj | Link against crypt32/ncrypt for cert-store features. |
| ide/winvs/api-test/api-test.vcxproj | Link against crypt32/ncrypt for cert-store features; normalize XML header. |
| examples/sftpclient/sftpclient.c | Add -W store:subject:flags support for client key from Windows cert store. |
| examples/echoserver/echoserver.c | Add -W support for server host key from Windows cert store; skip key-file root search when using store. |
| examples/client/common.h | Declare helper functions for cert-store key loading/auth setup. |
| examples/client/common.c | Implement cert-store key loading wrapper + auth globals setup for x509v3 publickey auth. |
| apps/wolfsshd/wolfsshd.c | Add host-key-from-store support and optional system/user CA store loading into wolfSSH cert manager. |
| apps/wolfsshd/configuration.h | Add config getters for host-key store and Windows user-CA store options. |
| apps/wolfsshd/configuration.c | Add parsing/storage for new config directives and defaults. |
| .github/workflows/windows-cert-store-test.yml | Add Windows CI workflow to validate store/file combinations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
66c8726 to
b545c32
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 23 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b545c32 to
de28863
Compare
de28863 to
6b8a1ca
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #900
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #900
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
4d116bd to
7b7aff5
Compare
18ca6b5 to
cca116e
Compare
05e4add to
e7aa41e
Compare
16ff5e5 to
61985d5
Compare
8cd9b4b to
52a9f9e
Compare
add Windows cert store test case make windows cert feature default disabled and simplify macro guard additional unit tests, advertise x509 and pubkey, use CN to match username, build check for WOLFSSL_SYS_CA_CERTS, fix for CM ref count additional build test, uniform enum name, fail on unkown cert store ecc curve, tie in of loading whole cert store for sys CA's
…nged 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
52a9f9e to
263cfa3
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces and wires up a new Windows credential-loading path across core auth, daemon config, examples, and build systems, which is high-impact and warrants final human review despite only one concrete nit found here.
Review details
- Files reviewed: 36/36 changed files
- Comments generated: 1
- Review effort level: Lite
…yStoreSubject, HostKeyStoreFlags
aidangarske
left a comment
There was a problem hiding this comment.
Skoll Multi-Scan Review
Modes: review + review-security
Overall recommendation: REQUEST_CHANGES
Findings: 5 total — 5 posted, 0 skipped
5 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [High] [review-security] Default Windows builds accept certificates restricted to non-SSH purposes —
apps/wolfsshd/wolfsshd.c:696-702 - [Medium] [review] Parse -W as an option before skipping the root search —
examples/echoserver/echoserver.c:3178-3182 - [Medium] [review] Exercise the non-FPKI Windows CN-binding path —
apps/wolfsshd/auth.c:2836-2856 - [Medium] [review] Do not turn arbitrary certificate-parser failures into skips —
tests/unit.c:18409-18416 - [Medium] [review] Distinguish certificate-store enumeration failure from end of store —
apps/wolfsshd/wolfsshd.c:739-744
Review generated by Skoll
| "[SSHD] LOCAL_MACHINE is the safer location for trust anchors."); | ||
| } | ||
|
|
||
| #ifdef WOLFSSH_NO_FPKI |
There was a problem hiding this comment.
Default Windows builds accept certificates restricted to non-SSH purposes · Security
BEFORE this PR, wolfsshd had no Windows user-CA-store trust-source path. AFTER, LoadUserCACertsFromStore() imports CA certificates from the configured store and explicitly permits authentication without FPKI, merely warning that TLS-server, S/MIME, or code-signing certificates may be accepted. An attacker-supplied chain reaches wolfSSH_CERTMAN_VerifyCerts_buffer(), while the WOLFSSH_NO_FPKI path omits application-purpose, EKU, and key-usage enforcement. The new Windows authentication branch then checks only that the leaf subject CN matches the requested username before creating the user's token. Consequently, anyone holding a valid certificate and private key issued by a configured CA can authenticate as the matching Windows account even when that certificate is explicitly restricted to serverAuth, S/MIME, code signing, or lacks digitalSignature usage. This affects the supplied Visual Studio configuration, which defines WOLFSSH_NO_FPKI. Existing tests use a certificate without an explicit incompatible EKU and therefore do not cover this bypass (RFC 6187 section 2.2 requires digitalSignature when KeyUsage is present and restricts EKU-bearing certificates to their indicated purposes).
Fix: Enforce SSH-appropriate leaf usage independently of FPKI: require digitalSignature whenever KeyUsage is present and, whenever EKU is present, require id-kp-secureShellClient or an explicitly configured equivalent. Retain FPKI as an additional profile check. Add Windows integration tests proving that valid matching-CN chains containing serverAuth-only or codeSigning-only leaves are rejected.
There was a problem hiding this comment.
I believe that this issue is pre-existing in wolfSSH master branch. The fix widens the scope some of this PR (will add more lines to it) but it is a good fix to have for all code paths making use of X509 certs without FPKI enabled.
There was a problem hiding this comment.
This wolfSSL PR (wolfSSL/wolfssl#11361) adds some of the EKU logic needed for a cleaner bit check instead of the added manual parsing of the ASN1 syntax used for now. Later wolfSSH will be able to conditionally remove the parsing based on version of wolfSSL linked with.
| int i; | ||
| const char* spec; | ||
|
|
||
| for (i = 1; i < argc; i++) { |
There was a problem hiding this comment.
Parse -W as an option before skipping the root search · bug
The PR's preliminary scan treats every argument beginning with -W as the certificate-store option. This includes values consumed by preceding options and tokens following --. For example, a valid option value such as -a -WolfCA.pem makes EchoserverUsingCertStore() return true even though getopt will not process a -W option. wolfSSH_Echoserver() consequently skips ChangeToWolfSshRoot(), after which relative certificate and default host-key paths are resolved from an unexpected directory. The SFTP client avoids this ambiguity by using mygetopt() for its preliminary scan.
Fix: Use the same option string as the main parser, scan with mygetopt(), and restore myoptind afterward. Add coverage for an argument value beginning with -W and for --.
There was a problem hiding this comment.
Had seen this previously, was trying to avoid having the options set as a macro. It seemed a bit clunky in the examples to do it that way and changing the location of ChangeToWolfSshRoot() teases out a Mac OS / clang bug that leads to a segfault. I'll update the echoserver though to have the macro set for options.
| if (upnRealmUnchecked) { | ||
| wolfSSH_Log(WS_LOG_WARN, "[SSHD] AuthorizedUPNDomains " | ||
| "not set; certificate UPN domain is not checked"); | ||
| #else |
There was a problem hiding this comment.
Exercise the non-FPKI Windows CN-binding path · test
The PR adds a distinct authentication path that binds certificates to Windows accounts through the subject CN when WOLFSSL_FPKI is absent. The new Windows workflow enables OPENSSL_ALL, which enables FPKI, and explicitly notes that only the UPN branch is exercised. Consequently, the newly introduced CN extraction, length comparison, and case-insensitive account matching can regress without any functional test detecting it.
Fix: Add a Windows build configuration without OPENSSL_ALL/WOLFSSL_FPKI and test a matching CN, a case-differing matching account name, and a mismatched CN.
There was a problem hiding this comment.
Will expand the test coverage.
| /* matching curve accepted; a failure here means the chain itself did | ||
| * not verify under this build's profile, so skip the curve vectors | ||
| * rather than fail on unrelated policy */ | ||
| if (result == 0) { |
There was a problem hiding this comment.
Do not turn arbitrary certificate-parser failures into skips · test
The new control vector converts every failure from the matching P-256 ParseECCPubKeyCert() path into a positive skip result. The test harness then ignores all positive results. A regression in the newly changed certificate-chain parser, curve binding, or root verification can therefore make this test print SKIPPED while CI remains green, preventing the subsequent mismatch vector from running as well.
Fix: Skip only for an explicitly identified unsupported build profile or unavailable fixture. Treat unexpected parser and verification errors as test failures, or provide a fixture valid under every profile in which the test is compiled.
There was a problem hiding this comment.
Now only WS_CERT_PROFILE_E should skip.
| return WS_FATAL_ERROR; | ||
| } | ||
|
|
||
| /* Passing the previous context frees it and advances the enumeration. */ |
There was a problem hiding this comment.
Distinguish certificate-store enumeration failure from end of store · bug
The PR's new loader treats any NULL result from CertEnumCertificatesInStore() as successful completion. Microsoft's API contract requires checking GetLastError() because NULL represents both normal exhaustion and enumeration errors. If an error occurs after at least one CA was loaded, loaded > 0 makes this function return success and report the partial trust-store load as complete, silently omitting the remaining configured authorities.
Fix: Capture GetLastError() immediately after enumeration returns NULL. Accept only the documented end-of-enumeration statuses such as CRYPT_E_NOT_FOUND and, where applicable, ERROR_NO_MORE_FILES; otherwise fail the load and avoid reporting partial success.
There was a problem hiding this comment.
Fixed to check GetLastError()
…tEnumCertificatesInStore, add extended key usage type check if present, fix for EKU checks with X509 use and add test case
No description provided.