From patchwork Thu Jul 28 09:35:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 2612 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director15.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id EGuTMknl4mInNAAAIUCqbw (envelope-from ) for ; Thu, 28 Jul 2022 15:36:41 -0400 Received: from proxy19.mail.ord1d.rsapps.net ([172.30.191.6]) by director15.mail.ord1d.rsapps.net with LMTP id KHc9Mknl4mIaWgAAIcMcQg (envelope-from ) for ; Thu, 28 Jul 2022 15:36:41 -0400 Received: from smtp32.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy19.mail.ord1d.rsapps.net with LMTPS id kC/XMUnl4mJcXgAAyH2SIw (envelope-from ) for ; Thu, 28 Jul 2022 15:36:41 -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: smtp32.gate.ord1d.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: 9a8f7976-0eac-11ed-8e0b-52540099eaf5-1-1 Received: from [216.105.38.7] ([216.105.38.7:48238] helo=lists.sourceforge.net) by smtp32.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 9F/22-18540-945E2E26; Thu, 28 Jul 2022 15:36:41 -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.94.2) (envelope-from ) id 1oH9IJ-0004zJ-7f; Thu, 28 Jul 2022 19:35:38 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1oH9II-0004zD-Ac for openvpn-devel@lists.sourceforge.net; Thu, 28 Jul 2022 19:35:37 +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=xNLheKHI4RJ5TktsjezJh2crtsiaxa7K2xevYwBzpkM=; b=K040yZqZ3EhP6kRzplVBWHHDCX CYkZz1gcCPX0DstTTXMPNXsugwkAIEq8c7IeCN20VW6MiaFA0e+u+NwkxG3Dred9ojWo2VO4TRZkK qHO02H+J8hvXkEPxUJquitXwViC/QMHnwBK34r1OEWJGqIJKKs4QtCPLbw7b8Un8Co38=; 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=xNLheKHI4RJ5TktsjezJh2crtsiaxa7K2xevYwBzpkM=; b=SIwrwU14BsCxi+afHOzldjoyHh +8cUshc+B0hZ5ytGfaA69vsrXb+HHawsX902S5C3U8NIunfTffN7BxFWchY49kO3ZWs8PFfQ5sXlp KRJ6RQDi3z9fRBqcabLTN4jLEjDUH2WcITBw1Kgx6JzSeT5SG474Tg0AJCAGXNBECam0=; Received: from s2.neomailbox.net ([5.148.176.60]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1oH9IB-00DHXc-Tv for openvpn-devel@lists.sourceforge.net; Thu, 28 Jul 2022 19:35:36 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Thu, 28 Jul 2022 21:35:42 +0200 Message-Id: <20220728193542.14801-1-a@unstable.cc> In-Reply-To: <20220624083809.23487-11-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: Data channel keys are periodically regenarated and installed in ovpn-dco. However, there is a certain moment when keys are rotated in order to elect the new primary one. Check the key status in userspace so that kernelspace can be informed as well when rotations happen. Content analysis details: (-0.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1oH9IB-00DHXc-Tv Subject: [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys 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 Data channel keys are periodically regenarated and installed in ovpn-dco. However, there is a certain moment when keys are rotated in order to elect the new primary one. Check the key status in userspace so that kernelspace can be informed as well when rotations happen. Signed-off-by: Antonio Quartulli --- Changes from v1: * added comments to ASSERT() in dco_update_keys() --- src/openvpn/dco.c | 101 ++++++++++++++++++++++++++++++++++++++++++ src/openvpn/dco.h | 14 ++++++ src/openvpn/forward.c | 19 ++++++++ 3 files changed, 134 insertions(+) diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c index 0471e4d0..07c16de1 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -94,6 +94,107 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, ciphername); } +/** + * Find a usable key that is not the primary (i.e. the secondary key) + * + * @param multi The TLS struct to retrieve keys from + * @param primary The primary key that should be skipped during the scan + * + * @return The secondary key or NULL if none could be found + */ +static struct key_state * +dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary) +{ + for (int i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = get_key_scan(multi, i); + struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi; + + if (ks == primary) + { + continue; + } + + if (ks->state >= S_GENERATED_KEYS && ks->authenticated == KS_AUTH_TRUE) + { + ASSERT(key->initialized); + return ks; + } + } + + return NULL; +} + +void +dco_update_keys(dco_context_t *dco, struct tls_multi *multi) +{ + msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id); + + /* this function checks if keys have to be swapped or erased, therefore it + * can't do much if we don't have any key installed + */ + if (multi->dco_keys_installed == 0) + { + return; + } + + struct key_state *primary = tls_select_encryption_key(multi); + /* either we have no primary key at all or, if we do, it must have been + * installed already (keys are installed upon generation in the TLS code) + */ + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED); + + /* no primary key available -> no usable key exists, therefore we should + * tell DCO to simply wipe all keys + */ + if (!primary) + { + msg(D_DCO, "No encryption key found. Purging data channel keys"); + + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY); + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY); + multi->dco_keys_installed = 0; + return; + } + + struct key_state *secondary = dco_get_secondary_key(multi, primary); + /* same reason as the primary key above */ + ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED); + + /* the current primary key was installed as secondary in DCO, this means + * that userspace has promoted it and we should tell DCO to swap keys + */ + if (primary->dco_status == DCO_INSTALLED_SECONDARY) + { + msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d", + primary->key_id, secondary ? secondary->key_id : -1); + + dco_swap_keys(dco, multi->peer_id); + primary->dco_status = DCO_INSTALLED_PRIMARY; + if (secondary) + { + secondary->dco_status = DCO_INSTALLED_SECONDARY; + } + } + + /* if we have no secondary key anymore, inform DCO about it */ + if (!secondary && multi->dco_keys_installed == 2) + { + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY); + multi->dco_keys_installed = 1; + } + + /* all keys that are not installed are set to NOT installed */ + for (int i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = get_key_scan(multi, i); + if (ks != primary && ks != secondary) + { + ks->dco_status = DCO_NOT_INSTALLED; + } + } +} + 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 1692f5c3..b926e236 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -132,6 +132,14 @@ 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); +/** + * Possibly swap or wipe keys from DCO + * + * @param dco DCO device context + * @param multi TLS multi instance + */ +void dco_update_keys(dco_context_t *dco, struct tls_multi *multi); + #else /* if defined(ENABLE_DCO) */ typedef void *dco_context_t; @@ -192,5 +200,11 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, return 0; } +static inline void +dco_update_keys(dco_context_t *dco, struct tls_multi *multi) +{ + ASSERT(false); +} + #endif /* defined(ENABLE_DCO) */ #endif /* ifndef DCO_H */ diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 28f3c088..38d2683c 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -41,6 +41,7 @@ #include "dhcp.h" #include "common.h" #include "ssl_verify.h" +#include "dco.h" #include "memdbg.h" @@ -140,6 +141,18 @@ context_reschedule_sec(struct context *c, int sec) } } +void +check_dco_key_status(struct context *c) +{ + /* DCO context is not yet initialised or enabled */ + if (!dco_enabled(&c->options)) + { + return; + } + + dco_update_keys(&c->c1.tuntap->dco, c->c2.tls_multi); +} + /* * In TLS mode, let TLS level respond to any control-channel * packets which were received, or prepare any packets for @@ -182,6 +195,12 @@ check_tls(struct context *c) interval_schedule_wakeup(&c->c2.tmp_int, &wakeup); + /* Our current code has no good hooks in the TLS machinery to update + * DCO keys. So we check the key status after the whole TLS machinery + * has been completed and potentially update them + */ + check_dco_key_status(c); + if (wakeup) { context_reschedule_sec(c, wakeup);