diff --git a/doc/management.txt b/doc/management.txt index 6aeb08d38..e88b24349 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1679,6 +1679,21 @@ add 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 + 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 add ssl crt-list Add an certificate in a crt-list. It can also be used for directories since @@ -1821,8 +1836,8 @@ commit ssl ca-file 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 Commit a temporary SSL certificate update transaction. @@ -2127,7 +2142,7 @@ httpclient new ssl ca-file 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 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 - 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 ada30f791..085c5c042 100644 --- a/include/haproxy/ssl_ckch.h +++ b/include/haproxy/ssl_ckch.h @@ -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); diff --git a/reg-tests/ssl/new_del_ssl_cafile.vtc b/reg-tests/ssl/new_del_ssl_cafile.vtc index 4b045715d..2123fb030 100644 --- a/reg-tests/ssl/new_del_ssl_cafile.vtc +++ b/reg-tests/ssl/new_del_ssl_cafile.vtc @@ -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.*" diff --git a/reg-tests/ssl/set_ssl_cafile.vtc b/reg-tests/ssl/set_ssl_cafile.vtc index bda620f4e..b93e78acf 100644 --- a/reg-tests/ssl/set_ssl_cafile.vtc +++ b/reg-tests/ssl/set_ssl_cafile.vtc @@ -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" - } diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index 3f4906737..d531d39ae 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -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 [] : 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 : 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 : add a certificate into the CA file", cli_parse_set_cafile, NULL, NULL }, { { "set", "ssl", "ca-file", NULL }, "set ssl ca-file : replace a CA file", cli_parse_set_cafile, NULL, NULL }, { { "commit", "ssl", "ca-file", NULL }, "commit ssl ca-file : 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 : abort a transaction for a CA file", cli_parse_abort_cafile, NULL, NULL },