Support the OpenSSL “pass zero for strlen” when setting X.509 hostnames.

BoringSSL does not generally support this quirk but, in this case, we
didn't make it a fatal error and it's instead a silent omission of
hostname checking. This doesn't affect Chrome but, in case something is
using BoringSSL and using this trick, this change makes it safe.

BUG=chromium:824799

Change-Id: If417817b997b9faa9963c09dfc95d06a5d445e0b
Reviewed-on: https://boringssl-review.googlesource.com/26724
Commit-Queue: Adam Langley <alangley@gmail.com>
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index b4cecca..ed4978a 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -487,7 +487,9 @@
                    const std::vector<X509 *> &intermediates,
                    const std::vector<X509_CRL *> &crls,
                    unsigned long flags,
-                   bool use_additional_untrusted) {
+                   bool use_additional_untrusted,
+                   const char *hostname,
+                   size_t hostname_len) {
   bssl::UniquePtr<STACK_OF(X509)> roots_stack(CertsToStack(roots));
   bssl::UniquePtr<STACK_OF(X509)> intermediates_stack(
       CertsToStack(intermediates));
@@ -526,6 +528,7 @@
   }
   X509_VERIFY_PARAM_set_time(param, 1474934400 /* Sep 27th, 2016 */);
   X509_VERIFY_PARAM_set_depth(param, 16);
+  X509_VERIFY_PARAM_set1_host(param, hostname, hostname_len);
   if (flags) {
     X509_VERIFY_PARAM_set_flags(param, flags);
   }
@@ -543,8 +546,10 @@
                    const std::vector<X509 *> &intermediates,
                    const std::vector<X509_CRL *> &crls,
                    unsigned long flags = 0) {
-  const int r1 = Verify(leaf, roots, intermediates, crls, flags, false);
-  const int r2 = Verify(leaf, roots, intermediates, crls, flags, true);
+  const int r1 =
+      Verify(leaf, roots, intermediates, crls, flags, false, nullptr, 0);
+  const int r2 =
+      Verify(leaf, roots, intermediates, crls, flags, true, nullptr, 0);
 
   if (r1 != r2) {
     fprintf(stderr,
@@ -612,6 +617,19 @@
             Verify(forgery.get(),
                    {intermediate_self_signed.get(), root_cross_signed.get()},
                    {leaf_no_key_usage.get(), intermediate.get()}, empty_crls));
+
+  static const char kHostname[] = "example.com";
+  static const char kWrongHostname[] = "example2.com";
+  ASSERT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls,
+                   0, false, kHostname, strlen(kHostname)));
+  // The wrong hostname should trigger a hostname error.
+  ASSERT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls,
+                   0, false, kWrongHostname, strlen(kWrongHostname)));
+  // Passing zero, for this API, is supported for compatibility with OpenSSL.
+  ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()},
+                              empty_crls, 0, false, kHostname, 0));
 }
 
 TEST(X509Test, TestCRL) {
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index d0f8f79..0b03361 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -89,6 +89,14 @@
 {
     char *copy;
 
+    // This is an OpenSSL quirk that BoringSSL typically doesn't support.
+    // However, we didn't make this a fatal error at the time, which was a
+    // mistake. Because of that, given the risk that someone could assume the
+    // OpenSSL semantics from BoringSSL, it's supported in this case.
+    if (name != NULL && namelen == 0) {
+        namelen = strlen(name);
+    }
+
     /*
      * Refuse names with embedded NUL bytes.
      * XXX: Do we need to push an error onto the error stack?