|
From: Steffan K. <ste...@fo...> - 2020-08-12 12:36:58
|
Hi,
Couldn't resist giving this a quick look.
Feature-ACK on the patch set, but some comments on the approach:
On 12-08-2020 10:55, Arne Schwabe wrote:
> This refactors the common code between mbed SSL and OpenSSL into
> export_user_keying_material and also prepares the backend functions
> to export more than one key.
>
> Also fix checking the return value of SSL_export_keying_material
> only 1 is a sucess, -1 is also an error.
>
> Signed-off-by: Arne Schwabe <ar...@rf...>
> ---
> src/openvpn/ssl.c | 33 ++++++++++++++++++++++++-
> src/openvpn/ssl_backend.h | 18 ++++++++++++--
> src/openvpn/ssl_mbedtls.c | 22 ++++++-----------
> src/openvpn/ssl_openssl.c | 51 +++++++++++++++++++++------------------
> 4 files changed, 83 insertions(+), 41 deletions(-)
>
> diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
> index f16114c2..390114e1 100644
> --- a/src/openvpn/ssl.c
> +++ b/src/openvpn/ssl.c
> @@ -2412,6 +2412,37 @@ error:
> return false;
> }
>
> +static void
> +export_user_keying_material(struct key_state_ssl *ssl,
> + struct tls_session *session)
> +{
> + if (session->opt->ekm_size > 0)
> + {
> + unsigned int size = session->opt->ekm_size;
> + struct gc_arena gc = gc_new();
> +
> + unsigned char *ekm;
> + if ((ekm = key_state_export_keying_material(ssl, session,
> + EXPORT_KEY_USER, &gc)))
> + {
Hmm, I don't think these abstractions are right. I would argue that the
crypto backends should know as few about OpenVPN specifics as possible.
So things like "EXPORT_KEY_USER" should probably not be passed to the
backend, and instead be handled in ssl.c.
Why not give the backend a prototype like
key_state_export_keying_material(label, label_len, ekm, ekm_len)
I understand that this would mean an extra memcpy() for the mbedtls
code, but this is not performance-critical at all, right?
(In the follow-up patch, just cache the master secret and client/server
random, instead of the derived key material in the mbedtls backend, so
you can generate the export at will. For OpenSSL, this API should be
trivial.)
> + unsigned int len = (size * 2) + 2;
> +
> + const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc);
> + setenv_str(session->opt->es, "exported_keying_material", key);
> +
> + dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
> + __func__, key);
> + secure_memzero(ekm, size);
> + }
> + else
> + {
> + msg(M_WARN, "WARNING: Export keying material failed!");
> + setenv_del(session->opt->es, "exported_keying_material");
> + }
> + gc_free(&gc);
> + }
> +}
> +
> /**
> * Handle reading key data, peer-info, username/password, OCC
> * from the TLS control channel (cleartext).
> @@ -2541,7 +2572,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio
> if ((ks->authenticated > KS_AUTH_FALSE)
> && plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL))
> {
> - key_state_export_keying_material(&ks->ks_ssl, session);
> + export_user_keying_material(&ks->ks_ssl, session);
>
> if (plugin_call(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
> {
> diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
> index 7f52ab1e..8faaefd5 100644
> --- a/src/openvpn/ssl_backend.h
> +++ b/src/openvpn/ssl_backend.h
> @@ -389,18 +389,32 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl);
> void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
> const char *crl_file, bool crl_inline);
>
> +
> +/* defines the different RFC5705 that are used in OpenVPN */
Make this /** to have it turn up nicely in the doxygen.
> +enum export_key_identifier {
> + EXPORT_KEY_USER
> +};
> +
> /**
> * Keying Material Exporters [RFC 5705] allows additional keying material to be
> * derived from existing TLS channel. This exported keying material can then be
> * used for a variety of purposes.
> *
> + * Note
> + *
Note ... what?
> * @param ks_ssl The SSL channel's state info
> * @param session The session associated with the given key_state
> + * @param key The key to export.
> + * @param gc gc_arena that might be used to allocate a string
Where "a string" is "the keying material", right? Maybe just say that :)
> + * @returns The exported key material, the caller may zero the
> + * string but should not free it
> */
>
> -void
> +unsigned char*
> key_state_export_keying_material(struct key_state_ssl *ks_ssl,
> - struct tls_session *session) __attribute__((nonnull));
> + struct tls_session *session,
> + enum export_key_identifier export_key,
> + struct gc_arena *gc) __attribute__((nonnull));
>
> /**************************************************************************/
> /** @addtogroup control_tls
> diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c
> index 9c874788..8ae6ec7b 100644
> --- a/src/openvpn/ssl_mbedtls.c
> +++ b/src/openvpn/ssl_mbedtls.c
> @@ -231,24 +231,18 @@ mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned char *ms,
> }
> #endif /* HAVE_EXPORT_KEYING_MATERIAL */
>
> -void
> +unsigned char *
> key_state_export_keying_material(struct key_state_ssl *ssl,
> - struct tls_session *session)
> + struct tls_session *session,
> + enum export_key_identifier key_id,
> + struct gc_arena *gc)
> {
> - if (ssl->exported_key_material)
> + if (key_id == EXPORT_KEY_USER)
> {
> - unsigned int size = session->opt->ekm_size;
> - struct gc_arena gc = gc_new();
> - unsigned int len = (size * 2) + 2;
> -
> - const char *key = format_hex_ex(ssl->exported_key_material,
> - size, len, 0, NULL, &gc);
> - setenv_str(session->opt->es, "exported_keying_material", key);
> -
> - dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
> - __func__, key);
> - gc_free(&gc);
> + return ssl->exported_key_material;
> }
> +
> + return NULL;
> }
>
>
> diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
> index 5ba74402..1aad4f89 100644
> --- a/src/openvpn/ssl_openssl.c
> +++ b/src/openvpn/ssl_openssl.c
> @@ -158,35 +158,38 @@ tls_ctx_initialised(struct tls_root_ctx *ctx)
> return NULL != ctx->ctx;
> }
>
> -void
> +unsigned char *
> key_state_export_keying_material(struct key_state_ssl *ssl,
> - struct tls_session *session)
> + struct tls_session *session,
> + enum export_key_identifier key_id,
> + struct gc_arena *gc)
> +
> {
> - if (session->opt->ekm_size > 0)
> - {
> - unsigned int size = session->opt->ekm_size;
> - struct gc_arena gc = gc_new();
> - unsigned char *ekm = (unsigned char *) gc_malloc(size, true, &gc);
> + const char *label;
> + size_t label_size, ekm_size;
>
> - if (SSL_export_keying_material(ssl->ssl, ekm, size,
> - session->opt->ekm_label,
> - session->opt->ekm_label_size,
> - NULL, 0, 0))
> - {
> - unsigned int len = (size * 2) + 2;
> + if (key_id == EXPORT_KEY_USER)
> + {
> + label = session->opt->ekm_label;
> + label_size = session->opt->ekm_label_size;
> + ekm_size = session->opt->ekm_size;
> + }
> + else
> + {
> + ASSERT(0);
> + }
>
> - const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc);
> - setenv_str(session->opt->es, "exported_keying_material", key);
> + unsigned char *ekm = (unsigned char *) gc_malloc(ekm_size, true, gc);
>
> - dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
> - __func__, key);
> - }
> - else
> - {
> - msg(M_WARN, "WARNING: Export keying material failed!");
> - setenv_del(session->opt->es, "exported_keying_material");
> - }
> - gc_free(&gc);
> + if (SSL_export_keying_material(ssl->ssl, ekm, ekm_size, label,
> + label_size, NULL, 0, 0) == 1)
> + {
> + return ekm;
> + }
> + else
> + {
> + secure_memzero(ekm, ekm_size);
> + return NULL;
> }
> }
>
>
-Steffan.
|