From 405c7888a7070c7106a94e7dfb96c15d267c0aeb Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 10 Dec 2021 23:57:34 -0500 Subject: [PATCH] Rewrite X.509 name-matching tests. This replaces v3name_test.cc which is rather difficult to follow. v3name_test.cc ran all pairs of names against each other, used a default case-insensitivity rule, and then had a list of string exceptions to that rule. This is hopefully easier for us to adjust later. It also fixes a testing bug we wouldn't notice if an expected "exception" didn't fire. Sadly, we cannot use designated initializers in C++ yet. MSVC does not support them until MSVC 2019. Change-Id: Ia8e3bf5f57d33a9bf1fc929ba1e8cd2a270a8a24 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50725 Reviewed-by: Adam Langley Commit-Queue: Adam Langley --- crypto/CMakeLists.txt | 1 - crypto/x509/x509_test.cc | 321 +++++++++++++++++++++++++++ crypto/x509v3/v3name_test.cc | 413 ----------------------------------- 3 files changed, 321 insertions(+), 414 deletions(-) delete mode 100644 crypto/x509v3/v3name_test.cc diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 9c724b74d..d73ce1e3b 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -552,7 +552,6 @@ add_executable( x509/x509_test.cc x509/x509_time_test.cc x509v3/tab_test.cc - x509v3/v3name_test.cc $ $ diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 77c383a66..01f0bc7b3 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -3533,3 +3533,324 @@ TEST(X509Test, BER) { // Padding bits in BIT STRINGs must be zero in BER. EXPECT_FALSE(CertFromPEM(kNonZeroPadding)); } + +TEST(X509Test, Names) { + bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); + ASSERT_TRUE(key); + bssl::UniquePtr root = + MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); + ASSERT_TRUE(root); + ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); + + struct { + std::vector> cert_subject; + std::vector cert_dns_names; + std::vector cert_emails; + std::vector valid_dns_names; + std::vector invalid_dns_names; + std::vector valid_emails; + std::vector invalid_emails; + unsigned flags; + } kTests[] = { + // DNS names only match DNS names and do so case-insensitively. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{"example.com", "WWW.EXAMPLE.COM"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/ + {"example.com", "EXAMPLE.COM", "www.example.com", "WWW.EXAMPLE.COM"}, + /*invalid_dns_names=*/{"test.example.com", "example.org"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{"test@example.com", "example.com"}, + /*flags=*/0, + }, + + // DNS wildcards match exactly one component. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{"*.example.com", "*.EXAMPLE.ORG"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/ + {"www.example.com", "WWW.EXAMPLE.COM", "www.example.org", + "WWW.EXAMPLE.ORG"}, + /*invalid_dns_names=*/{"example.com", "test.www.example.com"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{"test@example.com", "www.example.com"}, + /*flags=*/0, + }, + + // DNS wildcards can be disabled. + // TODO(davidben): Can we remove this feature? Does anyone use it? + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{"example.com", "*.example.com"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{"example.com"}, + /*invalid_dns_names=*/{"www.example.com"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/X509_CHECK_FLAG_NO_WILDCARDS, + }, + + // Invalid DNS wildcards do not match. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/ + {"a.*", "**.b.example", "*c.example", "d*.example", "e*e.example", + "*", ".", "..", "*."}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/ + {"a.example", "test.b.example", "cc.example", "dd.example", + "eee.example", "f", "g."}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // IDNs match like any other DNS labels. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/ + {"xn--rger-koa.a.example", "*.xn--rger-koa.b.example", + "www.xn--rger-koa.c.example"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/ + {"xn--rger-koa.a.example", "www.xn--rger-koa.b.example", + "www.xn--rger-koa.c.example"}, + /*invalid_dns_names=*/ + {"www.xn--rger-koa.a.example", "xn--rger-koa.b.example", + "www.xn--rger-koa.d.example"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // For now, DNS names are also extracted out of the common name, but only + // there is no SAN list. + // TODO(https://crbug.com/boringssl/464): Remove this. + { + /*cert_subject=*/{{NID_commonName, "a.example"}, + {NID_commonName, "*.b.example"}}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{}, + /*valid_dns_names=*/ + {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, + /*invalid_dns_names=*/{}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + { + /*cert_subject=*/{{NID_commonName, "a.example"}, + {NID_commonName, "*.b.example"}}, + /*cert_dns_names=*/{"example.com"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/ + {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // Other subject RDNs do not provide DNS names. + { + /*cert_subject=*/{{NID_organizationName, "example.com"}}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/{"example.com"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // Input DNS names cannot have wildcards. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{"www.example.com"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/{"*.example.com"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // However, OpenSSL has some non-standard behavior that implements this + // with a different syntax. + // TODO(https://crbug.com/boringssl/463): Remove this. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{"www.a.example", "*.b.test"}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{".a.example", ".b.test", ".example", ".test"}, + /*invalid_dns_names=*/{".www.a.example", ".www.b.test"}, + /*valid_emails=*/{}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + + // Emails match case-sensitively before the '@' and case-insensitively + // after. They do not match DNS names. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{"test@a.example", "TEST@B.EXAMPLE"}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/{"a.example", "b.example"}, + /*valid_emails=*/ + {"test@a.example", "test@A.EXAMPLE", "TEST@b.example", + "TEST@B.EXAMPLE"}, + /*invalid_emails=*/ + {"TEST@a.example", "test@B.EXAMPLE", "another-test@a.example", + "est@a.example"}, + /*flags=*/0, + }, + + // Emails may also be found in the subject. + { + /*cert_subject=*/{{NID_pkcs9_emailAddress, "test@a.example"}, + {NID_pkcs9_emailAddress, "TEST@B.EXAMPLE"}}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/{"a.example", "b.example"}, + /*valid_emails=*/ + {"test@a.example", "test@A.EXAMPLE", "TEST@b.example", + "TEST@B.EXAMPLE"}, + /*invalid_emails=*/ + {"TEST@a.example", "test@B.EXAMPLE", "another-test@a.example", + "est@a.example"}, + /*flags=*/0, + }, + + // There are no email wildcard names. + { + /*cert_subject=*/{}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{"test@*.a.example", "@b.example", "*@c.example"}, + /*valid_dns_names=*/{}, + /*invalid_dns_names=*/{}, + /*valid_emails=*/{}, + /*invalid_emails=*/ + {"test@test.a.example", "test@b.example", "test@c.example"}, + /*flags=*/0, + }, + + // Unrelated RDNs can be skipped when looking in the subject. + { + /*cert_subject=*/{{NID_organizationName, "Acme Corporation"}, + {NID_commonName, "a.example"}, + {NID_pkcs9_emailAddress, "test@b.example"}, + {NID_countryName, "US"}}, + /*cert_dns_names=*/{}, + /*cert_emails=*/{}, + /*valid_dns_names=*/{"a.example"}, + /*invalid_dns_names=*/{}, + /*valid_emails=*/{"test@b.example"}, + /*invalid_emails=*/{}, + /*flags=*/0, + }, + }; + + size_t i = 0; + for (const auto &t : kTests) { + SCOPED_TRACE(i++); + + // Issue a test certificate. + bssl::UniquePtr cert = + MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); + ASSERT_TRUE(cert); + if (!t.cert_subject.empty()) { + bssl::UniquePtr subject(X509_NAME_new()); + ASSERT_TRUE(subject); + for (const auto &entry : t.cert_subject) { + ASSERT_TRUE(X509_NAME_add_entry_by_NID( + subject.get(), entry.first, MBSTRING_ASC, + reinterpret_cast(entry.second.data()), + entry.second.size(), /*loc=*/-1, /*set=*/0)); + } + ASSERT_TRUE(X509_set_subject_name(cert.get(), subject.get())); + } + bssl::UniquePtr sans(sk_GENERAL_NAME_new_null()); + ASSERT_TRUE(sans); + for (const auto &dns : t.cert_dns_names) { + bssl::UniquePtr name(GENERAL_NAME_new()); + ASSERT_TRUE(name); + name->type = GEN_DNS; + name->d.dNSName = ASN1_IA5STRING_new(); + ASSERT_TRUE(name->d.dNSName); + ASSERT_TRUE(ASN1_STRING_set(name->d.dNSName, dns.data(), dns.size())); + ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); + } + for (const auto &email : t.cert_emails) { + bssl::UniquePtr name(GENERAL_NAME_new()); + ASSERT_TRUE(name); + name->type = GEN_EMAIL; + name->d.rfc822Name = ASN1_IA5STRING_new(); + ASSERT_TRUE(name->d.rfc822Name); + ASSERT_TRUE( + ASN1_STRING_set(name->d.rfc822Name, email.data(), email.size())); + ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); + } + if (sk_GENERAL_NAME_num(sans.get()) != 0) { + ASSERT_TRUE(X509_add1_ext_i2d(cert.get(), NID_subject_alt_name, + sans.get(), /*crit=*/0, /*flags=*/0)); + } + ASSERT_TRUE(X509_sign(cert.get(), key.get(), EVP_sha256())); + + for (const auto &dns : t.valid_dns_names) { + SCOPED_TRACE(dns); + EXPECT_EQ(1, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, + /*peername=*/nullptr)); + EXPECT_EQ(X509_V_OK, + Verify(cert.get(), {root.get()}, /*intermediates=*/{}, + /*crls=*/{}, /*flags=*/0, [&](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( + param, dns.data(), dns.size())); + X509_VERIFY_PARAM_set_hostflags(param, t.flags); + })); + } + + for (const auto &dns : t.invalid_dns_names) { + SCOPED_TRACE(dns); + EXPECT_EQ(0, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, + /*peername=*/nullptr)); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + Verify(cert.get(), {root.get()}, /*intermediates=*/{}, + /*crls=*/{}, /*flags=*/0, [&](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( + param, dns.data(), dns.size())); + X509_VERIFY_PARAM_set_hostflags(param, t.flags); + })); + } + + for (const auto &email : t.valid_emails) { + SCOPED_TRACE(email); + EXPECT_EQ( + 1, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); + EXPECT_EQ(X509_V_OK, + Verify(cert.get(), {root.get()}, /*intermediates=*/{}, + /*crls=*/{}, /*flags=*/0, [&](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( + param, email.data(), email.size())); + X509_VERIFY_PARAM_set_hostflags(param, t.flags); + })); + } + + for (const auto &email : t.invalid_emails) { + SCOPED_TRACE(email); + EXPECT_EQ( + 0, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); + EXPECT_EQ(X509_V_ERR_EMAIL_MISMATCH, + Verify(cert.get(), {root.get()}, /*intermediates=*/{}, + /*crls=*/{}, /*flags=*/0, [&](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( + param, email.data(), email.size())); + X509_VERIFY_PARAM_set_hostflags(param, t.flags); + })); + } + } +} diff --git a/crypto/x509v3/v3name_test.cc b/crypto/x509v3/v3name_test.cc deleted file mode 100644 index a501115cd..000000000 --- a/crypto/x509v3/v3name_test.cc +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project - * 1999. - */ -/* ==================================================================== - * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * licensing@OpenSSL.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). */ - -#include -#include - -#include - -#include -#include -#include -#include - -#include "../internal.h" -#include "internal.h" - - -static const char *const names[] = { - "a", "b", ".", "*", "@", - ".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..", - "-example.com", "example-.com", - "@@", "**", "*.com", "*com", "*.*.com", "*com", "com*", "*example.com", - "*@example.com", "test@*.example.com", "example.com", "www.example.com", - "test.www.example.com", "*.example.com", "*.www.example.com", - "test.*.example.com", "www.*.com", - ".www.example.com", "*www.example.com", - "example.net", "xn--rger-koa.example.com", - "*.xn--rger-koa.example.com", "www.xn--rger-koa.example.com", - "*.good--example.com", "www.good--example.com", - "*.xn--bar.com", "xn--foo.xn--bar.com", - "a.example.com", "b.example.com", - "postmaster@example.com", "Postmaster@example.com", - "postmaster@EXAMPLE.COM", - NULL -}; - -static const char *const exceptions[] = { - "set CN: host: [*.example.com] matches [a.example.com]", - "set CN: host: [*.example.com] matches [b.example.com]", - "set CN: host: [*.example.com] matches [www.example.com]", - "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]", - "set CN: host: [*.www.example.com] matches [test.www.example.com]", - "set CN: host: [*.www.example.com] matches [.www.example.com]", - "set CN: host: [*www.example.com] matches [www.example.com]", - "set CN: host: [test.www.example.com] matches [.www.example.com]", - "set CN: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]", - "set CN: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]", - "set CN: host: [*.good--example.com] matches [www.good--example.com]", - "set CN: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", - "set CN: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", - "set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]", - "set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]", - "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]", - "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]", - "set dnsName: host: [*.example.com] matches [www.example.com]", - "set dnsName: host: [*.example.com] matches [a.example.com]", - "set dnsName: host: [*.example.com] matches [b.example.com]", - "set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]", - "set dnsName: host: [*.www.example.com] matches [test.www.example.com]", - "set dnsName: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", - "set dnsName: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", - "set dnsName: host: [*.www.example.com] matches [.www.example.com]", - "set dnsName: host: [*www.example.com] matches [www.example.com]", - "set dnsName: host: [test.www.example.com] matches [.www.example.com]", - "set dnsName: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]", - "set dnsName: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]", - "set dnsName: host: [*.good--example.com] matches [www.good--example.com]", - "set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]", - "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]", - "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]", - "set rfc822Name: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]", - NULL -}; - -static int is_exception(const char *msg) -{ - const char *const *p; - for (p = exceptions; *p; ++p) - if (strcmp(msg, *p) == 0) - return 1; - return 0; -} - -static int set_cn(X509 *crt, ...) -{ - int ret = 0; - X509_NAME *n = NULL; - va_list ap; - va_start(ap, crt); - n = X509_NAME_new(); - if (n == NULL) - goto out; - while (1) { - int nid; - const char *name; - nid = va_arg(ap, int); - if (nid == 0) - break; - name = va_arg(ap, const char *); - if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC, - (unsigned char *)name, -1, -1, 1)) - goto out; - } - if (!X509_set_subject_name(crt, n)) - goto out; - ret = 1; - out: - X509_NAME_free(n); - va_end(ap); - return ret; -} - -/* - * int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc); X509_EXTENSION - * *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid, int crit, - * ASN1_OCTET_STRING *data); int X509_add_ext(X509 *x, X509_EXTENSION *ex, - * int loc); - */ - -static int set_altname(X509 *crt, ...) -{ - int ret = 0; - GENERAL_NAMES *gens = NULL; - GENERAL_NAME *gen = NULL; - ASN1_IA5STRING *ia5 = NULL; - va_list ap; - va_start(ap, crt); - gens = sk_GENERAL_NAME_new_null(); - if (gens == NULL) - goto out; - while (1) { - int type; - const char *name; - type = va_arg(ap, int); - if (type == 0) - break; - name = va_arg(ap, const char *); - - gen = GENERAL_NAME_new(); - if (gen == NULL) - goto out; - ia5 = ASN1_IA5STRING_new(); - if (ia5 == NULL) - goto out; - if (!ASN1_STRING_set(ia5, name, -1)) - goto out; - switch (type) { - case GEN_EMAIL: - case GEN_DNS: - GENERAL_NAME_set0_value(gen, type, ia5); - ia5 = NULL; - break; - default: - abort(); - } - sk_GENERAL_NAME_push(gens, gen); - gen = NULL; - } - if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0)) - goto out; - ret = 1; - out: - ASN1_IA5STRING_free(ia5); - GENERAL_NAME_free(gen); - GENERAL_NAMES_free(gens); - va_end(ap); - return ret; -} - -static int set_cn1(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, name, 0); -} - -static int set_cn_and_email(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, name, - NID_pkcs9_emailAddress, "dummy@example.com", 0); -} - -static int set_cn2(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, "dummy value", - NID_commonName, name, 0); -} - -static int set_cn3(X509 *crt, const char *name) -{ - return set_cn(crt, NID_commonName, name, - NID_commonName, "dummy value", 0); -} - -static int set_email1(X509 *crt, const char *name) -{ - return set_cn(crt, NID_pkcs9_emailAddress, name, 0); -} - -static int set_email2(X509 *crt, const char *name) -{ - return set_cn(crt, NID_pkcs9_emailAddress, "dummy@example.com", - NID_pkcs9_emailAddress, name, 0); -} - -static int set_email3(X509 *crt, const char *name) -{ - return set_cn(crt, NID_pkcs9_emailAddress, name, - NID_pkcs9_emailAddress, "dummy@example.com", 0); -} - -static int set_email_and_cn(X509 *crt, const char *name) -{ - return set_cn(crt, NID_pkcs9_emailAddress, name, - NID_commonName, "www.example.org", 0); -} - -static int set_altname_dns(X509 *crt, const char *name) -{ - return set_altname(crt, GEN_DNS, name, 0); -} - -static int set_altname_email(X509 *crt, const char *name) -{ - return set_altname(crt, GEN_EMAIL, name, 0); -} - -struct set_name_fn { - int (*fn) (X509 *, const char *); - const char *name; - int host; - int email; -}; - -static const struct set_name_fn name_fns[] = { - {set_cn1, "set CN", 1, 0}, - {set_cn2, "set CN", 1, 0}, - {set_cn3, "set CN", 1, 0}, - {set_cn_and_email, "set CN", 1, 0}, - {set_email1, "set emailAddress", 0, 1}, - {set_email2, "set emailAddress", 0, 1}, - {set_email3, "set emailAddress", 0, 1}, - {set_email_and_cn, "set emailAddress", 0, 1}, - {set_altname_dns, "set dnsName", 1, 0}, - {set_altname_email, "set rfc822Name", 0, 1}, - {NULL, NULL, 0, 0}, -}; - -static X509 *make_cert(void) -{ - X509 *ret = NULL; - X509 *crt = NULL; - X509_NAME *issuer = NULL; - crt = X509_new(); - if (crt == NULL) - goto out; - if (!X509_set_version(crt, X509_VERSION_3)) - goto out; - ret = crt; - crt = NULL; - out: - X509_NAME_free(issuer); - return ret; -} - -static int errors; - -static void check_message(const struct set_name_fn *fn, const char *op, - const char *nameincert, int match, const char *name) -{ - char msg[1024]; - if (match < 0) - return; - BIO_snprintf(msg, sizeof(msg), "%s: %s: [%s] %s [%s]", - fn->name, op, nameincert, - match ? "matches" : "does not match", name); - if (is_exception(msg)) - return; - puts(msg); - ++errors; -} - -static void run_cert(X509 *crt, const char *nameincert, - const struct set_name_fn *fn) -{ - const char *const *pname = names; - while (*pname) { - int samename = OPENSSL_strcasecmp(nameincert, *pname) == 0; - size_t namelen = strlen(*pname); - char *name = (char *)malloc(namelen); - int match, ret; - OPENSSL_memcpy(name, *pname, namelen); - - ret = X509_check_host(crt, name, namelen, 0, NULL); - match = -1; - if (ret < 0) { - fprintf(stderr, "internal error in X509_check_host\n"); - ++errors; - } else if (fn->host) { - if (ret == 1 && !samename) - match = 1; - if (ret == 0 && samename) - match = 0; - } else if (ret == 1) - match = 1; - check_message(fn, "host", nameincert, match, *pname); - - ret = X509_check_host(crt, name, namelen, - X509_CHECK_FLAG_NO_WILDCARDS, NULL); - match = -1; - if (ret < 0) { - fprintf(stderr, "internal error in X509_check_host\n"); - ++errors; - } else if (fn->host) { - if (ret == 1 && !samename) - match = 1; - if (ret == 0 && samename) - match = 0; - } else if (ret == 1) - match = 1; - check_message(fn, "host-no-wildcards", nameincert, match, *pname); - - ret = X509_check_email(crt, name, namelen, 0); - match = -1; - if (fn->email) { - if (ret && !samename) - match = 1; - if (!ret && samename && strchr(nameincert, '@') != NULL) - match = 0; - } else if (ret) - match = 1; - check_message(fn, "email", nameincert, match, *pname); - ++pname; - free(name); - } -} - -// TODO(davidben): Convert this test to GTest more thoroughly. -TEST(X509V3Test, NameTest) { - const struct set_name_fn *pfn = name_fns; - while (pfn->name) { - const char *const *pname = names; - while (*pname) { - // The common name fallback requires the name look sufficiently - // DNS-like. - if (strcmp(pfn->name, "set CN") == 0 && - !x509v3_looks_like_dns_name( - reinterpret_cast(*pname), - strlen(*pname))) { - ++pname; - continue; - } - bssl::UniquePtr crt(make_cert()); - ASSERT_TRUE(crt); - ASSERT_TRUE(pfn->fn(crt.get(), *pname)); - run_cert(crt.get(), *pname, pfn); - ++pname; - } - ++pfn; - } - EXPECT_EQ(0, errors); -}