From: Patrick Steinhardt <ps@pks.im>
To: graysongordon-gl <graysongordon1@gmail.com>
Cc: gitster@pobox.com, git@vger.kernel.org
Subject: Re: [PATCH v4] http: add http.sslVerifyStatus to check stapled OCSP responses
Date: Tue, 18 Aug 2026 09:50:28 +0200 [thread overview]
Message-ID: <aoQOxISPfEwh-ik2@pks.im> (raw)
In-Reply-To: <20260817185242.22736-1-ggordon@gitlab.com>
On Mon, Aug 17, 2026 at 02:52:42PM -0400, graysongordon-gl wrote:
> From: Grayson Gordon <graysongordon1@gmail.com>
>
> git asks libcurl to verify the peer certificate and the hostname, but it
> never sets CURLOPT_SSL_VERIFYSTATUS, so the "Certificate Status Request"
> TLS extension is never requested and any stapled OCSP response the server
> does send is ignored.
>
> On an OpenSSL-linked build this is silent. OpenSSL hands the stapled
> response to the application and takes no view on it:
> SSL_CTX_set_tlsext_status_cb(3) says the callback "should determine
> whether the returned OCSP response(s) are acceptable or not", and libcurl
> only installs that callback when CURLOPT_SSL_VERIFYSTATUS is set. So git
> will fetch from a server whose own staple says its certificate has been
> revoked.
>
> A GnuTLS-linked build behaves differently, and the difference does not
> come from curl. GnuTLS consults a stapled response inside
> gnutls_certificate_verify_peers(), so the failure surfaces through the
> verifypeer branch of curl's GnuTLS backend (lib/vtls/gtls.c) whether or
> not CURLOPT_SSL_VERIFYSTATUS was ever set. The same git, against the same
> server, therefore enforces revocation or not depending only on how its
> libcurl was built. That difference is documented here rather than papered
> over: this option turns the check on where the backend needs asking, and
> setting it to false does not turn the check off on GnuTLS.
This is only part of the story though: GnuTLS 3.8 introduced
GNUTLS_NO_STATUS_REQUEST, and curl 8.10 started to set that option in
case of `!verifystatus`. So with new-enough versions of both libraries,
Git behaves the same no matter whether we use OpenSSL or GnuTLS as
backend. See also aeb1a281ca (gtls: fix OCSP stapling management,
2024-08-20) in curl.
> Add an http.sslVerifyStatus boolean that sets CURLOPT_SSL_VERIFYSTATUS.
> Because http_options() is the collect_fn of a urlmatch config, the
> per-URL form works with no further changes:
>
> git config http.https://example.com/.sslVerifyStatus true
>
> It defaults to false, and has to. The option is fail-closed: libcurl fails
> verification when the server staples nothing at all, so turning this on
> globally would break every remote that does not staple.
>
> Leaving the default to libcurl is not an option either. The same
> complaint was raised there in https://github.com/curl/curl/issues/15483
> and closed as intentional ("Marked as enhancement since this was done on
> purpose"), with the observation that stapling is expected to see less use
> as Let's Encrypt drops OCSP support. If the check is to be reachable at
> all, the lever has to come from the application.
But... don't we still leave the default to libcurl? If
"http.sslVerifyStatus" is not set then we don't touch
`CURLOPT_SSL_VERIFYSTATUS`, either.
I might be misreading this though, as the whole commit message is quite
hard to digest. I'd assume that this is because it's generated by AI,
and it added a lot of the usual weird phrases to the message. It might
be a good idea to adapt the message to have a bit more of a human touch
to it.
> If the TLS backend cannot check the staple, curl_easy_setopt() returns
> CURLE_NOT_BUILT_IN. Fail loudly there rather than carrying on, since
> silently not checking is precisely what this option exists to prevent.
Makes sense.
> diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
> index 792a71b413..40b849bf7f 100644
> --- a/Documentation/config/http.adoc
> +++ b/Documentation/config/http.adoc
> @@ -196,6 +196,23 @@ http.sslVerify::
> over HTTPS. Defaults to true. Can be overridden by the
> `GIT_SSL_NO_VERIFY` environment variable.
>
> +http.sslVerifyStatus::
> + Whether to check the revocation status of the server
> + certificate using the stapled OCSP response supplied during
> + the TLS handshake ("OCSP stapling"). Defaults to false.
> ++
> +This is fail-closed: if the server staples no response, verification
> +fails. Set it per remote, e.g.
> +`http.https://example.com/.sslVerifyStatus`, rather than globally.
> ++
> +What it changes depends on the TLS backend libcurl was built against.
> +An OpenSSL-linked build ignores a stapled response unless this is set.
> +A GnuTLS-linked build consults the staple during ordinary certificate
> +verification, so it already rejects a revoked certificate under
> +`http.sslVerify` alone, and setting this to `false` does not disable
> +that. Where a backend cannot check the staple at all, git fails with an
> +error rather than continuing unchecked.
This information is not accurate because recent GnuTLS+libcurl versions
handle this the same as OpenSSL, as mentioned above.
Also, it might make sense to convert the backend-specific information
into a bulleted list as we may add more items to it going forward. Do we
have any info how other backends like mbedTLS behave? Or do we know that
those all fail.
> diff --git a/http.c b/http.c
> index caccf2108e..94f8dd817a 100644
> --- a/http.c
> +++ b/http.c
> @@ -400,6 +401,10 @@ static int http_options(const char *var, const char *value,
> curl_ssl_verify = git_config_bool(var, value);
> return 0;
> }
> + if (!strcmp("http.sslverifystatus", var)) {
> + curl_ssl_verify_status = git_config_bool(var, value);
> + return 0;
> + }
> if (!strcmp("http.sslcipherlist", var))
> return git_config_string(&ssl_cipherlist, var, value);
> if (!strcmp("http.sslversion", var))
> @@ -1133,6 +1138,11 @@ static CURL *get_curl_handle(void)
> curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2L);
> }
>
> + if (curl_ssl_verify_status &&
> + curl_easy_setopt(result, CURLOPT_SSL_VERIFYSTATUS, 1L) != CURLE_OK)
> + die(_("http.sslVerifyStatus is set, but the TLS backend of "
> + "this libcurl cannot verify certificate status"));
Should we include the output of `curl_easy_strerror()` in the error
message? That'd cause us to include the following error message in case
we see CURLE_NOT_BUILT_IN:
case CURLE_NOT_BUILT_IN:
return "A requested feature, protocol or option was not found built-in in"
" this libcurl due to a build-time decision.";
So we could instead do:
if (curl_ssl_verify_status) {
CURLcode error = curl_easy_setopt(result, CURLOPT_SSL_VERIFYSTATUS, 1L);
if (error != CURLE_OK)
die(_("http.sslVerifyStatus is set, but could not enable OCSP status verification: %s"),
curl_easy_strerror(error));
}
> diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
> index 805bec025c..c11e96c1ac 100755
> --- a/t/t5551-http-fetch-smart.sh
> +++ b/t/t5551-http-fetch-smart.sh
> @@ -680,6 +680,35 @@ test_expect_success 'passing hostname resolution information works' '
> git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
> '
>
> +test_lazy_prereq SSL_VERIFYSTATUS '
> + test "$HTTPD_PROTO" = "https" &&
> + test_might_fail git -c http.sslVerifyStatus=true \
> + ls-remote "$HTTPD_URL/smart/repo.git" 2>err &&
> + ! grep "cannot verify certificate status" err
> +'
> +
> +test_expect_success SSL_VERIFYSTATUS 'http.sslVerifyStatus=true fails without a staple' '
> + test_must_fail git -c http.sslVerifyStatus=true \
> + ls-remote "$HTTPD_URL/smart/repo.git"
> +'
> +
> +test_expect_success SSL_VERIFYSTATUS 'http.sslVerifyStatus=false is a no-op' '
> + git -c http.sslVerifyStatus=false \
> + ls-remote "$HTTPD_URL/smart/repo.git" >actual &&
> + test_line_count -gt 0 actual
> +'
> +
> +test_expect_success SSL_VERIFYSTATUS 'per-URL sslVerifyStatus applies to a matching URL' '
> + test_must_fail git -c "http.$HTTPD_URL/.sslVerifyStatus=true" \
> + ls-remote "$HTTPD_URL/smart/repo.git"
> +'
> +
> +test_expect_success SSL_VERIFYSTATUS 'per-URL sslVerifyStatus is not applied to other URLs' '
> + git -c "http.https://example.com/.sslVerifyStatus=true" \
> + ls-remote "$HTTPD_URL/smart/repo.git" >actual &&
> + test_line_count -gt 0 actual
> +'
Can we reasonably add tests that send OCSP information and verify that
enabling "sslVerifyStatus" makes this work as expected?
Patrick
next prev parent reply other threads:[~2026-08-18 7:50 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 17:02 [PATCH] http: add http.sslVerifyStatus to check stapled OCSP responses graysongordon-gl
2026-08-11 19:28 ` Junio C Hamano
2026-08-11 20:44 ` [PATCH v2] " graysongordon-gl
2026-08-12 6:25 ` Patrick Steinhardt
2026-08-12 15:53 ` Grayson Gordon
2026-08-12 14:17 ` Junio C Hamano
2026-08-12 18:25 ` [PATCH v3] " graysongordon-gl
2026-08-12 21:34 ` Junio C Hamano
2026-08-13 16:06 ` Junio C Hamano
2026-08-17 18:52 ` [PATCH v4] " graysongordon-gl
2026-08-17 19:19 ` Junio C Hamano
2026-08-18 7:50 ` Patrick Steinhardt [this message]
2026-08-18 14:51 ` Grayson Gordon
2026-08-18 16:40 ` Junio C Hamano
2026-08-18 19:37 ` [PATCH v5] " graysongordon-gl
2026-08-18 20:12 ` Junio C Hamano
2026-08-18 21:22 ` Grayson Gordon
2026-08-18 21:48 ` [PATCH v6] " graysongordon-gl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aoQOxISPfEwh-ik2@pks.im \
--to=ps@pks.im \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=graysongordon1@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox