Don't allow SGC EKUs for server certificates.

The Server Gated Cryptography EKUs are a left-over from 1990's export
regulations. For historical reaons, the SGC EKUs are accepted as a valid
substitute for a server authentication EKU, but they shouldn't be. This
change makes it so that we ignore them.

Change-Id: Ie184c52f588ae391a95d61c474ee6324bdd8f4f2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37545
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 1f664b9..521d757 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -817,6 +817,117 @@
     "-----END CERTIFICATE-----\n";
 static const char kCommonNameNotDNS[] = "Not a DNS name";
 
+// The following six certificates are issued by |kSANTypesRoot| and have
+// different extended key usage values. They were created with the following
+// Go program:
+//
+// func main() {
+//     block, _ := pem.Decode([]byte(rootKeyPEM))
+//     rootPriv, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
+//     block, _ = pem.Decode([]byte(rootCertPEM))
+//     root, _ := x509.ParseCertificate(block.Bytes)
+//
+//     leafTemplate := &x509.Certificate{
+//         SerialNumber: big.NewInt(3),
+//         Subject: pkix.Name{
+//             CommonName: "EKU msSGC",
+//         },
+//         NotBefore:             time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
+//         NotAfter:              time.Date(2099, time.January, 1, 0, 0, 0, 0, time.UTC),
+//         BasicConstraintsValid: true,
+//         ExtKeyUsage:           []x509.ExtKeyUsage{FILL IN HERE},
+//     }
+//     leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+//     leafDER, err := x509.CreateCertificate(rand.Reader, leafTemplate, root, &leafKey.PublicKey, rootPriv)
+//     if err != nil {
+//         panic(err)
+//     }
+//     pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: leafDER})
+// }
+
+static const char kMicrosoftSGCCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBtDCCAR2gAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAEEn61v3Vs+q6bTyyRnrJvuKBE8PTNVLbXGB52jig4Qse2\n"
+    "mGygNEysS0uzZ0luz+rn2hDRUFL6sHLUs1d8UMbI/6NEMEIwFQYDVR0lBA4wDAYK\n"
+    "KwYBBAGCNwoDAzAMBgNVHRMBAf8EAjAAMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5U\n"
+    "BCeMWQEwDQYJKoZIhvcNAQELBQADgYEAgDQI9RSo3E3ZVnU71TV/LjG9xwHtfk6I\n"
+    "rlNnlJJ0lsTHAuMc1mwCbzhtsmasetwYlIa9G8GFWB9Gh/QqHA7G649iGGmXShqe\n"
+    "aVDuWgeSEJxBPE2jILoMm4pEYF7jfonTn7XXX6O78yuSlP+NPIU0gUKHkWZ1sWk0\n"
+    "cC4l0r/6jik=\n"
+    "-----END CERTIFICATE-----\n";
+
+static const char kNetscapeSGCCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBszCCARygAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAE3NbT+TnBfq1DWJCezjaUL52YhDU7cOkI2S2PoWgJ1v7x\n"
+    "kKLwBonUFZjppZs69SyBHeJdti+KoJ3qTW+hCG08EaNDMEEwFAYDVR0lBA0wCwYJ\n"
+    "YIZIAYb4QgQBMAwGA1UdEwEB/wQCMAAwGwYDVR0jBBQwEoAQQDfXAftAL7gcflQE\n"
+    "J4xZATANBgkqhkiG9w0BAQsFAAOBgQBuiyVcfazekHkCWksxdFmjPmMtWCxFjkzc\n"
+    "8VBxFE0CfSHQAfZ8J7tXd1FbAq/eXdZvvo8v0JB4sOM4Ex1ob1fuvDFHdSAHAD7W\n"
+    "dhKIjJyzVojoxjCjyue0XMeEPl7RiqbdxoS/R5HFAqAF0T2OeQAqP9gTpOXoau1M\n"
+    "RQHX6HQJJg==\n"
+    "-----END CERTIFICATE-----\n";
+
+static const char kServerEKUCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBsjCCARugAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAEDd35i+VWPwIOKLrLWTuP5cqD+yJDB5nujEzPgkXP5LKJ\n"
+    "SZRbHTqTdpYZB2jy6y90RY2Bsjx7FfZ7nN5G2g1GOKNCMEAwEwYDVR0lBAwwCgYI\n"
+    "KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQn\n"
+    "jFkBMA0GCSqGSIb3DQEBCwUAA4GBAIKmbMBjuivL/rxDu7u7Vr3o3cdmEggBJxwL\n"
+    "iatNW3x1wg0645aNYOktW/iQ7mAAiziTY73GFyfiJDWqnY+CwA94ZWyQidjHdN/I\n"
+    "6BR52sN/dkYEoInYEbmDNMc/if+T0yqeBQLP4BeKLiT8p0qqaimae6LgibS19hDP\n"
+    "2hoEMdz2\n"
+    "-----END CERTIFICATE-----\n";
+
+static const char kServerEKUPlusMicrosoftSGCCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBvjCCASegAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAEDO1MYPxq+U4oXMIK8UnsS4C696wpcu4UOmcMJJ5CUd5Z\n"
+    "ZpJShN6kYKnrb3GK/6xEgbUGntmrzSRG5FYqk6QgD6NOMEwwHwYDVR0lBBgwFgYI\n"
+    "KwYBBQUHAwEGCisGAQQBgjcKAwMwDAYDVR0TAQH/BAIwADAbBgNVHSMEFDASgBBA\n"
+    "N9cB+0AvuBx+VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GBAHOu2IBa4lHzVGS36HxS\n"
+    "SejUE87Ji1ysM6BgkYbfxfS9MuV+J3UnqH57JjbH/3CFl4ZDWceF6SGBSCn8LqKa\n"
+    "KHpwoNFU3zA99iQzVJgbUyN0PbKwHEanLyKDJZyFk71R39ToxhSNQgaQYjZYCy1H\n"
+    "5V9oXd1bodEqVsOZ/mur24Ku\n"
+    "-----END CERTIFICATE-----\n";
+
+static const char kAnyEKU[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBrjCCARegAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAE9nsLABDporlTvx1OBUc4Hd5vxfX+8nS/OhbHmKtFLYNu\n"
+    "1CLLrImbwMQYD2G+PgLO6sQHmASq2jmJKp6ZWsRkTqM+MDwwDwYDVR0lBAgwBgYE\n"
+    "VR0lADAMBgNVHRMBAf8EAjAAMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEw\n"
+    "DQYJKoZIhvcNAQELBQADgYEAxgjgn1SAzQ+2GeCicZ5ndvVhKIeFelGCQ989XTVq\n"
+    "uUbAYBW6v8GXNuVzoXYxDgNSanF6U+w+INrJ6daKVrIxAxdk9QFgBXqJoupuRAA3\n"
+    "/OqnmYux0EqOTLbTK1P8DhaiaD0KV6dWGUwzqsgBmPkZ0lgNaPjvb1mKV3jhBkjz\n"
+    "L6A=\n"
+    "-----END CERTIFICATE-----\n";
+
+static const char kNoEKU[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBnTCCAQagAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+    "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+    "MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C\n"
+    "AQYIKoZIzj0DAQcDQgAEpSFSqbYY86ZcMamE606dqdyjWlwhSHKOLUFsUUIzkMPz\n"
+    "KHRu/x3Yzi8+Hm8eFK/TnCbkpYsYw4hIw00176dYzaMtMCswDAYDVR0TAQH/BAIw\n"
+    "ADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GB\n"
+    "AHvYzynIkjLThExHRS+385hfv4vgrQSMmCM1SAnEIjSBGsU7RPgiGAstN06XivuF\n"
+    "T1fNugRmTu4OtOIbfdYkcjavJufw9hR9zWTt77CNMTy9XmOZLgdS5boFTtLCztr3\n"
+    "TXHOSQQD8Dl4BK0wOet+TP6LBEjHlRFjAqK4bu9xpxV2\n"
+    "-----END CERTIFICATE-----\n";
+
 // CertFromPEM parses the given, NUL-terminated pem block and returns an
 // |X509*|.
 static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
@@ -2073,3 +2184,45 @@
   EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
             verify_cert(not_dns.get(), 0 /* no flags */, kCommonNameNotDNS));
 }
+
+TEST(X509Test, ServerGatedCryptoEKUs) {
+  bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot);
+  ASSERT_TRUE(root);
+  bssl::UniquePtr<X509> ms_sgc = CertFromPEM(kMicrosoftSGCCert);
+  ASSERT_TRUE(ms_sgc);
+  bssl::UniquePtr<X509> ns_sgc = CertFromPEM(kNetscapeSGCCert);
+  ASSERT_TRUE(ns_sgc);
+  bssl::UniquePtr<X509> server_eku = CertFromPEM(kServerEKUCert);
+  ASSERT_TRUE(server_eku);
+  bssl::UniquePtr<X509> server_eku_plus_ms_sgc =
+      CertFromPEM(kServerEKUPlusMicrosoftSGCCert);
+  ASSERT_TRUE(server_eku_plus_ms_sgc);
+  bssl::UniquePtr<X509> any_eku = CertFromPEM(kAnyEKU);
+  ASSERT_TRUE(any_eku);
+  bssl::UniquePtr<X509> no_eku = CertFromPEM(kNoEKU);
+  ASSERT_TRUE(no_eku);
+
+  auto verify_cert = [&root](X509 *leaf) {
+    return Verify(leaf, {root.get()}, /*intermediates=*/{}, /*crls=*/{},
+                  /*flags=*/0, /*use_additional_untrusted=*/false,
+                  [&](X509_VERIFY_PARAM *param) {
+                    ASSERT_TRUE(X509_VERIFY_PARAM_set_purpose(
+                        param, X509_PURPOSE_SSL_SERVER));
+                  });
+  };
+
+  // Neither the Microsoft nor Netscape SGC EKU should be sufficient for
+  // |X509_PURPOSE_SSL_SERVER|. The "any" EKU probably, technically, should be.
+  // However, we've never accepted it and it's not acceptable in leaf
+  // certificates by the Baseline, so perhaps we don't need this complexity.
+  for (X509 *leaf : {ms_sgc.get(), ns_sgc.get(), any_eku.get()}) {
+    EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, verify_cert(leaf));
+  }
+
+  // The server-auth EKU is sufficient, and it doesn't matter if an SGC EKU is
+  // also included. Lastly, not specifying an EKU is also valid.
+  for (X509 *leaf : {server_eku.get(), server_eku_plus_ms_sgc.get(),
+                     no_eku.get()}) {
+    EXPECT_EQ(X509_V_OK, verify_cert(leaf));
+  }
+}
diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c
index 5e1f641..25768c0 100644
--- a/crypto/x509v3/v3_purp.c
+++ b/crypto/x509v3/v3_purp.c
@@ -611,7 +611,7 @@
 static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x,
                                     int ca)
 {
-    if (xku_reject(x, XKU_SSL_SERVER | XKU_SGC))
+    if (xku_reject(x, XKU_SSL_SERVER))
         return 0;
     if (ca)
         return check_ca(x);