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".
diff --git a/doc/management.txt b/doc/management.txt
index 6aeb08d..e88b243 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1679,6 +1679,21 @@
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 @@
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 @@
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 @@
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" | \
diff --git a/include/haproxy/ssl_ckch.h b/include/haproxy/ssl_ckch.h
index ada30f7..085c5c0 100644
--- a/include/haproxy/ssl_ckch.h
+++ b/include/haproxy/ssl_ckch.h
@@ -63,6 +63,7 @@
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);
diff --git a/reg-tests/ssl/new_del_ssl_cafile.vtc b/reg-tests/ssl/new_del_ssl_cafile.vtc
index 4b04571..2123fb0 100644
--- a/reg-tests/ssl/new_del_ssl_cafile.vtc
+++ b/reg-tests/ssl/new_del_ssl_cafile.vtc
@@ -81,6 +81,26 @@
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.*"
diff --git a/reg-tests/ssl/set_ssl_cafile.vtc b/reg-tests/ssl/set_ssl_cafile.vtc
index bda620f..b93e78a 100644
--- a/reg-tests/ssl/set_ssl_cafile.vtc
+++ b/reg-tests/ssl/set_ssl_cafile.vtc
@@ -138,7 +138,9 @@
# 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" -
}
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c
index 3f49067..d531d39 100644
--- a/src/ssl_ckch.c
+++ b/src/ssl_ckch.c
@@ -1076,6 +1076,67 @@
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 @@
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 @@
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 @@
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 @@
{ { "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 },