From f8399d4806ec2bb0b5d40e008957ed95f6fab89b Mon Sep 17 00:00:00 2001
From: Emmanuel Hocdet <manu@gandi.net>
Date: Thu, 24 Oct 2019 11:32:47 +0200
Subject: [PATCH 1/2] MINOR: ssl: deduplicate ca-file

Typically server line like:
'server-template srv 1-1000 *:443 ssl ca-file ca-certificates.crt'
load ca-certificates.crt 1000 times and stay duplicated in memory.
Same 'ca-file' can be load one time only and stay deduplicated in
memory. This optimisation need compatibility with OpenSSL >= 1.0.2.
---
 src/ssl_sock.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 144484590..7e61261e3 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -371,6 +371,49 @@ static int ssl_locking_init(void)
 
 __decl_hathreads(HA_SPINLOCK_T ckch_lock);
 
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+/*
+ * deduplicate cafile
+ */
+struct cafile_entry {
+	X509_STORE *ca_store;
+	struct ebmb_node node;
+	char path[0];
+};
+
+static struct eb_root cafile_tree = EB_ROOT_UNIQUE;
+
+static int ssl_load_verify_locations(SSL_CTX *ctx, char *path)
+{
+	struct ebmb_node *eb;
+	struct cafile_entry *ca_e;
+
+	eb = ebst_lookup(&cafile_tree, path);
+	if (eb)
+		ca_e = ebmb_entry(eb, struct cafile_entry, node);
+	else {
+		X509_STORE *store = X509_STORE_new();
+		if (X509_STORE_load_locations(store, path, NULL)) {
+			int pathlen;
+			pathlen = strlen(path);
+			ca_e = calloc(1, sizeof(*ca_e) + pathlen + 1);
+			memcpy(ca_e->path, path, pathlen + 1);
+			ca_e->ca_store = store;
+			ebst_insert(&cafile_tree, &ca_e->node);
+		} else {
+			X509_STORE_free(store);
+			return 0;
+		}
+	}
+	return SSL_CTX_set1_verify_cert_store(ctx, ca_e->ca_store);
+}
+#else
+static inline int ssl_load_verify_locations(SSL_CTX *ctx, char *path)
+{
+	return SSL_CTX_load_verify_locations(ctx, path, NULL);
+}
+#endif
+
 /* This memory pool is used for capturing clienthello parameters. */
 struct ssl_capture {
 	unsigned long long int xxh64;
@@ -4878,7 +4921,7 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_
 		char *crl_file = (ssl_conf && ssl_conf->crl_file) ? ssl_conf->crl_file : bind_conf->ssl_conf.crl_file;
 		if (ca_file) {
 			/* load CAfile to verify */
-			if (!SSL_CTX_load_verify_locations(ctx, ca_file, NULL)) {
+			if (!ssl_load_verify_locations(ctx, ca_file)) {
 				ha_alert("Proxy '%s': unable to load CA file '%s' for bind '%s' at [%s:%d].\n",
 					 curproxy->id, ca_file, bind_conf->arg, bind_conf->file, bind_conf->line);
 				cfgerr++;
@@ -5376,7 +5419,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
 	if (verify & SSL_VERIFY_PEER) {
 		if (srv->ssl_ctx.ca_file) {
 			/* load CAfile to verify */
-			if (!SSL_CTX_load_verify_locations(srv->ssl_ctx.ctx, srv->ssl_ctx.ca_file, NULL)) {
+			if (!ssl_load_verify_locations(srv->ssl_ctx.ctx, srv->ssl_ctx.ca_file)) {
 				ha_alert("Proxy '%s', server '%s' [%s:%d] unable to load CA file '%s'.\n",
 					 curproxy->id, srv->id,
 					 srv->conf.file, srv->conf.line, srv->ssl_ctx.ca_file);
-- 
2.11.0

