diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 478f0bcc3..731d61309 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -40,7 +42,9 @@ #include #include #include +#include #include +#include /****************** 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 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 }, }};