diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 05:11:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 05:11:11 +0000 |
commit | 669c3ea68099b330943d5c3215f0cf381880c3ad (patch) | |
tree | b5fbb97171c91fb994c2c3744dd33073f8f07407 /src/ssl_ckch.c | |
parent | Releasing progress-linux version 2.9.7-1~progress7.99u1. (diff) | |
download | haproxy-669c3ea68099b330943d5c3215f0cf381880c3ad.tar.xz haproxy-669c3ea68099b330943d5c3215f0cf381880c3ad.zip |
Merging upstream version 3.0.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/ssl_ckch.c')
-rw-r--r-- | src/ssl_ckch.c | 664 |
1 files changed, 648 insertions, 16 deletions
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index ebab1f3..b178078 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -28,6 +28,7 @@ #include <haproxy/applet.h> #include <haproxy/base64.h> +#include <haproxy/cfgparse.h> #include <haproxy/channel.h> #include <haproxy/cli.h> #include <haproxy/errors.h> @@ -111,6 +112,7 @@ struct commit_cacrlfile_ctx { enum { CACRL_ST_INIT = 0, CACRL_ST_GEN, + CACRL_ST_CRLCB, CACRL_ST_INSERT, CACRL_ST_SUCCESS, CACRL_ST_FIN, @@ -119,6 +121,18 @@ struct commit_cacrlfile_ctx { }; +/* + * Callback function, which is called if defined after loading CRLs from disk + * when starting HAProxy (function __ssl_store_load_locations_file()), and after + * committing new CRLs via CLI (function cli_io_handler_commit_cafile_crlfile()). + * + * The input parameters of the function are the path for the CRL data and + * a structure containing information about X.509 certificates and CRLs. + * In case of error, returns -1 with an error message in err; or the number + * of revoked certificates (>= 0) otherwise. + */ +int (*ssl_commit_crlfile_cb)(const char *path, X509_STORE *ctx, char **err) = NULL; + /******************** cert_key_and_chain functions ************************* * These are the functions that fills a cert_key_and_chain structure. For the * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c @@ -721,8 +735,27 @@ void ssl_sock_free_cert_key_and_chain_contents(struct ckch_data *data) X509_free(data->ocsp_issuer); data->ocsp_issuer = NULL; - OCSP_CERTID_free(data->ocsp_cid); - data->ocsp_cid = NULL; + + /* We need to properly remove the reference to the corresponding + * certificate_ocsp structure if it exists (which it should). + */ +#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL) + if (data->ocsp_cid) { + struct certificate_ocsp *ocsp = NULL; + unsigned char certid[OCSP_MAX_CERTID_ASN1_LENGTH] = {}; + unsigned int certid_length = 0; + + if (ssl_ocsp_build_response_key(data->ocsp_cid, (unsigned char*)certid, &certid_length) >= 0) { + HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock); + ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, certid, OCSP_MAX_CERTID_ASN1_LENGTH); + HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock); + ssl_sock_free_ocsp(ocsp); + } + + OCSP_CERTID_free(data->ocsp_cid); + data->ocsp_cid = NULL; + } +#endif } /* @@ -794,8 +827,6 @@ struct ckch_data *ssl_sock_copy_cert_key_and_chain(struct ckch_data *src, dst->ocsp_cid = OCSP_CERTID_dup(src->ocsp_cid); - dst->ocsp_update_mode = src->ocsp_update_mode; - return dst; error: @@ -877,6 +908,9 @@ void ckch_store_free(struct ckch_store *store) ssl_sock_free_cert_key_and_chain_contents(store->data); ha_free(&store->data); + /* free the ckch_conf content */ + ckch_conf_clean(&store->conf); + free(store); } @@ -928,6 +962,9 @@ struct ckch_store *ckchs_dup(const struct ckch_store *src) if (!ssl_sock_copy_cert_key_and_chain(src->data, dst->data)) goto error; + + dst->conf.ocsp_update_mode = src->conf.ocsp_update_mode; + return dst; error: @@ -953,7 +990,7 @@ struct ckch_store *ckchs_lookup(char *path) /* * This function allocate a ckch_store and populate it with certificates from files. */ -struct ckch_store *ckchs_load_cert_file(char *path, char **err) +struct ckch_store *ckch_store_new_load_files_path(char *path, char **err) { struct ckch_store *ckchs; @@ -966,6 +1003,8 @@ struct ckch_store *ckchs_load_cert_file(char *path, char **err) if (ssl_sock_load_files_into_ckch(path, ckchs->data, err) == 1) goto end; + ckchs->conf.used = CKCH_CONF_SET_EMPTY; + /* insert into the ckchs tree */ memcpy(ckchs->path, path, strlen(path) + 1); ebst_insert(&ckchs_tree, &ckchs->node); @@ -977,6 +1016,51 @@ end: return NULL; } +/* + * This function allocate a ckch_store and populate it with certificates using + * the ckch_conf structure. + */ +struct ckch_store *ckch_store_new_load_files_conf(char *name, struct ckch_conf *conf, char **err) +{ + struct ckch_store *ckchs; + int cfgerr = ERR_NONE; + char *tmpcrt = conf->crt; + + ckchs = ckch_store_new(name); + if (!ckchs) { + memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : ""); + goto end; + } + + /* this is done for retro-compatibility. When no "filename" crt-store + * options were configured in a crt-list, try to load the files by + * auto-detecting them. */ + if ((conf->used == CKCH_CONF_SET_EMPTY || conf->used == CKCH_CONF_SET_CRTLIST) && + (!conf->key && !conf->ocsp && !conf->issuer && !conf->sctl)) { + cfgerr = ssl_sock_load_files_into_ckch(conf->crt, ckchs->data, err); + if (cfgerr & ERR_FATAL) + goto end; + /* set conf->crt to NULL so it's not erased */ + conf->crt = NULL; + } + + /* load files using the ckch_conf */ + cfgerr = ckch_store_load_files(conf, ckchs, 0, err); + if (cfgerr & ERR_FATAL) + goto end; + + conf->crt = tmpcrt; + + /* insert into the ckchs tree */ + memcpy(ckchs->path, name, strlen(name) + 1); + ebst_insert(&ckchs_tree, &ckchs->node); + return ckchs; + +end: + ckch_store_free(ckchs); + + return NULL; +} /******************** ckch_inst functions ******************************/ @@ -1383,6 +1467,14 @@ scandir_err: goto err; } + if (ssl_commit_crlfile_cb != NULL) { + if (ssl_commit_crlfile_cb(path, store, NULL) == -1) { + if (!shuterror) + ha_alert("crl-file: couldn't load '%s'\n", path); + goto err; + } + } + objs = X509_STORE_get0_objects(store); cert_count = sk_X509_OBJECT_num(objs); if (cert_count == 0) { @@ -1978,7 +2070,7 @@ int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi, if (ckchi->is_server_instance) errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err); else - errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, new_inst, err); + errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, ckchi->is_default, new_inst, err); if (errcode & ERR_CODE) return 1; @@ -2115,16 +2207,11 @@ void ckch_store_replace(struct ckch_store *old_ckchs, struct ckch_store *new_ckc static int cli_io_handler_commit_cert(struct appctx *appctx) { struct commit_cert_ctx *ctx = appctx->svcctx; - struct stconn *sc = appctx_sc(appctx); int y = 0; struct ckch_store *old_ckchs, *new_ckchs = NULL; struct ckch_inst *ckchi; usermsgs_clr("CLI"); - /* FIXME: Don't watch the other side !*/ - if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE)) - goto end; - while (1) { switch (ctx->state) { case CERT_ST_INIT: @@ -2801,17 +2888,12 @@ error: static int cli_io_handler_commit_cafile_crlfile(struct appctx *appctx) { struct commit_cacrlfile_ctx *ctx = appctx->svcctx; - struct stconn *sc = appctx_sc(appctx); int y = 0; struct cafile_entry *old_cafile_entry = ctx->old_entry; struct cafile_entry *new_cafile_entry = ctx->new_entry; struct ckch_inst_link *ckchi_link; char *path; - /* FIXME: Don't watch the other side !*/ - if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE)) - goto end; - /* The ctx was already validated by the ca-file/crl-file parsing * function. Entries can only be NULL in CACRL_ST_SUCCESS or * CACRL_ST_FIN states @@ -2888,6 +2970,15 @@ static int cli_io_handler_commit_cafile_crlfile(struct appctx *appctx) y++; } + ctx->state = CACRL_ST_CRLCB; + __fallthrough; + case CACRL_ST_CRLCB: + if ((ctx->cafile_type == CAFILE_CRL) && (ssl_commit_crlfile_cb != NULL)) { + if (ssl_commit_crlfile_cb(crlfile_transaction.path, crlfile_transaction.new_crlfile_entry->ca_store, &ctx->err) == -1) { + ctx->state = CACRL_ST_ERROR; + goto error; + } + } ctx->state = CACRL_ST_INSERT; __fallthrough; case CACRL_ST_INSERT: @@ -3947,3 +4038,544 @@ static struct cli_kw_list cli_kws = {{ },{ INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); +static char *current_crtbase = NULL; +static char *current_keybase = NULL; +static int crtstore_load = 0; /* did we already load in this crt-store */ + +struct ckch_conf_kws ckch_conf_kws[] = { + { "alias", -1, PARSE_TYPE_NONE, NULL, NULL }, + { "crt", offsetof(struct ckch_conf, crt), PARSE_TYPE_STR, ckch_conf_load_pem, ¤t_crtbase }, + { "key", offsetof(struct ckch_conf, key), PARSE_TYPE_STR, ckch_conf_load_key, ¤t_keybase }, + { "ocsp", offsetof(struct ckch_conf, ocsp), PARSE_TYPE_STR, ckch_conf_load_ocsp_response, ¤t_crtbase }, + { "issuer", offsetof(struct ckch_conf, issuer), PARSE_TYPE_STR, ckch_conf_load_ocsp_issuer, ¤t_crtbase }, + { "sctl", offsetof(struct ckch_conf, sctl), PARSE_TYPE_STR, ckch_conf_load_sctl, ¤t_crtbase }, + { "ocsp-update", offsetof(struct ckch_conf, ocsp_update_mode), PARSE_TYPE_ONOFF, ocsp_update_init, NULL }, + { NULL, -1, PARSE_TYPE_STR, NULL, NULL } +}; + +/* crt-store does not try to find files, but use the stored filename */ +int ckch_store_load_files(struct ckch_conf *f, struct ckch_store *c, int cli, char **err) +{ + int i; + int err_code = 0; + int rc = 1; + struct ckch_data *d = c->data; + + for (i = 0; ckch_conf_kws[i].name; i++) { + void *src = NULL; + + if (ckch_conf_kws[i].offset < 0) + continue; + + if (!ckch_conf_kws[i].func) + continue; + + src = (void *)((intptr_t)f + (ptrdiff_t)ckch_conf_kws[i].offset); + + switch (ckch_conf_kws[i].type) { + case PARSE_TYPE_STR: + { + char *v; + char *path; + char **base = ckch_conf_kws[i].base; + char path_base[PATH_MAX]; + + v = *(char **)src; + if (!v) + goto next; + + path = v; + if (base && *base && *path != '/') { + int rv = snprintf(path_base, sizeof(path_base), "%s/%s", *base, path); + if (rv >= sizeof(path_base)) { + memprintf(err, "'%s/%s' : path too long", *base, path); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + path = path_base; + } + rc = ckch_conf_kws[i].func(path, NULL, d, cli, err); + if (rc) { + err_code |= ERR_ALERT | ERR_FATAL; + memprintf(err, "%s '%s' cannot be read or parsed.", err && *err ? *err : "", path); + goto out; + } + break; + } + + case PARSE_TYPE_INT: + case PARSE_TYPE_ONOFF: + { + int v = *(int *)src; + rc = ckch_conf_kws[i].func(&v, NULL, d, cli, err); + if (rc) { + err_code |= ERR_ALERT | ERR_FATAL; + memprintf(err, "%s '%d' cannot be read or parsed.", err && *err ? *err : "", v); + goto out; + } + + break; + } + + default: + break; + } +next: + ; + } + +out: + if (err_code & ERR_FATAL) + ssl_sock_free_cert_key_and_chain_contents(d); + ERR_clear_error(); + + return err_code; +} + +/* Parse a local crt-base or key-base for a crt-store */ +static int crtstore_parse_path_base(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, + const char *file, int linenum, char **err) +{ + int err_code = ERR_NONE; + + if (!*args[1]) { + memprintf(err, "parsing [%s:%d] : '%s' requires a <path> argument.", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (crtstore_load) { + memprintf(err, "parsing [%s:%d] : '%s' can't be used after a load line, use it at the beginning of the section.", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (args[0][1] == 'r') { + /* crt-base */ + free(current_crtbase); + current_crtbase = strdup(args[1]); + } else if (args[0][1] == 'e') { + /* key-base */ + free(current_keybase); + current_keybase = strdup(args[1]); + } +out: + return err_code; +} + +/* + * Check if ckch_conf <prev> and <new> are compatible: + * + * new \ prev | EMPTY | CRTLIST | CRTSTORE + * ---------------------------------------- + * EMPTY | OK | X | OK + * ---------------------------------------- + * CRTLIST | X | CMP | CMP + * ---------------------------------------- + * + * Return: + * 1 when the 2 structures have different variables or are incompatible + * 0 when the 2 structures have equal variables or are compatibles + */ +int ckch_conf_cmp(struct ckch_conf *prev, struct ckch_conf *new, char **err) +{ + int ret = 0; + int i; + + if (!prev || !new) + return 1; + + /* compatibility check */ + + if (prev->used == CKCH_CONF_SET_EMPTY) { + if (new->used == CKCH_CONF_SET_CRTLIST) { + memprintf(err, "%sCan't use the certificate previously defined without any keyword with these keywords:\n", *err ? *err : ""); + ret = 1; + } + if (new->used == CKCH_CONF_SET_EMPTY) + return 0; + + } else if (prev->used == CKCH_CONF_SET_CRTLIST) { + if (new->used == CKCH_CONF_SET_EMPTY) { + memprintf(err, "%sCan't use the certificate previously defined with keywords without these keywords:\n", *err ? *err : ""); + ret = 1; + } + } else if (prev->used == CKCH_CONF_SET_CRTSTORE) { + if (new->used == CKCH_CONF_SET_EMPTY) + return 0; + } + + + for (i = 0; ckch_conf_kws[i].name != NULL; i++) { + + if (strcmp(ckch_conf_kws[i].name, "crt") == 0) + continue; + + switch (ckch_conf_kws[i].type) { + case PARSE_TYPE_STR: { + char *avail1, *avail2; + avail1 = *(char **)((intptr_t)prev + (ptrdiff_t)ckch_conf_kws[i].offset); + avail2 = *(char **)((intptr_t)new + (ptrdiff_t)ckch_conf_kws[i].offset); + + /* must alert when strcmp is wrong, or when one of the field is NULL */ + if (((avail1 && avail2) && strcmp(avail1, avail2) != 0) || (!!avail1 ^ !!avail2)) { + memprintf(err, "%s- different parameter '%s' : previously '%s' vs '%s'\n", *err ? *err : "", ckch_conf_kws[i].name, avail1, avail2); + ret = 1; + } + } + break; + + default: + break; + } + /* special case for ocsp-update and default */ + if (strcmp(ckch_conf_kws[i].name, "ocsp-update") == 0) { + int o1, o2; /* ocsp-update from the configuration */ + int q1, q2; /* final ocsp-update value (from default) */ + + + o1 = *(int *)((intptr_t)prev + (ptrdiff_t)ckch_conf_kws[i].offset); + o2 = *(int *)((intptr_t)new + (ptrdiff_t)ckch_conf_kws[i].offset); + + q1 = (o1 == SSL_SOCK_OCSP_UPDATE_DFLT) ? global_ssl.ocsp_update.mode : o1; + q2 = (o2 == SSL_SOCK_OCSP_UPDATE_DFLT) ? global_ssl.ocsp_update.mode : o2; + + if (q1 != q2) { + int j = 1; + int o = o1; + int q = q1; + memprintf(err, "%s- different parameter '%s' : previously ", *err ? *err : "", ckch_conf_kws[i].name); + + do { + switch (o) { + case SSL_SOCK_OCSP_UPDATE_DFLT: + memprintf(err, "%s'default' (ocsp-update.mode %s)", *err ? *err : "", (q > 0) ? "on" : "off"); + break; + case SSL_SOCK_OCSP_UPDATE_ON: + memprintf(err, "%s'%s'", *err ? *err : "", "on"); + break; + case SSL_SOCK_OCSP_UPDATE_OFF: + memprintf(err, "%s'%s'", *err ? *err : "", "off"); + break; + } + o = o2; + q = q2; + if (j) + memprintf(err, "%s vs ", *err ? *err : ""); + } while (j--); + memprintf(err, "%s\n", *err ? *err : ""); + ret = 1; + } + } + } + +out: + return ret; +} + +/* + * Compare a previously generated ckch_conf with an empty one, using ckch_conf_cmp(). + */ +int ckch_conf_cmp_empty(struct ckch_conf *prev, char **err) +{ + struct ckch_conf new = {}; + + return ckch_conf_cmp(prev, &new, err); +} + +/* parse ckch_conf keywords for crt-list */ +int ckch_conf_parse(char **args, int cur_arg, struct ckch_conf *f, int *found, const char *file, int linenum, char **err) +{ + int i; + int err_code = 0; + + for (i = 0; ckch_conf_kws[i].name != NULL; i++) { + if (strcmp(ckch_conf_kws[i].name, args[cur_arg]) == 0) { + void *target; + *found = 1; + target = (char **)((intptr_t)f + (ptrdiff_t)ckch_conf_kws[i].offset); + + if (ckch_conf_kws[i].type == PARSE_TYPE_STR) { + char **t = target; + + *t = strdup(args[cur_arg + 1]); + if (!*t) { + ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + } else if (ckch_conf_kws[i].type == PARSE_TYPE_INT) { + int *t = target; + char *stop; + + *t = strtol(args[cur_arg + 1], &stop, 10); + if (*stop != '\0') { + memprintf(err, "parsing [%s:%d] : cannot parse '%s' value '%s', an integer is expected.\n", + file, linenum, args[cur_arg], args[cur_arg + 1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } else if (ckch_conf_kws[i].type == PARSE_TYPE_ONOFF) { + int *t = target; + + if (strcmp(args[cur_arg + 1], "on") == 0) { + *t = 1; + } else if (strcmp(args[cur_arg + 1], "off") == 0) { + *t = -1; + } else { + memprintf(err, "parsing [%s:%d] : cannot parse '%s' value '%s', 'on' or 'off' is expected.\n", + file, linenum, args[cur_arg], args[cur_arg + 1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + break; + } + } +out: + return err_code; +} + +/* freeing the content of a ckch_conf structure */ +void ckch_conf_clean(struct ckch_conf *conf) +{ + free(conf->crt); + free(conf->key); + free(conf->ocsp); + free(conf->issuer); + free(conf->sctl); +} + +static char current_crtstore_name[PATH_MAX] = {}; + +static int crtstore_parse_load(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, + const char *file, int linenum, char **err) +{ + int err_code = 0; + int cur_arg = 0; + struct ckch_conf f = {}; + struct ckch_store *c = NULL; + char store_path[PATH_MAX]; /* complete path with crt_base */ + char alias_name[PATH_MAX]; /* complete alias name with the store prefix '@/' */ + char *final_name = NULL; /* name used as a key in the ckch_store */ + + cur_arg++; /* skip "load" */ + + while (*(args[cur_arg])) { + int found = 0; + + if (strcmp("alias", args[cur_arg]) == 0) { + int rv; + + if (*args[cur_arg + 1] == '/') { + memprintf(err, "parsing [%s:%d] : cannot parse '%s' value '%s', '/' is forbidden as the first character.\n", + file, linenum, args[cur_arg], args[cur_arg + 1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + rv = snprintf(alias_name, sizeof(alias_name), "@%s/%s", current_crtstore_name, args[cur_arg + 1]); + if (rv >= sizeof(alias_name)) { + memprintf(err, "parsing [%s:%d] : cannot parse '%s' value '%s', too long, max len is %zd.\n", + file, linenum, args[cur_arg], args[cur_arg + 1], sizeof(alias_name)); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + final_name = alias_name; + found = 1; + } else { + err_code |= ckch_conf_parse(args, cur_arg, &f, &found, file, linenum, err); + if (err_code & ERR_FATAL) + goto out; + } + + if (!found) { + memprintf(err,"parsing [%s:%d] : '%s %s' in section 'crt-store': unknown keyword '%s'.", + file, linenum, args[0], args[cur_arg],args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + cur_arg += 2; + } + + if (!f.crt) { + memprintf(err,"parsing [%s:%d] : '%s' in section 'crt-store': mandatory 'crt' parameter not found.", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + crtstore_load = 1; + + if (!final_name) { + final_name = f.crt; + + /* if no alias was used: + * - when a crt-store exists, use @store/crt + * - or use the absolute file (crt_base + crt) + * - or the relative file when no crt_base exists + */ + if (current_crtstore_name[0] != '\0') { + int rv; + + /* add the crt-store name, avoid a double / if the crt starts by it */ + rv = snprintf(alias_name, sizeof(alias_name), "@%s%s%s", current_crtstore_name, f.crt[0] != '/' ? "/" : "", f.crt); + if (rv >= sizeof(alias_name)) { + memprintf(err, "parsing [%s:%d] : cannot parse '%s' value '%s', too long, max len is %zd.\n", + file, linenum, args[cur_arg], args[cur_arg + 1], sizeof(alias_name)); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + final_name = alias_name; + } else if (global_ssl.crt_base && *f.crt != '/') { + int rv; + /* When no crt_store name, complete the name in the ckch_tree with 'crt-base' */ + + rv = snprintf(store_path, sizeof(store_path), "%s/%s", global_ssl.crt_base, f.crt); + if (rv >= sizeof(store_path)) { + memprintf(err, "'%s/%s' : path too long", global_ssl.crt_base, f.crt); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + final_name = store_path; + } + } + /* process and insert the ckch_store */ + c = ckch_store_new(final_name); + if (!c) + goto alloc_error; + + err_code |= ckch_store_load_files(&f, c, 0, err); + if (err_code & ERR_FATAL) + goto out; + + c->conf = f; + c->conf.used = CKCH_CONF_SET_CRTSTORE; + + if (ebst_insert(&ckchs_tree, &c->node) != &c->node) { + memprintf(err,"parsing [%s:%d] : '%s' in section 'crt-store': store '%s' was already defined.", + file, linenum, args[0], c->path); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + +out: + /* free ckch_conf content */ + if (err_code & ERR_FATAL) + ckch_store_free(c); + return err_code; + +alloc_error: + ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; +} + +/* + * Parse "crt-store" section and create corresponding ckch_stores. + * + * The function returns 0 in success case, otherwise, it returns error + * flags. + */ +static int cfg_parse_crtstore(const char *file, int linenum, char **args, int kwm) +{ + struct cfg_kw_list *kwl; + const char *best; + int index; + int rc = 0; + int err_code = 0; + char *errmsg = NULL; + + if (strcmp(args[0], "crt-store") == 0) { /* new crt-store section */ + if (!*args[1]) { + current_crtstore_name[0] = '\0'; + } else { + rc = snprintf(current_crtstore_name, sizeof(current_crtstore_name), "%s", args[1]); + if (rc >= sizeof(current_crtstore_name)) { + ha_alert("parsing [%s:%d] : 'crt-store' <name> argument is too long.\n", file, linenum); + current_crtstore_name[0] = '\0'; + err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT; + goto out; + } + } + + if (*args[2]) { + ha_alert("parsing [%s:%d] : 'crt-store' section only supports a <name> argument.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT; + goto out; + } + /* copy the crt_base and key_base */ + ha_free(¤t_crtbase); + if (global_ssl.crt_base) + current_crtbase = strdup(global_ssl.crt_base); + ha_free(¤t_keybase); + if (global_ssl.key_base) + current_keybase = strdup(global_ssl.key_base); + crtstore_load = 0; + + goto out; + } + + list_for_each_entry(kwl, &cfg_keywords.list, list) { + for (index = 0; kwl->kw[index].kw != NULL; index++) { + if (kwl->kw[index].section != CFG_CRTSTORE) + continue; + if (strcmp(kwl->kw[index].kw, args[0]) == 0) { + if (check_kw_experimental(&kwl->kw[index], file, linenum, &errmsg)) { + ha_alert("%s\n", errmsg); + err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT; + goto out; + } + + /* prepare error message just in case */ + rc = kwl->kw[index].parse(args, CFG_CRTSTORE, NULL, NULL, file, linenum, &errmsg); + if (rc & ERR_ALERT) { + ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); + err_code |= rc; + goto out; + } + else if (rc & ERR_WARN) { + ha_warning("parsing [%s:%d] : %s\n", file, linenum, errmsg); + err_code |= rc; + goto out; + } + goto out; + } + } + } + + best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_CRTSTORE, NULL); + if (best) + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best); + else + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + +out: + if (err_code & ERR_FATAL) + err_code |= ERR_ABORT; + free(errmsg); + return err_code; +} + +static int cfg_post_parse_crtstore() +{ + current_crtstore_name[0] = '\0'; + ha_free(¤t_crtbase); + ha_free(¤t_keybase); + + return ERR_NONE; +} + +REGISTER_CONFIG_SECTION("crt-store", cfg_parse_crtstore, cfg_post_parse_crtstore); + +static struct cfg_kw_list cfg_kws = {ILH, { + { CFG_CRTSTORE, "crt-base", crtstore_parse_path_base }, + { CFG_CRTSTORE, "key-base", crtstore_parse_path_base }, + { CFG_CRTSTORE, "load", crtstore_parse_load }, + { 0, NULL, NULL }, +}}; +INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); |