-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce CA certificate bundle reparsing #9620
Conversation
In terms of performance improvement, to load the BBC News site in NetSurf:
Note, these numbers include all the rest of the processing done to render the page, not just the work done in fetching over HTTPS connections. To test, my command was:
I am just loading the one page and then exiting, so the page still requires that initial building of the The profile for running against the master libcurl branch shows that the The profile for running against this libcurl branch shows that the |
@bagder This is a draft merge request for now because it currently doesn't try to stat the file to see if it has changed, or optimise cases where the certificate store isn't loaded from a |
The fuzzer test fails with:
It looks like both of those were added in OpenSSL 1.1.0. So we could make the caching dependent on OpenSSL >= 1.1.0. Which version is the fuzzer build built against? |
9da89f4
to
cc528ac
Compare
From the log:
That's weird, the documentation states:
|
You should assume that people will use OpenSSL from even before 1.0.0, so yes the code needs to have the proper #ifdef conditions to only use the APIs that are available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without globals, you can also skip the mutex protections.
Yep, I'll add that. I'm just confused about the fuzzer test compilation failing when it seemingly has a sufficient openssl version. |
I'm not sure how we can store a shared X509_STORE without some form of mutex
protections though.
Would an atomic swap work?
|
On Thu, Sep 29, 2022 at 09:26:47AM -0700, Michael Drake wrote:
@bagder This is a draft merge request for now because it currently doesn't try
to stat the file to see if it has changed, or optimise cases where the
certificate store isn't loaded from a CAfile. Please let me know what you think
of it so far, all suggestions/ideas/comments welcome.
This is the biggest question IMHO. Until now, changes to the certificates will
take effect on the next connection, but now they never will be. And, it's
probably not straightforward to tell exactly when they've changed, since
systems using a symlink tree based on hashes could have hundreds or even
thousands of files to stat, not just one.
Unless that can be somehow solved automatically in all cases, I think this
feature is going to have to be hidden behind a setopt that 1) enables it, and
2) sets a caching policy. And you might be stuck with policies that are
entirely time based or rely on explicit cache clearing, niether of which are
ideal.
|
The fuzzer builds OpenSSL from source:
|
12b8b6c
to
bba9232
Compare
That's true, just
I'm happy to do this if we can reach a consensus on what it should actually look like. Another option may be to let the client build the X509_STORE itself, and let it pass that in as an alternative to |
bba9232
to
4cead27
Compare
Applications also have the option to use |
Oh, my understanding was that the |
Ah right, I didn't realize it was that part that is so costly. Never mind me! 😬 |
83ee405
to
29752a4
Compare
For what it's worth, I've experimented with CURLOPT_CAINFO_BLOB to confirm that, and found it makes no meaningful difference to performance on stock libcurl, compared to using In terms of the speedup gained by caching the X509_STORE, over the course of a reasonable browsing session, it reduces the time spent in |
I found that libressl claims openssl version 1.1.1, but does not have So I changed it to calling |
I've been thinking some more, and I think this is the best option, for a number of reasons:
However there's one thing I don't like, which is if we just pass in a pointer to the built X509_STORE, it needs to match the certificate store format for the SSL backend. I don't even know how compatible different OpenSSL backends (LibreSSL/BoringSSL) would be. So my proposal is basically to let libcurl build the X509_STORE and allow the client to request it. The new API would be something like: CURLcode curl_easy_create_castore(const CURL *curl, void **castore);
CURLcode curl_easy_destroy_castore(void *castore); In more detail: Client sets up an easy handle with whatever certificate store source they want, exactly as before. For example: CURLcode code;
CURL *easy_handle;
/* ... */
code = curl_easy_setopt(easy_handle, CURLOPT_CAINFO,
get_option_cafile());
if (code != CURLE_OK)
goto curl_easy_setopt_failed;
code = curl_easy_setopt(easy_handle, CURLOPT_CAPATH,
get_option_capath());
if (code != CURLE_OK)
goto curl_easy_setopt_failed; And then, to opt in to the new cached store stuff, the client would get the cached store with the new API: void *castore = NULL;
code = curl_easy_create_castore(easy_handle, CURLINFO info, void **castore);
if (code != CURLE_OK) {
/* We probably want a new CURLcode for something like
* `CURLE_SSL_BACKEND_UNIMPLEMENTED`. */
} And then set a new if (castore != NULL) {
code = curl_easy_setopt(easy_handle, CURLOPT_CASTORE,
castore);
if (code != CURLE_OK)
goto curl_easy_setopt_failed;
} There's no real need to worry about whether the used libcurl SSL backend implements the functionality either, because if it doesn't the Basically if a Does anyone have any thoughts? Reasons I shouldn't implement this? Better ideas? |
There probably needs to be a |
The idea of the |
Since the CURLcode curl_castore_create(const CURL *curl, void **castore);
void curl_castore_destroy(void *castore); |
I think we can simplify based on the common usage patterns.
I could imagine a system that caches the CA context per multi handle for a given file name. Either as a cache for multiple file names ( This would then by default make libcurl not load new CA certs when subsequent new connections are made, contrary to current functionality. We could add a bit to |
828ea1b
to
36a4caa
Compare
lib/urldata.h
Outdated
int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ | ||
int dns_cache_timeout; /* DNS cache timeout (seconds) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, I put this in struct UserDefined
, next to dns_cache_timeout
.
But maybe you'd prefer it in struct ssl_general_config
?
I don't think it makes sense to put it in struct ssl_config_data
because it doesn't have a different value for the proxy case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided it belonged in struct ssl_general_config
so I moved it.
36a4caa
to
9ac3132
Compare
9ac3132
to
e7c57e6
Compare
Rebased on master and retested. Still gives the optimisation. |
free(mbackend->CAfile); | ||
free(mbackend); | ||
#else /* HAVE_SSL_X509_STORE_SHARE */ | ||
(void)mbackend; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CWE 416 advisory: potential use after free
ℹ️ Learn about @sonatype-lift commands
You can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.
Command | Usage |
---|---|
@sonatype-lift ignore |
Leave out the above finding from this PR |
@sonatype-lift ignoreall |
Leave out all the existing findings from this PR |
@sonatype-lift exclude <file|issue|path|tool> |
Exclude specified file|issue|path|tool from Lift findings by updating your config.toml file |
Note: When talking to LiftBot, you need to refresh the page to see its response.
Click here to add LiftBot to another repo.
Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a false positive.
53cdb3a
to
d12ff16
Compare
Rebased on master and updated the date in |
Rather than calling the SSL_CTX_* API to manipulate the x509 store, pass the X509_STORE, and call the X509_STORE_* API directly.
d12ff16
to
6693d57
Compare
@bagder Rebased on master to fix conflict. I dropped the commit with with the unrelated changes to |
Making HTTPS connections was slow because libcurl forces openssl to rebuild the same X509_STORE from the same cabundle every time a connection is made. This is a slow process and it can happen many times per page. These patches change libcurl to keep the built X509_STORE cached and reuse it for subsequent connections. These patches are being upstreamed here: curl/curl#9620
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks for your work on this. I found but a single little nit.
lib/vtls/openssl.c
Outdated
#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) | ||
#define HAVE_THREADS | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This chunk can be removed again too right? HAVE_THREADS
does not seem to be used in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good spot! I'll fix in a moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@badger Done!
Loading a certificate store from CAfile is a processor intensive operation. Here we cache the x509 store when loaded from CAfile, and reuse it if we are set up to load certificates from the same CAfile.
Adds a new option to control the maximum time that a cached certificate store may be retained for. Currently only the OpenSSL backend implements support for caching certificate stores.
6693d57
to
4d1cc92
Compare
Thanks! |
Adds a new option to control the maximum time that a cached certificate store may be retained for. Currently only the OpenSSL backend implements support for caching certificate stores. Closes #9620
This implements TODO 13.12:
With this change, the first X509_STORE to be loaded exclusively via a
CAfile
is cached. Any subsequent SSL contexts that are created will reuse the same X509_STORE, provided they would also be loaded exclusively via the sameCAfile
.