From patchwork Sun Aug 30 04:07:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1407 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.30.191.6]) by backend41.mail.ord1d.rsapps.net with LMTP id cONZKii4S18fHQAAqwncew for ; Sun, 30 Aug 2020 10:31:04 -0400 Received: from proxy5.mail.ord1d.rsapps.net ([172.30.191.6]) by director12.mail.ord1d.rsapps.net with LMTP id SG1SKii4S196UgAAIasKDg (envelope-from ) for ; Sun, 30 Aug 2020 10:31:04 -0400 Received: from smtp27.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy5.mail.ord1d.rsapps.net with LMTPS id GMsaKii4S1/oRgAA8Zzt7w (envelope-from ) for ; Sun, 30 Aug 2020 10:31:04 -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: smtp27.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=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: 6ed760dc-eacd-11ea-9e99-5254003773d7-1-1 Received: from [216.105.38.7] ([216.105.38.7:46518] helo=lists.sourceforge.net) by smtp27.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id E8/9A-26506-828BB4F5; Sun, 30 Aug 2020 10:31:04 -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.90_1) (envelope-from ) id 1kCOLg-0002VD-5M; Sun, 30 Aug 2020 14:30:24 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kCOLe-0002V1-NX for openvpn-devel@lists.sourceforge.net; Sun, 30 Aug 2020 14:30:22 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To: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:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=I/JHV0o5Ois7naVCiG8ULLwBy3+zEZSR99BQaARJzp0=; b=cx136/YJMbG22ALc0HssGaqsfn ZlwUt/CMhsWer5+HzQRRuvxZQYjAjpVbx4BUImCU/IJpL3nebi8QNyXCHNWFKtIIQCZbt5Fa6wumJ 3WW9HN9zywd1PhEObIPbbqST+V6A249DpSvNq143M7z33yM2YpAEAXoKN5XqzEhx+llg=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To: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:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=I/JHV0o5Ois7naVCiG8ULLwBy3+zEZSR99BQaARJzp0=; b=RuuiRxWtivBuDnUyUeAzjRomoa zBE0DqJ8V/rkmN70jjUPOsDFKOAlzf6TdgvyzQzRn/2gzooaSj5n8Zj409j5REkBY+EDdKzCeNsBB LGZZV0NcM9Tg1tZJ7faWecP+veJYHPpL8ZkYnBuIQko9sYIkOSz5zkQyW7MUtYtFx8a8=; Received: from [192.26.174.232] (helo=mail.blinkt.de) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1kCOLc-007B7B-J2 for openvpn-devel@lists.sourceforge.net; Sun, 30 Aug 2020 14:30:22 +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 1kCNzd-000Jhv-02 for openvpn-devel@lists.sourceforge.net; Sun, 30 Aug 2020 16:07:37 +0200 Received: (nullmailer pid 16621 invoked by uid 10006); Sun, 30 Aug 2020 14:07:36 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Sun, 30 Aug 2020 16:07:35 +0200 Message-Id: <20200830140736.16571-2-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200830140736.16571-1-arne@rfc2549.org> References: <20200830140736.16571-1-arne@rfc2549.org> 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 1.0 RDNS_NONE Delivered to internal network by a host with no rDNS -0.3 AWL AWL: Adjusted score from AWL reputation of From: address X-Headers-End: 1kCOLc-007B7B-J2 Subject: [Openvpn-devel] [PATCH 1/2] Normalise ncp-ciphers option and restrict it to 127 bytes 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 In scenarios of mbed TLS vs OpenSSL we already normalise the ciphers that are send via the wire protocol via OCC to not have a mismatch warning between server and client. This is done by translate_cipher_name_from_openvpn. The same applies also to the ncp-ciphers list. Specifying non normalised names in ncp-ciphers will cause negotation not to succeed if ciphers are not in the same form. Therefore we will normalise the ciphers in options_postmutate. The alternative and a lot less user friendly alternative would be to bail if on of the ciphers in ncp-ciphers is not in its normalised form. Also restrict the ncp-ciphers list to 127. This is somewhat arbitrary but should prevent too large IV_CIPHER messages and problems sending those. The server will accept also large IV_CIPHER values from clients. Cherry picked from be4531564e2be7c8a0222e6923e3f7580b358cab and adjusted for 2.4 (methods added to ssl.h/ssl.c instead ssl_ncp.c/.h Signed-off-by: Arne Schwabe Acked-by: Gert Doering --- doc/openvpn.8 | 2 + src/openvpn/options.c | 9 ++++ src/openvpn/ssl.c | 63 +++++++++++++++++++++++ src/openvpn/ssl.h | 23 +++++++++ src/openvpn/ssl_ncp.h | 116 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+) create mode 100644 src/openvpn/ssl_ncp.h diff --git a/doc/openvpn.8 b/doc/openvpn.8 index 8038e1f4..77cc361d 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -4273,6 +4273,8 @@ NCP server (v2.4+) with "\-\-cipher BF\-CBC" and "\-\-ncp\-ciphers AES\-256\-GCM:AES\-256\-CBC" set can either specify "\-\-cipher BF\-CBC" or "\-\-cipher AES\-256\-CBC" and both will work. +This list is restricted to be 127 chars long after conversion to OpenVPN +ciphers. .\"********************************************************* .TP .B \-\-ncp\-disable diff --git a/src/openvpn/options.c b/src/openvpn/options.c index de30fcb0..f69bbb22 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -2982,6 +2982,15 @@ options_postprocess_mutate(struct options *o) options_postprocess_mutate_invariant(o); + if (o->ncp_enabled) + { + o->ncp_ciphers = mutate_ncp_cipher_list(o->ncp_ciphers, &o->gc); + if (o->ncp_ciphers == NULL) + { + msg(M_USAGE, "NCP cipher list contains unsupported ciphers or is too long."); + } + } + if (o->remote_list && !o->connection_list) { /* diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 215147f3..dd9c52fb 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -4318,6 +4318,69 @@ delayed_auth_pass_purge(void) purge_user_pass(&auth_user_pass, false); } +char * +mutate_ncp_cipher_list(const char *list, struct gc_arena *gc) +{ + bool error_found = false; + + struct buffer new_list = alloc_buf(MAX_NCP_CIPHERS_LENGTH); + + char *const tmp_ciphers = string_alloc(list, NULL); + const char *token = strtok(tmp_ciphers, ":"); + while (token) + { + /* + * Going through a roundtrip by using translate_cipher_name_from_openvpn + * and translate_cipher_name_to_openvpn also normalises the cipher name, + * e.g. replacing AeS-128-gCm with AES-128-GCM + */ + const char *cipher_name = translate_cipher_name_from_openvpn(token); + const cipher_kt_t *ktc = cipher_kt_get(cipher_name); + if (!ktc) + { + msg(M_WARN, "Unsupported cipher in --ncp-ciphers: %s", token); + error_found = true; + } + else + { + const char *ovpn_cipher_name = + translate_cipher_name_to_openvpn(cipher_kt_name(ktc)); + + if (buf_len(&new_list)> 0) + { + /* The next if condition ensure there is always space for + * a : + */ + buf_puts(&new_list, ":"); + } + + /* Ensure buffer has capacity for cipher name + : + \0 */ + if (!(buf_forward_capacity(&new_list) > + strlen(ovpn_cipher_name) + 2)) + { + msg(M_WARN, "Length of --ncp-ciphers is over the " + "limit of 127 chars"); + error_found = true; + } + else + { + buf_puts(&new_list, ovpn_cipher_name); + } + } + token = strtok(NULL, ":"); + } + + char *ret = NULL; + if (!error_found && buf_len(&new_list) > 0) + { + buf_null_terminate(&new_list); + ret = string_alloc(buf_str(&new_list), gc); + } + free(tmp_ciphers); + free_buf(&new_list); + + return ret; +} #else /* if defined(ENABLE_CRYPTO) */ static void dummy(void) diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 3266f386..703de99b 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -527,6 +527,29 @@ bool tls_check_ncp_cipher_list(const char *list); */ bool tls_item_in_cipher_list(const char *item, const char *list); +/** + * Check whether the ciphers in the supplied list are supported. + * + * @param list Colon-separated list of ciphers + * @parms gc gc_arena to allocate the returned string + * + * @returns colon separated string of normalised (via + * translate_cipher_name_from_openvpn) and + * zero terminated string iff all ciphers + * in list are supported and the total length + * is short than MAX_NCP_CIPHERS_LENGTH. NULL + * otherwise. + */ +char * +mutate_ncp_cipher_list(const char *list, struct gc_arena *gc); + +/** + * The maximum length of a ncp-cipher string that is accepted. + * + * Since this list needs to be pushed as IV_CIPHERS, we are conservative + * about its length. + */ +#define MAX_NCP_CIPHERS_LENGTH 127 /* * inline functions diff --git a/src/openvpn/ssl_ncp.h b/src/openvpn/ssl_ncp.h new file mode 100644 index 00000000..d00c222d --- /dev/null +++ b/src/openvpn/ssl_ncp.h @@ -0,0 +1,116 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2018 OpenVPN Inc + * Copyright (C) 2010-2018 Fox Crypto B.V. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file Control Channel SSL/Data dynamic negotion Module + * This file is split from ssl.h to be able to unit test it. + */ + +#ifndef OPENVPN_SSL_NCP_H +#define OPENVPN_SSL_NCP_H + +#include "buffer.h" +#include "options.h" + +/** + * Returns whether the client supports NCP either by + * announcing IV_NCP>=2 or the IV_CIPHERS list + */ +bool +tls_peer_supports_ncp(const char *peer_info); + +/** + * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. + * Allows non-NCP peers to upgrade their cipher individually. + * + * Make sure to call tls_session_update_crypto_params() after calling this + * function. + */ +void tls_poor_mans_ncp(struct options *o, const char *remote_ciphername); + +/** + * Iterates through the ciphers in server_list and return the first + * cipher that is also supported by the peer according to the IV_NCP + * and IV_CIPHER values in peer_info. + * + * We also accept a cipher that is the remote cipher of the client for + * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. + * Allows non-NCP peers to upgrade their cipher individually. + * + * Make sure to call tls_session_update_crypto_params() after calling this + * function. + * + * @param gc gc arena that is ONLY used to allocate the returned string + * + * @returns NULL if no common cipher is available, otherwise the best common + * cipher + */ +char * +ncp_get_best_cipher(const char *server_list, const char *server_cipher, + const char *peer_info, const char *remote_cipher, + struct gc_arena *gc); + + +/** + * Returns the support cipher list from the peer according to the IV_NCP + * and IV_CIPHER values in peer_info. + * + * @returns Either a string containing the ncp list that is either static + * or allocated via gc. If no information is available an empty string + * ("") is returned. + */ +const char * +tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc); + +/** + * Check whether the ciphers in the supplied list are supported. + * + * @param list Colon-separated list of ciphers + * @parms gc gc_arena to allocate the returned string + * + * @returns colon separated string of normalised (via + * translate_cipher_name_from_openvpn) and + * zero terminated string iff all ciphers + * in list are supported and the total length + * is short than MAX_NCP_CIPHERS_LENGTH. NULL + * otherwise. + */ +char * +mutate_ncp_cipher_list(const char *list, struct gc_arena *gc); + +/** + * Return true iff item is present in the colon-separated zero-terminated + * cipher list. + */ +bool tls_item_in_cipher_list(const char *item, const char *list); + +/** + * The maximum length of a ncp-cipher string that is accepted. + * + * Since this list needs to be pushed as IV_CIPHERS, we are conservative + * about its length. + */ +#define MAX_NCP_CIPHERS_LENGTH 127 + +#endif /* ifndef OPENVPN_SSL_NCP_H */