From: Joe Orton Date: Fri, 12 Sep 2025 08:05:11 +0000 (+0000) Subject: mod_ssl: Add support for Encrypted Client Hello (ECH) based off X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0c9cd095ce9081fd225f0da7787419e80de7c701;p=thirdparty%2Fapache%2Fhttpd.git mod_ssl: Add support for Encrypted Client Hello (ECH) based off proposed OpenSSL 4.0 API. Notes from PR #551: This build only supports ECH "shared-mode" where mod_ssl does the ECH decryption and also hosts both the ECH `public-name` and `backend` web sites. ## Build > [!NOTE] > ECH is not yet a part of an OpenSSL release, our current goal is that ECH be > part of an OpenSSL 4.0 release in spring 2026. There is client and server ECH code in the OpenSSL ECH feature branch at [https://github.com/openssl/openssl/tree/feature/ech](https://github.com/openssl/openssl/tree/feature/ech). At present, ECH-enabling apache2 therefore requires building from source, using the OpenSSL ECH feature branch. ## Code changes - All code changes are within `modules/ssl` and are protected via `#ifdef HAVE_OPENSSL_ECH`. That's defined in `ssl_private.h` if the included `ssl.h` defines `SSL_OP_ECH_GREASE`. - There're a bunch of changes to add the new `SSLECHKeyDir` directive that are mosly obvious. - We load the keys from `SSLECHKeyDir` using the `load_echkeys()` function in `ssl_engine_init.c`. That also ECH-enables the `SSL_CTX` when keys are loaded, which triggers ECH decryption as needed. > [!NOTE] > `load_echkeys()` will include the public component all loaded keys in the ECH > `retry-configs` in the fallback scenario. If desired, we could add a naming > convention or additional configuration setting to distinguish which to > include in `retry-configs` or not. For now, we assume that'd better be done > in a subsequent PR, if experience shows the feature is really useful/needed. > (We can envisage some odd deployments where that might be the case, but not > clear those'd really happen - it'd seem to need loads of key pairs or else > some that are never published in the DNS that we don't want to expose to > random clients - neither seems compelling.) - We add a callback to `SSL_CTX_ech_set_callback` also in `ssl_engine_init.c`. - We add calls to set the `SSL_ECH_STATUS` etc. variables to the environment (for PHP etc) in `ssl_engine_kernel.c` and also do the logging of ECH outcomes (to the error log). Submitted by: sftcd , rpluem Github: closes #551 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1928357 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 449532d5df..dd89c3aebf 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -316,6 +316,18 @@ jobs: APU_CONFIG="--without-crypto" pkgs: subversion # ------------------------------------------------------------------------- + - name: OpenSSL ECH branch + config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto + notest-cflags: -Werror -O2 + env: | + TEST_OPENSSL3=ech + TEST_OPENSSL3_BRANCH=feature/ech + OPENSSL_CONFIG=no-engine + APR_VERSION=1.7.6 + APU_VERSION=1.6.3 + APU_CONFIG="--without-crypto" + pkgs: subversion + # ------------------------------------------------------------------------- runs-on: ${{ matrix.os == '' && 'ubuntu-latest' || matrix.os }} timeout-minutes: 30 env: diff --git a/changes-entries/ech.txt b/changes-entries/ech.txt new file mode 100644 index 0000000000..3d3ce480a5 --- /dev/null +++ b/changes-entries/ech.txt @@ -0,0 +1,2 @@ + *) mod_ssl: Add support for Encrypted Client Hello (ECH) + Github #551. [Stephen Farrell ] diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 9a5b59edd0..772a41124d 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -10519 +10542 diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index fe92af5bb7..854a84dc73 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -118,6 +118,9 @@ compatibility variables.

SSL_CLIENTHELLO_SIG_ALGOS string Value of Signature Algorithms extension (13) from ClientHello as four hex encoded characters per item SSL_CLIENTHELLO_ALPN string Value of ALPN extension (16) from ClientHello as hex encoded string including leading string lengths SSL_CLIENTHELLO_VERSIONS string Value of Supported Versions extension (43) from ClientHello as four hex encoded characters per item +SSL_ECH_STATUS string success means that others also mean what they say +SSL_ECH_INNER_SNI string SNI value that was encrypted in ECH (or `NONE`) +SSL_ECH_OUTER_SNI string SNI value that was seen in plaintext SNI (or `NONE`)

x509 specifies a component of an X.509 DN; one of @@ -3016,4 +3019,123 @@ httpd -t -D DUMP_SSL_POLICIES + +SSLECHKeyDir +Load the set of Encrypted Client Hello (ECH) PEM files in the named directory +SSLECHKeyDir dirname +server config +Available in Apache HTTP Server 2.5.1 and later + + + +

+ECH is specified in + draft-ietf-tls-esni +httpd supports ECH "shared-mode" where the httpd instance does the +ECH decryption and also hosts both the ECH `public-name` and `backend` web +sites. +

+ +

+The SSLECHKeyDir directive +names the directory where ECH PEM files (named *.ech) are stored. +Once an ECH PEM file is successfully loaded, httpd will perform ECH decryption +and, if that succeeds, will process the relevant TLS session using the +SNI from the inner ClientHello. +

+ +Example ECH Config + +... +SSLEngine On +SSLProtocol TLSv1.3 +SSLECHKeyDir /etc/apache2/echkeydir +... +# virtual hosts +<VirtualHost *:443> + SSLEngine On + SSLProtocol TLSv1.3 + ServerName example.com + DocumentRoot "/var/www/dir-example.com" +</VirtualHost> +<VirtualHost *:443> + SSLEngine On + SSLProtocol TLSv1.3 + ServerName foo.example.com + DocumentRoot "/var/www/dir-foo.example.com" +</VirtualHost> +... + + + +ECH Key Generation and Publication +

+In the above, we describe a configuration that uses example.com as the +ECH public-name and where foo.example.com is a web-site for which we want +ECH to be used, with both hosted on the same httpd instance. +

+

+Using ECH requries that httpd load an ECH key pair with a private value for ECH +decryption. Browsers will require that the public component of that key pair be +published in the DNS. With OpenSSL we generate and store that key pair in an ECH PEM +formatted file as shown below. +

+

+To generate ECH PEM files, use the ECH-enabled openssl command line +to generate an ECH key pair and store the result in an ECH PEM file. +You must also supply the public-name required by the ECH protocol. +

+

+Key generation operations should be carried out under whatever local account is +used for httpd configuration. +

+Example: ECH Key Generation + +~# OSSL=/home/user/code/openssl/apps/openssl +~# mkdir -p /etc/apache2/echkeydir +~# chmod 700 /etc/apache2/echkeydir +~# cd /etc/apache2/echkeydir +~# $OSSL ech -public-name example.com -o example.com.pem.ech +~# cat example.com.pem.ech +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VuBCIEIJi22Im2rJ/lJqzNFZdGfsVfmknXAc8xz3fYPhD0Na5I +-----END PRIVATE KEY----- +-----BEGIN ECHCONFIG----- +AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEA +AQALZXhhbXBsZS5vcmcAAA== +-----END ECHCONFIG----- + + +

+The ECHConfig value then needs to be published in an HTTPS resource record in +the DNS, so as to be accessible as shown below: +

+Accessing an ECH config from DNS + +$ dig +short HTTPS foo.example.com +1 . ech=AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEAAQALZXhhbXBsZS5vcmcAAA== + + +

+Various other fields may be included in an HTTPS resource record. For many +httpd deployments, existing methods for publishing DNS records may be used to +achieve the above. In some cases, one might use + +A well-known URI for publishing service parameters +designed to assist web servers in handling e.g. frequent ECH key rotation. +

+
+ +Reloading ECH Keys + +

+Giving httpd a command line argument of -k graceful causes a graceful reload +of the configuration, without dropping existing connections. +

+ +
+ + + + diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index e48efb1a47..7ccc06006c 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -121,6 +121,11 @@ static const command_rec ssl_config_cmds[] = { SSL_CMD_SRV(SessionTicketKeyFile, TAKE1, "TLS session ticket encryption/decryption key file (RFC 5077) " "('/path/to/file' - file with 48 bytes of random data)") +#endif +#ifdef HAVE_OPENSSL_ECH + SSL_CMD_SRV(ECHKeyDir, TAKE1, + "TLS ECH Key Directory" + "('/path/to/dir' - directory with ECH key pairs)") #endif SSL_CMD_ALL(CACertificatePath, TAKE1, "SSL CA Certificate path " diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 50c61bca32..e9c65e9995 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -222,6 +222,9 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) #endif sc->clienthello_vars = UNSET; sc->session_tickets = UNSET; +#ifdef HAVE_OPENSSL_ECH + sc->echkeydir = NULL; +#endif modssl_ctx_init_server(sc, p); @@ -356,6 +359,9 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) cfgMergeBool(compression); #endif cfgMergeBool(session_tickets); +#ifdef HAVE_OPENSSL_ECH + cfgMergeString(echkeydir); +#endif modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server); @@ -840,6 +846,25 @@ const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg) return "Argument must be On or Off"; } +#ifdef HAVE_OPENSSL_ECH +const char *ssl_cmd_SSLECHKeyDir(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->echkeydir=arg; + +#if !defined(SSL_HAVE_PROTOCOL_TLSV1_3) + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10533) + "ECHKeyDir configured but TLSv1.3 not supported - exiting."); + return "ECHKeyDir configured but TLSv1.3 not supported"; +#endif + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, cmd->server, + "%s: ECHKeyDir set to %s", + cmd->cmd->name, sc->echkeydir); + return NULL; +} +#endif + const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag) { #ifdef HAVE_FIPS @@ -2654,6 +2679,9 @@ static void ssl_srv_dump(SSLSrvConfigRec *sc, apr_pool_t *p, DMP_LONG( "SSLSessionCacheTimeout", sc->session_cache_timeout); DMP_ON_OFF("SSLStrictSNIVHostCheck", sc->strict_sni_vhost_check); DMP_ON_OFF("SSLSessionTickets", sc->session_tickets); +#ifdef HAVE_OPENSSL_ECH + DMP_STRING("SSLECHKeyDir", sc->echkeydir); +#endif } static void ssl_policy_dump(SSLSrvConfigRec *policy, apr_pool_t *p, diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index bdbe594215..c47684a3da 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -189,6 +189,114 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf, modver, AP_SERVER_BASEVERSION, incver); } +#ifdef HAVE_OPENSSL_ECH +/* + * Load any ECH PEM files we find in the ECHKeyDir directory + * Those are files matching "*.ech" + * The caller checks that echdir is non-NULL. + */ +static int load_echkeys(SSL_CTX *ctx, const char *echdir, server_rec *s, + apr_pool_t *ptemp) +{ + size_t elen = 0; + int keystried = 0, keysworked = 0, keysloaded=0; + OSSL_ECHSTORE *es = NULL; + apr_dir_t *dir = NULL; + apr_finfo_t direntry; + apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME; + apr_status_t dorv; + + elen = strlen(echdir); + if ((elen + 7) >= PATH_MAX) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10521) + "load_echkeys: directory name too long: %s - exiting", echdir); + return -1; + } + dorv = apr_dir_open(&dir, echdir, ptemp); + if (dorv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10522) + "load_echkeys: can't open directory %s - exiting (error %d)", + echdir, dorv); + return -1; + } + es = OSSL_ECHSTORE_new(NULL, NULL); + if (es == NULL) { + apr_dir_close(dir); + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10523) + "load_echkeys: can't alloc store"); + return -1; + } + + while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) { + const char *fname; + size_t pnlen = 0; + apr_finfo_t theinfo; + + if (direntry.filetype == APR_DIR) { + continue; /* don't try to load directories */ + } + fname = apr_pstrcat(ptemp, echdir, "/", direntry.name, NULL); + if (!fname) { + continue; + } + pnlen = strlen(fname); + if (pnlen < 5 || pnlen > (PATH_MAX-1)) { + continue; + } + if (!(fname[pnlen - 4] == '.' + && fname[pnlen - 3] == 'e' + && fname[pnlen - 2] == 'c' + && fname[pnlen - 1] == 'h')) { + continue; + } + if (apr_stat(&theinfo, fname, APR_FINFO_MIN, ptemp) == APR_SUCCESS) { + BIO *in = BIO_new_file(fname, "r"); + const int is_retry_config = OSSL_ECH_FOR_RETRY; + + keystried++; + if (in && OSSL_ECHSTORE_read_pem(es, in, is_retry_config) == 1) { + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, + "load_echkeys: worked for %s",fname); + keysworked++; + } + else { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10525) + "load_echkeys: failed for %s (could be non-fatal)", + fname); + } + BIO_free_all(in); + } + + } + apr_dir_close(dir); + + if (!OSSL_ECHSTORE_num_keys(es, &keysloaded)) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10526) + "OSSL_ECHSTORE_num_keys failed - exiting"); + OSSL_ECHSTORE_free(es); + return -1; + } + if (SSL_CTX_set1_echstore(ctx, es) != 1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10527) + "load_echkeys: SSL_CTX_set1_echstore failed"); + OSSL_ECHSTORE_free(es); + return -1; + } + OSSL_ECHSTORE_free(es); + if (keysworked == 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10528) + "load_echkeys: didn't load new keys (%d tried/failed) " + "but we have already some (%d) - continuing", + keystried, keysloaded); + } + else { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10529) + "ECH: %d keys loaded", keysloaded); + } + return 0; +} +#endif + /* _________________________________________________________________ ** ** Let other answer special connection attempts. @@ -545,6 +653,9 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s, modssl_ctx_t *mctx) { apr_status_t rv; +#ifdef HAVE_OPENSSL_ECH + SSLSrvConfigRec *sc = mySrvConfig(s); +#endif /* * Configure TLS extensions support @@ -577,6 +688,13 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s, SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL); #endif +#ifdef HAVE_OPENSSL_ECH + if (sc != NULL && sc->echkeydir != NULL) { + /* callback logs ECH outcome */ + SSL_CTX_ech_set_callback(mctx->ssl_ctx, ssl_callback_ECH); + } +#endif + #ifdef HAVE_OCSP_STAPLING /* * OCSP Stapling support, status_request extension @@ -903,7 +1021,30 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s, SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); } #endif - + +#ifdef HAVE_OPENSSL_ECH + /* ECH only really makes sense for TLSv1.3 */ + prot = SSL_CTX_get_max_proto_version(ctx); + if (sc->echkeydir) { + if (prot == TLS1_3_VERSION) { + /* try load the keys */ + if (load_echkeys(ctx, sc->echkeydir, s, ptemp) != 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10531) + "ECHKeyDir failed to load keys - exiting."); + SSL_CTX_free(ctx); + mctx->ssl_ctx = NULL; + return ssl_die(s); + } + } else { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10532) + "ECHKeyDir configured but TLSv1.3 turned off - exiting."); + SSL_CTX_free(ctx); + mctx->ssl_ctx = NULL; + return ssl_die(s); + } + } +#endif + return APR_SUCCESS; } diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index 45d986364f..aa9c76ec04 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -33,6 +33,10 @@ #include "util_md5.h" #include "scoreboard.h" +#ifdef HAVE_OPENSSL_ECH +#include +#endif + static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn); #ifdef HAVE_TLSEXT static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s); @@ -1516,6 +1520,11 @@ int ssl_hook_Fixup(request_rec *r) apr_table_set(env, "SSL_TLS_SNI", servername); } #endif +#ifdef HAVE_OPENSSL_ECH + extract_to_env(r, env, "SSL_ECH_INNER_SNI"); + extract_to_env(r, env, "SSL_ECH_OUTER_SNI"); + extract_to_env(r, env, "SSL_ECH_STATUS"); +#endif /* standard SSL environment variables */ if (dc->nOptions & SSL_OPT_STDENVVARS) { @@ -2377,6 +2386,46 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername) return APR_NOTFOUND; } +#ifdef HAVE_OPENSSL_ECH +unsigned int ssl_callback_ECH(SSL *ssl, const char *str) +{ + char *inner_sni = NULL, *outer_sni = NULL; + int echrv; + conn_rec *c = NULL; + const char *ech_servername; + + c = (conn_rec *)SSL_get_app_data(ssl); + ech_servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (ech_servername == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + echrv = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni); + switch (echrv) { + case SSL_ECH_STATUS_NOT_TRIED: + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH not attempted"); + break; + case SSL_ECH_STATUS_FAILED: + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH tried but failed"); + break; + case SSL_ECH_STATUS_BAD_NAME: + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH worked but bad name"); + break; + case SSL_ECH_STATUS_SUCCESS: + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "ECH success outer_sni: %s inner_sni: %s", + (outer_sni ? outer_sni : "NONE"), + (inner_sni ? inner_sni : "NONE")); + break; + default: + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "Error getting ECH status"); + } + OPENSSL_free(inner_sni); + OPENSSL_free(outer_sni); + return 1; +} +#endif + /* * This callback function is executed when OpenSSL encounters an extended * client hello with a server name indication extension ("SNI", cf. RFC 6066). diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c index 45a5a5bab6..34989f1d24 100644 --- a/modules/ssl/ssl_engine_vars.c +++ b/modules/ssl/ssl_engine_vars.c @@ -430,6 +430,50 @@ const char *ssl_var_lookup(apr_pool_t *p, server_rec *s, return result ? result : ""; } +#ifdef HAVE_OPENSSL_ECH +/* Extract ECH status variable from SSL object 'ssl' */ +static const char *ssl_var_lookup_ech_status(apr_pool_t *p, const char *var, + SSL *ssl) +{ + char *inner_sni = NULL, *outer_sni = NULL; + int echrv; + const char *result = NULL; + + if (ssl == NULL) + return result; + echrv = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni); + if (strcEQ(var, "STATUS")) { + switch (echrv) { + case SSL_ECH_STATUS_NOT_TRIED: + result = "not attempted"; + break; + case SSL_ECH_STATUS_FAILED: + result = "tried but failed"; + break; + case SSL_ECH_STATUS_BAD_NAME: + result = "ECH worked but bad name"; + break; + case SSL_ECH_STATUS_SUCCESS: + result = "success"; + break; + default: + result = "error getting ECH status"; + } + } + else if (echrv == SSL_ECH_STATUS_SUCCESS) { + if (strcEQ(var, "INNER_SNI")) { + result = apr_pstrdup(p, inner_sni); + } + if (strcEQ(var, "OUTER_SNI")) { + result = apr_pstrdup(p, outer_sni); + } + } + OPENSSL_free(inner_sni); + OPENSSL_free(outer_sni); + return result; +} +#endif + static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn, request_rec *r, const char *var) { @@ -544,6 +588,11 @@ static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn, } } #endif +#ifdef HAVE_OPENSSL_ECH + else if (ssl != NULL && strcEQn(var, "ECH_", 4)) { + result = ssl_var_lookup_ech_status(p, var+4, ssl); + } +#endif return result; } diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 071bd69b4c..3f64841737 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -127,6 +127,16 @@ #define MODSSL_HAVE_OPENSSL_STORE 0 #endif +/* + * Check if we have an ECH-enabled OpenSSL. If we do then this symbol will + * be defined in ssl.h and we compile in ECH code. + */ +#if defined(SSL_OP_ECH_GREASE) +#define HAVE_OPENSSL_ECH +#else +#undef HAVE_OPENSSL_ECH +#endif + #if (OPENSSL_VERSION_NUMBER < 0x0090801f) #error mod_ssl requires OpenSSL 0.9.8a or later #endif @@ -870,6 +880,9 @@ struct SSLSrvConfigRec { #endif BOOL session_tickets; BOOL clienthello_vars; +#ifdef HAVE_OPENSSL_ECH + const char *echkeydir; +#endif }; /** @@ -917,6 +930,9 @@ const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *); const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *); +#ifdef HAVE_OPENSSL_ECH +const char *ssl_cmd_SSLECHKeyDir(cmd_parms *cmd, void *dcfg, const char *arg); +#endif const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *); const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *); @@ -1031,6 +1047,9 @@ 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 *); +#ifdef HAVE_OPENSSL_ECH +unsigned int ssl_callback_ECH(SSL *, const char *); +#endif #endif #ifdef HAVE_TLS_SESSION_TICKETS int ssl_callback_SessionTicket(SSL *ssl, diff --git a/test/travis_before_linux.sh b/test/travis_before_linux.sh index 9b6267722d..b163e759f0 100755 --- a/test/travis_before_linux.sh +++ b/test/travis_before_linux.sh @@ -172,8 +172,12 @@ if test -v TEST_OPENSSL3; then mkdir -p build/openssl pushd build/openssl - curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" | - tar -xzf - + if test -v TEST_OPENSSL3_BRANCH; then + git clone -b $TEST_OPENSSL3_BRANCH -q https://github.com/openssl/openssl openssl-${TEST_OPENSSL3} + else + curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" | + tar -xzf - + fi cd openssl-${TEST_OPENSSL3} # Build with RPATH so ./bin/openssl doesn't require $LD_LIBRARY_PATH ./Configure --prefix=$HOME/root/openssl3 \