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.
This commit is contained in:
William Lallemand 2024-04-10 17:21:50 +02:00
parent 8526d666d2
commit d616932076
5 changed files with 113 additions and 63 deletions

View File

@ -60,6 +60,7 @@ struct ckch_data {
/* configuration for the ckch_store */ /* configuration for the ckch_store */
struct ckch_conf { struct ckch_conf {
int used;
char *crt; char *crt;
char *key; char *key;
char *ocsp; char *ocsp;

View File

@ -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); 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_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 */ /* ckch_inst functions */
void ckch_inst_free(struct ckch_inst *inst); void ckch_inst_free(struct ckch_inst *inst);
struct ckch_inst *ckch_inst_new(); struct ckch_inst *ckch_inst_new();

View File

@ -38,7 +38,7 @@ void crtlist_free(struct crtlist *crtlist);
struct crtlist *crtlist_new(const char *filename, int unique); struct crtlist *crtlist_new(const char *filename, int unique);
/* file loading */ /* 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_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); int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err);

View File

@ -4127,60 +4127,32 @@ static int crtstore_parse_path_base(char **args, int section_type, struct proxy
free(current_keybase); free(current_keybase);
current_keybase = strdup(args[1]); current_keybase = strdup(args[1]);
} }
out: out:
return err_code; return err_code;
} }
static char current_crtstore_name[PATH_MAX] = {};
static int crtstore_parse_load(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, /* parse ckch_conf keywords for crt-list */
const char *file, int linenum, char **err) 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 i;
int err_code = 0; 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;
for (i = 0; ckch_conf_kws[i].name != NULL; i++) { for (i = 0; ckch_conf_kws[i].name != NULL; i++) {
if (strcmp(ckch_conf_kws[i].name, args[cur_arg]) == 0) { if (strcmp(ckch_conf_kws[i].name, args[cur_arg]) == 0) {
void *target; void *target;
found = 1; *found = 1;
target = (char **)((intptr_t)&f + (ptrdiff_t)ckch_conf_kws[i].offset); target = (char **)((intptr_t)f + (ptrdiff_t)ckch_conf_kws[i].offset);
if (strcmp("alias", args[cur_arg]) == 0) { if (ckch_conf_kws[i].type == PARSE_TYPE_STR) {
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;
} else if (ckch_conf_kws[i].type == PARSE_TYPE_STR) {
char **t = target; char **t = target;
*t = strdup(args[cur_arg + 1]); *t = strdup(args[cur_arg + 1]);
if (!*t) if (!*t) {
goto alloc_error; 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) { } else if (ckch_conf_kws[i].type == PARSE_TYPE_INT) {
int *t = target; int *t = target;
char *stop; char *stop;
@ -4208,8 +4180,54 @@ static int crtstore_parse_load(char **args, int section_type, struct proxy *curp
} }
break; break;
} }
} }
out:
return err_code;
}
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) { if (!found) {
memprintf(err,"parsing [%s:%d] : '%s %s' in section 'crt-store': unknown keyword '%s'.", memprintf(err,"parsing [%s:%d] : '%s %s' in section 'crt-store': unknown keyword '%s'.",
file, linenum, args[0], args[cur_arg],args[cur_arg]); file, linenum, args[0], args[cur_arg],args[cur_arg]);

View File

@ -356,7 +356,7 @@ struct crtlist *crtlist_new(const char *filename, int unique)
* <crt_path> is a ptr in <line> * <crt_path> is a ptr in <line>
* Return an error code * 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 cfgerr = 0;
int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 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; cur_arg = ssl_b ? ssl_b : 1;
while (cur_arg < ssl_e) { while (cur_arg < ssl_e) {
newarg = 0; newarg = 0;
/* look for ssl_conf keywords */
for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) { for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) { if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
if (!ssl_conf) if (!ssl_conf)
@ -462,9 +463,20 @@ int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry,
goto error; goto error;
} }
cur_arg += 1 + ssl_crtlist_kws[i].skip; 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) { if (!cfgerr && !newarg) {
memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s", memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
file, linenum, args[cur_arg]); 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 *crt_path;
char path[MAXPATHLEN+1]; char path[MAXPATHLEN+1];
struct ckch_store *ckchs; struct ckch_store *ckchs;
struct ckch_conf cc = {};
int found = 0; int found = 0;
if (missing_lf != -1) { if (missing_lf != -1) {
@ -564,7 +577,7 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
goto error; 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) if (cfgerr & ERR_CODE)
goto error; goto error;
@ -591,13 +604,20 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
if (ckchs == NULL) { if (ckchs == NULL) {
if (stat(crt_path, &buf) == 0) { if (stat(crt_path, &buf) == 0) {
found++; found++;
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); ckchs = ckch_store_new_load_files_path(crt_path, err);
}
if (ckchs == NULL) { if (ckchs == NULL) {
cfgerr |= ERR_ALERT | ERR_FATAL; cfgerr |= ERR_ALERT | ERR_FATAL;
goto error; goto error;
} }
ckchs->conf = cc;
entry->node.key = ckchs; entry->node.key = ckchs;
entry->crtlist = newlist; entry->crtlist = newlist;
if (entry->ssl_conf) 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}; char fp[MAXPATHLEN+1] = {0};
int n = 0; int n = 0;
struct crtlist_entry *entry_dup = entry; /* use the previous created entry */ struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) { for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
struct stat buf; struct stat buf;
int ret; int ret;
@ -629,6 +650,12 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
ckchs = ckchs_lookup(fp); ckchs = ckchs_lookup(fp);
if (!ckchs) { if (!ckchs) {
if (stat(fp, &buf) == 0) { 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); ckchs = ckch_store_new_load_files_path(fp, err);
if (!ckchs) { if (!ckchs) {
cfgerr |= ERR_ALERT | ERR_FATAL; cfgerr |= ERR_ALERT | ERR_FATAL;
@ -719,6 +746,8 @@ int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *cu
error: error:
crtlist_entry_free(entry); crtlist_entry_free(entry);
/* FIXME: free cc */
fclose(f); fclose(f);
crtlist_free(newlist); crtlist_free(newlist);
return cfgerr; return cfgerr;
@ -1307,7 +1336,8 @@ static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appc
goto error; goto error;
} }
/* cert_path is filled here */ /* 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) if (cfgerr & ERR_CODE)
goto error; goto error;
} else { } else {