Hello,
I have some patches to support dynamically loading and unloading PEM
certificates through the CLI. It is mainly a big refactoring of some
part of the SSL code (thanks Thierry for your patches, we came to the
same conclusion :) !).
When loading a PEM certificate, one also wants to load OCSP data/issuer
and/or SCTL. With the current implementation, all of these are fetched
from the filesystem. To have a full "dynamic load/unload" support, all
the functions that deal with these data need to be modified to use
buffers instead.
As a start, here is a draft of two patches that refactors some parts and
adds two new CLI commands:
- add ssl cert <frontend[:luid]> <PEM certificate>
- del ssl cert <frontend[:luid]> <domain>
Only loading a simple PEM is supported for now.
Support could be added gradually (PEM, then OCSP/issuer then SCTL) but
the main goal of this draft is to gather comments whether this is the
good way to go.
Warnings:
Until multi-lines support is added to the CLI, PEM certificates fed
through the CLI must not exceed 60 lines (CLI takes a maximum of 64
words and "add ssl cert" + frontend is 4 words).
The syntax is not very user-friendly as it requires a luid. I didn't
find a better way yet to specify for which 'listener' the add/del
operation has to be performed. Using a name could be better but it's an
optional value in the configuration.
Examples:
# echo "add ssl cert fe $(cat cert.pem | tr '\n' ' ') | socat /tmp/sock1 -
# echo "del ssl cert fe:2 example.com" | socat /tmp/sock1 -
Thanks !
--
Aurélien.
>From 46d5169a4dbd88d302c64b586748e22737bdadba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
Date: Wed, 18 Apr 2018 15:34:33 +0200
Subject: [PATCH 1/2] MINOR/WIP: ssl: Refactor ssl_sock_load_cert_file() into
ssl_sock_load_cert2()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- ssl_sock_get_dh_from_file() -> ssl_sock_get_dh()
- ssl_sock_load_dh_params() takes a BIO * instead of a char *
- ssl_sock_load_cert_chain_file() -> ssl_sock_load_cert_chain() + takes a
BIO * instead of a char *
Signed-off-by: Aurélien Nephtali <[email protected]>
---
src/ssl_sock.c | 210 +++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 136 insertions(+), 74 deletions(-)
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 23ad35b18..7543c7a69 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -2583,43 +2583,39 @@ static DH *ssl_get_tmp_dh(SSL *ssl, int export, int keylen)
return dh;
}
-static DH * ssl_sock_get_dh_from_file(const char *filename)
+static DH * ssl_sock_get_dh(BIO *in)
{
- DH *dh = NULL;
- BIO *in = BIO_new(BIO_s_file());
+ return PEM_read_bio_DHparams(in, NULL, NULL, NULL);
+}
- if (in == NULL)
- goto end;
+int ssl_sock_load_global_dh_param_from_file(const char *filename)
+{
+ BIO *in;
+ int ret = -1;
+
+ in = BIO_new(BIO_s_file());
+ if (!in)
+ return -1;
if (BIO_read_filename(in, filename) <= 0)
goto end;
- dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
+ global_dh = ssl_sock_get_dh(in);
+ if (global_dh)
+ ret = 0;
end:
- if (in)
- BIO_free(in);
+ BIO_free(in);
- return dh;
-}
-
-int ssl_sock_load_global_dh_param_from_file(const char *filename)
-{
- global_dh = ssl_sock_get_dh_from_file(filename);
-
- if (global_dh) {
- return 0;
- }
-
- return -1;
+ return ret;
}
/* Loads Diffie-Hellman parameter from a file. Returns 1 if loaded, else -1
if an error occured, and 0 if parameter not found. */
-int ssl_sock_load_dh_params(SSL_CTX *ctx, const char *file)
+int ssl_sock_load_dh_params(SSL_CTX *ctx, BIO *in)
{
int ret = -1;
- DH *dh = ssl_sock_get_dh_from_file(file);
+ DH *dh = ssl_sock_get_dh(in);
if (dh) {
ret = 1;
@@ -3192,10 +3188,9 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
/* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
* an early error happens and the caller must call SSL_CTX_free() by itelf.
*/
-static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s,
- struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount)
+static int ssl_sock_load_cert_chain(SSL_CTX *ctx, BIO *in, struct bind_conf *s,
+ struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount)
{
- BIO *in;
X509 *x = NULL, *ca;
int i, err;
int ret = -1;
@@ -3211,14 +3206,6 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct
STACK_OF(GENERAL_NAME) *names;
#endif
- in = BIO_new(BIO_s_file());
- if (in == NULL)
- goto end;
-
- if (BIO_read_filename(in, file) <= 0)
- goto end;
-
-
passwd_cb = SSL_CTX_get_default_passwd_cb(ctx);
passwd_cb_userdata = SSL_CTX_get_default_passwd_cb_userdata(ctx);
@@ -3308,44 +3295,110 @@ end:
if (x)
X509_free(x);
- if (in)
- BIO_free(in);
-
return ret;
}
-static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
- char **sni_filter, int fcount, char **err)
+struct ssl_input {
+ const char *cert_path;
+ struct chunk *cert_buf;
+};
+
+static int ssl_sock_load_cert2(struct ssl_input *si, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+ char **sni_filter, int fcount, char **err)
{
int ret;
SSL_CTX *ctx;
+ BIO *in;
+
+ if (!si->cert_path && (!si->cert_buf || !si->cert_buf->len))
+ return 1;
ctx = SSL_CTX_new(SSLv23_server_method());
if (!ctx) {
- memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
- err && *err ? *err : "", path);
+ if (si->cert_path)
+ memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+ err && *err ? *err : "", si->cert_path);
+ else
+ memprintf(err, "%sunable to allocate SSL context.\n",
+ err && *err ? *err : "");
return 1;
}
- if (SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
- memprintf(err, "%sunable to load SSL private key from PEM file '%s'.\n",
- err && *err ? *err : "", path);
- SSL_CTX_free(ctx);
- return 1;
+ if (si->cert_path) {
+ if (SSL_CTX_use_PrivateKey_file(ctx, si->cert_path, SSL_FILETYPE_PEM) <= 0) {
+ memprintf(err, "%sunable to load SSL private key from PEM file '%s'.\n",
+ err && *err ? *err : "", si->cert_path);
+ SSL_CTX_free(ctx);
+ return 1;
+ }
+
+ in = BIO_new(BIO_s_file());
+ if (!in) {
+ SSL_CTX_free(ctx);
+ return 1;
+ }
+
+ if (BIO_read_filename(in, si->cert_path) <= 0) {
+ SSL_CTX_free(ctx);
+ BIO_free(in);
+ return 1;
+ }
}
+ else {
+ EVP_PKEY *pkey;
+
+ in = BIO_new_mem_buf(si->cert_buf->str, si->cert_buf->len);
+ if (!in) {
+ memprintf(err, "%sunable to create a memory buffer.\n",
+ err && *err ? *err : "");
+ SSL_CTX_free(ctx);
+ return 1;
+ }
- ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, ssl_conf, sni_filter, fcount);
+ pkey = PEM_read_bio_PrivateKey(in, NULL,
+ SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ if (!pkey) {
+ memprintf(err, "%sunable to read the private key.\n",
+ err && *err ? *err : "");
+ BIO_free(in);
+ SSL_CTX_free(ctx);
+ return 1;
+ }
+ ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+ EVP_PKEY_free(pkey);
+ if (ret <= 0) {
+ memprintf(err, "%sunable to use the private key.\n",
+ err && *err ? *err : "");
+ BIO_free(in);
+ SSL_CTX_free(ctx);
+ return 1;
+ }
+ }
+
+ BIO_reset(in);
+ ret = ssl_sock_load_cert_chain(ctx, in, bind_conf, ssl_conf, sni_filter, fcount);
if (ret <= 0) {
- memprintf(err, "%sunable to load SSL certificate from PEM file '%s'.\n",
- err && *err ? *err : "", path);
+ if (si->cert_path)
+ memprintf(err, "%sunable to load SSL certificate from PEM file '%s'.\n",
+ err && *err ? *err : "", si->cert_path);
+ else
+ memprintf(err, "%sunable to load SSL certificate.\n",
+ err && *err ? *err : "");
+ BIO_free(in);
if (ret < 0) /* serious error, must do that ourselves */
SSL_CTX_free(ctx);
return 1;
}
if (SSL_CTX_check_private_key(ctx) <= 0) {
- memprintf(err, "%sinconsistencies between private key and certificate loaded from PEM file '%s'.\n",
- err && *err ? *err : "", path);
+ if (si->cert_path)
+ memprintf(err, "%sinconsistencies between private key and certificate loaded from PEM file '%s'.\n",
+ err && *err ? *err : "", si->cert_path);
+ else
+ memprintf(err, "%sinconsistencies between private key and certificate.\n",
+ err && *err ? *err : "");
+ BIO_free(in);
return 1;
}
@@ -3359,38 +3412,47 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
SSL_CTX_set_ex_data(ctx, ssl_dh_ptr_index, NULL);
}
- ret = ssl_sock_load_dh_params(ctx, path);
+ BIO_reset(in);
+ ret = ssl_sock_load_dh_params(ctx, in);
+ BIO_free(in);
if (ret < 0) {
- if (err)
- memprintf(err, "%sunable to load DH parameters from file '%s'.\n",
- *err ? *err : "", path);
+ if (err) {
+ if (si->cert_path)
+ memprintf(err, "%sunable to load DH parameters from file '%s'.\n",
+ *err ? *err : "", si->cert_path);
+ else
+ memprintf(err, "%sunable to load DH parameters.\n",
+ *err ? *err : "");
+ }
return 1;
}
#endif
+ if (si->cert_path) {
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
- ret = ssl_sock_load_ocsp(ctx, path);
- if (ret < 0) {
- if (err)
- memprintf(err, "%s '%s.ocsp' is present and activates OCSP but it is impossible to compute the OCSP certificate ID (maybe the issuer could not be found)'.\n",
- *err ? *err : "", path);
- return 1;
- }
+ ret = ssl_sock_load_ocsp(ctx, si->cert_path);
+ if (ret < 0) {
+ if (err)
+ memprintf(err, "%s '%s.ocsp' is present and activates OCSP but it is impossible to compute the OCSP certificate ID (maybe the issuer could not be found)'.\n",
+ *err ? *err : "", si->cert_path);
+ return 1;
+ }
#elif (defined OPENSSL_IS_BORINGSSL)
- ssl_sock_set_ocsp_response_from_file(ctx, path);
+ ssl_sock_set_ocsp_response_from_file(ctx, si->cert_path);
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
- if (sctl_ex_index >= 0) {
- ret = ssl_sock_load_sctl(ctx, path);
- if (ret < 0) {
- if (err)
- memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
- *err ? *err : "", path);
- return 1;
+ if (sctl_ex_index >= 0) {
+ ret = ssl_sock_load_sctl(ctx, si->cert_path);
+ if (ret < 0) {
+ if (err)
+ memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
+ *err ? *err : "", si->cert_path);
+ return 1;
+ }
}
- }
#endif
+ }
#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
if (bind_conf->default_ctx) {
@@ -3424,7 +3486,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
if (stat(path, &buf) == 0) {
dir = opendir(path);
if (!dir)
- return ssl_sock_load_cert_file(path, bind_conf, NULL, NULL, 0, err);
+ return ssl_sock_load_cert2(&(struct ssl_input){ .cert_path = path }, bind_conf, NULL, NULL, 0, err);
/* strip trailing slashes, including first one */
for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
@@ -3492,7 +3554,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
}
#endif
- cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, NULL, 0, err);
+ cfgerr += ssl_sock_load_cert2(&(struct ssl_input){ .cert_path = fp }, bind_conf, NULL, NULL, 0, err);
ignore_entry:
free(de);
}
@@ -3683,8 +3745,8 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
}
if (stat(crt_path, &buf) == 0) {
- cfgerr = ssl_sock_load_cert_file(crt_path, bind_conf, ssl_conf,
- &args[cur_arg], arg - cur_arg - 1, err);
+ cfgerr = ssl_sock_load_cert2(&(struct ssl_input){ .cert_path = crt_path }, bind_conf, ssl_conf,
+ &args[cur_arg], arg - cur_arg - 1, err);
} else {
cfgerr = ssl_sock_load_multi_cert(crt_path, bind_conf, ssl_conf,
&args[cur_arg], arg - cur_arg - 1, err);
--
2.11.0
>From f432620b386e0658ed19a5e6caa311767100a0b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Nephtali?= <[email protected]>
Date: Wed, 18 Apr 2018 18:20:12 +0200
Subject: [PATCH 2/2] MINOR/WIP: ssl: Add "add ssl cert" and "del ssl cert"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Aurélien Nephtali <[email protected]>
---
doc/management.txt | 14 +++
include/common/hathreads.h | 2 +
include/types/listener.h | 1 +
src/ssl_sock.c | 221 +++++++++++++++++++++++++++++++++++++++------
4 files changed, 209 insertions(+), 29 deletions(-)
diff --git a/doc/management.txt b/doc/management.txt
index 4b6901851..023237ac3 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1703,6 +1703,20 @@ set ssl tls-key <id> <tlskey>
#<id> or <file> returned by "show tls-keys". <tlskey> is a base64 encoded 48
bit TLS ticket key (ex. openssl rand -base64 48).
+add ssl cert <frontend[:luid]> <PEM certificate>
+ Add a new SSL certificate to a frontend.
+
+ Example:
+ echo "add ssl cert fe:1 $(cat cert.pem | tr '\n' ' ') | socat /tmp/sock1 -
+
+del ssl cert <frontend[:luid]> <domain>
+ Delete an SSL certificate from a frontend.
+
+ Example:
+ echo "del ssl cert fe:1 example.com" | socat /tmp/sock1 -
+
+ The default certificate cannot be deleted.
+
set table <table> key <key> [data.<data_type> <value>]*
Create or update a stick-table entry in the table. If the key is not present,
an entry is inserted. See stick-table in section 4.2 to find all possible
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index 0f10b48ca..b54778e30 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -295,6 +295,7 @@ enum lock_label {
START_LOCK,
TLSKEYS_REF_LOCK,
PENDCONN_LOCK,
+ SSL_SNI_LOCK,
LOCK_LABELS
};
struct lock_stat {
@@ -413,6 +414,7 @@ static inline const char *lock_label(enum lock_label label)
case START_LOCK: return "START";
case TLSKEYS_REF_LOCK: return "TLSKEYS_REF";
case PENDCONN_LOCK: return "PENDCONN";
+ case SSL_SNI_LOCK: return "SSL_SNI";
case LOCK_LABELS: break; /* keep compiler happy */
};
/* only way to come here is consecutive to an internal bug */
diff --git a/include/types/listener.h b/include/types/listener.h
index c55569cd3..05e1d94d3 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -147,6 +147,7 @@ struct bind_conf {
int ssl_options; /* ssl options */
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
+ __decl_hathreads(HA_RWLOCK_T sni_lock); /* used to protect sni_ctx and sni_w_ctx */
struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
char *ca_sign_file; /* CAFile used to generate and sign server certificates */
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 7543c7a69..259755892 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -2227,6 +2227,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
trash.str[i] = 0;
/* lookup in full qualified names */
+ HA_RWLOCK_RDLOCK(SSL_SNI_LOCK, &s->sni_lock);
node = ebst_lookup(&s->sni_ctx, trash.str);
/* lookup a not neg filter */
@@ -2294,8 +2295,10 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
if (conf->early_data)
allow_early = 1;
}
+ HA_RWLOCK_RDUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
goto allow_early;
}
+ HA_RWLOCK_RDUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
#if (!defined SSL_NO_GENERATE_CERTIFICATES)
if (s->generate_certs && ssl_sock_generate_certificate(trash.str, s, ssl)) {
/* switch ctx done in ssl_sock_generate_certificate */
@@ -2363,6 +2366,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv)
trash.str[i] = 0;
/* lookup in full qualified names */
+ HA_RWLOCK_RDLOCK(SSL_SNI_LOCK, &s->sni_lock);
node = ebst_lookup(&s->sni_ctx, trash.str);
/* lookup a not neg filter */
@@ -2377,6 +2381,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv)
node = ebst_lookup(&s->sni_w_ctx, wildp);
}
if (!node || container_of(node, struct sni_ctx, name)->neg) {
+ HA_RWLOCK_RDUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
#if (!defined SSL_NO_GENERATE_CERTIFICATES)
if (s->generate_certs && ssl_sock_generate_certificate(servername, s, ssl)) {
/* switch ctx done in ssl_sock_generate_certificate */
@@ -2391,6 +2396,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv)
/* switch ctx */
ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx);
+ HA_RWLOCK_RDUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
return SSL_TLSEXT_ERR_OK;
}
#endif /* (!) OPENSSL_IS_BORINGSSL */
@@ -2688,19 +2694,24 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, struct ssl_b
trash.str[j] = 0;
/* Check for duplicates. */
+ HA_RWLOCK_WRLOCK(SSL_SNI_LOCK, &s->sni_lock);
if (wild)
node = ebst_lookup(&s->sni_w_ctx, trash.str);
else
node = ebst_lookup(&s->sni_ctx, trash.str);
for (; node; node = ebmb_next_dup(node)) {
sc = ebmb_entry(node, struct sni_ctx, name);
- if (sc->ctx == ctx && sc->conf == conf && sc->neg == neg)
+ if (sc->ctx == ctx && sc->conf == conf && sc->neg == neg) {
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
return order;
+ }
}
sc = malloc(sizeof(struct sni_ctx) + len + 1);
- if (!sc)
+ if (!sc) {
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
return order;
+ }
memcpy(sc->name.key, trash.str, len + 1);
sc->ctx = ctx;
sc->conf = conf;
@@ -2713,6 +2724,7 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, struct ssl_b
ebst_insert(&s->sni_w_ctx, &sc->name);
else
ebst_insert(&s->sni_ctx, &sc->name);
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &s->sni_lock);
}
return order;
}
@@ -3230,6 +3242,35 @@ static int ssl_sock_load_cert_chain(SSL_CTX *ctx, BIO *in, struct bind_conf *s,
EVP_PKEY_free(pkey);
}
+ if (!SSL_CTX_use_certificate(ctx, x))
+ goto end;
+
+#ifdef SSL_CTX_clear_extra_chain_certs
+ SSL_CTX_clear_extra_chain_certs(ctx);
+#else
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+#endif
+
+ while ((ca = PEM_read_bio_X509(in, NULL, passwd_cb, passwd_cb_userdata))) {
+ if (!SSL_CTX_add_extra_chain_cert(ctx, ca)) {
+ X509_free(ca);
+ goto end;
+ }
+ }
+
+ err = ERR_get_error();
+ if (!err || (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ /* we successfully reached the last cert in the file */
+ ret = 1;
+ }
+ ERR_clear_error();
+
+ if (!ret)
+ goto end;
+
if (fcount) {
while (fcount--)
order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, kinfo, sni_filter[fcount], order);
@@ -3264,33 +3305,6 @@ static int ssl_sock_load_cert_chain(SSL_CTX *ctx, BIO *in, struct bind_conf *s,
}
}
- ret = 0; /* the caller must not free the SSL_CTX argument anymore */
- if (!SSL_CTX_use_certificate(ctx, x))
- goto end;
-
-#ifdef SSL_CTX_clear_extra_chain_certs
- SSL_CTX_clear_extra_chain_certs(ctx);
-#else
- if (ctx->extra_certs != NULL) {
- sk_X509_pop_free(ctx->extra_certs, X509_free);
- ctx->extra_certs = NULL;
- }
-#endif
-
- while ((ca = PEM_read_bio_X509(in, NULL, passwd_cb, passwd_cb_userdata))) {
- if (!SSL_CTX_add_extra_chain_cert(ctx, ca)) {
- X509_free(ca);
- goto end;
- }
- }
-
- err = ERR_get_error();
- if (!err || (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
- /* we successfully reached the last cert in the file */
- ret = 1;
- }
- ERR_clear_error();
-
end:
if (x)
X509_free(x);
@@ -4749,6 +4763,7 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf)
if (bind_conf->default_ctx)
err += ssl_sock_prepare_ctx(bind_conf, bind_conf->default_ssl_conf, bind_conf->default_ctx);
+ HA_RWLOCK_RDLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
node = ebmb_first(&bind_conf->sni_ctx);
while (node) {
sni = ebmb_entry(node, struct sni_ctx, name);
@@ -4768,6 +4783,7 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf)
err += ssl_sock_prepare_ctx(bind_conf, sni->conf, sni->ctx);
node = ebmb_next(node);
}
+ HA_RWLOCK_RDUNLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
return err;
}
@@ -4824,6 +4840,8 @@ int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf)
/* initialize CA variables if the certificates generation is enabled */
err += ssl_sock_load_ca(bind_conf);
+ HA_RWLOCK_INIT(&bind_conf->sni_lock);
+
return -err;
}
@@ -8670,12 +8688,157 @@ static int cli_parse_set_ocspresponse(char **args, struct appctx *appctx, void *
}
+static int cli_parse_add_ssl_cert(char **args, struct appctx *appctx, void *private)
+{
+ char *p;
+ int luid = -1;
+ struct proxy *px;
+ struct bind_conf *bind_conf;
+ int i, banner;
+ const char *sep;
+ struct chunk *c;
+
+ if (!*args[4]) {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "'add ssl cert' expects a frontend and a certificate.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ return 1;
+ }
+ p = strrchr(args[3], ':');
+ if (p) {
+ *p = 0;
+ luid = atoi(p + 1);
+ }
+ px = cli_find_frontend(appctx, args[3]);
+ if (!px) {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "Couldn't find frontend.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ return 1;
+ }
+
+ c = get_trash_chunk();
+
+ for (i = 4, banner = 0; i < MAX_STATS_ARGS && *args[i]; i++) {
+ if (*args[i] == '-')
+ banner = 1;
+
+ if (banner && args[i][strlen(args[i]) - 1] == '-')
+ banner = 0;
+
+ if (banner)
+ sep = " ";
+ else
+ sep = "\n";
+
+ chunk_appendf(c, "%s%s", args[i], sep);
+ }
+
+ list_for_each_entry(bind_conf, &px->conf.bind, by_fe) {
+ char *err = NULL;
+ struct listener *l;
+
+ if (!bind_conf->is_ssl)
+ continue;
+
+ list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+ if (luid > 0 && l->luid != luid)
+ continue;
+
+ if (ssl_sock_load_cert2(&(struct ssl_input){ .cert_buf = c }, bind_conf, NULL, NULL, 0, &err)) {
+ if (err) {
+ appctx->ctx.cli.err = err;
+ appctx->st0 = CLI_ST_PRINT_FREE;
+ }
+ else {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "An error occured while loading the certificate.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int cli_parse_del_ssl_cert(char **args, struct appctx *appctx, void *private)
+{
+ char *p;
+ int luid = -1;
+ struct proxy *px;
+ struct bind_conf *bind_conf;
+
+ if (!*args[4]) {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "'del ssl cert' expects a frontend and a domain.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ return 1;
+ }
+ p = strrchr(args[3], ':');
+ if (p) {
+ *p = 0;
+ luid = atoi(p + 1);
+ }
+ px = cli_find_frontend(appctx, args[3]);
+ if (!px) {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "Couldn't find frontend.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ return 1;
+ }
+ list_for_each_entry(bind_conf, &px->conf.bind, by_fe) {
+ struct listener *l;
+ struct ebmb_node *node;
+ struct sni_ctx *sni;
+
+ if (!bind_conf->is_ssl)
+ continue;
+
+ list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+ if (luid > 0 && l->luid != luid)
+ continue;
+
+ HA_RWLOCK_WRLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
+ node = ebst_lookup(&bind_conf->sni_ctx, args[4]);
+ if (!node) {
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
+ continue;
+ }
+ sni = ebmb_entry(node, struct sni_ctx, name);
+ if (sni->ctx == bind_conf->default_ctx) {
+ appctx->ctx.cli.severity = LOG_ERR;
+ appctx->ctx.cli.msg = "Default certificate can't be deleted.\n";
+ appctx->st0 = CLI_ST_PRINT;
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
+ break;
+ }
+ ebmb_delete(node);
+ if (!sni->order) {
+ SSL_CTX_free(sni->ctx);
+ ssl_sock_free_ssl_conf(sni->conf);
+ free(sni->conf);
+ }
+ free(sni);
+ HA_RWLOCK_WRUNLOCK(SSL_SNI_LOCK, &bind_conf->sni_lock);
+
+ break;
+ }
+ }
+
+ return 1;
+}
+
/* register cli keywords */
static struct cli_kw_list cli_kws = {{ },{
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
{ { "show", "tls-keys", NULL }, "show tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specified", cli_parse_show_tlskeys, NULL },
{ { "set", "ssl", "tls-key", NULL }, "set ssl tls-key [id|keyfile] <tlskey>: set the next TLS key for the <id> or <keyfile> listener to <tlskey>", cli_parse_set_tlskeys, NULL },
#endif
+ { { "add", "ssl", "cert", NULL }, "add ssl cert <frontend[:luid]> <PEM certificate>: add an SSL certificate to a frontend", cli_parse_add_ssl_cert, NULL },
+ { { "del", "ssl", "cert", NULL }, "del ssl cert <frontend[:luid]> <domain>: delete an SSL certificate from a frontend", cli_parse_del_ssl_cert, NULL },
{ { "set", "ssl", "ocsp-response", NULL }, NULL, cli_parse_set_ocspresponse, NULL },
{ { NULL }, NULL, NULL, NULL }
}};
--
2.11.0