From 3460bf44bc4d5f0d82066d8bcf7a1c963a05d69b Mon Sep 17 00:00:00 2001 From: Alexandre Besnard Date: Wed, 11 Mar 2026 09:42:18 +0100 Subject: [PATCH] openssl: start using OpenSSL3 providers for certificate and key OpenSSL v3 introduced store providers as the go-to way to retrieve certificate(s) and keys from any source. The main point of this change is, abstracting the storage-specific code for data retrieval in an OpenSSL provider, which is not known from the application itself (instead of OpenSSL engines which require per engine specific code). This change leverages this feature, using it for all key and certificates loading if OpenSSL version is compatible. Default (file) storage and custom (HSM, ...) key storage should work transparently, assuming OpenSSL configuration loads the needed providers. The implementation itself is based on OpenSSL applications source code as reference and should be updated upon changes. Signed-off-by: Alexandre Besnard --- src/mod_openssl.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 5 deletions(-) diff --git a/src/mod_openssl.c b/src/mod_openssl.c index 24ad84cda..9da2a6453 100644 --- a/src/mod_openssl.c +++ b/src/mod_openssl.c @@ -70,6 +70,10 @@ #ifndef OPENSSL_NO_OCSP #include #endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#endif #ifdef AWSLC_API_VERSION /* alt: OPENSSL_IS_AWSLC */ /* AWS-LC derived from BoringSSL, but AWSLC_API_VERSION has different meaning. * Reuse BORINGSSL_API_VERSION for (presently) small num of API version checks*/ @@ -2355,7 +2359,7 @@ mod_openssl_cert_is_active (const X509 *crt) #endif } - +#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(TLSEXT_TYPE_application_layer_protocol_negotiation) static X509 * mod_openssl_load_pem_file (const char *file, log_error_st *errh, STACK_OF(X509) **chain) { @@ -2424,6 +2428,8 @@ mod_openssl_evp_pkey_load_pem_file (const char *file, log_error_st *errh) return x; } +#endif //OPENSSL_VERSION_NUMBER < 0x30000000L || defined(TLSEXT_TYPE_application_layer_protocol_negotiation) + #if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER) @@ -2858,6 +2864,113 @@ mod_openssl_crt_must_staple (const X509 *crt) #endif /* OPENSSL_NO_OCSP */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +/* + Load values from an openssl store. + + Passed pointers will be allocated and filled if matches are found + when looking up openssl stores. For instance, if the URI is a path to + a pem file with both certificate and private key, both values can be + filled and returned. + + Note that, the returned status only shows if something went wrong during + the lookup, and will report OK when the lookup is a miss. + */ +static int mod_openssl_load_store_data(const char *uri, + EVP_PKEY **ppkey, EVP_PKEY **ppubkey, + X509 **pcert, STACK_OF(X509) **pcert_stack, + log_error_st *errh) { + + OSSL_STORE_CTX *store_ctx = NULL; + OSSL_STORE_INFO *store_info = NULL; + + if (uri == NULL) + return 1; + + /* Allocate the certificates stack if we are expecting one */ + if (pcert_stack != NULL && *pcert_stack == NULL) + *pcert_stack = sk_X509_new_null(); + + /* Open the store using provided URI */ + store_ctx = OSSL_STORE_open(uri, UI_get_default_method(), NULL, NULL, NULL); + if (store_ctx == NULL) { + log_error(errh, __FILE__, __LINE__, + "SSL: failed to open OpenSSL store with URI %s", uri); + goto failed; + } + + while (!OSSL_STORE_eof(store_ctx)) { + int info_type; + store_info = OSSL_STORE_load(store_ctx); + + /* Not really a problem here, check out openssl comments */ + if (store_info == NULL) + continue; + + info_type = OSSL_STORE_INFO_get_type(store_info); + switch (info_type) { + case OSSL_STORE_INFO_PKEY: + if (ppkey != NULL && *ppkey == NULL) { + *ppkey = OSSL_STORE_INFO_get1_PKEY(store_info); + if (*ppkey == NULL) { + log_error(errh, __FILE__, __LINE__, + "SSL: error while loading privkey with URI %s", uri); + goto failed; + } + } + /* Keep going since a private key also holds public info that may be wanted */ + /* fallthrough */ + case OSSL_STORE_INFO_PUBKEY: + if (ppubkey != NULL && *ppubkey == NULL) { + *ppubkey = OSSL_STORE_INFO_get1_PUBKEY(store_info); + if (*ppubkey == NULL) { + log_error(errh, __FILE__, __LINE__, + "SSL: error while loading pubkey with URI %s", uri); + goto failed; + } + } + break; + case OSSL_STORE_INFO_CERT: + /* When hitting a cert, only report the first one: it should be the right one with a cert chain */ + if (pcert != NULL && *pcert == NULL) { + *pcert = OSSL_STORE_INFO_get1_CERT(store_info); + if (*pcert == NULL) { + log_error(errh, __FILE__, __LINE__, + "SSL: error while loading certificate with URI %s", uri); + goto failed; + } + } + /* If we are expecting a cert stack, fill it with all certs found */ + if (pcert_stack != NULL) { + if (!X509_add_cert(*pcert_stack, + OSSL_STORE_INFO_get1_CERT(store_info), X509_ADD_FLAG_DEFAULT)) { + log_error(errh, __FILE__, __LINE__, + "SSL: error while loading certificate into cert chain with URI %s", uri); + goto failed; + } + } + break; + } + OSSL_STORE_INFO_free(store_info); + } + + OSSL_STORE_close(store_ctx); + return 0; + +failed: + OSSL_STORE_INFO_free(store_info); + OSSL_STORE_close(store_ctx); + if (ppkey != NULL) + EVP_PKEY_free(*ppkey); + if (ppubkey != NULL) + EVP_PKEY_free(*ppubkey); + if (pcert != NULL) + X509_free(*pcert); + if (pcert_stack != NULL) + sk_X509_pop_free(*pcert_stack, X509_free); + return 1; +} +#endif __attribute_noinline__ static plugin_cert * @@ -2866,13 +2979,27 @@ network_openssl_load_pemfile (server *srv, const buffer *pemfile, const buffer * if (!mod_openssl_init_once_openssl(srv)) return NULL; STACK_OF(X509) *ssl_pemfile_chain = NULL; - X509 *ssl_pemfile_x509 = - mod_openssl_load_pem_file(pemfile->ptr, srv->errh, &ssl_pemfile_chain); + X509 *ssl_pemfile_x509 = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (0 == mod_openssl_load_store_data(pemfile->ptr, NULL, NULL, &ssl_pemfile_x509, &ssl_pemfile_chain, srv->errh) + && NULL == ssl_pemfile_x509) + log_error(srv->errh, __FILE__, __LINE__, + "SSL: no certificate found using URI '%s'", pemfile->ptr); +#else + ssl_pemfile_x509 = mod_openssl_load_pem_file(pemfile->ptr, srv->errh, &ssl_pemfile_chain); +#endif if (NULL == ssl_pemfile_x509) return NULL; - EVP_PKEY *ssl_pemfile_pkey = - mod_openssl_evp_pkey_load_pem_file(privkey->ptr, srv->errh); + EVP_PKEY *ssl_pemfile_pkey = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (0 == mod_openssl_load_store_data(privkey->ptr, &ssl_pemfile_pkey, NULL, NULL, NULL, srv->errh) + && NULL == ssl_pemfile_pkey) + log_error(srv->errh, __FILE__, __LINE__, + "SSL: no privkey found using URI '%s'", privkey->ptr); +#else + ssl_pemfile_pkey = mod_openssl_evp_pkey_load_pem_file(privkey->ptr, srv->errh); +#endif if (NULL == ssl_pemfile_pkey) { X509_free(ssl_pemfile_x509); sk_X509_pop_free(ssl_pemfile_chain, X509_free);