From a27665b872ef1ec5d4ec8fe75933cf2a9215b33c Mon Sep 17 00:00:00 2001 From: Alexandre Besnard Date: Mon, 23 Mar 2026 11:10:36 +0100 Subject: [PATCH 1/2] openssl: change default file type to provider Openssl 3+ encourages using providers for all data storages. This commit sets it as the default file type when supported, when the file type is not explicitly defined (instead of PEM by default). This change also removes the explicit check on a provider being loaded, before trying to open the matching store: openssl should raise an explicit error when opening it, and an explicit check in libcurl breaks the possibility of a default provider in Openssl. This change should not have any impact on behavior, since openssl default implementation embeds a provider for PEM file types. This change full leverages the Openssl store providers: with this, the end user does not need to maintain the actual storage type in calls to libcurl, and can freely ignore storage types by changing the URI (or file path). --- lib/vtls/openssl.c | 204 ++++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 105 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 3af360b731..fc71a630d7 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -861,7 +861,11 @@ static CURLcode ossl_seed(struct Curl_easy *data) static int ossl_do_file_type(const char *type) { if(!type || !type[0]) +#ifdef OPENSSL_HAS_PROVIDERS + return SSL_FILETYPE_PROVIDER; +#else return SSL_FILETYPE_PEM; +#endif if(curl_strequal(type, "PEM")) return SSL_FILETYPE_PEM; if(curl_strequal(type, "DER")) @@ -1113,6 +1117,10 @@ static int providercheck(struct Curl_easy *data, const char *key_file) { #ifdef OPENSSL_HAS_PROVIDERS + EVP_PKEY *priv_key = NULL; + OSSL_STORE_CTX *store = NULL; + OSSL_STORE_INFO *info = NULL; + UI_METHOD *ui_method = NULL; char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * key_file is a PKCS#11 URI */ @@ -1124,67 +1132,58 @@ static int providercheck(struct Curl_easy *data, } } - if(data->state.provider_loaded) { - /* Load the private key from the provider */ - EVP_PKEY *priv_key = NULL; - OSSL_STORE_CTX *store = NULL; - OSSL_STORE_INFO *info = NULL; - UI_METHOD *ui_method = UI_create_method("curl user interface"); - if(!ui_method) { - failf(data, "unable to create " OSSL_PACKAGE " user-interface method"); - return 0; - } - UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); - UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); - UI_method_set_reader(ui_method, ssl_ui_reader); - UI_method_set_writer(ui_method, ssl_ui_writer); - - store = OSSL_STORE_open_ex(key_file, data->state.libctx, - data->state.propq, ui_method, NULL, NULL, - NULL, NULL); - if(!store) { - failf(data, "Failed to open OpenSSL store: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - UI_destroy_method(ui_method); - return 0; - } - if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { - failf(data, "Failed to set store preference. Ignoring the error: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - } - - info = OSSL_STORE_load(store); - if(info) { - int ossl_type = OSSL_STORE_INFO_get_type(info); - - if(ossl_type == OSSL_STORE_INFO_PKEY) - priv_key = OSSL_STORE_INFO_get1_PKEY(info); - OSSL_STORE_INFO_free(info); - } - OSSL_STORE_close(store); - UI_destroy_method(ui_method); - if(!priv_key) { - failf(data, "No private key found in the openssl store: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } - - if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { - failf(data, "unable to set private key [%s]", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - EVP_PKEY_free(priv_key); - return 0; - } - EVP_PKEY_free(priv_key); /* we do not need the handle any more... */ - } - else { - failf(data, "crypto provider not set, cannot load private key"); + /* Load the private key from the provider */ + ui_method = UI_create_method("curl user interface"); + if(!ui_method) { + failf(data, "unable to create " OSSL_PACKAGE " user-interface method"); return 0; } + UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); + UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); + UI_method_set_reader(ui_method, ssl_ui_reader); + UI_method_set_writer(ui_method, ssl_ui_writer); + + store = OSSL_STORE_open_ex(key_file, data->state.libctx, + data->state.propq, ui_method, NULL, NULL, + NULL, NULL); + if(!store) { + failf(data, "Failed to open OpenSSL store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + UI_destroy_method(ui_method); + return 0; + } + if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { + failf(data, "Failed to set store preference. Ignoring the error: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + } + + info = OSSL_STORE_load(store); + if(info) { + int ossl_type = OSSL_STORE_INFO_get_type(info); + + if(ossl_type == OSSL_STORE_INFO_PKEY) + priv_key = OSSL_STORE_INFO_get1_PKEY(info); + OSSL_STORE_INFO_free(info); + } + OSSL_STORE_close(store); + UI_destroy_method(ui_method); + if(!priv_key) { + failf(data, "No private key found in the openssl store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + + if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { + failf(data, "unable to set private key [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + EVP_PKEY_free(priv_key); + return 0; + } + EVP_PKEY_free(priv_key); /* we do not need the handle any more... */ return 1; #else (void)ctx; @@ -1269,6 +1268,10 @@ static int providerload(struct Curl_easy *data, const char *cert_file) { #ifdef OPENSSL_HAS_PROVIDERS + OSSL_STORE_INFO *info = NULL; + X509 *cert = NULL; + OSSL_STORE_CTX *store = NULL; + int rc; char error_buffer[256]; /* Implicitly use pkcs11 provider if none was provided and the * cert_file is a PKCS#11 URI */ @@ -1280,55 +1283,46 @@ static int providerload(struct Curl_easy *data, } } - if(data->state.provider_loaded) { - /* Load the certificate from the provider */ - OSSL_STORE_INFO *info = NULL; - X509 *cert = NULL; - OSSL_STORE_CTX *store = - OSSL_STORE_open_ex(cert_file, data->state.libctx, - NULL, NULL, NULL, NULL, NULL, NULL); - int rc; + /* Load the certificate from the provider */ - if(!store) { - failf(data, "Failed to open OpenSSL store: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } - if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) { - failf(data, "Failed to set store preference. Ignoring the error: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - } + store = OSSL_STORE_open_ex(cert_file, data->state.libctx, + NULL, NULL, NULL, NULL, NULL, NULL); - info = OSSL_STORE_load(store); - if(info) { - int ossl_type = OSSL_STORE_INFO_get_type(info); - - if(ossl_type == OSSL_STORE_INFO_CERT) - cert = OSSL_STORE_INFO_get1_CERT(info); - OSSL_STORE_INFO_free(info); - } - OSSL_STORE_close(store); - if(!cert) { - failf(data, "No cert found in the openssl store: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } - - rc = SSL_CTX_use_certificate(ctx, cert); - X509_free(cert); /* we do not need the handle any more... */ - - if(rc != 1) { - failf(data, "unable to set client certificate [%s]", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } + if(!store) { + failf(data, "Failed to open OpenSSL store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; } - else { - failf(data, "crypto provider not set, cannot load certificate"); + if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) { + failf(data, "Failed to set store preference. Ignoring the error: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + } + + info = OSSL_STORE_load(store); + if(info) { + int ossl_type = OSSL_STORE_INFO_get_type(info); + + if(ossl_type == OSSL_STORE_INFO_CERT) + cert = OSSL_STORE_INFO_get1_CERT(info); + OSSL_STORE_INFO_free(info); + } + OSSL_STORE_close(store); + if(!cert) { + failf(data, "No cert found in the openssl store: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + + rc = SSL_CTX_use_certificate(ctx, cert); + X509_free(cert); /* we do not need the handle any more... */ + + if(rc != 1) { + failf(data, "unable to set client certificate [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); return 0; } return 1; From 31e2cc3d020ccb106a8d7311bc019602f96076ec Mon Sep 17 00:00:00 2001 From: Alexandre Besnard Date: Tue, 24 Mar 2026 14:22:55 +0100 Subject: [PATCH 2/2] openssl: extend store provider code to load certificate chains Openssl store provider mechanics allow loading several items with a single name. This change leverages the API to allow loading certificates chains with store providers: a single name (for instance, a .pem file) can contain a chain of certificates, which will be loaded in the SSL context. --- lib/vtls/openssl.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index fc71a630d7..3491e44f65 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1268,8 +1268,8 @@ static int providerload(struct Curl_easy *data, const char *cert_file) { #ifdef OPENSSL_HAS_PROVIDERS - OSSL_STORE_INFO *info = NULL; X509 *cert = NULL; + STACK_OF(X509) *cert_chain = NULL; OSSL_STORE_CTX *store = NULL; int rc; char error_buffer[256]; @@ -1300,14 +1300,31 @@ static int providerload(struct Curl_easy *data, sizeof(error_buffer))); } - info = OSSL_STORE_load(store); - if(info) { - int ossl_type = OSSL_STORE_INFO_get_type(info); + while(!OSSL_STORE_eof(store)) { + OSSL_STORE_INFO *info = OSSL_STORE_load(store); + /* Not really an error, keep looping */ + if(!info) { + continue; + } + + if(OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_CERT) { + /* Only load the first cert hit: when using a cert chain, + first one should be the right one.*/ + if(!cert) { + cert = OSSL_STORE_INFO_get1_CERT(info); + } + + /* Load all certs found in the chain. */ + if(!cert_chain) { + cert_chain = sk_X509_new_null(); + } + X509_add_cert(cert_chain, OSSL_STORE_INFO_get1_CERT(info), + X509_ADD_FLAG_DEFAULT); + } - if(ossl_type == OSSL_STORE_INFO_CERT) - cert = OSSL_STORE_INFO_get1_CERT(info); OSSL_STORE_INFO_free(info); } + OSSL_STORE_close(store); if(!cert) { failf(data, "No cert found in the openssl store: %s", @@ -1323,6 +1340,17 @@ static int providerload(struct Curl_easy *data, failf(data, "unable to set client certificate [%s]", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); + sk_X509_pop_free(cert_chain, X509_free); + return 0; + } + + rc = (int) SSL_CTX_set1_chain(ctx, cert_chain); + sk_X509_pop_free(cert_chain, X509_free); + + if(rc != 1) { + failf(data, "unable to set client certificate chain [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); return 0; } return 1;