MINOR: ssl/cli: implement "add ssl ca-file"

In ticket #1805 an user is impacted by the limitation of size of the CLI
buffer when updating a ca-file.

This patch allows a user to append new certificates to a ca-file instead
of trying to put them all with "set ssl ca-file"

The implementation use a new function ssl_store_dup_cafile_entry() which
duplicates a cafile_entry and its X509_STORE.

ssl_store_load_ca_from_buf() was modified to take an apped parameter so
we could share the function for "set" and "add".
This commit is contained in:
William Lallemand 2022-07-29 17:50:58 +02:00
parent d4774d3cfa
commit 62c0b99e3b
5 changed files with 129 additions and 20 deletions

View File

@ -1679,6 +1679,21 @@ add server <backend>/<server> [args]*
Their syntax is similar to the server line from the configuration file,
please refer to their individual documentation for details.
add ssl ca-file <cafile> <payload>
Add a new certificate to a ca-file. This command is useful when you reached
the buffer size limit on the CLI and want to add multiple certicates.
Instead of doing a "set" with all the certificates you are able to add each
certificate individually. A "set ssl ca-file" will reset the ca-file.
Example:
echo -e "set ssl ca-file cafile.pem <<\n$(cat rootCA.crt)\n" | \
socat /var/run/haproxy.stat -
echo -e "add ssl ca-file cafile.pem <<\n$(cat intermediate1.crt)\n" | \
socat /var/run/haproxy.stat -
echo -e "add ssl ca-file cafile.pem <<\n$(cat intermediate2.crt)\n" | \
socat /var/run/haproxy.stat -
echo "commit ssl ca-file cafile.pem" | socat /var/run/haproxy.stat -
add ssl crt-list <crtlist> <certificate>
add ssl crt-list <crtlist> <payload>
Add an certificate in a crt-list. It can also be used for directories since
@ -1821,8 +1836,8 @@ commit ssl ca-file <cafile>
contexts that use it, you will need to add it to a crt-list with "add ssl
crt-list".
See also "new ssl ca-file", "set ssl ca-file", "abort ssl ca-file" and
"add ssl crt-list".
See also "new ssl ca-file", "set ssl ca-file", "add ssl ca-file",
"abort ssl ca-file" and "add ssl crt-list".
commit ssl cert <filename>
Commit a temporary SSL certificate update transaction.
@ -2127,7 +2142,7 @@ httpclient <method> <URI>
new ssl ca-file <cafile>
Create a new empty CA file tree entry to be filled with a set of CA
certificates and added to a crt-list. This command should be used in
combination with "set ssl ca-file" and "add ssl crt-list".
combination with "set ssl ca-file", "add ssl ca-file" and "add ssl crt-list".
new ssl cert <filename>
Create a new empty SSL certificate store to be filled with a certificate and
@ -2309,15 +2324,16 @@ set severity-output [ none | number | string ]
duration of the current session.
set ssl ca-file <cafile> <payload>
This command is part of a transaction system, the "commit ssl ca-file" and
this command is part of a transaction system, the "commit ssl ca-file" and
"abort ssl ca-file" commands could be required.
If there is no on-going transaction, it will create a CA file tree entry into
which the certificates contained in the payload will be stored. The CA file
entry will not be stored in the CA file tree and will only be kept in a
temporary transaction. If a transaction with the same filename already exists,
the previous CA file entry will be deleted and replaced by the new one.
Once the modifications are done, you have to commit the transaction through
a "commit ssl ca-file" call.
if there is no on-going transaction, it will create a ca file tree entry into
which the certificates contained in the payload will be stored. the ca file
entry will not be stored in the ca file tree and will only be kept in a
temporary transaction. if a transaction with the same filename already exists,
the previous ca file entry will be deleted and replaced by the new one.
once the modifications are done, you have to commit the transaction through
a "commit ssl ca-file" call. If you want to add multiple certificates
separately, you can use the "add ssl ca-file" command
Example:
echo -e "set ssl ca-file cafile.pem <<\n$(cat rootCA.crt)\n" | \

View File

@ -63,6 +63,7 @@ struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry);
X509_STORE* ssl_store_get0_locations_file(char *path);
int ssl_store_add_uncommitted_cafile_entry(struct cafile_entry *entry);
struct cafile_entry *ssl_store_create_cafile_entry(char *path, X509_STORE *store, enum cafile_type type);
struct cafile_entry *ssl_store_dup_cafile_entry(struct cafile_entry *src);
void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e);
int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf, int append);
int ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type);

View File

@ -81,6 +81,26 @@ shell {
echo "commit ssl ca-file new_cafile.crt" | socat "${tmpdir}/h1/stats" -
}
# Remove the unliked CA file and create a new one with the "add ssl ca-file method"
haproxy h1 -cli {
send "del ssl ca-file new_cafile.crt"
expect ~ "CA file 'new_cafile.crt' deleted!"
send "new ssl ca-file new_cafile.crt"
expect ~ "New CA file created 'new_cafile.crt'!"
}
shell {
printf "add ssl ca-file new_cafile.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
echo "commit ssl ca-file new_cafile.crt" | socat "${tmpdir}/h1/stats" -
}
shell {
printf "set ssl ca-file new_cafile.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
echo "commit ssl ca-file new_cafile.crt" | socat "${tmpdir}/h1/stats" -
}
haproxy h1 -cli {
send "show ssl ca-file"
expect ~ ".*new_cafile.crt - 1 certificate.*"

View File

@ -138,7 +138,9 @@ client c1 -connect ${h1_clearverifiedlst_sock} {
# Update the server line's ca-file. The server certificate should now be accepted by
# the frontend. We replace the single CA by a list of CAs that includes the correct one.
shell {
printf "set ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n$(cat ${testdir}/set_cafile_interCA2.crt)\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
printf "set ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA1.crt)\n\n" | socat "${tmpdir}/h1/stats" -
printf "add ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_interCA2.crt)\n\n" | socat "${tmpdir}/h1/stats" -
printf "add ssl ca-file ${testdir}/set_cafile_interCA1.crt <<\n$(cat ${testdir}/set_cafile_rootCA.crt)\n\n" | socat "${tmpdir}/h1/stats" -
echo "commit ssl ca-file ${testdir}/set_cafile_interCA1.crt" | socat "${tmpdir}/h1/stats" -
}

View File

@ -1076,6 +1076,67 @@ struct cafile_entry *ssl_store_create_cafile_entry(char *path, X509_STORE *store
return ca_e;
}
/* Duplicate a cafile_entry
* Allocate the X509_STORE and copy the X509 and CRL inside.
*
* Return the newly allocated cafile_entry or NULL.
*
*/
struct cafile_entry *ssl_store_dup_cafile_entry(struct cafile_entry *src)
{
struct cafile_entry *dst = NULL;
X509_STORE *store = NULL;
STACK_OF(X509_OBJECT) *objs;
int i;
if (!src)
return NULL;
if (src->ca_store) {
/* if there was a store in the src, copy it */
store = X509_STORE_new();
if (!store)
goto err;
objs = X509_STORE_get0_objects(src->ca_store);
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
X509 *cert;
X509_CRL *crl;
cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
if (cert) {
if (X509_STORE_add_cert(store, cert) == 0) {
/* only exits on error if the error is not about duplicate certificates */
if (!(ERR_GET_REASON(ERR_get_error()) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) {
goto err;
}
}
}
crl = X509_OBJECT_get0_X509_CRL(sk_X509_OBJECT_value(objs, i));
if (crl) {
if (X509_STORE_add_crl(store, crl) == 0) {
/* only exits on error if the error is not about duplicate certificates */
if (!(ERR_GET_REASON(ERR_get_error()) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) {
goto err;
}
}
}
}
}
dst = ssl_store_create_cafile_entry(src->path, store, src->type);
return dst;
err:
X509_STORE_free(store);
ha_free(&dst);
return NULL;
}
/* Delete a cafile_entry. The caller is responsible from removing this entry
* from the cafile_tree first if is was previously added into it. */
void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e)
@ -2584,10 +2645,15 @@ static int cli_parse_set_cafile(char **args, char *payload, struct appctx *appct
char *err = NULL;
int errcode = 0;
struct buffer *buf;
int add_cmd = 0;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
/* this is "add ssl ca-file" */
if (*args[0] == 'a')
add_cmd = 1;
if (!*args[3] || !payload)
return cli_err(appctx, "'set ssl ca-file expects a filename and CAs as a payload\n");
@ -2620,8 +2686,7 @@ static int cli_parse_set_cafile(char **args, char *payload, struct appctx *appct
goto end;
}
old_cafile_entry = cafile_transaction.old_cafile_entry;
}
else {
} else {
/* lookup for the certificate in the tree */
old_cafile_entry = ssl_store_get_cafile_entry(buf->area, 0);
}
@ -2633,17 +2698,21 @@ static int cli_parse_set_cafile(char **args, char *payload, struct appctx *appct
goto end;
}
/* Create a new cafile_entry without adding it to the cafile tree. */
new_cafile_entry = ssl_store_create_cafile_entry(old_cafile_entry->path, NULL, CAFILE_CERT);
/* if the transaction is new, duplicate the old_ca_file_entry, otherwise duplicate the cafile in the current transaction */
if (cafile_transaction.new_cafile_entry)
new_cafile_entry = ssl_store_dup_cafile_entry(cafile_transaction.new_cafile_entry);
else
new_cafile_entry = ssl_store_dup_cafile_entry(old_cafile_entry);
if (!new_cafile_entry) {
memprintf(&err, "%sCannot allocate memory!\n",
err ? err : "");
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
}
/* Fill the new entry with the new CAs. */
if (ssl_store_load_ca_from_buf(new_cafile_entry, payload, 0)) {
/* Fill the new entry with the new CAs. The add_cmd variable determine
if we flush the X509_STORE or not */
if (ssl_store_load_ca_from_buf(new_cafile_entry, payload, add_cmd)) {
memprintf(&err, "%sInvalid payload\n", err ? err : "");
errcode |= ERR_ALERT | ERR_FATAL;
goto end;
@ -3853,6 +3922,7 @@ static struct cli_kw_list cli_kws = {{ },{
{ { "show", "ssl", "cert", NULL }, "show ssl cert [<certfile>] : display the SSL certificates used in memory, or the details of a file", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert },
{ { "new", "ssl", "ca-file", NULL }, "new ssl ca-file <cafile> : create a new CA file to be used in a crt-list", cli_parse_new_cafile, NULL, NULL },
{ { "add", "ssl", "ca-file", NULL }, "add ssl ca-file <cafile> <payload> : add a certificate into the CA file", cli_parse_set_cafile, NULL, NULL },
{ { "set", "ssl", "ca-file", NULL }, "set ssl ca-file <cafile> <payload> : replace a CA file", cli_parse_set_cafile, NULL, NULL },
{ { "commit", "ssl", "ca-file", NULL }, "commit ssl ca-file <cafile> : commit a CA file", cli_parse_commit_cafile, cli_io_handler_commit_cafile_crlfile, cli_release_commit_cafile },
{ { "abort", "ssl", "ca-file", NULL }, "abort ssl ca-file <cafile> : abort a transaction for a CA file", cli_parse_abort_cafile, NULL, NULL },