svn commit: r1936849 - in httpd/httpd/trunk: . docs/log-message-tags docs/manual/mod modules/ssl
[email protected] Mon, 03 Aug 2026 22:48:58 -0000
| Newsgroups | gmane.comp.apache.cvs |
|---|---|
| Message-ID | <178579733829.1996249.15325483925075562129@svn03-he-fi> |
Author: minfrin
Date: Mon Aug 3 22:48:57 2026
New Revision: 1936849
Log:
mod_ssl: Add support for OpenSSL provider based certificate
stores.
Modified:
httpd/httpd/trunk/CHANGES
httpd/httpd/trunk/docs/log-message-tags/next-number
httpd/httpd/trunk/docs/manual/mod/mod_ssl.xml
httpd/httpd/trunk/modules/ssl/mod_ssl.c
httpd/httpd/trunk/modules/ssl/mod_ssl_openssl.h
httpd/httpd/trunk/modules/ssl/ssl_engine_config.c
httpd/httpd/trunk/modules/ssl/ssl_engine_init.c
httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c
httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c
httpd/httpd/trunk/modules/ssl/ssl_private.h
Modified: httpd/httpd/trunk/CHANGES
==============================================================================
--- httpd/httpd/trunk/CHANGES Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/CHANGES Mon Aug 3 22:48:57 2026 (r1936849)
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.1
+ * mod_ssl: Add support for OpenSSL provider based certificate
+ stores. [Graham Leggett]
+
*) mod_proxy_beacon: Back-end reverse proxy servers can announce
themselves and be auto-added to their front-end proxy balancer.
[Jim Jagielski]
Modified: httpd/httpd/trunk/docs/log-message-tags/next-number
==============================================================================
--- httpd/httpd/trunk/docs/log-message-tags/next-number Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/docs/log-message-tags/next-number Mon Aug 3 22:48:57 2026 (r1936849)
@@ -1 +1 @@
-10600
+10618
Modified: httpd/httpd/trunk/docs/manual/mod/mod_ssl.xml
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_ssl.xml Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/docs/manual/mod/mod_ssl.xml Mon Aug 3 22:48:57 2026 (r1936849)
@@ -1168,6 +1168,89 @@ effect.</p>
</directivesynopsis>
<directivesynopsis>
+<name>SSLCertificateURI</name>
+<description>Server certificate and key store</description>
+<syntax>SSLCertificateURI <var>uri</var></syntax>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+<compatibility>Available in httpd 2.5.1 and later, when linked with
+OpenSSL v3 or later.</compatibility>
+
+<usage>
+<p>
+This directive points to a certificate store containing certificates,
+intermediate certificates, and private keys, represented by a URI.
+</p>
+<p>
+If no scheme is specified, the path will default to a <var>file:</var>
+URI, pointing at PEM encoded data, or a PKCS12 file. Other schemes
+include, but are not limited to, <var>pkcs11:</var> for smartcards and
+HSMs, <var>cng:</var> for the Windows certificate store, and
+<var>handle:</var> for TPMs. On Windows, where a file path is also a
+valid URI, the <var>file:</var> scheme must be used.
+</p>
+<p>
+The directive can be specified multiple times with tightly scoped
+URIs to target specific certificates and keys, or could be specified
+with a general URI like <var>pkcs11:</var> that considers all possible
+certificates and keys. Certificates, intermediate certificates, and keys
+can be defined in any order.
+</p>
+<p>Certificates and keys are processed as follows.
+</p>
+<ul>
+<li>Leaf certificates that do not have the purpose <var>Server Authentication</var>
+are skipped.</li>
+<li>Remaining leaf certificates are checked whether the
+<directive module="core">ServerName</directive> and all
+<directive module="core">ServerAlias</directive> directives match the
+hostname or IP address of the certificate, and if no match is found they
+are skipped.</li>
+<li>Intermediate certificates are considered for building certificate
+chains on a best effort basis.</li>
+<li>Keys are matched up with leaf certificates, any certificate
+without a private key is skipped.</li>
+<li>Leaf certificates with private keys are sorted oldest to newest and
+passed on for configuration.</li>
+<li>The most recently issued certificate and key pair for each algorithm
+type (RSA, ECDSA, etc) will be used for each virtual host.</li>
+<li>The server will report back to you how many certificates of each type
+were found to help you if no certificates match.</li>
+</ul>
+
+<p>If the private key is encrypted, the pass phrase dialog is forced
+at startup time.</p>
+
+<example><title>Example</title>
+<highlight language="config">
+# Example using a PEM-encoded file.
+SSLCertificateURI "/usr/local/apache2/conf/ssl.crt/server.crt"
+# Example using a PKCS12 file.
+SSLCertificateURI "/usr/local/apache2/conf/ssl.crt/server.p12"
+# Example use of a certificate and private key from a PKCS#11 token:
+SSLCertificateURI "pkcs11:token=My%20Token%20Name;id=45"
+</highlight>
+</example>
+
+<p>These URIs are read at server startup, while the server is still running
+as <code>root</code> (before privilege dropping), so it may be owned by
+and readable only by <code>root</code>. The URI is not re-read during
+normal operation; a server restart is required for changes to take
+effect.</p>
+
+<note type="warning"><title>Using SSLCertificateFile and SSLCertificateURI
+together</title>
+<p>
+You can use both SSLCertificateFile and SSLCertificateURI together, however
+there is no overlap between the mechanisms. A certificate defined by
+SSLCertificateFile will not be matched with a key from SSLCertificateURI.
+</p>
+</note>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>SSLCACertificatePath</name>
<description>Directory of PEM-encoded CA Certificates for
Client Auth</description>
@@ -1233,6 +1316,42 @@ effect.</p>
</directivesynopsis>
<directivesynopsis>
+<name>SSLCACertificateURI</name>
+<description>Server CA certificate store for Client Authentication</description>
+<syntax>SSLCACertificateURI <var>uri</var></syntax>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+<override>AuthConfig</override>
+<compatibility>Available in httpd 2.5.1 and later, when linked with
+OpenSSL v3 or later.</compatibility>
+
+<usage>
+<p>
+This directive sets the <em>all-in-one</em> URI where you can assemble the
+Certificates of Certification Authorities (CA) whose <em>clients</em> you deal
+with. These are used for Client Authentication. This can be used alternatively
+and/or additionally to <directive module="mod_ssl">SSLCACertificateFile</directive>
+or <directive module="mod_ssl">SSLCACertificatePath</directive>.</p>
+<example><title>Example</title>
+<highlight language="config">
+# trust certs in a PEM encoded certificate bundle
+SSLCACertificateURI "/usr/local/apache2/conf/ssl.crt/ca-bundle-client.crt"
+# trust all certs in a typical Linux machine
+SSLCACertificateURI "pkcs11:token=System%20Trust"
+# trust all certs in the Windows trust store
+SSLCACertificateURI "org.openssl.winstore:"
+</highlight>
+</example>
+
+<p>This URI is read at server startup, while the server is still running
+as <code>root</code> (before privilege dropping), so it may be owned by
+and readable only by <code>root</code>. The URI is not re-read during
+normal operation; a server restart is required for changes to take
+effect.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>SSLCADNRequestFile</name>
<description>File of concatenated PEM-encoded CA Certificates
for defining acceptable CA names</description>
@@ -1385,6 +1504,36 @@ effect.</p>
</directivesynopsis>
<directivesynopsis>
+<name>SSLCARevocationURI</name>
+<description>Server CA certificate revocation list store for Client Authentication</description>
+<syntax>SSLCARevocationURI <var>uri</var></syntax>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+
+<usage>
+<p>
+This directive sets the <em>all-in-one</em> file where you can
+assemble the Certificate Revocation Lists (CRL) of Certification
+Authorities (CA) whose <em>clients</em> you deal with. These are used
+for Client Authentication. This can be used alternatively and/or
+additionally to <directive
+module="mod_ssl">SSLCARevocationFile</directive> and <directive
+module="mod_ssl">SSLCARevocationPath</directive>.</p>
+<example><title>Example</title>
+<highlight language="config">
+SSLCARevocationURI "/usr/local/apache2/conf/ssl.crl/ca-bundle-client.crl"
+</highlight>
+</example>
+
+<p>This URI is read at server startup, while the server is still running
+as <code>root</code> (before privilege dropping), so it may be owned by
+and readable only by <code>root</code>. The URI is not re-read during
+normal operation; a server restart is required for changes to take
+effect.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>SSLCARevocationCheck</name>
<description>Enable CRL-based revocation checking</description>
<syntax>SSLCARevocationCheck chain|leaf|none [<var>flag</var>s ...]</syntax>
@@ -2163,6 +2312,102 @@ SSLProxyMachineCertificateChainFile "/us
</directivesynopsis>
<directivesynopsis>
+<name>SSLProxyMachineCertificateURI</name>
+<description>Proxy certificate and key stores</description>
+<syntax>SSLProxyMachineCertificateURI <var>uri</var></syntax>
+<contextlist><context>server config</context> <context>virtual host</context>
+<context>proxy section</context></contextlist>
+<compatibility>Available in httpd 2.5.1 and later, when linked with
+OpenSSL v3 or later.</compatibility>
+
+<usage>
+<p>
+This directive points to a certificate store containing certificates,
+intermediate certificates, and private keys, represented by a URI,
+to be used when authenticating to another proxy server.
+</p>
+<p>
+If no scheme is specified, the path will default to a <var>file:</var>
+URI, pointing at PEM encoded data, or a PKCS12 file. Other schemes
+include, but are not limited to, <var>pkcs11:</var> for smartcards and
+HSMs, <var>cng:</var> for the Windows certificate store, and
+<var>handle:</var> for TPMs. On Windows, where a file path is also a
+valid URI, the <var>file:</var> scheme must be used.
+</p>
+<p>
+The directive can be specified multiple times with tightly scoped
+URIs to target specific certificates and keys, or could be specified
+with a general URI like <var>pkcs11:</var> that considers all possible
+certificates and keys. Certificates, intermediate certificates, and keys
+can be defined in any order.
+</p>
+<p>Proxy certificates and keys are processed as follows.
+</p>
+<ul>
+<li>Leaf certificates that do not have the purpose <var>Client Authentication</var>
+are skipped.</li>
+<li>Intermediate certificates are considered for building certificate
+chains on a best effort basis.</li>
+<li>Keys are matched up with leaf certificates, any certificate
+without a private key is skipped.</li>
+<li>Leaf certificates with private keys are sorted newest to oldest and
+are considered during each SSL handshake with a proxy.</li>
+<li>The proxy will report back to you how many certificates of each type
+were found to help you if no certificates match.</li>
+</ul>
+
+<p>If the private key is encrypted, the pass phrase dialog is forced
+at startup time.</p>
+
+<example><title>Example</title>
+<highlight language="config">
+# Example using a PEM-encoded file.
+SSLProxyMachineCertificateURI "/usr/local/apache2/conf/ssl.crt/proxy.pem"
+# Example using a PKCS12 file.
+SSLProxyMachineCertificateURI "/usr/local/apache2/conf/ssl.crt/proxy.p12"
+# Example use of a certificate and private key from a PKCS#11 token:
+SSLProxyMachineCertificateURI "pkcs11:token=My%20Token%20Name;id=45"
+</highlight>
+</example>
+
+<p>These URIs are read at server startup, while the server is still running
+as <code>root</code> (before privilege dropping), so it may be owned by
+and readable only by <code>root</code>. The URI is not re-read during
+normal operation; a server restart is required for changes to take
+effect.</p>
+
+<p>When challenged to provide a client certificate by a remote server,
+the server should provide a list of <em>acceptable certificate
+authority names</em> in the challenge. If such a list is <em>not</em>
+provided, <module>mod_ssl</module> will use the most recently
+issued client certificate and key. If a list of CA names
+<em>is</em> provided, <module>mod_ssl</module> will iterate through
+that list, and attempt to find a configured client certificate which
+was issued either directly by that CA, or indirectly via any number of
+intermediate CA certificates.
+</p>
+
+<p>If the list of CA names <em>is</em> provided by the remote server,
+and <em>no</em> matching client certificate can be found, no client
+certificate will be provided by <module>mod_ssl</module>, which will
+likely fail the SSL/TLS handshake (depending on the remote server
+configuration).</p>
+
+<note type="warning"><title>Using SSLProxyMachineCertificateFile and
+SSLProxyMachineCertificateURI together</title>
+<p>
+You can use both SSLProxyMachineCertificateFile and
+SSLProxyMachineCertificateURI together, however there is
+no overlap between the mechanisms. A certificate defined by
+SSLProxyMachineCertificateFile will not be matched with a
+key from SSLProxyMachineCertificateURI.
+</p>
+</note>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>SSLProxyVerify</name>
<description>Type of remote server Certificate verification</description>
<syntax>SSLProxyVerify <var>level</var></syntax>
@@ -2470,6 +2715,31 @@ SSLProxyCACertificateFile "/usr/local/ap
</directivesynopsis>
<directivesynopsis>
+<name>SSLProxyCACertificateURI</name>
+<description>Proxy CA certificate store for Remote Server Auth</description>
+<syntax>SSLProxyCACertificateURI <var>uri</var></syntax>
+<contextlist><context>server config</context> <context>virtual host</context>
+<context>proxy section</context></contextlist>
+<compatibility>Available in httpd 2.5.1 and later, when linked with
+OpenSSL v3 or later.</compatibility>
+
+<usage>
+<p>
+This directive sets the <em>all-in-one</em> URI where you can assemble the
+Certificates of Certification Authorities (CA) whose <em>remote servers</em> you deal
+with. These are used for Remote Server Authentication. This can be used alternatively
+and/or additionally to
+<directive module="mod_ssl">SSLProxyCACertificateFile</directive> and
+<directive module="mod_ssl">SSLProxyCACertificatePath</directive>.</p>
+<example><title>Example</title>
+<highlight language="config">
+SSLProxyCACertificateURI "/usr/local/apache2/conf/ssl.crt/ca-bundle-remote-server.crt"
+</highlight>
+</example>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>SSLProxyCARevocationPath</name>
<description>Directory of PEM-encoded CA CRLs for
Remote Server Auth</description>
@@ -2521,6 +2791,32 @@ SSLProxyCARevocationFile "/usr/local/apa
</highlight>
</example>
</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SSLProxyCARevocationURI</name>
+<description>Proxy CA certificate revocation list store for Remote Server Auth</description>
+<syntax>SSLProxyCARevocationURI <var>uri</var></syntax>
+<contextlist><context>server config</context> <context>virtual host</context>
+<context>proxy section</context></contextlist>
+<compatibility>Available in httpd 2.5.1 and later, when linked with
+OpenSSL v3 or later.</compatibility>
+
+<usage>
+<p>
+This directive sets the <em>all-in-one</em> URI where you can
+assemble the Certificate Revocation Lists (CRL) of Certification
+Authorities (CA) whose <em>remote servers</em> you deal with. These are used
+for Remote Server Authentication. This can be
+used alternatively and/or additionally to <directive
+module="mod_ssl">SSLProxyCARevocationFile</directive> and <directive
+module="mod_ssl">SSLProxyCARevocationPath</directive>.</p>
+<example><title>Example</title>
+<highlight language="config">
+SSLProxyCARevocationURI "/usr/local/apache2/conf/ssl.crl/ca-bundle-remote-server.crl"
+</highlight>
+</example>
+</usage>
</directivesynopsis>
<directivesynopsis>
Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.c
==============================================================================
--- httpd/httpd/trunk/modules/ssl/mod_ssl.c Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/mod_ssl.c Mon Aug 3 22:48:57 2026 (r1936849)
@@ -110,6 +110,9 @@ static const command_rec ssl_config_cmds
SSL_CMD_ALL(CipherSuite, TAKE12,
"Colon-delimited list of permitted SSL Ciphers, optional preceded "
"by protocol identifier ('XXX:...:XXX' - see manual)")
+ SSL_CMD_SRV(CertificateURI, TAKE1,
+ "SSL Server Certificate/Key uri "
+ "('file:', 'pkcs11:' - URI of certificate or key)")
SSL_CMD_SRV(CertificateFile, TAKE1,
"SSL Server Certificate file "
"('/path/to/file' - PEM or DER encoded)")
@@ -129,6 +132,9 @@ static const command_rec ssl_config_cmds
"TLS ECH Key Directory"
"('/path/to/dir' - directory with ECH key pairs)")
#endif
+ SSL_CMD_SRV(CACertificateURI, TAKE1,
+ "SSL CA Certificate uri "
+ "('file:', 'pkcs11:' - URI of CA certificates)")
SSL_CMD_ALL(CACertificatePath, TAKE1,
"SSL CA Certificate path "
"('/path/to/dir' - contains PEM encoded files)")
@@ -141,6 +147,9 @@ static const command_rec ssl_config_cmds
SSL_CMD_SRV(CADNRequestFile, TAKE1,
"SSL CA Distinguished Name file "
"('/path/to/file' - PEM encoded to derive acceptable CA names to request)")
+ SSL_CMD_SRV(CARevocationURI, TAKE1,
+ "SSL CA Certificate Revocation List (CRL) uri "
+ "('file:', 'pkcs11:' - URI of CRLs)")
SSL_CMD_SRV(CARevocationPath, TAKE1,
"SSL CA Certificate Revocation List (CRL) path "
"('/path/to/dir' - contains PEM encoded files)")
@@ -216,12 +225,18 @@ static const command_rec ssl_config_cmds
SSL_CMD_PXY(ProxyVerifyDepth, TAKE1,
"SSL Proxy: maximum certificate verification depth "
"('N' - number of intermediate certificates)")
+ SSL_CMD_PXY(ProxyCACertificateURI, TAKE1,
+ "SSL Proxy: uri referring to server certificates "
+ "('file:', 'pkcs11:' - URI of CA certificates)")
SSL_CMD_PXY(ProxyCACertificateFile, TAKE1,
"SSL Proxy: file containing server certificates "
"('/path/to/file' - PEM encoded certificates)")
SSL_CMD_PXY(ProxyCACertificatePath, TAKE1,
"SSL Proxy: directory containing server certificates "
"('/path/to/dir' - contains PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyCARevocationURI, TAKE1,
+ "SSL Proxy: CA Certificate Revocation List (CRL) uri "
+ "('file:', 'pkcs11:' - URI of CRLs)")
SSL_CMD_PXY(ProxyCARevocationPath, TAKE1,
"SSL Proxy: CA Certificate Revocation List (CRL) path "
"('/path/to/dir' - contains PEM encoded files)")
@@ -230,6 +245,9 @@ static const command_rec ssl_config_cmds
"('/path/to/file' - PEM encoded)")
SSL_CMD_PXY(ProxyCARevocationCheck, RAW_ARGS,
"SSL Proxy: CA Certificate Revocation List (CRL) checking mode")
+ SSL_CMD_PXY(ProxyMachineCertificateURI, TAKE1,
+ "SSL Proxy: uri referring to client certificates "
+ "('file:', 'pkcs11:' - URI of certificate or key)")
SSL_CMD_PXY(ProxyMachineCertificateFile, TAKE1,
"SSL Proxy: file containing client certificates "
"('/path/to/file' - PEM encoded certificates)")
Modified: httpd/httpd/trunk/modules/ssl/mod_ssl_openssl.h
==============================================================================
--- httpd/httpd/trunk/modules/ssl/mod_ssl_openssl.h Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/mod_ssl_openssl.h Mon Aug 3 22:48:57 2026 (r1936849)
@@ -33,6 +33,8 @@
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000
#include <openssl/macros.h> /* for OPENSSL_API_LEVEL */
+#include <openssl/store.h> /* for OSSL_STORE_open_ex */
+#include <openssl/ui.h> /* for UI_null */
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10001000
/* must be defined before including ssl.h */
Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_config.c
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_engine_config.c Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/ssl_engine_config.c Mon Aug 3 22:48:57 2026 (r1936849)
@@ -113,6 +113,7 @@ static void modssl_ctx_init(modssl_ctx_t
mctx->sc = NULL; /* set during module init */
mctx->ssl_ctx = NULL; /* set during module init */
+ mctx->libctx = NULL; /* set during module init */
mctx->pks = NULL;
mctx->pkp = NULL;
@@ -131,8 +132,10 @@ static void modssl_ctx_init(modssl_ctx_t
mctx->crl_path = NULL;
mctx->crl_file = NULL;
+ mctx->crl_uri = NULL;
mctx->crl_check_mask = UNSET;
+ mctx->auth.ca_cert_uri = NULL;
mctx->auth.ca_cert_path = NULL;
mctx->auth.ca_cert_file = NULL;
mctx->auth.cipher_suite = NULL;
@@ -200,6 +203,7 @@ static void modssl_ctx_init_server(SSLSr
mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks));
+ mctx->pks->uris = apr_array_make(p, 3, sizeof(char *));
mctx->pks->cert_files = apr_array_make(p, 3, sizeof(char *));
mctx->pks->key_files = apr_array_make(p, 3, sizeof(char *));
@@ -279,6 +283,7 @@ static void modssl_ctx_cfg_merge(apr_poo
cfgMerge(crl_file, NULL);
cfgMergeInt(crl_check_mask);
+ cfgMergeString(auth.ca_cert_uri);
cfgMergeString(auth.ca_cert_path);
cfgMergeString(auth.ca_cert_file);
cfgMergeString(auth.cipher_suite);
@@ -333,9 +338,11 @@ static void modssl_ctx_cfg_merge_server(
{
modssl_ctx_cfg_merge(p, base, add, mrg);
+ cfgMergeArray(pks->uris);
cfgMergeArray(pks->cert_files);
cfgMergeArray(pks->key_files);
+ cfgMergeString(pks->ca_name_uri);
cfgMergeString(pks->ca_name_path);
cfgMergeString(pks->ca_name_file);
@@ -386,6 +393,7 @@ static void modssl_ctx_init_proxy(SSLDir
mctx->pkp = apr_palloc(p, sizeof(*mctx->pkp));
+ mctx->pkp->uris = apr_array_make(p, 3, sizeof(char *));
mctx->pkp->cert_file = NULL;
mctx->pkp->cert_path = NULL;
mctx->pkp->ca_cert_file = NULL;
@@ -429,6 +437,7 @@ static void modssl_ctx_cfg_merge_proxy(a
{
modssl_ctx_cfg_merge(p, base, add, mrg);
+ cfgMergeArray(pkp->uris);
cfgMergeString(pkp->cert_file);
cfgMergeString(pkp->cert_path);
cfgMergeString(pkp->ca_cert_file);
@@ -962,6 +971,44 @@ static const char *ssl_cmd_check_file(cm
}
+static const char *ssl_cmd_check_uri(cmd_parms *parms,
+ const char *uri)
+{
+ OSSL_STORE_CTX *ctx;
+ unsigned long err;
+ int reason;
+
+ /* If only dumping the config, don't verify the paths */
+ if (ap_state_query(AP_SQ_RUN_MODE) == AP_SQ_RM_CONFIG_DUMP) {
+ return NULL;
+ }
+
+ ctx = OSSL_STORE_open_ex(uri, NULL, NULL, UI_null(),
+ NULL, NULL, NULL, NULL);
+
+ if (ctx) {
+ OSSL_STORE_close(ctx);
+ return NULL;
+ }
+
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_OSSL_STORE) {
+ reason = ERR_GET_REASON(err);
+
+ if (reason == OSSL_STORE_R_UNREGISTERED_SCHEME) {
+
+ return apr_pstrcat(parms->pool, parms->cmd->name,
+ ": uri '", uri,
+ "' is not recognised", NULL);
+
+ }
+ }
+
+ ERR_clear_error();
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
{
#if !defined(OPENSSL_NO_COMP)
@@ -1048,6 +1095,22 @@ static const char *ssl_cmd_check_dir(cmd
}
+const char *ssl_cmd_SSLCertificateURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ *(const char **)apr_array_push(sc->server->pks->uris) = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -1121,6 +1184,28 @@ const char *ssl_cmd_SSLSessionTicketKeyF
#define NO_PER_DIR_SSL_CA \
"Your SSL library does not have support for per-directory CA"
+const char *ssl_cmd_SSLCACertificateURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ if (cmd->path) {
+ return NO_PER_DIR_SSL_CA;
+ }
+
+ /* XXX: bring back per-dir */
+ sc->server->auth.ca_cert_uri = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLCACertificatePath(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -1165,6 +1250,21 @@ const char *ssl_cmd_SSLCACertificateFile
return NULL;
}
+const char *ssl_cmd_SSLCADNRequestURI(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ sc->server->pks->ca_name_uri = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *cmd, void *dcfg,
const char *arg)
{
@@ -1195,6 +1295,22 @@ const char *ssl_cmd_SSLCADNRequestFile(c
return NULL;
}
+const char *ssl_cmd_SSLCARevocationURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ sc->server->crl_uri = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLCARevocationPath(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -1740,6 +1856,22 @@ const char *ssl_cmd_SSLProxyVerifyDepth(
return NULL;
}
+const char *ssl_cmd_SSLProxyCACertificateURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ dc->proxy->auth.ca_cert_uri = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -1772,6 +1904,22 @@ const char *ssl_cmd_SSLProxyCACertificat
return NULL;
}
+const char *ssl_cmd_SSLProxyCARevocationURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ dc->proxy->crl_uri = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -1813,6 +1961,22 @@ const char *ssl_cmd_SSLProxyCARevocation
return ssl_cmd_crlcheck_parse(cmd, arg, &dc->proxy->crl_check_mask);
}
+const char *ssl_cmd_SSLProxyMachineCertificateURI(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_uri(cmd, arg))) {
+ return err;
+ }
+
+ *(const char **)apr_array_push(dc->proxy->pkp->uris) = arg;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *cmd,
void *dcfg,
const char *arg)
@@ -2293,7 +2457,7 @@ const char *ssl_cmd_SSLSRPUnknownUserSee
/* OCSP Responder File Function to read in value */
const char *ssl_cmd_SSLOCSPResponderCertificateFile(cmd_parms *cmd, void *dcfg,
- const char *arg)
+ const char *arg)
{
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
const char *err;
@@ -2332,6 +2496,14 @@ void ssl_hook_ConfigTest(apr_pool_t *pco
modssl_pk_server_t *const pks = sc->server->pks;
int i;
+ for (i = 0; (i < pks->uris->nelts) &&
+ APR_ARRAY_IDX(pks->uris, i, const char *);
+ i++) {
+ apr_file_printf(out, " %s\n",
+ APR_ARRAY_IDX(pks->uris,
+ i, const char *));
+ }
+
for (i = 0; (i < pks->cert_files->nelts) &&
APR_ARRAY_IDX(pks->cert_files, i, const char *);
i++) {
@@ -2356,6 +2528,10 @@ void ssl_hook_ConfigTest(apr_pool_t *pco
SSLSrvConfigRec *sc = mySrvConfig(s);
if (sc && sc->server) {
+ if (sc->server->auth.ca_cert_uri) {
+ apr_file_printf(out, " %s\n",
+ sc->server->auth.ca_cert_uri);
+ }
if (sc->server->auth.ca_cert_path) {
apr_file_printf(out, " %s\n",
sc->server->auth.ca_cert_path);
@@ -2623,6 +2799,7 @@ static void modssl_auth_ctx_dump(modssl_
#endif
DMP_VERIFY(proxy? "SSLProxyVerify" : "SSLVerifyClient", auth->verify_mode);
DMP_LONG( proxy? "SSLProxyVerify" : "SSLVerifyDepth", auth->verify_depth);
+ DMP_STRING(proxy? "SSLProxyCACertificateURI" : "SSLCACertificateURI", auth->ca_cert_uri);
DMP_STRING(proxy? "SSLProxyCACertificateFile" : "SSLCACertificateFile", auth->ca_cert_file);
DMP_STRING(proxy? "SSLProxyCACertificatePath" : "SSLCACertificatePath", auth->ca_cert_path);
}
@@ -2640,14 +2817,17 @@ static void modssl_ctx_dump(modssl_ctx_t
modssl_auth_ctx_dump(&ctx->auth, p, proxy, out, indent, psep);
+ DMP_STRING(proxy? "SSLProxyCARevocationURI" : "SSLCARevocationURI", ctx->crl_uri);
DMP_STRING(proxy? "SSLProxyCARevocationFile" : "SSLCARevocationFile", ctx->crl_file);
DMP_STRING(proxy? "SSLProxyCARevocationPath" : "SSLCARevocationPath", ctx->crl_path);
DMP_CRLCHK(proxy? "SSLProxyCARevocationCheck" : "SSLCARevocationCheck", ctx->crl_check_mask);
if (!proxy) {
DMP_PHRASE("SSLPassPhraseDialog", ctx->pphrase_dialog_type, ctx->pphrase_dialog_path);
if (ctx->pks) {
+ DMP_STRING("SSLCADNRequestURI", ctx->pks->ca_name_uri);
DMP_STRING("SSLCADNRequestFile", ctx->pks->ca_name_file);
DMP_STRING("SSLCADNRequestPath", ctx->pks->ca_name_path);
+ DMP_STRARR("SSLCertificateURI", ctx->pks->uris);
DMP_STRARR("SSLCertificateFile", ctx->pks->cert_files);
DMP_STRARR("SSLCertificateKeyFile", ctx->pks->key_files);
}
@@ -2698,6 +2878,7 @@ static void modssl_ctx_dump(modssl_ctx_t
}
else { /* proxy */
if (ctx->pkp) {
+ DMP_STRARR("SSLProxyMachineCertificateURI", ctx->pkp->uris);
DMP_STRING("SSLProxyMachineCertificateFile", ctx->pkp->cert_file);
DMP_STRING("SSLProxyMachineCertificatePath", ctx->pkp->cert_path);
DMP_STRING("SSLProxyMachineCertificateChainFile", ctx->pkp->ca_cert_file);
Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_init.c
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_engine_init.c Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/ssl_engine_init.c Mon Aug 3 22:48:57 2026 (r1936849)
@@ -334,6 +334,7 @@ static void hash_sni_policy_pk(apr_pool_
md5_ifstr_update(hash, "ciphers:", ctx->auth.cipher_suite);
md5_ifstr_update(hash, "tls13_ciphers:", ctx->auth.tls13_ciphers);
+ md5_strarray_hash(ptemp, hash, "uris:", ctx->pks->uris);
md5_strarray_hash(ptemp, hash, "cert_files:", ctx->pks->cert_files);
md5_strarray_hash(ptemp, hash, "key_files:", ctx->pks->key_files);
}
@@ -346,10 +347,13 @@ static void hash_sni_policy_auth(apr_md5
md5_fmt_update(hash, "verify_depth:%d", a->verify_depth);
md5_fmt_update(hash, "verify_mode:%d", a->verify_mode);
+ md5_ifstr_update(hash, "ca_name_uri:", pks->ca_name_uri);
md5_ifstr_update(hash, "ca_name_path:", pks->ca_name_path);
md5_ifstr_update(hash, "ca_name_file:", pks->ca_name_file);
+ md5_ifstr_update(hash, "ca_cert_uri:", a->ca_cert_uri);
md5_ifstr_update(hash, "ca_cert_path:", a->ca_cert_path);
md5_ifstr_update(hash, "ca_cert_file:", a->ca_cert_file);
+ md5_ifstr_update(hash, "crl_uri:", ctx->crl_uri);
md5_ifstr_update(hash, "crl_path:", ctx->crl_path);
md5_ifstr_update(hash, "crl_file:", ctx->crl_file);
md5_fmt_update(hash, "crl_check_mask:%d", ctx->crl_check_mask);
@@ -848,7 +852,7 @@ static apr_status_t ssl_init_ctx_tls_ext
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
/*
* Enable/disable SSLProtocol. If the mod_ssl enables protocol
* which is disabled by default by OpenSSL, show a warning.
@@ -887,7 +891,7 @@ static apr_status_t ssl_init_ctx_protoco
int protocol = mctx->protocol;
SSLSrvConfigRec *sc = mySrvConfig(s);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
- (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
+ (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
/* default is highest supported version, will be overridden below */
#if SSL_HAVE_PROTOCOL_TLSV1_3
int prot = TLS1_3_VERSION;
@@ -978,7 +982,7 @@ static apr_status_t ssl_init_ctx_protoco
SSL_CTX_set_options(ctx, SSL_OP_ALL);
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
/* always disable SSLv2, as per RFC 6176 */
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
@@ -1222,6 +1226,103 @@ int modssl_CTX_load_verify_locations(SSL
return 1;
}
+/*
+ * OpenSSL has a SSL_CTX_load_verify_store() function, but
+ * this function loads both leaf certs and CA certs.
+ *
+ * An end user reasonably wants to configure a URI pointing at
+ * CA certs and not have any surprises if the scope of the URI
+ * included leaf certificates.
+ *
+ * As a result we consider CA certs exclusively below.
+ */
+
+static APR_INLINE
+apr_status_t modssl_CTX_load_verify_store(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *uri,
+ int depth,
+ modssl_ctx_t *mctx)
+{
+#if MODSSL_HAVE_OPENSSL_STORE
+ OSSL_STORE_CTX *sctx;
+ OSSL_STORE_INFO *info;
+
+ apr_status_t rv = APR_SUCCESS;
+
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
+
+ if (!uri) {
+ return APR_SUCCESS;
+ }
+
+ if ((!(sctx = OSSL_STORE_open_ex(uri, mctx->libctx, NULL, NULL, NULL,
+ NULL, NULL, NULL)))) {
+ return APR_EGENERAL;
+ }
+
+ while (!OSSL_STORE_eof(sctx) && !OSSL_STORE_error(sctx)) {
+
+ if (!(info = OSSL_STORE_load(sctx))) {
+ continue;
+ }
+
+ switch(OSSL_STORE_INFO_get_type(info)) {
+ case OSSL_STORE_INFO_NAME: {
+
+ if (depth > 0) {
+ rv = modssl_CTX_load_verify_store(s, ptemp,
+ OSSL_STORE_INFO_get0_NAME(info),
+ depth - 1, mctx);
+ if (APR_SUCCESS != rv) {
+ OSSL_STORE_close(sctx);
+ return rv;
+ }
+ }
+
+ break;
+ }
+ case OSSL_STORE_INFO_CERT: {
+
+ X509 *cert;
+
+ if (!(cert = OSSL_STORE_INFO_get0_CERT(info))) {
+ return APR_EGENERAL;
+ }
+ else if (!X509_check_ca(cert)) {
+ /* ignore leaf certificates */
+ continue;
+ }
+ if (X509_STORE_add_cert(store, cert)) {
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10617)
+ "Host %s: Trusted certificate from URI: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+
+ }
+
+ break;
+ }
+ }
+ }
+
+ OSSL_STORE_close(sctx);
+
+ return rv;
+#else
+ if (!uri) {
+ return APR_SUCCESS;
+ }
+
+ return APR_ENOTIMPL;
+#endif
+}
+
+
static apr_status_t ssl_init_ctx_verify(server_rec *s,
apr_pool_t *p,
apr_pool_t *ptemp,
@@ -1258,10 +1359,24 @@ static apr_status_t ssl_init_ctx_verify(
/*
* Configure Client Authentication details
*/
- if (mctx->auth.ca_cert_file || mctx->auth.ca_cert_path) {
+
+ if (mctx->auth.ca_cert_file || mctx->auth.ca_cert_path ||
+ mctx->auth.ca_cert_uri) {
+
+ apr_status_t rv;
+
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
"Configuring client authentication");
+ if ((rv = modssl_CTX_load_verify_store(s, ptemp,
+ mctx->auth.ca_cert_uri, 1, mctx)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(10600)
+ "Unable to configure verify store "
+ "for client authentication");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
if (!modssl_CTX_load_verify_locations(ctx, mctx->auth.ca_cert_file,
mctx->auth.ca_cert_path)) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01895)
@@ -1271,22 +1386,32 @@ static apr_status_t ssl_init_ctx_verify(
return ssl_die(s);
}
- if (mctx->pks && (mctx->pks->ca_name_file || mctx->pks->ca_name_path)) {
+ if (mctx->pks && (mctx->pks->ca_name_file || mctx->pks->ca_name_path ||
+ mctx->pks->ca_name_uri)) {
ca_list = ssl_init_FindCAList(s, ptemp,
- mctx->pks->ca_name_file,
- mctx->pks->ca_name_path);
- } else
+ mctx->pks->ca_name_file,
+ mctx->pks->ca_name_path,
+ mctx->pks->ca_name_uri,
+ mctx);
+
+ } else {
ca_list = ssl_init_FindCAList(s, ptemp,
- mctx->auth.ca_cert_file,
- mctx->auth.ca_cert_path);
+ mctx->auth.ca_cert_file,
+ mctx->auth.ca_cert_path,
+ mctx->auth.ca_cert_uri,
+ mctx);
+ }
+
if (sk_X509_NAME_num(ca_list) <= 0) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01896)
"Unable to determine list of acceptable "
"CA certificates for client authentication");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
return ssl_die(s);
}
SSL_CTX_set_client_CA_list(ctx, ca_list);
+
}
/*
@@ -1354,17 +1479,113 @@ int modssl_X509_STORE_load_locations(X50
const char *path)
{
#if OPENSSL_VERSION_NUMBER < 0x30000000L
- if (!X509_STORE_load_locations(store, file, path))
+ if (!X509_STORE_load_locations(store, file, path)) {
return 0;
+ }
#else
- if (file && !X509_STORE_load_file(store, file))
+ if (file && !X509_STORE_load_file(store, file)) {
return 0;
- if (path && !X509_STORE_load_path(store, path))
+ }
+ if (path && !X509_STORE_load_path(store, path)) {
return 0;
+ }
#endif
return 1;
}
+/*
+ * OpenSSL has a X509_STORE_load_store() function, but this
+ * function has side effects - it loads both CRLs and trusted
+ * CA certificates.
+ *
+ * An end user reasonably wants to configure a URI pointing at
+ * CRLs and not have any surprises if the scope of the URI
+ * included trusted CA certificates for whatever reason.
+ *
+ * As a result we consider CRLs exclusively below.
+ */
+
+static APR_INLINE
+apr_status_t modssl_X509_STORE_load_crl(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *uri,
+ int depth,
+ modssl_ctx_t *mctx)
+{
+#if MODSSL_HAVE_OPENSSL_STORE
+ OSSL_STORE_CTX *sctx;
+ OSSL_STORE_INFO *info;
+
+ apr_status_t rv = APR_SUCCESS;
+
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
+
+ if (!uri) {
+ return APR_SUCCESS;
+ }
+
+ if ((!(sctx = OSSL_STORE_open_ex(uri, mctx->libctx, NULL, NULL, NULL,
+ NULL, NULL, NULL)))) {
+ return APR_EGENERAL;
+ }
+
+ while (!OSSL_STORE_eof(sctx) && !OSSL_STORE_error(sctx)) {
+
+ if (!(info = OSSL_STORE_load(sctx))) {
+ continue;
+ }
+
+ switch(OSSL_STORE_INFO_get_type(info)) {
+ case OSSL_STORE_INFO_NAME: {
+
+ if (depth > 0) {
+ rv = modssl_X509_STORE_load_crl(s, ptemp,
+ OSSL_STORE_INFO_get0_NAME(info),
+ depth - 1, mctx);
+ if (APR_SUCCESS != rv) {
+ OSSL_STORE_close(sctx);
+ return rv;
+ }
+ }
+
+ break;
+ }
+ case OSSL_STORE_INFO_CRL: {
+
+ X509_CRL *crl;
+
+ if (!(crl = OSSL_STORE_INFO_get0_CRL(info))) {
+ return APR_EGENERAL;
+ }
+ if (X509_STORE_add_crl(store, crl)) {
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10601)
+ "Host %s: Certificate revocation list from URI: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_CRL_get_issuer(crl), 0));
+
+ }
+
+ break;
+ }
+ }
+ }
+
+ OSSL_STORE_close(sctx);
+
+ return rv;
+#else
+ if (!uri) {
+ return APR_SUCCESS;
+ }
+
+ return APR_ENOTIMPL;
+#endif
+}
+
static apr_status_t ssl_init_ctx_crl(server_rec *s,
apr_pool_t *p,
apr_pool_t *ptemp,
@@ -1374,6 +1595,9 @@ static apr_status_t ssl_init_ctx_crl(ser
unsigned long crlflags = 0;
char *cfgp = mctx->pkp ? "SSLProxy" : "SSL";
int crl_check_mode;
+ apr_status_t rv;
+
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
if (mctx->ocsp_mask == UNSET) {
mctx->ocsp_mask = SSL_OCSPCHECK_NONE;
@@ -1388,13 +1612,13 @@ static apr_status_t ssl_init_ctx_crl(ser
* Configure Certificate Revocation List (CRL) Details
*/
- if (!(mctx->crl_file || mctx->crl_path)) {
+ if (!(mctx->crl_uri || mctx->crl_file || mctx->crl_path)) {
if (crl_check_mode == SSL_CRLCHECK_LEAF ||
crl_check_mode == SSL_CRLCHECK_CHAIN) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01899)
"Host %s: CRL checking has been enabled, but "
- "neither %sCARevocationFile nor %sCARevocationPath "
- "is configured", mctx->sc->vhost_id, cfgp, cfgp);
+ "neither %sCARevocationURI, %sCARevocationFile nor %sCARevocationPath "
+ "is configured", mctx->sc->vhost_id, cfgp, cfgp, cfgp);
return ssl_die(s);
}
return APR_SUCCESS;
@@ -1403,8 +1627,16 @@ static apr_status_t ssl_init_ctx_crl(ser
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01900)
"Configuring certificate revocation facility");
- if (!store || !modssl_X509_STORE_load_locations(store, mctx->crl_file,
- mctx->crl_path)) {
+ if ((rv = modssl_X509_STORE_load_crl(s, ptemp, mctx->crl_uri, 1, mctx)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(10602)
+ "Host %s: unable to configure X.509 CRL uri "
+ "for certificate revocation", mctx->sc->vhost_id);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ if (!modssl_X509_STORE_load_locations(store,
+ mctx->crl_file, mctx->crl_path)) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01901)
"Host %s: unable to configure X.509 CRL storage "
"for certificate revocation", mctx->sc->vhost_id);
@@ -1636,6 +1868,386 @@ static int ssl_no_passwd_prompt_cb(char
return 0;
}
+
+#ifndef DEFINE_STACK_OF_EVP_PKEY
+DEFINE_STACK_OF(EVP_PKEY)
+#endif
+
+#if MODSSL_HAVE_OPENSSL_STORE
+
+static apr_status_t ssl_init_uri_cleanup(void *data)
+{
+ modssl_ctx_uri_t *uctx = (modssl_ctx_uri_t *)data;
+
+ sk_X509_pop_free(uctx->cert_list, X509_free);
+ sk_EVP_PKEY_pop_free(uctx->key_list, EVP_PKEY_free);
+ sk_X509_pop_free(uctx->ca_list, X509_free);
+
+ return APR_SUCCESS;
+}
+
+/*
+ * Sort certificates oldest to newest (last one wins).
+ */
+static int compare_certs_asc(const X509 *const *a, const X509 *const *b)
+{
+ const ASN1_TIME *time_a = X509_get0_notBefore(*a);
+ const ASN1_TIME *time_b = X509_get0_notBefore(*b);
+
+ /* ASN1_TIME_compare returns:
+ * -1 if time_a is earlier than time_b
+ * 0 if they are identical
+ * 1 if time_a is later than time_b
+ */
+ return ASN1_TIME_compare(time_a, time_b);
+}
+
+static int cert_match(apr_pool_t *p, X509 *cert, char *id)
+{
+ if (id[0] == '[') {
+ const char *end = strchr(id, ']');
+ if (end && X509_check_ip_asc(cert,
+ apr_pstrndup(p, id + 1, end - id - 1), 0) == 1) {
+ return 1;
+ }
+ return 0;
+ }
+ if (X509_check_ip_asc(cert, id, 0) == 1) {
+ return 1;
+ }
+ if (X509_check_host(cert, id, 0, 0, NULL) == 1) {
+ return 1;
+ }
+ return 0;
+}
+
+static apr_status_t ssl_init_uri(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *uri,
+ int depth,
+ modssl_ctx_uri_t *uctx)
+{
+ OSSL_STORE_CTX *sctx;
+ OSSL_STORE_INFO *info;
+
+ apr_status_t rv = APR_SUCCESS;
+
+ if (!uri) {
+ return rv;
+ }
+
+ if ((!(sctx = OSSL_STORE_open_ex(uri, uctx->mctx->libctx, NULL,
+ modssl_get_passphrase_ui(ptemp),
+ modssl_get_passphrase_cb(s, ptemp,
+ uctx->mctx->sc->vhost_id, uri),
+ NULL, NULL, NULL)))) {
+ return APR_EGENERAL;
+ }
+
+ while (!OSSL_STORE_eof(sctx) && !OSSL_STORE_error(sctx)) {
+
+ if (!(info = OSSL_STORE_load(sctx))) {
+ continue;
+ }
+
+ switch(OSSL_STORE_INFO_get_type(info)) {
+ case OSSL_STORE_INFO_NAME: {
+
+ if (depth > 0) {
+ rv = ssl_init_uri(s, ptemp,
+ OSSL_STORE_INFO_get0_NAME(info),
+ depth - 1, uctx);
+ if (APR_SUCCESS != rv) {
+ OSSL_STORE_close(sctx);
+ return rv;
+ }
+ }
+
+ break;
+ }
+ case OSSL_STORE_INFO_CERT: {
+
+ X509 *cert;
+
+ if (!(cert = OSSL_STORE_INFO_get1_CERT(info))) {
+ return APR_EGENERAL;
+ }
+ else if (X509_check_ca(cert)) {
+
+ if (X509_self_signed(cert, 1)) {
+
+ uctx->num_ca_certs++;
+
+ /* ignore root certificates */
+ X509_free(cert);
+ continue;
+ }
+
+ if (sk_X509_push(uctx->ca_list, cert) <= 0) {
+ X509_free(cert);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_intermediate_certs++;
+
+ }
+ else {
+
+ uctx->num_leaf_certs++;
+
+ if (!X509_check_purpose(cert, X509_PURPOSE_SSL_SERVER, 0)) {
+ /* ignore non server certs */
+ X509_free(cert);
+ continue;
+ }
+
+ /* check for a match on server name */
+ if (s->server_hostname) {
+ if (!cert_match(ptemp, cert, s->server_hostname)) {
+ X509_free(cert);
+ continue;
+ }
+ }
+
+ /* check for a match on all server aliases */
+ if (s->names && !apr_is_empty_array(s->names)) {
+ char **aliases = (char **)s->names->elts;
+ int i;
+ for (i = 0; i < s->names->nelts; i++) {
+ if (!cert_match(ptemp, cert, aliases[i])) {
+ X509_free(cert);
+ continue;
+ }
+ }
+ }
+
+ /* If we get here and a server name or server alias was
+ * not specified, we use the most recently issued leaf
+ * certificate in scope and assume the admin knows what
+ * they are doing.
+ */
+
+ if (sk_X509_push(uctx->cert_list, cert) <= 0) {
+ X509_free(cert);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_server_certs++;
+
+ }
+
+ uctx->num_certs++;
+
+ break;
+ }
+ case OSSL_STORE_INFO_PKEY: {
+
+ EVP_PKEY *key;
+
+ if (!(key = OSSL_STORE_INFO_get1_PKEY(info))) {
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+ if (sk_EVP_PKEY_push(uctx->key_list, key) <= 0) {
+ EVP_PKEY_free(key);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_keys++;
+
+ break;
+ }
+ }
+ }
+
+ OSSL_STORE_close(sctx);
+
+ return rv;
+}
+
+
+/*
+ * Load certs from all URIs.
+ *
+ * The end user might point their URI at a single set of
+ * PEM encoded certs using the file: scheme, or might point
+ * the URI at pkcs11: or the whole MacOS keychain and
+ * expect us to figure it out for them. Lets help as much
+ * as possible.
+ *
+ * - Load all certs across all uris.
+ * - Consider intermediate certs, add them to the store
+ * - Consider leaf certs that match the ServerName and
+ * ServerAliases and drop if no match.
+ * - Sort certs by start date, oldest to newest
+ * - Load all keys across all uris.
+ * - Consider certs with a private key, drop the rest.
+ * - Pass each cert and key, in order, using
+ * SSL_CTX_use_certificate and SSL_CTX_use_PrivateKey.
+ * - End result, the most recent cert for each type (RSA,
+ * ECDSA, etc) wins.
+ *
+ */
+
+static apr_status_t ssl_init_server_uris(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx,
+ apr_array_header_t *pphrases)
+{
+ const char *uri;
+ int i, k;
+ int found = 0;
+ apr_status_t rv = APR_SUCCESS;
+
+ modssl_ctx_uri_t *uctx = apr_pcalloc(ptemp, sizeof(modssl_ctx_uri_t));
+
+ uctx->mctx = mctx;
+
+ uctx->cert_list = sk_X509_new_null();
+ uctx->key_list = sk_EVP_PKEY_new_null();
+ uctx->ca_list = sk_X509_new_null();
+
+ apr_pool_cleanup_register(ptemp, uctx,
+ ssl_init_uri_cleanup,
+ apr_pool_cleanup_null);
+
+ sk_X509_set_cmp_func(uctx->cert_list, compare_certs_asc);
+
+ /* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
+ SSL_CTX_set_default_passwd_cb(mctx->ssl_ctx, ssl_no_passwd_prompt_cb);
+
+ /* Iterate over the SSLCertificateURI array */
+ for (i = 0; (i < mctx->pks->uris->nelts) &&
+ (uri = APR_ARRAY_IDX(mctx->pks->uris, i,
+ const char *));
+ i++) {
+
+ if (ssl_init_uri(s, ptemp, uri, 1, uctx) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10603)
+ "Host %s: Failed to open URI `%s'",
+ mctx->sc->vhost_id, uri);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ }
+
+ /* oldest to newest, last one wins */
+ sk_X509_sort(uctx->cert_list);
+
+ /* Match certs to keys */
+ for (i = sk_X509_num(uctx->cert_list) - 1; i >= 0; i--) {
+ X509 *cert = sk_X509_value(uctx->cert_list, i);
+
+ for (k = 0; k < sk_EVP_PKEY_num(uctx->key_list); k++) {
+ EVP_PKEY *pkey = sk_EVP_PKEY_value(uctx->key_list, k);
+
+ if (X509_check_private_key(cert, pkey) == 1) {
+
+ if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10604)
+ "Host %s: Failed to use certificate: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10605)
+ "Host %s: Failed to use private key: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10606)
+ "Host %s: Server certificate from URI: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+
+ found = 1;
+ break;
+ }
+
+ }
+ }
+
+ if (!found) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10607)
+ "Host %s: No matching certificate/key pairs found among "
+ "%d certs, %d CA certs, %d intermediate certs, "
+ "%d leaf certs, %d server certs, %d keys.",
+ mctx->sc->vhost_id,
+ uctx->num_certs, uctx->num_ca_certs, uctx->num_intermediate_certs,
+ uctx->num_leaf_certs,
+ uctx->num_server_certs, uctx->num_keys);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* Handle intermediates, must happen after cert handling */
+ for (i = sk_X509_num(uctx->ca_list) - 1; i >= 0; i--) {
+ X509 *cert = sk_X509_value(uctx->ca_list, i);
+ if (!SSL_CTX_add1_chain_cert(mctx->ssl_ctx, cert)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10608)
+ "Host %s: Failed to add intermediate certificate: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+ }
+
+ /*
+ * Do our best to build as much of the chain as possible with
+ * the certs we were provided.
+ */
+
+ if (!SSL_CTX_build_cert_chain(mctx->ssl_ctx, SSL_BUILD_CHAIN_FLAG_NO_ROOT |
+ SSL_BUILD_CHAIN_FLAG_UNTRUSTED |
+ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR |
+ SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10609)
+ "Host %s: Could not build the certificate chain from "
+ "%d certs, %d CA certs, %d intermediate certs, "
+ "%d leaf certs, %d server certs, %d keys.",
+ mctx->sc->vhost_id,
+ uctx->num_certs, uctx->num_ca_certs, uctx->num_intermediate_certs,
+ uctx->num_leaf_certs,
+ uctx->num_server_certs, uctx->num_keys);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ return rv;
+}
+#else
+static apr_status_t ssl_init_server_uris(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx,
+ apr_array_header_t *pphrases)
+{
+ const char *vhost_id = mctx->sc->vhost_id;
+
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10610)
+ "Host %s: Server certificate URIs are not supported on this platform.",
+ mctx->sc->vhost_id);
+
+ return APR_ENOTIMPL;
+}
+#endif
+
/* SSL_CTX_use_PrivateKey_file() can fail either because the private
* key was encrypted, or due to a mismatch between an already-loaded
* cert and the key - a common misconfiguration - from calling
@@ -2001,6 +2613,295 @@ static apr_status_t ssl_init_ticket_key(
}
#endif
+#if MODSSL_HAVE_OPENSSL_STORE
+
+/*
+ * Sort certificates newest to oldest (first one wins).
+ */
+static int compare_certs_desc(const X509 *const *a, const X509 *const *b)
+{
+ const ASN1_TIME *time_a = X509_get0_notBefore(*a);
+ const ASN1_TIME *time_b = X509_get0_notBefore(*b);
+
+ /* ASN1_TIME_compare returns:
+ * -1 if time_a is earlier than time_b
+ * 0 if they are identical
+ * 1 if time_a is later than time_b
+ */
+ return -ASN1_TIME_compare(time_a, time_b);
+}
+
+static apr_status_t ssl_init_proxy_uri(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *uri,
+ int depth,
+ modssl_ctx_uri_t *uctx)
+{
+ OSSL_STORE_CTX *sctx;
+ OSSL_STORE_INFO *info;
+
+ apr_status_t rv = APR_SUCCESS;
+
+ if (!uri) {
+ return rv;
+ }
+
+ if ((!(sctx = OSSL_STORE_open_ex(uri, uctx->mctx->libctx, NULL,
+ modssl_get_passphrase_ui(ptemp),
+ modssl_get_passphrase_cb(s, ptemp,
+ uctx->mctx->sc->vhost_id, uri),
+ NULL, NULL, NULL)))) {
+ return APR_EGENERAL;
+ }
+
+ while (!OSSL_STORE_eof(sctx) && !OSSL_STORE_error(sctx)) {
+
+ if (!(info = OSSL_STORE_load(sctx))) {
+ continue;
+ }
+
+ switch(OSSL_STORE_INFO_get_type(info)) {
+ case OSSL_STORE_INFO_NAME: {
+
+ if (depth > 0) {
+ rv = ssl_init_uri(s, ptemp,
+ OSSL_STORE_INFO_get0_NAME(info),
+ depth - 1, uctx);
+ if (APR_SUCCESS != rv) {
+ OSSL_STORE_close(sctx);
+ return rv;
+ }
+ }
+
+ break;
+ }
+ case OSSL_STORE_INFO_CERT: {
+
+ X509 *cert;
+
+ if (!(cert = OSSL_STORE_INFO_get1_CERT(info))) {
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+ else if (X509_check_ca(cert)) {
+
+ if (X509_self_signed(cert, 1)) {
+
+ uctx->num_ca_certs++;
+
+ /* ignore root certificates */
+ X509_free(cert);
+ break;
+ }
+
+ if (sk_X509_push(uctx->ca_list, cert) <= 0) {
+ X509_free(cert);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_intermediate_certs++;
+
+ }
+ else {
+
+ uctx->num_leaf_certs++;
+
+ if (!X509_check_purpose(cert, X509_PURPOSE_SSL_CLIENT, 0)) {
+ /* ignore non client certs */
+ X509_free(cert);
+ break;
+ }
+
+ if (sk_X509_push(uctx->cert_list, cert) <= 0) {
+ X509_free(cert);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_client_certs++;
+
+ }
+
+ uctx->num_certs++;
+
+ break;
+ }
+ case OSSL_STORE_INFO_PKEY: {
+
+ EVP_PKEY *key;
+
+ if (!(key = OSSL_STORE_INFO_get1_PKEY(info))) {
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+ if (sk_EVP_PKEY_push(uctx->key_list, key) <= 0) {
+ EVP_PKEY_free(key);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ uctx->num_keys++;
+
+ break;
+ }
+ }
+ }
+
+ OSSL_STORE_close(sctx);
+
+ return rv;
+}
+
+static apr_status_t ssl_init_proxy_uris(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ const char *uri;
+ modssl_pk_proxy_t *pkp = mctx->pkp;
+ modssl_ctx_uri_t *uctx;
+ STACK_OF(X509_INFO) *sk;
+
+ int i, k;
+ int found = 0;
+ apr_status_t rv = APR_SUCCESS;
+
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
+
+ if (!pkp->uris->nelts) {
+ return APR_SUCCESS;
+ }
+
+ SSL_CTX_set_post_handshake_auth(mctx->ssl_ctx, 1);
+
+ SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
+ ssl_callback_proxy_cert);
+
+ sk = pkp->certs;
+ if (!sk) {
+ pkp->certs = sk = sk_X509_INFO_new_null();
+ }
+
+ uctx = apr_pcalloc(ptemp, sizeof(modssl_ctx_uri_t));
+
+ uctx->mctx = mctx;
+
+ uctx->cert_list = sk_X509_new_null();
+ uctx->key_list = sk_EVP_PKEY_new_null();
+ uctx->ca_list = sk_X509_new_null();
+
+ apr_pool_cleanup_register(ptemp, uctx,
+ ssl_init_uri_cleanup,
+ apr_pool_cleanup_null);
+
+ sk_X509_set_cmp_func(uctx->cert_list, compare_certs_desc);
+
+ /* Iterate over the SSLProxyCertificateURI array */
+ for (i = 0; (i < mctx->pkp->uris->nelts) &&
+ (uri = APR_ARRAY_IDX(mctx->pkp->uris, i,
+ const char *));
+ i++) {
+
+ if (uri &&
+ ssl_init_proxy_uri(s, ptemp, uri, 1, uctx) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10611)
+ "Host %s: Failed to open proxy URI `%s'",
+ mctx->sc->vhost_id, uri);
+ return APR_EGENERAL;
+ }
+
+ }
+
+ /* newest to oldest, first one wins */
+ sk_X509_sort(uctx->cert_list);
+
+ /* Match certs to keys */
+ for (i = sk_X509_num(uctx->cert_list) - 1; i >= 0; i--) {
+ X509 *cert = sk_X509_value(uctx->cert_list, i);
+
+ for (k = 0; k < sk_EVP_PKEY_num(uctx->key_list); k++) {
+ EVP_PKEY *pkey = sk_EVP_PKEY_value(uctx->key_list, k);
+
+ if (X509_check_private_key(cert, pkey) == 1) {
+
+ X509_INFO *info = X509_INFO_new();
+ info->x_pkey = X509_PKEY_new();
+
+ X509_up_ref(cert);
+ info->x509 = cert;
+
+ EVP_PKEY_up_ref(pkey);
+ info->x_pkey->dec_pkey = pkey;
+
+ sk_X509_INFO_push(sk, info);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10612)
+ "Host %s: Proxy certificate from URI: %s",
+ mctx->sc->vhost_id,
+ modssl_X509_NAME_to_string(ptemp,
+ X509_get_subject_name(cert), 0));
+
+ found = 1;
+ break;
+ }
+
+ }
+ }
+
+ if (!found) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10613)
+ "Host %s: No matching proxy certificate/key pairs found among "
+ "%d certs, %d CA certs, %d intermediate certs, "
+ "%d leaf certs, %d client certs, %d keys.",
+ mctx->sc->vhost_id,
+ uctx->num_certs, uctx->num_ca_certs, uctx->num_intermediate_certs,
+ uctx->num_leaf_certs,
+ uctx->num_client_certs, uctx->num_keys);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ sk_X509_INFO_free(sk);
+ return APR_EGENERAL;
+ }
+
+ /* Handle intermediates, must happen after cert handling */
+ for (i = sk_X509_num(uctx->ca_list) - 1; i >= 0; i--) {
+ X509 *cert = sk_X509_value(uctx->ca_list, i);
+ X509_STORE_add_cert(store, cert); /* increments cert */
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10614)
+ "Host %s: loaded %d client certs for SSL proxy among "
+ "%d certs, %d CA certs, %d intermediate certs, "
+ "%d leaf certs, %d client certs, %d keys.",
+ mctx->sc->vhost_id,
+ sk_X509_INFO_num(sk),
+ uctx->num_certs, uctx->num_ca_certs, uctx->num_intermediate_certs,
+ uctx->num_leaf_certs,
+ uctx->num_client_certs, uctx->num_keys);
+
+ return rv;
+}
+#else
+static apr_status_t ssl_init_proxy_uris(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ if (pkp->uris->nelts) {
+ const char *vhost_id = mctx->sc->vhost_id;
+
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10615)
+ "Host %s: Proxy certificate URIs are not supported on this platform.",
+ mctx->sc->vhost_id);
+
+ return APR_ENOTIMPL;
+ }
+
+ return APR_SUCCESS;
+}
+#endif
+
static BOOL load_x509_info(apr_pool_t *ptemp,
STACK_OF(X509_INFO) *sk,
const char *filename)
@@ -2030,14 +2931,10 @@ static apr_status_t ssl_init_proxy_certs
apr_pool_t *ptemp,
modssl_ctx_t *mctx)
{
- int n, ncerts = 0;
STACK_OF(X509_INFO) *sk;
modssl_pk_proxy_t *pkp = mctx->pkp;
- STACK_OF(X509) *chain;
- X509_STORE_CTX *sctx;
X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
- int addl_chain = 0; /* non-zero if additional chain certs were
- * added to store */
+ int n, ncerts = 0;
ap_assert(store != NULL); /* safe to assume always non-NULL? */
@@ -2047,7 +2944,7 @@ static apr_status_t ssl_init_proxy_certs
* https://github.com/openssl/openssl/issues/6933 */
SSL_CTX_set_post_handshake_auth(mctx->ssl_ctx, 1);
#endif
-
+
SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
ssl_callback_proxy_cert);
@@ -2082,7 +2979,6 @@ static apr_status_t ssl_init_proxy_certs
X509_INFO_free(inf);
sk_X509_INFO_delete(sk, n);
n--;
- addl_chain = 1;
continue;
}
@@ -2093,7 +2989,7 @@ static apr_status_t ssl_init_proxy_certs
"(missing or encrypted private key?)");
return ssl_die(s);
}
-
+
if (X509_check_private_key(inf->x509, inf->x_pkey->dec_pkey) != 1) {
ssl_log_xerror(SSLLOG_MARK, APLOG_STARTUP, 0, ptemp, s, inf->x509,
APLOGNO(02326) "proxy client certificate and "
@@ -2115,36 +3011,51 @@ static apr_status_t ssl_init_proxy_certs
ncerts);
pkp->certs = sk;
- /* If any chain certs are configured, build the ->ca_certs chains
- * corresponding to the loaded keypairs. */
- if (!pkp->ca_cert_file && !addl_chain) {
- return APR_SUCCESS;
+ if (pkp->ca_cert_file) {
+ modssl_X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
}
- /* If SSLProxyMachineCertificateChainFile is configured, load all
- * the CA certs and have OpenSSL attempt to construct a full chain
- * from each configured end-entity cert up to a root. This will
- * allow selection of the correct cert given a list of root CA
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_proxy_ca_certs(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ modssl_pk_proxy_t *pkp = mctx->pkp;
+
+ X509_STORE_CTX *sctx;
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+ STACK_OF(X509) *chain;
+
+ int n, ncerts = 0;
+
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
+
+ ncerts = pkp->certs ? sk_X509_INFO_num(pkp->certs) : 0;
+
+ /* If intermediate certificates have been configured, have
+ * OpenSSL attempt to construct a full chain from each
+ * configured end-entity cert up to a root. This will allow
+ * selection of the correct cert given a list of root CA
* names in the certificate request from the server. */
- pkp->ca_certs = (STACK_OF(X509) **) apr_pcalloc(p, ncerts * sizeof(sk));
+
+ pkp->ca_certs = (STACK_OF(X509) **) apr_pcalloc(p, ncerts * sizeof(STACK_OF(X509_INFO) *));
sctx = X509_STORE_CTX_new();
if (!sctx) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02208)
"SSL proxy client cert initialization failed");
ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
- sk_X509_INFO_free(sk);
return ssl_die(s);
}
- modssl_X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
-
for (n = 0; n < ncerts; n++) {
int i;
X509_INFO *inf = sk_X509_INFO_value(pkp->certs, n);
if (!X509_STORE_CTX_init(sctx, store, inf->x509, NULL)) {
- sk_X509_INFO_free(sk);
X509_STORE_CTX_free(sctx);
return ssl_die(s);
}
@@ -2268,6 +3179,14 @@ static apr_status_t ssl_init_proxy_ctx(s
return rv;
}
+ if ((rv = ssl_init_proxy_uris(s, p, ptemp, proxy)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if ((rv = ssl_init_proxy_ca_certs(s, p, ptemp, proxy)) != APR_SUCCESS) {
+ return rv;
+ }
+
return APR_SUCCESS;
}
@@ -2319,16 +3238,30 @@ static apr_status_t ssl_init_server_ctx(
/* additionally installed certs overrides any old chain configuration */
sc->server->cert_chain = NULL;
}
-
+
if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {
return rv;
}
- if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server, pphrases))
- != APR_SUCCESS) {
- return rv;
+ if (pks->cert_files->nelts) {
+
+ if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server, pphrases))
+ != APR_SUCCESS) {
+ return rv;
+ }
+
}
+ if (pks->uris->nelts) {
+
+ if ((rv = ssl_init_server_uris(s, p, ptemp, sc->server, pphrases))
+ != APR_SUCCESS) {
+ return rv;
+ }
+
+ }
+
+
#ifdef HAVE_SSL_CONF_CMD
SSL_CONF_CTX_set_ssl_ctx(cctx, sc->server->ssl_ctx);
for (i = 0; i < sc->server->ssl_ctx_param->nelts; i++, param++) {
@@ -2425,10 +3358,10 @@ apr_status_t ssl_init_ConfigureServer(se
return rv;
}
- /* Initialize OCSP Responder certificate if OCSP enabled */
- #ifndef OPENSSL_NO_OCSP
- ssl_init_ocsp_certificates(s, sc->server);
- #endif
+ /* Initialize OCSP Responder certificate if OCSP enabled */
+ #ifndef OPENSSL_NO_OCSP
+ ssl_init_ocsp_certificates(s, sc->server);
+ #endif
}
@@ -2566,6 +3499,101 @@ int ssl_proxy_section_post_config(apr_po
return OK;
}
+static int ssl_init_x509_name_cmp(const X509_NAME *const *a, const X509_NAME *const *b)
+{
+ return X509_NAME_cmp(*a, *b);
+}
+
+static apr_status_t ssl_init_ca_cert_uri(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *uri,
+ STACK_OF(X509_NAME) *ca_list,
+ int depth,
+ modssl_ctx_t *mctx)
+{
+#if MODSSL_HAVE_OPENSSL_STORE
+ OSSL_STORE_CTX *sctx;
+ OSSL_STORE_INFO *info;
+
+ apr_status_t rv = APR_SUCCESS;
+
+ sk_X509_NAME_set_cmp_func(ca_list, ssl_init_x509_name_cmp);
+
+ if (!uri) {
+ return rv;
+ }
+
+ if ((!(sctx = OSSL_STORE_open_ex(uri, mctx->libctx, NULL, NULL, NULL,
+ NULL, NULL, NULL)))) {
+ return APR_EGENERAL;
+ }
+
+ while (!OSSL_STORE_eof(sctx) && !OSSL_STORE_error(sctx)) {
+
+ if (!(info = OSSL_STORE_load(sctx))) {
+ continue;
+ }
+
+ switch(OSSL_STORE_INFO_get_type(info)) {
+ case OSSL_STORE_INFO_NAME: {
+
+ if (depth > 0) {
+ rv = ssl_init_ca_cert_uri(s, ptemp,
+ OSSL_STORE_INFO_get0_NAME(info),
+ ca_list, depth - 1, mctx);
+ if (APR_SUCCESS != rv) {
+ OSSL_STORE_close(sctx);
+ return rv;
+ }
+ }
+
+ break;
+ }
+ case OSSL_STORE_INFO_CERT: {
+
+ X509 *cert;
+ const X509_NAME *name;
+ X509_NAME *xname;
+
+ if (!(cert = OSSL_STORE_INFO_get0_CERT(info))) {
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+ else if (!X509_check_ca(cert)) {
+ /* ignore leaf certificates */
+ continue;
+ }
+ else if (!(name = X509_get_subject_name(cert)) ||
+ !(xname = X509_NAME_dup(name))) {
+ return APR_EGENERAL;
+ }
+ if (sk_X509_NAME_find(ca_list, xname) >= 0) {
+ /* duplicate */
+ X509_NAME_free(xname);
+ }
+ else if (!sk_X509_NAME_push(ca_list, xname)) {
+ X509_NAME_free(xname);
+ OSSL_STORE_close(sctx);
+ return APR_EGENERAL;
+ }
+
+ break;
+ }
+ }
+ }
+
+ OSSL_STORE_close(sctx);
+
+ return rv;
+#else
+ if (!uri) {
+ return APR_SUCCESS;
+ }
+
+ return APR_ENOTIMPL;
+#endif
+}
+
static apr_status_t ssl_init_ca_cert_path(server_rec *s,
apr_pool_t *ptemp,
const char *path,
@@ -2603,24 +3631,34 @@ static apr_status_t ssl_init_ca_cert_pat
STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
apr_pool_t *ptemp,
const char *ca_file,
- const char *ca_path)
+ const char *ca_path,
+ const char *ca_uri,
+ modssl_ctx_t *mctx)
{
STACK_OF(X509_NAME) *ca_list = sk_X509_NAME_new_null();;
/*
+ * Process CA certificate store uri
+ */
+ if (ca_uri &&
+ ssl_init_ca_cert_uri(s, ptemp,
+ ca_uri, ca_list, 1, mctx) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10616)
+ "Failed to open Certificate URI `%s'", ca_uri);
+ sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
+ return NULL;
+ }
+
+ /*
* Process CA certificate bundle file
*/
- if (ca_file) {
- SSL_add_file_cert_subjects_to_stack(ca_list, ca_file);
- /*
- * If ca_list is still empty after trying to load ca_file
- * then the file failed to load, and users should hear about that.
- */
- if (sk_X509_NAME_num(ca_list) == 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02210)
+ if (ca_file &&
+ !SSL_add_file_cert_subjects_to_stack(ca_list, ca_file)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02210)
"Failed to load SSLCACertificateFile: %s", ca_file);
- ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
- }
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
+ return NULL;
}
/*
@@ -2675,10 +3713,10 @@ apr_status_t ssl_init_ModuleKill(void *d
ssl_init_ctx_cleanup(sc->server);
- /* Not Sure but possibly clear X509 trusted cert file */
- #ifndef OPENSSL_NO_OCSP
- sk_X509_pop_free(sc->server->ocsp_certs, X509_free);
- #endif
+ /* Not Sure but possibly clear X509 trusted cert file */
+ #ifndef OPENSSL_NO_OCSP
+ sk_X509_pop_free(sc->server->ocsp_certs, X509_free);
+ #endif
}
Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c Mon Aug 3 22:48:57 2026 (r1936849)
@@ -29,6 +29,7 @@
time I was too famous.''
-- Unknown */
#include "ssl_private.h"
+#include "httpd.h"
#include "mod_ssl.h"
#include "util_md5.h"
#include "scoreboard.h"
Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/ssl_engine_pphrase.c Mon Aug 3 22:48:57 2026 (r1936849)
@@ -780,7 +780,7 @@ static apr_status_t pp_ui_method_cleanup
return APR_SUCCESS;
}
-static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
+UI_METHOD *modssl_get_passphrase_ui(apr_pool_t *p)
{
UI_METHOD *ui_method = UI_create_method("Passphrase UI");
@@ -794,6 +794,21 @@ static UI_METHOD *get_passphrase_ui(apr_
return ui_method;
}
+
+void *modssl_get_passphrase_cb(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *uri)
+{
+ pphrase_cb_arg_t *ppcb = apr_pcalloc(p, sizeof(pphrase_cb_arg_t));
+
+ ppcb->s = s;
+ ppcb->p = p;
+ ppcb->bPassPhraseDialogOnce = TRUE;
+ ppcb->key_id = vhostid;
+ ppcb->pkey_file = uri;
+
+ return ppcb;
+}
#endif
#if MODSSL_HAVE_ENGINE_API
@@ -819,7 +834,7 @@ static apr_status_t modssl_load_keypair_
{
const char *c, *scheme;
ENGINE *e;
- UI_METHOD *ui_method = get_passphrase_ui(ptemp);
+ UI_METHOD *ui_method = modssl_get_passphrase_ui(ptemp);
pphrase_cb_arg_t ppcb;
memset(&ppcb, 0, sizeof ppcb);
@@ -904,7 +919,7 @@ static OSSL_STORE_INFO *modssl_load_stor
const char *uri, int info_type)
{
OSSL_STORE_CTX *sctx;
- UI_METHOD *ui_method = get_passphrase_ui(p);
+ UI_METHOD *ui_method = modssl_get_passphrase_ui(p);
pphrase_cb_arg_t ppcb;
OSSL_STORE_INFO *info = NULL;
Modified: httpd/httpd/trunk/modules/ssl/ssl_private.h
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_private.h Mon Aug 3 22:31:47 2026 (r1936848)
+++ httpd/httpd/trunk/modules/ssl/ssl_private.h Mon Aug 3 22:48:57 2026 (r1936849)
@@ -755,19 +755,23 @@ typedef struct {
* a given vhost */
typedef struct {
/* Lists of configured certs and keys for this server */
+ apr_array_header_t *uris;
apr_array_header_t *cert_files;
apr_array_header_t *key_files;
/** Certificates which specify the set of CA names which should be
* sent in the CertificateRequest message: */
+ const char *ca_name_uri;
const char *ca_name_path;
const char *ca_name_file;
-
+
/* TLS service for this server is suspended */
int service_unavailable;
} modssl_pk_server_t;
typedef struct {
+ /* Lists of configured certs and keys for this proxy */
+ apr_array_header_t *uris;
/** proxy can have any number of cert/key pairs */
const char *cert_file;
const char *cert_path;
@@ -784,6 +788,7 @@ typedef struct {
/** stuff related to authentication that can also be per-dir */
typedef struct {
/** known/trusted CAs */
+ const char *ca_cert_uri;
const char *ca_cert_path;
const char *ca_cert_file;
@@ -822,6 +827,9 @@ typedef struct {
typedef struct {
SSLSrvConfigRec *sc; /** pointer back to server config */
SSL_CTX *ssl_ctx;
+#if MODSSL_HAVE_OPENSSL_STORE
+ OSSL_LIB_CTX *libctx;
+#endif
/** we are one or the other */
modssl_pk_server_t *pks;
@@ -841,6 +849,7 @@ typedef struct {
const char *cert_chain;
/** certificate revocation list */
+ const char *crl_uri;
const char *crl_path;
const char *crl_file;
int crl_check_mask;
@@ -892,6 +901,24 @@ typedef struct {
BOOL ssl_check_peer_expire;
} modssl_ctx_t;
+
+typedef struct {
+ modssl_ctx_t* mctx;
+
+ STACK_OF(X509) *cert_list;
+ STACK_OF(EVP_PKEY) *key_list;
+ STACK_OF(X509) *ca_list;
+
+ int num_certs;
+ int num_ca_certs;
+ int num_intermediate_certs;
+ int num_leaf_certs;
+ int num_server_certs;
+ int num_client_certs;
+ int num_keys;
+
+} modssl_ctx_uri_t;
+
struct SSLSrvConfigRec {
SSLModConfigRec *mc;
ssl_enabled_t enabled;
@@ -963,13 +990,17 @@ const char *ssl_cmd_SSLEngine(cmd_parms
const char *ssl_cmd_SSLECHKeyDir(cmd_parms *cmd, void *dcfg, const char *arg);
#endif
const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *);
+const char *ssl_cmd_SSLCertificateURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCACertificateURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCACertificatePath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCACertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCADNRequestURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCADNRequestFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCARevocationURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *, void *, const char *);
@@ -996,11 +1027,14 @@ const char *ssl_cmd_SSLProxyProtocol(cm
const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *, const char *);
const char *ssl_cmd_SSLProxyVerify(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCACertificateURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCARevocationURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyMachineCertificateURI(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *, void *, const char *);
@@ -1046,7 +1080,7 @@ int ssl_proxy_section_post_conf
apr_pool_t *ptemp, server_rec *s,
ap_conf_vector_t *section_config);
STACK_OF(X509_NAME)
- *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *);
+ *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *, const char *, modssl_ctx_t *);
void ssl_init_Child(apr_pool_t *, server_rec *);
apr_status_t ssl_init_ModuleKill(void *data);
@@ -1181,6 +1215,12 @@ apr_status_t modssl_load_engine_keypair(
const char *certid, const char *keyid,
X509 **pubkey, EVP_PKEY **privkey);
+UI_METHOD *modssl_get_passphrase_ui(apr_pool_t *p);
+void *modssl_get_passphrase_cb(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *uri);
+
+
/** Diffie-Hellman Parameter Support */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH *modssl_dh_from_file(const char *);