MINOR: ssl: Factorize code from "new/set ssl cert" CLI command

This allows to perform the same kind of operation without the need for
an appctx.
This commit is contained in:
Remi Tricot-Le Breton 2026-05-06 14:16:39 +02:00 committed by William Lallemand
parent acf1331ed8
commit efe6c97488
2 changed files with 108 additions and 82 deletions

View File

@ -48,6 +48,8 @@ struct ckch_store *ckch_store_new(const char *filename);
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, int cli, const char *file, int linenum, char **err);
int ckch_store_create(char *path, char **err);
int ckch_store_load_payload(char *path, char *payload, char **err);
/* ckch_conf functions */

View File

@ -3066,53 +3066,42 @@ error:
}
/*
* Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
* It uses a set_cert_ctx context, and ckchs_transaction under a lock.
* Load the contents of <payload> (x509 format) into the ckch_store indexed by
* <path> in the ckch_store tree. The entry must already exist in the tree.
* Return 0 in case of success.
*/
static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
int ckch_store_load_payload(char *path, char *payload, char **err)
{
struct ckch_store *new_ckchs = NULL;
struct ckch_store *old_ckchs = NULL;
char *err = NULL;
int i;
int errcode = 0;
char *end;
struct cert_exts *cert_ext = &cert_exts[0]; /* default one, PEM */
struct ckch_data *data;
struct buffer *buf;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
if (!*args[3] || !payload)
return cli_err(appctx, "'set ssl cert' expects a filename and a certificate as a payload\n");
int errcode = 0;
int i;
struct buffer *pathbuf = NULL;
/* The operations on the CKCH architecture are locked so we can
* manipulate ckch_store and ckch_inst */
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
if ((buf = alloc_trash_chunk()) == NULL) {
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
errcode |= ERR_ALERT | ERR_FATAL;
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
memprintf(err, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
goto end;
}
if (!chunk_strcpy(buf, args[3])) {
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
pathbuf = alloc_trash_chunk();
if (!pathbuf || !chunk_strcpy(pathbuf, path)) {
memprintf(err, "%sCan't allocate memory\n", (err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
/* check which type of file we want to update */
for (i = 0; cert_exts[i].ext != NULL; i++) {
end = strrchr(buf->area, '.');
end = strrchr(pathbuf->area, '.');
if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
*end = '\0';
buf->data = strlen(buf->area);
pathbuf->data = strlen(pathbuf->area);
cert_ext = &cert_exts[i];
break;
}
@ -3121,29 +3110,29 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
/* if there is an ongoing transaction */
if (ckchs_transaction.path) {
/* if there is an ongoing transaction, check if this is the same file */
if (strcmp(ckchs_transaction.path, buf->area) != 0) {
if (strcmp(ckchs_transaction.path, pathbuf->area) != 0) {
/* we didn't find the transaction, must try more cases below */
/* if the del-ext option is activated we should try to take a look at a ".crt" too. */
if (cert_ext->type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
if (!chunk_strcat(buf, ".crt")) {
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
if (!chunk_strcat(pathbuf, ".crt")) {
memprintf(err, "%sCan't allocate memory\n", (err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
/* check again with the right extension */
if (strcmp(ckchs_transaction.path, buf->area) != 0) {
if (strcmp(ckchs_transaction.path, pathbuf->area) != 0) {
/* remove .crt of the error message */
*(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
b_sub(buf, strlen(".crt"));
*(b_orig(pathbuf) + b_data(pathbuf) + strlen(".crt")) = '\0';
b_sub(pathbuf, strlen(".crt"));
memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
memprintf(err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, pathbuf->area);
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
} else {
/* without del-ext the error is definitive */
memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
memprintf(err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, pathbuf->area);
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
@ -3154,24 +3143,24 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
} else {
/* lookup for the certificate in the tree */
old_ckchs = ckchs_lookup(buf->area);
old_ckchs = ckchs_lookup(pathbuf->area);
if (!old_ckchs) {
/* if the del-ext option is activated we should try to take a look at a ".crt" too. */
if (cert_ext->type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
if (!chunk_strcat(buf, ".crt")) {
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
if (!chunk_strcat(pathbuf, ".crt")) {
memprintf(err, "%sCan't allocate memory\n", (err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
old_ckchs = ckchs_lookup(buf->area);
old_ckchs = ckchs_lookup(pathbuf->area);
}
}
}
if (!old_ckchs) {
memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
err ? err : "");
memprintf(err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
(err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
@ -3179,8 +3168,7 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
/* duplicate the ckch store */
new_ckchs = ckchs_dup(old_ckchs);
if (!new_ckchs) {
memprintf(&err, "%sCannot allocate memory!\n",
err ? err : "");
memprintf(err, "%sCannot allocate memory!\n", (err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
@ -3196,8 +3184,8 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
data = new_ckchs->data;
/* apply the change on the duplicate */
if (cert_ext->load(buf->area, payload, data, &err) != 0) {
memprintf(&err, "%sCan't load the payload\n", err ? err : "");
if (cert_ext->load(pathbuf->area, payload, data, err) != 0) {
memprintf(err, "%sCan't load the payload\n", (err && *err) ? *err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
@ -3208,10 +3196,9 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
if (!ckchs_transaction.old_ckchs) {
ckchs_transaction.old_ckchs = old_ckchs;
ckchs_transaction.path = old_ckchs->path;
err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
memprintf(err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
} else {
err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
memprintf(err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
}
/* free the previous ckchs if there was a transaction */
@ -3219,20 +3206,39 @@ static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx,
ckchs_transaction.new_ckchs = new_ckchs;
end:
if (errcode & ERR_CODE)
ckch_store_free(new_ckchs);
free_trash_chunk(pathbuf);
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
return errcode;
}
/*
* Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
* It uses a set_cert_ctx context, and ckchs_transaction under a lock.
*/
static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
{
char *err = NULL;
int errcode = 0;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
if (!*args[3] || !payload)
return cli_err(appctx, "'set ssl cert' expects a filename and a certificate as a payload\n");
errcode = ckch_store_load_payload(args[3], payload, &err);
/* creates the SNI ctxs later in the IO handler */
end:
free_trash_chunk(buf);
if (errcode & ERR_CODE) {
ckch_store_free(new_ckchs);
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
if (errcode & ERR_CODE)
return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
} else {
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
return cli_dynmsg(appctx, LOG_NOTICE, err);
}
return cli_dynmsg(appctx, LOG_NOTICE, err);
/* TODO: handle the ERR_WARN which are not handled because of the io_handler */
}
@ -3279,12 +3285,54 @@ error:
return cli_dynerr(appctx, err);
}
/*
* Create ckch_store and insert it in the ckch_store tree.
* The lock one the tree must not be taken beforehand.
* Return 0 in case of success, 1 if an entry with the same path already exists,
* -1 otherwise.
*/
int ckch_store_create(char *path, char **err)
{
struct ckch_store *store = NULL;
/* The operations on the CKCH architecture are locked so we can
* manipulate ckch_store and ckch_inst */
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
memprintf(err, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
goto error;
}
store = ckchs_lookup(path);
if (store != NULL) {
memprintf(err, "Certificate '%s' already exists!\n", path);
store = NULL; /* we don't want to free it */
goto error;
}
/* we won't support multi-certificate bundle here */
store = ckch_store_new(path);
if (!store) {
memprintf(err, "unable to allocate memory.\n");
goto error;
}
/* insert into the ckchs tree */
ebst_insert(&ckchs_tree, &store->node);
memprintf(err, "New empty certificate store '%s'!\n", path);
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
return 0;
error:
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
free(store);
return 1;
}
/* parsing function of 'new ssl cert' */
static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
{
struct ckch_store *store;
char *err = NULL;
char *path;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
@ -3292,35 +3340,11 @@ static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx,
if (!*args[3])
return cli_err(appctx, "'new ssl cert' expects a filename\n");
path = args[3];
/* The operations on the CKCH architecture are locked so we can
* manipulate ckch_store and ckch_inst */
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
store = ckchs_lookup(path);
if (store != NULL) {
memprintf(&err, "Certificate '%s' already exists!\n", path);
store = NULL; /* we don't want to free it */
if (ckch_store_create(args[3], &err))
goto error;
}
/* we won't support multi-certificate bundle here */
store = ckch_store_new(path);
if (!store) {
memprintf(&err, "unable to allocate memory.\n");
goto error;
}
/* insert into the ckchs tree */
ebst_insert(&ckchs_tree, &store->node);
memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
return cli_dynmsg(appctx, LOG_NOTICE, err);
error:
free(store);
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
return cli_dynerr(appctx, err);
}