From patchwork Sat Sep 17 13:32:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 2772 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director13.mail.ord1d.rsapps.net ([172.31.255.6]) by backend30.mail.ord1d.rsapps.net with LMTP id GGvFJU9ZJmOHBQAAIUCqbw (envelope-from ) for ; Sat, 17 Sep 2022 19:33:35 -0400 Received: from proxy14.mail.iad3b.rsapps.net ([172.31.255.6]) by director13.mail.ord1d.rsapps.net with LMTP id wEnGJU9ZJmO4eAAA91zNiA (envelope-from ) for ; Sat, 17 Sep 2022 19:33:35 -0400 Received: from smtp23.gate.iad3b ([172.31.255.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy14.mail.iad3b.rsapps.net with LMTPS id kHT/Hk9ZJmPpDgAA+7ETDg (envelope-from ) for ; Sat, 17 Sep 2022 19:33:35 -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: smtp23.gate.iad3b.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; dkim=fail (signature verification failed) header.d=unstable.cc; dmarc=none (p=nil; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: 25935aae-36e1-11ed-90b5-525400aa5716-1-1 Received: from [216.105.38.7] ([216.105.38.7:43516] helo=lists.sourceforge.net) by smtp23.gate.iad3b.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id D4/58-22300-E4956236; Sat, 17 Sep 2022 19:33:35 -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.95) (envelope-from ) id 1oZhJ1-0006Hk-98; Sat, 17 Sep 2022 23:33:03 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1oZhJ0-0006He-L8 for openvpn-devel@lists.sourceforge.net; Sat, 17 Sep 2022 23:33:02 +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: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:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=a/8wiGziVtoXOV1qSCNO+x6VVJJRrPtLitjKcquI6uA=; b=BTHybLYIYEvdNR4/NGB53WjEZw sbZqGYgOdtVJc19AWHg2y8lf1fiL/vkpKWVBeTit136UO5X+jxrp4Zp2YKd1kxxLmmTod5qfOGgrV vPefI8xqVCJrmOhuFcHQ0cLjmmjTBCOS+FLKOKfaJTfyNOzIkedXHofQCh/8znFutlm0=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version: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:In-Reply-To: References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=a/8wiGziVtoXOV1qSCNO+x6VVJJRrPtLitjKcquI6uA=; b=F bwz1G4oDc3KeonkVSMnRcpa6fAEtbJGQnhOBEPNjc8MJ1YklUjre5/FS7EgZ4DYrrh83Gn4vuJaGK yRMlYw5MafNPqJYfNrFQx8aqFHGMy2YA5EJuoUaterjprLZSPLAH3xMmmbuXnF53lR1PdumFpuIbI hgR1ca/u43TgiPh0=; Received: from wilbur.contactoffice.com ([212.3.242.68]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1oZhIz-00BgDI-Gy for openvpn-devel@lists.sourceforge.net; Sat, 17 Sep 2022 23:33:02 +0000 Received: from smtpauth1.co-bxl (smtpauth1.co-bxl [10.2.0.15]) by wilbur.contactoffice.com (Postfix) with ESMTP id 5A37611FE; Sun, 18 Sep 2022 01:32:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1663457575; s=20220809-q8oc; d=unstable.cc; i=a@unstable.cc; h=From:Cc:Date:Message-Id:MIME-Version:Content-Transfer-Encoding; l=7941; bh=a/8wiGziVtoXOV1qSCNO+x6VVJJRrPtLitjKcquI6uA=; b=Cwc35uF23LtmD8xNfG7B6Sdbkia9ncDEcupiD4AI6Mslpi/Y8NbeC9u/Zrq5jVRM 63bdn7tMY925CPM+i8oYx2JEKsdPEX8SuTSp2GYV780037h3l5kFDMSAoGcbl0AMVP3 va7jqCi/88nobRQCJN49NYpltm1+253/Uxkwy9O7wwyBM6SyWXL9riZd7VW6M6VIzP/ SdC14S4zxiDpe37EoCZyYYkWOOfSKo98oBauFsHcMyu6Q+EFEAdjgXlZmgIbFcCMBCQ K27YgjmRu+O0du0j3jIBRYAnlibNN3lwKRTiJVtmhkDe64ZTAKBniniVAkndoBkA+wE X97m7m8XjA== Received: by smtp.mailfence.com with ESMTPSA ; Sun, 18 Sep 2022 01:32:52 +0200 (CEST) From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Sun, 18 Sep 2022 01:32:44 +0200 Message-Id: <20220917233244.13774-1-a@unstable.cc> X-Mailer: git-send-email 2.35.1 MIME-Version: 1.0 X-Spam-Status: No, hits=-2.9 required=4.7 symbols=ALL_TRUSTED, BAYES_00 device=10.2.0.20 X-ContactOffice-Account: com:375058688 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: From: Mateusz Markowicz When using "--verify-x509-name [hostname] subject-alt-name" hostname will now be accepted also when matched against one of the X509v3 Subject Alternative Name IP or DNS entries (instead of just Subjec [...] Content analysis details: (-0.9 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/, low trust [212.3.242.68 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 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: unstable.cc] 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1oZhIz-00BgDI-Gy Subject: [Openvpn-devel] [PATCH] openssl: alternative names support for --verify-x509-name CN checks 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: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Mateusz Markowicz When using "--verify-x509-name [hostname] subject-alt-name" hostname will now be accepted also when matched against one of the X509v3 Subject Alternative Name IP or DNS entries (instead of just Subject's CN). While at it, fix a few uncrustify complaints to allow committing this change. Signed-off-by: Mateusz Markowicz --- doc/man-sections/tls-options.rst | 9 ++++--- src/openvpn/options.c | 4 +++ src/openvpn/ssl_verify.c | 46 ++++++++++++++++++-------------- src/openvpn/ssl_verify.h | 1 + src/openvpn/ssl_verify_backend.h | 8 ++++-- src/openvpn/ssl_verify_mbedtls.c | 7 ++++- src/openvpn/ssl_verify_openssl.c | 16 +++++++---- 7 files changed, 59 insertions(+), 32 deletions(-) diff --git a/doc/man-sections/tls-options.rst b/doc/man-sections/tls-options.rst index d51aff77..257c779a 100644 --- a/doc/man-sections/tls-options.rst +++ b/doc/man-sections/tls-options.rst @@ -647,10 +647,11 @@ If the option is inlined, ``algo`` is always :code:`SHA256`. Which X.509 name is compared to ``name`` depends on the setting of type. ``type`` can be :code:`subject` to match the complete subject DN - (default), :code:`name` to match a subject RDN or :code:`name-prefix` to - match a subject RDN prefix. Which RDN is verified as name depends on the - ``--x509-username-field`` option. But it defaults to the common name - (CN), e.g. a certificate with a subject DN + (default), :code:`name` to match a subject RDN, :code:`name-prefix` to + match a subject RDN prefix or :code:`subject-alt-name` to match the subject + SAN (or the CN if SAN is not specified). Which RDN is verified as name + depends on the ``--x509-username-field`` option. But it defaults to the + common name (CN), e.g. a certificate with a subject DN :: C=KG, ST=NA, L=Bishkek, CN=Server-1 diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 76c09a0a..bde7ec98 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -8894,6 +8894,10 @@ add_option(struct options *options, { type = VERIFY_X509_SUBJECT_RDN_PREFIX; } + else if (streq(p[2], "subject-alt-name")) + { + type = VERIFY_X509_SAN; + } else { msg(msglevel, "unknown X.509 name type: %s", p[2]); diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 45545c83..786d23ba 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -377,31 +377,37 @@ verify_peer_cert(const struct tls_options *opt, openvpn_x509_cert_t *peer_cert, /* verify X509 name or username against --verify-x509-[user]name */ if (opt->verify_x509_type != VERIFY_X509_NONE) { - if ( (opt->verify_x509_type == VERIFY_X509_SUBJECT_DN - && strcmp(opt->verify_x509_name, subject) == 0) - || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN - && strcmp(opt->verify_x509_name, common_name) == 0) - || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN_PREFIX - && strncmp(opt->verify_x509_name, common_name, - strlen(opt->verify_x509_name)) == 0) ) + bool match; + if (opt->verify_x509_type == VERIFY_X509_SAN) { - msg(D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject); + bool have_alt_names; + match = x509v3_is_host_in_alternative_names(peer_cert, opt->verify_x509_name, + &have_alt_names); + if (!match && !have_alt_names) + { + match = (strcmp(opt->verify_x509_name, common_name) == 0); + } } else { - bool verfified_with_alt_names = opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN && - x509v3_is_host_in_alternative_names(peer_cert, opt->verify_x509_name); + match = (opt->verify_x509_type == VERIFY_X509_SUBJECT_DN + && strcmp(opt->verify_x509_name, subject) == 0) + || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN + && strcmp(opt->verify_x509_name, common_name) == 0) + || (opt->verify_x509_type == VERIFY_X509_SUBJECT_RDN_PREFIX + && strncmp(opt->verify_x509_name, common_name, + strlen(opt->verify_x509_name)) == 0); + } - if (verfified_with_alt_names) - { - msg(D_HANDSHAKE, "VERIFY X509NAME OK (ALTERNATIVE): %s", opt->verify_x509_name); - } - else - { - msg(D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s", - subject, opt->verify_x509_name); - return FAILURE; /* Reject connection */ - } + if (match) + { + msg(D_HANDSHAKE, "VERIFY X509NAME OK: %s", opt->verify_x509_name); + } + else + { + msg(D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s", + subject, opt->verify_x509_name); + return FAILURE; /* Reject connection */ } } diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h index 30dfc9bc..214243d8 100644 --- a/src/openvpn/ssl_verify.h +++ b/src/openvpn/ssl_verify.h @@ -64,6 +64,7 @@ struct cert_hash_set { #define VERIFY_X509_SUBJECT_DN 1 #define VERIFY_X509_SUBJECT_RDN 2 #define VERIFY_X509_SUBJECT_RDN_PREFIX 3 +#define VERIFY_X509_SAN 4 enum tls_auth_status { diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h index 4e17e751..948daae2 100644 --- a/src/openvpn/ssl_verify_backend.h +++ b/src/openvpn/ssl_verify_backend.h @@ -269,8 +269,12 @@ result_t x509_write_pem(FILE *peercert_file, openvpn_x509_cert_t *peercert); bool tls_verify_crl_missing(const struct tls_options *opt); /** - * Return true iff {host} was found in {cert} Subject Alternative Names DNS or IP entries. + * Return true iff {host} was found in {cert} Subject Alternative Names DNS or + * IP entries. + * If has_alt_names is not NULL it'll be set to true iff Subject Alternative + * Names were defined for cert. */ -bool x509v3_is_host_in_alternative_names(openvpn_x509_cert_t *cert, const char* host); +bool x509v3_is_host_in_alternative_names(openvpn_x509_cert_t *cert, + const char *host, bool *has_alt_name); #endif /* SSL_VERIFY_BACKEND_H_ */ diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c index 63e94b79..228e7fb6 100644 --- a/src/openvpn/ssl_verify_mbedtls.c +++ b/src/openvpn/ssl_verify_mbedtls.c @@ -263,9 +263,14 @@ x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc) } bool -x509v3_is_host_in_alternative_names(mbedtls_x509_crt *cert, const char* host) +x509v3_is_host_in_alternative_names(mbedtls_x509_crt *cert, const char *host, + bool *has_alt_names) { msg(M_WARN, "Missing support for subject alternative names in mbedtls."); + if (has_alt_names) + { + *has_alt_names = false; + } return false; } diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c index 3f3d99d0..3c6f629b 100644 --- a/src/openvpn/ssl_verify_openssl.c +++ b/src/openvpn/ssl_verify_openssl.c @@ -377,9 +377,14 @@ err: } bool -x509v3_is_host_in_alternative_names(X509 *cert, const char* host) +x509v3_is_host_in_alternative_names(X509 *cert, const char *host, + bool *has_alt_names) { - GENERAL_NAMES* altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + GENERAL_NAMES *altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (has_alt_names) + { + *has_alt_names = (altnames != NULL); + } if (altnames == NULL) { return false; @@ -388,7 +393,7 @@ x509v3_is_host_in_alternative_names(X509 *cert, const char* host) int n = sk_GENERAL_NAME_num(altnames); for (int i = 0; i < n; i++) { - GENERAL_NAME* altname = sk_GENERAL_NAME_value(altnames, i); + GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i); ASN1_STRING *altname_asn1 = NULL; if (altname->type == GEN_DNS) { @@ -401,8 +406,9 @@ x509v3_is_host_in_alternative_names(X509 *cert, const char* host) if (altname_asn1 != NULL) { - char* altname_cstr = NULL; - if (ASN1_STRING_to_UTF8((unsigned char **)&altname_cstr, altname_asn1) >= 0) { + char *altname_cstr = NULL; + if (ASN1_STRING_to_UTF8((unsigned char **)&altname_cstr, altname_asn1) >= 0) + { bool match = strcmp(host, altname_cstr) == 0; OPENSSL_free(altname_cstr); if (match)