From patchwork Tue Sep 8 05:41:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1424 X-Patchwork-Delegate: a@unstable.cc Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.28.255.1]) by backend30.mail.ord1d.rsapps.net with LMTP id MBcgIIimV19qFwAAIUCqbw (envelope-from ) for ; Tue, 08 Sep 2020 11:43:04 -0400 Received: from proxy2.mail.ord1c.rsapps.net ([172.28.255.1]) by director12.mail.ord1d.rsapps.net with LMTP id QNbdH4imV19kHwAAIasKDg (envelope-from ) for ; Tue, 08 Sep 2020 11:43:04 -0400 Received: from smtp19.gate.ord1c ([172.28.255.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy2.mail.ord1c.rsapps.net with LMTPS id uJAVH4imV1/DMgAA311kuQ (envelope-from ) for ; Tue, 08 Sep 2020 11:43: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: smtp19.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=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: f9852a70-f1e9-11ea-a65b-bc305bf036e4-1-1 Received: from [216.105.38.7] ([216.105.38.7:36578] helo=lists.sourceforge.net) by smtp19.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 9F/0C-02135-486A75F5; Tue, 08 Sep 2020 11:43:01 -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 1kFflC-0005Jb-3X; Tue, 08 Sep 2020 15:42:18 +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 1kFflA-0005JP-RY for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:16 +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=NRPCLwb3FMsZ11Lu+S4lmjmG8pBKFpsgNdGptoLuym0=; b=nB2KGqoGFaq6297WdGNIg4riRE 4N0E3AGsZW38pnH5v92v7e5J7WDbsNmKfFiQIj4inprk5xnAVNshEeqFiaVkpuAw1JPwKP7hw30st dFucqiVsqts1beLRUYoh//TBynpLAXWVdwSP1Jf4gfHPShJ+4b6eGH9/e9iiMV2md0m4=; 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=NRPCLwb3FMsZ11Lu+S4lmjmG8pBKFpsgNdGptoLuym0=; b=W9AJhoAtoRBaRTlz+SkmFNcU9E dhFCJkx+sDoYfxiCPaXwiaSBKHk+HBnWuekBoS85LET4ijCkBcofJWf2wAzdMQw1HsNFYqpfQIJuS BnqD8fmNOaikbOJQIlnDHyYo+bn7H0ORyS595b6uJaRoptJghwA3fAPBNDdF3OS8LYpo=; 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 1kFfl6-00GxmQ-5X for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:16 +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 1kFfkr-0002sx-SE for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 17:41:57 +0200 Received: (nullmailer pid 13857 invoked by uid 10006); Tue, 08 Sep 2020 15:41:57 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Tue, 8 Sep 2020 17:41:54 +0200 Message-Id: <20200908154157.13809-2-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200908154157.13809-1-arne@rfc2549.org> References: <20200908154157.13809-1-arne@rfc2549.org> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.2 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: 1kFfl6-00GxmQ-5X Subject: [Openvpn-devel] [PATCH 1/4] Extend verify-hash to allow multiple hashes 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 For a new syntax introduced now it does not make much sense to support deprecated and old hashes, so support only SHA-256. Also give a warning about SHA1 hash being deprecated to verify certificates as it is now "industry standard". Signed-off-by: Arne Schwabe --- doc/man-sections/inline-files.rst | 4 +- doc/man-sections/tls-options.rst | 10 ++++ src/openvpn/options.c | 91 +++++++++++++++++++++++++++---- src/openvpn/options.h | 11 +++- src/openvpn/ssl_common.h | 2 +- src/openvpn/ssl_verify.c | 17 +++++- 6 files changed, 119 insertions(+), 16 deletions(-) diff --git a/doc/man-sections/inline-files.rst b/doc/man-sections/inline-files.rst index 819bd3c8..303bb3c8 100644 --- a/doc/man-sections/inline-files.rst +++ b/doc/man-sections/inline-files.rst @@ -4,8 +4,8 @@ INLINE FILE SUPPORT OpenVPN allows including files in the main configuration for the ``--ca``, ``--cert``, ``--dh``, ``--extra-certs``, ``--key``, ``--pkcs12``, ``--secret``, ``--crl-verify``, ``--http-proxy-user-pass``, ``--tls-auth``, -``--auth-gen-token-secret``, ``--tls-crypt`` and ``--tls-crypt-v2`` -options. +``--auth-gen-token-secret``, ``--tls-crypt``, ``--tls-crypt-v2`` and +``--verify-hash`` options. Each inline file started by the line ```` diff --git a/doc/man-sections/tls-options.rst b/doc/man-sections/tls-options.rst index 8c2db7cd..52d4137e 100644 --- a/doc/man-sections/tls-options.rst +++ b/doc/man-sections/tls-options.rst @@ -579,6 +579,16 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa The ``algo`` flag can be either :code:`SHA1` or :code:`SHA256`. If not provided, it defaults to :code:`SHA1`. + This option can also be inlined + :: + + + 00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff + 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00 + + +If the option is inlined, ``algo`` is always :code:`SHA256`. + --verify-x509-name args Accept connections only if a host's X.509 name is equal to **name.** The remote host must also pass all other tests of verification. diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 8bf82c57..068f3e75 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -1082,12 +1082,24 @@ string_substitute(const char *src, int from, int to, struct gc_arena *gc) return ret; } -static uint8_t * +/** + * Parses a hexstring and checks if the string has the correct length. Return + * a verify_hash_list of the parsed string. + * + * @param str String to check/parse + * @param nbytes Number of bytes expected in the hexstr (e.g. 20 for SHA1) + * @param msglevel message level to use when printing warnings/errors + * @param gc The returned string will be allocated in this gc. + */ +static struct verify_hash_list * parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_arena *gc) { int i; const char *cp = str; - uint8_t *ret = (uint8_t *) gc_malloc(nbytes, true, gc); + + struct verify_hash_list *ret; + ALLOC_OBJ_CLEAR_GC(ret, struct verify_hash_list, gc); + char term = 1; int byte; char bs[3]; @@ -1106,7 +1118,7 @@ parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_aren { msg(msglevel, "format error in hash fingerprint hex byte: %s", str); } - ret[i] = (uint8_t)byte; + ret->hash[i] = (uint8_t)byte; term = *cp++; if (term != ':' && term != 0) { @@ -1120,10 +1132,54 @@ parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_aren if (term != 0 || i != nbytes-1) { msg(msglevel, "hash fingerprint is different length than expected (%d bytes): %s", nbytes, str); + return NULL; } return ret; } +/** + * Parses a string consisting of multiple lines of hexstrings and checks if each + * string has the correct length. Lines that are empty are ignored. Returns + * a verify_hash_list. + * + * @param str String to check/parse + * @param nbytes Number of bytes expected in the hexstr (e.g. 20 for SHA1) + * @param msglevel message level to use when printing warnings/errors + * @param gc The returned string will be allocated in this gc. + */ +static struct verify_hash_list * +parse_hash_fingerprint_multiline(const char *str, int nbytes, int msglevel, + struct gc_arena *gc) +{ + struct gc_arena gc_temp = gc_new(); + char *lines = string_alloc(str, &gc_temp); + + struct verify_hash_list *ret = NULL; + + const char *line; + while ((line = strsep(&lines, "\n"))) + { + /* skip empty lines */ + if (strlen(line) == 0) + { + continue; + } + + struct verify_hash_list *hash = parse_hash_fingerprint(line, nbytes, + msglevel, gc); + + if (!hash) + { + return NULL; + } + + hash->next = ret; + ret = hash; + } + gc_free(&gc_temp); + + return ret; +} #ifdef _WIN32 #ifndef ENABLE_SMALL @@ -8138,22 +8194,37 @@ add_option(struct options *options, } else if (streq(p[0], "verify-hash") && p[1] && !p[3]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); + options->verify_hash_algo = MD_SHA256; + + int digest_len = SHA256_DIGEST_LENGTH; if (!p[2] || (p[2] && streq(p[2], "SHA1"))) { - options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc); options->verify_hash_algo = MD_SHA1; + msg(M_WARN, "DEPRECATED FEATURE: Usage of SHA1 fingerprints for " + "verify-hash is deprecated. You should switch to the " + "SHA256."); + options->verify_hash_algo = MD_SHA1; + digest_len = SHA_DIGEST_LENGTH; } - else if (p[2] && streq(p[2], "SHA256")) + else if (p[2] && !streq(p[2], "SHA256")) { - options->verify_hash = parse_hash_fingerprint(p[1], SHA256_DIGEST_LENGTH, msglevel, &options->gc); - options->verify_hash_algo = MD_SHA256; + msg(msglevel, "invalid or unsupported hashing algorithm: %s (only SHA1 and SHA256 are valid)", p[2]); + goto err; + } + + if (is_inline) + { + /* We do not have a inline flag in options struct as it is either + * a one line or multiline string */ + options->verify_hash = parse_hash_fingerprint_multiline(p[1], digest_len, msglevel, + &options->gc); } else { - msg(msglevel, "invalid or unsupported hashing algorithm: %s (only SHA1 and SHA256 are valid)", p[2]); - goto err; + options->verify_hash = parse_hash_fingerprint(p[1], digest_len, msglevel, + &options->gc); } } #ifdef ENABLE_CRYPTOAPI diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 877e9396..c0dbbd8a 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -191,6 +191,15 @@ enum genkey_type { GENKEY_AUTH_TOKEN }; +struct verify_hash_list +{ + /* Currently we only support SHA256 for longer lists, for a one item + * list with SHA1 we ignore the 12 byte memory wasted */ + uint8_t hash[32]; + struct verify_hash_list *next; + +}; + /* Command line options */ struct options { @@ -553,7 +562,7 @@ struct options int ns_cert_type; /* set to 0, NS_CERT_CHECK_SERVER, or NS_CERT_CHECK_CLIENT */ unsigned remote_cert_ku[MAX_PARMS]; const char *remote_cert_eku; - uint8_t *verify_hash; + struct verify_hash_list *verify_hash; hash_algo_type verify_hash_algo; unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */ diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 96897e48..7ccfc0f8 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -282,7 +282,7 @@ struct tls_options int ns_cert_type; unsigned remote_cert_ku[MAX_PARMS]; const char *remote_cert_eku; - uint8_t *verify_hash; + struct verify_hash_list *verify_hash; hash_algo_type verify_hash_algo; char *x509_username_field; diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 97ccb93b..73d14e01 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -720,9 +720,22 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep goto cleanup; } - if (memcmp(BPTR(&ca_hash), opt->verify_hash, BLEN(&ca_hash))) + struct verify_hash_list *current_hash = opt->verify_hash; + bool hash_matched = false; + + while (current_hash) + { + if (memcmp_constant_time(BPTR(&ca_hash), current_hash->hash, + BLEN(&ca_hash)) == 0) + { + hash_matched = true; + } + current_hash = current_hash->next; + } + + if (!hash_matched) { - msg(D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed"); + msg(D_TLS_ERRORS, "TLS Error: --tls-verify certificate hash verification failed"); goto cleanup; } } From patchwork Tue Sep 8 05:41:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1427 X-Patchwork-Delegate: a@unstable.cc Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id ONKCLIqmV184PQAAIUCqbw (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -0400 Received: from proxy13.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id MJpULIqmV1+FYQAAfY0hYg (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -0400 Received: from smtp7.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy13.mail.ord1d.rsapps.net with LMTPS id aBCnK4qmV1/MIwAAgjf6aA (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -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: smtp7.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=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: fb9e7e9c-f1e9-11ea-bf6f-bc305bf04148-1-1 Received: from [216.105.38.7] ([216.105.38.7:39422] helo=lists.sourceforge.net) by smtp7.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id C2/E4-21739-886A75F5; Tue, 08 Sep 2020 11:43:04 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1kFfl7-0001JH-OW; Tue, 08 Sep 2020 15:42:13 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kFfl5-0001IT-Bj for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:11 +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=kqJQpe/oTqO5+PZ0iaLbnhGVr2jF4YMtOVyrpzX8sOE=; b=i3h8ShPRBmz4NQFieT9cFAid60 FD7+FmnolsbvkurqXeuH9Ta+iy+W+qotEnQZRLeM+Cze9S+hYaJseVs08y2kAqLFkpJOZyT8UkFeJ t1hu1snjcsc1917J2DmBn9N4SvbAwRfcCEHyjBBahtIOsbhqvo5mNungQ7GhJ99TBcSs=; 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=kqJQpe/oTqO5+PZ0iaLbnhGVr2jF4YMtOVyrpzX8sOE=; b=DO18QNLHLdnjQw00Mc16/RM74G ZlWzZGg4cnmW6IUa6C0M10YZN6SvfQD+fZAcvGOmZyal9pJP7pW0v0+t0w3V1klu/SF3C5OjyLqB6 twVdCrI9wow9lxaNGXCsSDg6deVyQBnuomJb9APA4O4ektL1pO7RPPWuTjqJHVoNiWnA=; 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 1kFfkz-00GxlQ-3f for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:11 +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 1kFfkr-0002t1-UM for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 17:41:57 +0200 Received: (nullmailer pid 13861 invoked by uid 10006); Tue, 08 Sep 2020 15:41:57 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Tue, 8 Sep 2020 17:41:55 +0200 Message-Id: <20200908154157.13809-3-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200908154157.13809-1-arne@rfc2549.org> References: <20200908154157.13809-1-arne@rfc2549.org> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.2 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: 1kFfkz-00GxlQ-3f Subject: [Openvpn-devel] [PATCH 2/4] Implement peer-fingerprint to check fingerprint of peer certificate 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 options allows to pin a certificate or a number of certificate. It also prepares for doing TLS authentication without a CA and just self-signed certificates. Signed-off-by: Arne Schwabe --- Changes.rst | 7 ++++++ doc/man-sections/inline-files.rst | 4 ++-- doc/man-sections/tls-options.rst | 22 ++++++++++++++++- src/openvpn/init.c | 1 + src/openvpn/options.c | 40 ++++++++++++++++++++++--------- src/openvpn/options.h | 1 + src/openvpn/ssl_common.h | 1 + src/openvpn/ssl_verify.c | 19 ++++++++------- 8 files changed, 73 insertions(+), 22 deletions(-) diff --git a/Changes.rst b/Changes.rst index f67e1d76..6007412c 100644 --- a/Changes.rst +++ b/Changes.rst @@ -1,5 +1,12 @@ Overview of changes in 2.5 ========================== +New features in 2.5.1 +--------------------- +Certificate pinning/verify peer fingerprint + The ``--peer-fingerprint`` option has been introduced to give users a + easy to use alternative to the ``tls-verify`` for matching the + fingerprint of the peer. The option has use a number of + SHA256 fingerprints. New features ------------ diff --git a/doc/man-sections/inline-files.rst b/doc/man-sections/inline-files.rst index 303bb3c8..01e4a840 100644 --- a/doc/man-sections/inline-files.rst +++ b/doc/man-sections/inline-files.rst @@ -4,8 +4,8 @@ INLINE FILE SUPPORT OpenVPN allows including files in the main configuration for the ``--ca``, ``--cert``, ``--dh``, ``--extra-certs``, ``--key``, ``--pkcs12``, ``--secret``, ``--crl-verify``, ``--http-proxy-user-pass``, ``--tls-auth``, -``--auth-gen-token-secret``, ``--tls-crypt``, ``--tls-crypt-v2`` and -``--verify-hash`` options. +``--auth-gen-token-secret``, ``--peer-fingerprint``, ``--tls-crypt``, +``--tls-crypt-v2`` and ``--verify-hash`` options. Each inline file started by the line ```` diff --git a/doc/man-sections/tls-options.rst b/doc/man-sections/tls-options.rst index 52d4137e..658300b8 100644 --- a/doc/man-sections/tls-options.rst +++ b/doc/man-sections/tls-options.rst @@ -268,7 +268,8 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa man-in-the-middle attack where an authorized client attempts to connect to another client by impersonating the server. The attack is easily prevented by having clients verify the server certificate using any one - of ``--remote-cert-tls``, ``--verify-x509-name``, or ``--tls-verify``. + of ``--remote-cert-tls``, ``--verify-x509-name``, ``--peer-fingerprint`` + or ``--tls-verify``. --tls-auth args Add an additional layer of HMAC authentication on top of the TLS control @@ -589,6 +590,25 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa If the option is inlined, ``algo`` is always :code:`SHA256`. +--peer-fingerprint args + Specify a SHA256 fingerprint or list of SHA256 fingerprints to verify + the peer certificate against. The peer certificate must match one of the + fingerprint or certificate verification will fail. The option can also + be inlined + + Valid syntax: + :: + + peer-fingerprint AD:B0:95:D8:09:... + + or inline: + :: + + + 00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff + 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00 + + --verify-x509-name args Accept connections only if a host's X.509 name is equal to **name.** The remote host must also pass all other tests of verification. diff --git a/src/openvpn/init.c b/src/openvpn/init.c index a785934a..0c2b823e 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2909,6 +2909,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.remote_cert_eku = options->remote_cert_eku; to.verify_hash = options->verify_hash; to.verify_hash_algo = options->verify_hash_algo; + to.verify_hash_depth = options->verify_hash_depth; #ifdef ENABLE_X509ALTUSERNAME to.x509_username_field = (char *) options->x509_username_field; #else diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 068f3e75..df9eef07 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -8192,26 +8192,44 @@ add_option(struct options *options, options->extra_certs_file = p[1]; options->extra_certs_file_inline = is_inline; } - else if (streq(p[0], "verify-hash") && p[1] && !p[3]) + else if ((streq(p[0], "verify-hash") && p[1] && !p[3]) + || (streq(p[0], "peer-fingerprint") && p[1] && !p[2])) { VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); + + options->verify_hash_depth = 0; options->verify_hash_algo = MD_SHA256; int digest_len = SHA256_DIGEST_LENGTH; - if (!p[2] || (p[2] && streq(p[2], "SHA1"))) + if (options->verify_hash) { - options->verify_hash_algo = MD_SHA1; - msg(M_WARN, "DEPRECATED FEATURE: Usage of SHA1 fingerprints for " - "verify-hash is deprecated. You should switch to the " - "SHA256."); - options->verify_hash_algo = MD_SHA1; - digest_len = SHA_DIGEST_LENGTH; + msg(msglevel, "ERROR: Setting %s not allowed. Option to verify " + "fingerprint of certificate of peer certificate " + "(--verify-hash or --peer-fingerprint) already set.", + p[0]); + goto err; } - else if (p[2] && !streq(p[2], "SHA256")) + + if (streq(p[0], "verify-hash")) { - msg(msglevel, "invalid or unsupported hashing algorithm: %s (only SHA1 and SHA256 are valid)", p[2]); - goto err; + /* verify level 1 cert, i.e. the CA that signed the leaf cert */ + options->verify_hash_depth = 1; + + if (!p[2] || (p[2] && streq(p[2], "SHA1"))) + { + options->verify_hash_algo = MD_SHA1; + msg(M_WARN, "DEPRECATED FEATURE: Usage of SHA1 fingerprints for " + "verify-hash is deprecated. You should switch to the " + "SHA256."); + options->verify_hash_algo = SHA_DIGEST_LENGTH; + digest_len = SHA_DIGEST_LENGTH; + } + else if (p[2] && !streq(p[2], "SHA256")) + { + msg(msglevel, "invalid or unsupported hashing algorithm: %s (only SHA1 and SHA256 are valid)", p[2]); + goto err; + } } if (is_inline) diff --git a/src/openvpn/options.h b/src/openvpn/options.h index c0dbbd8a..eee6bd21 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -564,6 +564,7 @@ struct options const char *remote_cert_eku; struct verify_hash_list *verify_hash; hash_algo_type verify_hash_algo; + int verify_hash_depth; unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */ #ifdef ENABLE_PKCS11 diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 7ccfc0f8..3a07c3d3 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -283,6 +283,7 @@ struct tls_options unsigned remote_cert_ku[MAX_PARMS]; const char *remote_cert_eku; struct verify_hash_list *verify_hash; + int verify_hash_depth; hash_algo_type verify_hash_algo; char *x509_username_field; diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 73d14e01..ac5e6271 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -693,19 +693,18 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep goto cleanup; /* Reject connection */ } - /* verify level 1 cert, i.e. the CA that signed our leaf cert */ - if (cert_depth == 1 && opt->verify_hash) + if (cert_depth == opt->verify_hash_depth && opt->verify_hash) { - struct buffer ca_hash = {0}; + struct buffer cert_fp = {0}; switch (opt->verify_hash_algo) { case MD_SHA1: - ca_hash = x509_get_sha1_fingerprint(cert, &gc); + cert_fp = x509_get_sha1_fingerprint(cert, &gc); break; case MD_SHA256: - ca_hash = x509_get_sha256_fingerprint(cert, &gc); + cert_fp = x509_get_sha256_fingerprint(cert, &gc); break; default: @@ -725,8 +724,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep while (current_hash) { - if (memcmp_constant_time(BPTR(&ca_hash), current_hash->hash, - BLEN(&ca_hash)) == 0) + if (memcmp_constant_time(BPTR(&cert_fp), current_hash->hash, + BLEN(&cert_fp)) == 0) { hash_matched = true; } @@ -735,7 +734,11 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep if (!hash_matched) { - msg(D_TLS_ERRORS, "TLS Error: --tls-verify certificate hash verification failed"); + const char *hex_fp = format_hex_ex(BPTR(&cert_fp), BLEN(&cert_fp), + 0, 1, ":", &gc); + msg(D_TLS_ERRORS, "TLS Error: --tls-verify/--peer-fingerprint" + "certificate hash verification failed. (got " + "fingerprint: %s", hex_fp); goto cleanup; } } From patchwork Tue Sep 8 05:41:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1426 X-Patchwork-Delegate: a@unstable.cc Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id oLjrJIqmV18wXAAAIUCqbw (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -0400 Received: from proxy7.mail.ord1d.rsapps.net ([172.30.191.6]) by director11.mail.ord1d.rsapps.net with LMTP id IPjWJIqmV18YTAAAvGGmqA (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -0400 Received: from smtp37.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy7.mail.ord1d.rsapps.net with LMTPS id ULA6JIqmV196RQAAMe1Fpw (envelope-from ) for ; Tue, 08 Sep 2020 11:43:06 -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: smtp37.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=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: f97ec22a-f1e9-11ea-b9d3-525400e8d833-1-1 Received: from [216.105.38.7] ([216.105.38.7:39320] helo=lists.sourceforge.net) by smtp37.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 1E/6F-32504-486A75F5; Tue, 08 Sep 2020 11:43:01 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1kFfl7-0001J4-JZ; Tue, 08 Sep 2020 15:42:13 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kFfl4-0001I8-2f for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:10 +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:Cc: To:From:Sender:Reply-To: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=fpnjbtx6O6sAbuQg8K42qyspz8q8+GZWbFpShlWubNs=; b=KwJn2Ewf7MqfmKy0+47Bf6nWRT mk6iscccd6nq2Mnr61YFWEryAN3uLo2myx7kgpM57835FsTsquws1QNemF2ZvPnEUIyPx2c1XI61c cXcZiaS20waz96bM1FJea6ba6ZGjw/m+N6PvXFzl7U1AIUwrWrP9MGoJynSsFP+JoRec=; 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:Cc:To:From:Sender:Reply-To :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=fpnjbtx6O6sAbuQg8K42qyspz8q8+GZWbFpShlWubNs=; b=D5B5gMRx4GevPFa+R8Scw8zuLX dmNHgBndRNxpj33nx+RK2Rp/5KfbCfPVexDApY6XERPD8v1f0rAjfijQPbTp6ZBizGg0RiPhO8hFJ vP1TJU9wNGc5I1XDtXKDd8CO5WezKlGK0pG2YkRDFbxR/dk/NDIT3+Z2/01aTl/sL7Gg=; Received: from mail.blinkt.de ([192.26.174.232]) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1kFfkz-00H6Qb-1z for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:10 +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 1kFfks-0002t4-0P; Tue, 08 Sep 2020 17:41:58 +0200 Received: (nullmailer pid 13865 invoked by uid 10006); Tue, 08 Sep 2020 15:41:57 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Tue, 8 Sep 2020 17:41:56 +0200 Message-Id: <20200908154157.13809-4-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200908154157.13809-1-arne@rfc2549.org> References: <20200908154157.13809-1-arne@rfc2549.org> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.2 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: 1kFfkz-00H6Qb-1z Subject: [Openvpn-devel] [PATCH 3/4] Support fingerprint authentication without CA certificate 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: "Jason A. Donenfeld" MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: "Jason A. Donenfeld" OpenVPN traditionally works around CAs. However many TLS-based protocols also allow an alternative simpler mode in which rather than verify certificates against CAs, the certificate itself is hashed and compared against a pre-known set of acceptable hashes. This is usually referred to as "fingerprint verification". It's popular across SMTP servers, IRC servers, XMPP servers, and even in the context of HTTP with pinning. * Allow not specifying the --ca parameter, to specify that certificates should not be checked against a CA. I've included some instructions on how to use all of this. Server side: ============ Make self-signed cert: $ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout serverkey.pem -out servercert.pem -nodes -sha256 -days 3650 -subj '/CN=server' Record our fingerprint in an environment variable for the client to use later: $ server_fingerprint="$(openssl x509 -in servercert.pem -noout -sha256 -fingerprint | sed 's/.*=//;s/\(.*\)/\1/')" Client side: ============ Make self-signed cert: $ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout clientkey.pem -out clientcert.pem -nodes -sha256 -days 3650 -subj '/CN=client' Record our fingerprint in an environment variable for the server to use later: $ client_fingerprint="$(openssl x509 -in clientcert.pem -noout -sha256 -fingerprint | sed 's/.*=//;s/\(.*\)/\1/')" Start server/client =================== Start openvpn with peer fingerprint verification: $ sudo openvpn --server 10.66.0.0 255.255.255.0 --dev tun --dh none --cert servercert.pem --key serverkey.pem --peer-fingerprint "$client_fingerprint" $ sudo openvpn --client --remote 127.0.0.1 --dev tun --cert clientcert.pem --key clientkey.pem --peer-fingerprint "$server_fingerprint" --nobind Signed-off-by: Jason A. Donenfeld Patch V2: Changes in V2 (by Arne Schwabe): - Only check peer certificates, not all cert levels, if you need multiple levels of certificate you should use a real CA - Use peer-fingerprint instead tls-verify on server side in example. - rename variable ca_file_none to verify_hash_no_ca - do no require --ca none but allow --ca simply to be absent when --peer-fingprint is present - adjust warnings/errors messages to also point to peer-fingerprint as valid verification method. - Fix mbed TLS version of not requiring CA not working Signed-off-by: Arne Schwabe --- src/openvpn/init.c | 2 ++ src/openvpn/options.c | 30 +++++++++++++++++++++++------- src/openvpn/options.h | 1 + src/openvpn/ssl.c | 2 +- src/openvpn/ssl_common.h | 1 + src/openvpn/ssl_verify_mbedtls.c | 17 +++++++++++++++++ src/openvpn/ssl_verify_openssl.c | 2 +- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 0c2b823e..585b5760 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2910,6 +2910,8 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.verify_hash = options->verify_hash; to.verify_hash_algo = options->verify_hash_algo; to.verify_hash_depth = options->verify_hash_depth; + to.verify_hash_no_ca = options->verify_hash_no_ca; + #ifdef ENABLE_X509ALTUSERNAME to.x509_username_field = (char *) options->x509_username_field; #else diff --git a/src/openvpn/options.c b/src/openvpn/options.c index df9eef07..8e00e896 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -2721,18 +2721,23 @@ options_postprocess_verify_ce(const struct options *options, const struct connec else { #ifdef ENABLE_CRYPTO_MBEDTLS - if (!(options->ca_file)) + if (!(options->ca_file || options->verify_hash_no_ca)) { - msg(M_USAGE, "You must define CA file (--ca)"); + msg(M_USAGE, "You must define CA file (--ca) and/or " + "peer fingeprint verification " + "(--peer-fingerprint)"); } if (options->ca_path) { msg(M_USAGE, "Parameter --capath cannot be used with the mbed TLS version version of OpenVPN."); } #else /* ifdef ENABLE_CRYPTO_MBEDTLS */ - if ((!(options->ca_file)) && (!(options->ca_path))) + if ((!(options->ca_file)) && (!(options->ca_path)) + && (!(options->verify_hash_no_ca))) { - msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)"); + msg(M_USAGE, "You must define CA file (--ca) or CA path " + "(--capath) and/or peer fingeprint verification " + "(--peer-fingerprint)"); } #endif if (pull) @@ -2751,7 +2756,8 @@ options_postprocess_verify_ce(const struct options *options, const struct connec #if P2MP if (!options->auth_user_pass_file) #endif - msg(M_USAGE, "No client-side authentication method is specified. You must use either --cert/--key, --pkcs12, or --auth-user-pass"); + msg(M_USAGE, "No client-side authentication method is specified. You must use either --cert/--key," + " --pkcs12, or --auth-user-pass"); } else if (sum == 2) { @@ -3241,6 +3247,13 @@ options_postprocess_mutate(struct options *o) options_postprocess_http_proxy_override(o); } #endif + if (!o->ca_file && !o->ca_path && o->verify_hash + && o->verify_hash_depth == 0) + { + msg(M_INFO, "Using certificate fingerprint to verify peer (no CA " + "option set). "); + o->verify_hash_no_ca = true; + } #if P2MP /* @@ -3476,8 +3489,11 @@ options_postprocess_filechecks(struct options *options) errs |= check_file_access_inline(options->dh_file_inline, CHKACC_FILE, options->dh_file, R_OK, "--dh"); - errs |= check_file_access_inline(options->ca_file_inline, CHKACC_FILE, - options->ca_file, R_OK, "--ca"); + if (!options->verify_hash_no_ca) + { + errs |= check_file_access_inline(options->ca_file_inline, CHKACC_FILE, + options->ca_file, R_OK, "--ca"); + } errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, options->ca_path, R_OK, "--capath"); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index eee6bd21..ea1b49d0 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -565,6 +565,7 @@ struct options struct verify_hash_list *verify_hash; hash_algo_type verify_hash_algo; int verify_hash_depth; + bool verify_hash_no_ca; unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */ #ifdef ENABLE_PKCS11 diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 3fcaa25f..45836821 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -684,7 +684,7 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx) } #endif - if (options->ca_file || options->ca_path) + if ((!options->verify_hash_no_ca && options->ca_file) || options->ca_path) { tls_ctx_load_ca(new_ctx, options->ca_file, options->ca_file_inline, options->ca_path, options->tls_server); diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 3a07c3d3..33fd8237 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -284,6 +284,7 @@ struct tls_options const char *remote_cert_eku; struct verify_hash_list *verify_hash; int verify_hash_depth; + bool verify_hash_no_ca; hash_algo_type verify_hash_algo; char *x509_username_field; diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c index 93891038..1ed1e0e2 100644 --- a/src/openvpn/ssl_verify_mbedtls.c +++ b/src/openvpn/ssl_verify_mbedtls.c @@ -62,6 +62,23 @@ verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, struct buffer cert_fingerprint = x509_get_sha256_fingerprint(cert, &gc); cert_hash_remember(session, cert_depth, &cert_fingerprint); + + if (session->opt->verify_hash_no_ca) + { + /* + * If we decide to verify the peer certificate based on the fingerprint + * we ignore wrong dates and the certificate not being trusted. + * Any other problem with the certificate (wrong key, bad cert,...) + * will still trigger an error. + * Clearing these flags relies on verify_cert will later rejecting a + * certificate that has no matching fingerprint. + */ + uint32_t flags_ignore = MBEDTLS_X509_BADCERT_NOT_TRUSTED + | MBEDTLS_X509_BADCERT_EXPIRED + | MBEDTLS_X509_BADCERT_FUTURE; + *flags = *flags & ~flags_ignore; + } + /* did peer present cert which was signed by our root cert? */ if (*flags != 0) { diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c index 454efeec..cb2a2e2a 100644 --- a/src/openvpn/ssl_verify_openssl.c +++ b/src/openvpn/ssl_verify_openssl.c @@ -67,7 +67,7 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx) cert_hash_remember(session, X509_STORE_CTX_get_error_depth(ctx), &cert_hash); /* did peer present cert which was signed by our root cert? */ - if (!preverify_ok) + if (!preverify_ok && !session->opt->verify_hash_no_ca) { /* get the X509 name */ char *subject = x509_get_subject(current_cert, &gc); From patchwork Tue Sep 8 05:41:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 1425 X-Patchwork-Delegate: a@unstable.cc Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id iEB9HommV19qFwAAIUCqbw (envelope-from ) for ; Tue, 08 Sep 2020 11:43:05 -0400 Received: from proxy3.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id aLyRHImmV1/tYQAAfY0hYg (envelope-from ) for ; Tue, 08 Sep 2020 11:43:05 -0400 Received: from smtp12.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy3.mail.ord1d.rsapps.net with LMTPS id QNwGGYmmV18BBgAA7WKfLA (envelope-from ) for ; Tue, 08 Sep 2020 11:43:05 -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: smtp12.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=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: f964dc2a-f1e9-11ea-a962-bc305bf03e5c-1-1 Received: from [216.105.38.7] ([216.105.38.7:44036] helo=lists.sourceforge.net) by smtp12.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 59/E0-11833-486A75F5; Tue, 08 Sep 2020 11:43:00 -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 1kFfl4-0004Or-Tc; Tue, 08 Sep 2020 15:42:10 +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 1kFfl4-0004Ok-3Q for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:10 +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=2zjy/23zry2/9Q+jUtDIrxVCW3wF/RMhXM4ZL73fr14=; b=dM/7rm3ApX4vlWn81JtwIP4Xad XrkgL7WA4O9tM9vnUrwtCTqo0a+HACruUVSqp5xU6yuBJkcUH0oMGwndgETa7w9sykdMcIzOYTfxU RmYTv0g8iyIvgulFMOyEF+Lr1KGn3bGON597p2RZ1yvoGYd4XeBtZGQvMRmwrkyGBrH8=; 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=2zjy/23zry2/9Q+jUtDIrxVCW3wF/RMhXM4ZL73fr14=; b=OBczQYSo/l6Se26ZUXsGYReM59 A+XkiNbySN6CR+VA8lGQAF+qtdSn7ZKCLb1Z3Wxkg3aPtXDMnKAdjaAU9hx0c0vCGrxT6jZdEi/EJ VRK/uDkGKUcyyTzGdnjdNeieYBFRnIGikkms2sUGBqeJg2isAQN8JSA8AGuTD+L+64sA=; Received: from mail.blinkt.de ([192.26.174.232]) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1kFfkz-00H6QY-1Y for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 15:42:10 +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 1kFfks-0002t6-2M for openvpn-devel@lists.sourceforge.net; Tue, 08 Sep 2020 17:41:58 +0200 Received: (nullmailer pid 13868 invoked by uid 10006); Tue, 08 Sep 2020 15:41:57 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Tue, 8 Sep 2020 17:41:57 +0200 Message-Id: <20200908154157.13809-5-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200908154157.13809-1-arne@rfc2549.org> References: <20200908154157.13809-1-arne@rfc2549.org> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.2 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: 1kFfkz-00H6QY-1Y Subject: [Openvpn-devel] [PATCH 4/4] Document the simple self-signed certificate setup in examples 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 Also remove the static key setup example as it is less secure and we want to avoid it for new setups as we want to slowly deprecate these. Signed-off-by: Arne Schwabe Acked-by: Antonio Quartulli --- Changes.rst | 5 ++++ doc/man-sections/examples.rst | 46 ++++++++++++++++++++------------ doc/man-sections/tls-options.rst | 4 +++ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Changes.rst b/Changes.rst index 6007412c..6ae706c6 100644 --- a/Changes.rst +++ b/Changes.rst @@ -8,6 +8,11 @@ Certificate pinning/verify peer fingerprint fingerprint of the peer. The option has use a number of SHA256 fingerprints. +TLS mode with self-signed certificates + When ``--peer-fingerprint`` is used, the ``--ca`` and ``--capath`` option + become optional. This allows for small OpenVPN setups without setting up + a PKI with Easy-RSA or similar software. + New features ------------ Client-specific tls-crypt keys (``--tls-crypt-v2``) diff --git a/doc/man-sections/examples.rst b/doc/man-sections/examples.rst index 3f494ea4..ae7a49b4 100644 --- a/doc/man-sections/examples.rst +++ b/doc/man-sections/examples.rst @@ -60,8 +60,8 @@ that is part of your local subnet for either of the tunnel endpoints, you will get a weird feedback loop. -Example 1: A simple tunnel without security -------------------------------------------- +Example 1: A simple tunnel without security (not recommended) +------------------------------------------------------------- On bob: :: @@ -92,31 +92,41 @@ The ``--verb 9`` option will produce verbose output, similar to the quietly. -Example 2: A tunnel with static-key security (i.e. using a pre-shared secret) ------------------------------------------------------------------------------ +Example 2: A tunnel with self-signed certificates and fingerprint +----------------------------------------------------------------- -First build a static key on bob. +First build a self-signed certificate on bob and display its fingerprint. :: - openvpn --genkey --secret key + openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout bob.pem -out bob.pem -nodes -sha256 -days 3650 -subj '/CN=bob' + openssl x509 -noout -sha256 -fingerprint -in bob.pem -This command will build a key file called ``key`` (in ascii format). Now -copy ``key`` to ``alice.example.com`` over a secure medium such as by using -the ``scp``\(1) program. +and the same on alice: +:: + + openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout alice.pem -out alice.pem -nodes -sha256 -days 3650 -subj '/CN=alice' + openssl x509 -noout -sha256 -fingerprint -in alice.pem + + +These commands will build a text file called ``bob.pem`` or ``alice.pem`` (in ascii format) +that contain both self-signed certificate and key and show the fingerprint of the certificates. +Transfer the fingerprints over a secure medium such as by using +the ``scp``\(1) or ``ssh``\(1) program. On bob: :: - openvpn --remote alice.example.com --dev tun1 \ - --ifconfig 10.4.0.1 10.4.0.2 --verb 5 \ - --secret key + openvpn --ifconfig 10.4.0.1 10.4.0.2 --tls-server --dev tun --dh none \ + --cert bob.pem --key bob.pem --cipher AES-256-GCM \ + --peer-fingerprint "$fingerprint_of_alices_cert" On alice: :: - openvpn --remote bob.example.com --dev tun1 \ - --ifconfig 10.4.0.2 10.4.0.1 --verb 5 \ - --secret key + openvpn --remote bob.example.com --tls-client --dev tun1 \ + --ifconfig 10.4.0.2 10.4.0.1 --cipher AES-256-GCM \ + --cert alice.pem --key alice.pem + --peer-fingerprint "$fingerprint_of_bobs_cert" Now verify the tunnel is working by pinging across the tunnel. @@ -130,9 +140,11 @@ On alice: ping 10.4.0.1 +Note: This example use a elliptic curve (`secp384`), which allows +``--dh`` and to ``none``. -Example 3: A tunnel with full TLS-based security ------------------------------------------------- +Example 3: A tunnel with full PKI and TLS-based security +-------------------------------------------------------- For this test, we will designate ``bob`` as the TLS client and ``alice`` as the TLS server. diff --git a/doc/man-sections/tls-options.rst b/doc/man-sections/tls-options.rst index 658300b8..c2bf573f 100644 --- a/doc/man-sections/tls-options.rst +++ b/doc/man-sections/tls-options.rst @@ -609,6 +609,10 @@ If the option is inlined, ``algo`` is always :code:`SHA256`. 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00 + When the ``--peer-fingerprint`` option is used, specifying a CA with ``--ca`` or ``--capath`` is + optional. This allows the he ``--peer-fingerprint`` to be used as alternative to a PKI with + self-signed certificates for small setups. See the examples section for such a setup. + --verify-x509-name args Accept connections only if a host's X.509 name is equal to **name.** The remote host must also pass all other tests of verification.