From: William Lallemand Date: Wed, 10 Apr 2024 15:21:50 +0000 (+0200) Subject: MEDIUM: ssl/crtlist: loading crt-store keywords from a crt-list X-Git-Tag: v3.0-dev12~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d6169320766de6006e777144e63239c583d59106;p=thirdparty%2Fhaproxy.git MEDIUM: ssl/crtlist: loading crt-store keywords from a crt-list This patch allows the usage of "crt-store" keywords from a "crt-list". The crtstore_parse_load() function was splitted into 2 functions, so the keywords parsing is done in ckch_conf_parse(). With this patch, crt are loaded with ckch_store_new_load_files_conf() or ckch_store_new_load_files_path() depending on weither or not there is a "crt-store" keyword. More checks need to be done on "crt" bind keywords to ensure that keywords are compatible. This patch does not introduce the feature on the CLI. --- diff --git a/include/haproxy/ssl_ckch-t.h b/include/haproxy/ssl_ckch-t.h index 6951126e2f..cb945ff277 100644 --- a/include/haproxy/ssl_ckch-t.h +++ b/include/haproxy/ssl_ckch-t.h @@ -60,6 +60,7 @@ struct ckch_data { /* configuration for the ckch_store */ struct ckch_conf { + int used; char *crt; char *key; char *ocsp; diff --git a/include/haproxy/ssl_ckch.h b/include/haproxy/ssl_ckch.h index 1bb82a2d51..41183791ce 100644 --- a/include/haproxy/ssl_ckch.h +++ b/include/haproxy/ssl_ckch.h @@ -46,6 +46,7 @@ void ckch_store_free(struct ckch_store *store); void ckch_store_replace(struct ckch_store *old_ckchs, struct ckch_store *new_ckchs); int ckch_store_load_files(struct ckch_conf *f, struct ckch_store *c, char **err); +int ckch_conf_parse(char **args, int cur_arg, struct ckch_conf *f, int *found, const char *file, int linenum, char **err); /* ckch_inst functions */ void ckch_inst_free(struct ckch_inst *inst); struct ckch_inst *ckch_inst_new(); diff --git a/include/haproxy/ssl_crtlist.h b/include/haproxy/ssl_crtlist.h index 961cfc385e..f81ee9ea01 100644 --- a/include/haproxy/ssl_crtlist.h +++ b/include/haproxy/ssl_crtlist.h @@ -38,7 +38,7 @@ void crtlist_free(struct crtlist *crtlist); struct crtlist *crtlist_new(const char *filename, int unique); /* file loading */ -int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, int from_cli, char **err); +int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, struct ckch_conf *conf, const char *file, int linenum, int from_cli, char **err); int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err); int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err); diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index f6e2e9a5b0..b7b881a018 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -4127,7 +4127,60 @@ static int crtstore_parse_path_base(char **args, int section_type, struct proxy free(current_keybase); current_keybase = strdup(args[1]); } +out: + return err_code; +} + +/* 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 = 0; + } 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; } @@ -4137,7 +4190,6 @@ 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 i; int err_code = 0; int cur_arg = 0; struct ckch_conf f = {}; @@ -4151,65 +4203,31 @@ static int crtstore_parse_load(char **args, int section_type, struct proxy *curp while (*(args[cur_arg])) { int found = 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 (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; - } + if (strcmp("alias", args[cur_arg]) == 0) { + int rv; - 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; - } else if (ckch_conf_kws[i].type == PARSE_TYPE_STR) { - char **t = target; - - *t = strdup(args[cur_arg + 1]); - if (!*t) - goto alloc_error; - } 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 = 0; - } 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; + 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]); diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c index 528db099d9..fc9bfb70e6 100644 --- a/src/ssl_crtlist.c +++ b/src/ssl_crtlist.c @@ -356,7 +356,7 @@ struct crtlist *crtlist_new(const char *filename, int unique) * is a ptr in * Return an error code */ -int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, int from_cli, char **err) +int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, struct ckch_conf *cc, const char *file, int linenum, int from_cli, char **err) { int cfgerr = 0; int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0; @@ -443,6 +443,7 @@ int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, cur_arg = ssl_b ? ssl_b : 1; while (cur_arg < ssl_e) { newarg = 0; + /* look for ssl_conf 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) @@ -462,9 +463,20 @@ int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, goto error; } cur_arg += 1 + ssl_crtlist_kws[i].skip; - break; + goto out; } } + if (cc) { + /* look for ckch_conf keywords */ + cfgerr |= ckch_conf_parse(args, cur_arg, cc, &newarg, file, linenum, err); + if (cfgerr & ERR_FATAL) + goto error; + + cc->used = 1; + if (newarg) /* skip 2 words if the keyword was found */ + cur_arg += 2; + } +out: if (!cfgerr && !newarg) { memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s", file, linenum, args[cur_arg]); @@ -523,6 +535,7 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu char *crt_path; char path[MAXPATHLEN+1]; struct ckch_store *ckchs; + struct ckch_conf cc = {}; int found = 0; if (missing_lf != -1) { @@ -564,7 +577,7 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu goto error; } - cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err); + cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, &cc, file, linenum, 0, err); if (cfgerr & ERR_CODE) goto error; @@ -591,13 +604,20 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu if (ckchs == NULL) { if (stat(crt_path, &buf) == 0) { found++; - - ckchs = ckch_store_new_load_files_path(crt_path, err); + if (cc.used) { + free(cc.crt); + cc.crt = strdup(crt_path); + ckchs = ckch_store_new_load_files_conf(crt_path, &cc, err); + } else { + ckchs = ckch_store_new_load_files_path(crt_path, err); + } if (ckchs == NULL) { cfgerr |= ERR_ALERT | ERR_FATAL; goto error; } + ckchs->conf = cc; + entry->node.key = ckchs; entry->crtlist = newlist; if (entry->ssl_conf) @@ -618,6 +638,7 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu char fp[MAXPATHLEN+1] = {0}; int n = 0; struct crtlist_entry *entry_dup = entry; /* use the previous created entry */ + for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) { struct stat buf; int ret; @@ -629,6 +650,12 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu ckchs = ckchs_lookup(fp); if (!ckchs) { if (stat(fp, &buf) == 0) { + + if (cc.used) { + memprintf(err, "%sCan't load '%s'. Using crt-store keyword is not compatible with multi certificates bundle.\n", + err && *err ? *err : "", crt_path); + cfgerr |= ERR_ALERT | ERR_FATAL; + } ckchs = ckch_store_new_load_files_path(fp, err); if (!ckchs) { cfgerr |= ERR_ALERT | ERR_FATAL; @@ -719,6 +746,8 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu error: crtlist_entry_free(entry); + /* FIXME: free cc */ + fclose(f); crtlist_free(newlist); return cfgerr; @@ -1307,7 +1336,8 @@ static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appc goto error; } /* cert_path is filled here */ - cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err); + cfgerr |= crtlist_parse_line(payload, &cert_path, entry, NULL, "CLI", 1, 1, &err); + /* FIXME: handle the ckch_conf */ if (cfgerr & ERR_CODE) goto error; } else {