From patchwork Wed Aug 12 04:01:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1381 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.27.255.7]) by backend30.mail.ord1d.rsapps.net with LMTP id EGS2G2f2M19+OwAAIUCqbw for ; Wed, 12 Aug 2020 10:02:15 -0400 Received: from proxy20.mail.iad3a.rsapps.net ([172.27.255.7]) by director11.mail.ord1d.rsapps.net with LMTP id oFv+GWf2M19JdwAAvGGmqA (envelope-from ) for ; Wed, 12 Aug 2020 10:02:15 -0400 Received: from smtp17.gate.iad3a ([172.27.255.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy20.mail.iad3a.rsapps.net with LMTP id 2P+WE2f2M18EWwAAtfLT2w ; Wed, 12 Aug 2020 10:02:15 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp17.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: 6c4c9ba2-dca4-11ea-9f59-525400723ca9-1-1 Received: from [216.105.38.7] ([216.105.38.7:49484] helo=lists.sourceforge.net) by smtp17.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 6F/5A-22810-666F33F5; Wed, 12 Aug 2020 10:02:14 -0400 Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1k5rJx-0005Om-QO; Wed, 12 Aug 2020 14:01:37 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1k5rJw-0005Of-3f for openvpn-devel@lists.sourceforge.net; Wed, 12 Aug 2020 14:01:36 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=piiqijoZJ7A4nLC+llpddWsUfy/p5Gtlt9V98mFtQJc=; b=ZEW5tT3l1NrpdCt2Jv83BWB88I 5fHzdaRGY+LB2l/7OvfL5ihxawYF9WHWIqd/BwGiU7BJmWCqHEHEUgf1k35JVpyhB0QNdCZtQwbpg w7xrBEd92Z1HY5bb72jMMRwVXiYqF4O79gTMgDs1oAk5c9s/VLpxIqV2LhPbby2IMJGs=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:MIME-Version: Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=piiqijoZJ7A4nLC+llpddWsUfy/p5Gtlt9V98mFtQJc=; b=IeYcd1jDostQDCLj/JQQmvk9i/ aIF3Li+bwHzGl4hGOvmo9ghoNWFqanSQ05q+rRxA6fEFgIHFdzY5cEC6nmqQ/3eWIs7s0E/gCo8ym KGHnA84mvccYGb/fOwD+4WHXkFHibcVrm4YEvBFQH3tDwFByVP8CaVG/c2Mo/Azjlpes=; Received: from mail.blinkt.de ([192.26.174.232]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1k5rJu-003Gs5-4h for openvpn-devel@lists.sourceforge.net; Wed, 12 Aug 2020 14:01:36 +0000 Received: from kamera.blinkt.de ([2001:638:502:390:20c:29ff:fec8:535c]) by mail.blinkt.de with smtp (Exim 4.94 (FreeBSD)) (envelope-from ) id 1k5rJg-000Bwj-Jv for openvpn-devel@lists.sourceforge.net; Wed, 12 Aug 2020 16:01:20 +0200 Received: (nullmailer pid 21332 invoked by uid 10006); Wed, 12 Aug 2020 14:01:20 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Wed, 12 Aug 2020 16:01:18 +0200 Message-Id: <20200812140120.21287-1-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: rfc2549.org] 0.0 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different 0.0 SPF_NONE SPF: sender does not publish an SPF Record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record X-Headers-End: 1k5rJu-003Gs5-4h Subject: [Openvpn-devel] [PATCH v2 1/3] Refactor key_state_export_keying_material functions X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox 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 Patch V2: Cache secrets for mbed TLS instead generating all ekms in the call back function Signed-off-by: Arne Schwabe --- src/openvpn/ssl.c | 36 ++++++++++++++++++- src/openvpn/ssl_backend.h | 16 +++++++-- src/openvpn/ssl_mbedtls.c | 73 ++++++++++++++++++++------------------- src/openvpn/ssl_mbedtls.h | 12 +++++-- src/openvpn/ssl_openssl.c | 43 +++++++++-------------- 5 files changed, 113 insertions(+), 67 deletions(-) diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index f16114c2..3fcaa25f 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -2412,6 +2412,40 @@ 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(session, + session->opt->ekm_label, + session->opt->ekm_label_size, + session->opt->ekm_size, + &gc))) + { + 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 +2575,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..eb7d541d 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -394,13 +394,23 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, * derived from existing TLS channel. This exported keying material can then be * used for a variety of purposes. * + * * @param ks_ssl The SSL channel's state info * @param session The session associated with the given key_state + * @param label The label to use when exporting the key + * @param label_size The size of the label to use when exporting the key + * + * @param gc gc_arena that might be used to allocate the string + * returned + * @returns The exported key material, the caller may zero the + * string but should not free it */ -void -key_state_export_keying_material(struct key_state_ssl *ks_ssl, - struct tls_session *session) __attribute__((nonnull)); +unsigned char* +key_state_export_keying_material(struct tls_session *session, + const char* label, size_t label_size, + size_t ekm_size, + 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..4287b59e 100644 --- a/src/openvpn/ssl_mbedtls.c +++ b/src/openvpn/ssl_mbedtls.c @@ -206,51 +206,54 @@ mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned char *ms, { struct tls_session *session = p_expkey; struct key_state_ssl *ks_ssl = &session->key[KS_PRIMARY].ks_ssl; - unsigned char client_server_random[64]; + struct tls_key_cache *cache = &ks_ssl->tls_key_cache; - ks_ssl->exported_key_material = gc_malloc(session->opt->ekm_size, - true, NULL); + static_assert(sizeof(ks_ssl->ctx->session->master) + == sizeof(cache->master_secret), "master size mismatch"); - memcpy(client_server_random, client_random, 32); - memcpy(client_server_random + 32, server_random, 32); + memcpy(cache->client_server_random, client_random, 32); + memcpy(cache->client_server_random + 32, server_random, 32); + memcpy(cache->master_secret, ms, sizeof(cache->master_secret)); + cache->tls_prf_type = tls_prf_type; - const size_t ms_len = sizeof(ks_ssl->ctx->session->master); - int ret = mbedtls_ssl_tls_prf(tls_prf_type, ms, ms_len, - session->opt->ekm_label, client_server_random, - sizeof(client_server_random), ks_ssl->exported_key_material, - session->opt->ekm_size); - - if (!mbed_ok(ret)) - { - secure_memzero(ks_ssl->exported_key_material, session->opt->ekm_size); - } - - secure_memzero(client_server_random, sizeof(client_server_random)); - - return ret; + return true; } -#endif /* HAVE_EXPORT_KEYING_MATERIAL */ -void -key_state_export_keying_material(struct key_state_ssl *ssl, - struct tls_session *session) +unsigned char * +key_state_export_keying_material(struct tls_session *session, + const char* label, size_t label_size, + size_t ekm_size, + struct gc_arena *gc) { - if (ssl->exported_key_material) + ASSERT(strlen(label) == label_size); + + struct tls_key_cache *cache = &session->key[KS_PRIMARY].ks_ssl.tls_key_cache; + + /* If the type is NONE, we either have no cached secrets or + * there is no PRF, in both cases we cannot generate key material */ + if (cache->tls_prf_type == MBEDTLS_SSL_TLS_PRF_NONE) { - unsigned int size = session->opt->ekm_size; - struct gc_arena gc = gc_new(); - unsigned int len = (size * 2) + 2; + return NULL; + } - const char *key = format_hex_ex(ssl->exported_key_material, - 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); + int ret = mbedtls_ssl_tls_prf(cache->tls_prf_type, cache->master_secret, + sizeof(cache->master_secret), + label, cache->client_server_random, + sizeof(cache->client_server_random), + ekm, ekm_size); - dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s", - __func__, key); - gc_free(&gc); + if (mbed_ok(ret)) + { + return ekm; + } + else + { + secure_memzero(ekm, session->opt->ekm_size); + return NULL; } } - +#endif /* HAVE_EXPORT_KEYING_MATERIAL */ bool tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) @@ -1178,7 +1181,7 @@ key_state_ssl_free(struct key_state_ssl *ks_ssl) { if (ks_ssl) { - free(ks_ssl->exported_key_material); + CLEAR(ks_ssl->tls_key_cache); if (ks_ssl->ctx) { diff --git a/src/openvpn/ssl_mbedtls.h b/src/openvpn/ssl_mbedtls.h index 0525134f..17aae551 100644 --- a/src/openvpn/ssl_mbedtls.h +++ b/src/openvpn/ssl_mbedtls.h @@ -82,6 +82,15 @@ struct external_context { void *sign_ctx; }; +/** struct to cache TLS secrets for keying material exporter (RFC 5705). + * The constants (64 and 48) are inherent to TLS version and + * the whole keying material export will likely change when they change */ +struct tls_key_cache { + unsigned char client_server_random[64]; + mbedtls_tls_prf_types tls_prf_type; + unsigned char master_secret[48]; +}; + /** * Structure that wraps the TLS context. Contents differ depending on the * SSL library used. @@ -114,8 +123,7 @@ struct key_state_ssl { mbedtls_ssl_context *ctx; /**< mbedTLS connection context */ bio_ctx *bio_ctx; - /** Keying material exporter cache (RFC 5705). */ - uint8_t *exported_key_material; + struct tls_key_cache tls_key_cache; }; diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 5ba74402..f52c7c39 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -158,35 +158,26 @@ tls_ctx_initialised(struct tls_root_ctx *ctx) return NULL != ctx->ctx; } -void -key_state_export_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 = (unsigned char *) gc_malloc(size, true, &gc); +unsigned char* +key_state_export_keying_material(struct tls_session *session, + const char* label, size_t label_size, + size_t ekm_size, + struct gc_arena *gc) - 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; +{ + unsigned char *ekm = (unsigned char *) gc_malloc(ekm_size, true, gc); - const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc); - setenv_str(session->opt->es, "exported_keying_material", key); + SSL* ssl = session->key[KS_PRIMARY].ks_ssl.ssl; - 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, ekm, ekm_size, label, + label_size, NULL, 0, 0) == 1) + { + return ekm; + } + else + { + secure_memzero(ekm, ekm_size); + return NULL; } }