mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-11-29 14:50:59 +01:00
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
This commit is contained in:
parent
fa1d8b4eaa
commit
c756bbd3df
@ -54,6 +54,8 @@ void ckch_store_free(struct ckch_store *store);
|
|||||||
void ckch_inst_free(struct ckch_inst *inst);
|
void ckch_inst_free(struct ckch_inst *inst);
|
||||||
struct ckch_inst *ckch_inst_new();
|
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 /* USE_OPENSSL */
|
||||||
#endif /* _PROTO_SSL_CRTLIST_H */
|
#endif /* _PROTO_SSL_CRTLIST_H */
|
||||||
|
|||||||
@ -35,9 +35,12 @@
|
|||||||
extern int sslconns;
|
extern int sslconns;
|
||||||
extern int totalsslconns;
|
extern int totalsslconns;
|
||||||
extern struct eb_root ckchs_tree;
|
extern struct eb_root ckchs_tree;
|
||||||
|
extern struct eb_root crtlists_tree;
|
||||||
extern int sctl_ex_index;
|
extern int sctl_ex_index;
|
||||||
extern struct global_ssl global_ssl;
|
extern struct global_ssl global_ssl;
|
||||||
extern struct ssl_bind_kw ssl_bind_kws[];
|
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 */
|
/* boolean, returns true if connection is over SSL */
|
||||||
static inline
|
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);
|
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);
|
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);
|
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)
|
#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_handler(int fd);
|
||||||
void ssl_async_fd_free(int fd);
|
void ssl_async_fd_free(int fd);
|
||||||
|
|||||||
@ -19,11 +19,16 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <ebpttree.h>
|
#include <ebpttree.h>
|
||||||
|
#include <ebsttree.h>
|
||||||
|
|
||||||
|
#include <types/cli.h>
|
||||||
#include <types/ssl_crtlist.h>
|
#include <types/ssl_crtlist.h>
|
||||||
#include <types/ssl_ckch.h>
|
#include <types/ssl_ckch.h>
|
||||||
#include <types/ssl_sock.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_crtlist.h>
|
||||||
#include <proto/ssl_ckch.h>
|
#include <proto/ssl_ckch.h>
|
||||||
#include <proto/ssl_sock.h>
|
#include <proto/ssl_sock.h>
|
||||||
@ -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 <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);
|
||||||
|
|
||||||
|
|||||||
688
src/ssl_sock.c
688
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
|
* *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;
|
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;
|
struct eb_root ckchs_tree = EB_ROOT_UNIQUE;
|
||||||
|
|
||||||
/* tree of crtlist (crt-list/directory) */
|
/* 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.
|
/* Loads Diffie-Hellman parameter from a ckchs to an SSL_CTX.
|
||||||
* If there is no DH parameter available in the ckchs, the global
|
* 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_ALERT if the reason of the error is available in err
|
||||||
* ERR_WARN if a warning is available into 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)
|
struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err)
|
||||||
{
|
{
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
@ -9555,685 +9555,6 @@ static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appc
|
|||||||
}
|
}
|
||||||
#endif
|
#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 */
|
/* Type of SSL payloads that can be updated over the CLI */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -11235,9 +10556,6 @@ static struct cli_kw_list cli_kws = {{ },{
|
|||||||
{ { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
|
{ { "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 },
|
{ { "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 },
|
{ { "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 }
|
{ { NULL }, NULL, NULL, NULL }
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user