REORG: ssl: move the CLI 'cert' functions to src/ssl_ckch.c

Move the 'ssl cert' CLI functions to src/ssl_ckch.c.
diff --git a/include/proto/ssl_ckch.h b/include/proto/ssl_ckch.h
index cff3095..d0df9b9 100644
--- a/include/proto/ssl_ckch.h
+++ b/include/proto/ssl_ckch.h
@@ -53,7 +53,9 @@
 /* ckch_inst functions */
 void ckch_inst_free(struct ckch_inst *inst);
 struct ckch_inst *ckch_inst_new();
-
+int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *ckchs,
+                                   struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                   char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err);
 int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
                              struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err);
 
diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
index 0090a6d..91eac9a 100644
--- a/include/proto/ssl_sock.h
+++ b/include/proto/ssl_sock.h
@@ -100,6 +100,10 @@
 void ssl_async_fd_handler(int fd);
 void ssl_async_fd_free(int fd);
 #endif
+struct issuer_chain* ssl_get0_issuer_chain(X509 *cert);
+int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out);
+int ssl_sock_get_serial(X509 *crt, struct buffer *out);
+int cert_get_pkey_algo(X509 *crt, struct buffer *out);
 
 /* ssl shctx macro */
 
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c
index 072cd3c..484997d 100644
--- a/src/ssl_ckch.c
+++ b/src/ssl_ckch.c
@@ -27,11 +27,25 @@
 
 #include <ebsttree.h>
 
+#include <types/cli.h>
 #include <types/ssl_ckch.h>
 #include <types/ssl_sock.h>
 
+#include <proto/cli.h>
+#include <proto/channel.h>
 #include <proto/ssl_ckch.h>
 #include <proto/ssl_sock.h>
+#include <proto/stream_interface.h>
+
+/* Uncommitted CKCH transaction */
+
+static struct {
+	struct ckch_store *new_ckchs;
+	struct ckch_store *old_ckchs;
+	char *path;
+} ckchs_transaction;
+
+
 
 /********************  cert_key_and_chain functions *************************
  * These are the functions that fills a cert_key_and_chain structure. For the
@@ -910,3 +924,985 @@
 	return ckch_inst;
 }
 
+/*************************** CLI commands ***********************/
+
+/* Type of SSL payloads that can be updated over the CLI */
+
+enum {
+	CERT_TYPE_PEM = 0,
+	CERT_TYPE_KEY,
+#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
+	CERT_TYPE_OCSP,
+#endif
+	CERT_TYPE_ISSUER,
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
+	CERT_TYPE_SCTL,
+#endif
+	CERT_TYPE_MAX,
+};
+
+struct {
+	const char *ext;
+	int type;
+	int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
+	/* add a parsing callback */
+} cert_exts[CERT_TYPE_MAX+1] = {
+	[CERT_TYPE_PEM]    = { "",        CERT_TYPE_PEM,      &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
+	[CERT_TYPE_KEY]    = { "key",     CERT_TYPE_KEY,      &ssl_sock_load_key_into_ckch },
+#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
+	[CERT_TYPE_OCSP]   = { "ocsp",    CERT_TYPE_OCSP,     &ssl_sock_load_ocsp_response_from_file },
+#endif
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
+	[CERT_TYPE_SCTL]   = { "sctl",    CERT_TYPE_SCTL,     &ssl_sock_load_sctl_from_file },
+#endif
+	[CERT_TYPE_ISSUER] = { "issuer",  CERT_TYPE_ISSUER,   &ssl_sock_load_issuer_file_into_ckch },
+	[CERT_TYPE_MAX]    = { NULL,      CERT_TYPE_MAX,      NULL },
+};
+
+
+/* release function of the  `show ssl cert' command */
+static void cli_release_show_cert(struct appctx *appctx)
+{
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+}
+
+/* IO handler of "show ssl cert <filename>" */
+static int cli_io_handler_show_cert(struct appctx *appctx)
+{
+	struct buffer *trash = alloc_trash_chunk();
+	struct ebmb_node *node;
+	struct stream_interface *si = appctx->owner;
+	struct ckch_store *ckchs;
+
+	if (trash == NULL)
+		return 1;
+
+	if (!appctx->ctx.ssl.old_ckchs) {
+		if (ckchs_transaction.old_ckchs) {
+			ckchs = ckchs_transaction.old_ckchs;
+			chunk_appendf(trash, "# transaction\n");
+			if (!ckchs->multi) {
+				chunk_appendf(trash, "*%s\n", ckchs->path);
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+			} else {
+				int n;
+
+				chunk_appendf(trash, "*%s:", ckchs->path);
+				for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
+					if (ckchs->ckch[n].cert)
+						chunk_appendf(trash, " %s.%s\n", ckchs->path, SSL_SOCK_KEYTYPE_NAMES[n]);
+				}
+				chunk_appendf(trash, "\n");
+#endif
+			}
+		}
+	}
+
+	if (!appctx->ctx.cli.p0) {
+		chunk_appendf(trash, "# filename\n");
+		node = ebmb_first(&ckchs_tree);
+	} else {
+		node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
+	}
+	while (node) {
+		ckchs = ebmb_entry(node, struct ckch_store, node);
+		if (!ckchs->multi) {
+			chunk_appendf(trash, "%s\n", ckchs->path);
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+		} else {
+			int n;
+
+			chunk_appendf(trash, "%s:", ckchs->path);
+			for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
+				if (ckchs->ckch[n].cert)
+					chunk_appendf(trash, " %s.%s", ckchs->path, SSL_SOCK_KEYTYPE_NAMES[n]);
+			}
+			chunk_appendf(trash, "\n");
+#endif
+		}
+
+		node = ebmb_next(node);
+		if (ci_putchk(si_ic(si), trash) == -1) {
+			si_rx_room_blk(si);
+			goto yield;
+		}
+	}
+
+	appctx->ctx.cli.p0 = NULL;
+	free_trash_chunk(trash);
+	return 1;
+yield:
+
+	free_trash_chunk(trash);
+	appctx->ctx.cli.p0 = ckchs;
+	return 0; /* should come back */
+}
+
+/*
+ * Extract and format the DNS SAN extensions and copy result into a chuink
+ * Return 0;
+ */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
+{
+	int i;
+	char *str;
+	STACK_OF(GENERAL_NAME) *names = NULL;
+
+	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	if (names) {
+		for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+			GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
+			if (i > 0)
+				chunk_appendf(out, ", ");
+			if (name->type == GEN_DNS) {
+				if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
+					chunk_appendf(out, "DNS:%s", str);
+					OPENSSL_free(str);
+				}
+			}
+		}
+		sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+	}
+	return 0;
+}
+#endif
+
+
+
+
+/* IO handler of the details "show ssl cert <filename>" */
+static int cli_io_handler_show_cert_detail(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+	struct ckch_store *ckchs = appctx->ctx.cli.p0;
+	struct buffer *out = alloc_trash_chunk();
+	struct buffer *tmp = alloc_trash_chunk();
+	X509_NAME *name = NULL;
+	STACK_OF(X509) *chain;
+	unsigned int len = 0;
+	int write = -1;
+	BIO *bio = NULL;
+	int i;
+
+	if (!tmp || !out)
+		goto end_no_putchk;
+
+	if (!ckchs->multi) {
+		chunk_appendf(out, "Filename: ");
+		if (ckchs == ckchs_transaction.new_ckchs)
+			chunk_appendf(out, "*");
+		chunk_appendf(out, "%s\n", ckchs->path);
+
+		chunk_appendf(out, "Status: ");
+		if (ckchs->ckch->cert == NULL)
+			chunk_appendf(out, "Empty\n");
+		else if (LIST_ISEMPTY(&ckchs->ckch_inst))
+			chunk_appendf(out, "Unused\n");
+		else
+			chunk_appendf(out, "Used\n");
+
+		if (ckchs->ckch->cert == NULL)
+			goto end;
+
+		chain = ckchs->ckch->chain;
+		if (chain == NULL) {
+			struct issuer_chain *issuer;
+			issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
+			if (issuer) {
+				chain = issuer->chain;
+				chunk_appendf(out, "Chain Filename: ");
+				chunk_appendf(out, "%s\n", issuer->path);
+			}
+		}
+		chunk_appendf(out, "Serial: ");
+		if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
+			goto end;
+		dump_binary(out, tmp->area, tmp->data);
+		chunk_appendf(out, "\n");
+
+		chunk_appendf(out, "notBefore: ");
+		chunk_reset(tmp);
+		if ((bio = BIO_new(BIO_s_mem())) ==  NULL)
+			goto end;
+		if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
+			goto end;
+		write = BIO_read(bio, tmp->area, tmp->size-1);
+		tmp->area[write] = '\0';
+		BIO_free(bio);
+		bio = NULL;
+		chunk_appendf(out, "%s\n", tmp->area);
+
+		chunk_appendf(out, "notAfter: ");
+		chunk_reset(tmp);
+		if ((bio = BIO_new(BIO_s_mem())) == NULL)
+			goto end;
+		if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
+			goto end;
+		if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
+			goto end;
+		tmp->area[write] = '\0';
+		BIO_free(bio);
+		bio = NULL;
+		chunk_appendf(out, "%s\n", tmp->area);
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+		chunk_appendf(out, "Subject Alternative Name: ");
+		if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
+		    goto end;
+		*(out->area + out->data) = '\0';
+		chunk_appendf(out, "\n");
+#endif
+		chunk_reset(tmp);
+		chunk_appendf(out, "Algorithm: ");
+		if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
+			goto end;
+		chunk_appendf(out, "%s\n", tmp->area);
+
+		chunk_reset(tmp);
+		chunk_appendf(out, "SHA1 FingerPrint: ");
+		if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
+			goto end;
+		tmp->data = len;
+		dump_binary(out, tmp->area, tmp->data);
+		chunk_appendf(out, "\n");
+
+		chunk_appendf(out, "Subject: ");
+		if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
+			goto end;
+		if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
+			goto end;
+		*(tmp->area + tmp->data) = '\0';
+		chunk_appendf(out, "%s\n", tmp->area);
+
+		chunk_appendf(out, "Issuer: ");
+		if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
+			goto end;
+		if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
+			goto end;
+		*(tmp->area + tmp->data) = '\0';
+		chunk_appendf(out, "%s\n", tmp->area);
+
+		/* Displays subject of each certificate in the chain */
+		for (i = 0; i < sk_X509_num(chain); i++) {
+			X509 *ca = sk_X509_value(chain, i);
+
+			chunk_appendf(out, "Chain Subject: ");
+			if ((name = X509_get_subject_name(ca)) == NULL)
+				goto end;
+			if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
+				goto end;
+			*(tmp->area + tmp->data) = '\0';
+			chunk_appendf(out, "%s\n", tmp->area);
+
+			chunk_appendf(out, "Chain Issuer: ");
+			if ((name = X509_get_issuer_name(ca)) == NULL)
+				goto end;
+			if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
+				goto end;
+			*(tmp->area + tmp->data) = '\0';
+			chunk_appendf(out, "%s\n", tmp->area);
+		}
+	}
+
+end:
+	if (ci_putchk(si_ic(si), out) == -1) {
+		si_rx_room_blk(si);
+		goto yield;
+	}
+
+end_no_putchk:
+	if (bio)
+		BIO_free(bio);
+	free_trash_chunk(tmp);
+	free_trash_chunk(out);
+	return 1;
+yield:
+	free_trash_chunk(tmp);
+	free_trash_chunk(out);
+	return 0; /* should come back */
+}
+
+/* parsing function for 'show ssl cert [certfile]' */
+static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct ckch_store *ckchs;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_OPER))
+		return cli_err(appctx, "Can't allocate memory!\n");
+
+	/* The operations on the CKCH architecture are locked so we can
+	 * manipulate ckch_store and ckch_inst */
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
+
+	/* check if there is a certificate to lookup */
+	if (*args[3]) {
+		if (*args[3] == '*') {
+			if (!ckchs_transaction.new_ckchs)
+				goto error;
+
+			ckchs = ckchs_transaction.new_ckchs;
+
+			if (strcmp(args[3] + 1, ckchs->path))
+				goto error;
+
+		} else {
+			if ((ckchs = ckchs_lookup(args[3])) == NULL)
+				goto error;
+
+		}
+
+		if (ckchs->multi)
+			goto error;
+
+		appctx->ctx.cli.p0 = ckchs;
+		/* use the IO handler that shows details */
+		appctx->io_handler = cli_io_handler_show_cert_detail;
+	}
+
+	return 0;
+
+error:
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
+}
+
+/* release function of the  `set ssl cert' command, free things and unlock the spinlock */
+static void cli_release_commit_cert(struct appctx *appctx)
+{
+	struct ckch_store *new_ckchs;
+
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+
+	if (appctx->st2 != SETCERT_ST_FIN) {
+		/* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
+		new_ckchs = appctx->ctx.ssl.new_ckchs;
+
+		/* if the allocation failed, we need to free everything from the temporary list */
+		ckch_store_free(new_ckchs);
+	}
+}
+
+/*
+ * This function tries to create the new ckch_inst and their SNIs
+ */
+static int cli_io_handler_commit_cert(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+	int y = 0;
+	char *err = NULL;
+	int errcode = 0;
+	struct ckch_store *old_ckchs, *new_ckchs = NULL;
+	struct ckch_inst *ckchi, *ckchis;
+	struct buffer *trash = alloc_trash_chunk();
+	struct sni_ctx *sc0, *sc0s;
+	struct crtlist_entry *entry;
+
+	if (trash == NULL)
+		goto error;
+
+	if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
+		goto error;
+
+	while (1) {
+		switch (appctx->st2) {
+			case SETCERT_ST_INIT:
+				/* This state just print the update message */
+				chunk_printf(trash, "Committing %s", ckchs_transaction.path);
+				if (ci_putchk(si_ic(si), trash) == -1) {
+					si_rx_room_blk(si);
+					goto yield;
+				}
+				appctx->st2 = SETCERT_ST_GEN;
+				/* fallthrough */
+			case SETCERT_ST_GEN:
+				/*
+				 * This state generates the ckch instances with their
+				 * sni_ctxs and SSL_CTX.
+				 *
+				 * Since the SSL_CTX generation can be CPU consumer, we
+				 * yield every 10 instances.
+				 */
+
+				old_ckchs = appctx->ctx.ssl.old_ckchs;
+				new_ckchs = appctx->ctx.ssl.new_ckchs;
+
+				if (!new_ckchs)
+					continue;
+
+				/* get the next ckchi to regenerate */
+				ckchi = appctx->ctx.ssl.next_ckchi;
+				/* we didn't start yet, set it to the first elem */
+				if (ckchi == NULL)
+					ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
+
+				/* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
+				list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
+					struct ckch_inst *new_inst;
+					char **sni_filter = NULL;
+					int fcount = 0;
+
+					/* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
+					if (y >= 10) {
+						/* save the next ckchi to compute */
+						appctx->ctx.ssl.next_ckchi = ckchi;
+						goto yield;
+					}
+
+					if (ckchi->crtlist_entry) {
+						sni_filter = ckchi->crtlist_entry->filters;
+						fcount = ckchi->crtlist_entry->fcount;
+					}
+
+					if (new_ckchs->multi)
+						errcode |= ckch_inst_new_load_multi_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err);
+					else
+						errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err);
+
+					if (errcode & ERR_CODE)
+						goto error;
+
+					/* if the previous ckchi was used as the default */
+					if (ckchi->is_default)
+						new_inst->is_default = 1;
+
+					/* we need to initialize the SSL_CTX generated */
+					/* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
+					list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
+						if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
+							errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
+							if (errcode & ERR_CODE)
+								goto error;
+						}
+					}
+
+
+					/* display one dot per new instance */
+					chunk_appendf(trash, ".");
+					/* link the new ckch_inst to the duplicate */
+					LIST_ADDQ(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
+					y++;
+				}
+				appctx->st2 = SETCERT_ST_INSERT;
+				/* fallthrough */
+			case SETCERT_ST_INSERT:
+				/* The generation is finished, we can insert everything */
+
+				old_ckchs = appctx->ctx.ssl.old_ckchs;
+				new_ckchs = appctx->ctx.ssl.new_ckchs;
+
+				if (!new_ckchs)
+					continue;
+
+				/* get the list of crtlist_entry in the old store, and update the pointers to the store */
+				LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
+				list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
+					ebpt_delete(&entry->node);
+					/* change the ptr and reinsert the node */
+					entry->node.key = new_ckchs;
+					ebpt_insert(&entry->crtlist->entries, &entry->node);
+				}
+
+				/* First, we insert every new SNIs in the trees, also replace the default_ctx */
+				list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
+					HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
+					ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
+					HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
+				}
+
+				/* delete the old sni_ctx, the old ckch_insts and the ckch_store */
+				list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
+					struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
+
+					HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
+					ckch_inst_free(ckchi);
+					HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
+				}
+
+				/* Replace the old ckchs by the new one */
+				ckch_store_free(old_ckchs);
+				ebst_insert(&ckchs_tree, &new_ckchs->node);
+				appctx->st2 = SETCERT_ST_FIN;
+				/* fallthrough */
+			case SETCERT_ST_FIN:
+				/* we achieved the transaction, we can set everything to NULL */
+				free(ckchs_transaction.path);
+				ckchs_transaction.path = NULL;
+				ckchs_transaction.new_ckchs = NULL;
+				ckchs_transaction.old_ckchs = NULL;
+				goto end;
+		}
+	}
+end:
+
+	chunk_appendf(trash, "\n");
+	if (errcode & ERR_WARN)
+		chunk_appendf(trash, "%s", err);
+	chunk_appendf(trash, "Success!\n");
+	if (ci_putchk(si_ic(si), trash) == -1)
+		si_rx_room_blk(si);
+	free_trash_chunk(trash);
+	/* success: call the release function and don't come back */
+	return 1;
+yield:
+	/* store the state */
+	if (ci_putchk(si_ic(si), trash) == -1)
+		si_rx_room_blk(si);
+	free_trash_chunk(trash);
+	si_rx_endp_more(si); /* let's come back later */
+	return 0; /* should come back */
+
+error:
+	/* spin unlock and free are done in the release  function */
+	if (trash) {
+		chunk_appendf(trash, "\n%sFailed!\n", err);
+		if (ci_putchk(si_ic(si), trash) == -1)
+			si_rx_room_blk(si);
+		free_trash_chunk(trash);
+	}
+	/* error: call the release function and don't come back */
+	return 1;
+}
+
+/*
+ * Parsing function of 'commit ssl cert'
+ */
+static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char *err = NULL;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		return 1;
+
+	if (!*args[3])
+		return cli_err(appctx, "'commit ssl cert expects a filename\n");
+
+	/* The operations on the CKCH architecture are locked so we can
+	 * manipulate ckch_store and ckch_inst */
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
+
+	if (!ckchs_transaction.path) {
+		memprintf(&err, "No ongoing transaction! !\n");
+		goto error;
+	}
+
+	if (strcmp(ckchs_transaction.path, args[3]) != 0) {
+		memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
+		goto error;
+	}
+
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+	if (ckchs_transaction.new_ckchs->multi) {
+		int n;
+
+		for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
+			if (ckchs_transaction.new_ckchs->ckch[n].cert && !X509_check_private_key(ckchs_transaction.new_ckchs->ckch[n].cert, ckchs_transaction.new_ckchs->ckch[n].key)) {
+				memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
+				goto error;
+			}
+		}
+	} else
+#endif
+	{
+		if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
+			memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
+			goto error;
+		}
+	}
+
+	/* init the appctx structure */
+	appctx->st2 = SETCERT_ST_INIT;
+	appctx->ctx.ssl.next_ckchi = NULL;
+	appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
+	appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
+
+	/* we don't unlock there, it will be unlock after the IO handler, in the release handler */
+	return 0;
+
+error:
+
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
+
+	return cli_dynerr(appctx, err);
+}
+
+
+
+
+/*
+ * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
+ */
+static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct ckch_store *new_ckchs = NULL;
+	struct ckch_store *old_ckchs = NULL;
+	char *err = NULL;
+	int i;
+	int bundle = -1; /* TRUE if >= 0 (ckch index) */
+	int errcode = 0;
+	char *end;
+	int type = CERT_TYPE_PEM;
+	struct cert_key_and_chain *ckch;
+	struct buffer *buf;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		return 1;
+
+	if ((buf = alloc_trash_chunk()) == NULL)
+		return cli_err(appctx, "Can't allocate memory\n");
+
+	if (!*args[3] || !payload)
+		return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
+
+	/* The operations on the CKCH architecture are locked so we can
+	 * manipulate ckch_store and ckch_inst */
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
+
+	if (!chunk_strcpy(buf, args[3])) {
+		memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
+		errcode |= ERR_ALERT | ERR_FATAL;
+		goto end;
+	}
+
+	/* check which type of file we want to update */
+	for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
+		end = strrchr(buf->area, '.');
+		if (end && *cert_exts[i].ext && (!strcmp(end + 1, cert_exts[i].ext))) {
+			*end = '\0';
+			type = cert_exts[i].type;
+			break;
+		}
+	}
+
+	appctx->ctx.ssl.old_ckchs = NULL;
+	appctx->ctx.ssl.new_ckchs = NULL;
+
+	/* if there is an ongoing transaction */
+	if (ckchs_transaction.path) {
+		/* if the ongoing transaction is a bundle, we need to find which part of the bundle need to be updated */
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+		if (ckchs_transaction.new_ckchs->multi) {
+			char *end;
+			int j;
+
+			/* check if it was used in a bundle by removing the
+			 *   .dsa/.rsa/.ecdsa at the end of the filename */
+			end = strrchr(buf->area, '.');
+			for (j = 0; end && j < SSL_SOCK_NUM_KEYTYPES; j++) {
+				if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
+					bundle = j; /* keep the type of certificate so we insert it at the right place */
+					*end = '\0'; /* it's a bundle let's end the string*/
+					break;
+				}
+			}
+			if (bundle < 0) {
+				memprintf(&err, "The ongoing transaction is the '%s' bundle. You need to specify which part of the bundle you want to update ('%s.{rsa,ecdsa,dsa}')\n", ckchs_transaction.path, buf->area);
+				errcode |= ERR_ALERT | ERR_FATAL;
+				goto end;
+			}
+		}
+#endif
+
+		/* if there is an ongoing transaction, check if this is the same file */
+		if (strcmp(ckchs_transaction.path, buf->area) != 0) {
+			memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
+			errcode |= ERR_ALERT | ERR_FATAL;
+			goto end;
+		}
+
+		appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
+
+	} else {
+		struct ckch_store *find_ckchs[2] = { NULL, NULL };
+
+		/* lookup for the certificate in the tree:
+		 * check if this is used as a bundle AND as a unique certificate */
+		for (i = 0; i < 2; i++) {
+
+			if ((find_ckchs[i] = ckchs_lookup(buf->area)) != NULL) {
+				/* only the bundle name is in the tree and you should
+				 * never update a bundle name, only a filename */
+				if (bundle < 0 && find_ckchs[i]->multi) {
+					/* we tried to look for a non-bundle and we found a bundle */
+					memprintf(&err, "%s%s is a multi-cert bundle. Try updating %s.{dsa,rsa,ecdsa}\n",
+						  err ? err : "", args[3], args[3]);
+					errcode |= ERR_ALERT | ERR_FATAL;
+					goto end;
+				}
+				/* If we want a bundle but this is not a bundle
+				 * example: When you try to update <file>.rsa, but
+				 * <file> is a regular file */
+				if (bundle >= 0 && find_ckchs[i]->multi == 0) {
+					find_ckchs[i] = NULL;
+					break;
+				}
+			}
+#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
+			{
+				char *end;
+				int j;
+
+				/* check if it was used in a bundle by removing the
+				 *   .dsa/.rsa/.ecdsa at the end of the filename */
+				end = strrchr(buf->area, '.');
+				for (j = 0; end && j < SSL_SOCK_NUM_KEYTYPES; j++) {
+					if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
+						bundle = j; /* keep the type of certificate so we insert it at the right place */
+						*end = '\0'; /* it's a bundle let's end the string*/
+						break;
+					}
+				}
+				if (bundle < 0) /* we didn't find a bundle extension */
+					break;
+			}
+#else
+			/* bundles are not supported here, so we don't need to lookup again */
+			break;
+#endif
+		}
+
+		if (find_ckchs[0] && find_ckchs[1]) {
+			memprintf(&err, "%sUpdating a certificate which is used in the HAProxy configuration as a bundle and as a unique certificate is not supported. ('%s' and '%s')\n",
+			          err ? err : "", find_ckchs[0]->path, find_ckchs[1]->path);
+			errcode |= ERR_ALERT | ERR_FATAL;
+			goto end;
+		}
+
+		appctx->ctx.ssl.old_ckchs = find_ckchs[0] ? find_ckchs[0] : find_ckchs[1];
+	}
+
+	if (!appctx->ctx.ssl.old_ckchs) {
+		memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
+		          err ? err : "");
+		errcode |= ERR_ALERT | ERR_FATAL;
+		goto end;
+	}
+
+	if (!appctx->ctx.ssl.path) {
+	/* this is a new transaction, set the path of the transaction */
+		appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
+		if (!appctx->ctx.ssl.path) {
+			memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
+			errcode |= ERR_ALERT | ERR_FATAL;
+			goto end;
+		}
+	}
+
+	old_ckchs = appctx->ctx.ssl.old_ckchs;
+
+	/* duplicate the ckch store */
+	new_ckchs = ckchs_dup(old_ckchs);
+	if (!new_ckchs) {
+		memprintf(&err, "%sCannot allocate memory!\n",
+			  err ? err : "");
+		errcode |= ERR_ALERT | ERR_FATAL;
+		goto end;
+	}
+
+	if (!new_ckchs->multi)
+		ckch = new_ckchs->ckch;
+	else
+		ckch = &new_ckchs->ckch[bundle];
+
+	/* appply the change on the duplicate */
+	if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
+		memprintf(&err, "%sCan't load the payload\n", err ? err : "");
+		errcode |= ERR_ALERT | ERR_FATAL;
+		goto end;
+	}
+
+	appctx->ctx.ssl.new_ckchs = new_ckchs;
+
+	/* we succeed, we can save the ckchs in the transaction */
+
+	/* if there wasn't a transaction, update the old ckchs */
+	if (!ckchs_transaction.old_ckchs) {
+		ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
+		ckchs_transaction.path = appctx->ctx.ssl.path;
+		err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
+	} else {
+		err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
+
+	}
+
+	/* free the previous ckchs if there was a transaction */
+	ckch_store_free(ckchs_transaction.new_ckchs);
+
+	ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
+
+
+	/* creates the SNI ctxs later in the IO handler */
+
+end:
+	free_trash_chunk(buf);
+
+	if (errcode & ERR_CODE) {
+
+		ckch_store_free(appctx->ctx.ssl.new_ckchs);
+		appctx->ctx.ssl.new_ckchs = NULL;
+
+		appctx->ctx.ssl.old_ckchs = NULL;
+
+		free(appctx->ctx.ssl.path);
+		appctx->ctx.ssl.path = NULL;
+
+		HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+		return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
+	} else {
+
+		HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+		return cli_dynmsg(appctx, LOG_NOTICE, err);
+	}
+	/* TODO: handle the ERR_WARN which are not handled because of the io_handler */
+}
+
+/* parsing function of 'abort ssl cert' */
+static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char *err = NULL;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		return 1;
+
+	if (!*args[3])
+		return cli_err(appctx, "'abort ssl cert' expects a filename\n");
+
+	/* The operations on the CKCH architecture are locked so we can
+	 * manipulate ckch_store and ckch_inst */
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
+
+	if (!ckchs_transaction.path) {
+		memprintf(&err, "No ongoing transaction!\n");
+		goto error;
+	}
+
+	if (strcmp(ckchs_transaction.path, args[3]) != 0) {
+		memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
+		goto error;
+	}
+
+	/* Only free the ckchs there, because the SNI and instances were not generated yet */
+	ckch_store_free(ckchs_transaction.new_ckchs);
+	ckchs_transaction.new_ckchs = NULL;
+	ckch_store_free(ckchs_transaction.old_ckchs);
+	ckchs_transaction.old_ckchs = NULL;
+	free(ckchs_transaction.path);
+	ckchs_transaction.path = NULL;
+
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+
+	err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
+	return cli_dynmsg(appctx, LOG_NOTICE, err);
+
+error:
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+
+	return cli_dynerr(appctx, err);
+}
+
+/* parsing function of 'new ssl cert' */
+static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct ckch_store *store;
+	char *err = NULL;
+	char *path;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		return 1;
+
+	if (!*args[3])
+		return cli_err(appctx, "'new ssl cert' expects a filename\n");
+
+	path = args[3];
+
+	/* The operations on the CKCH architecture are locked so we can
+	 * manipulate ckch_store and ckch_inst */
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
+
+	store = ckchs_lookup(path);
+	if (store != NULL) {
+		memprintf(&err, "Certificate '%s' already exists!\n", path);
+		store = NULL; /* we don't want to free it */
+		goto error;
+	}
+	/* we won't support multi-certificate bundle here */
+	store = ckch_store_new(path, 1);
+	if (!store) {
+		memprintf(&err, "unable to allocate memory.\n");
+		goto error;
+	}
+
+	/* insert into the ckchs tree */
+	ebst_insert(&ckchs_tree, &store->node);
+	memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
+
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	return cli_dynmsg(appctx, LOG_NOTICE, err);
+error:
+	free(store);
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	return cli_dynerr(appctx, err);
+}
+
+/* parsing function of 'del ssl cert' */
+static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct ckch_store *store;
+	char *err = NULL;
+	char *filename;
+
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		return 1;
+
+	if (!*args[3])
+		return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
+
+	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+		return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
+
+	filename = args[3];
+
+	store = ckchs_lookup(filename);
+	if (store == NULL) {
+		memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
+		goto error;
+	}
+	if (!LIST_ISEMPTY(&store->ckch_inst)) {
+		memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
+		goto error;
+	}
+
+	ebmb_delete(&store->node);
+	ckch_store_free(store);
+
+	memprintf(&err, "Certificate '%s' deleted!\n", filename);
+
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	return cli_dynmsg(appctx, LOG_NOTICE, err);
+
+error:
+	memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
+	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+	return cli_dynerr(appctx, err);
+}
+
+
+/* register cli keywords */
+static struct cli_kw_list cli_kws = {{ },{
+	{ { "new", "ssl", "cert", NULL }, "new ssl cert <certfile> : create a new certificate file to be used in a crt-list or a directory", cli_parse_new_cert, NULL, NULL },
+	{ { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
+	{ { "commit", "ssl", "cert", NULL }, "commit ssl cert <certfile> : commit a certificate file", cli_parse_commit_cert, cli_io_handler_commit_cert, cli_release_commit_cert },
+	{ { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
+	{ { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
+	{ { "show", "ssl", "cert", NULL }, "show ssl cert [<certfile>] : display the SSL certificates used in memory, or the details of a <certfile>", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert },
+	{ { NULL }, NULL, NULL, NULL }
+}};
+
+INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
+
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 2f5319d..6017fb6 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -108,7 +108,6 @@
 int nb_engines = 0;
 
 static struct eb_root cert_issuer_tree = EB_ROOT; /* issuers tree from "issuers-chain-path" */
-static struct issuer_chain* ssl_get0_issuer_chain(X509 *cert);
 
 struct global_ssl global_ssl = {
 #ifdef LISTEN_DEFAULT_CIPHERS
@@ -293,13 +292,6 @@
 
 __decl_hathreads(HA_SPINLOCK_T ckch_lock);
 
-/* Uncommitted CKCH transaction */
-
-static struct {
-	struct ckch_store *new_ckchs;
-	struct ckch_store *old_ckchs;
-	char *path;
-} ckchs_transaction;
 
 /*
  * deduplicate cafile (and crlfile)
@@ -3063,9 +3055,9 @@
  *     ERR_WARN if a warning is available into err
  *
  */
-static int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *ckchs,
-                                          struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
-                                          char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err)
+int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *ckchs,
+                                   struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                   char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err)
 {
 	int i = 0, n = 0;
 	struct cert_key_and_chain *certs_and_keys;
@@ -3298,9 +3290,9 @@
 }
 #else
 /* This is a dummy, that just logs an error and returns error */
-static int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *ckchs,
-                                          struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
-                                          char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err)
+int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *ckchs,
+                                   struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                   char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err)
 {
 	memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
 	          err && *err ? *err : "", path, strerror(errno));
@@ -5876,7 +5868,7 @@
 }
 
 /* fill a buffer with the algorithm and size of a public key */
-static int cert_get_pkey_algo(X509 *crt, struct buffer *out)
+int cert_get_pkey_algo(X509 *crt, struct buffer *out)
 {
 	int bits = 0;
 	int sig = TLSEXT_signature_anonymous;
@@ -5995,8 +5987,7 @@
  * Returns 1 if serial is found and copied, 0 if no serial found and
  * -1 if output is not large enough.
  */
-static int
-ssl_sock_get_serial(X509 *crt, struct buffer *out)
+int ssl_sock_get_serial(X509 *crt, struct buffer *out)
 {
 	ASN1_INTEGER *serial;
 
@@ -6136,36 +6127,6 @@
 }
 
 /*
- * Extract and format the DNS SAN extensions and copy result into a chuink
- * Return 0;
- */
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
-{
-	int i;
-	char *str;
-	STACK_OF(GENERAL_NAME) *names = NULL;
-
-	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-	if (names) {
-		for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
-			GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
-			if (i > 0)
-				chunk_appendf(out, ", ");
-			if (name->type == GEN_DNS) {
-				if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
-					chunk_appendf(out, "DNS:%s", str);
-					OPENSSL_free(str);
-				}
-			}
-		}
-		sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
-	}
-	return 0;
-}
-#endif
-
-/*
  * Extract the DN in the specified format from the X509_NAME and copy result to a chunk.
  * Currently supports rfc2253 for returning LDAP V3 DNs.
  * Returns 1 if dn entries exist, 0 if no dn entry was found.
@@ -6201,8 +6162,7 @@
 /* Extract and format full DN from a X509_NAME and copy result into a chunk
  * Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
  */
-static int
-ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out)
+int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out)
 {
 	X509_NAME_ENTRY *ne;
 	ASN1_OBJECT *obj;
@@ -8850,7 +8810,7 @@
 	return ret;
 }
 
-static struct issuer_chain* ssl_get0_issuer_chain(X509 *cert)
+ struct issuer_chain* ssl_get0_issuer_chain(X509 *cert)
 {
 	AUTHORITY_KEYID *akid;
 	struct issuer_chain *issuer = NULL;
@@ -9553,940 +9513,7 @@
 
 	return cli_msg(appctx, LOG_INFO, "TLS ticket key updated!\n");
 }
-#endif
-
-/* Type of SSL payloads that can be updated over the CLI */
-
-enum {
-	CERT_TYPE_PEM = 0,
-	CERT_TYPE_KEY,
-#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
-	CERT_TYPE_OCSP,
-#endif
-	CERT_TYPE_ISSUER,
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
-	CERT_TYPE_SCTL,
-#endif
-	CERT_TYPE_MAX,
-};
-
-struct {
-	const char *ext;
-	int type;
-	int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
-	/* add a parsing callback */
-} cert_exts[CERT_TYPE_MAX+1] = {
-	[CERT_TYPE_PEM]    = { "",        CERT_TYPE_PEM,      &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
-	[CERT_TYPE_KEY]    = { "key",     CERT_TYPE_KEY,      &ssl_sock_load_key_into_ckch },
-#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
-	[CERT_TYPE_OCSP]   = { "ocsp",    CERT_TYPE_OCSP,     &ssl_sock_load_ocsp_response_from_file },
-#endif
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
-	[CERT_TYPE_SCTL]   = { "sctl",    CERT_TYPE_SCTL,     &ssl_sock_load_sctl_from_file },
-#endif
-	[CERT_TYPE_ISSUER] = { "issuer",  CERT_TYPE_ISSUER,   &ssl_sock_load_issuer_file_into_ckch },
-	[CERT_TYPE_MAX]    = { NULL,      CERT_TYPE_MAX,      NULL },
-};
-
-
-/* release function of the  `show ssl cert' command */
-static void cli_release_show_cert(struct appctx *appctx)
-{
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-}
-
-/* IO handler of "show ssl cert <filename>" */
-static int cli_io_handler_show_cert(struct appctx *appctx)
-{
-	struct buffer *trash = alloc_trash_chunk();
-	struct ebmb_node *node;
-	struct stream_interface *si = appctx->owner;
-	struct ckch_store *ckchs;
-
-	if (trash == NULL)
-		return 1;
-
-	if (!appctx->ctx.ssl.old_ckchs) {
-		if (ckchs_transaction.old_ckchs) {
-			ckchs = ckchs_transaction.old_ckchs;
-			chunk_appendf(trash, "# transaction\n");
-			if (!ckchs->multi) {
-				chunk_appendf(trash, "*%s\n", ckchs->path);
-#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
-			} else {
-				int n;
-
-				chunk_appendf(trash, "*%s:", ckchs->path);
-				for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
-					if (ckchs->ckch[n].cert)
-						chunk_appendf(trash, " %s.%s\n", ckchs->path, SSL_SOCK_KEYTYPE_NAMES[n]);
-				}
-				chunk_appendf(trash, "\n");
-#endif
-			}
-		}
-	}
-
-	if (!appctx->ctx.cli.p0) {
-		chunk_appendf(trash, "# filename\n");
-		node = ebmb_first(&ckchs_tree);
-	} else {
-		node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
-	}
-	while (node) {
-		ckchs = ebmb_entry(node, struct ckch_store, node);
-		if (!ckchs->multi) {
-			chunk_appendf(trash, "%s\n", ckchs->path);
-#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
-		} else {
-			int n;
-
-			chunk_appendf(trash, "%s:", ckchs->path);
-			for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
-				if (ckchs->ckch[n].cert)
-					chunk_appendf(trash, " %s.%s", ckchs->path, SSL_SOCK_KEYTYPE_NAMES[n]);
-			}
-			chunk_appendf(trash, "\n");
-#endif
-		}
-
-		node = ebmb_next(node);
-		if (ci_putchk(si_ic(si), trash) == -1) {
-			si_rx_room_blk(si);
-			goto yield;
-		}
-	}
-
-	appctx->ctx.cli.p0 = NULL;
-	free_trash_chunk(trash);
-	return 1;
-yield:
-
-	free_trash_chunk(trash);
-	appctx->ctx.cli.p0 = ckchs;
-	return 0; /* should come back */
-}
-
-/* IO handler of the details "show ssl cert <filename>" */
-static int cli_io_handler_show_cert_detail(struct appctx *appctx)
-{
-	struct stream_interface *si = appctx->owner;
-	struct ckch_store *ckchs = appctx->ctx.cli.p0;
-	struct buffer *out = alloc_trash_chunk();
-	struct buffer *tmp = alloc_trash_chunk();
-	X509_NAME *name = NULL;
-	STACK_OF(X509) *chain;
-	unsigned int len = 0;
-	int write = -1;
-	BIO *bio = NULL;
-	int i;
-
-	if (!tmp || !out)
-		goto end_no_putchk;
-
-	if (!ckchs->multi) {
-		chunk_appendf(out, "Filename: ");
-		if (ckchs == ckchs_transaction.new_ckchs)
-			chunk_appendf(out, "*");
-		chunk_appendf(out, "%s\n", ckchs->path);
-
-		chunk_appendf(out, "Status: ");
-		if (ckchs->ckch->cert == NULL)
-			chunk_appendf(out, "Empty\n");
-		else if (LIST_ISEMPTY(&ckchs->ckch_inst))
-			chunk_appendf(out, "Unused\n");
-		else
-			chunk_appendf(out, "Used\n");
-
-		if (ckchs->ckch->cert == NULL)
-			goto end;
-
-		chain = ckchs->ckch->chain;
-		if (chain == NULL) {
-			struct issuer_chain *issuer;
-			issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
-			if (issuer) {
-				chain = issuer->chain;
-				chunk_appendf(out, "Chain Filename: ");
-				chunk_appendf(out, "%s\n", issuer->path);
-			}
-		}
-		chunk_appendf(out, "Serial: ");
-		if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
-			goto end;
-		dump_binary(out, tmp->area, tmp->data);
-		chunk_appendf(out, "\n");
-
-		chunk_appendf(out, "notBefore: ");
-		chunk_reset(tmp);
-		if ((bio = BIO_new(BIO_s_mem())) ==  NULL)
-			goto end;
-		if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
-			goto end;
-		write = BIO_read(bio, tmp->area, tmp->size-1);
-		tmp->area[write] = '\0';
-		BIO_free(bio);
-		bio = NULL;
-		chunk_appendf(out, "%s\n", tmp->area);
-
-		chunk_appendf(out, "notAfter: ");
-		chunk_reset(tmp);
-		if ((bio = BIO_new(BIO_s_mem())) == NULL)
-			goto end;
-		if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
-			goto end;
-		if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
-			goto end;
-		tmp->area[write] = '\0';
-		BIO_free(bio);
-		bio = NULL;
-		chunk_appendf(out, "%s\n", tmp->area);
-
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-		chunk_appendf(out, "Subject Alternative Name: ");
-		if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
-		    goto end;
-		*(out->area + out->data) = '\0';
-		chunk_appendf(out, "\n");
 #endif
-		chunk_reset(tmp);
-		chunk_appendf(out, "Algorithm: ");
-		if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
-			goto end;
-		chunk_appendf(out, "%s\n", tmp->area);
-
-		chunk_reset(tmp);
-		chunk_appendf(out, "SHA1 FingerPrint: ");
-		if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
-			goto end;
-		tmp->data = len;
-		dump_binary(out, tmp->area, tmp->data);
-		chunk_appendf(out, "\n");
-
-		chunk_appendf(out, "Subject: ");
-		if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
-			goto end;
-		if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
-			goto end;
-		*(tmp->area + tmp->data) = '\0';
-		chunk_appendf(out, "%s\n", tmp->area);
-
-		chunk_appendf(out, "Issuer: ");
-		if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
-			goto end;
-		if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
-			goto end;
-		*(tmp->area + tmp->data) = '\0';
-		chunk_appendf(out, "%s\n", tmp->area);
-
-		/* Displays subject of each certificate in the chain */
-		for (i = 0; i < sk_X509_num(chain); i++) {
-			X509 *ca = sk_X509_value(chain, i);
-
-			chunk_appendf(out, "Chain Subject: ");
-			if ((name = X509_get_subject_name(ca)) == NULL)
-				goto end;
-			if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
-				goto end;
-			*(tmp->area + tmp->data) = '\0';
-			chunk_appendf(out, "%s\n", tmp->area);
-
-			chunk_appendf(out, "Chain Issuer: ");
-			if ((name = X509_get_issuer_name(ca)) == NULL)
-				goto end;
-			if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
-				goto end;
-			*(tmp->area + tmp->data) = '\0';
-			chunk_appendf(out, "%s\n", tmp->area);
-		}
-	}
-
-end:
-	if (ci_putchk(si_ic(si), out) == -1) {
-		si_rx_room_blk(si);
-		goto yield;
-	}
-
-end_no_putchk:
-	if (bio)
-		BIO_free(bio);
-	free_trash_chunk(tmp);
-	free_trash_chunk(out);
-	return 1;
-yield:
-	free_trash_chunk(tmp);
-	free_trash_chunk(out);
-	return 0; /* should come back */
-}
-
-/* parsing function for 'show ssl cert [certfile]' */
-static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	struct ckch_store *ckchs;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_OPER))
-		return cli_err(appctx, "Can't allocate memory!\n");
-
-	/* The operations on the CKCH architecture are locked so we can
-	 * manipulate ckch_store and ckch_inst */
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
-
-	/* check if there is a certificate to lookup */
-	if (*args[3]) {
-		if (*args[3] == '*') {
-			if (!ckchs_transaction.new_ckchs)
-				goto error;
-
-			ckchs = ckchs_transaction.new_ckchs;
-
-			if (strcmp(args[3] + 1, ckchs->path))
-				goto error;
-
-		} else {
-			if ((ckchs = ckchs_lookup(args[3])) == NULL)
-				goto error;
-
-		}
-
-		if (ckchs->multi)
-			goto error;
-
-		appctx->ctx.cli.p0 = ckchs;
-		/* use the IO handler that shows details */
-		appctx->io_handler = cli_io_handler_show_cert_detail;
-	}
-
-	return 0;
-
-error:
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
-}
-
-/* release function of the  `set ssl cert' command, free things and unlock the spinlock */
-static void cli_release_commit_cert(struct appctx *appctx)
-{
-	struct ckch_store *new_ckchs;
-
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-
-	if (appctx->st2 != SETCERT_ST_FIN) {
-		/* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
-		new_ckchs = appctx->ctx.ssl.new_ckchs;
-
-		/* if the allocation failed, we need to free everything from the temporary list */
-		ckch_store_free(new_ckchs);
-	}
-}
-
-
-/*
- * This function tries to create the new ckch_inst and their SNIs
- */
-static int cli_io_handler_commit_cert(struct appctx *appctx)
-{
-	struct stream_interface *si = appctx->owner;
-	int y = 0;
-	char *err = NULL;
-	int errcode = 0;
-	struct ckch_store *old_ckchs, *new_ckchs = NULL;
-	struct ckch_inst *ckchi, *ckchis;
-	struct buffer *trash = alloc_trash_chunk();
-	struct sni_ctx *sc0, *sc0s;
-	struct crtlist_entry *entry;
-
-	if (trash == NULL)
-		goto error;
-
-	if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
-		goto error;
-
-	while (1) {
-		switch (appctx->st2) {
-			case SETCERT_ST_INIT:
-				/* This state just print the update message */
-				chunk_printf(trash, "Committing %s", ckchs_transaction.path);
-				if (ci_putchk(si_ic(si), trash) == -1) {
-					si_rx_room_blk(si);
-					goto yield;
-				}
-				appctx->st2 = SETCERT_ST_GEN;
-				/* fallthrough */
-			case SETCERT_ST_GEN:
-				/*
-				 * This state generates the ckch instances with their
-				 * sni_ctxs and SSL_CTX.
-				 *
-				 * Since the SSL_CTX generation can be CPU consumer, we
-				 * yield every 10 instances.
-				 */
-
-				old_ckchs = appctx->ctx.ssl.old_ckchs;
-				new_ckchs = appctx->ctx.ssl.new_ckchs;
-
-				if (!new_ckchs)
-					continue;
-
-				/* get the next ckchi to regenerate */
-				ckchi = appctx->ctx.ssl.next_ckchi;
-				/* we didn't start yet, set it to the first elem */
-				if (ckchi == NULL)
-					ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
-
-				/* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
-				list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
-					struct ckch_inst *new_inst;
-					char **sni_filter = NULL;
-					int fcount = 0;
-
-					/* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
-					if (y >= 10) {
-						/* save the next ckchi to compute */
-						appctx->ctx.ssl.next_ckchi = ckchi;
-						goto yield;
-					}
-
-					if (ckchi->crtlist_entry) {
-						sni_filter = ckchi->crtlist_entry->filters;
-						fcount = ckchi->crtlist_entry->fcount;
-					}
-
-					if (new_ckchs->multi)
-						errcode |= ckch_inst_new_load_multi_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err);
-					else
-						errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err);
-
-					if (errcode & ERR_CODE)
-						goto error;
-
-					/* if the previous ckchi was used as the default */
-					if (ckchi->is_default)
-						new_inst->is_default = 1;
-
-					/* we need to initialize the SSL_CTX generated */
-					/* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
-					list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
-						if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
-							errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
-							if (errcode & ERR_CODE)
-								goto error;
-						}
-					}
-
-
-					/* display one dot per new instance */
-					chunk_appendf(trash, ".");
-					/* link the new ckch_inst to the duplicate */
-					LIST_ADDQ(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
-					y++;
-				}
-				appctx->st2 = SETCERT_ST_INSERT;
-				/* fallthrough */
-			case SETCERT_ST_INSERT:
-				/* The generation is finished, we can insert everything */
-
-				old_ckchs = appctx->ctx.ssl.old_ckchs;
-				new_ckchs = appctx->ctx.ssl.new_ckchs;
-
-				if (!new_ckchs)
-					continue;
-
-				/* get the list of crtlist_entry in the old store, and update the pointers to the store */
-				LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
-				list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
-					ebpt_delete(&entry->node);
-					/* change the ptr and reinsert the node */
-					entry->node.key = new_ckchs;
-					ebpt_insert(&entry->crtlist->entries, &entry->node);
-				}
-
-				/* First, we insert every new SNIs in the trees, also replace the default_ctx */
-				list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
-					HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
-					ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
-					HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
-				}
-
-				/* delete the old sni_ctx, the old ckch_insts and the ckch_store */
-				list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
-					struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
-
-					HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
-					ckch_inst_free(ckchi);
-					HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
-				}
-
-				/* Replace the old ckchs by the new one */
-				ckch_store_free(old_ckchs);
-				ebst_insert(&ckchs_tree, &new_ckchs->node);
-				appctx->st2 = SETCERT_ST_FIN;
-				/* fallthrough */
-			case SETCERT_ST_FIN:
-				/* we achieved the transaction, we can set everything to NULL */
-				free(ckchs_transaction.path);
-				ckchs_transaction.path = NULL;
-				ckchs_transaction.new_ckchs = NULL;
-				ckchs_transaction.old_ckchs = NULL;
-				goto end;
-		}
-	}
-end:
-
-	chunk_appendf(trash, "\n");
-	if (errcode & ERR_WARN)
-		chunk_appendf(trash, "%s", err);
-	chunk_appendf(trash, "Success!\n");
-	if (ci_putchk(si_ic(si), trash) == -1)
-		si_rx_room_blk(si);
-	free_trash_chunk(trash);
-	/* success: call the release function and don't come back */
-	return 1;
-yield:
-	/* store the state */
-	if (ci_putchk(si_ic(si), trash) == -1)
-		si_rx_room_blk(si);
-	free_trash_chunk(trash);
-	si_rx_endp_more(si); /* let's come back later */
-	return 0; /* should come back */
-
-error:
-	/* spin unlock and free are done in the release  function */
-	if (trash) {
-		chunk_appendf(trash, "\n%sFailed!\n", err);
-		if (ci_putchk(si_ic(si), trash) == -1)
-			si_rx_room_blk(si);
-		free_trash_chunk(trash);
-	}
-	/* error: call the release function and don't come back */
-	return 1;
-}
-
-/*
- * Parsing function of 'commit ssl cert'
- */
-static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	char *err = NULL;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
-		return 1;
-
-	if (!*args[3])
-		return cli_err(appctx, "'commit ssl cert expects a filename\n");
-
-	/* The operations on the CKCH architecture are locked so we can
-	 * manipulate ckch_store and ckch_inst */
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
-
-	if (!ckchs_transaction.path) {
-		memprintf(&err, "No ongoing transaction! !\n");
-		goto error;
-	}
-
-	if (strcmp(ckchs_transaction.path, args[3]) != 0) {
-		memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
-		goto error;
-	}
-
-#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
-	if (ckchs_transaction.new_ckchs->multi) {
-		int n;
-
-		for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
-			if (ckchs_transaction.new_ckchs->ckch[n].cert && !X509_check_private_key(ckchs_transaction.new_ckchs->ckch[n].cert, ckchs_transaction.new_ckchs->ckch[n].key)) {
-				memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
-				goto error;
-			}
-		}
-	} else
-#endif
-	{
-		if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
-			memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
-			goto error;
-		}
-	}
-
-	/* init the appctx structure */
-	appctx->st2 = SETCERT_ST_INIT;
-	appctx->ctx.ssl.next_ckchi = NULL;
-	appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
-	appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
-
-	/* we don't unlock there, it will be unlock after the IO handler, in the release handler */
-	return 0;
-
-error:
-
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
-
-	return cli_dynerr(appctx, err);
-}
-
-/*
- * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
- */
-static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	struct ckch_store *new_ckchs = NULL;
-	struct ckch_store *old_ckchs = NULL;
-	char *err = NULL;
-	int i;
-	int bundle = -1; /* TRUE if >= 0 (ckch index) */
-	int errcode = 0;
-	char *end;
-	int type = CERT_TYPE_PEM;
-	struct cert_key_and_chain *ckch;
-	struct buffer *buf;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
-		return 1;
-
-	if ((buf = alloc_trash_chunk()) == NULL)
-		return cli_err(appctx, "Can't allocate memory\n");
-
-	if (!*args[3] || !payload)
-		return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
-
-	/* The operations on the CKCH architecture are locked so we can
-	 * manipulate ckch_store and ckch_inst */
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
-
-	if (!chunk_strcpy(buf, args[3])) {
-		memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
-		errcode |= ERR_ALERT | ERR_FATAL;
-		goto end;
-	}
-
-	/* check which type of file we want to update */
-	for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
-		end = strrchr(buf->area, '.');
-		if (end && *cert_exts[i].ext && (!strcmp(end + 1, cert_exts[i].ext))) {
-			*end = '\0';
-			type = cert_exts[i].type;
-			break;
-		}
-	}
-
-	appctx->ctx.ssl.old_ckchs = NULL;
-	appctx->ctx.ssl.new_ckchs = NULL;
-
-	/* if there is an ongoing transaction */
-	if (ckchs_transaction.path) {
-		/* if the ongoing transaction is a bundle, we need to find which part of the bundle need to be updated */
-#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
-		if (ckchs_transaction.new_ckchs->multi) {
-			char *end;
-			int j;
-
-			/* check if it was used in a bundle by removing the
-			 *   .dsa/.rsa/.ecdsa at the end of the filename */
-			end = strrchr(buf->area, '.');
-			for (j = 0; end && j < SSL_SOCK_NUM_KEYTYPES; j++) {
-				if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
-					bundle = j; /* keep the type of certificate so we insert it at the right place */
-					*end = '\0'; /* it's a bundle let's end the string*/
-					break;
-				}
-			}
-			if (bundle < 0) {
-				memprintf(&err, "The ongoing transaction is the '%s' bundle. You need to specify which part of the bundle you want to update ('%s.{rsa,ecdsa,dsa}')\n", ckchs_transaction.path, buf->area);
-				errcode |= ERR_ALERT | ERR_FATAL;
-				goto end;
-			}
-		}
-#endif
-
-		/* if there is an ongoing transaction, check if this is the same file */
-		if (strcmp(ckchs_transaction.path, buf->area) != 0) {
-			memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
-			errcode |= ERR_ALERT | ERR_FATAL;
-			goto end;
-		}
-
-		appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
-
-	} else {
-		struct ckch_store *find_ckchs[2] = { NULL, NULL };
-
-		/* lookup for the certificate in the tree:
-		 * check if this is used as a bundle AND as a unique certificate */
-		for (i = 0; i < 2; i++) {
-
-			if ((find_ckchs[i] = ckchs_lookup(buf->area)) != NULL) {
-				/* only the bundle name is in the tree and you should
-				 * never update a bundle name, only a filename */
-				if (bundle < 0 && find_ckchs[i]->multi) {
-					/* we tried to look for a non-bundle and we found a bundle */
-					memprintf(&err, "%s%s is a multi-cert bundle. Try updating %s.{dsa,rsa,ecdsa}\n",
-						  err ? err : "", args[3], args[3]);
-					errcode |= ERR_ALERT | ERR_FATAL;
-					goto end;
-				}
-				/* If we want a bundle but this is not a bundle
-				 * example: When you try to update <file>.rsa, but
-				 * <file> is a regular file */
-				if (bundle >= 0 && find_ckchs[i]->multi == 0) {
-					find_ckchs[i] = NULL;
-					break;
-				}
-			}
-#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
-			{
-				char *end;
-				int j;
-
-				/* check if it was used in a bundle by removing the
-				 *   .dsa/.rsa/.ecdsa at the end of the filename */
-				end = strrchr(buf->area, '.');
-				for (j = 0; end && j < SSL_SOCK_NUM_KEYTYPES; j++) {
-					if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
-						bundle = j; /* keep the type of certificate so we insert it at the right place */
-						*end = '\0'; /* it's a bundle let's end the string*/
-						break;
-					}
-				}
-				if (bundle < 0) /* we didn't find a bundle extension */
-					break;
-			}
-#else
-			/* bundles are not supported here, so we don't need to lookup again */
-			break;
-#endif
-		}
-
-		if (find_ckchs[0] && find_ckchs[1]) {
-			memprintf(&err, "%sUpdating a certificate which is used in the HAProxy configuration as a bundle and as a unique certificate is not supported. ('%s' and '%s')\n",
-			          err ? err : "", find_ckchs[0]->path, find_ckchs[1]->path);
-			errcode |= ERR_ALERT | ERR_FATAL;
-			goto end;
-		}
-
-		appctx->ctx.ssl.old_ckchs = find_ckchs[0] ? find_ckchs[0] : find_ckchs[1];
-	}
-
-	if (!appctx->ctx.ssl.old_ckchs) {
-		memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
-		          err ? err : "");
-		errcode |= ERR_ALERT | ERR_FATAL;
-		goto end;
-	}
-
-	if (!appctx->ctx.ssl.path) {
-	/* this is a new transaction, set the path of the transaction */
-		appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
-		if (!appctx->ctx.ssl.path) {
-			memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
-			errcode |= ERR_ALERT | ERR_FATAL;
-			goto end;
-		}
-	}
-
-	old_ckchs = appctx->ctx.ssl.old_ckchs;
-
-	/* duplicate the ckch store */
-	new_ckchs = ckchs_dup(old_ckchs);
-	if (!new_ckchs) {
-		memprintf(&err, "%sCannot allocate memory!\n",
-			  err ? err : "");
-		errcode |= ERR_ALERT | ERR_FATAL;
-		goto end;
-	}
-
-	if (!new_ckchs->multi)
-		ckch = new_ckchs->ckch;
-	else
-		ckch = &new_ckchs->ckch[bundle];
-
-	/* appply the change on the duplicate */
-	if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
-		memprintf(&err, "%sCan't load the payload\n", err ? err : "");
-		errcode |= ERR_ALERT | ERR_FATAL;
-		goto end;
-	}
-
-	appctx->ctx.ssl.new_ckchs = new_ckchs;
-
-	/* we succeed, we can save the ckchs in the transaction */
-
-	/* if there wasn't a transaction, update the old ckchs */
-	if (!ckchs_transaction.old_ckchs) {
-		ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
-		ckchs_transaction.path = appctx->ctx.ssl.path;
-		err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
-	} else {
-		err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
-
-	}
-
-	/* free the previous ckchs if there was a transaction */
-	ckch_store_free(ckchs_transaction.new_ckchs);
-
-	ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
-
-
-	/* creates the SNI ctxs later in the IO handler */
-
-end:
-	free_trash_chunk(buf);
-
-	if (errcode & ERR_CODE) {
-
-		ckch_store_free(appctx->ctx.ssl.new_ckchs);
-		appctx->ctx.ssl.new_ckchs = NULL;
-
-		appctx->ctx.ssl.old_ckchs = NULL;
-
-		free(appctx->ctx.ssl.path);
-		appctx->ctx.ssl.path = NULL;
-
-		HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-		return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
-	} else {
-
-		HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-		return cli_dynmsg(appctx, LOG_NOTICE, err);
-	}
-	/* TODO: handle the ERR_WARN which are not handled because of the io_handler */
-}
-
-/* parsing function of 'abort ssl cert' */
-static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	char *err = NULL;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
-		return 1;
-
-	if (!*args[3])
-		return cli_err(appctx, "'abort ssl cert' expects a filename\n");
-
-	/* The operations on the CKCH architecture are locked so we can
-	 * manipulate ckch_store and ckch_inst */
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
-
-	if (!ckchs_transaction.path) {
-		memprintf(&err, "No ongoing transaction!\n");
-		goto error;
-	}
-
-	if (strcmp(ckchs_transaction.path, args[3]) != 0) {
-		memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
-		goto error;
-	}
-
-	/* Only free the ckchs there, because the SNI and instances were not generated yet */
-	ckch_store_free(ckchs_transaction.new_ckchs);
-	ckchs_transaction.new_ckchs = NULL;
-	ckch_store_free(ckchs_transaction.old_ckchs);
-	ckchs_transaction.old_ckchs = NULL;
-	free(ckchs_transaction.path);
-	ckchs_transaction.path = NULL;
-
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-
-	err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
-	return cli_dynmsg(appctx, LOG_NOTICE, err);
-
-error:
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-
-	return cli_dynerr(appctx, err);
-}
-
-/* parsing function of 'new ssl cert' */
-static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	struct ckch_store *store;
-	char *err = NULL;
-	char *path;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
-		return 1;
-
-	if (!*args[3])
-		return cli_err(appctx, "'new ssl cert' expects a filename\n");
-
-	path = args[3];
-
-	/* The operations on the CKCH architecture are locked so we can
-	 * manipulate ckch_store and ckch_inst */
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
-
-	store = ckchs_lookup(path);
-	if (store != NULL) {
-		memprintf(&err, "Certificate '%s' already exists!\n", path);
-		store = NULL; /* we don't want to free it */
-		goto error;
-	}
-	/* we won't support multi-certificate bundle here */
-	store = ckch_store_new(path, 1);
-	if (!store) {
-		memprintf(&err, "unable to allocate memory.\n");
-		goto error;
-	}
-
-	/* insert into the ckchs tree */
-	ebst_insert(&ckchs_tree, &store->node);
-	memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
-
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	return cli_dynmsg(appctx, LOG_NOTICE, err);
-error:
-	free(store);
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	return cli_dynerr(appctx, err);
-}
-
-/* parsing function of 'del ssl cert' */
-static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
-{
-	struct ckch_store *store;
-	char *err = NULL;
-	char *filename;
-
-	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
-		return 1;
-
-	if (!*args[3])
-		return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
-
-	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
-		return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
-
-	filename = args[3];
-
-	store = ckchs_lookup(filename);
-	if (store == NULL) {
-		memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
-		goto error;
-	}
-	if (!LIST_ISEMPTY(&store->ckch_inst)) {
-		memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
-		goto error;
-	}
-
-	ebmb_delete(&store->node);
-	ckch_store_free(store);
-
-	memprintf(&err, "Certificate '%s' deleted!\n", filename);
-
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	return cli_dynmsg(appctx, LOG_NOTICE, err);
-
-error:
-	memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
-	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-	return cli_dynerr(appctx, err);
-}
-
-
 
 static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
 {
@@ -10550,12 +9577,6 @@
 	{ { "set", "ssl", "tls-key", NULL }, "set ssl tls-key [id|keyfile] <tlskey>: set the next TLS key for the <id> or <keyfile> listener to <tlskey>", cli_parse_set_tlskeys, NULL },
 #endif
 	{ { "set", "ssl", "ocsp-response", NULL }, NULL, cli_parse_set_ocspresponse, NULL },
-	{ { "new", "ssl", "cert", NULL }, "new ssl cert <certfile> : create a new certificate file to be used in a crt-list or a directory", cli_parse_new_cert, NULL, NULL },
-	{ { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
-	{ { "commit", "ssl", "cert", NULL }, "commit ssl cert <certfile> : commit a certificate file", cli_parse_commit_cert, cli_io_handler_commit_cert, cli_release_commit_cert },
-	{ { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
-	{ { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
-	{ { "show", "ssl", "cert", NULL }, "show ssl cert [<certfile>] : display the SSL certificates used in memory, or the details of a <certfile>", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert },
 	{ { NULL }, NULL, NULL, NULL }
 }};