From: William Lallemand Date: Wed, 13 May 2020 15:23:59 +0000 (+0200) Subject: REORG: ssl: move the crt-list CLI functions in src/ssl_crtlist.c X-Git-Tag: v2.2-dev8~60 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c756bbd3df713744a4e3b5d1e7d616696009c3bc;p=thirdparty%2Fhaproxy.git 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 52b358cfb2..cff3095ba5 100644 --- a/include/proto/ssl_ckch.h +++ b/include/proto/ssl_ckch.h @@ -54,6 +54,8 @@ void ckch_store_free(struct ckch_store *store); 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 5d17b2b524..0090a6d3d3 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_assign_generated_cert(unsigned int key, struct bind_conf *bind 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 cf3382f9d0..b60f452d72 100644 --- a/src/ssl_crtlist.c +++ b/src/ssl_crtlist.c @@ -19,11 +19,16 @@ #include #include +#include +#include #include #include #include +#include +#include +#include #include #include #include @@ -573,3 +578,697 @@ end: } +/* + * 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 ' */ +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 " 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 " 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 [options] : add a line to a crt-list ", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist }, + { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list : delete a line in a crt-list ", cli_parse_del_crtlist, NULL, NULL }, + { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [] : show the list of crt-lists or the content of a crt-list ", 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 43ba750aef..2f5319dfcc 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2744,7 +2744,7 @@ static int ckch_inst_add_cert_sni(SSL_CTX *ctx, struct ckch_inst *ckch_inst, * * *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 @@ static void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf 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 @@ static int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *c * 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 @@ static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appc } #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 ' */ -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 " 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 " 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 @@ static struct cli_kw_list cli_kws = {{ },{ { { "abort", "ssl", "cert", NULL }, "abort ssl cert : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL }, { { "del", "ssl", "cert", NULL }, "del ssl cert : delete an unused certificate file", cli_parse_del_cert, NULL, NULL }, { { "show", "ssl", "cert", NULL }, "show ssl cert [] : display the SSL certificates used in memory, or the details of a ", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert }, - { { "add", "ssl", "crt-list", NULL }, "add ssl crt-list [options] : add a line to a crt-list ", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist }, - { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list : delete a line in a crt-list ", cli_parse_del_crtlist, NULL, NULL }, - { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [] : show the list of crt-lists or the content of a crt-list ", cli_parse_dump_crtlist, cli_io_handler_dump_crtlist, NULL }, { { NULL }, NULL, NULL, NULL } }};