MEDIUM: ssl: Chain ckch instances in ca-file entries
Each ca-file entry of the tree will now hold a list of the ckch
instances that use it so that we can iterate over them when updating the
ca-file via a cli command. Since the link between the SSL contexts and
the CA file tree entries is only built during the ssl_sock_prepare_ctx
function, which are called after all the ckch instances are created, we
need to add a little post processing after each ssl_sock_prepare_ctx
that builds the link between the corresponding ckch instance and CA file
tree entries.
In order to manage the ca-file and ca-verify-file options, any ckch
instance can be linked to multiple CA file tree entries and any CA file
entry can link multiple ckch instances. This is done thanks to a
dedicated list of ckch_inst references stored in the CA file tree
entries over which we can iterate (during an update for instance). We
avoid having one of those instances go stale by keeping a list of
references to those references in the instances.
When deleting a ckch_inst, we can then remove all the ckch_inst_link
instances that reference it, and when deleting a cafile_entry, we
iterate over the list of ckch_inst reference and clear the corresponding
entry in their own list of ckch_inst_link references.
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c
index c8f0f2f..f2344ec 100644
--- a/src/ssl_ckch.c
+++ b/src/ssl_ckch.c
@@ -888,6 +888,7 @@
void ckch_inst_free(struct ckch_inst *inst)
{
struct sni_ctx *sni, *sni_s;
+ struct ckch_inst_link_ref *link_ref, *link_ref_s;
if (inst == NULL)
return;
@@ -902,6 +903,13 @@
inst->ctx = NULL;
LIST_DELETE(&inst->by_ckchs);
LIST_DELETE(&inst->by_crtlist_entry);
+
+ list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
+ LIST_DELETE(&link_ref->link->list);
+ LIST_DELETE(&link_ref->list);
+ free(link_ref);
+ }
+
free(inst);
}
@@ -917,6 +925,7 @@
LIST_INIT(&ckch_inst->sni_ctx);
LIST_INIT(&ckch_inst->by_ckchs);
LIST_INIT(&ckch_inst->by_crtlist_entry);
+ LIST_INIT(&ckch_inst->cafile_link_refs);
return ckch_inst;
}
@@ -932,7 +941,7 @@
* given path, the original one and the new one set via the CLI but not
* committed yet).
*/
-static struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry)
+struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry)
{
struct cafile_entry *ca_e = NULL;
struct ebmb_node *eb;
@@ -979,6 +988,7 @@
if (ca_e) {
memcpy(ca_e->path, path, pathlen + 1);
ca_e->ca_store = store;
+ LIST_INIT(&ca_e->ckch_inst_link);
ebst_insert(&cafile_tree, &ca_e->node);
}
} else {
@@ -1108,6 +1118,120 @@
}
#endif
+/*
+ * Build the ckch_inst_link that will be chained in the CA file entry and the
+ * corresponding ckch_inst_link_ref that will be chained in the ckch instance.
+ * Return 0 in case of success.
+ */
+static int do_chain_inst_and_cafile(struct cafile_entry *cafile_entry, struct ckch_inst *ckch_inst)
+{
+ struct ckch_inst_link *new_link;
+ if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
+ struct ckch_inst_link *link = LIST_ELEM(cafile_entry->ckch_inst_link.n,
+ typeof(link), list);
+ /* Do not add multiple references to the same
+ * instance in a cafile_entry */
+ if (link->ckch_inst == ckch_inst) {
+ return 1;
+ }
+ }
+
+ new_link = calloc(1, sizeof(*new_link));
+ if (new_link) {
+ struct ckch_inst_link_ref *new_link_ref = calloc(1, sizeof(*new_link_ref));
+ if (!new_link_ref) {
+ free(new_link);
+ return 1;
+ }
+
+ new_link->ckch_inst = ckch_inst;
+ new_link_ref->link = new_link;
+ LIST_INIT(&new_link->list);
+ LIST_INIT(&new_link_ref->list);
+
+ LIST_APPEND(&cafile_entry->ckch_inst_link, &new_link->list);
+ LIST_APPEND(&ckch_inst->cafile_link_refs, &new_link_ref->list);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Link a CA file tree entry to the ckch instance that uses it.
+ * To determine if and which CA file tree entries need to be linked to the
+ * instance, we follow the same logic performed in ssl_sock_prepare_ctx when
+ * processing the verify option.
+ * This function works for a frontend as well as for a backend, depending on the
+ * configuration parameters given (bind_conf or server).
+ */
+void ckch_inst_add_cafile_link(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf,
+ struct ssl_bind_conf *ssl_conf, const struct server *srv)
+{
+ int verify = SSL_VERIFY_NONE;
+
+ if (srv) {
+
+ if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
+ verify = SSL_VERIFY_PEER;
+ switch (srv->ssl_ctx.verify) {
+ case SSL_SOCK_VERIFY_NONE:
+ verify = SSL_VERIFY_NONE;
+ break;
+ case SSL_SOCK_VERIFY_REQUIRED:
+ verify = SSL_VERIFY_PEER;
+ break;
+ }
+ }
+ else {
+ switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
+ case SSL_SOCK_VERIFY_NONE:
+ verify = SSL_VERIFY_NONE;
+ break;
+ case SSL_SOCK_VERIFY_OPTIONAL:
+ verify = SSL_VERIFY_PEER;
+ break;
+ case SSL_SOCK_VERIFY_REQUIRED:
+ verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ break;
+ }
+ }
+
+ if (verify & SSL_VERIFY_PEER) {
+ struct cafile_entry *ca_file_entry = NULL;
+ struct cafile_entry *ca_verify_file_entry = NULL;
+ if (srv) {
+ if (srv->ssl_ctx.ca_file) {
+ ca_file_entry = ssl_store_get_cafile_entry(srv->ssl_ctx.ca_file, 0);
+
+ }
+ }
+ else {
+ char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
+ char *ca_verify_file = (ssl_conf && ssl_conf->ca_verify_file) ? ssl_conf->ca_verify_file : bind_conf->ssl_conf.ca_verify_file;
+
+ if (ca_file)
+ ca_file_entry = ssl_store_get_cafile_entry(ca_file, 0);
+ if (ca_verify_file)
+ ca_verify_file_entry = ssl_store_get_cafile_entry(ca_verify_file, 0);
+ }
+
+ if (ca_file_entry) {
+ /* If we have a ckch instance that is not already in the
+ * cafile_entry's list, add it to it. */
+ if (do_chain_inst_and_cafile(ca_file_entry, ckch_inst))
+ return;
+
+ }
+ if (ca_verify_file_entry && (ca_file_entry != ca_verify_file_entry)) {
+ /* If we have a ckch instance that is not already in the
+ * cafile_entry's list, add it to it. */
+ if (do_chain_inst_and_cafile(ca_verify_file_entry, ckch_inst))
+ return;
+ }
+ }
+}
+
@@ -1407,7 +1531,7 @@
new_inst->server = ckchi->server;
/* Create a new SSL_CTX and link it to the new instance. */
if (new_inst->is_server_instance) {
- retval = ssl_sock_prepare_srv_ssl_ctx(ckchi->server, new_inst->ctx);
+ retval = ssl_sock_prep_srv_ctx_and_inst(ckchi->server, new_inst->ctx, new_inst);
if (retval)
goto error;
}
@@ -1419,7 +1543,7 @@
/* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
- errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
+ errcode |= ssl_sock_prep_ctx_and_inst(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, sc0->ckch_inst, &err);
if (errcode & ERR_CODE)
goto error;
}