REORG: ssl: move the crt-list CLI functions in src/ssl_crtlist.c
Move the crtlist functions for the CLI to src/ssl_crtlist.c
diff --git a/include/proto/ssl_ckch.h b/include/proto/ssl_ckch.h
index 52b358c..cff3095 100644
--- a/include/proto/ssl_ckch.h
+++ b/include/proto/ssl_ckch.h
@@ -54,6 +54,8 @@
void ckch_inst_free(struct ckch_inst *inst);
struct ckch_inst *ckch_inst_new();
+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);
#endif /* USE_OPENSSL */
#endif /* _PROTO_SSL_CRTLIST_H */
diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
index 5d17b2b..0090a6d 100644
--- a/include/proto/ssl_sock.h
+++ b/include/proto/ssl_sock.h
@@ -35,9 +35,12 @@
extern int sslconns;
extern int totalsslconns;
extern struct eb_root ckchs_tree;
+extern struct eb_root crtlists_tree;
extern int sctl_ex_index;
extern struct global_ssl global_ssl;
extern struct ssl_bind_kw ssl_bind_kws[];
+extern struct methodVersions methodVersions[];
+__decl_hathreads(extern HA_SPINLOCK_T ckch_lock);
/* boolean, returns true if connection is over SSL */
static inline
@@ -92,7 +95,7 @@
SSL_CTX *ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_conf);
int ssl_sock_set_generated_cert(SSL_CTX *ctx, unsigned int key, struct bind_conf *bind_conf);
unsigned int ssl_sock_generated_cert_key(const void *data, size_t len);
-
+void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf);
#if (HA_OPENSSL_VERSION_NUMBER >= 0x1010000fL) && !defined(OPENSSL_NO_ASYNC) && !defined(LIBRESSL_VERSION_NUMBER)
void ssl_async_fd_handler(int fd);
void ssl_async_fd_free(int fd);
diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c
index cf3382f..b60f452 100644
--- a/src/ssl_crtlist.c
+++ b/src/ssl_crtlist.c
@@ -19,11 +19,16 @@
#include <dirent.h>
#include <ebpttree.h>
+#include <ebsttree.h>
+#include <types/cli.h>
#include <types/ssl_crtlist.h>
#include <types/ssl_ckch.h>
#include <types/ssl_sock.h>
+#include <proto/channel.h>
+#include <proto/cli.h>
+#include <proto/stream_interface.h>
#include <proto/ssl_crtlist.h>
#include <proto/ssl_ckch.h>
#include <proto/ssl_sock.h>
@@ -573,3 +578,697 @@
}
+/*
+ * Take an ssl_bind_conf structure and append the configuration line used to
+ * create it in the buffer
+ */
+static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
+{
+ int space = 0;
+
+ if (conf == NULL)
+ return;
+
+ chunk_appendf(buf, " [");
+#ifdef OPENSSL_NPN_NEGOTIATED
+ if (conf->npn_str) {
+ int len = conf->npn_len;
+ char *ptr = conf->npn_str;
+ int comma = 0;
+
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "npn ");
+ while (len) {
+ unsigned short size;
+
+ size = *ptr;
+ ptr++;
+ if (comma)
+ chunk_memcat(buf, ",", 1);
+ chunk_memcat(buf, ptr, size);
+ ptr += size;
+ len -= size + 1;
+ comma = 1;
+ }
+ chunk_memcat(buf, "", 1); /* finish with a \0 */
+ space++;
+ }
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ if (conf->alpn_str) {
+ int len = conf->alpn_len;
+ char *ptr = conf->alpn_str;
+ int comma = 0;
+
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "alpn ");
+ while (len) {
+ unsigned short size;
+
+ size = *ptr;
+ ptr++;
+ if (comma)
+ chunk_memcat(buf, ",", 1);
+ chunk_memcat(buf, ptr, size);
+ ptr += size;
+ len -= size + 1;
+ comma = 1;
+ }
+ chunk_memcat(buf, "", 1); /* finish with a \0 */
+ space++;
+ }
+#endif
+ /* verify */
+ {
+ if (conf->verify == SSL_SOCK_VERIFY_NONE) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "verify none");
+ space++;
+ } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "verify optional");
+ space++;
+ } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "verify required");
+ space++;
+ }
+ }
+
+ if (conf->no_ca_names) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "no-ca-names");
+ space++;
+ }
+
+ if (conf->early_data) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "allow-0rtt");
+ space++;
+ }
+ if (conf->ca_file) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ca-file %s", conf->ca_file);
+ space++;
+ }
+ if (conf->crl_file) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "crl-file %s", conf->crl_file);
+ space++;
+ }
+ if (conf->ciphers) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ciphers %s", conf->ciphers);
+ space++;
+ }
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+ if (conf->ciphersuites) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
+ space++;
+ }
+#endif
+ if (conf->curves) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "curves %s", conf->curves);
+ space++;
+ }
+ if (conf->ecdhe) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
+ space++;
+ }
+
+ /* the crt-lists only support ssl-min-ver and ssl-max-ver */
+ /* XXX: this part need to be revamp so we don't dump the default settings */
+ if (conf->ssl_methods.min) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods.min].name);
+ space++;
+ }
+
+ if (conf->ssl_methods.max) {
+ if (space) chunk_appendf(buf, " ");
+ chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods.max].name);
+ space++;
+ }
+
+ chunk_appendf(buf, "]");
+
+ return;
+}
+
+/* dump a list of filters */
+static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
+{
+ int i;
+
+ if (!entry->fcount)
+ return;
+
+ for (i = 0; i < entry->fcount; i++) {
+ chunk_appendf(buf, " %s", entry->filters[i]);
+ }
+ return;
+}
+
+/************************** CLI functions ****************************/
+
+
+/* CLI IO handler for '(show|dump) ssl crt-list' */
+static int cli_io_handler_dump_crtlist(struct appctx *appctx)
+{
+ struct buffer *trash = alloc_trash_chunk();
+ struct stream_interface *si = appctx->owner;
+ struct ebmb_node *lnode;
+
+ if (trash == NULL)
+ return 1;
+
+ /* dump the list of crt-lists */
+ lnode = appctx->ctx.cli.p1;
+ if (lnode == NULL)
+ lnode = ebmb_first(&crtlists_tree);
+ while (lnode) {
+ chunk_appendf(trash, "%s\n", lnode->key);
+ if (ci_putchk(si_ic(si), trash) == -1) {
+ si_rx_room_blk(si);
+ goto yield;
+ }
+ lnode = ebmb_next(lnode);
+ }
+ free_trash_chunk(trash);
+ return 1;
+yield:
+ appctx->ctx.cli.p1 = lnode;
+ free_trash_chunk(trash);
+ return 0;
+}
+
+/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
+static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
+{
+ struct buffer *trash = alloc_trash_chunk();
+ struct crtlist *crtlist;
+ struct stream_interface *si = appctx->owner;
+ struct crtlist_entry *entry;
+
+ if (trash == NULL)
+ return 1;
+
+ crtlist = ebmb_entry(appctx->ctx.cli.p0, struct crtlist, node);
+
+ entry = appctx->ctx.cli.p1;
+ if (entry == NULL) {
+ entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
+ chunk_appendf(trash, "# %s\n", crtlist->node.key);
+ if (ci_putchk(si_ic(si), trash) == -1) {
+ si_rx_room_blk(si);
+ goto yield;
+ }
+ }
+
+ list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
+ struct ckch_store *store;
+ const char *filename;
+
+ store = entry->node.key;
+ filename = store->path;
+ chunk_appendf(trash, "%s", filename);
+ if (appctx->ctx.cli.i0 == 's') /* show */
+ chunk_appendf(trash, ":%d", entry->linenum);
+ dump_crtlist_sslconf(trash, entry->ssl_conf);
+ dump_crtlist_filters(trash, entry);
+ chunk_appendf(trash, "\n");
+
+ if (ci_putchk(si_ic(si), trash) == -1) {
+ si_rx_room_blk(si);
+ goto yield;
+ }
+ }
+ free_trash_chunk(trash);
+ return 1;
+yield:
+ appctx->ctx.cli.p1 = entry;
+ free_trash_chunk(trash);
+ return 0;
+}
+
+/* CLI argument parser for '(show|dump) ssl crt-list' */
+static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ struct ebmb_node *lnode;
+ char *filename = NULL;
+ int mode;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ appctx->ctx.cli.p0 = NULL;
+ appctx->ctx.cli.p1 = NULL;
+
+ if (*args[3] && !strcmp(args[3], "-n")) {
+ mode = 's';
+ filename = args[4];
+ } else {
+ mode = 'd';
+ filename = args[3];
+ }
+
+ if (mode == 's' && !*args[4])
+ return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
+
+ if (filename && *filename) {
+ lnode = ebst_lookup(&crtlists_tree, filename);
+ if (lnode == NULL)
+ return cli_err(appctx, "didn't find the specified filename\n");
+
+ appctx->ctx.cli.p0 = lnode;
+ appctx->io_handler = cli_io_handler_dump_crtlist_entries;
+ }
+ appctx->ctx.cli.i0 = mode;
+
+ return 0;
+}
+
+/* release function of the "add ssl crt-list' command, free things and unlock
+ the spinlock */
+static void cli_release_add_crtlist(struct appctx *appctx)
+{
+ struct crtlist_entry *entry = appctx->ctx.cli.p1;
+
+ if (appctx->st2 != SETCERT_ST_FIN) {
+ struct ckch_inst *inst, *inst_s;
+ /* upon error free the ckch_inst and everything inside */
+ ebpt_delete(&entry->node);
+ LIST_DEL(&entry->by_crtlist);
+ LIST_DEL(&entry->by_ckch_store);
+
+ list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
+ ckch_inst_free(inst);
+ }
+ crtlist_free_filters(entry->filters);
+ ssl_sock_free_ssl_conf(entry->ssl_conf);
+ free(entry->ssl_conf);
+ free(entry);
+ }
+
+ HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+}
+
+
+/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
+ * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
+ *
+ * The logic is the same as the "commit ssl cert" command but without the
+ * freeing of the old structures, because there are none.
+ */
+static int cli_io_handler_add_crtlist(struct appctx *appctx)
+{
+ struct bind_conf_list *bind_conf_node;
+ struct stream_interface *si = appctx->owner;
+ struct crtlist *crtlist = appctx->ctx.cli.p0;
+ struct crtlist_entry *entry = appctx->ctx.cli.p1;
+ struct ckch_store *store = entry->node.key;
+ struct buffer *trash = alloc_trash_chunk();
+ struct ckch_inst *new_inst;
+ char *err = NULL;
+ int i = 0;
+ int errcode = 0;
+
+ if (trash == NULL)
+ goto error;
+
+ /* for each bind_conf which use the crt-list, a new ckch_inst must be
+ * created.
+ */
+ 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, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
+ 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:
+ bind_conf_node = appctx->ctx.cli.p2; /* get the previous ptr from the yield */
+ if (bind_conf_node == NULL)
+ bind_conf_node = crtlist->bind_conf;
+ for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
+ struct bind_conf *bind_conf = bind_conf_node->bind_conf;
+ struct sni_ctx *sni;
+
+ /* yield every 10 generations */
+ if (i > 10) {
+ appctx->ctx.cli.p2 = bind_conf_node;
+ goto yield;
+ }
+
+ /* we don't support multi-cert bundles, only simple ones */
+ errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &err);
+ if (errcode & ERR_CODE)
+ goto error;
+
+ /* 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(sni, &new_inst->sni_ctx, by_ckch_inst) {
+ if (!sni->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
+ errcode |= ssl_sock_prepare_ctx(bind_conf, new_inst->ssl_conf, sni->ctx, &err);
+ if (errcode & ERR_CODE)
+ goto error;
+ }
+ }
+ /* display one dot for each new instance */
+ chunk_appendf(trash, ".");
+ i++;
+ LIST_ADDQ(&store->ckch_inst, &new_inst->by_ckchs);
+ }
+ appctx->st2 = SETCERT_ST_INSERT;
+ /* fallthrough */
+ case SETCERT_ST_INSERT:
+ /* insert SNIs in bind_conf */
+ list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
+ HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
+ ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
+ HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
+ }
+ entry->linenum = ++crtlist->linecount;
+ appctx->st2 = SETCERT_ST_FIN;
+ 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;
+}
+
+
+/*
+ * Parse a "add ssl crt-list <crt-list> <certfile>" line.
+ * Filters and option must be passed through payload:
+ */
+static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ int cfgerr = 0;
+ struct ckch_store *store;
+ char *err = NULL;
+ char path[MAXPATHLEN+1];
+ char *crtlist_path;
+ char *cert_path = NULL;
+ struct ebmb_node *eb;
+ struct ebpt_node *inserted;
+ struct crtlist *crtlist;
+ struct crtlist_entry *entry = NULL;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ if (!*args[3] || (!payload && !*args[4]))
+ return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
+
+ crtlist_path = args[3];
+
+ if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+ return cli_err(appctx, "Operations on certificates are currently locked!\n");
+
+ eb = ebst_lookup(&crtlists_tree, crtlist_path);
+ if (!eb) {
+ memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
+ goto error;
+ }
+ crtlist = ebmb_entry(eb, struct crtlist, node);
+
+ entry = crtlist_entry_new();
+ if (entry == NULL) {
+ memprintf(&err, "Not enough memory!");
+ goto error;
+ }
+
+ if (payload) {
+ char *lf;
+
+ lf = strrchr(payload, '\n');
+ if (lf) {
+ memprintf(&err, "only one line of payload is supported!");
+ goto error;
+ }
+ /* cert_path is filled here */
+ cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, &err);
+ if (cfgerr & ERR_CODE)
+ goto error;
+ } else {
+ cert_path = args[4];
+ }
+
+ if (!cert_path) {
+ memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
+ cfgerr |= ERR_ALERT | ERR_FATAL;
+ goto error;
+ }
+
+ if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
+ char *slash;
+
+ slash = strrchr(cert_path, '/');
+ if (!slash) {
+ memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
+ goto error;
+ }
+ /* temporary replace / by 0 to do an strcmp */
+ *slash = '\0';
+ if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
+ *slash = '/';
+ memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
+ goto error;
+ }
+ *slash = '/';
+ }
+
+ if (*cert_path != '/' && global_ssl.crt_base) {
+ if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > MAXPATHLEN) {
+ memprintf(&err, "'%s' : path too long", cert_path);
+ cfgerr |= ERR_ALERT | ERR_FATAL;
+ goto error;
+ }
+ snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path);
+ cert_path = path;
+ }
+
+ store = ckchs_lookup(cert_path);
+ if (store == NULL) {
+ memprintf(&err, "certificate '%s' does not exist!", cert_path);
+ goto error;
+ }
+ if (store->multi) {
+ memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
+ goto error;
+ }
+ if (store->ckch == NULL || store->ckch->cert == NULL) {
+ memprintf(&err, "certificate '%s' is empty!", cert_path);
+ goto error;
+ }
+
+ /* check if it's possible to insert this new crtlist_entry */
+ entry->node.key = store;
+ inserted = ebpt_insert(&crtlist->entries, &entry->node);
+ if (inserted != &entry->node) {
+ memprintf(&err, "file already exists in this directory!");
+ goto error;
+ }
+
+ /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
+ if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
+ memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
+ goto error;
+ }
+
+ LIST_ADDQ(&crtlist->ord_entries, &entry->by_crtlist);
+ entry->crtlist = crtlist;
+ LIST_ADDQ(&store->crtlist_entry, &entry->by_ckch_store);
+
+ appctx->st2 = SETCERT_ST_INIT;
+ appctx->ctx.cli.p0 = crtlist;
+ appctx->ctx.cli.p1 = entry;
+
+ /* unlock is done in the release handler */
+ return 0;
+
+error:
+ crtlist_entry_free(entry);
+ HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+ err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
+ return cli_dynerr(appctx, err);
+}
+
+/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
+static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ struct ckch_store *store;
+ char *err = NULL;
+ char *crtlist_path, *cert_path;
+ struct ebmb_node *ebmb;
+ struct ebpt_node *ebpt;
+ struct crtlist *crtlist;
+ struct crtlist_entry *entry = NULL;
+ struct ckch_inst *inst, *inst_s;
+ int linenum = 0;
+ char *colons;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ if (!*args[3] || !*args[4])
+ return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
+
+ if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
+ return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
+
+ crtlist_path = args[3];
+ cert_path = args[4];
+
+ colons = strchr(cert_path, ':');
+ if (colons) {
+ char *endptr;
+
+ linenum = strtol(colons + 1, &endptr, 10);
+ if (colons + 1 == endptr || *endptr != '\0') {
+ memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
+ goto error;
+ }
+ *colons = '\0';
+ }
+ /* look for crtlist */
+ ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
+ if (!ebmb) {
+ memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
+ goto error;
+ }
+ crtlist = ebmb_entry(ebmb, struct crtlist, node);
+
+ /* look for store */
+ store = ckchs_lookup(cert_path);
+ if (store == NULL) {
+ memprintf(&err, "certificate '%s' does not exist!", cert_path);
+ goto error;
+ }
+ if (store->multi) {
+ memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
+ goto error;
+ }
+ if (store->ckch == NULL || store->ckch->cert == NULL) {
+ memprintf(&err, "certificate '%s' is empty!", cert_path);
+ goto error;
+ }
+
+ ebpt = ebpt_lookup(&crtlist->entries, store);
+ if (!ebpt) {
+ memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
+ goto error;
+ }
+
+ /* list the line number of entries for errors in err, and select the right ebpt */
+ for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
+ struct crtlist_entry *tmp;
+
+ tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
+ memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
+
+ /* select the entry we wanted */
+ if (linenum == 0 || tmp->linenum == linenum) {
+ if (!entry)
+ entry = tmp;
+ }
+ }
+
+ /* we didn't found the specified entry */
+ if (!entry) {
+ memprintf(&err, "found a certificate '%s' but the line number is incorrect, please specify a correct line number preceded by colons (%s)!", cert_path, err ? err : NULL);
+ goto error;
+ }
+
+ /* we didn't specified a line number but there were several entries */
+ if (linenum == 0 && ebpt_next_dup(&entry->node)) {
+ memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
+ goto error;
+ }
+
+ /* upon error free the ckch_inst and everything inside */
+
+ ebpt_delete(&entry->node);
+ LIST_DEL(&entry->by_crtlist);
+ LIST_DEL(&entry->by_ckch_store);
+
+ list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
+ struct sni_ctx *sni, *sni_s;
+
+ HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
+ list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
+ ebmb_delete(&sni->name);
+ LIST_DEL(&sni->by_ckch_inst);
+ SSL_CTX_free(sni->ctx);
+ free(sni);
+ }
+ HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
+ LIST_DEL(&inst->by_ckchs);
+ free(inst);
+ }
+
+ crtlist_free_filters(entry->filters);
+ ssl_sock_free_ssl_conf(entry->ssl_conf);
+ free(entry->ssl_conf);
+ free(entry);
+
+ HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+ err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
+ return cli_dynmsg(appctx, LOG_NOTICE, err);
+
+error:
+ HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
+ err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
+ return cli_dynerr(appctx, err);
+}
+
+
+
+/* register cli keywords */
+static struct cli_kw_list cli_kws = {{ },{
+ { { "add", "ssl", "crt-list", NULL }, "add ssl crt-list <filename> <certfile> [options] : add a line <certfile> to a crt-list <filename>", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist },
+ { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list <filename> <certfile[:line]> : delete a line <certfile> in a crt-list <filename>", cli_parse_del_crtlist, NULL, NULL },
+ { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [<filename>] : show the list of crt-lists or the content of a crt-list <filename>", cli_parse_dump_crtlist, cli_io_handler_dump_crtlist, NULL },
+ { { 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 43ba750..2f5319d 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -2744,7 +2744,7 @@
*
* *CAUTION*: The caller must lock the sni tree if called in multithreading mode
*/
-static void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf)
+void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf)
{
struct sni_ctx *sc0, *sc0b, *sc1;
@@ -2801,7 +2801,7 @@
struct eb_root ckchs_tree = EB_ROOT_UNIQUE;
/* tree of crtlist (crt-list/directory) */
-static struct eb_root crtlists_tree = EB_ROOT_UNIQUE;
+struct eb_root crtlists_tree = EB_ROOT_UNIQUE;
/* Loads Diffie-Hellman parameter from a ckchs to an SSL_CTX.
* If there is no DH parameter available in the ckchs, the global
@@ -3317,7 +3317,7 @@
* ERR_ALERT if the reason of the error is available in err
* ERR_WARN if a warning is available into err
*/
-static int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
+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)
{
SSL_CTX *ctx;
@@ -9555,685 +9555,6 @@
}
#endif
-/*
- * Take an ssl_bind_conf structure and append the configuration line used to
- * create it in the buffer
- */
-static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
-{
- int space = 0;
-
- if (conf == NULL)
- return;
-
- chunk_appendf(buf, " [");
-#ifdef OPENSSL_NPN_NEGOTIATED
- if (conf->npn_str) {
- int len = conf->npn_len;
- char *ptr = conf->npn_str;
- int comma = 0;
-
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "npn ");
- while (len) {
- unsigned short size;
-
- size = *ptr;
- ptr++;
- if (comma)
- chunk_memcat(buf, ",", 1);
- chunk_memcat(buf, ptr, size);
- ptr += size;
- len -= size + 1;
- comma = 1;
- }
- chunk_memcat(buf, "", 1); /* finish with a \0 */
- space++;
- }
-#endif
-#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
- if (conf->alpn_str) {
- int len = conf->alpn_len;
- char *ptr = conf->alpn_str;
- int comma = 0;
-
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "alpn ");
- while (len) {
- unsigned short size;
-
- size = *ptr;
- ptr++;
- if (comma)
- chunk_memcat(buf, ",", 1);
- chunk_memcat(buf, ptr, size);
- ptr += size;
- len -= size + 1;
- comma = 1;
- }
- chunk_memcat(buf, "", 1); /* finish with a \0 */
- space++;
- }
-#endif
- /* verify */
- {
- if (conf->verify == SSL_SOCK_VERIFY_NONE) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "verify none");
- space++;
- } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "verify optional");
- space++;
- } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "verify required");
- space++;
- }
- }
-
- if (conf->no_ca_names) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "no-ca-names");
- space++;
- }
-
- if (conf->early_data) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "allow-0rtt");
- space++;
- }
- if (conf->ca_file) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ca-file %s", conf->ca_file);
- space++;
- }
- if (conf->crl_file) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "crl-file %s", conf->crl_file);
- space++;
- }
- if (conf->ciphers) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ciphers %s", conf->ciphers);
- space++;
- }
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
- if (conf->ciphersuites) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
- space++;
- }
-#endif
- if (conf->curves) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "curves %s", conf->curves);
- space++;
- }
- if (conf->ecdhe) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
- space++;
- }
-
- /* the crt-lists only support ssl-min-ver and ssl-max-ver */
- /* XXX: this part need to be revamp so we don't dump the default settings */
- if (conf->ssl_methods.min) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods.min].name);
- space++;
- }
-
- if (conf->ssl_methods.max) {
- if (space) chunk_appendf(buf, " ");
- chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods.max].name);
- space++;
- }
-
- chunk_appendf(buf, "]");
-
- return;
-}
-
-/* dump a list of filters */
-static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
-{
- int i;
-
- if (!entry->fcount)
- return;
-
- for (i = 0; i < entry->fcount; i++) {
- chunk_appendf(buf, " %s", entry->filters[i]);
- }
- return;
-}
-
-/* CLI IO handler for '(show|dump) ssl crt-list' */
-static int cli_io_handler_dump_crtlist(struct appctx *appctx)
-{
- struct buffer *trash = alloc_trash_chunk();
- struct stream_interface *si = appctx->owner;
- struct ebmb_node *lnode;
-
- if (trash == NULL)
- return 1;
-
- /* dump the list of crt-lists */
- lnode = appctx->ctx.cli.p1;
- if (lnode == NULL)
- lnode = ebmb_first(&crtlists_tree);
- while (lnode) {
- chunk_appendf(trash, "%s\n", lnode->key);
- if (ci_putchk(si_ic(si), trash) == -1) {
- si_rx_room_blk(si);
- goto yield;
- }
- lnode = ebmb_next(lnode);
- }
- free_trash_chunk(trash);
- return 1;
-yield:
- appctx->ctx.cli.p1 = lnode;
- free_trash_chunk(trash);
- return 0;
-}
-
-/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
-static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
-{
- struct buffer *trash = alloc_trash_chunk();
- struct crtlist *crtlist;
- struct stream_interface *si = appctx->owner;
- struct crtlist_entry *entry;
-
- if (trash == NULL)
- return 1;
-
- crtlist = ebmb_entry(appctx->ctx.cli.p0, struct crtlist, node);
-
- entry = appctx->ctx.cli.p1;
- if (entry == NULL) {
- entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
- chunk_appendf(trash, "# %s\n", crtlist->node.key);
- if (ci_putchk(si_ic(si), trash) == -1) {
- si_rx_room_blk(si);
- goto yield;
- }
- }
-
- list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
- struct ckch_store *store;
- const char *filename;
-
- store = entry->node.key;
- filename = store->path;
- chunk_appendf(trash, "%s", filename);
- if (appctx->ctx.cli.i0 == 's') /* show */
- chunk_appendf(trash, ":%d", entry->linenum);
- dump_crtlist_sslconf(trash, entry->ssl_conf);
- dump_crtlist_filters(trash, entry);
- chunk_appendf(trash, "\n");
-
- if (ci_putchk(si_ic(si), trash) == -1) {
- si_rx_room_blk(si);
- goto yield;
- }
- }
- free_trash_chunk(trash);
- return 1;
-yield:
- appctx->ctx.cli.p1 = entry;
- free_trash_chunk(trash);
- return 0;
-}
-
-/* CLI argument parser for '(show|dump) ssl crt-list' */
-static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
-{
- struct ebmb_node *lnode;
- char *filename = NULL;
- int mode;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- return 1;
-
- appctx->ctx.cli.p0 = NULL;
- appctx->ctx.cli.p1 = NULL;
-
- if (*args[3] && !strcmp(args[3], "-n")) {
- mode = 's';
- filename = args[4];
- } else {
- mode = 'd';
- filename = args[3];
- }
-
- if (mode == 's' && !*args[4])
- return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
-
- if (filename && *filename) {
- lnode = ebst_lookup(&crtlists_tree, filename);
- if (lnode == NULL)
- return cli_err(appctx, "didn't find the specified filename\n");
-
- appctx->ctx.cli.p0 = lnode;
- appctx->io_handler = cli_io_handler_dump_crtlist_entries;
- }
- appctx->ctx.cli.i0 = mode;
-
- return 0;
-}
-
-/* release function of the "add ssl crt-list' command, free things and unlock
- the spinlock */
-static void cli_release_add_crtlist(struct appctx *appctx)
-{
- struct crtlist_entry *entry = appctx->ctx.cli.p1;
-
- if (appctx->st2 != SETCERT_ST_FIN) {
- struct ckch_inst *inst, *inst_s;
- /* upon error free the ckch_inst and everything inside */
- ebpt_delete(&entry->node);
- LIST_DEL(&entry->by_crtlist);
- LIST_DEL(&entry->by_ckch_store);
-
- list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
- ckch_inst_free(inst);
- }
- crtlist_free_filters(entry->filters);
- ssl_sock_free_ssl_conf(entry->ssl_conf);
- free(entry->ssl_conf);
- free(entry);
- }
-
- HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
-}
-
-
-/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
- * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
- *
- * The logic is the same as the "commit ssl cert" command but without the
- * freeing of the old structures, because there are none.
- */
-static int cli_io_handler_add_crtlist(struct appctx *appctx)
-{
- struct bind_conf_list *bind_conf_node;
- struct stream_interface *si = appctx->owner;
- struct crtlist *crtlist = appctx->ctx.cli.p0;
- struct crtlist_entry *entry = appctx->ctx.cli.p1;
- struct ckch_store *store = entry->node.key;
- struct buffer *trash = alloc_trash_chunk();
- struct ckch_inst *new_inst;
- char *err = NULL;
- int i = 0;
- int errcode = 0;
-
- if (trash == NULL)
- goto error;
-
- /* for each bind_conf which use the crt-list, a new ckch_inst must be
- * created.
- */
- 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, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
- 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:
- bind_conf_node = appctx->ctx.cli.p2; /* get the previous ptr from the yield */
- if (bind_conf_node == NULL)
- bind_conf_node = crtlist->bind_conf;
- for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
- struct bind_conf *bind_conf = bind_conf_node->bind_conf;
- struct sni_ctx *sni;
-
- /* yield every 10 generations */
- if (i > 10) {
- appctx->ctx.cli.p2 = bind_conf_node;
- goto yield;
- }
-
- /* we don't support multi-cert bundles, only simple ones */
- errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &err);
- if (errcode & ERR_CODE)
- goto error;
-
- /* 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(sni, &new_inst->sni_ctx, by_ckch_inst) {
- if (!sni->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
- errcode |= ssl_sock_prepare_ctx(bind_conf, new_inst->ssl_conf, sni->ctx, &err);
- if (errcode & ERR_CODE)
- goto error;
- }
- }
- /* display one dot for each new instance */
- chunk_appendf(trash, ".");
- i++;
- LIST_ADDQ(&store->ckch_inst, &new_inst->by_ckchs);
- }
- appctx->st2 = SETCERT_ST_INSERT;
- /* fallthrough */
- case SETCERT_ST_INSERT:
- /* insert SNIs in bind_conf */
- list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
- HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
- ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
- HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
- }
- entry->linenum = ++crtlist->linecount;
- appctx->st2 = SETCERT_ST_FIN;
- 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;
-}
-
-
-/*
- * Parse a "add ssl crt-list <crt-list> <certfile>" line.
- * Filters and option must be passed through payload:
- */
-static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
-{
- int cfgerr = 0;
- struct ckch_store *store;
- char *err = NULL;
- char path[MAXPATHLEN+1];
- char *crtlist_path;
- char *cert_path = NULL;
- struct ebmb_node *eb;
- struct ebpt_node *inserted;
- struct crtlist *crtlist;
- struct crtlist_entry *entry = NULL;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- return 1;
-
- if (!*args[3] || (!payload && !*args[4]))
- return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
-
- crtlist_path = args[3];
-
- if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
- return cli_err(appctx, "Operations on certificates are currently locked!\n");
-
- eb = ebst_lookup(&crtlists_tree, crtlist_path);
- if (!eb) {
- memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
- goto error;
- }
- crtlist = ebmb_entry(eb, struct crtlist, node);
-
- entry = crtlist_entry_new();
- if (entry == NULL) {
- memprintf(&err, "Not enough memory!");
- goto error;
- }
-
- if (payload) {
- char *lf;
-
- lf = strrchr(payload, '\n');
- if (lf) {
- memprintf(&err, "only one line of payload is supported!");
- goto error;
- }
- /* cert_path is filled here */
- cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, &err);
- if (cfgerr & ERR_CODE)
- goto error;
- } else {
- cert_path = args[4];
- }
-
- if (!cert_path) {
- memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
- cfgerr |= ERR_ALERT | ERR_FATAL;
- goto error;
- }
-
- if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
- char *slash;
-
- slash = strrchr(cert_path, '/');
- if (!slash) {
- memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
- goto error;
- }
- /* temporary replace / by 0 to do an strcmp */
- *slash = '\0';
- if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
- *slash = '/';
- memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
- goto error;
- }
- *slash = '/';
- }
-
- if (*cert_path != '/' && global_ssl.crt_base) {
- if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > MAXPATHLEN) {
- memprintf(&err, "'%s' : path too long", cert_path);
- cfgerr |= ERR_ALERT | ERR_FATAL;
- goto error;
- }
- snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path);
- cert_path = path;
- }
-
- store = ckchs_lookup(cert_path);
- if (store == NULL) {
- memprintf(&err, "certificate '%s' does not exist!", cert_path);
- goto error;
- }
- if (store->multi) {
- memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
- goto error;
- }
- if (store->ckch == NULL || store->ckch->cert == NULL) {
- memprintf(&err, "certificate '%s' is empty!", cert_path);
- goto error;
- }
-
- /* check if it's possible to insert this new crtlist_entry */
- entry->node.key = store;
- inserted = ebpt_insert(&crtlist->entries, &entry->node);
- if (inserted != &entry->node) {
- memprintf(&err, "file already exists in this directory!");
- goto error;
- }
-
- /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
- if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
- memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
- goto error;
- }
-
- LIST_ADDQ(&crtlist->ord_entries, &entry->by_crtlist);
- entry->crtlist = crtlist;
- LIST_ADDQ(&store->crtlist_entry, &entry->by_ckch_store);
-
- appctx->st2 = SETCERT_ST_INIT;
- appctx->ctx.cli.p0 = crtlist;
- appctx->ctx.cli.p1 = entry;
-
- /* unlock is done in the release handler */
- return 0;
-
-error:
- crtlist_entry_free(entry);
- HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
- err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
- return cli_dynerr(appctx, err);
-}
-
-/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
-static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
-{
- struct ckch_store *store;
- char *err = NULL;
- char *crtlist_path, *cert_path;
- struct ebmb_node *ebmb;
- struct ebpt_node *ebpt;
- struct crtlist *crtlist;
- struct crtlist_entry *entry = NULL;
- struct ckch_inst *inst, *inst_s;
- int linenum = 0;
- char *colons;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- return 1;
-
- if (!*args[3] || !*args[4])
- return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
-
- if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
- return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
-
- crtlist_path = args[3];
- cert_path = args[4];
-
- colons = strchr(cert_path, ':');
- if (colons) {
- char *endptr;
-
- linenum = strtol(colons + 1, &endptr, 10);
- if (colons + 1 == endptr || *endptr != '\0') {
- memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
- goto error;
- }
- *colons = '\0';
- }
- /* look for crtlist */
- ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
- if (!ebmb) {
- memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
- goto error;
- }
- crtlist = ebmb_entry(ebmb, struct crtlist, node);
-
- /* look for store */
- store = ckchs_lookup(cert_path);
- if (store == NULL) {
- memprintf(&err, "certificate '%s' does not exist!", cert_path);
- goto error;
- }
- if (store->multi) {
- memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
- goto error;
- }
- if (store->ckch == NULL || store->ckch->cert == NULL) {
- memprintf(&err, "certificate '%s' is empty!", cert_path);
- goto error;
- }
-
- ebpt = ebpt_lookup(&crtlist->entries, store);
- if (!ebpt) {
- memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
- goto error;
- }
-
- /* list the line number of entries for errors in err, and select the right ebpt */
- for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
- struct crtlist_entry *tmp;
-
- tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
- memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
-
- /* select the entry we wanted */
- if (linenum == 0 || tmp->linenum == linenum) {
- if (!entry)
- entry = tmp;
- }
- }
-
- /* we didn't found the specified entry */
- if (!entry) {
- memprintf(&err, "found a certificate '%s' but the line number is incorrect, please specify a correct line number preceded by colons (%s)!", cert_path, err ? err : NULL);
- goto error;
- }
-
- /* we didn't specified a line number but there were several entries */
- if (linenum == 0 && ebpt_next_dup(&entry->node)) {
- memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
- goto error;
- }
-
- /* upon error free the ckch_inst and everything inside */
-
- ebpt_delete(&entry->node);
- LIST_DEL(&entry->by_crtlist);
- LIST_DEL(&entry->by_ckch_store);
-
- list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
- struct sni_ctx *sni, *sni_s;
-
- HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
- list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
- ebmb_delete(&sni->name);
- LIST_DEL(&sni->by_ckch_inst);
- SSL_CTX_free(sni->ctx);
- free(sni);
- }
- HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
- LIST_DEL(&inst->by_ckchs);
- free(inst);
- }
-
- crtlist_free_filters(entry->filters);
- ssl_sock_free_ssl_conf(entry->ssl_conf);
- free(entry->ssl_conf);
- free(entry);
-
- HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
- err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
- return cli_dynmsg(appctx, LOG_NOTICE, err);
-
-error:
- HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
- err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
- return cli_dynerr(appctx, err);
-}
-
/* Type of SSL payloads that can be updated over the CLI */
enum {
@@ -11235,9 +10556,6 @@
{ { "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 },
- { { "add", "ssl", "crt-list", NULL }, "add ssl crt-list <filename> <certfile> [options] : add a line <certfile> to a crt-list <filename>", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist },
- { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list <filename> <certfile[:line]> : delete a line <certfile> in a crt-list <filename>", cli_parse_del_crtlist, NULL, NULL },
- { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [<filename>] : show the list of crt-lists or the content of a crt-list <filename>", cli_parse_dump_crtlist, cli_io_handler_dump_crtlist, NULL },
{ { NULL }, NULL, NULL, NULL }
}};