]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: ssl: move the CLI 'cert' functions to src/ssl_ckch.c
authorWilliam Lallemand <wlallemand@haproxy.com>
Thu, 14 May 2020 08:14:37 +0000 (10:14 +0200)
committerWilliam Lallemand <wlallemand@haproxy.org>
Fri, 15 May 2020 12:11:54 +0000 (14:11 +0200)
Move the 'ssl cert' CLI functions to src/ssl_ckch.c.

include/proto/ssl_ckch.h
include/proto/ssl_sock.h
src/ssl_ckch.c
src/ssl_sock.c

index cff3095ba5367c96a2d18da7aa25765171419f05..d0df9b9ac6a63300c90723c3778bb9f33d1e7165 100644 (file)
@@ -53,7 +53,9 @@ void ckch_store_free(struct ckch_store *store);
 /* 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);
 
index 0090a6d3d3d4ac1a167fcf227aca7f1b1e647558..91eac9ab75f2fa9f183780c126d5938bb3468626 100644 (file)
@@ -100,6 +100,10 @@ void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_
 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 */
 
index 072cd3c18ec7f3c153d5f9d6d2b6aff9804019cb..484997d7f5cb8a8433d7566eed31c8d99db82792 100644 (file)
 
 #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 @@ struct ckch_inst *ckch_inst_new()
        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);
+
index 2f5319dfccd73d26112ac55dd25f96eeb9dc858b..6017fb6bfe630f11d7a463e622a1244bbaacc8c4 100644 (file)
@@ -108,7 +108,6 @@ static struct xprt_ops ssl_sock;
 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 @@ static int ssl_locking_init(void)
 
 __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 @@ static int ssl_sock_populate_sni_keytypes_hplr(const char *str, struct eb_root *
  *     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 @@ end:
 }
 #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 @@ static void ssl_sock_shutw(struct connection *conn, void *xprt_ctx, int clean)
 }
 
 /* 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 @@ const char *ssl_sock_get_proto_version(struct connection *conn)
  * 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;
 
@@ -6135,36 +6126,6 @@ ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
 
 }
 
-/*
- * 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.
@@ -6201,8 +6162,7 @@ out:
 /* 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 @@ static int ssl_load_global_issuer_from_BIO(BIO *in, char *fp, char **err)
        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;
@@ -9555,939 +9515,6 @@ static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appc
 }
 #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)
 {
 #if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
@@ -10550,12 +9577,6 @@ static struct cli_kw_list cli_kws = {{ },{
        { { "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 }
 }};