feat(windows): build curl against OpenSSL instead of Schannel - #1
Merged
Conversation
Schannel verifies against a CA file instead of the Windows certificate store, and refuses a file larger than 1 MiB. The CLI has to pass a CA file, because the openssl extension cannot read the store at all, so a root certificate installed by an organization is never trusted, and requests fail with cURL error 60. curl built against OpenSSL loads a CA file and the Windows stores together, and has no size limit. OpenSSL is already built here for the openssl extension. static-php-cli chose Schannel in its pull request 674, which also removed OpenSSL from curl's Windows dependencies, so both are changed back. The patch script fails the build if static-php-cli no longer looks the way it expects, rather than quietly producing a Schannel build, and a new step checks the backend of the binary that was built. Related to upsun/cli#110 Written by Claude Code.
The windows-latest image now ships Visual Studio 2026, and static-php-cli only looks for 2022 or 2019, so its doctor check fails before anything is built. Nothing had been built since April, so this went unnoticed. Written by Claude Code.
The windows-2025 image ships Visual Studio 2026 as well, so pinning it was not enough. Written by Claude Code.
Schannel fell back to the Windows certificate store when curl was given no CA file. OpenSSL does not, and CURL_CA_NATIVE is off by default, so without this a request with no CA file would have no trust anchors at all. curl only auto-enables the store when no CA file is set, so this does not change the case where the CLI passes its bundle. Written by Claude Code.
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds curl against OpenSSL instead of Schannel on Windows, so that the CLI can trust a root certificate installed by an organization. Related to upsun/cli#110.
Why
Schannel verifies against a CA file instead of the Windows certificate store: given a file, it builds a chain engine whose only trust anchors are that file's certificates. It also refuses a CA file larger than 1 MiB (
MAX_CAFILE_SIZEin curl'sschannel_verify.c).The CLI has to pass a CA file, because the
opensslextension cannot read the store at all and the CLI uses openssl streams for some requests. So on Windows the CLI trusts only the certificates shipped with it, and a machine whose TLS traffic is inspected — where every other program trusts the organization's root certificate from the store — fails withcURL error 60.This was measured on a Windows runner rather than inferred. With a CA installed in the machine's root store and a server signed by it:
openssl.cafileset to the shipped bundleERR:60:schannel: the certificate or certificate chain is based on an untrusted rootopenssl.cafileset to the shipped bundle plus the store's certificatesERR:77:schannel: CA file exceeds max size of 1048576 bytescurl built against OpenSSL loads a CA file and the Windows stores together (
lib/vtls/openssl.c,ossl_windows_load_anchors, reported as "Native: Windows System Stores ROOT+CA"), and no backend other than Schannel caps the file size. OpenSSL is already built here, for theopensslextension.What changed
scripts/patch-spc-windows-curl.phppatches the static-php-cli checkout before the build:CURL_USE_SCHANNEL/USE_WINDOWS_SSPIoff andCURL_USE_OPENSSLonopensslback to curl's Windows dependencies inconfig/lib.json, so the build order puts it firststatic-php-cli chose Schannel in crazywhalecc/static-php-cli#674 — "disable openssl due to certificate issue" — which is presumably that OpenSSL-built curl has no default CA bundle on Windows and so fails out of the box. That does not apply here, because the CLI always passes a bundle.
The script fails the build if static-php-cli no longer looks the way it expects, rather than quietly producing a Schannel build, which would reintroduce the bug silently. A new step then checks the backend of the binary that was actually built.
The Windows job now checks out this repository, which it did not need before.
Testing
The patch script was run against a clean static-php-cli 2.8.5 checkout: it makes the four expected changes and is idempotent. The build itself is what this PR is for — see the run on this branch.
Written by Claude Code.