summaryrefslogtreecommitdiffstats
path: root/modules/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ssl')
-rw-r--r--modules/ssl/mod_ssl.c162
-rw-r--r--modules/ssl/mod_ssl.h29
-rw-r--r--modules/ssl/mod_ssl_openssl.h49
-rw-r--r--modules/ssl/ssl_engine_config.c56
-rw-r--r--modules/ssl/ssl_engine_init.c723
-rw-r--r--modules/ssl/ssl_engine_io.c341
-rw-r--r--modules/ssl/ssl_engine_kernel.c355
-rw-r--r--modules/ssl/ssl_engine_log.c18
-rw-r--r--modules/ssl/ssl_engine_ocsp.c3
-rw-r--r--modules/ssl/ssl_engine_pphrase.c322
-rw-r--r--modules/ssl/ssl_engine_vars.c52
-rw-r--r--modules/ssl/ssl_private.h171
-rw-r--r--modules/ssl/ssl_scache.c4
-rw-r--r--modules/ssl/ssl_util.c51
-rw-r--r--modules/ssl/ssl_util_ocsp.c6
-rw-r--r--modules/ssl/ssl_util_ssl.c180
-rw-r--r--modules/ssl/ssl_util_ssl.h31
-rw-r--r--modules/ssl/ssl_util_stapling.c190
18 files changed, 1995 insertions, 748 deletions
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index 9fdf9e0..fb66d18 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -25,8 +25,7 @@
*/
#include "ssl_private.h"
-#include "mod_ssl.h"
-#include "mod_ssl_openssl.h"
+
#include "util_md5.h"
#include "util_mutex.h"
#include "ap_provider.h"
@@ -75,11 +74,9 @@ static const command_rec ssl_config_cmds[] = {
SSL_CMD_SRV(SessionCache, TAKE1,
"SSL Session Cache storage "
"('none', 'nonenotnull', 'dbm:/path/to/file')")
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
SSL_CMD_SRV(CryptoDevice, TAKE1,
"SSL external Crypto Device usage "
"('builtin', '...')")
-#endif
SSL_CMD_SRV(RandomSeed, TAKE23,
"SSL Pseudo Random Number Generator (PRNG) seeding source "
"('startup|connect builtin|file:/path|exec:/path [bytes]')")
@@ -94,7 +91,7 @@ static const command_rec ssl_config_cmds[] = {
"Enable FIPS-140 mode "
"(`on', `off')")
SSL_CMD_ALL(CipherSuite, TAKE12,
- "Colon-delimited list of permitted SSL Ciphers, optional preceeded "
+ "Colon-delimited list of permitted SSL Ciphers, optional preceded "
"by protocol identifier ('XXX:...:XXX' - see manual)")
SSL_CMD_SRV(CertificateFile, TAKE1,
"SSL Server Certificate file "
@@ -187,7 +184,7 @@ static const command_rec ssl_config_cmds[] = {
"('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
SSL_CMD_PXY(ProxyCipherSuite, TAKE12,
"SSL Proxy: colon-delimited list of permitted SSL ciphers "
- ", optionally preceeded by protocol specifier ('XXX:...:XXX' - see manual)")
+ ", optionally preceded by protocol specifier ('XXX:...:XXX' - see manual)")
SSL_CMD_PXY(ProxyVerify, TAKE1,
"SSL Proxy: whether to verify the remote certificate "
"('on' or 'off')")
@@ -328,12 +325,17 @@ static int modssl_is_prelinked(void)
static apr_status_t ssl_cleanup_pre_config(void *data)
{
- /*
- * Try to kill the internals of the SSL library.
+#if HAVE_OPENSSL_INIT_SSL || (OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ /* Openssl v1.1+ handles all termination automatically from
+ * OPENSSL_init_ssl(). Do nothing in this case.
*/
-#ifdef HAVE_FIPS
- FIPS_mode_set(0);
-#endif
+
+#else
+ /* Termination below is for legacy Openssl versions v1.0.x and
+ * older.
+ */
+
/* Corresponds to OBJ_create()s */
OBJ_cleanup();
/* Corresponds to OPENSSL_load_builtin_modules() */
@@ -373,12 +375,14 @@ static apr_status_t ssl_cleanup_pre_config(void *data)
if (!modssl_running_statically) {
CRYPTO_cleanup_all_ex_data();
}
+#endif
/*
* TODO: determine somewhere we can safely shove out diagnostics
* (when enabled) at this late stage in the game:
* CRYPTO_mem_leaks_fp(stderr);
*/
+
return APR_SUCCESS;
}
@@ -388,16 +392,23 @@ static int ssl_hook_pre_config(apr_pool_t *pconf,
{
modssl_running_statically = modssl_is_prelinked();
- /* Some OpenSSL internals are allocated per-thread, make sure they
- * are associated to the/our same thread-id until cleaned up.
+#if HAVE_OPENSSL_INIT_SSL || (OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ /* Openssl v1.1+ handles all initialisation automatically, apart
+ * from hints as to how we want to use the library.
+ *
+ * We tell openssl we want to include engine support.
+ */
+ OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
+
+#else
+ /* Configuration below is for legacy versions Openssl v1.0 and
+ * older.
*/
+
#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
ssl_util_thread_id_setup(pconf);
#endif
-
- /* We must register the library in full, to ensure our configuration
- * code can successfully test the SSL environment.
- */
#if MODSSL_USE_OPENSSL_PRE_1_1_API || defined(LIBRESSL_VERSION_NUMBER)
(void)CRYPTO_malloc_init();
#else
@@ -411,6 +422,7 @@ static int ssl_hook_pre_config(apr_pool_t *pconf,
#endif
OpenSSL_add_all_algorithms();
OPENSSL_load_builtin_modules();
+#endif
if (OBJ_txt2nid("id-on-dnsSRV") == NID_undef) {
(void)OBJ_create("1.3.6.1.5.5.7.8.7", "id-on-dnsSRV",
@@ -445,17 +457,30 @@ static int ssl_hook_pre_config(apr_pool_t *pconf,
}
static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
- ap_conf_vector_t *per_dir_config)
+ ap_conf_vector_t *per_dir_config,
+ int reinit)
{
SSLConnRec *sslconn = myConnConfig(c);
- SSLSrvConfigRec *sc;
-
- if (sslconn) {
+ int need_setup = 0;
+
+ /* mod_proxy's (r->)per_dir_config has the lifetime of the request, thus
+ * it uses ssl_engine_set() to reset sslconn->dc when reusing SSL backend
+ * connections, so we must fall through here. But in the case where we are
+ * called from ssl_init_ssl_connection() with no per_dir_config (which also
+ * includes mod_proxy's later run_pre_connection call), sslconn->dc should
+ * be preserved if it's already set.
+ */
+ if (!sslconn) {
+ sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
+ need_setup = 1;
+ }
+ else if (!reinit) {
return sslconn;
}
- sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
-
+ /* Reinit dc in any case because it may be r->per_dir_config scoped
+ * and thus a caller like mod_proxy needs to update it per request.
+ */
if (per_dir_config) {
sslconn->dc = ap_get_module_config(per_dir_config, &ssl_module);
}
@@ -464,12 +489,19 @@ static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
&ssl_module);
}
- sslconn->server = c->base_server;
- sslconn->verify_depth = UNSET;
- sc = mySrvConfig(c->base_server);
- sslconn->cipher_suite = sc->server->auth.cipher_suite;
+ if (need_setup) {
+ sslconn->server = c->base_server;
+ sslconn->verify_depth = UNSET;
+ if (c->outgoing) {
+ sslconn->cipher_suite = sslconn->dc->proxy->auth.cipher_suite;
+ }
+ else {
+ SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
+ sslconn->cipher_suite = sc->server->auth.cipher_suite;
+ }
- myConnConfigSet(c, sslconn);
+ myConnConfigSet(c, sslconn);
+ }
return sslconn;
}
@@ -480,10 +512,11 @@ static int ssl_engine_status(conn_rec *c, SSLConnRec *sslconn)
return DECLINED;
}
if (sslconn) {
+ /* This connection has already been configured. Check what applies. */
if (sslconn->disabled) {
return SUSPENDED;
}
- if (sslconn->is_proxy) {
+ if (c->outgoing) {
if (!sslconn->dc->proxy_enabled) {
return DECLINED;
}
@@ -495,54 +528,48 @@ static int ssl_engine_status(conn_rec *c, SSLConnRec *sslconn)
}
}
else {
- if (mySrvConfig(c->base_server)->enabled != SSL_ENABLED_TRUE) {
+ /* we decline by default for outgoing connections and for incoming
+ * where the base_server is not enabled. */
+ if (c->outgoing || mySrvConfig(c->base_server)->enabled != SSL_ENABLED_TRUE) {
return DECLINED;
}
}
return OK;
}
-static int ssl_engine_set(conn_rec *c,
- ap_conf_vector_t *per_dir_config,
- int proxy, int enable)
+static int ssl_hook_ssl_bind_outgoing(conn_rec *c,
+ ap_conf_vector_t *per_dir_config,
+ int enable_ssl)
{
SSLConnRec *sslconn;
int status;
-
- if (proxy) {
- sslconn = ssl_init_connection_ctx(c, per_dir_config);
- sslconn->is_proxy = 1;
- }
- else {
- sslconn = myConnConfig(c);
+
+ sslconn = ssl_init_connection_ctx(c, per_dir_config, 1);
+ if (sslconn->ssl) {
+ /* we are already bound to this connection. We have rebound
+ * or removed the reference to a previous per_dir_config,
+ * there is nothing more to do. */
+ return OK;
}
status = ssl_engine_status(c, sslconn);
-
- if (proxy && status == DECLINED) {
- if (enable) {
+ if (enable_ssl) {
+ if (status != OK) {
SSLSrvConfigRec *sc = mySrvConfig(sslconn->server);
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01961)
- "SSL Proxy requested for %s but not enabled "
- "[Hint: SSLProxyEngine]", sc->vhost_id);
+ sslconn->disabled = 1;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10272)
+ "SSL Proxy requested for %s but not enabled for us.",
+ sc->vhost_id);
+ }
+ else {
+ sslconn->disabled = 0;
+ return OK;
}
- sslconn->disabled = 1;
}
- else if (sslconn) {
- sslconn->disabled = !enable;
+ else {
+ sslconn->disabled = 1;
}
-
- return status != DECLINED;
-}
-
-static int ssl_proxy_enable(conn_rec *c)
-{
- return ssl_engine_set(c, NULL, 1, 1);
-}
-
-static int ssl_engine_disable(conn_rec *c)
-{
- return ssl_engine_set(c, NULL, 0, 0);
+ return DECLINED;
}
int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
@@ -558,7 +585,7 @@ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
/*
* Create or retrieve SSL context
*/
- sslconn = ssl_init_connection_ctx(c, r ? r->per_dir_config : NULL);
+ sslconn = ssl_init_connection_ctx(c, r ? r->per_dir_config : NULL, 0);
server = sslconn->server;
sc = mySrvConfig(server);
@@ -566,9 +593,9 @@ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
* Seed the Pseudo Random Number Generator (PRNG)
*/
ssl_rand_seed(server, c->pool, SSL_RSCTX_CONNECT,
- sslconn->is_proxy ? "Proxy: " : "Server: ");
+ c->outgoing ? "Proxy: " : "Server: ");
- mctx = myCtxConfig(sslconn, sc);
+ mctx = myConnCtxConfig(c, sc);
/*
* Create a new SSL connection with the configured server SSL context and
@@ -586,7 +613,7 @@ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
return DECLINED; /* XXX */
}
- rc = ssl_run_pre_handshake(c, ssl, sslconn->is_proxy ? 1 : 0);
+ rc = ssl_run_pre_handshake(c, ssl, c->outgoing ? 1 : 0);
if (rc != OK && rc != DECLINED) {
return rc;
}
@@ -718,10 +745,7 @@ static void ssl_register_hooks(apr_pool_t *p)
APR_HOOK_MIDDLE);
ssl_var_register(p);
-
- APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
- APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
- APR_REGISTER_OPTIONAL_FN(ssl_engine_set);
+ ap_hook_ssl_bind_outgoing (ssl_hook_ssl_bind_outgoing, NULL, NULL, APR_HOOK_MIDDLE);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
AUTHZ_PROVIDER_VERSION,
diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h
index 24a65a0..a360911 100644
--- a/modules/ssl/mod_ssl.h
+++ b/modules/ssl/mod_ssl.h
@@ -29,6 +29,7 @@
#include "httpd.h"
#include "http_config.h"
#include "apr_optional.h"
+#include "apr_tables.h" /* for apr_array_header_t */
/* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and
* SSL_DECLARE_DATA with appropriate export and import tags for the platform
@@ -86,6 +87,34 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *,
ap_conf_vector_t *,
int proxy, int enable));
+
+/* Check for availability of new hooks */
+#define SSL_CERT_HOOKS
+#ifdef SSL_CERT_HOOKS
+
+/** Lets others add certificate and key files to the given server.
+ * For each cert a key must also be added.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+/** In case no certificates are available for a server, this
+ * lets other modules add a fallback certificate for the time
+ * being. Regular requests against this server will be answered
+ * with a 503.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+#endif /* SSL_CERT_HOOKS */
#endif /* __MOD_SSL_H__ */
/** @} */
diff --git a/modules/ssl/mod_ssl_openssl.h b/modules/ssl/mod_ssl_openssl.h
index 0fa654a..e251bd9 100644
--- a/modules/ssl/mod_ssl_openssl.h
+++ b/modules/ssl/mod_ssl_openssl.h
@@ -30,14 +30,17 @@
/* OpenSSL headers */
-#ifndef SSL_PRIVATE_H
#include <openssl/opensslv.h>
-#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#include <openssl/macros.h> /* for OPENSSL_API_LEVEL */
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10001000
/* must be defined before including ssl.h */
#define OPENSSL_NO_SSL_INTERN
#endif
#include <openssl/ssl.h>
-#endif
+#include <openssl/evp.h>
+#include <openssl/x509.h>
/**
* init_server hook -- allow SSL_CTX-specific initialization to be performed by
@@ -69,5 +72,45 @@ APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, pre_handshake,
APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, proxy_post_handshake,
(conn_rec *c, SSL *ssl))
+/** On TLS connections that do not relate to a configured virtual host,
+ * allow other modules to provide a X509 certificate and EVP_PKEY to
+ * be used on the connection. This first hook which does not
+ * return DECLINED will determine the outcome. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey))
+
+/** During post_config phase, ask around if someone wants to provide
+ * OCSP stapling status information for the given cert (with the also
+ * provided issuer certificate). The first hook which does not
+ * return DECLINED promises to take responsibility (and respond
+ * in later calls via hook ssl_get_stapling_status).
+ * If no hook takes over, mod_ssl's own stapling implementation will
+ * be applied (if configured).
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer))
+
+/** Anyone answering positive to ssl_init_stapling_status for a
+ * certificate, needs to register here and supply the actual OCSP stapling
+ * status data (OCSP_RESP) for a new connection.
+ * A hook supplying the response data must return APR_SUCCESS.
+ * The data is returned in DER encoded bytes via pder and pderlen. The
+ * returned pointer may be NULL, which indicates that data is (currently)
+ * unavailable.
+ * If DER data is returned, it MUST come from a response with
+ * status OCSP_RESPONSE_STATUS_SUCCESSFUL and V_OCSP_CERTSTATUS_GOOD
+ * or V_OCSP_CERTSTATUS_REVOKED, not V_OCSP_CERTSTATUS_UNKNOWN. This means
+ * errors in OCSP retrieval are to be handled/logged by the hook and
+ * are not done by mod_ssl.
+ * Any DER bytes returned MUST be allocated via malloc() and ownership
+ * passes to mod_ssl. Meaning, the hook must return a malloced copy of
+ * the data it has. mod_ssl (or OpenSSL) will free it.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert))
+
#endif /* __MOD_SSL_OPENSSL_H__ */
/** @} */
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 6c10bb5..9af6f70 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -27,6 +27,7 @@
damned if you don't.''
-- Unknown */
#include "ssl_private.h"
+
#include "util_mutex.h"
#include "ap_provider.h"
@@ -75,6 +76,13 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
mc->stapling_refresh_mutex = NULL;
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ mc->keylog_file = NULL;
+#endif
+#ifdef HAVE_FIPS
+ mc->fips = UNSET;
+#endif
+
apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
apr_pool_cleanup_null,
pool);
@@ -220,9 +228,6 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
#ifdef HAVE_TLSEXT
sc->strict_sni_vhost_check = SSL_ENABLED_UNSET;
#endif
-#ifdef HAVE_FIPS
- sc->fips = UNSET;
-#endif
#ifndef OPENSSL_NO_COMP
sc->compression = UNSET;
#endif
@@ -261,9 +266,11 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
modssl_ctx_t *mrg)
{
if (add->protocol_set) {
+ mrg->protocol_set = 1;
mrg->protocol = add->protocol;
}
else {
+ mrg->protocol_set = base->protocol_set;
mrg->protocol = base->protocol;
}
@@ -393,9 +400,6 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
#ifdef HAVE_TLSEXT
cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
#endif
-#ifdef HAVE_FIPS
- cfgMergeBool(fips);
-#endif
#ifndef OPENSSL_NO_COMP
cfgMergeBool(compression);
#endif
@@ -589,14 +593,15 @@ const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *cmd,
return NULL;
}
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd,
void *dcfg,
const char *arg)
{
SSLModConfigRec *mc = myModConfig(cmd->server);
const char *err;
+#if MODSSL_HAVE_ENGINE_API
ENGINE *e;
+#endif
if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
return err;
@@ -605,13 +610,16 @@ const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd,
if (strcEQ(arg, "builtin")) {
mc->szCryptoDevice = NULL;
}
+#if MODSSL_HAVE_ENGINE_API
else if ((e = ENGINE_by_id(arg))) {
mc->szCryptoDevice = arg;
ENGINE_free(e);
}
+#endif
else {
err = "SSLCryptoDevice: Invalid argument; must be one of: "
"'builtin' (none)";
+#if MODSSL_HAVE_ENGINE_API
e = ENGINE_get_first();
while (e) {
err = apr_pstrcat(cmd->pool, err, ", '", ENGINE_get_id(e),
@@ -620,12 +628,12 @@ const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd,
* on the 'old' e, per the docs in engine.h. */
e = ENGINE_get_next(e);
}
+#endif
return err;
}
return NULL;
}
-#endif
const char *ssl_cmd_SSLRandomSeed(cmd_parms *cmd,
void *dcfg,
@@ -743,7 +751,7 @@ const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg)
const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
{
#ifdef HAVE_FIPS
- SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ SSLModConfigRec *mc = myModConfig(cmd->server);
#endif
const char *err;
@@ -752,9 +760,9 @@ const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
}
#ifdef HAVE_FIPS
- if ((sc->fips != UNSET) && (sc->fips != (BOOL)(flag ? TRUE : FALSE)))
+ if ((mc->fips != UNSET) && (mc->fips != (BOOL)(flag ? TRUE : FALSE)))
return "Conflicting SSLFIPS options, cannot be both On and Off";
- sc->fips = flag ? TRUE : FALSE;
+ mc->fips = flag ? TRUE : FALSE;
#else
if (flag)
return "SSLFIPS invalid, rebuild httpd and openssl compiled for FIPS";
@@ -795,7 +803,7 @@ const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
return NULL;
}
#endif
- return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
+ return apr_pstrcat(cmd->pool, "protocol '", arg1, "' not supported", NULL);
}
#define SSL_FLAGS_CHECK_FILE \
@@ -807,8 +815,14 @@ const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
static const char *ssl_cmd_check_file(cmd_parms *parms,
const char **file)
{
- const char *filepath = ap_server_root_relative(parms->pool, *file);
+ const char *filepath;
+
+ /* 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;
+ }
+ filepath = ap_server_root_relative(parms->pool, *file);
if (!filepath) {
return apr_pstrcat(parms->pool, parms->cmd->name,
": Invalid file path ", *file, NULL);
@@ -847,10 +861,12 @@ const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
}
}
sc->compression = flag ? TRUE : FALSE;
- return NULL;
#else
- return "Setting Compression mode unsupported; not implemented by the SSL library";
+ if (flag) {
+ return "Setting Compression mode unsupported; not implemented by the SSL library";
+ }
#endif
+ return NULL;
}
const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
@@ -916,7 +932,9 @@ const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd,
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
const char *err;
- if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ /* Only check for non-ENGINE based certs. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
return err;
}
@@ -932,7 +950,9 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd,
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
const char *err;
- if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ /* Check keyfile exists for non-ENGINE keys. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
return err;
}
@@ -1549,7 +1569,7 @@ const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
return NULL;
}
#endif
- return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
+ return apr_pstrcat(cmd->pool, "protocol '", arg1, "' not supported", NULL);
}
const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd,
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 18d18c6..c2ec048 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -27,15 +27,36 @@
see Recursive.''
-- Unknown */
#include "ssl_private.h"
-#include "mod_ssl.h"
-#include "mod_ssl_openssl.h"
+
#include "mpm_common.h"
#include "mod_md.h"
+static apr_status_t ssl_init_ca_cert_path(server_rec *, apr_pool_t *, const char *,
+ STACK_OF(X509_NAME) *, STACK_OF(X509_INFO) *);
+
APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_server,
(server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx),
(s,p,is_proxy,ctx), OK, DECLINED)
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey),
+ (c, server_name, pcert, pkey),
+ DECLINED, DECLINED)
+
+
/* _________________________________________________________________
**
** Module Initialization
@@ -69,7 +90,6 @@ static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
return 1;
}
-#endif
/*
* Grab well-defined DH parameters from OpenSSL, see the BN_get_rfc*
@@ -149,40 +169,64 @@ DH *modssl_get_dh_params(unsigned keylen)
return NULL; /* impossible to reach. */
}
+#endif
-static void ssl_add_version_components(apr_pool_t *p,
+static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf,
server_rec *s)
{
- char *modver = ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_INTERFACE");
- char *libver = ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_LIBRARY");
- char *incver = ssl_var_lookup(p, s, NULL, NULL,
+ char *modver = ssl_var_lookup(ptemp, s, NULL, NULL, "SSL_VERSION_INTERFACE");
+ char *libver = ssl_var_lookup(ptemp, s, NULL, NULL, "SSL_VERSION_LIBRARY");
+ char *incver = ssl_var_lookup(ptemp, s, NULL, NULL,
"SSL_VERSION_LIBRARY_INTERFACE");
- ap_add_version_component(p, libver);
+ ap_add_version_component(pconf, libver);
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01876)
"%s compiled against Server: %s, Library: %s",
modver, AP_SERVER_BASEVERSION, incver);
}
-/**************************************************************************************************/
-/* Managed Domains Interface */
-
-static APR_OPTIONAL_FN_TYPE(md_is_managed) *md_is_managed;
-static APR_OPTIONAL_FN_TYPE(md_get_certificate) *md_get_certificate;
-static APR_OPTIONAL_FN_TYPE(md_is_challenge) *md_is_challenge;
+/* _________________________________________________________________
+**
+** Let other answer special connection attempts.
+** Used in ACME challenge handling by mod_md.
+** _________________________________________________________________
+*/
int ssl_is_challenge(conn_rec *c, const char *servername,
- X509 **pcert, EVP_PKEY **pkey)
+ X509 **pcert, EVP_PKEY **pkey,
+ const char **pcert_pem, const char **pkey_pem)
{
- if (md_is_challenge) {
- return md_is_challenge(c, servername, pcert, pkey);
- }
*pcert = NULL;
*pkey = NULL;
+ *pcert_pem = *pkey_pem = NULL;
+ if (ap_ssl_answer_challenge(c, servername, pcert_pem, pkey_pem)) {
+ return 1;
+ }
+ else if (OK == ssl_run_answer_challenge(c, servername, pcert, pkey)) {
+ return 1;
+ }
return 0;
}
+#ifdef HAVE_FIPS
+static apr_status_t modssl_fips_cleanup(void *data)
+{
+ modssl_fips_enable(0);
+ return APR_SUCCESS;
+}
+#endif
+
+static APR_INLINE unsigned long modssl_runtime_lib_version(void)
+{
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ return SSLeay();
+#else
+ return OpenSSL_version_num();
+#endif
+}
+
+
/*
* Per-module initialization
*/
@@ -190,18 +234,22 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp,
server_rec *base_server)
{
+ unsigned long runtime_lib_version = modssl_runtime_lib_version();
SSLModConfigRec *mc = myModConfig(base_server);
SSLSrvConfigRec *sc;
server_rec *s;
apr_status_t rv;
apr_array_header_t *pphrases;
- if (SSLeay() < MODSSL_LIBRARY_VERSION) {
+ AP_DEBUG_ASSERT(mc);
+
+ if (runtime_lib_version < MODSSL_LIBRARY_VERSION) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01882)
"Init: this version of mod_ssl was compiled against "
- "a newer library (%s, version currently loaded is %s)"
+ "a newer library (%s (%s), version currently loaded is 0x%lX)"
" - may result in undefined or erroneous behavior",
- MODSSL_LIBRARY_TEXT, MODSSL_LIBRARY_DYNTEXT);
+ MODSSL_LIBRARY_TEXT, MODSSL_LIBRARY_DYNTEXT,
+ runtime_lib_version);
}
/* We initialize mc->pid per-process in the child init,
@@ -223,16 +271,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
ssl_config_global_create(base_server); /* just to avoid problems */
ssl_config_global_fix(mc);
- /* Initialize our interface to mod_md, if it is loaded
- */
- md_is_managed = APR_RETRIEVE_OPTIONAL_FN(md_is_managed);
- md_get_certificate = APR_RETRIEVE_OPTIONAL_FN(md_get_certificate);
- md_is_challenge = APR_RETRIEVE_OPTIONAL_FN(md_is_challenge);
- if (!md_is_managed || !md_get_certificate) {
- md_is_managed = NULL;
- md_get_certificate = NULL;
- }
-
/*
* try to fix the configuration and open the dedicated SSL
* logfile as early as possible
@@ -279,12 +317,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
if (sc->server && sc->server->pphrase_dialog_type == SSL_PPTYPE_UNSET) {
sc->server->pphrase_dialog_type = SSL_PPTYPE_BUILTIN;
}
-
-#ifdef HAVE_FIPS
- if (sc->fips == UNSET) {
- sc->fips = FALSE;
- }
-#endif
}
#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
@@ -294,13 +326,11 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
/*
* SSL external crypto device ("engine") support
*/
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
if ((rv = ssl_init_Engine(base_server, p)) != APR_SUCCESS) {
return rv;
}
-#endif
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01883)
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, APLOGNO(01883)
"Init: Initialized %s library", MODSSL_LIBRARY_NAME);
/*
@@ -311,22 +341,28 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
ssl_rand_seed(base_server, ptemp, SSL_RSCTX_STARTUP, "Init: ");
#ifdef HAVE_FIPS
- if(sc->fips) {
- if (!FIPS_mode()) {
- if (FIPS_mode_set(1)) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01884)
- "Operating in SSL FIPS mode");
- }
- else {
- ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01885) "FIPS mode failed");
- ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
- return ssl_die(s);
- }
+ if (!modssl_fips_is_enabled() && mc->fips == TRUE) {
+ if (!modssl_fips_enable(1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, base_server, APLOGNO(01885)
+ "Could not enable FIPS mode");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, base_server);
+ return ssl_die(base_server);
}
+
+ apr_pool_cleanup_register(p, NULL, modssl_fips_cleanup,
+ apr_pool_cleanup_null);
+ }
+
+ /* Log actual FIPS mode which the SSL library is operating under,
+ * which may have been set outside of the mod_ssl
+ * configuration. */
+ if (modssl_fips_is_enabled()) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, base_server, APLOGNO(01884)
+ MODSSL_LIBRARY_NAME " has FIPS mode enabled");
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01886)
- "SSL FIPS mode disabled");
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(01886)
+ MODSSL_LIBRARY_NAME " has FIPS mode disabled");
}
#endif
@@ -409,25 +445,48 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
* Announce mod_ssl and SSL library in HTTP Server field
* as ``mod_ssl/X.X.X OpenSSL/X.X.X''
*/
- ssl_add_version_components(p, base_server);
+ ssl_add_version_components(ptemp, p, base_server);
modssl_init_app_data2_idx(); /* for modssl_get_app_data2() at request time */
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
init_dh_params();
-#if !MODSSL_USE_OPENSSL_PRE_1_1_API
+#else
init_bio_methods();
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ {
+ const char *logfn = getenv("SSLKEYLOGFILE");
+
+ if (logfn) {
+ rv = apr_file_open(&mc->keylog_file, logfn,
+ APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_APPEND|APR_FOPEN_LARGEFILE,
+ APR_FPROT_UREAD|APR_FPROT_UWRITE,
+ mc->pPool);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s, APLOGNO(10226)
+ "Could not open log file '%s' configured via SSLKEYLOGFILE",
+ logfn);
+ return rv;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10227)
+ "Init: Logging SSL private key material to %s", logfn);
+ }
+ }
+#endif
+
return OK;
}
/*
* Support for external a Crypto Device ("engine"), usually
- * a hardware accellerator card for crypto operations.
+ * a hardware accelerator card for crypto operations.
*/
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
apr_status_t ssl_init_Engine(server_rec *s, apr_pool_t *p)
{
+#if MODSSL_HAVE_ENGINE_API
SSLModConfigRec *mc = myModConfig(s);
ENGINE *e;
@@ -459,10 +518,9 @@ apr_status_t ssl_init_Engine(server_rec *s, apr_pool_t *p)
ENGINE_free(e);
}
-
+#endif
return APR_SUCCESS;
}
-#endif
#ifdef HAVE_TLSEXT
static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
@@ -479,7 +537,9 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
"Configuring TLS extension handling");
/*
- * Server name indication (SNI)
+ * The Server Name Indication (SNI) provided by the ClientHello can be
+ * used to select the right (name-based-)vhost and its SSL configuration
+ * before the handshake takes place.
*/
if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
ssl_callback_ServerNameIndication) ||
@@ -491,6 +551,16 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
return ssl_die(s);
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * The ClientHello callback also allows to retrieve the SNI, but since it
+ * runs at the earliest possible connection stage we can even set the TLS
+ * protocol version(s) according to the selected (name-based-)vhost, which
+ * is not possible at the SNI callback stage (due to OpenSSL internals).
+ */
+ SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL);
+#endif
+
#ifdef HAVE_OCSP_STAPLING
/*
* OCSP Stapling support, status_request extension
@@ -659,9 +729,9 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
#else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
/* We first determine the maximum protocol version we should provide */
#if SSL_HAVE_PROTOCOL_TLSV1_3
- if (SSL_HAVE_PROTOCOL_TLSV1_3 && (protocol & SSL_PROTOCOL_TLSV1_3)) {
+ if (protocol & SSL_PROTOCOL_TLSV1_3) {
prot = TLS1_3_VERSION;
- } else
+ } else
#endif
if (protocol & SSL_PROTOCOL_TLSV1_2) {
prot = TLS1_2_VERSION;
@@ -767,6 +837,20 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
* https://github.com/openssl/openssl/issues/7178 */
SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ if (mctx->sc->mc->keylog_file) {
+ SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
+ }
+#endif
+
+#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
+ /* For server-side SSL_CTX, enable ignoring unexpected EOF */
+ /* (OpenSSL 1.1.1 behavioural compatibility).. */
+ if (!mctx->pkp) {
+ SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
+ }
+#endif
return APR_SUCCESS;
}
@@ -795,7 +879,11 @@ static void ssl_init_ctx_callbacks(server_rec *s,
{
SSL_CTX *ctx = mctx->ssl_ctx;
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ /* Note that for OpenSSL>=1.1, auto selection is enabled via
+ * SSL_CTX_set_dh_auto(,1) if no parameter is configured. */
SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH);
+#endif
SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
@@ -804,6 +892,23 @@ static void ssl_init_ctx_callbacks(server_rec *s,
#endif
}
+static APR_INLINE
+int modssl_CTX_load_verify_locations(SSL_CTX *ctx,
+ const char *file,
+ const char *path)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (!SSL_CTX_load_verify_locations(ctx, file, path))
+ return 0;
+#else
+ if (file && !SSL_CTX_load_verify_file(ctx, file))
+ return 0;
+ if (path && !SSL_CTX_load_verify_dir(ctx, path))
+ return 0;
+#endif
+ return 1;
+}
+
static apr_status_t ssl_init_ctx_verify(server_rec *s,
apr_pool_t *p,
apr_pool_t *ptemp,
@@ -844,10 +949,8 @@ static apr_status_t ssl_init_ctx_verify(server_rec *s,
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
"Configuring client authentication");
- if (!SSL_CTX_load_verify_locations(ctx,
- mctx->auth.ca_cert_file,
- mctx->auth.ca_cert_path))
- {
+ 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)
"Unable to configure verify locations "
"for client authentication");
@@ -932,6 +1035,23 @@ static apr_status_t ssl_init_ctx_cipher_suite(server_rec *s,
return APR_SUCCESS;
}
+static APR_INLINE
+int modssl_X509_STORE_load_locations(X509_STORE *store,
+ const char *file,
+ const char *path)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (!X509_STORE_load_locations(store, file, path))
+ return 0;
+#else
+ if (file && !X509_STORE_load_file(store, file))
+ return 0;
+ if (path && !X509_STORE_load_path(store, path))
+ return 0;
+#endif
+ return 1;
+}
+
static apr_status_t ssl_init_ctx_crl(server_rec *s,
apr_pool_t *p,
apr_pool_t *ptemp,
@@ -970,8 +1090,8 @@ static apr_status_t ssl_init_ctx_crl(server_rec *s,
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01900)
"Configuring certificate revocation facility");
- if (!store || !X509_STORE_load_locations(store, mctx->crl_file,
- mctx->crl_path)) {
+ if (!store || !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);
@@ -1200,6 +1320,22 @@ static int ssl_no_passwd_prompt_cb(char *buf, int size, int rwflag,
return 0;
}
+/* 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
+ * X509_check_private_key(). This macro is passed the last error code
+ * off the OpenSSL stack and evaluates to true only for the first
+ * case. With OpenSSL < 3 the second case is identifiable by the
+ * function code, but function codes are not used from 3.0. */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_FUNC(ec) != X509_F_X509_CHECK_PRIVATE_KEY)
+#else
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_LIB(ec) != ERR_LIB_X509 \
+ || (ERR_GET_REASON(ec) != X509_R_KEY_TYPE_MISMATCH \
+ && ERR_GET_REASON(ec) != X509_R_KEY_VALUES_MISMATCH \
+ && ERR_GET_REASON(ec) != X509_R_UNKNOWN_KEY_TYPE))
+#endif
+
static apr_status_t ssl_init_server_certs(server_rec *s,
apr_pool_t *p,
apr_pool_t *ptemp,
@@ -1209,15 +1345,10 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
SSLModConfigRec *mc = myModConfig(s);
const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile;
int i;
- X509 *cert;
- DH *dhparams;
+ EVP_PKEY *pkey;
#ifdef HAVE_ECC
- EC_GROUP *ecparams = NULL;
- int nid;
- EC_KEY *eckey = NULL;
-#endif
-#ifndef HAVE_SSL_CONF_CMD
- SSL *ssl;
+ EC_GROUP *ecgroup = NULL;
+ int curve_nid = 0;
#endif
/* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
@@ -1228,12 +1359,18 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
(certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
const char *));
i++) {
+ X509 *cert = NULL;
+ const char *engine_certfile = NULL;
+
key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
ERR_clear_error();
/* first the certificate (public key) */
- if (mctx->cert_chain) {
+ if (modssl_is_engine_id(certfile)) {
+ engine_certfile = certfile;
+ }
+ else if (mctx->cert_chain) {
if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
SSL_FILETYPE_PEM) < 1)) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
@@ -1262,12 +1399,43 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
ERR_clear_error();
- if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
- SSL_FILETYPE_PEM) < 1) &&
- (ERR_GET_FUNC(ERR_peek_last_error())
- != X509_F_X509_CHECK_PRIVATE_KEY)) {
+ if (modssl_is_engine_id(keyfile)) {
+ apr_status_t rv;
+
+ if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id,
+ engine_certfile, keyfile,
+ &cert, &pkey))) {
+ return rv;
+ }
+
+ 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);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the cert. */
+ X509_free(cert);
+ }
+
+ 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);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the key */
+ EVP_PKEY_free(pkey);
+ }
+ else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+ SSL_FILETYPE_PEM) < 1)
+ && CHECK_PRIVKEY_ERROR(ERR_peek_last_error())) {
ssl_asn1_t *asn1;
- EVP_PKEY *pkey;
const unsigned char *ptr;
ERR_clear_error();
@@ -1304,22 +1472,21 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
* assume that if SSL_CONF is available, it's OpenSSL 1.0.2 or later,
* and SSL_CTX_get0_certificate is implemented.)
*/
- if (!(cert = SSL_CTX_get0_certificate(mctx->ssl_ctx))) {
+ cert = SSL_CTX_get0_certificate(mctx->ssl_ctx);
#else
- ssl = SSL_new(mctx->ssl_ctx);
- if (ssl) {
- /* Workaround bug in SSL_get_certificate in OpenSSL 0.9.8y */
- SSL_set_connect_state(ssl);
- cert = SSL_get_certificate(ssl);
+ {
+ SSL *ssl = SSL_new(mctx->ssl_ctx);
+ if (ssl) {
+ /* Workaround bug in SSL_get_certificate in OpenSSL 0.9.8y */
+ SSL_set_connect_state(ssl);
+ cert = SSL_get_certificate(ssl);
+ SSL_free(ssl);
+ }
}
- if (!ssl || !cert) {
#endif
+ if (!cert) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02566)
"Unable to retrieve certificate %s", key_id);
-#ifndef HAVE_SSL_CONF_CMD
- if (ssl)
- SSL_free(ssl);
-#endif
return APR_EGENERAL;
}
@@ -1334,18 +1501,13 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
* loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
* later, we defer to the code in ssl_init_server_ctx.
*/
- if ((mctx->stapling_enabled == TRUE) &&
- !ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
+ if (!ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
"Unable to configure certificate %s for stapling",
key_id);
}
#endif
-#ifndef HAVE_SSL_CONF_CMD
- SSL_free(ssl);
-#endif
-
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02568)
"Certificate and private key %s configured from %s and %s",
key_id, certfile, keyfile);
@@ -1354,27 +1516,69 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
/*
* Try to read DH parameters from the (first) SSLCertificateFile
*/
- if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
- (dhparams = ssl_dh_GetParamFromFile(certfile))) {
- SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
- "Custom DH parameters (%d bits) for %s loaded from %s",
- DH_bits(dhparams), vhost_id, certfile);
- DH_free(dhparams);
+ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
+ if (certfile && !modssl_is_engine_id(certfile)) {
+ int done = 0, num_bits = 0;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ DH *dh = modssl_dh_from_file(certfile);
+ if (dh) {
+ num_bits = DH_bits(dh);
+ SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dh);
+ DH_free(dh);
+ done = 1;
+ }
+#else
+ pkey = modssl_dh_pkey_from_file(certfile);
+ if (pkey) {
+ num_bits = EVP_PKEY_get_bits(pkey);
+ if (!SSL_CTX_set0_tmp_dh_pkey(mctx->ssl_ctx, pkey)) {
+ EVP_PKEY_free(pkey);
+ }
+ else {
+ done = 1;
+ }
+ }
+#endif
+ if (done) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
+ "Custom DH parameters (%d bits) for %s loaded from %s",
+ num_bits, vhost_id, certfile);
+ }
+ }
+#if !MODSSL_USE_OPENSSL_PRE_1_1_API
+ else {
+ /* If no parameter is manually configured, enable auto
+ * selection. */
+ SSL_CTX_set_dh_auto(mctx->ssl_ctx, 1);
}
+#endif
#ifdef HAVE_ECC
/*
* Similarly, try to read the ECDH curve name from SSLCertificateFile...
*/
- if ((certfile != NULL) &&
- (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
- (nid = EC_GROUP_get_curve_name(ecparams)) &&
- (eckey = EC_KEY_new_by_curve_name(nid))) {
- SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
- "ECDH curve %s for %s specified in %s",
- OBJ_nid2sn(nid), vhost_id, certfile);
+ if (certfile && !modssl_is_engine_id(certfile)
+ && (ecgroup = modssl_ec_group_from_file(certfile))
+ && (curve_nid = EC_GROUP_get_curve_name(ecgroup))) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(curve_nid);
+ if (eckey) {
+ SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+ EC_KEY_free(eckey);
+ }
+ else {
+ curve_nid = 0;
+ }
+#else
+ if (!SSL_CTX_set1_curves(mctx->ssl_ctx, &curve_nid, 1)) {
+ curve_nid = 0;
+ }
+#endif
+ if (curve_nid) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
+ "ECDH curve %s for %s specified in %s",
+ OBJ_nid2sn(curve_nid), vhost_id, certfile);
+ }
}
/*
* ...otherwise, enable auto curve selection (OpenSSL 1.0.2)
@@ -1382,18 +1586,20 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
* ECDH is always enabled in 1.1.0 unless excluded from SSLCipherList
*/
#if MODSSL_USE_OPENSSL_PRE_1_1_API
- else {
+ if (!curve_nid) {
#if defined(SSL_CTX_set_ecdh_auto)
SSL_CTX_set_ecdh_auto(mctx->ssl_ctx, 1);
#else
- eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ if (eckey) {
+ SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+ EC_KEY_free(eckey);
+ }
#endif
}
#endif
/* OpenSSL assures us that _free() is NULL-safe */
- EC_KEY_free(eckey);
- EC_GROUP_free(ecparams);
+ EC_GROUP_free(ecgroup);
#endif
return APR_SUCCESS;
@@ -1411,6 +1617,7 @@ static apr_status_t ssl_init_ticket_key(server_rec *s,
char buf[TLSEXT_TICKET_KEY_LEN];
char *path;
modssl_ticket_key_t *ticket_key = mctx->ticket_key;
+ int res;
if (!ticket_key->file_path) {
return APR_SUCCESS;
@@ -1438,11 +1645,22 @@ static apr_status_t ssl_init_ticket_key(server_rec *s,
}
memcpy(ticket_key->key_name, buf, 16);
- memcpy(ticket_key->hmac_secret, buf + 16, 16);
memcpy(ticket_key->aes_key, buf + 32, 16);
-
- if (!SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx,
- ssl_callback_SessionTicket)) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ memcpy(ticket_key->hmac_secret, buf + 16, 16);
+ res = SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx,
+ ssl_callback_SessionTicket);
+#else
+ ticket_key->mac_params[0] =
+ OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, buf + 16, 16);
+ ticket_key->mac_params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, "sha256", 0);
+ ticket_key->mac_params[2] =
+ OSSL_PARAM_construct_end();
+ res = SSL_CTX_set_tlsext_ticket_key_evp_cb(mctx->ssl_ctx,
+ ssl_callback_SessionTicket);
+#endif
+ if (!res) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01913)
"Unable to initialize TLS session ticket key callback "
"(incompatible OpenSSL version?)");
@@ -1493,8 +1711,12 @@ static apr_status_t ssl_init_proxy_certs(server_rec *s,
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 */
-#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
+ ap_assert(store != NULL); /* safe to assume always non-NULL? */
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(LIBRESSL_VERSION_NUMBER)
/* For OpenSSL >=1.1.1, turn on client cert support which is
* otherwise turned off by default (by design).
* https://github.com/openssl/openssl/issues/6933 */
@@ -1515,42 +1737,31 @@ static apr_status_t ssl_init_proxy_certs(server_rec *s,
}
if (pkp->cert_path) {
- apr_dir_t *dir;
- apr_finfo_t dirent;
- apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
-
- if (apr_dir_open(&dir, pkp->cert_path, ptemp) == APR_SUCCESS) {
- while ((apr_dir_read(&dirent, finfo_flags, dir)) == APR_SUCCESS) {
- const char *fullname;
-
- if (dirent.filetype == APR_DIR) {
- continue; /* don't try to load directories */
- }
-
- fullname = apr_pstrcat(ptemp,
- pkp->cert_path, "/", dirent.name,
- NULL);
- load_x509_info(ptemp, sk, fullname);
- }
-
- apr_dir_close(dir);
- }
- }
-
- if ((ncerts = sk_X509_INFO_num(sk)) <= 0) {
- sk_X509_INFO_free(sk);
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02206)
- "no client certs found for SSL proxy");
- return APR_SUCCESS;
+ ssl_init_ca_cert_path(s, ptemp, pkp->cert_path, NULL, sk);
}
/* Check that all client certs have got certificates and private
- * keys. */
- for (n = 0; n < ncerts; n++) {
+ * keys. Note the number of certs in the stack may decrease
+ * during the loop. */
+ for (n = 0; n < sk_X509_INFO_num(sk); n++) {
X509_INFO *inf = sk_X509_INFO_value(sk, n);
+ int has_privkey = inf->x_pkey && inf->x_pkey->dec_pkey;
+
+ /* For a lone certificate in the file, trust it as a
+ * CA/intermediate certificate. */
+ if (inf->x509 && !has_privkey && !inf->enc_data) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, ptemp, s, inf->x509,
+ APLOGNO(10261) "Trusting non-leaf certificate");
+ X509_STORE_add_cert(store, inf->x509); /* increments inf->x509 */
+ /* Delete from the stack and iterate again. */
+ X509_INFO_free(inf);
+ sk_X509_INFO_delete(sk, n);
+ n--;
+ addl_chain = 1;
+ continue;
+ }
- if (!inf->x509 || !inf->x_pkey || !inf->x_pkey->dec_pkey ||
- inf->enc_data) {
+ if (!has_privkey || inf->enc_data) {
sk_X509_INFO_free(sk);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, APLOGNO(02252)
"incomplete client cert configured for SSL proxy "
@@ -1567,13 +1778,21 @@ static apr_status_t ssl_init_proxy_certs(server_rec *s,
}
}
+ if ((ncerts = sk_X509_INFO_num(sk)) <= 0) {
+ sk_X509_INFO_free(sk);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02206)
+ "no client certs found for SSL proxy");
+ return APR_SUCCESS;
+ }
+
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02207)
"loaded %d client certs for SSL proxy",
ncerts);
pkp->certs = sk;
-
- if (!pkp->ca_cert_file || !store) {
+ /* 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;
}
@@ -1589,16 +1808,21 @@ static apr_status_t ssl_init_proxy_certs(server_rec *s,
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);
}
- X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
+ 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);
- X509_STORE_CTX_init(sctx, store, inf->x509, NULL);
+ if (!X509_STORE_CTX_init(sctx, store, inf->x509, NULL)) {
+ sk_X509_INFO_free(sk);
+ X509_STORE_CTX_free(sctx);
+ return ssl_die(s);
+ }
/* Attempt to verify the client cert */
if (X509_verify_cert(sctx) != 1) {
@@ -1729,11 +1953,13 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
apr_array_header_t *pphrases)
{
apr_status_t rv;
+ modssl_pk_server_t *pks;
#ifdef HAVE_SSL_CONF_CMD
ssl_ctx_param_t *param = (ssl_ctx_param_t *)sc->server->ssl_ctx_param->elts;
SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
int i;
#endif
+ int n;
/*
* Check for problematic re-initializations
@@ -1745,52 +1971,30 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
return APR_EGENERAL;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10083)
- "Init: (%s) mod_md support is %s.", ssl_util_vhostid(p, s),
- md_is_managed? "available" : "unavailable");
- if (md_is_managed && md_is_managed(s)) {
- modssl_pk_server_t *const pks = sc->server->pks;
- if (pks->cert_files->nelts > 0 || pks->key_files->nelts > 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084)
- "Init: (%s) You configured certificate/key files on this host, but "
- "is is covered by a Managed Domain. You need to remove these directives "
- "for the Managed Domain to take over.", ssl_util_vhostid(p, s));
- }
- else {
- const char *key_file, *cert_file, *chain_file;
-
- key_file = cert_file = chain_file = NULL;
-
- if (md_get_certificate) {
- rv = md_get_certificate(s, p, &key_file, &cert_file);
- }
- else {
- rv = APR_ENOTIMPL;
- }
-
- if (key_file && cert_file) {
- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
- "%s: installing key=%s, cert=%s, chain=%s",
- ssl_util_vhostid(p, s), key_file, cert_file, chain_file);
- APR_ARRAY_PUSH(pks->key_files, const char *) = key_file;
- APR_ARRAY_PUSH(pks->cert_files, const char *) = cert_file;
- sc->server->cert_chain = chain_file;
- }
-
- if (APR_STATUS_IS_EAGAIN(rv)) {
- /* Managed Domain not ready yet. This is not a reason to fail the config */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
- "Init: %s will respond with '503 Service Unavailable' for now. This "
- "host is part of a Managed Domain, but no SSL certificate is "
- "available (yet).", ssl_util_vhostid(p, s));
- pks->service_unavailable = 1;
- }
- else if (rv != APR_SUCCESS) {
- return rv;
- }
+ /* Allow others to provide certificate files */
+ pks = sc->server->pks;
+ n = pks->cert_files->nelts;
+ ap_ssl_add_cert_files(s, p, pks->cert_files, pks->key_files);
+ ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files);
+
+ if (apr_is_empty_array(pks->cert_files)) {
+ /* does someone propose a certiciate to fall back on here? */
+ ap_ssl_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+ ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+ if (n < pks->cert_files->nelts) {
+ pks->service_unavailable = 1;
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
+ "Init: %s will respond with '503 Service Unavailable' for now. There "
+ "are no SSL certificates configured and no other module contributed any.",
+ ssl_util_vhostid(p, s));
}
}
+ if (n < pks->cert_files->nelts) {
+ /* 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;
}
@@ -1841,7 +2045,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
* (late) point makes sure that we catch both certificates loaded
* via SSLCertificateFile and SSLOpenSSLConfCmd Certificate.
*/
- if (sc->server->stapling_enabled == TRUE) {
+ do {
X509 *cert;
int i = 0;
int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
@@ -1858,7 +2062,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
SSL_CERT_SET_NEXT);
i++;
}
- }
+ } while(0);
#endif
#ifdef HAVE_TLS_SESSION_TICKETS
@@ -2038,50 +2242,38 @@ int ssl_proxy_section_post_config(apr_pool_t *p, apr_pool_t *plog,
return OK;
}
-static int ssl_init_FindCAList_X509NameCmp(const X509_NAME * const *a,
- const X509_NAME * const *b)
-{
- return(X509_NAME_cmp(*a, *b));
-}
-
-static void ssl_init_PushCAList(STACK_OF(X509_NAME) *ca_list,
- server_rec *s, apr_pool_t *ptemp,
- const char *file)
+static apr_status_t ssl_init_ca_cert_path(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *path,
+ STACK_OF(X509_NAME) *ca_list,
+ STACK_OF(X509_INFO) *xi_list)
{
- int n;
- STACK_OF(X509_NAME) *sk;
+ apr_dir_t *dir;
+ apr_finfo_t direntry;
+ apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
- sk = (STACK_OF(X509_NAME) *)
- SSL_load_client_CA_file(file);
-
- if (!sk) {
- return;
+ if (!path || (!ca_list && !xi_list) ||
+ (apr_dir_open(&dir, path, ptemp) != APR_SUCCESS)) {
+ return APR_EGENERAL;
}
- for (n = 0; n < sk_X509_NAME_num(sk); n++) {
- X509_NAME *name = sk_X509_NAME_value(sk, n);
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02209)
- "CA certificate: %s",
- modssl_X509_NAME_to_string(ptemp, name, 0));
-
- /*
- * note that SSL_load_client_CA_file() checks for duplicates,
- * but since we call it multiple times when reading a directory
- * we must also check for duplicates ourselves.
- */
-
- if (sk_X509_NAME_find(ca_list, name) < 0) {
- /* this will be freed when ca_list is */
- sk_X509_NAME_push(ca_list, name);
+ while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) {
+ const char *file;
+ if (direntry.filetype == APR_DIR) {
+ continue; /* don't try to load directories */
}
- else {
- /* need to free this ourselves, else it will leak */
- X509_NAME_free(name);
+ file = apr_pstrcat(ptemp, path, "/", direntry.name, NULL);
+ if (ca_list) {
+ SSL_add_file_cert_subjects_to_stack(ca_list, file);
+ }
+ if (xi_list) {
+ load_x509_info(ptemp, xi_list, file);
}
}
- sk_X509_NAME_free(sk);
+ apr_dir_close(dir);
+
+ return APR_SUCCESS;
}
STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
@@ -2089,19 +2281,13 @@ STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
const char *ca_file,
const char *ca_path)
{
- STACK_OF(X509_NAME) *ca_list;
-
- /*
- * Start with a empty stack/list where new
- * entries get added in sorted order.
- */
- ca_list = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp);
+ STACK_OF(X509_NAME) *ca_list = sk_X509_NAME_new_null();;
/*
* Process CA certificate bundle file
*/
if (ca_file) {
- ssl_init_PushCAList(ca_list, s, ptemp, 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.
@@ -2116,37 +2302,15 @@ STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
/*
* Process CA certificate path files
*/
- if (ca_path) {
- apr_dir_t *dir;
- apr_finfo_t direntry;
- apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
- apr_status_t rv;
-
- if ((rv = apr_dir_open(&dir, ca_path, ptemp)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02211)
- "Failed to open Certificate Path `%s'",
- ca_path);
- sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
- return NULL;
- }
-
- while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) {
- const char *file;
- if (direntry.filetype == APR_DIR) {
- continue; /* don't try to load directories */
- }
- file = apr_pstrcat(ptemp, ca_path, "/", direntry.name, NULL);
- ssl_init_PushCAList(ca_list, s, ptemp, file);
- }
-
- apr_dir_close(dir);
+ if (ca_path &&
+ ssl_init_ca_cert_path(s, ptemp,
+ ca_path, ca_list, NULL) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02211)
+ "Failed to open Certificate Path `%s'", ca_path);
+ sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
+ return NULL;
}
- /*
- * Cleanup
- */
- (void) sk_X509_NAME_set_cmp_func(ca_list, NULL);
-
return ca_list;
}
@@ -2192,10 +2356,11 @@ apr_status_t ssl_init_ModuleKill(void *data)
}
-#if !MODSSL_USE_OPENSSL_PRE_1_1_API
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ free_dh_params();
+#else
free_bio_methods();
#endif
- free_dh_params();
return APR_SUCCESS;
}
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 6da8f10..b91f784 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -28,8 +28,7 @@
core keeps dumping.''
-- Unknown */
#include "ssl_private.h"
-#include "mod_ssl.h"
-#include "mod_ssl_openssl.h"
+
#include "apr_date.h"
APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, proxy_post_handshake,
@@ -152,6 +151,9 @@ static int bio_filter_out_flush(BIO *bio)
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
apr_bucket *e;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
+ "bio_filter_out_write: flush");
+
AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(outctx->bb));
e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
@@ -191,6 +193,10 @@ static int bio_filter_destroy(BIO *bio)
static int bio_filter_out_read(BIO *bio, char *out, int outl)
{
/* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_read");
+ AP_DEBUG_ASSERT(0);
return -1;
}
@@ -208,6 +214,9 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
return -1;
}
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
+ "bio_filter_out_write: %i bytes", inl);
+
/* Use a transient bucket for the output data - any downstream
* filter must setaside if necessary. */
e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc);
@@ -287,12 +296,20 @@ static long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
static int bio_filter_out_gets(BIO *bio, char *buf, int size)
{
/* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_gets");
+ AP_DEBUG_ASSERT(0);
return -1;
}
static int bio_filter_out_puts(BIO *bio, const char *str)
{
/* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_puts");
+ AP_DEBUG_ASSERT(0);
return -1;
}
@@ -527,22 +544,46 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
static int bio_filter_in_write(BIO *bio, const char *in, int inl)
{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_write");
+ AP_DEBUG_ASSERT(0);
return -1;
}
static int bio_filter_in_puts(BIO *bio, const char *str)
{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_puts");
+ AP_DEBUG_ASSERT(0);
return -1;
}
static int bio_filter_in_gets(BIO *bio, char *buf, int size)
{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_gets");
+ AP_DEBUG_ASSERT(0);
return -1;
}
static long bio_filter_in_ctrl(BIO *bio, int cmd, long num, void *ptr)
{
- return -1;
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ switch (cmd) {
+#ifdef BIO_CTRL_EOF
+ case BIO_CTRL_EOF:
+ return inctx->rc == APR_EOF;
+#endif
+ default:
+ break;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: bio_filter_in_ctrl() should not be called with cmd=%i",
+ cmd);
+ return 0;
}
#if MODSSL_USE_OPENSSL_PRE_1_1_API
@@ -567,7 +608,7 @@ static BIO_METHOD bio_filter_in_method = {
bio_filter_in_read,
bio_filter_in_puts, /* puts is never called */
bio_filter_in_gets, /* gets is never called */
- bio_filter_in_ctrl, /* ctrl is never called */
+ bio_filter_in_ctrl, /* ctrl is called for EOF check */
bio_filter_create,
bio_filter_destroy,
NULL
@@ -846,6 +887,9 @@ static apr_status_t ssl_filter_write(ap_filter_t *f,
return APR_EGENERAL;
}
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, f->c,
+ "ssl_filter_write: %"APR_SIZE_T_FMT" bytes", len);
+
/* We rely on SSL_get_error() after the write, which requires an empty error
* queue before the write in order to work properly.
*/
@@ -934,7 +978,7 @@ static apr_status_t ssl_filter_write(ap_filter_t *f,
alloc)
/* Custom apr_status_t error code, used when a plain HTTP request is
- * recevied on an SSL port. */
+ * received on an SSL port. */
#define MODSSL_ERROR_HTTP_ON_HTTPS (APR_OS_START_USERERR + 0)
/* Custom apr_status_t error code, used when the proxy cannot
@@ -1162,11 +1206,13 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
}
server = sslconn->server;
- if (sslconn->is_proxy) {
+ if (c->outgoing) {
#ifdef HAVE_TLSEXT
apr_ipsubnet_t *ip;
#ifdef HAVE_TLS_ALPN
const char *alpn_note;
+ apr_array_header_t *alpn_proposed = NULL;
+ int alpn_empty_ok = 1;
#endif
#endif
const char *hostname_note = apr_table_get(c->notes,
@@ -1182,9 +1228,16 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
#ifdef HAVE_TLS_ALPN
alpn_note = apr_table_get(c->notes, "proxy-request-alpn-protos");
if (alpn_note) {
- char *protos, *s, *p, *last;
+ char *protos, *s, *p, *last, *proto;
apr_size_t len;
+ /* Transform the note into a protocol formatted byte array:
+ * (len-byte proto-char+)*
+ * We need the remote server to agree on one of these, unless 'http/1.1'
+ * is also among our proposals. Because pre-ALPN remotes will speak this.
+ */
+ alpn_proposed = apr_array_make(c->pool, 3, sizeof(const char*));
+ alpn_empty_ok = 0;
s = protos = apr_pcalloc(c->pool, strlen(alpn_note)+1);
p = apr_pstrdup(c->pool, alpn_note);
while ((p = apr_strtok(p, ", ", &last))) {
@@ -1196,6 +1249,11 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
return APR_EGENERAL;
}
+ proto = apr_pstrndup(c->pool, p, len);
+ APR_ARRAY_PUSH(alpn_proposed, const char*) = proto;
+ if (!strcmp("http/1.1", proto)) {
+ alpn_empty_ok = 1;
+ }
*s++ = (unsigned char)len;
while (len--) {
*s++ = *p++;
@@ -1211,6 +1269,8 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(03310)
"error setting alpn protos from '%s'", alpn_note);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
+ /* If ALPN was requested and we cannot do it, we must fail */
+ return MODSSL_ERROR_BAD_GATEWAY;
}
}
#endif /* defined HAVE_TLS_ALPN */
@@ -1238,7 +1298,7 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
}
}
-#endif
+#endif /* defined HAVE_TLSEXT */
if ((n = SSL_connect(filter_ctx->pssl)) <= 0) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02003)
@@ -1267,7 +1327,6 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
((dc->proxy->ssl_check_peer_cn != FALSE) ||
(dc->proxy->ssl_check_peer_name == TRUE)) &&
hostname_note) {
- apr_table_unset(c->notes, "proxy-request-hostname");
if (!cert
|| modssl_X509_match_name(c->pool, cert, hostname_note,
TRUE, server) == FALSE) {
@@ -1284,7 +1343,6 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
hostname = ssl_var_lookup(NULL, server, c, NULL,
"SSL_CLIENT_S_DN_CN");
- apr_table_unset(c->notes, "proxy-request-hostname");
/* Do string match or simplest wildcard match if that
* fails. */
@@ -1304,6 +1362,50 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
}
}
+#ifdef HAVE_TLS_ALPN
+ /* If we proposed ALPN protocol(s), we need to check if the server
+ * agreed to one of them. While <https://www.rfc-editor.org/rfc/rfc7301.txt>
+ * chapter 3.2 says the server SHALL error the handshake in such a case,
+ * the reality is that some servers fall back to their default, e.g. http/1.1.
+ * (we also do this right now)
+ * We need to treat this as an error for security reasons.
+ */
+ if (alpn_proposed && alpn_proposed->nelts > 0) {
+ const char *selected;
+ unsigned int slen;
+
+ SSL_get0_alpn_selected(filter_ctx->pssl, (const unsigned char**)&selected, &slen);
+ if (!selected || !slen) {
+ /* No ALPN selection reported by the remote server. This could mean
+ * it does not support ALPN (old server) or that it does not support
+ * any of our proposals (Apache itself up to 2.4.48 at least did that). */
+ if (!alpn_empty_ok) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10273)
+ "SSL Proxy: Peer did not select any of our ALPN protocols [%s].",
+ alpn_note);
+ proxy_ssl_check_peer_ok = FALSE;
+ }
+ }
+ else {
+ const char *proto;
+ int i, found = 0;
+ for (i = 0; !found && i < alpn_proposed->nelts; ++i) {
+ proto = APR_ARRAY_IDX(alpn_proposed, i, const char *);
+ found = !strncmp(selected, proto, slen);
+ }
+ if (!found) {
+ /* From a conforming peer, this should never happen,
+ * but life always finds a way... */
+ proto = apr_pstrndup(c->pool, selected, slen);
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10274)
+ "SSL Proxy: Peer proposed ALPN protocol %s which is none "
+ "of our proposals [%s].", proto, alpn_note);
+ proxy_ssl_check_peer_ok = FALSE;
+ }
+ }
+ }
+#endif
+
if (proxy_ssl_check_peer_ok == TRUE) {
/* another chance to fail */
post_handshake_rc = ssl_run_proxy_post_handshake(c, filter_ctx->pssl);
@@ -1587,18 +1689,32 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
}
-/* ssl_io_filter_output() produces one SSL/TLS message per bucket
+/* ssl_io_filter_output() produces one SSL/TLS record per bucket
* passed down the output filter stack. This results in a high
- * overhead (network packets) for any output comprising many small
- * buckets. SSI page applied through the HTTP chunk filter, for
- * example, may produce many brigades containing small buckets -
- * [chunk-size CRLF] [chunk-data] [CRLF].
+ * overhead (more network packets & TLS processing) for any output
+ * comprising many small buckets. SSI output passed through the HTTP
+ * chunk filter, for example, may produce many brigades containing
+ * small buckets - [chunk-size CRLF] [chunk-data] [CRLF].
*
- * The coalescing filter merges many small buckets into larger buckets
- * where possible, allowing the SSL I/O output filter to handle them
- * more efficiently. */
+ * Sending HTTP response headers as a separate TLS record to the
+ * response body also reveals information to a network observer (the
+ * size of headers) which can be significant.
+ *
+ * The coalescing filter merges data buckets with the aim of producing
+ * fewer, larger TLS records - without copying/buffering all content
+ * and introducing unnecessary overhead.
+ *
+ * ### This buffering could be probably be done more comprehensively
+ * ### in ssl_io_filter_output itself.
+ *
+ * ### Another possible performance optimisation in particular for the
+ * ### [HEAP] [FILE] HTTP response case is using a brigade rather than
+ * ### a char array to buffer; using apr_brigade_write() to append
+ * ### will use already-allocated memory from the HEAP, reducing # of
+ * ### copies.
+ */
-#define COALESCE_BYTES (2048)
+#define COALESCE_BYTES (AP_IOBUFSIZE)
struct coalesce_ctx {
char buffer[COALESCE_BYTES];
@@ -1611,11 +1727,12 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
apr_bucket *e, *upto;
apr_size_t bytes = 0;
struct coalesce_ctx *ctx = f->ctx;
+ apr_size_t buffered = ctx ? ctx->bytes : 0; /* space used on entry */
unsigned count = 0;
/* The brigade consists of zero-or-more small data buckets which
- * can be coalesced (the prefix), followed by the remainder of the
- * brigade.
+ * can be coalesced (referred to as the "prefix"), followed by the
+ * remainder of the brigade.
*
* Find the last bucket - if any - of that prefix. count gives
* the number of buckets in the prefix. The "prefix" must contain
@@ -1630,24 +1747,100 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
e != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_METADATA(e)
&& e->length != (apr_size_t)-1
- && e->length < COALESCE_BYTES
- && (bytes + e->length) < COALESCE_BYTES
- && (ctx == NULL
- || bytes + ctx->bytes + e->length < COALESCE_BYTES);
+ && e->length <= COALESCE_BYTES
+ && (buffered + bytes + e->length) <= COALESCE_BYTES;
e = APR_BUCKET_NEXT(e)) {
- if (e->length) count++; /* don't count zero-length buckets */
- bytes += e->length;
+ /* don't count zero-length buckets */
+ if (e->length) {
+ bytes += e->length;
+ count++;
+ }
+ }
+
+ /* If there is room remaining and the next bucket is a data
+ * bucket, try to include it in the prefix to coalesce. For a
+ * typical [HEAP] [FILE] HTTP response brigade, this handles
+ * merging the headers and the start of the body into a single TLS
+ * record. */
+ if (bytes + buffered > 0
+ && bytes + buffered < COALESCE_BYTES
+ && e != APR_BRIGADE_SENTINEL(bb)
+ && !APR_BUCKET_IS_METADATA(e)) {
+ apr_status_t rv = APR_SUCCESS;
+
+ /* For an indeterminate length bucket (PIPE/CGI/...), try a
+ * non-blocking read to have it morph into a HEAP. If the
+ * read fails with EAGAIN, it is harmless to try a split
+ * anyway, split is ENOTIMPL for most PIPE-like buckets. */
+ if (e->length == (apr_size_t)-1) {
+ const char *discard;
+ apr_size_t ignore;
+
+ rv = apr_bucket_read(e, &discard, &ignore, APR_NONBLOCK_READ);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10232)
+ "coalesce failed to read from %s bucket",
+ e->type->name);
+ return AP_FILTER_ERROR;
+ }
+ }
+
+ if (rv == APR_SUCCESS) {
+ /* If the read above made the bucket morph, it may now fit
+ * entirely within the buffer. Otherwise, split it so it does
+ * fit. */
+ if (e->length > COALESCE_BYTES
+ || e->length + buffered + bytes > COALESCE_BYTES) {
+ rv = apr_bucket_split(e, COALESCE_BYTES - (buffered + bytes));
+ }
+
+ if (rv == APR_SUCCESS && e->length == 0) {
+ /* As above, don't count in the prefix if the bucket is
+ * now zero-length. */
+ }
+ else if (rv == APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "coalesce: adding %" APR_SIZE_T_FMT " bytes "
+ "from split %s bucket, total %" APR_SIZE_T_FMT,
+ e->length, e->type->name, bytes + buffered);
+
+ count++;
+ bytes += e->length;
+ e = APR_BUCKET_NEXT(e);
+ }
+ else if (rv != APR_ENOTIMPL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10233)
+ "coalesce: failed to split data bucket");
+ return AP_FILTER_ERROR;
+ }
+ }
}
+
+ /* The prefix is zero or more buckets. upto now points to the
+ * bucket AFTER the end of the prefix, which may be the brigade
+ * sentinel. */
upto = e;
- /* Coalesce the prefix, if:
- * a) more than one bucket is found to coalesce, or
- * b) the brigade contains only a single data bucket, or
- * c) the data bucket is not last but we have buffered data already.
+ /* Coalesce the prefix, if any of the following are true:
+ *
+ * a) the prefix is more than one bucket
+ * OR
+ * b) the prefix is the entire brigade, which is a single bucket
+ * AND the prefix length is smaller than the buffer size,
+ * OR
+ * c) the prefix is a single bucket
+ * AND there is buffered data from a previous pass.
+ *
+ * The aim with (b) is to buffer a small bucket so it can be
+ * coalesced with future invocations of this filter. e.g. three
+ * calls each with a single 100 byte HEAP bucket should get
+ * coalesced together. But an invocation with a 8192 byte HEAP
+ * should pass through untouched.
*/
if (bytes > 0
&& (count > 1
- || (upto == APR_BRIGADE_SENTINEL(bb))
+ || (upto == APR_BRIGADE_SENTINEL(bb)
+ && bytes < COALESCE_BYTES)
|| (ctx && ctx->bytes > 0))) {
/* If coalescing some bytes, ensure a context has been
* created. */
@@ -1658,7 +1851,8 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
"coalesce: have %" APR_SIZE_T_FMT " bytes, "
- "adding %" APR_SIZE_T_FMT " more", ctx->bytes, bytes);
+ "adding %" APR_SIZE_T_FMT " more (buckets=%u)",
+ ctx->bytes, bytes, count);
/* Iterate through the prefix segment. For non-fatal errors
* in this loop it is safe to break out and fall back to the
@@ -1673,7 +1867,8 @@ static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
if (APR_BUCKET_IS_METADATA(e)
|| e->length == (apr_size_t)-1) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02012)
- "unexpected bucket type during coalesce");
+ "unexpected %s bucket during coalesce",
+ e->type->name);
break; /* non-fatal error; break out */
}
@@ -1945,7 +2140,7 @@ static apr_status_t ssl_io_filter_buffer(ap_filter_t *f,
}
if (APR_BRIGADE_EMPTY(ctx->bb)) {
- /* Suprisingly (and perhaps, wrongly), the request body can be
+ /* Surprisingly (and perhaps, wrongly), the request body can be
* pulled from the input filter stack more than once; a
* handler may read it, and ap_discard_request_body() will
* attempt to do so again after *every* request. So input
@@ -2087,14 +2282,7 @@ void ssl_io_filter_init(conn_rec *c, request_rec *r, SSL *ssl)
ssl_io_filter_cleanup, apr_pool_cleanup_null);
if (APLOG_CS_IS_LEVEL(c, mySrvFromConn(c), APLOG_TRACE4)) {
- BIO *rbio = SSL_get_rbio(ssl),
- *wbio = SSL_get_wbio(ssl);
- BIO_set_callback(rbio, ssl_io_data_cb);
- BIO_set_callback_arg(rbio, (void *)ssl);
- if (wbio && wbio != rbio) {
- BIO_set_callback(wbio, ssl_io_data_cb);
- BIO_set_callback_arg(wbio, (void *)ssl);
- }
+ modssl_set_io_callbacks(ssl);
}
return;
@@ -2123,19 +2311,18 @@ static void ssl_io_data_dump(conn_rec *c, server_rec *s,
const char *b, long len)
{
char buf[256];
- char tmp[64];
- int i, j, rows, trunc;
+ int i, j, rows, trunc, pos;
unsigned char ch;
trunc = 0;
- for(; (len > 0) && ((b[len-1] == ' ') || (b[len-1] == '\0')); len--)
+ for (; (len > 0) && ((b[len-1] == ' ') || (b[len-1] == '\0')); len--)
trunc++;
rows = (len / DUMP_WIDTH);
if ((rows * DUMP_WIDTH) < len)
rows++;
ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
"+-------------------------------------------------------------------------+");
- for(i = 0 ; i< rows; i++) {
+ for (i = 0 ; i < rows; i++) {
#if APR_CHARSET_EBCDIC
char ebcdic_text[DUMP_WIDTH];
j = DUMP_WIDTH;
@@ -2146,32 +2333,30 @@ static void ssl_io_data_dump(conn_rec *c, server_rec *s,
memcpy(ebcdic_text,(char *)(b) + i * DUMP_WIDTH, j);
ap_xlate_proto_from_ascii(ebcdic_text, j);
#endif /* APR_CHARSET_EBCDIC */
- apr_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
- apr_cpystrn(buf, tmp, sizeof(buf));
+ pos = 0;
+ pos += apr_snprintf(buf, sizeof(buf)-pos, "| %04x: ", i * DUMP_WIDTH);
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
- apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
else {
ch = ((unsigned char)*((char *)(b) + i * DUMP_WIDTH + j)) & 0xff;
- apr_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
- apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%02x%c", ch , j==7 ? '-' : ' ');
}
}
- apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
- apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
else {
ch = ((unsigned char)*((char *)(b) + i * DUMP_WIDTH + j)) & 0xff;
#if APR_CHARSET_EBCDIC
- apr_snprintf(tmp, sizeof(tmp), "%c", (ch >= 0x20 && ch <= 0x7F) ? ebcdic_text[j] : '.');
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%c", (ch >= 0x20 && ch <= 0x7F) ? ebcdic_text[j] : '.');
#else /* APR_CHARSET_EBCDIC */
- apr_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
#endif /* APR_CHARSET_EBCDIC */
- apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
}
}
- apr_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " |");
ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s, "%s", buf);
}
if (trunc > 0)
@@ -2179,16 +2364,24 @@ static void ssl_io_data_dump(conn_rec *c, server_rec *s,
"| %04ld - <SPACES/NULS>", len + trunc);
ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
"+-------------------------------------------------------------------------+");
- return;
}
-long ssl_io_data_cb(BIO *bio, int cmd,
- const char *argp,
- int argi, long argl, long rc)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static long modssl_io_cb(BIO *bio, int cmd, const char *argp,
+ size_t len, int argi, long argl, int rc,
+ size_t *processed)
+#else
+static long modssl_io_cb(BIO *bio, int cmd, const char *argp,
+ int argi, long argl, long rc)
+#endif
{
SSL *ssl;
conn_rec *c;
server_rec *s;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ (void)len;
+ (void)processed;
+#endif
if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
return rc;
@@ -2210,7 +2403,7 @@ long ssl_io_data_cb(BIO *bio, int cmd,
"%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
MODSSL_LIBRARY_NAME,
(cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
- rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+ (long)rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
bio, argp, dump);
if (*dump != '\0' && argp != NULL)
ssl_io_data_dump(c, s, argp, rc);
@@ -2225,3 +2418,25 @@ long ssl_io_data_cb(BIO *bio, int cmd,
}
return rc;
}
+
+static APR_INLINE void set_bio_callback(BIO *bio, void *arg)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ BIO_set_callback_ex(bio, modssl_io_cb);
+#else
+ BIO_set_callback(bio, modssl_io_cb);
+#endif
+ BIO_set_callback_arg(bio, arg);
+}
+
+void modssl_set_io_callbacks(SSL *ssl)
+{
+ BIO *rbio = SSL_get_rbio(ssl),
+ *wbio = SSL_get_wbio(ssl);
+ if (rbio) {
+ set_bio_callback(rbio, ssl);
+ }
+ if (wbio && wbio != rbio) {
+ set_bio_callback(wbio, ssl);
+ }
+}
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 81c0f63..fe0496f 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -114,6 +114,45 @@ static int has_buffered_data(request_rec *r)
return result;
}
+/* If a renegotiation is required for the location, and the request
+ * includes a message body (and the client has not requested a "100
+ * Continue" response), then the client will be streaming the request
+ * body over the wire already. In that case, it is not possible to
+ * stop and perform a new SSL handshake immediately; once the SSL
+ * library moves to the "accept" state, it will reject the SSL packets
+ * which the client is sending for the request body.
+ *
+ * To allow authentication to complete in the hook, the solution used
+ * here is to fill a (bounded) buffer with the request body, and then
+ * to reinject that request body later.
+ *
+ * This function is called to fill the renegotiation buffer for the
+ * location as required, or fail. Returns zero on success or HTTP_
+ * error code on failure.
+ */
+static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc)
+{
+ int rv;
+ apr_size_t rsize;
+
+ /* ### this is HTTP/1.1 specific, special case for protocol? */
+ if (r->expecting_100 || !ap_request_has_body(r)) {
+ return 0;
+ }
+
+ rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE : dc->nRenegBufferSize;
+ if (rsize > 0) {
+ /* Fill the I/O buffer with the request body if possible. */
+ rv = ssl_io_buffer_fill(r, rsize);
+ }
+ else {
+ /* If the reneg buffer size is set to zero, just fail. */
+ rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ return rv;
+}
+
#ifdef HAVE_TLSEXT
static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2)
{
@@ -814,41 +853,14 @@ static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirCo
}
}
- /* If a renegotiation is now required for this location, and the
- * request includes a message body (and the client has not
- * requested a "100 Continue" response), then the client will be
- * streaming the request body over the wire already. In that
- * case, it is not possible to stop and perform a new SSL
- * handshake immediately; once the SSL library moves to the
- * "accept" state, it will reject the SSL packets which the client
- * is sending for the request body.
- *
- * To allow authentication to complete in this auth hook, the
- * solution used here is to fill a (bounded) buffer with the
- * request body, and then to reinject that request body later.
- */
- if (renegotiate && !renegotiate_quick
- && !r->expecting_100
- && ap_request_has_body(r)) {
- int rv;
- apr_size_t rsize;
-
- rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE :
- dc->nRenegBufferSize;
- if (rsize > 0) {
- /* Fill the I/O buffer with the request body if possible. */
- rv = ssl_io_buffer_fill(r, rsize);
- }
- else {
- /* If the reneg buffer size is set to zero, just fail. */
- rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
-
- if (rv) {
+ /* Fill reneg buffer if required. */
+ if (renegotiate && !renegotiate_quick) {
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02257)
"could not buffer message body to allow "
"SSL renegotiation to proceed");
- return rv;
+ return rc;
}
}
@@ -1132,6 +1144,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
}
}
+ /* Fill reneg buffer if required. */
if (change_vmode) {
char peekbuf[1];
@@ -1144,7 +1157,16 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
return HTTP_FORBIDDEN;
}
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10129) "verify client post handshake");
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10228)
+ "could not buffer message body to allow "
+ "TLS Post-Handshake Authentication to proceed");
+ return rc;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10129)
+ "verify client post handshake");
SSL_set_verify(ssl, vmode_needed, ssl_callback_SSLVerify);
@@ -1154,6 +1176,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
apr_table_setn(r->notes, "error-notes",
"Reason: Cannot perform Post-Handshake Authentication.<br />");
+ SSL_set_verify(ssl, vmode_inplace, NULL);
return HTTP_FORBIDDEN;
}
@@ -1175,6 +1198,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
* Finally check for acceptable renegotiation results
*/
if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ SSL_set_verify(ssl, vmode_inplace, NULL);
return rc;
}
}
@@ -1661,6 +1685,7 @@ const authz_provider ssl_authz_provider_verify_client =
** _________________________________________________________________
*/
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
/*
* Hand out standard DH parameters, based on the authentication strength
*/
@@ -1706,6 +1731,7 @@ DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen)
return modssl_get_dh_params(keylen);
}
+#endif
/*
* This OpenSSL callback function is called when OpenSSL
@@ -1723,7 +1749,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
SSLSrvConfigRec *sc = mySrvConfig(s);
SSLConnRec *sslconn = myConnConfig(conn);
SSLDirConfigRec *dc = r ? myDirConfig(r) : sslconn->dc;
- modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
+ modssl_ctx_t *mctx = myConnCtxConfig(conn, sc);
int crl_check_mode = mctx->crl_check_mask & ~SSL_CRLCHECK_FLAGS;
/* Get verify ingredients */
@@ -1747,7 +1773,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
* Check for optionally acceptable non-verifiable issuer situation
*/
if (dc) {
- if (sslconn->is_proxy) {
+ if (conn->outgoing) {
verify = dc->proxy->auth.verify_mode;
}
else {
@@ -1810,8 +1836,8 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
/*
* Perform OCSP-based revocation checks
*/
- if (ok && ((sc->server->ocsp_mask & SSL_OCSPCHECK_CHAIN) ||
- (errdepth == 0 && (sc->server->ocsp_mask & SSL_OCSPCHECK_LEAF)))) {
+ if (ok && ((mctx->ocsp_mask & SSL_OCSPCHECK_CHAIN) ||
+ (errdepth == 0 && (mctx->ocsp_mask & SSL_OCSPCHECK_LEAF)))) {
/* If there was an optional verification error, it's not
* possible to perform OCSP validation since the issuer may be
* missing/untrusted. Fail in that case. */
@@ -1859,7 +1885,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
* Finally check the depth of the certificate verification
*/
if (dc) {
- if (sslconn->is_proxy) {
+ if (conn->outgoing) {
depth = dc->proxy->auth.verify_depth;
}
else {
@@ -1911,7 +1937,7 @@ static void modssl_proxy_info_log(conn_rec *c,
*cert = info->x509; \
CRYPTO_add(&(*cert)->references, +1, CRYPTO_LOCK_X509); \
*pkey = info->x_pkey->dec_pkey; \
- CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_X509_PKEY)
+ CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_EVP_PKEY)
#else
#define modssl_set_cert_info(info, cert, pkey) \
*cert = info->x509; \
@@ -2268,7 +2294,7 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
/* If the reneg state is to reject renegotiations, check the SSL
* state machine and move to ABORT if a Client Hello is being
* read. */
- if (!sslconn->is_proxy &&
+ if (!c->outgoing &&
(where & SSL_CB_HANDSHAKE_START) &&
sslconn->reneg_state == RENEG_REJECT) {
sslconn->reneg_state = RENEG_ABORT;
@@ -2290,60 +2316,89 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
}
#ifdef HAVE_TLSEXT
+
+static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
+ SSL *ssl, X509 *cert, EVP_PKEY *key,
+ const char *cert_pem, const char *key_pem)
+{
+ SSLConnRec *sslcon = myConnConfig(c);
+ apr_status_t rv = APR_SUCCESS;
+ int our_data = 0;
+
+ sslcon->service_unavailable = 1;
+ if (cert_pem) {
+ cert = NULL;
+ key = NULL;
+ our_data = 1;
+
+ rv = modssl_read_cert(c->pool, cert_pem, key_pem, NULL, NULL, &cert, &key);
+ if (rv != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10266)
+ "Failed to parse PEM of challenge certificate %s",
+ servername);
+ goto cleanup;
+ }
+ }
+
+ if ((SSL_use_certificate(ssl, cert) < 1)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
+ "Failed to configure challenge certificate %s",
+ servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+ if (!SSL_use_PrivateKey(ssl, key)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087)
+ "error '%s' using Challenge key: %s",
+ ERR_error_string(ERR_peek_last_error(), NULL),
+ servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+ if (SSL_check_private_key(ssl) < 1) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
+ "Challenge certificate and private key %s "
+ "do not match", servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (our_data && cert) X509_free(cert);
+ if (our_data && key) EVP_PKEY_free(key);
+ return APR_SUCCESS;
+}
+
/*
* This function sets the virtual host from an extended
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
*/
-static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
+static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
{
- const char *servername;
- X509 *cert;
- EVP_PKEY *key;
-
if (c) {
SSLConnRec *sslcon = myConnConfig(c);
-
- if (sslcon->server != c->base_server) {
- /* already found the vhost */
- return APR_SUCCESS;
+
+ if (sslcon->vhost_found) {
+ /* already found the vhost? */
+ return sslcon->vhost_found > 0 ? APR_SUCCESS : APR_NOTFOUND;
}
+ sslcon->vhost_found = -1;
- servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (!servername) {
+ servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ }
if (servername) {
if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
(void *)servername)) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
"SSL virtual host for servername %s found",
servername);
-
+
+ sslcon->vhost_found = +1;
return APR_SUCCESS;
}
- else if (ssl_is_challenge(c, servername, &cert, &key)) {
-
- sslcon->service_unavailable = 1;
- if ((SSL_use_certificate(ssl, cert) < 1)) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
- "Failed to configure challenge certificate %s",
- servername);
- return APR_EGENERAL;
- }
-
- if (!SSL_use_PrivateKey(ssl, key)) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087)
- "error '%s' using Challenge key: %s",
- ERR_error_string(ERR_peek_last_error(), NULL),
- servername);
- return APR_EGENERAL;
- }
-
- if (SSL_check_private_key(ssl) < 1) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
- "Challenge certificate and private key %s "
- "do not match", servername);
- return APR_EGENERAL;
- }
-
- }
else {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
"No matching SSL virtual host for servername "
@@ -2383,11 +2438,71 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
{
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
- apr_status_t status = init_vhost(c, ssl);
+ apr_status_t status = init_vhost(c, ssl, NULL);
return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * This callback function is called when the ClientHello is received.
+ */
+int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
+{
+ char *servername = NULL;
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ const unsigned char *pos;
+ size_t len, remaining;
+ (void)arg;
+
+ /* We can't use SSL_get_servername() at this earliest OpenSSL connection
+ * stage, and there is no SSL_client_hello_get0_servername() provided as
+ * of OpenSSL 1.1.1. So the code below, that extracts the SNI from the
+ * ClientHello's TLS extensions, is taken from some test code in OpenSSL,
+ * i.e. client_hello_select_server_ctx() in "test/handshake_helper.c".
+ */
+
+ /*
+ * The server_name extension was given too much extensibility when it
+ * was written, so parsing the normal case is a bit complex.
+ */
+ if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos,
+ &remaining)
+ || remaining <= 2)
+ goto give_up;
+
+ /* Extract the length of the supplied list of names. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+ remaining = len;
+
+ /*
+ * The list in practice only has a single element, so we only consider
+ * the first one.
+ */
+ if (remaining <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name)
+ goto give_up;
+ remaining--;
+
+ /* Now we can finally pull out the byte array with the actual hostname. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+
+ /* Use the SNI to switch to the relevant vhost, should it differ from
+ * c->base_server.
+ */
+ servername = apr_pstrmemdup(c->pool, (const char *)pos, len);
+
+give_up:
+ init_vhost(c, ssl, servername);
+ return SSL_CLIENT_HELLO_SUCCESS;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
+
/*
* Find a (name-based) SSL virtual host where either the ServerName
* or one of the ServerAliases matches the supplied name (to be used
@@ -2407,12 +2522,25 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
if (found && (ssl = sslcon->ssl) &&
(sc = mySrvConfig(s))) {
SSL_CTX *ctx = SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
+
/*
* SSL_set_SSL_CTX() only deals with the server cert,
* so we need to duplicate a few additional settings
* from the ctx by hand
*/
SSL_set_options(ssl, SSL_CTX_get_options(ctx));
+#if OPENSSL_VERSION_NUMBER >= 0x1010007fL \
+ && (!defined(LIBRESSL_VERSION_NUMBER) \
+ || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
+ /*
+ * Don't switch the protocol if none is configured for this vhost,
+ * the default in this case is still the base server's SSLProtocol.
+ */
+ if (myConnCtxConfig(c, sc)->protocol_set) {
+ SSL_set_min_proto_version(ssl, SSL_CTX_get_min_proto_version(ctx));
+ SSL_set_max_proto_version(ssl, SSL_CTX_get_max_proto_version(ctx));
+ }
+#endif
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0)) {
/*
@@ -2453,6 +2581,7 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
sc->server->pks->service_unavailable : 0;
ap_update_child_status_from_server(c->sbh, SERVER_BUSY_READ, c, s);
+
/*
* There is one special filter callback, which is set
* very early depending on the base_server's log level.
@@ -2461,14 +2590,7 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
* we need to set that callback here.
*/
if (APLOGtrace4(s)) {
- BIO *rbio = SSL_get_rbio(ssl),
- *wbio = SSL_get_wbio(ssl);
- BIO_set_callback(rbio, ssl_io_data_cb);
- BIO_set_callback_arg(rbio, (void *)ssl);
- if (wbio && wbio != rbio) {
- BIO_set_callback(wbio, ssl_io_data_cb);
- BIO_set_callback_arg(wbio, (void *)ssl);
- }
+ modssl_set_io_callbacks(ssl);
}
return 1;
@@ -2488,14 +2610,17 @@ int ssl_callback_SessionTicket(SSL *ssl,
unsigned char *keyname,
unsigned char *iv,
EVP_CIPHER_CTX *cipher_ctx,
- HMAC_CTX *hctx,
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX *hmac_ctx,
+#else
+ EVP_MAC_CTX *mac_ctx,
+#endif
int mode)
{
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
server_rec *s = mySrvFromConn(c);
SSLSrvConfigRec *sc = mySrvConfig(s);
- SSLConnRec *sslconn = myConnConfig(c);
- modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
+ modssl_ctx_t *mctx = myConnCtxConfig(c, sc);
modssl_ticket_key_t *ticket_key = mctx->ticket_key;
if (mode == 1) {
@@ -2515,7 +2640,13 @@ int ssl_callback_SessionTicket(SSL *ssl,
}
EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
ticket_key->aes_key, iv);
- HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_Init_ex(hmac_ctx, ticket_key->hmac_secret, 16,
+ tlsext_tick_md(), NULL);
+#else
+ EVP_MAC_CTX_set_params(mac_ctx, ticket_key->mac_params);
+#endif
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02289)
"TLS session ticket key for %s successfully set, "
@@ -2536,7 +2667,13 @@ int ssl_callback_SessionTicket(SSL *ssl,
EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
ticket_key->aes_key, iv);
- HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_Init_ex(hmac_ctx, ticket_key->hmac_secret, 16,
+ tlsext_tick_md(), NULL);
+#else
+ EVP_MAC_CTX_set_params(mac_ctx, ticket_key->mac_params);
+#endif
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02290)
"TLS session ticket key for %s successfully set, "
@@ -2609,7 +2746,7 @@ int ssl_callback_alpn_select(SSL *ssl,
* they callback the SNI. We need to make sure that we know which vhost
* we are dealing with so we respect the correct protocols.
*/
- init_vhost(c, ssl);
+ init_vhost(c, ssl, NULL);
proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos);
if (!proposed) {
@@ -2635,6 +2772,26 @@ int ssl_callback_alpn_select(SSL *ssl,
proposed);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
+
+ /* protocol was switched, this could be a challenge protocol such as "acme-tls/1".
+ * For that to work, we need to allow overrides to our ssl certificate.
+ * However, exclude challenge checks on our best known traffic protocol.
+ * (http/1.1 is the default, we never switch to it anyway.)
+ */
+ if (strcmp("h2", proposed)) {
+ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ X509 *cert;
+ EVP_PKEY *key;
+ const char *cert_pem, *key_pem;
+
+ if (ssl_is_challenge(c, servername, &cert, &key, &cert_pem, &key_pem)) {
+ if (set_challenge_creds(c, servername, ssl, cert, key,
+ cert_pem, key_pem) != APR_SUCCESS) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
+ }
+ }
}
return SSL_TLSEXT_ERR_OK;
@@ -2676,3 +2833,17 @@ int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
}
#endif /* HAVE_SRP */
+
+
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line)
+{
+ conn_rec *conn = SSL_get_app_data(ssl);
+ SSLSrvConfigRec *sc = mySrvConfig(conn->base_server);
+
+ if (sc && sc->mc->keylog_file) {
+ apr_file_printf(sc->mc->keylog_file, "%s\n", line);
+ }
+}
+#endif
diff --git a/modules/ssl/ssl_engine_log.c b/modules/ssl/ssl_engine_log.c
index d2f9ed0..3b3ceac 100644
--- a/modules/ssl/ssl_engine_log.c
+++ b/modules/ssl/ssl_engine_log.c
@@ -78,6 +78,16 @@ apr_status_t ssl_die(server_rec *s)
return APR_EGENERAL;
}
+static APR_INLINE
+unsigned long modssl_ERR_peek_error_data(const char **data, int *flags)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ return ERR_peek_error_line_data(NULL, NULL, data, flags);
+#else
+ return ERR_peek_error_data(data, flags);
+#endif
+}
+
/*
* Prints the SSL library error information.
*/
@@ -87,7 +97,7 @@ void ssl_log_ssl_error(const char *file, int line, int level, server_rec *s)
const char *data;
int flags;
- while ((e = ERR_peek_error_line_data(NULL, NULL, &data, &flags))) {
+ while ((e = modssl_ERR_peek_error_data(&data, &flags))) {
const char *annotation;
char err[256];
@@ -123,10 +133,8 @@ static void ssl_log_cert_error(const char *file, int line, int level,
int msglen, n;
char *name;
- apr_vsnprintf(buf, sizeof buf, format, ap);
-
- msglen = strlen(buf);
-
+ msglen = apr_vsnprintf(buf, sizeof buf, format, ap);
+
if (cert) {
BIO *bio = BIO_new(BIO_s_mem());
diff --git a/modules/ssl/ssl_engine_ocsp.c b/modules/ssl/ssl_engine_ocsp.c
index ef92c37..5e04512 100644
--- a/modules/ssl/ssl_engine_ocsp.c
+++ b/modules/ssl/ssl_engine_ocsp.c
@@ -86,7 +86,7 @@ static apr_uri_t *determine_responder_uri(SSLSrvConfigRec *sc, X509 *cert,
return NULL;
}
- if (strcasecmp(u->scheme, "http") != 0) {
+ if (ap_cstr_casecmp(u->scheme, "http") != 0) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01920)
"cannot handle OCSP responder URI '%s'", s);
return NULL;
@@ -284,6 +284,7 @@ int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
/* Create a temporary pool to constrain memory use (the passed-in
* pool may be e.g. a connection pool). */
apr_pool_create(&vpool, pool);
+ apr_pool_tag(vpool, "modssl_verify_ocsp");
rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool);
diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c
index 8c29443..699019f 100644
--- a/modules/ssl/ssl_engine_pphrase.c
+++ b/modules/ssl/ssl_engine_pphrase.c
@@ -30,6 +30,8 @@
-- Clifford Stoll */
#include "ssl_private.h"
+#include <openssl/ui.h>
+
typedef struct {
server_rec *s;
apr_pool_t *p;
@@ -143,8 +145,6 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
EVP_PKEY *pPrivateKey = NULL;
ssl_asn1_t *asn1;
- unsigned char *ucp;
- long int length;
int nPassPhrase = (*pphrases)->nelts;
int nPassPhraseRetry = 0;
apr_time_t pkey_mtime = 0;
@@ -221,7 +221,7 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
* is not empty. */
ERR_clear_error();
- pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file, NULL,
+ pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
ssl_pphrase_Handle_CB, &ppcb_arg);
/* If the private key was successfully read, nothing more to
do here. */
@@ -351,19 +351,12 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
nPassPhrase++;
}
- /*
- * Insert private key into the global module configuration
- * (we convert it to a stand-alone DER byte sequence
- * because the SSL library uses static variables inside a
- * RSA structure which do not survive DSO reloads!)
- */
- length = i2d_PrivateKey(pPrivateKey, NULL);
- ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
- (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+ /* Cache the private key in the global module configuration so it
+ * can be used after subsequent reloads. */
+ asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
if (ppcb_arg.nPassPhraseDialogCur != 0) {
/* remember mtime of encrypted keys */
- asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
asn1->source_mtime = pkey_mtime;
}
@@ -614,3 +607,306 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
*/
return (len);
}
+
+#if MODSSL_HAVE_ENGINE_API
+
+/* OpenSSL UI implementation for passphrase entry; largely duplicated
+ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
+ * worth trying to shift pphrase handling over to the UI API
+ * completely. */
+static int passphrase_ui_open(UI *ui)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+
+ ppcb->nPassPhraseDialog++;
+ ppcb->nPassPhraseDialogCur++;
+
+ /*
+ * Builtin or Pipe dialog
+ */
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (!readtty) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
+ APLOGNO(10143)
+ "Init: Creating pass phrase dialog pipe child "
+ "'%s'", sc->server->pphrase_dialog_path);
+ if (ssl_pipe_child_create(ppcb->p,
+ sc->server->pphrase_dialog_path)
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
+ APLOGNO(10144)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
+ "Init: Requesting pass phrase via piped dialog");
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+#ifdef WIN32
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+#else
+ /*
+ * stderr has already been redirected to the error_log.
+ * rather than attempting to temporarily rehook it to the terminal,
+ * we print the prompt to stdout before EVP_read_pw_string turns
+ * off tty echo
+ */
+ apr_file_open_stdout(&writetty, ppcb->p);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
+ "Init: Requesting pass phrase via builtin terminal "
+ "dialog");
+#endif
+ }
+
+ /*
+ * The first time display a header to inform the user about what
+ * program he actually speaks to, which module is responsible for
+ * this terminal dialog and why to the hell he has to enter
+ * something...
+ */
+ if (ppcb->nPassPhraseDialog == 1) {
+ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
+ AP_SERVER_BASEVERSION);
+ apr_file_printf(writetty,
+ "A pass phrase is required to access the private key.\n");
+ }
+ if (ppcb->bPassPhraseDialogOnce) {
+ ppcb->bPassPhraseDialogOnce = FALSE;
+ apr_file_printf(writetty, "\n");
+ apr_file_printf(writetty, "Private key %s (%s)\n",
+ ppcb->key_id, ppcb->pkey_file);
+ }
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_read(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+ const char *prompt;
+ int i;
+ int bufsize;
+ int len;
+ char *buf;
+
+ prompt = UI_get0_output_string(uis);
+ if (prompt == NULL) {
+ prompt = "Enter pass phrase:";
+ }
+
+ /*
+ * Get the maximum expected size and allocate the buffer
+ */
+ bufsize = UI_get_result_maxsize(uis);
+ buf = apr_pcalloc(ppcb->p, bufsize);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ /*
+ * Get the pass phrase through a callback.
+ * Empty input is not accepted.
+ */
+ for (;;) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+ i = EVP_read_pw_string(buf, bufsize, "", FALSE);
+ }
+ if (i != 0) {
+ OPENSSL_cleanse(buf, bufsize);
+ return 0;
+ }
+ len = strlen(buf);
+ if (len < 1){
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
+ "empty (needs to be at least 1 character).\n");
+ apr_file_puts(prompt, writetty);
+ }
+ else {
+ break;
+ }
+ }
+ }
+ /*
+ * Filter program
+ */
+ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
+ const char *cmd = sc->server->pphrase_dialog_path;
+ const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
+ char *result;
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
+ "Init: Requesting pass phrase from dialog filter "
+ "program (%s)", cmd);
+
+ argv[0] = cmd;
+ argv[1] = ppcb->key_id;
+ argv[2] = NULL;
+
+ result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
+ apr_cpystrn(buf, result, bufsize);
+ len = strlen(buf);
+ }
+
+ /*
+ * Ok, we now have the pass phrase, so give it back
+ */
+ ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
+ UI_set_result(ui, uis, buf);
+
+ /* Clear sensitive data. */
+ OPENSSL_cleanse(buf, bufsize);
+ return 1;
+}
+
+static int passphrase_ui_write(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc;
+ const char *prompt;
+
+ sc = mySrvConfig(ppcb->s);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ prompt = UI_get0_output_string(uis);
+ apr_file_puts(prompt, writetty);
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_close(UI *ui)
+{
+ /*
+ * Close the pipes if they were opened
+ */
+ if (readtty) {
+ apr_file_close(readtty);
+ apr_file_close(writetty);
+ readtty = writetty = NULL;
+ }
+ return 1;
+}
+
+static apr_status_t pp_ui_method_cleanup(void *uip)
+{
+ UI_METHOD *uim = uip;
+
+ UI_destroy_method(uim);
+
+ return APR_SUCCESS;
+}
+
+static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
+{
+ UI_METHOD *ui_method = UI_create_method("Passphrase UI");
+
+ UI_method_set_opener(ui_method, passphrase_ui_open);
+ UI_method_set_reader(ui_method, passphrase_ui_read);
+ UI_method_set_writer(ui_method, passphrase_ui_write);
+ UI_method_set_closer(ui_method, passphrase_ui_close);
+
+ apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
+ pp_ui_method_cleanup);
+
+ return ui_method;
+}
+#endif
+
+
+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)
+{
+#if MODSSL_HAVE_ENGINE_API
+ const char *c, *scheme;
+ ENGINE *e;
+ UI_METHOD *ui_method = get_passphrase_ui(p);
+ pphrase_cb_arg_t ppcb;
+
+ memset(&ppcb, 0, sizeof ppcb);
+ ppcb.s = s;
+ ppcb.p = p;
+ ppcb.bPassPhraseDialogOnce = TRUE;
+ ppcb.key_id = vhostid;
+ ppcb.pkey_file = keyid;
+
+ c = ap_strchr_c(keyid, ':');
+ if (!c || c == keyid) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+ "Init: Unrecognized private key identifier `%s'",
+ keyid);
+ return ssl_die(s);
+ }
+
+ scheme = apr_pstrmemdup(p, 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",
+ keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ if (!ENGINE_init(e)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10149)
+ "Init: Failed to initialize engine %s for private key %s",
+ scheme, keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Init: Initialized engine %s for private key %s",
+ scheme, keyid);
+
+ if (APLOGdebug(s)) {
+ ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
+ }
+
+ if (certid) {
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params = { certid, NULL };
+
+ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &params, NULL, 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
+ "Init: Unable to get the certificate");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ *pubkey = params.cert;
+ }
+
+ *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
+ if (*privkey == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
+ "Init: Unable to get the private key");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ENGINE_finish(e);
+ ENGINE_free(e);
+
+ return APR_SUCCESS;
+#else
+ return APR_ENOTIMPL;
+#endif
+}
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
index 5724f18..418d849 100644
--- a/modules/ssl/ssl_engine_vars.c
+++ b/modules/ssl/ssl_engine_vars.c
@@ -65,10 +65,10 @@ static SSLConnRec *ssl_get_effective_config(conn_rec *c)
return sslconn;
}
-static int ssl_is_https(conn_rec *c)
+static int ssl_conn_is_ssl(conn_rec *c)
{
- SSLConnRec *sslconn = ssl_get_effective_config(c);
- return sslconn && sslconn->ssl;
+ const SSLConnRec *sslconn = ssl_get_effective_config(c);
+ return (sslconn && sslconn->ssl)? OK : DECLINED;
}
static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
@@ -137,7 +137,7 @@ void ssl_var_register(apr_pool_t *p)
{
char *cp, *cp2;
- APR_REGISTER_OPTIONAL_FN(ssl_is_https);
+ ap_hook_ssl_conn_is_ssl(ssl_conn_is_ssl, NULL, NULL, APR_HOOK_MIDDLE);
APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
@@ -460,18 +460,13 @@ static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
}
else {
BIO* bio;
- int n;
unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
+
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
X509_NAME_print_ex(bio, xsname, 0, flags);
- n = BIO_pending(bio);
- if (n > 0) {
- result = apr_palloc(p, n+1);
- n = BIO_read(bio, result, n);
- result[n] = NUL;
- }
- BIO_free(bio);
+
+ result = modssl_bio_free_read(p, bio);
}
return result;
}
@@ -635,7 +630,8 @@ static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname,
static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
{
- int type, numlen;
+ int type;
+ apr_size_t numlen;
const char *onf = NULL;
apr_array_header_t *entries;
@@ -678,19 +674,13 @@ static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
{
- char *result;
BIO* bio;
- int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
ASN1_TIME_print(bio, tm);
- n = BIO_pending(bio);
- result = apr_pcalloc(p, n+1);
- n = BIO_read(bio, result, n);
- result[n] = NUL;
- BIO_free(bio);
- return result;
+
+ return modssl_bio_free_read(p, bio);
}
#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
@@ -739,19 +729,13 @@ static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
{
- char *result;
BIO *bio;
- int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
- n = BIO_pending(bio);
- result = apr_pcalloc(p, n+1);
- n = BIO_read(bio, result, n);
- result[n] = NUL;
- BIO_free(bio);
- return result;
+
+ return modssl_bio_free_read(p, bio);
}
static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
@@ -806,19 +790,13 @@ static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl)
static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
{
- char *result;
BIO *bio;
- int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
PEM_write_bio_X509(bio, xs);
- n = BIO_pending(bio);
- result = apr_pcalloc(p, n+1);
- n = BIO_read(bio, result, n);
- result[n] = NUL;
- BIO_free(bio);
- return result;
+
+ return modssl_bio_free_read(p, bio);
}
static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn)
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index f46814d..859e932 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -27,6 +27,7 @@
*/
/** Apache headers */
+#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
@@ -35,6 +36,7 @@
#include "http_connection.h"
#include "http_request.h"
#include "http_protocol.h"
+#include "http_ssl.h"
#include "http_vhost.h"
#include "util_script.h"
#include "util_filter.h"
@@ -81,13 +83,13 @@
#include "ap_expr.h"
-/* OpenSSL headers */
-#include <openssl/opensslv.h>
-#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
-/* must be defined before including ssl.h */
-#define OPENSSL_NO_SSL_INTERN
+/* keep first for compat API */
+#ifndef OPENSSL_API_COMPAT
+#define OPENSSL_API_COMPAT 0x10101000 /* for ENGINE_ API */
#endif
-#include <openssl/ssl.h>
+#include "mod_ssl_openssl.h"
+
+/* OpenSSL headers */
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
@@ -97,12 +99,23 @@
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include <openssl/ocsp.h>
+#include <openssl/dh.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#include <openssl/core_names.h>
+#endif
/* Avoid tripping over an engine build installed globally and detected
* when the user points at an explicit non-engine flavor of OpenSSL
*/
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) \
+ && (OPENSSL_VERSION_NUMBER < 0x30000000 \
+ || (defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL < 30000)) \
+ && !defined(OPENSSL_NO_ENGINE)
#include <openssl/engine.h>
+#define MODSSL_HAVE_ENGINE_API 1
+#endif
+#ifndef MODSSL_HAVE_ENGINE_API
+#define MODSSL_HAVE_ENGINE_API 0
#endif
#if (OPENSSL_VERSION_NUMBER < 0x0090801f)
@@ -132,18 +145,25 @@
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
#define SSL_CTX_set_max_proto_version(ctx, version) \
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
-#elif LIBRESSL_VERSION_NUMBER < 0x2070000f
+#endif /* LIBRESSL_VERSION_NUMBER < 0x2060000f */
/* LibreSSL before 2.7 declares OPENSSL_VERSION_NUMBER == 2.0 but does not
* include most changes from OpenSSL >= 1.1 (new functions, macros,
* deprecations, ...), so we have to work around this...
*/
-#define MODSSL_USE_OPENSSL_PRE_1_1_API (1)
-#endif /* LIBRESSL_VERSION_NUMBER < 0x2060000f */
+#if LIBRESSL_VERSION_NUMBER < 0x2070000f
+#define MODSSL_USE_OPENSSL_PRE_1_1_API 1
+#else
+#define MODSSL_USE_OPENSSL_PRE_1_1_API 0
+#endif
#else /* defined(LIBRESSL_VERSION_NUMBER) */
-#define MODSSL_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define MODSSL_USE_OPENSSL_PRE_1_1_API 1
+#else
+#define MODSSL_USE_OPENSSL_PRE_1_1_API 0
#endif
+#endif /* defined(LIBRESSL_VERSION_NUMBER) */
-#if defined(OPENSSL_FIPS)
+#if defined(OPENSSL_FIPS) || OPENSSL_VERSION_NUMBER >= 0x30000000L
#define HAVE_FIPS
#endif
@@ -207,7 +227,10 @@
#endif
/* Secure Remote Password */
-#if !defined(OPENSSL_NO_SRP) && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
+#if !defined(OPENSSL_NO_SRP) \
+ && (OPENSSL_VERSION_NUMBER < 0x30000000L \
+ || (defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL < 30000)) \
+ && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
#define HAVE_SRP
#include <openssl/srp.h>
#endif
@@ -250,6 +273,28 @@ void free_bio_methods(void);
#endif
#endif
+/* those may be deprecated */
+#ifndef X509_get_notBefore
+#define X509_get_notBefore X509_getm_notBefore
+#endif
+#ifndef X509_get_notAfter
+#define X509_get_notAfter X509_getm_notAfter
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+#define HAVE_OPENSSL_KEYLOG
+#endif
+
+#ifdef HAVE_FIPS
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#define modssl_fips_is_enabled() EVP_default_properties_is_fips_enabled(NULL)
+#define modssl_fips_enable(to) EVP_default_properties_enable_fips(NULL, (to))
+#else
+#define modssl_fips_is_enabled() FIPS_mode()
+#define modssl_fips_enable(to) FIPS_mode_set((to))
+#endif
+#endif /* HAVE_FIPS */
+
/* mod_ssl headers */
#include "ssl_util_ssl.h"
@@ -305,8 +350,8 @@ APLOG_USE_MODULE(ssl);
((SSLSrvConfigRec *)ap_get_module_config(srv->module_config, &ssl_module))
#define myDirConfig(req) \
((SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module))
-#define myCtxConfig(sslconn, sc) \
- (sslconn->is_proxy ? sslconn->dc->proxy : sc->server)
+#define myConnCtxConfig(c, sc) \
+ (c->outgoing ? myConnConfig(c)->dc->proxy : sc->server)
#define myModConfig(srv) mySrvConfig((srv))->mc
#define mySrvFromConn(c) myConnConfig(c)->server
#define myDirConfigFromConn(c) myConnConfig(c)->dc
@@ -527,7 +572,6 @@ typedef struct {
const char *verify_info;
const char *verify_error;
int verify_depth;
- int is_proxy;
int disabled;
enum {
NON_SSL_OK = 0, /* is SSL request, or error handling completed */
@@ -554,6 +598,7 @@ typedef struct {
const char *cipher_suite; /* cipher suite used in last reneg */
int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
+ int vhost_found; /* whether we found vhost from SNI already */
} SSLConnRec;
/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
@@ -607,9 +652,7 @@ typedef struct {
* index), for example the string "vhost.example.com:443:0". */
apr_hash_t *tPrivateKey;
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
- const char *szCryptoDevice;
-#endif
+ const char *szCryptoDevice; /* ENGINE device (if available) */
#ifdef HAVE_OCSP_STAPLING
const ap_socache_provider_t *stapling_cache;
@@ -617,6 +660,14 @@ typedef struct {
apr_global_mutex_t *stapling_cache_mutex;
apr_global_mutex_t *stapling_refresh_mutex;
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ /* Used for logging if SSLKEYLOGFILE is set at startup. */
+ apr_file_t *keylog_file;
+#endif
+
+#ifdef HAVE_FIPS
+ BOOL fips;
+#endif
} SSLModConfigRec;
/** Structure representing configured filenames for certs and keys for
@@ -640,10 +691,13 @@ typedef struct {
const char *cert_file;
const char *cert_path;
const char *ca_cert_file;
- STACK_OF(X509_INFO) *certs; /* Contains End Entity certs */
- STACK_OF(X509) **ca_certs; /* Contains ONLY chain certs for
- * each item in certs.
- * (ptr to array of ptrs) */
+ /* certs is a stack of configured cert, key pairs. */
+ STACK_OF(X509_INFO) *certs;
+ /* ca_certs contains ONLY chain certs for each item in certs.
+ * ca_certs[n] is a pointer to the (STACK_OF(X509) *) stack which
+ * holds the cert chain for the 'n'th cert in the certs stack, or
+ * NULL if no chain is configured. */
+ STACK_OF(X509) **ca_certs;
} modssl_pk_proxy_t;
/** stuff related to authentication that can also be per-dir */
@@ -668,7 +722,11 @@ typedef struct {
typedef struct {
const char *file_path;
unsigned char key_name[16];
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
unsigned char hmac_secret[16];
+#else
+ OSSL_PARAM mac_params[3];
+#endif
unsigned char aes_key[16];
} modssl_ticket_key_t;
#endif
@@ -765,9 +823,6 @@ struct SSLSrvConfigRec {
#ifdef HAVE_TLSEXT
ssl_enabled_t strict_sni_vhost_check;
#endif
-#ifdef HAVE_FIPS
- BOOL fips;
-#endif
#ifndef OPENSSL_NO_COMP
BOOL compression;
#endif
@@ -928,9 +983,20 @@ void ssl_callback_Info(const SSL *, int, int);
#ifdef HAVE_TLSEXT
int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+int ssl_callback_ClientHello(SSL *, int *, void *);
+#endif
#ifdef HAVE_TLS_SESSION_TICKETS
-int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
- EVP_CIPHER_CTX *, HMAC_CTX *, int);
+int ssl_callback_SessionTicket(SSL *ssl,
+ unsigned char *keyname,
+ unsigned char *iv,
+ EVP_CIPHER_CTX *cipher_ctx,
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX *hmac_ctx,
+#else
+ EVP_MAC_CTX *mac_ctx,
+#endif
+ int mode);
#endif
#ifdef HAVE_TLS_ALPN
@@ -970,10 +1036,15 @@ int ssl_stapling_init_cert(server_rec *, apr_pool_t *, apr_pool_t *,
int ssl_callback_SRPServerParams(SSL *, int *, void *);
#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line);
+#endif
+
/** I/O */
void ssl_io_filter_init(conn_rec *, request_rec *r, SSL *);
void ssl_io_filter_register(apr_pool_t *);
-long ssl_io_data_cb(BIO *, int, const char *, int, long, long);
+void modssl_set_io_callbacks(SSL *ssl);
/* ssl_io_buffer_fill fills the setaside buffering of the HTTP request
* to allow an SSL renegotiation to take place. */
@@ -1002,21 +1073,32 @@ BOOL ssl_util_vhost_matches(const char *servername, server_rec *s);
apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
const char *, apr_array_header_t **);
+/* 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,
+ const char *vhostid,
+ const char *certid, const char *keyid,
+ X509 **pubkey, EVP_PKEY **privkey);
+
/** Diffie-Hellman Parameter Support */
-DH *ssl_dh_GetParamFromFile(const char *);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DH *modssl_dh_from_file(const char *);
+#else
+EVP_PKEY *modssl_dh_pkey_from_file(const char *);
+#endif
#ifdef HAVE_ECC
-EC_GROUP *ssl_ec_GetParamFromFile(const char *);
+EC_GROUP *modssl_ec_group_from_file(const char *);
#endif
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
- const char *key,
- long int length);
-
-ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
- const char *key);
-
-void ssl_asn1_table_unset(apr_hash_t *table,
- const char *key);
+/* Store the EVP_PKEY key (serialized into DER) in the hash table with
+ * key, returning the ssl_asn1_t structure pointer. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey);
+/* Retrieve the ssl_asn1_t structure with given key from the hash. */
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key);
+/* Remove and free the ssl_asn1_t structure with given key. */
+void ssl_asn1_table_unset(apr_hash_t *table, const char *key);
/** Mutex Support */
int ssl_mutex_init(server_rec *, apr_pool_t *);
@@ -1096,10 +1178,12 @@ void ssl_init_ocsp_certificates(server_rec *s, modssl_ctx_t *mctx);
#endif
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
/* Retrieve DH parameters for given key length. Return value should
* be treated as unmutable, since it is stored in process-global
* memory. */
DH *modssl_get_dh_params(unsigned keylen);
+#endif
/* Returns non-zero if the request was made over SSL/TLS. If sslconn
* is non-NULL and the request is using SSL/TLS, sets *sslconn to the
@@ -1107,7 +1191,12 @@ DH *modssl_get_dh_params(unsigned keylen);
int modssl_request_is_tls(const request_rec *r, SSLConnRec **sslconn);
int ssl_is_challenge(conn_rec *c, const char *servername,
- X509 **pcert, EVP_PKEY **pkey);
+ X509 **pcert, EVP_PKEY **pkey,
+ const char **pcert_file, const char **pkey_file);
+
+/* Returns non-zero if the cert/key filename should be handled through
+ * the configured ENGINE. */
+int modssl_is_engine_id(const char *name);
#endif /* SSL_PRIVATE_H */
/** @} */
diff --git a/modules/ssl/ssl_scache.c b/modules/ssl/ssl_scache.c
index 7b4a203..c0a0950 100644
--- a/modules/ssl/ssl_scache.c
+++ b/modules/ssl/ssl_scache.c
@@ -59,7 +59,7 @@ apr_status_t ssl_scache_init(server_rec *s, apr_pool_t *p)
hints.expiry_interval = 300;
rv = mc->stapling_cache->init(mc->stapling_cache_context,
- "mod_ssl-stapling", &hints, s, p);
+ "mod_ssl-staple", &hints, s, p);
if (rv) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01872)
"Could not initialize stapling cache. Exiting.");
@@ -84,7 +84,7 @@ apr_status_t ssl_scache_init(server_rec *s, apr_pool_t *p)
hints.avg_id_len = 30;
hints.expiry_interval = 30;
- rv = mc->sesscache->init(mc->sesscache_context, "mod_ssl-session", &hints, s, p);
+ rv = mc->sesscache->init(mc->sesscache_context, "mod_ssl-sess", &hints, s, p);
if (rv) {
ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01874)
"Could not initialize session cache. Exiting.");
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c
index 0d23465..87ddfa7 100644
--- a/modules/ssl/ssl_util.c
+++ b/modules/ssl/ssl_util.c
@@ -192,45 +192,37 @@ BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p)
return TRUE;
}
-/*
- * certain key data needs to survive restarts,
- * which are stored in the user data table of s->process->pool.
- * to prevent "leaking" of this data, we use malloc/free
- * rather than apr_palloc and these wrappers to help make sure
- * we do not leak the malloc-ed data.
- */
-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
- const char *key,
- long int length)
+/* Decrypted private keys are cached to survive restarts. The cached
+ * data must have lifetime of the process (hence malloc/free rather
+ * than pools), and uses raw DER since the EVP_PKEY structure
+ * internals may not survive across a module reload. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey)
{
apr_ssize_t klen = strlen(key);
ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+ apr_size_t length = i2d_PrivateKey(pkey, NULL);
+ unsigned char *p;
- /*
- * if a value for this key already exists,
- * reuse as much of the already malloc-ed data
- * as possible.
- */
+ /* Re-use structure if cached previously. */
if (asn1) {
if (asn1->nData != length) {
- free(asn1->cpData); /* XXX: realloc? */
- asn1->cpData = NULL;
+ asn1->cpData = ap_realloc(asn1->cpData, length);
}
}
else {
asn1 = ap_malloc(sizeof(*asn1));
asn1->source_mtime = 0; /* used as a note for encrypted private keys */
- asn1->cpData = NULL;
- }
-
- asn1->nData = length;
- if (!asn1->cpData) {
asn1->cpData = ap_malloc(length);
+
+ apr_hash_set(table, key, klen, asn1);
}
- apr_hash_set(table, key, klen, asn1);
+ asn1->nData = length;
+ p = asn1->cpData;
+ i2d_PrivateKey(pkey, &p); /* increases p by length */
- return asn1->cpData; /* caller will assign a value to this */
+ return asn1;
}
ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
@@ -306,6 +298,7 @@ static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file,
* away in the destruction callback.
*/
apr_pool_create(&p, dynlockpool);
+ apr_pool_tag(p, "modssl_dynlock_value");
ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, p,
"Creating dynamic lock");
@@ -480,3 +473,13 @@ void ssl_util_thread_id_setup(apr_pool_t *p)
}
#endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
+
+int modssl_is_engine_id(const char *name)
+{
+#if MODSSL_HAVE_ENGINE_API
+ /* ### Can handle any other special ENGINE key names here? */
+ return strncmp(name, "pkcs11:", 7) == 0;
+#else
+ return 0;
+#endif
+}
diff --git a/modules/ssl/ssl_util_ocsp.c b/modules/ssl/ssl_util_ocsp.c
index b66e151..a202a72 100644
--- a/modules/ssl/ssl_util_ocsp.c
+++ b/modules/ssl/ssl_util_ocsp.c
@@ -46,6 +46,7 @@ static BIO *serialize_request(OCSP_REQUEST *req, const apr_uri_t *uri,
BIO_printf(bio, "%s%s%s HTTP/1.0\r\n"
"Host: %s:%d\r\n"
"Content-Type: application/ocsp-request\r\n"
+ "Connection: close\r\n"
"Content-Length: %d\r\n"
"\r\n",
uri->path ? uri->path : "/",
@@ -369,8 +370,11 @@ static STACK_OF(X509) *modssl_read_ocsp_certificates(const char *file)
while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
if (!other_certs) {
other_certs = sk_X509_new_null();
- if (!other_certs)
+ if (!other_certs) {
+ X509_free(x509);
+ BIO_free(bio);
return NULL;
+ }
}
if (!sk_X509_push(other_certs, x509)) {
diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c
index b7f0eca..44930b7 100644
--- a/modules/ssl/ssl_util_ssl.c
+++ b/modules/ssl/ssl_util_ssl.c
@@ -74,7 +74,7 @@ void modssl_set_app_data2(SSL *ssl, void *arg)
** _________________________________________________________________
*/
-EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s)
{
EVP_PKEY *rc;
BIO *bioS;
@@ -83,7 +83,7 @@ EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_passw
/* 1. try PEM (= DER+Base64+headers) */
if ((bioS=BIO_new_file(filename, "r")) == NULL)
return NULL;
- rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
+ rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s);
BIO_free(bioS);
if (rc == NULL) {
@@ -107,41 +107,9 @@ EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_passw
BIO_free(bioS);
}
}
- if (rc != NULL && key != NULL) {
- if (*key != NULL)
- EVP_PKEY_free(*key);
- *key = rc;
- }
return rc;
}
-typedef struct {
- const char *pass;
- int pass_len;
-} pass_ctx;
-
-static int provide_pass(char *buf, int size, int rwflag, void *baton)
-{
- pass_ctx *ctx = baton;
- if (ctx->pass_len > 0) {
- if (ctx->pass_len < size) {
- size = (int)ctx->pass_len;
- }
- memcpy(buf, ctx->pass, size);
- }
- return ctx->pass_len;
-}
-
-EVP_PKEY *modssl_read_encrypted_pkey(const char *filename, EVP_PKEY **key,
- const char *pass, apr_size_t pass_len)
-{
- pass_ctx ctx;
-
- ctx.pass = pass;
- ctx.pass_len = pass_len;
- return modssl_read_privatekey(filename, key, provide_pass, &ctx);
-}
-
/* _________________________________________________________________
**
** Smart shutdown
@@ -156,7 +124,7 @@ int modssl_smart_shutdown(SSL *ssl)
/*
* Repeat the calls, because SSL_shutdown internally dispatches through a
- * little state machine. Usually only one or two interation should be
+ * little state machine. Usually only one or two iterations should be
* needed, so we restrict the total number of restrictions in order to
* avoid process hangs in case the client played bad with the socket
* connection and OpenSSL cannot recognize it.
@@ -166,7 +134,7 @@ int modssl_smart_shutdown(SSL *ssl)
for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
rc = SSL_shutdown(ssl);
if (rc >= 0 && flush && (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN)) {
- /* Once the close notity is sent through the output filters,
+ /* Once the close notify is sent through the output filters,
* ensure it is flushed through the socket.
*/
if (BIO_flush(SSL_get_wbio(ssl)) <= 0) {
@@ -217,14 +185,27 @@ BOOL modssl_X509_getBC(X509 *cert, int *ca, int *pathlen)
return TRUE;
}
+char *modssl_bio_free_read(apr_pool_t *p, BIO *bio)
+{
+ int len = BIO_pending(bio);
+ char *result = NULL;
+
+ if (len > 0) {
+ result = apr_palloc(p, len+1);
+ len = BIO_read(bio, result, len);
+ result[len] = NUL;
+ }
+ BIO_free(bio);
+ return result;
+}
+
/* Convert ASN.1 string to a pool-allocated char * string, escaping
* control characters. If raw is zero, convert to UTF-8, otherwise
* unchanged from the character set. */
static char *asn1_string_convert(apr_pool_t *p, ASN1_STRING *asn1str, int raw)
{
- char *result = NULL;
BIO *bio;
- int len, flags = ASN1_STRFLGS_ESC_CTRL;
+ int flags = ASN1_STRFLGS_ESC_CTRL;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
@@ -232,14 +213,8 @@ static char *asn1_string_convert(apr_pool_t *p, ASN1_STRING *asn1str, int raw)
if (!raw) flags |= ASN1_STRFLGS_UTF8_CONVERT;
ASN1_STRING_print_ex(bio, asn1str, flags);
- len = BIO_pending(bio);
- if (len > 0) {
- result = apr_palloc(p, len+1);
- len = BIO_read(bio, result, len);
- result[len] = NUL;
- }
- BIO_free(bio);
- return result;
+
+ return modssl_bio_free_read(p, bio);
}
#define asn1_string_to_utf8(p, a) asn1_string_convert(p, a, 0)
@@ -489,29 +464,52 @@ BOOL modssl_X509_match_name(apr_pool_t *p, X509 *x509, const char *name,
** _________________________________________________________________
*/
-DH *ssl_dh_GetParamFromFile(const char *file)
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DH *modssl_dh_from_file(const char *file)
{
- DH *dh = NULL;
+ DH *dh;
BIO *bio;
if ((bio = BIO_new_file(file, "r")) == NULL)
return NULL;
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
- return (dh);
+
+ return dh;
+}
+#else
+EVP_PKEY *modssl_dh_pkey_from_file(const char *file)
+{
+ EVP_PKEY *pkey;
+ BIO *bio;
+
+ if ((bio = BIO_new_file(file, "r")) == NULL)
+ return NULL;
+ pkey = PEM_read_bio_Parameters(bio, NULL);
+ BIO_free(bio);
+
+ return pkey;
}
+#endif
#ifdef HAVE_ECC
-EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
+EC_GROUP *modssl_ec_group_from_file(const char *file)
{
- EC_GROUP *group = NULL;
+ EC_GROUP *group;
BIO *bio;
if ((bio = BIO_new_file(file, "r")) == NULL)
return NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
+#else
+ group = PEM_ASN1_read_bio((void *)d2i_ECPKParameters,
+ PEM_STRING_ECPARAMETERS, bio,
+ NULL, NULL, NULL);
+#endif
BIO_free(bio);
- return (group);
+
+ return group;
}
#endif
@@ -536,3 +534,81 @@ char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *id, int idlen,
return str;
}
+
+/* _________________________________________________________________
+**
+** Certificate/Key Stuff
+** _________________________________________________________________
+*/
+
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey)
+{
+ BIO *in;
+ X509 *x = NULL;
+ EVP_PKEY *key = NULL;
+ apr_status_t rv = APR_SUCCESS;
+
+ in = BIO_new_mem_buf(cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM;
+ goto cleanup;
+ }
+
+ x = PEM_read_bio_X509(in, NULL, cb, ud);
+ if (x == NULL) {
+ rv = APR_ENOENT;
+ goto cleanup;
+ }
+
+ BIO_free(in);
+ in = BIO_new_mem_buf(key_pem? key_pem : cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM;
+ goto cleanup;
+ }
+ key = PEM_read_bio_PrivateKey(in, NULL, cb, ud);
+ if (key == NULL) {
+ rv = APR_ENOENT;
+ goto cleanup;
+ }
+
+cleanup:
+ if (rv == APR_SUCCESS) {
+ *pcert = x;
+ *pkey = key;
+ }
+ else {
+ *pcert = NULL;
+ *pkey = NULL;
+ if (x) X509_free(x);
+ if (key) EVP_PKEY_free(key);
+ }
+ if (in != NULL) BIO_free(in);
+ return rv;
+}
+
+apr_status_t modssl_cert_get_pem(apr_pool_t *p,
+ X509 *cert1, X509 *cert2,
+ const char **ppem)
+{
+ apr_status_t rv = APR_ENOMEM;
+ BIO *bio;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto cleanup;
+ if (PEM_write_bio_X509(bio, cert1) != 1) goto cleanup;
+ if (cert2 && PEM_write_bio_X509(bio, cert2) != 1) goto cleanup;
+ rv = APR_SUCCESS;
+
+cleanup:
+ if (rv != APR_SUCCESS) {
+ *ppem = NULL;
+ if (bio) BIO_free(bio);
+ }
+ else {
+ *ppem = modssl_bio_free_read(p, bio);
+ }
+ return rv;
+}
diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h
index c67dacf..443c1b7 100644
--- a/modules/ssl/ssl_util_ssl.h
+++ b/modules/ssl/ssl_util_ssl.h
@@ -64,8 +64,11 @@
void modssl_init_app_data2_idx(void);
void *modssl_get_app_data2(SSL *);
void modssl_set_app_data2(SSL *, void *);
-EVP_PKEY *modssl_read_privatekey(const char *, EVP_PKEY **, pem_password_cb *, void *);
-EVP_PKEY *modssl_read_encrypted_pkey(const char *, EVP_PKEY **, const char *, apr_size_t);
+
+/* Read private key from filename in either PEM or raw base64(DER)
+ * format, using password entry callback cb and userdata. */
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud);
+
int modssl_smart_shutdown(SSL *ssl);
BOOL modssl_X509_getBC(X509 *, int *, int *);
char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
@@ -75,6 +78,30 @@ BOOL modssl_X509_getSAN(apr_pool_t *, X509 *, int, const char *, int, apr
BOOL modssl_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *);
char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *, int, char *, int);
+/* Reads the remaining data in BIO, if not empty, and copies it into a
+ * pool-allocated string. If empty, returns NULL. BIO_free(bio) is
+ * called for both cases. */
+char *modssl_bio_free_read(apr_pool_t *p, BIO *bio);
+
+/* Read a single certificate and its private key from the given string in PEM format.
+ * If `key_pem` is NULL, it will expect the key in `cert_pem`.
+ */
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey);
+
+/* Convert a certificate (and optionally a second) into a PEM string.
+ * @param p pool for allocations
+ * @param cert1 the certificate to convert
+ * @param cert2 a second cert to add to the PEM afterwards or NULL.
+ * @param ppem the certificate(s) in PEM format, NUL-terminated.
+ * @return APR_SUCCESS if ppem is valid.
+ */
+apr_status_t modssl_cert_get_pem(apr_pool_t *p,
+ X509 *cert1, X509 *cert2,
+ const char **ppem);
+
#endif /* __SSL_UTIL_SSL_H__ */
/** @} */
diff --git a/modules/ssl/ssl_util_stapling.c b/modules/ssl/ssl_util_stapling.c
index c3e2cfa..563de55 100644
--- a/modules/ssl/ssl_util_stapling.c
+++ b/modules/ssl/ssl_util_stapling.c
@@ -29,16 +29,32 @@
-- Alexei Sayle */
#include "ssl_private.h"
+
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer),
+ (s, p, cert, issuer),
+ DECLINED, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert),
+ (pder, pderlen, c, s, cert),
+ DECLINED, DECLINED)
+
+
#ifdef HAVE_OCSP_STAPLING
static int stapling_cache_mutex_on(server_rec *s);
static int stapling_cache_mutex_off(server_rec *s);
+static int stapling_cb(SSL *ssl, void *arg);
+
/**
- * Maxiumum OCSP stapling response size. This should be the response for a
+ * Maximum OCSP stapling response size. This should be the response for a
* single certificate and will typically include the responder certificate chain
* so 10K should be more than enough.
*
@@ -101,8 +117,10 @@ static X509 *stapling_get_issuer(modssl_ctx_t *mctx, X509 *x)
}
inctx = X509_STORE_CTX_new();
- if (!X509_STORE_CTX_init(inctx, st, NULL, NULL))
+ if (!X509_STORE_CTX_init(inctx, st, NULL, NULL)) {
+ X509_STORE_CTX_free(inctx);
return 0;
+ }
if (X509_STORE_CTX_get1_issuer(&issuer, inctx, x) <= 0)
issuer = NULL;
X509_STORE_CTX_cleanup(inctx);
@@ -118,10 +136,51 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
X509 *issuer = NULL;
OCSP_CERTID *cid = NULL;
STACK_OF(OPENSSL_STRING) *aia = NULL;
+ const char *pem = NULL;
+ int rv = 1; /* until further notice */
- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1))
+ if (x == NULL)
return 0;
+ if (!(issuer = stapling_get_issuer(mctx, x))) {
+ /* In Apache pre 2.4.40, we use to come here only when mod_ssl stapling
+ * was enabled. With the new hooks, we give other modules the chance
+ * to provide stapling status. However, we do not want to log ssl errors
+ * where we did not do so in the past. */
+ if (mctx->stapling_enabled == TRUE) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217)
+ "ssl_stapling_init_cert: can't retrieve issuer "
+ "certificate!");
+ return 0;
+ }
+ return 1;
+ }
+
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (modssl_cert_get_pem(ptemp, x, issuer, &pem) != APR_SUCCESS) {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (ap_ssl_ocsp_prime(s, p, (const char*)idx, sizeof(idx), pem) == APR_SUCCESS
+ || ssl_run_init_stapling_status(s, p, x, issuer) == OK) {
+ /* Someone's taken over or mod_ssl's own implementation is not enabled */
+ if (mctx->stapling_enabled != TRUE) {
+ SSL_CTX_set_tlsext_status_cb(mctx->ssl_ctx, stapling_cb);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10177) "OCSP stapling added via hook");
+ }
+ goto cleanup;
+ }
+
+ if (mctx->stapling_enabled != TRUE) {
+ /* mod_ssl's own implementation is not enabled */
+ goto cleanup;
+ }
+
cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx));
if (cinf) {
/*
@@ -134,25 +193,18 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
APLOGNO(02814) "ssl_stapling_init_cert: no OCSP URI "
"in certificate and no SSLStaplingForceURL "
"configured for server %s", mctx->sc->vhost_id);
- return 0;
+ rv = 0;
}
- return 1;
- }
-
- if (!(issuer = stapling_get_issuer(mctx, x))) {
- ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217)
- "ssl_stapling_init_cert: can't retrieve issuer "
- "certificate!");
- return 0;
+ goto cleanup;
}
cid = OCSP_cert_to_id(NULL, x, issuer);
- X509_free(issuer);
if (!cid) {
ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02815)
"ssl_stapling_init_cert: can't create CertID "
"for OCSP request");
- return 0;
+ rv = 0;
+ goto cleanup;
}
aia = X509_get1_ocsp(x);
@@ -161,7 +213,8 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x,
APLOGNO(02218) "ssl_stapling_init_cert: no OCSP URI "
"in certificate and no SSLStaplingForceURL set");
- return 0;
+ rv = 0;
+ goto cleanup;
}
/* At this point, we have determined that there's something to store */
@@ -183,19 +236,16 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
apr_hash_set(stapling_certinfo, cinf->idx, sizeof(cinf->idx), cinf);
- return 1;
+cleanup:
+ X509_free(issuer);
+ return rv;
}
-static certinfo *stapling_get_certinfo(server_rec *s, modssl_ctx_t *mctx,
- SSL *ssl)
+static certinfo *stapling_get_certinfo(server_rec *s, UCHAR *idx, apr_size_t idx_len,
+ modssl_ctx_t *mctx, SSL *ssl)
{
certinfo *cinf;
- X509 *x;
- UCHAR idx[SHA_DIGEST_LENGTH];
- x = SSL_get_certificate(ssl);
- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1))
- return NULL;
- cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx));
+ cinf = apr_hash_get(stapling_certinfo, idx, idx_len);
if (cinf && cinf->cid)
return cinf;
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01926)
@@ -397,7 +447,7 @@ static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx,
rv = SSL_TLSEXT_ERR_NOACK;
}
- if (status != V_OCSP_CERTSTATUS_GOOD) {
+ if (status != V_OCSP_CERTSTATUS_GOOD && pok) {
char snum[MAX_STRING_LEN] = { '\0' };
BIO *bio = BIO_new(BIO_s_mem());
@@ -418,12 +468,6 @@ static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx,
(reason != OCSP_REVOKED_STATUS_NOSTATUS) ?
OCSP_crl_reason_str(reason) : "n/a",
snum[0] ? snum : "[n/a]");
-
- if (mctx->stapling_return_errors == FALSE) {
- if (pok)
- *pok = FALSE;
- rv = SSL_TLSEXT_ERR_NOACK;
- }
}
}
@@ -482,6 +526,7 @@ static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl,
/* Create a temporary pool to constrain memory use */
apr_pool_create(&vpool, conn->pool);
+ apr_pool_tag(vpool, "modssl_stapling_renew");
if (apr_uri_parse(vpool, ocspuri, &uri) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01939)
@@ -732,6 +777,23 @@ static int get_and_check_cached_response(server_rec *s, modssl_ctx_t *mctx,
return 0;
}
+typedef struct {
+ unsigned char *data;
+ apr_size_t len;
+} ocsp_resp;
+
+static void copy_ocsp_resp(const unsigned char *der, apr_size_t der_len, void *userdata)
+{
+ ocsp_resp *resp = userdata;
+
+ resp->len = 0;
+ resp->data = der? OPENSSL_malloc(der_len) : NULL;
+ if (resp->data) {
+ memcpy(resp->data, der, der_len);
+ resp->len = der_len;
+ }
+}
+
/* Certificate Status callback. This is called when a client includes a
* certificate status request extension.
*
@@ -744,24 +806,53 @@ static int stapling_cb(SSL *ssl, void *arg)
conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
server_rec *s = mySrvFromConn(conn);
SSLSrvConfigRec *sc = mySrvConfig(s);
- SSLConnRec *sslconn = myConnConfig(conn);
- modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
+ modssl_ctx_t *mctx = myConnCtxConfig(conn, sc);
+ UCHAR idx[SHA_DIGEST_LENGTH];
+ ocsp_resp resp;
certinfo *cinf = NULL;
OCSP_RESPONSE *rsp = NULL;
int rv;
BOOL ok = TRUE;
+ X509 *x;
+ int rspderlen, provided = 0;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
+ "stapling_cb: OCSP Stapling callback called");
+
+ x = SSL_get_certificate(ssl);
+ if (x == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (ap_ssl_ocsp_get_resp(s, conn, (const char*)idx, sizeof(idx),
+ copy_ocsp_resp, &resp) == APR_SUCCESS) {
+ provided = 1;
+ }
+ else if (ssl_run_get_stapling_status(&resp.data, &rspderlen, conn, s, x) == APR_SUCCESS) {
+ resp.len = (apr_size_t)rspderlen;
+ provided = 1;
+ }
+ if (provided) {
+ /* a hook handles stapling for this certificate and determines the response */
+ if (resp.data == NULL || resp.len == 0) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ SSL_set_tlsext_status_ocsp_resp(ssl, resp.data, (int)resp.len);
+ return SSL_TLSEXT_ERR_OK;
+ }
+
if (sc->server->stapling_enabled != TRUE) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950)
"stapling_cb: OCSP Stapling disabled");
return SSL_TLSEXT_ERR_NOACK;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
- "stapling_cb: OCSP Stapling callback called");
-
- cinf = stapling_get_certinfo(s, mctx, ssl);
- if (cinf == NULL) {
+ if ((cinf = stapling_get_certinfo(s, idx, sizeof(idx), mctx, ssl)) == NULL) {
return SSL_TLSEXT_ERR_NOACK;
}
@@ -818,15 +909,21 @@ static int stapling_cb(SSL *ssl, void *arg)
if (rsp && ((ok == TRUE) || (mctx->stapling_return_errors == TRUE))) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01956)
"stapling_cb: setting response");
- if (!stapling_set_response(ssl, rsp))
- return SSL_TLSEXT_ERR_ALERT_FATAL;
- return SSL_TLSEXT_ERR_OK;
+ if (!stapling_set_response(ssl, rsp)) {
+ rv = SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ else {
+ rv = SSL_TLSEXT_ERR_OK;
+ }
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01957)
- "stapling_cb: no suitable response available");
-
- return SSL_TLSEXT_ERR_NOACK;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01957)
+ "stapling_cb: no suitable response available");
+ rv = SSL_TLSEXT_ERR_NOACK;
+ }
+ OCSP_RESPONSE_free(rsp); /* NULL safe */
+ return rv;
}
apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p,
@@ -864,9 +961,10 @@ apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p,
if (mctx->stapling_responder_timeout == UNSET) {
mctx->stapling_responder_timeout = 10 * APR_USEC_PER_SEC;
}
+
SSL_CTX_set_tlsext_status_cb(ctx, stapling_cb);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01960) "OCSP stapling initialized");
-
+
return APR_SUCCESS;
}