#include <sys/stat.h>
#include <sys/types.h>
+#include <import/ebsttree.h>
+
#include <haproxy/api.h>
#include <haproxy/base64.h>
#include <haproxy/cfgparse.h>
#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 ********************************************/
#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.
{ 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 },
}};