Disable the common name fallback on *any* SAN list. This aligns with the Go crypto/x509 behavior and reduces the cases when the SAN to CN fallback occurs. If the certificate is new enough to have a SAN list, even if it only contains email or IP addresses, it is reasonable to assume the certificate is new enough that the common name is not a DNS name. Update-Note: Our certificate verification is getting slightly stricter. Change-Id: I9e3466d8dd8a722405c546181a589f797efa43f9 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35647 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 93bb582..517635e 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc
@@ -693,6 +693,40 @@ "pFFXI2B5usgI\n" "-----END CERTIFICATE-----\n"; +// kCommonNameWithEmailSAN is a leaf certificate signed by kSANTypesRoot, with +// *.host1.test as the common name and the email address test@host2.test in the +// SAN list. +static const char kCommonNameWithEmailSAN[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBvDCCASWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n" + "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n" + "MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI\n" + "zj0CAQYIKoZIzj0DAQcDQgAEtevOxcTjpPzlNGoUMFfZyr1k03/Hiuh+EsnuScDs\n" + "8XLKi6fDkvSaDClI99ycabQZRPIrvyT+dglDC6ugQd+CYqNJMEcwDAYDVR0TAQH/\n" + "BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMBoGA1UdEQQTMBGBD3Rl\n" + "c3RAaG9zdDIudGVzdDANBgkqhkiG9w0BAQsFAAOBgQCGbqb78OWJWl4zb+qw0Dz2\n" + "HJgZZJt6/+nNG/XJKdaYeS4eofsbwsJI4fuuOF6ZvYCJxVNtGqdfZDgycvFA9hjv\n" + "NGosBF1/spP17cmzTahLjxs71jDvHV/EQJbKGl/Zpta1Em1VrzSrwoOFabPXzZTJ\n" + "aet/mER21Z/9ZsTUoJQPJw==\n" + "-----END CERTIFICATE-----\n"; + +// kCommonNameWithIPSAN is a leaf certificate signed by kSANTypesRoot, with +// *.host1.test as the common name and the IP address 127.0.0.1 in the +// SAN list. +static const char kCommonNameWithIPSAN[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBsTCCARqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n" + "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n" + "MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI\n" + "zj0CAQYIKoZIzj0DAQcDQgAEFKrgkxm8PysXbwnHQeTD3p8YY0+sY4ssnZgmj8wX\n" + "KTyn893fdBHWlz71GO6t82wMTF5d+ZYwI2XU52pfl4SB2aM+MDwwDAYDVR0TAQH/\n" + "BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMA8GA1UdEQQIMAaHBH8A\n" + "AAEwDQYJKoZIhvcNAQELBQADgYEAQWZ8Oj059ZjS109V/ijMYT28xuAN5n6HHxCO\n" + "DopTP56Zu9+gme5wTETWEfocspZvgecoUOcedTFoKSQ7JafO09NcVLA+D6ddYpju\n" + "mgfuiLy9dDhqvX/NHaLBMxOBWWbOLwWE+ibyX+pOzjWRCw1L7eUXOr6PhZAOQsmU\n" + "D0+O6KI=\n" + "-----END CERTIFICATE-----\n"; + // CertFromPEM parses the given, NUL-terminated pem block and returns an // |X509*|. static bssl::UniquePtr<X509> CertFromPEM(const char *pem) { @@ -1785,6 +1819,10 @@ ASSERT_TRUE(with_sans); bssl::UniquePtr<X509> without_sans = CertFromPEM(kCommonNameWithoutSANs); ASSERT_TRUE(without_sans); + bssl::UniquePtr<X509> with_email = CertFromPEM(kCommonNameWithEmailSAN); + ASSERT_TRUE(with_email); + bssl::UniquePtr<X509> with_ip = CertFromPEM(kCommonNameWithIPSAN); + ASSERT_TRUE(with_ip); auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) { return Verify( @@ -1804,6 +1842,10 @@ verify_cert(with_sans.get(), 0 /* no flags */, "foo.host3.test")); EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(), 0 /* no flags */, "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_email.get(), 0 /* no flags */, "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_ip.get(), 0 /* no flags */, "foo.host1.test")); // X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT is ignored. EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, @@ -1818,6 +1860,12 @@ EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_email.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, + "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_ip.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, + "foo.host1.test")); // X509_CHECK_FLAG_NEVER_CHECK_SUBJECT implements the correct behavior: the // common name is never checked. @@ -1833,4 +1881,10 @@ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(without_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_email.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, + "foo.host1.test")); + EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, + verify_cert(with_ip.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, + "foo.host1.test")); }
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index ebda63a..51db478 100644 --- a/crypto/x509v3/v3_utl.c +++ b/crypto/x509v3/v3_utl.c
@@ -955,7 +955,6 @@ int j; int cnid = NID_undef; int alt_type; - int san_present = 0; int rv = 0; equal_fn equal; @@ -988,7 +987,6 @@ gen = sk_GENERAL_NAME_value(gens, i); if (gen->type != check_type) continue; - san_present = 1; if (check_type == GEN_EMAIL) cstr = gen->d.rfc822Name; else if (check_type == GEN_DNS) @@ -1001,10 +999,7 @@ break; } GENERAL_NAMES_free(gens); - if (rv != 0) - return rv; - if (san_present) - return 0; + return rv; } /* We're done if CN-ID is not pertinent */