From patchwork Thu Jul 28 05:20:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 2611 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director9.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id kFs7Lmep4mKlewAAIUCqbw (envelope-from ) for ; Thu, 28 Jul 2022 11:21:11 -0400 Received: from proxy18.mail.ord1d.rsapps.net ([172.30.191.6]) by director9.mail.ord1d.rsapps.net with LMTP id OOBKLmep4mICfwAAalYnBA (envelope-from ) for ; Thu, 28 Jul 2022 11:21:11 -0400 Received: from smtp40.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy18.mail.ord1d.rsapps.net with LMTPS id MNMALmep4mK8aQAATCaURg (envelope-from ) for ; Thu, 28 Jul 2022 11:21:11 -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: smtp40.gate.ord1c.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=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: e8aed24c-0e88-11ed-b592-525400b3abc9-1-1 Received: from [216.105.38.7] ([216.105.38.7:60980] helo=lists.sourceforge.net) by smtp40.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 4A/9E-02554-669A2E26; Thu, 28 Jul 2022 11:21:10 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1oH5Iv-0002Dt-42; Thu, 28 Jul 2022 15:20:02 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1oH5It-0002DZ-OB for openvpn-devel@lists.sourceforge.net; Thu, 28 Jul 2022 15:20:00 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=mOC7r1ZLPxM2x4FyXC7+nqi9UuK/q+RB1c82jrAySsQ=; b=EkBSja6aVTMaTULoRDNj6V+rPk PCKMlMa8FYQIsMkiJOS/RsXXAR1MOjtbbjvMmQOK+j/Vxs47HkaHwxvySWTAdcJn0Nhx6CQn6Erwr Pt1zZ/qdAxOfPjkbyLEZs2LLISE68jYEZRhEJ5TOeNKZlvMwTGSv4eJcPb+3OlnUEHSs=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=mOC7r1ZLPxM2x4FyXC7+nqi9UuK/q+RB1c82jrAySsQ=; b=eKf/Yr9xkkvGeoKHkrIfQl+9ww agw96WywmARml93Bh6YUUonHuLN1P5uVBilUuGPNlqDGBVuwsGqC7Edjc6u18ww39iDuqrMGg+5y4 2CiBWi2qw8eG9XyQ09m7iBBbnMFQG4fOq0iA70Id6azBQLqcjWq28LVeElhvzCtA5hHo=; Received: from s2.neomailbox.net ([5.148.176.60]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1oH5Is-0003Jw-8S for openvpn-devel@lists.sourceforge.net; Thu, 28 Jul 2022 15:20:00 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Thu, 28 Jul 2022 17:20:12 +0200 Message-Id: <20220728152012.18643-1-a@unstable.cc> In-Reply-To: <20220720123249.909-1-a@unstable.cc> References: MIME-Version: 1.0 X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: The ovpn-dco kernel module needs to be informed about the keys to be used to encrypt/decrypt data traffic to/from a peer. Configure keys in DCO right afte they are generated by the SSL code, to avoid keeping them in memory longer than needed. Content analysis details: (0.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1oH5Is-0003Jw-8S Subject: [Openvpn-devel] [PATCH v3 09/25] dco: configure keys in DCO right after generating them 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: , Cc: Antonio Quartulli Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox The ovpn-dco kernel module needs to be informed about the keys to be used to encrypt/decrypt data traffic to/from a peer. Configure keys in DCO right afte they are generated by the SSL code, to avoid keeping them in memory longer than needed. Signed-off-by: Antonio Quartulli Acked-By: Arne Schwabe --- Changes from v2: * re-enable explicit-exit-notification in every case * add check to drop packet when attempting to send data packet and DCO is enabled (print warning as well) Changes from v1: * adapt to new member name dco_enabled * invert if blocks and condition in init_key_contexts() [and use 'else'] * fix comment for init_key_contexts() * disable explicit-exit-notification in mutate_ce() when DCO is enabled --- src/openvpn/dco.c | 55 ++++++++++++++++++++++++++++++++ src/openvpn/dco.h | 29 +++++++++++++++++ src/openvpn/forward.c | 7 +++++ src/openvpn/init.c | 11 ++++--- src/openvpn/multi.c | 2 +- src/openvpn/options.c | 1 - src/openvpn/ssl.c | 73 ++++++++++++++++++++++++++++++++----------- src/openvpn/ssl.h | 7 +++-- 8 files changed, 158 insertions(+), 27 deletions(-) diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c index b3fd135f..0471e4d0 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -33,12 +33,67 @@ #if defined(ENABLE_DCO) #include "syshead.h" +#include "crypto.h" #include "dco.h" +#include "errlevel.h" #include "networking.h" +#include "openvpn.h" #include "options.h" +#include "ssl_common.h" #include "ssl_ncp.h" #include "tun.h" +static int +dco_install_key(struct tls_multi *multi, struct key_state *ks, + const uint8_t *encrypt_key, const uint8_t *encrypt_iv, + const uint8_t *decrypt_key, const uint8_t *decrypt_iv, + const char *ciphername) + +{ + msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d", __func__, multi->peer_id, + ks->key_id); + + /* Install a key in the PRIMARY slot only when no other key exist. + * From that moment on, any new key will be installed in the SECONDARY + * slot and will be promoted to PRIMARY when userspace says so (a swap + * will be performed in that case) + */ + dco_key_slot_t slot = OVPN_KEY_SLOT_PRIMARY; + if (multi->dco_keys_installed > 0) + { + slot = OVPN_KEY_SLOT_SECONDARY; + } + + int ret = dco_new_key(multi->dco, multi->peer_id, ks->key_id, slot, + encrypt_key, encrypt_iv, + decrypt_key, decrypt_iv, + ciphername); + if ((ret == 0) && (multi->dco_keys_installed < 2)) + { + multi->dco_keys_installed++; + ks->dco_status = (slot == OVPN_KEY_SLOT_PRIMARY) ? DCO_INSTALLED_PRIMARY : + DCO_INSTALLED_SECONDARY; + } + + return ret; +} + +int +init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, + const struct key2 *key2, int key_direction, + const char *ciphername, bool server) +{ + struct key_direction_state kds; + key_direction_state_init(&kds, key_direction); + + return dco_install_key(multi, ks, + key2->keys[kds.out_key].cipher, + key2->keys[(int)server].hmac, + key2->keys[kds.in_key].cipher, + key2->keys[1 - (int)server].hmac, + ciphername); +} + static bool dco_check_option_conflict_platform(int msglevel, const struct options *o) { diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h index 063e5028..1692f5c3 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -35,7 +35,10 @@ * order problems) */ struct event_set; +struct key2; +struct key_state; struct options; +struct tls_multi; struct tuntap; #define DCO_DEFAULT_METRIC 200 @@ -111,6 +114,24 @@ int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf); */ void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg); +/** + * Install the key material in DCO for the specified peer. + * The key is installed in the primary slot when no other key was yet installed. + * Any subsequent invocation will install the key in the secondary slot. + * + * @param multi the TLS context of the current instance + * @param ks the state of the key being installed + * @param key2 the container for the raw key material + * @param key_direction the key direction to be used to extract the material + * @param ciphername the name of the cipher to use the key with + * @param server whether we are running on a server instance or not + * + * @return 0 on success or a negative error code otherwise + */ +int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, + const struct key2 *key2, int key_direction, + const char *ciphername, bool server); + #else /* if defined(ENABLE_DCO) */ typedef void *dco_context_t; @@ -163,5 +184,13 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { } +static inline int +init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, + const struct key2 *key2, int key_direction, + const char *ciphername, bool server) +{ + return 0; +} + #endif /* defined(ENABLE_DCO) */ #endif /* ifndef DCO_H */ diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 6afe152b..28f3c088 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -530,6 +530,13 @@ encrypt_sign(struct context *c, bool comp_frag) const uint8_t *orig_buf = c->c2.buf.data; struct crypto_options *co = NULL; + if (dco_enabled(&c->options)) + { + msg(M_WARN, "Attempting to send data packet while data channel offload is in use. " + "Dropping packet"); + c->c2.buf.len = 0; + } + /* * Drop non-TLS outgoing packet if client-connect script/plugin * has not yet succeeded. In non-TLS tls_multi mode is not defined diff --git a/src/openvpn/init.c b/src/openvpn/init.c index b6e1707f..338d797b 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2226,8 +2226,9 @@ do_deferred_p2p_ncp(struct context *c) } #endif - if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame, - frame_fragment, get_link_socket_info(c))) + if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options, + &c->c2.frame, frame_fragment, + get_link_socket_info(c))) { msg(D_TLS_ERRORS, "ERROR: failed to set crypto cipher"); return false; @@ -2340,8 +2341,10 @@ do_deferred_options(struct context *c, const unsigned int found) #endif struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; - if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame, - frame_fragment, get_link_socket_info(c))) + if (!tls_session_update_crypto_params(c->c2.tls_multi, session, + &c->options, &c->c2.frame, + frame_fragment, + get_link_socket_info(c))) { msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options"); return false; diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index ba2f6d58..c72575ae 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -2286,7 +2286,7 @@ multi_client_generate_tls_keys(struct context *c) } #endif struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; - if (!tls_session_update_crypto_params(session, &c->options, + if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options, &c->c2.frame, frame_fragment, get_link_socket_info(c))) { diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 11919f6d..3eae14a7 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -3193,7 +3193,6 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) msg(M_WARN, "NOTICE: --explicit-exit-notify ignored for --proto tcp"); ce->explicit_exit_notification = 0; } - } #ifdef _WIN32 diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 24d7f3f4..fc5a8587 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -63,6 +63,7 @@ #include "ssl_util.h" #include "auth_token.h" #include "mss.h" +#include "dco.h" #include "memdbg.h" @@ -1429,21 +1430,48 @@ openvpn_PRF(const uint8_t *secret, } static void -init_key_contexts(struct key_ctx_bi *key, +init_key_contexts(struct key_state *ks, + struct tls_multi *multi, const struct key_type *key_type, bool server, - struct key2 *key2) + struct key2 *key2, + bool dco_enabled) { + struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi; + /* Initialize key contexts */ int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL; - init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel"); - /* Initialize implicit IVs */ - key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac, - MAX_HMAC_KEY_LENGTH); - key_ctx_update_implicit_iv(&key->decrypt, key2->keys[1 - (int)server].hmac, - MAX_HMAC_KEY_LENGTH); + if (dco_enabled) + { + if (key->encrypt.hmac) + { + msg(M_FATAL, "FATAL: DCO does not support --auth"); + } + + int ret = init_key_dco_bi(multi, ks, key2, key_direction, + key_type->cipher, server); + if (ret < 0) + { + msg(M_FATAL, "Impossible to install key material in DCO: %s", + strerror(-ret)); + } + /* encrypt/decrypt context are unused with DCO */ + CLEAR(key->encrypt); + CLEAR(key->decrypt); + key->initialized = true; + } + else + { + init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel"); + /* Initialize implicit IVs */ + key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac, + MAX_HMAC_KEY_LENGTH); + key_ctx_update_implicit_iv(&key->decrypt, + key2->keys[1 - (int)server].hmac, + MAX_HMAC_KEY_LENGTH); + } } static bool @@ -1519,9 +1547,10 @@ generate_key_expansion_openvpn_prf(const struct tls_session *session, struct key * master key. */ static bool -generate_key_expansion(struct key_ctx_bi *key, +generate_key_expansion(struct tls_multi *multi, struct key_state *ks, struct tls_session *session) { + struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi; bool ret = false; struct key2 key2; @@ -1562,7 +1591,9 @@ generate_key_expansion(struct key_ctx_bi *key, goto exit; } } - init_key_contexts(key, &session->opt->key_type, server, &key2); + + init_key_contexts(ks, multi, &session->opt->key_type, server, &key2, + session->opt->dco_enabled); ret = true; exit: @@ -1594,7 +1625,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) * can thus be called only once per session. */ bool -tls_session_generate_data_channel_keys(struct tls_session *session) +tls_session_generate_data_channel_keys(struct tls_multi *multi, + struct tls_session *session) { bool ret = false; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ @@ -1607,7 +1639,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session) ks->crypto_options.flags = session->opt->crypto_flags; - if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, session)) + if (!generate_key_expansion(multi, ks, session)) { msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed"); goto cleanup; @@ -1625,8 +1657,10 @@ cleanup: } bool -tls_session_update_crypto_params_do_work(struct tls_session *session, - struct options *options, struct frame *frame, +tls_session_update_crypto_params_do_work(struct tls_multi *multi, + struct tls_session *session, + struct options *options, + struct frame *frame, struct frame *frame_fragment, struct link_socket_info *lsi) { @@ -1669,11 +1703,12 @@ tls_session_update_crypto_params_do_work(struct tls_session *session, frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms"); } - return tls_session_generate_data_channel_keys(session); + return tls_session_generate_data_channel_keys(multi, session); } bool -tls_session_update_crypto_params(struct tls_session *session, +tls_session_update_crypto_params(struct tls_multi *multi, + struct tls_session *session, struct options *options, struct frame *frame, struct frame *frame_fragment, struct link_socket_info *lsi) @@ -1695,8 +1730,8 @@ tls_session_update_crypto_params(struct tls_session *session, /* Import crypto settings that might be set by pull/push */ session->opt->crypto_flags |= options->data_channel_crypto_flags; - return tls_session_update_crypto_params_do_work(session, options, frame, - frame_fragment, lsi); + return tls_session_update_crypto_params_do_work(multi, session, options, + frame, frame_fragment, lsi); } @@ -3092,7 +3127,7 @@ tls_multi_process(struct tls_multi *multi, /* Session is now fully authenticated. * tls_session_generate_data_channel_keys will move ks->state * from S_ACTIVE to S_GENERATED_KEYS */ - if (!tls_session_generate_data_channel_keys(session)) + if (!tls_session_generate_data_channel_keys(multi, session)) { msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed"); ks->authenticated = KS_AUTH_FALSE; diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index c8802707..76b1b674 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -426,6 +426,7 @@ void tls_update_remote_addr(struct tls_multi *multi, * channel keys based on the supplied options. Does nothing if keys are already * generated. * + * @param multi The TLS object for this instance. * @param session The TLS session to update. * @param options The options to use when updating session. * @param frame The frame options for this session (frame overhead is @@ -436,7 +437,8 @@ void tls_update_remote_addr(struct tls_multi *multi, * * @return true if updating succeeded or keys are already generated, false otherwise. */ -bool tls_session_update_crypto_params(struct tls_session *session, +bool tls_session_update_crypto_params(struct tls_multi *multi, + struct tls_session *session, struct options *options, struct frame *frame, struct frame *frame_fragment, @@ -551,7 +553,8 @@ show_available_tls_ciphers(const char *cipher_list, * can thus be called only once per session. */ bool -tls_session_generate_data_channel_keys(struct tls_session *session); +tls_session_generate_data_channel_keys(struct tls_multi *multi, + struct tls_session *session); /** * Load ovpn.xkey provider used for external key signing