# USE_OPENSSL : enable use of OpenSSL. Recommended, but see below.
# USE_OPENSSL_AWSLC : enable use of AWS-LC
# USE_OPENSSL_WOLFSSL : enable use of wolfSSL with the OpenSSL API
+# USE_ECH : enable use of ECH with the OpenSSL API
# USE_QUIC : enable use of QUIC with the quictls API (quictls, libressl, boringssl)
# USE_QUIC_OPENSSL_COMPAT : enable use of QUIC with the standard openssl API (limited features)
# USE_ENGINE : enable use of OpenSSL Engine.
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
+ USE_ECH \
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
- src/httpclient_cli.o src/version.o src/ncbmbuf.o
+ src/httpclient_cli.o src/version.o src/ncbmbuf.o src/ech.o
ifneq ($(TRACE),)
OBJS += src/calltrace.o
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifdef USE_ECH
+
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <haproxy/applet.h>
+#include <haproxy/cli.h>
+#include <haproxy/ech.h>
+#include <haproxy/fd.h>
+#include <haproxy/global.h>
+#include <haproxy/listener.h>
+#include <haproxy/log.h>
+#include <haproxy/obj_type.h>
+#include <haproxy/openssl-compat.h>
+#include <haproxy/proxy.h>
+#include <haproxy/ssl_sock-t.h>
+
+
+/*
+ * load any key files called <name>.ech we find in the named
+ * directory
+ */
+int load_echkeys(SSL_CTX *ctx, char *dirname, int *loaded)
+{
+ struct dirent **de_list = NULL;
+ struct stat thestat;
+ int rv = 0, i, nrv, somekeyworked = 0;
+ char *den = NULL, *last4 = NULL, privname[PATH_MAX];
+ size_t elen = 0, nlen = 0;
+ OSSL_ECHSTORE * const es = OSSL_ECHSTORE_new(NULL, NULL);
+
+ if (es == NULL)
+ goto end;
+ nrv = scandir(dirname, &de_list, 0, alphasort);
+ if (nrv < 0)
+ goto end;
+ for (i = 0; i != nrv; i++) {
+ struct dirent *de = de_list[i];
+
+ den = de->d_name;
+ nlen = strlen(den);
+ if (nlen > 4) {
+ last4 = den + nlen - 4;
+ if (strncmp(last4, ".ech", 4))
+ goto ignore_entry;
+ if ((elen + 1 + nlen + 1) >= PATH_MAX)
+ goto ignore_entry;
+ snprintf(privname, PATH_MAX,"%s/%s", dirname, den);
+ if (stat(privname, &thestat) == 0) {
+ BIO *in = BIO_new_file(privname, "r");
+ const int is_retry_config = OSSL_ECH_FOR_RETRY;
+
+ if (in != NULL && 1 == OSSL_ECHSTORE_read_pem(es, in, is_retry_config))
+ somekeyworked = 1;
+ BIO_free_all(in);
+ }
+ }
+ignore_entry:
+ free(de);
+ }
+
+ if (somekeyworked == 0)
+ goto end;
+ if (OSSL_ECHSTORE_num_keys(es, loaded) != 1)
+ goto end;
+ if (1 != SSL_CTX_set1_echstore(ctx, es))
+ goto end;
+ rv = 1;
+end:
+ free(de_list);
+ OSSL_ECHSTORE_free(es);
+ return rv;
+}
+
+
+static int bind_parse_ech(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ if (!experimental_directives_allowed) {
+ memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'",
+ args[0]);
+ return -1;
+ }
+ mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
+
+ free(conf->ssl_conf.ech_filedir);
+ conf->ssl_conf.ech_filedir = strdup(args[cur_arg+1]);
+ return 0;
+}
+
+
+static struct bind_kw_list bind_kws = { "SSL", { }, {
+ { "ech", bind_parse_ech, 1 }, /* set ECH PEM file */
+ { 0, NULL, 0 },
+}};
+
+
+INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
+#endif
#include <haproxy/ssl_ocsp.h>
#include <haproxy/trace.h>
#include <haproxy/ssl_trace.h>
-
+#ifdef USE_ECH
+#include <haproxy/ech.h>
+#endif
/* ***** READ THIS before adding code here! *****
*
if (global_ssl.security_level > -1)
SSL_CTX_set_security_level(ctx, global_ssl.security_level);
+#ifdef USE_ECH
+ if (bind_conf->ssl_conf.ech_filedir) {
+ int loaded = 0;
+
+ if (load_echkeys(ctx, bind_conf->ssl_conf.ech_filedir, &loaded) != 1) {
+ cfgerr += 1;
+ ha_alert("Proxy '%s': failed to load ECH key s from %s for '%s' at [%s:%d].\n",
+ bind_conf->frontend->id, bind_conf->ssl_conf.ech_filedir,
+ bind_conf->arg, bind_conf->file, bind_conf->line);
+ }
+ }
+#endif
+
if (conf_ssl_methods->flags && (conf_ssl_methods->min || conf_ssl_methods->max))
ha_warning("Proxy '%s': no-sslv3/no-tlsv1x are ignored for bind '%s' at [%s:%d]. "
"Use only 'ssl-min-ver' and 'ssl-max-ver' to fix.\n",
px->id, bind_conf->arg, bind_conf->file, bind_conf->line);
}
else {
+#ifdef USE_ECH
+ if (!bind_conf->ssl_conf.ech_filedir) {
+#endif
ha_alert("Proxy '%s': no SSL certificate specified for bind '%s' at [%s:%d] (use 'crt').\n",
px->id, bind_conf->arg, bind_conf->file, bind_conf->line);
return -1;
+#ifdef USE_ECH
+ }
+#endif
}
}