On Tue, Dec 20, 2022 at 11:51:33AM -0500, Xavier Simonart wrote:
> stream_ssl_set_key_and_cert is supposed to, whenever either the certificate or
> the private key file changes, re-read both of them.
> It was re-reading them only when both changed.
> So, if, for instance, certificate was changed a few seconds only after
> changing
> the key, the new key and certificate were never applied.
>
> A few patches have been proposed on similar issues.
> This patch tries to take into account the inputs/comments from them i.e.
> - avoid crash on NULL private key and valid certificate
> (from d5d0c94551b6 ("stream-ssl: Fix crash on NULL private key and valid
> certificate."))
> - avoid breaking setup while the second component is not updated
> (from
> https://patchwork.ozlabs.org/project/openvswitch/patch/[email protected]/
> - update key and cert, if they are valid.
>
> Fixes: d5d0c94551b6 ("stream-ssl: Fix crash on NULL private key and valid
> certificate.")
>
> Signed-off-by: Xavier Simonart <[email protected]>
Hi,
I'm not sure if this patch is still in-flight.
Nonetheless, I've provided some review below.
> ---
> v2: fix 'rl' shadows an earlier one
> v3: fix uggly memory leak
> ---
> lib/stream-ssl.c | 120 +++++++++++++++++++++++++++++++-----------
> tests/ovsdb-server.at | 36 +++++++++++++
> 2 files changed, 126 insertions(+), 30 deletions(-)
>
> diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
> index 62da9febb..f56cb1ec7 100644
> --- a/lib/stream-ssl.c
> +++ b/lib/stream-ssl.c
> @@ -76,6 +76,12 @@ enum session_type {
> SERVER
> };
>
> +enum ssl_update_result {
> + SSL_UPDATE_ERROR,
> + SSL_NOT_UPDATED,
> + SSL_UPDATED
> +};
> +
...
> @@ -1224,16 +1248,52 @@ stream_ssl_set_certificate_file(const char *file_name)
> *
> * This function avoids both problems by, whenever either the certificate or
> * the private key file changes, re-reading both of them, in the correct
> order.
> + *
> + * If either the certificate or the private key file failed to update (e.g.
> + * empty file), then those files are not applied as this used to cause segv.
> + * This function also avoids breaking setup while the second component is not
> + * updated.
> */
> void
> stream_ssl_set_key_and_cert(const char *private_key_file,
> const char *certificate_file)
> {
> - if (update_ssl_config(&private_key, private_key_file)
> - && update_ssl_config(&certificate, certificate_file)) {
> + int priv_key_updated = update_ssl_config(&private_key, private_key_file);
enum ssl_update_result might be a better type for priv_key_updated.
> + SSL_CTX *tmp_ctx = NULL;
> +
> + if (((priv_key_updated == SSL_UPDATED) &&
> + (update_ssl_config(&certificate,certificate_file)
> + != SSL_UPDATE_ERROR)) ||
> + ((priv_key_updated != SSL_UPDATE_ERROR) &&
> + (update_ssl_config(&certificate, certificate_file) ==
> SSL_UPDATED))) {
This is a but hard to parse mentally,
so I wrote it out for myself.
1st pass:
priv_key_updated and cert_updated => result
---------------- ------------ ------
1. SSL_UPDATED SSL_UPDATED perform update
2. SSL_UPDATED SSL_NOT_UPDATED perform update
all other cases go to 2nd chance draw
2nd chance draw:
priv_key_updated and cert_updated => action
---------------- ------------ ------
2. (SSL_UPDATED SSL_UPDATED perform update) [*]
SSL_UPDATED SSL_UPDATE_ERROR do not perform update
3. SSL_NOT_UPDATED SSL_UPDATED perform update
SSL_NOT_UPDATED SSL_NOT_UPDATED do not perform update
SSL_NOT_UPDATED SSL_UPDATE_ERROR do not perform update
SSL_UPDATE_ERROR * do not perform update
[*] Not evaluated, as alreaady evaluated in first pass
This leads to the following truth table
priv_key_updated and cert_updated => result
---------------- ------------ ------
1. SSL_UPDATED SSL_UPDATED perform update
2. SSL_UPDATED SSL_NOT_UPDATED perform update
3. SSL_NOT_UPDATED SSL_UPDATED perform update
all othere cases do not perform update
Or in other words, update if:
* There is no error
* At least one SSL_UPDATED
Maybe this is a bit easier on the eyes.
It also avoids the case where update_ssl_config() is called
twice for the certificate.
* completely untested! *
enum ssl_update_result key_updated;
bool do_update = false;
priv_key_updated = update_ssl_config(&private_key, private_key_file);
if (priv_key_updated != SSL_UPDATE_ERROR) {
enum cert_updated;
cert_updated = update_ssl_config(&certificate,
certificate_file);
do_update = priv_key_updated == SSL_UPDATED ||
cert_updated == SSL_UPDATED;
}
if (do_update) {
> + tmp_ctx = new_ssl_ctx();
> + if (tmp_ctx == NULL) {
> + goto error;
> + }
> + if (SSL_CTX_use_certificate_file(tmp_ctx, certificate_file,
> + SSL_FILETYPE_PEM) != 1) {
> + VLOG_ERR_RL(&rl, "SSL_use_certificate_file: %s",
> + ERR_error_string(ERR_get_error(), NULL));
> + goto error;
> + }
> + if (SSL_CTX_use_PrivateKey_file(tmp_ctx, private_key_file,
> + SSL_FILETYPE_PEM) != 1) {
> + VLOG_ERR_RL(&rl, "SSL_use_PrivateKey_file: %s",
> + ERR_error_string(ERR_get_error(), NULL));
> + goto error;
> + }
> + if (SSL_CTX_check_private_key(tmp_ctx) != 1) {
> + VLOG_ERR_RL(&rl, "SSL_check_private_key: %s",
> + ERR_error_string(ERR_get_error(), NULL));
> + goto error;
> + }
> stream_ssl_set_certificate_file__(certificate_file);
> stream_ssl_set_private_key_file__(private_key_file);
> }
> +error:
> + if (tmp_ctx) {
The check on tmp_ctx is not needed,
as SSL_CTX_free does nothing when passed NULL.
Ref: man SSL_CTX_free(3ssl)
> + SSL_CTX_free(tmp_ctx);
> + }
> }
>
> /* Sets SSL ciphers based on string input. Aborts with an error message
> @@ -1427,7 +1487,7 @@ stream_ssl_set_ca_cert_file__(const char *file_name,
> {
> struct stat s;
>
> - if (!update_ssl_config(&ca_cert, file_name) && !force) {
> + if ((update_ssl_config(&ca_cert, file_name) != SSL_UPDATED) && !force) {
> return;
> }
>
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index c7b2fe3ae..c2b80748b 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -1311,6 +1311,42 @@ AT_CHECK([test $(get_memory_value atoms) -eq
> $initial_db_atoms])
> OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> AT_CLEANUP
>
> +
> +AT_SETUP([ssl certificate update])
> +AT_KEYWORDS([ovsdb server ssl])
> +AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
> +PKIDIR=$abs_top_builddir/tests
> +AT_CHECK([ovsdb-tool create db $abs_top_srcdir/vswitchd/vswitch.ovsschema],
> [0], [stdout], [ignore])
> +on_exit 'kill `cat *.pid`'
> +
> +# ssl certificate update with empty private_key
> +AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile
> --private-key=db:Open_vSwitch,SSL,private_key
> --certificate=db:Open_vSwitch,SSL,certificate
> --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --remote=punix:./db.sock
> --remote=db:Open_vSwitch,Open_vSwitch,manager_options db], [0], [ignore],
> [ignore])
> +AT_CHECK([ovs-vsctl --no-wait init])
> +AT_CHECK([ovs-vsctl --no-wait set-ssl pkey.key cert.cert ca.cert])
> +AT_CHECK([ovs-vsctl --no-wait set SSL . private_key='""'])
> +AT_CHECK([ovs-vsctl --no-wait set SSL . certificate='cert.new'])
> +
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +# Use wrong key/cert combination
> +cp $PKIDIR/testpki-privkey2.pem key.pem
> +cp $PKIDIR/testpki-cert.pem cert.pem
> +AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile
> --private-key=key.pem --certificate=cert.pem
> --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 db], [0],
> [ignore], [ignore])
> +PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
> +OVS_WAIT_UNTIL([grep "ERR|SSL_use_PrivateKey_file" ovsdb-server.log | grep
> mismatch])
> +
> +# Use good combination and try to connect
> +cp $PKIDIR/testpki-cert2.pem cert.pem
> +AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem
> --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem
> wait ssl:127.0.0.1:$SSL_PORT _Server connected > ovsdb-client.stdout 2>
> ovsdb-client.stderr])
> +
> +# Set wrong key/cert combination
> +cp $PKIDIR/testpki-privkey.pem key.pem
> +# Check that we can still connect
> +AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem
> --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem
> wait ssl:127.0.0.1:$SSL_PORT _Server connected > ovsdb-client.stdout 2>
> ovsdb-client.stderr])
> +
> +OVSDB_SERVER_SHUTDOWN
> +AT_CLEANUP
> +
> AT_BANNER([OVSDB -- ovsdb-server transactions (SSL IPv4 sockets)])
>
> # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
> --
> 2.31.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev