]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl/crtlist: "crt" keyword in frontend
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 11 Feb 2025 14:15:06 +0000 (15:15 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 17 Feb 2025 17:26:37 +0000 (18:26 +0100)
This patch implements the "crt" keywords in frontend, declaring an
implicit crt-list named after the frontend.

The patch is split in two steps:

The first step is the crt keyword parser, which parses crt lines and
fill a "cfg_crt_node" struct containing a ssl_bind_conf and a
ckch_conf which are put in a list to be used later.

After parsing the frontend section, as a 2nd step, a
post_section_parser is called, it will create a crt-list named after
the frontend and will fill it with certificates from the list of
cfg_crt_node. Once created this crt-list will be loaded in every "ssl"
bind lines that didn't declare any crt or crt-list.

Example:

    listen https
       bind :443 ssl
       crt foobar.pem
       crt test1.net.crt key test1.net.key

Implements part of #2854

src/cfgparse-ssl.c

index 478f0bcc373b0b3e05632a2ad097299eb35c4648..731d613091a961a5c65c829276a9a171d3c05084 100644 (file)
@@ -30,6 +30,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <import/ebsttree.h>
+
 #include <haproxy/api.h>
 #include <haproxy/base64.h>
 #include <haproxy/cfgparse.h>
@@ -40,7 +42,9 @@
 #include <haproxy/ssl_utils.h>
 #include <haproxy/tools.h>
 #include <haproxy/ssl_ckch.h>
+#include <haproxy/ssl_crtlist.h>
 #include <haproxy/ssl_ocsp.h>
+#include <haproxy/ssl_sock.h>
 
 
 /****************** Global Section Parsing ********************************************/
@@ -2153,7 +2157,205 @@ static int ssl_parse_skip_self_issued_ca(char **args, int section_type, struct p
 #endif
 }
 
+struct cfg_crt_node {
+       int linenum;
+       char *filename;
+       struct ssl_bind_conf *ssl_conf;
+       struct ckch_conf *ckch_conf;
+       struct list list;
+};
+
+/* list used for inline crt-list initialization */
+static struct list cur_crtlist = LIST_HEAD_INIT(cur_crtlist);
+/*
+ * Parse a "crt" line in a frontend.
+ */
+static int proxy_parse_crt(char **args, int section_type, struct proxy *curpx,
+                           const struct proxy *defpx, const char *file, int linenum,
+                           char **err)
+{
+       int cfgerr = 0;
+       struct ssl_bind_conf *ssl_conf = NULL;
+       struct ckch_conf *ckch_conf = NULL;
+       struct cfg_crt_node *cfg_crt_node = NULL;
+       int cur_arg = 2;
+       int i;
+
+       if (!*args[1]) {
+               memprintf(err, "parsing [%s:%d] : '%s' : expects a certificate name", file, linenum, args[0]);
+               goto error;
+       }
+
+       cfg_crt_node = calloc(1, sizeof *cfg_crt_node);
+       if (!cfg_crt_node) {
+               memprintf(err, "not enough memory!");
+               goto error;
+       }
+
+       ckch_conf = calloc(1, sizeof *ckch_conf);
+       if (!ckch_conf) {
+               memprintf(err, "not enough memory!");
+               goto error;
+       }
+
+       ckch_conf->crt = strdup(args[1]);
+       if (!ckch_conf->crt) {
+               memprintf(err, "not enough memory!");
+               goto error;
+       }
 
+       while (*args[cur_arg]) {
+               int found = 0;
+
+               /* first look for crt-list keywords */
+               for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
+                       if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
+
+                               if (!ssl_conf)
+                                       ssl_conf = calloc(1, sizeof *ssl_conf);
+                               if (!ssl_conf) {
+                                       memprintf(err, "not enough memory!");
+                                       goto error;
+                               }
+
+                               cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, 0, err);
+                               if (cfgerr & ERR_CODE)
+                                       goto error;
+                               cur_arg += 1 + ssl_crtlist_kws[i].skip;
+                               found = 1;
+                               goto next;
+                       }
+               }
+
+               /* then look for ckch_conf keywords */
+               cfgerr |= ckch_conf_parse(args, cur_arg, ckch_conf, &found, file, linenum, err);
+               if (cfgerr & ERR_CODE)
+                       goto error;
+               if (found) {
+                       cur_arg += 2;  /* skip 2 words if the keyword was found */
+                       ckch_conf->used = CKCH_CONF_SET_CRTLIST; /* if they are options they must be used everywhere */
+                       goto next;
+               }
+
+next:
+               if (!found) {
+                       memprintf(err, "unknown crt keyword '%s'", args[cur_arg]);
+                       goto error;
+               }
+       }
+
+       cfg_crt_node->ssl_conf = ssl_conf;
+       cfg_crt_node->ckch_conf = ckch_conf;
+       LIST_INSERT(&cur_crtlist, &cfg_crt_node->list);
+
+       return 0;
+error:
+       ckch_conf_clean(ckch_conf);
+       ha_free(&ckch_conf);
+       ssl_sock_free_ssl_conf(ssl_conf);
+       ha_free(&ssl_conf);
+       ha_free(&cfg_crt_node);
+       return -1;
+}
+
+/*
+ * After parsing the crt keywords in a frontend/listen section, create the corresponding crt-list and initialize the
+ * certificates
+ */
+
+static int post_section_frontend_crt_init()
+{
+       struct crtlist *newlist = NULL;
+       struct crtlist_entry *entry = NULL;
+       int err_code = 0;
+       struct cfg_crt_node *n, *r;
+       struct bind_conf *b;
+       char *crtlist_name = NULL;
+       char *err = NULL;
+
+       list_for_each_entry_safe(n, r, &cur_crtlist, list) {
+
+               /* create a new crt-list with the frontend name or a specified name */
+               if (!crtlist_name)
+                       memprintf(&crtlist_name, "@%s", curproxy->id);
+               if (!crtlist_name) {
+                       memprintf(&err, "Not enough memory!");
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto error;
+               }
+
+               if (!newlist)
+                       newlist = crtlist_new(crtlist_name, 0);
+               if (!newlist) {
+                       memprintf(&err, "Not enough memory!");
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto error;
+               }
+
+               entry = crtlist_entry_new();
+               if (entry == NULL) {
+                       memprintf(&err, "Not enough memory!");
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto error;
+               }
+
+               /* must set the ssl_conf in case of duplication of the crtlist_entry */
+               entry->ssl_conf = n->ssl_conf;
+
+               err_code |= crtlist_load_crt(n->ckch_conf->crt, n->ckch_conf, newlist, entry, n->filename, n->linenum, &err);
+               if (err_code & ERR_CODE)
+                       goto error;
+
+               LIST_DELETE(&n->list);
+               /* n->ssl_conf is reused so we don't free them here */
+               free(n->ckch_conf);
+               free(n);
+       }
+
+       if (newlist) {
+
+               if (ebst_insert(&crtlists_tree, &newlist->node) != &newlist->node) {
+                       memprintf(&err, "Couldn't create the crt-list '%s', this name is already used by another crt-list!", crtlist_name);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto error;
+               }
+
+               /* look for "ssl" bind lines without any crt nor crt-line */
+               list_for_each_entry(b, &curproxy->conf.bind, by_fe) {
+                       if (b->options & BC_O_USE_SSL) {
+                               if (eb_is_empty(&b->sni_ctx) && eb_is_empty(&b->sni_w_ctx)) {
+                                       err_code |= ssl_sock_load_cert_list_file(crtlist_name, 0, b, curproxy, &err);
+                                       if (err_code & ERR_CODE)
+                                               goto error;
+                               }
+                       }
+               }
+       }
+
+       return err_code;
+error:
+
+       if (err)
+               ha_alert("%s.\n", err);
+       free(err);
+
+       list_for_each_entry_safe(n, r, &cur_crtlist, list) {
+               ckch_conf_clean(n->ckch_conf);
+               ha_free(&n->ckch_conf);
+               ssl_sock_free_ssl_conf(n->ssl_conf);
+               ha_free(&n->ssl_conf);
+               LIST_DELETE(&n->list);
+               ha_free(&n);
+       }
+
+       ha_free(&crtlist_name);
+       crtlist_entry_free(entry);
+       crtlist_free(newlist);
+       return err_code;
+}
+
+REGISTER_CONFIG_POST_SECTION("listen",   post_section_frontend_crt_init);
+REGISTER_CONFIG_POST_SECTION("frontend", post_section_frontend_crt_init);
 
 
 /* Note: must not be declared <const> as its list will be overwritten.
@@ -2343,6 +2545,9 @@ static struct cfg_kw_list cfg_kws = {ILH, {
        { CFG_GLOBAL, "ssl-default-server-ciphersuites", ssl_parse_global_ciphersuites },
        { CFG_GLOBAL, "ssl-load-extra-files", ssl_parse_global_extra_files },
        { CFG_GLOBAL, "ssl-load-extra-del-ext", ssl_parse_global_extra_noext },
+
+       { CFG_LISTEN, "crt", proxy_parse_crt },
+
        { 0, NULL, NULL },
 }};