From: Joe Orton Date: Thu, 11 Jul 2024 07:28:53 +0000 (+0000) Subject: Merge r1914365, r1914622, r1916057, r1918024 from trunk: X-Git-Tag: 2.4.62-rc1-candidate~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f0b011bc41c4dbb21ddb86d63298c00cf57942b;p=thirdparty%2Fapache%2Fhttpd.git Merge r1914365, r1914622, r1916057, r1918024 from trunk: mod_ssl: Add support for loading keys from OpenSSL 3.x providers via the STORE API. Separates compile-time support for the STORE API (supported in 3.x) from support for the ENGINE API (deprecated in 3.x). * modules/ssl/ssl_private.h: Define MODSSL_HAVE_OPENSSL_STORE for OpenSSL 3.0+. * modules/ssl/ssl_engine_pphrase.c (modssl_load_store_uri, modssl_load_keypair_store): New functions. (modssl_load_keypair_engine): Renamed from modssl_load_keypair_engine. (modssl_load_engine_keypair): Reimplement to use new STORE-based functions if SSLCryptoDevice was not configured, or else old ENGINE implementation. * modules/ssl/ssl_util.c (modssl_is_engine_id): Match pkcs11: URIs also for the OpenSSL 3.x STORE API. * modules/ssl/ssl_engine_init.c (ssl_init_server_certs): Tweak log message on error paths for the provider/STORE case. Signed-off-by: Ingo Franzki Submitted by: Ingo Franzki * modules/ssl/ssl_engine_pphrase.c (modssl_load_engine_keypair): Fix build (hopefully) for OpenSSL 3.x with OPENSSL_NO_ENGINE defined. * modules/ssl/ssl_engine_pphrase.c (modssl_load_engine_keypair): Update to avoid GCC warning for no-engine builds where the SSLModConfigRec is not used. Also log an error for the ENOTIMPL path. Fix ENGINE use with OpenSSL 3.2, which appears to be broken due to a refcounting issue in mod_ssl. * modules/ssl/ssl_engine_pphrase.c (modssl_engine_cleanup): New function. (modssl_load_keypair_engine): Take pconf & ptemp arguments, don't call ENGINE_finish() immediately but register the above cleanup. (modssl_load_engine_keypair): Pass through pconf & ptemp. * modules/ssl/ssl_engine_init.c (ssl_init_server_certs): Pass through pconf and ptemp to modssl_load_engine_keypair. Submitted by: jorton Reviewed by: jorton, ylavic, rpluem Github: closes #455 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1919123 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/changes-entries/modssl-pkcs11.txt b/changes-entries/modssl-pkcs11.txt new file mode 100644 index 00000000000..391f8a27d6b --- /dev/null +++ b/changes-entries/modssl-pkcs11.txt @@ -0,0 +1,2 @@ + *) mod_ssl: Fix crashes in PKCS#11 ENGINE support with OpenSSL 3.2. + [Joe Orton] diff --git a/changes-entries/ssl-providers.txt b/changes-entries/ssl-providers.txt new file mode 100644 index 00000000000..65b5655afa9 --- /dev/null +++ b/changes-entries/ssl-providers.txt @@ -0,0 +1,2 @@ + *) mod_ssl: Add support for loading certs/keys from pkcs11: URIs + via OpenSSL 3.x providers. [Ingo Franzki ] diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index 7660c1e6d16..f953da00c2a 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -952,7 +952,7 @@ files, a certificate identifier can be used to identify a certificate stored in a token. Currently, only PKCS#11 URIs are recognized as certificate identifiers, and can be used in conjunction -with the OpenSSL pkcs11 engine. If pkcs11 engine or provider. If SSLCertificateKeyFile is omitted, the certificate and private key can be loaded through the single identifier specified with identifier can be used to identify a private key stored in a token. Currently, only PKCS#11 URIs are recognized as private key identifiers, and can be used in conjunction with the OpenSSL -pkcs11 engine.

+pkcs11 engine or provider.

Example @@ -2422,6 +2422,15 @@ separate "-engine" releases of OpenSSL 0.9.6 must be used.

SSLCryptoDevice ubsec
+ +

+With OpenSSL 3.0 or later, if no engine is specified but the key or certificate +is specified using a PKCS#11 URIs +then it is tried to load the key and certificate from an OpenSSL provider. +The OpenSSL provider to use must be defined and configured in the OpenSSL config file, +and it must support the STORE method +for PKCS#11 URIs. +

diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index 443eac4513c..598e89fc0fb 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1424,7 +1424,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s, if (modssl_is_engine_id(keyfile)) { apr_status_t rv; - if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id, + if ((rv = modssl_load_engine_keypair(s, p, ptemp, vhost_id, engine_certfile, keyfile, &cert, &pkey))) { return rv; @@ -1433,8 +1433,10 @@ static apr_status_t ssl_init_server_certs(server_rec *s, if (cert) { if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137) - "Failed to configure engine certificate %s, check %s", - key_id, certfile); + "Failed to configure certificate %s from %s, check %s", + key_id, mc->szCryptoDevice ? + mc->szCryptoDevice : "provider", + certfile); ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); return APR_EGENERAL; } @@ -1445,8 +1447,9 @@ static apr_status_t ssl_init_server_certs(server_rec *s, if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130) - "Failed to configure private key %s from engine", - keyfile); + "Failed to configure private key %s from %s", + keyfile, mc->szCryptoDevice ? + mc->szCryptoDevice : "provider"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); return APR_EGENERAL; } diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c index 699019fca17..8a08ede67af 100644 --- a/modules/ssl/ssl_engine_pphrase.c +++ b/modules/ssl/ssl_engine_pphrase.c @@ -31,6 +31,9 @@ #include "ssl_private.h" #include +#if MODSSL_HAVE_OPENSSL_STORE +#include +#endif typedef struct { server_rec *s; @@ -608,7 +611,7 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv) return (len); } -#if MODSSL_HAVE_ENGINE_API +#if MODSSL_HAVE_ENGINE_API || MODSSL_HAVE_OPENSSL_STORE /* OpenSSL UI implementation for passphrase entry; largely duplicated * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be @@ -826,21 +829,32 @@ static UI_METHOD *get_passphrase_ui(apr_pool_t *p) } #endif +#if MODSSL_HAVE_ENGINE_API +static apr_status_t modssl_engine_cleanup(void *engine) +{ + ENGINE *e = engine; -apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, - const char *vhostid, - const char *certid, const char *keyid, - X509 **pubkey, EVP_PKEY **privkey) + ENGINE_finish(e); + + return APR_SUCCESS; +} + +static apr_status_t modssl_load_keypair_engine(server_rec *s, apr_pool_t *pconf, + apr_pool_t *ptemp, + const char *vhostid, + const char *certid, + const char *keyid, + X509 **pubkey, + EVP_PKEY **privkey) { -#if MODSSL_HAVE_ENGINE_API const char *c, *scheme; ENGINE *e; - UI_METHOD *ui_method = get_passphrase_ui(p); + UI_METHOD *ui_method = get_passphrase_ui(ptemp); pphrase_cb_arg_t ppcb; memset(&ppcb, 0, sizeof ppcb); ppcb.s = s; - ppcb.p = p; + ppcb.p = ptemp; ppcb.bPassPhraseDialogOnce = TRUE; ppcb.key_id = vhostid; ppcb.pkey_file = keyid; @@ -853,7 +867,7 @@ apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, return ssl_die(s); } - scheme = apr_pstrmemdup(p, keyid, c - keyid); + scheme = apr_pstrmemdup(ptemp, keyid, c - keyid); if (!(e = ENGINE_by_id(scheme))) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132) "Init: Failed to load engine for private key %s", @@ -902,11 +916,136 @@ apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, return ssl_die(s); } - ENGINE_finish(e); + /* Release the functional reference obtained by ENGINE_init() only + * when after the ENGINE is no longer used. */ + apr_pool_cleanup_register(pconf, e, modssl_engine_cleanup, modssl_engine_cleanup); + + /* Release the structural reference obtained by ENGINE_by_id() + * immediately. */ ENGINE_free(e); return APR_SUCCESS; +} +#endif + +#if MODSSL_HAVE_OPENSSL_STORE +static OSSL_STORE_INFO *modssl_load_store_uri(server_rec *s, apr_pool_t *p, + const char *vhostid, + const char *uri, int info_type) +{ + OSSL_STORE_CTX *sctx; + UI_METHOD *ui_method = get_passphrase_ui(p); + pphrase_cb_arg_t ppcb; + OSSL_STORE_INFO *info = NULL; + + memset(&ppcb, 0, sizeof ppcb); + ppcb.s = s; + ppcb.p = p; + ppcb.bPassPhraseDialogOnce = TRUE; + ppcb.key_id = vhostid; + ppcb.pkey_file = uri; + + sctx = OSSL_STORE_open(uri, ui_method, &ppcb, NULL, NULL); + if (!sctx) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10491) + "Init: OSSL_STORE_open failed for PKCS#11 URI `%s'", + uri); + return NULL; + } + + while (!OSSL_STORE_eof(sctx)) { + info = OSSL_STORE_load(sctx); + if (!info) + break; + + if (OSSL_STORE_INFO_get_type(info) == info_type) + break; + + OSSL_STORE_INFO_free(info); + info = NULL; + } + + OSSL_STORE_close(sctx); + + return info; +} + +static apr_status_t modssl_load_keypair_store(server_rec *s, apr_pool_t *p, + const char *vhostid, + const char *certid, + const char *keyid, + X509 **pubkey, + EVP_PKEY **privkey) +{ + OSSL_STORE_INFO *info = NULL; + + *privkey = NULL; + *pubkey = NULL; + + info = modssl_load_store_uri(s, p, vhostid, keyid, OSSL_STORE_INFO_PKEY); + if (!info) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10492) + "Init: OSSL_STORE_INFO_PKEY lookup failed for private key identifier `%s'", + keyid); + return ssl_die(s); + } + + *privkey = OSSL_STORE_INFO_get1_PKEY(info); + OSSL_STORE_INFO_free(info); + if (!*privkey) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10493) + "Init: OSSL_STORE_INFO_PKEY lookup failed for private key identifier `%s'", + keyid); + return ssl_die(s); + } + + if (certid) { + info = modssl_load_store_uri(s, p, vhostid, certid, OSSL_STORE_INFO_CERT); + if (!info) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10494) + "Init: OSSL_STORE_INFO_CERT lookup failed for certificate identifier `%s'", + keyid); + return ssl_die(s); + } + + *pubkey = OSSL_STORE_INFO_get1_CERT(info); + OSSL_STORE_INFO_free(info); + if (!*pubkey) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10495) + "Init: OSSL_STORE_INFO_CERT lookup failed for certificate identifier `%s'", + certid); + return ssl_die(s); + } + } + + return APR_SUCCESS; +} +#endif + +apr_status_t modssl_load_engine_keypair(server_rec *s, + apr_pool_t *pconf, apr_pool_t *ptemp, + const char *vhostid, + const char *certid, const char *keyid, + X509 **pubkey, EVP_PKEY **privkey) +{ +#if MODSSL_HAVE_ENGINE_API + SSLModConfigRec *mc = myModConfig(s); + + /* For OpenSSL 3.x, use the STORE-based API if either ENGINE + * support was not present compile-time, or if it's built but + * SSLCryptoDevice is not configured. */ + if (mc->szCryptoDevice) + return modssl_load_keypair_engine(s, pconf, ptemp, + vhostid, certid, keyid, + pubkey, privkey); +#endif +#if MODSSL_HAVE_OPENSSL_STORE + return modssl_load_keypair_store(s, ptemp, vhostid, certid, keyid, + pubkey, privkey); #else + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10496) + "Init: no method for loading keypair for %s (%s | %s)", + vhostid, certid ? certid : "no cert", keyid); return APR_ENOTIMPL; #endif } diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index fef2525e429..c517a7bdc01 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -118,6 +118,15 @@ #define MODSSL_HAVE_ENGINE_API 0 #endif +/* Use OpenSSL 3.x STORE for loading URI keys and certificates starting with + * OpenSSL 3.0 + */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000 +#define MODSSL_HAVE_OPENSSL_STORE 1 +#else +#define MODSSL_HAVE_OPENSSL_STORE 0 +#endif + #if (OPENSSL_VERSION_NUMBER < 0x0090801f) #error mod_ssl requires OpenSSL 0.9.8a or later #endif @@ -1081,7 +1090,8 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int, /* Load public and/or private key from the configured ENGINE. Private * key returned as *pkey. certid can be NULL, in which case *pubkey * is not altered. Errors logged on failure. */ -apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, +apr_status_t modssl_load_engine_keypair(server_rec *s, + apr_pool_t *pconf, apr_pool_t *ptemp, const char *vhostid, const char *certid, const char *keyid, X509 **pubkey, EVP_PKEY **privkey); diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c index 87ddfa7715a..7473edbe6c0 100644 --- a/modules/ssl/ssl_util.c +++ b/modules/ssl/ssl_util.c @@ -476,7 +476,7 @@ void ssl_util_thread_id_setup(apr_pool_t *p) int modssl_is_engine_id(const char *name) { -#if MODSSL_HAVE_ENGINE_API +#if MODSSL_HAVE_ENGINE_API || MODSSL_HAVE_OPENSSL_STORE /* ### Can handle any other special ENGINE key names here? */ return strncmp(name, "pkcs11:", 7) == 0; #else