From 38df1c8006a2adf97f4ad5a183f80cfdcba3da8a Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Wed, 4 Dec 2019 15:39:35 +0100 Subject: [PATCH] MINOR: ssl/cli: support crt-list filters Generate a list of the previous filters when updating a certificate which use filters in crt-list. Then pass this list to the function generating the sni_ctx during the commit. This feature allows the update of the crt-list certificates which uses the filters with "set ssl cert". This function could be probably replaced by creating a new ckch_inst_new_load_store() function which take the previous sni_ctx list as an argument instead of the char **sni_filter, avoiding the allocation/copy during runtime for each filter. But since are still handling the multi-cert bundles, it's better this way to avoid code duplication. --- include/types/ssl_sock.h | 1 - src/ssl_sock.c | 108 ++++++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h index feca10004..bb64e4972 100644 --- a/include/types/ssl_sock.h +++ b/include/types/ssl_sock.h @@ -111,7 +111,6 @@ struct cert_key_and_chain { struct ckch_store { struct cert_key_and_chain *ckch; unsigned int multi:1; /* is it a multi-cert bundle ? */ - unsigned int filters:1;/* one of the instances is using filters, TODO:remove this flag once filters are supported */ struct list ckch_inst; /* list of ckch_inst which uses this ckch_node */ struct ebmb_node node; char path[0]; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 2688117e5..86894cb44 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -3895,6 +3895,84 @@ end: return NULL; } + +/* + * Free a sni filters array generated by ckch_inst_sni_ctx_to_sni_filters() + */ +static inline void free_sni_filters(char **sni_filter, int fcount) +{ + int i; + + if (sni_filter) { + for (i = 0; i < fcount; i++) { + if (sni_filter[i]) { + free(sni_filter[i]); + sni_filter[i] = NULL; + } + } + free(sni_filter); + } +} + +/* + * Fill <*sni_filter> with an allocated array of ptr to the existing filters, + * The caller should free <*sni_filter>. + * Fill <*fcount> with the number of filters + * Return an ERR_* code. + */ +static int ckch_inst_sni_ctx_to_sni_filters(const struct ckch_inst *ckchi, char ***sni_filter, int *fcount, char **err) +{ + struct sni_ctx *sc0; + int errcode = 0; + int i = 0; + char **tmp_filter; + int tmp_fcount = 0; + + list_for_each_entry(sc0, &ckchi->sni_ctx, by_ckch_inst) { + tmp_fcount++; + } + + if (!tmp_fcount) + goto end; + + tmp_filter = malloc(sizeof(*tmp_filter) * tmp_fcount); + if (!tmp_filter) { + errcode |= ERR_FATAL|ERR_ALERT; + goto error; + } + + list_for_each_entry(sc0, &ckchi->sni_ctx, by_ckch_inst) { + size_t len = strlen((char *)sc0->name.key); + + /* we need to alloc and copy to insert a '!' or/and a '*' */ + tmp_filter[i] = calloc(1, len + sc0->neg + sc0->wild + 1); + if (!tmp_filter[i]) { + errcode |= ERR_FATAL|ERR_ALERT; + goto error; + } + + if (sc0->neg) + *tmp_filter[i] = '!'; + if (sc0->wild) + *(tmp_filter[i] + sc0->neg) = '*'; + + memcpy(tmp_filter[i] + sc0->neg + sc0->wild, (char *)sc0->name.key, len + 1); + i++; + } +end: + *sni_filter = tmp_filter; + *fcount = tmp_fcount; + + return errcode; +error: + memprintf(err, "%sUnable to generate filters!", + err && *err ? *err : ""); + free_sni_filters(tmp_filter, tmp_fcount); + + return errcode; +} + + #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL /* @@ -3949,10 +4027,6 @@ static int ckch_inst_new_load_multi_store(const char *path, struct ckch_store *c certs_and_keys = ckchs->ckch; - /* at least one of the instances is using filters during the config - * parsing, that's ok to inherit this during loading on CLI */ - ckchs->filters |= !!fcount; - /* Process each ckch and update keytypes for each CN/SAN * for example, if CN/SAN www.a.com is associated with * certs with keytype 0 and 2, then at the end of the loop, @@ -4193,10 +4267,6 @@ static int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, ckch = ckchs->ckch; - /* at least one of the instances is using filters during the config - * parsing, that's ok to inherit this during loading on CLI */ - ckchs->filters |= !!fcount; - ctx = SSL_CTX_new(SSLv23_server_method()); if (!ctx) { memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n", @@ -10924,6 +10994,8 @@ static int cli_io_handler_commit_cert(struct appctx *appctx) /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */ list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) { struct ckch_inst *new_inst; + char **sni_filter = NULL; + int fcount = 0; /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */ if (y >= 10) { @@ -10932,10 +11004,17 @@ static int cli_io_handler_commit_cert(struct appctx *appctx) goto yield; } + errcode |= ckch_inst_sni_ctx_to_sni_filters(ckchi, &sni_filter, &fcount, &err); + if (errcode & ERR_CODE) + goto error; + if (new_ckchs->multi) - errcode |= ckch_inst_new_load_multi_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, NULL, 0, &new_inst, &err); + errcode |= ckch_inst_new_load_multi_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err); else - errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, NULL, 0, &new_inst, &err); + errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err); + + free_sni_filters(sni_filter, fcount); + sni_filter = NULL; if (errcode & ERR_CODE) goto error; @@ -11103,7 +11182,6 @@ error: return cli_dynerr(appctx, err); } - /* * Parsing function of `set ssl cert`, it updates or creates a temporary ckch. */ @@ -11266,14 +11344,6 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, old_ckchs = appctx->ctx.ssl.old_ckchs; - /* TODO: handle filters */ - if (old_ckchs->filters) { - memprintf(&err, "%sCertificates used in crt-list with filters are not supported!\n", - err ? err : ""); - errcode |= ERR_ALERT | ERR_FATAL; - goto end; - } - /* duplicate the ckch store */ new_ckchs = ckchs_dup(old_ckchs); if (!new_ckchs) {