summaryrefslogtreecommitdiffstats
path: root/src/ssl_ckch.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:11:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:11:10 +0000
commitcff6d757e3ba609c08ef2aaa00f07e53551e5bf6 (patch)
tree08c4fc3255483ad397d712edb4214ded49149fd9 /src/ssl_ckch.c
parentAdding upstream version 2.9.7. (diff)
downloadhaproxy-cff6d757e3ba609c08ef2aaa00f07e53551e5bf6.tar.xz
haproxy-cff6d757e3ba609c08ef2aaa00f07e53551e5bf6.zip
Adding upstream version 3.0.0.upstream/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.c664
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, &current_crtbase },
+ { "key", offsetof(struct ckch_conf, key), PARSE_TYPE_STR, ckch_conf_load_key, &current_keybase },
+ { "ocsp", offsetof(struct ckch_conf, ocsp), PARSE_TYPE_STR, ckch_conf_load_ocsp_response, &current_crtbase },
+ { "issuer", offsetof(struct ckch_conf, issuer), PARSE_TYPE_STR, ckch_conf_load_ocsp_issuer, &current_crtbase },
+ { "sctl", offsetof(struct ckch_conf, sctl), PARSE_TYPE_STR, ckch_conf_load_sctl, &current_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(&current_crtbase);
+ if (global_ssl.crt_base)
+ current_crtbase = strdup(global_ssl.crt_base);
+ ha_free(&current_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(&current_crtbase);
+ ha_free(&current_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);