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/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 },