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 <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 9c724b7..d73ce1e 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -552,7 +552,6 @@
x509/x509_test.cc
x509/x509_time_test.cc
x509v3/tab_test.cc
- x509v3/v3name_test.cc
$<TARGET_OBJECTS:crypto_test_data>
$<TARGET_OBJECTS:boringssl_gtest_main>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 77c383a..01f0bc7 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -3533,3 +3533,324 @@
// Padding bits in BIT STRINGs must be zero in BER.
EXPECT_FALSE(CertFromPEM(kNonZeroPadding));
}
+
+TEST(X509Test, Names) {
+ bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key);
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<X509> 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<std::pair<int, std::string>> cert_subject;
+ std::vector<std::string> cert_dns_names;
+ std::vector<std::string> cert_emails;
+ std::vector<std::string> valid_dns_names;
+ std::vector<std::string> invalid_dns_names;
+ std::vector<std::string> valid_emails;
+ std::vector<std::string> 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<X509> cert =
+ MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false);
+ ASSERT_TRUE(cert);
+ if (!t.cert_subject.empty()) {
+ bssl::UniquePtr<X509_NAME> 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<const unsigned char *>(entry.second.data()),
+ entry.second.size(), /*loc=*/-1, /*set=*/0));
+ }
+ ASSERT_TRUE(X509_set_subject_name(cert.get(), subject.get()));
+ }
+ bssl::UniquePtr<GENERAL_NAMES> sans(sk_GENERAL_NAME_new_null());
+ ASSERT_TRUE(sans);
+ for (const auto &dns : t.cert_dns_names) {
+ bssl::UniquePtr<GENERAL_NAME> 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<GENERAL_NAME> 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 a501115..0000000
--- 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 <stdarg.h>
-#include <string.h>
-
-#include <gtest/gtest.h>
-
-#include <openssl/crypto.h>
-#include <openssl/mem.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-
-#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<const unsigned char*>(*pname),
- strlen(*pname))) {
- ++pname;
- continue;
- }
- bssl::UniquePtr<X509> 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);
-}