Fix handling of EXFLAG_INVALID_POLICY on the leaf.

X509_policy_check returns -1 if some certificate had an unparseable
extension, in which case it sets EXFLAG_INVALID_POLICY on it. The
calling code then iterates over the certificates to find the offending
one, so the callback has a chance to undo it. But it skips i = 0, the
leaf, and instead just silentely returns success.

We really should cut down on the callback's ability to mess things up
here but, in the meantime, fix this. Also add a test covering this case.

While I'm here, I've updated make_invalid_extensions.go, which I pulled
some code from, to rename fooOrPanic to mustFoo. That seems to be the
convention in the Go standard library. (regexp.MustCompile, etc.)

Change-Id: Ib07c9f4175e66483bd7c0f7d49aea931bf36e53f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55748
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/test/make_invalid_extensions.go b/crypto/x509/test/make_invalid_extensions.go
index d0c2cee..aba2d71 100644
--- a/crypto/x509/test/make_invalid_extensions.go
+++ b/crypto/x509/test/make_invalid_extensions.go
@@ -49,9 +49,9 @@
 var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey
 
 func init() {
-	leafKey = ecdsaKeyFromPEMOrPanic(leafKeyPEM)
-	intermediateKey = ecdsaKeyFromPEMOrPanic(intermediateKeyPEM)
-	rootKey = ecdsaKeyFromPEMOrPanic(rootKeyPEM)
+	leafKey = mustParseECDSAKey(leafKeyPEM)
+	intermediateKey = mustParseECDSAKey(intermediateKeyPEM)
+	rootKey = mustParseECDSAKey(rootKeyPEM)
 }
 
 type templateAndKey struct {
@@ -59,7 +59,7 @@
 	key      *ecdsa.PrivateKey
 }
 
-func generateCertificateOrPanic(path string, subject, issuer *templateAndKey) []byte {
+func mustGenerateCertificate(path string, subject, issuer *templateAndKey) []byte {
 	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
 	if err != nil {
 		panic(err)
@@ -135,9 +135,9 @@
 	}
 
 	// Generate a valid certificate chain from the templates.
-	generateCertificateOrPanic("invalid_extension_root.pem", &root, &root)
-	generateCertificateOrPanic("invalid_extension_intermediate.pem", &intermediate, &root)
-	leafDER := generateCertificateOrPanic("invalid_extension_leaf.pem", &leaf, &intermediate)
+	mustGenerateCertificate("invalid_extension_root.pem", &root, &root)
+	mustGenerateCertificate("invalid_extension_intermediate.pem", &intermediate, &root)
+	leafDER := mustGenerateCertificate("invalid_extension_leaf.pem", &leaf, &intermediate)
 
 	leafCert, err := x509.ParseCertificate(leafDER)
 	if err != nil {
@@ -151,15 +151,15 @@
 
 		rootInvalid := root
 		rootInvalid.template.ExtraExtensions = invalidExtension
-		generateCertificateOrPanic(fmt.Sprintf("invalid_extension_root_%s.pem", ext.name), &rootInvalid, &rootInvalid)
+		mustGenerateCertificate(fmt.Sprintf("invalid_extension_root_%s.pem", ext.name), &rootInvalid, &rootInvalid)
 
 		intermediateInvalid := intermediate
 		intermediateInvalid.template.ExtraExtensions = invalidExtension
-		generateCertificateOrPanic(fmt.Sprintf("invalid_extension_intermediate_%s.pem", ext.name), &intermediateInvalid, &root)
+		mustGenerateCertificate(fmt.Sprintf("invalid_extension_intermediate_%s.pem", ext.name), &intermediateInvalid, &root)
 
 		leafInvalid := leaf
 		leafInvalid.template.ExtraExtensions = invalidExtension
-		generateCertificateOrPanic(fmt.Sprintf("invalid_extension_leaf_%s.pem", ext.name), &leafInvalid, &intermediate)
+		mustGenerateCertificate(fmt.Sprintf("invalid_extension_leaf_%s.pem", ext.name), &leafInvalid, &intermediate)
 
 		// Additionally generate a copy of the leaf certificate with extra data in
 		// the extension.
@@ -177,7 +177,7 @@
 
 		leafTrailingData := leaf
 		leafTrailingData.template.ExtraExtensions = trailingDataExtension
-		generateCertificateOrPanic(fmt.Sprintf("trailing_data_leaf_%s.pem", ext.name), &leafTrailingData, &intermediate)
+		mustGenerateCertificate(fmt.Sprintf("trailing_data_leaf_%s.pem", ext.name), &leafTrailingData, &intermediate)
 	}
 }
 
@@ -199,7 +199,7 @@
 ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt
 -----END PRIVATE KEY-----`
 
-func ecdsaKeyFromPEMOrPanic(in string) *ecdsa.PrivateKey {
+func mustParseECDSAKey(in string) *ecdsa.PrivateKey {
 	keyBlock, _ := pem.Decode([]byte(in))
 	if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" {
 		panic("could not decode private key")
diff --git a/crypto/x509/test/make_policy_certs.go b/crypto/x509/test/make_policy_certs.go
new file mode 100644
index 0000000..18a6ffe
--- /dev/null
+++ b/crypto/x509/test/make_policy_certs.go
@@ -0,0 +1,174 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+// make_policy_certs.go generates certificates for testing policy handling.
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/pem"
+	"math/big"
+	"os"
+	"time"
+)
+
+var (
+	// https://davidben.net/oid
+	testOID1 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 1})
+	testOID2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 2})
+
+	// https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4
+	certificatePoliciesOID = asn1.ObjectIdentifier([]int{2, 5, 29, 32})
+)
+
+var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey
+
+func init() {
+	leafKey = mustParseECDSAKey(leafKeyPEM)
+	intermediateKey = mustParseECDSAKey(intermediateKeyPEM)
+	rootKey = mustParseECDSAKey(rootKeyPEM)
+}
+
+type templateAndKey struct {
+	template x509.Certificate
+	key      *ecdsa.PrivateKey
+}
+
+func mustGenerateCertificate(path string, subject, issuer *templateAndKey) []byte {
+	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
+	if err != nil {
+		panic(err)
+	}
+	file, err := os.Create(path)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+	err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	if err != nil {
+		panic(err)
+	}
+	return cert
+}
+
+func main() {
+	notBefore, err := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
+	if err != nil {
+		panic(err)
+	}
+	notAfter, err := time.Parse(time.RFC3339, "2100-01-01T00:00:00Z")
+	if err != nil {
+		panic(err)
+	}
+
+	root := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(1),
+			Subject:               pkix.Name{CommonName: "Policy Root"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+		},
+		key: rootKey,
+	}
+	intermediate := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(2),
+			Subject:               pkix.Name{CommonName: "Policy Intermediate"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			PolicyIdentifiers:     []asn1.ObjectIdentifier{testOID1, testOID2},
+		},
+		key: intermediateKey,
+	}
+	leaf := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(3),
+			Subject:               pkix.Name{CommonName: "www.example.com"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  false,
+			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			DNSNames:              []string{"www.example.com"},
+			PolicyIdentifiers:     []asn1.ObjectIdentifier{testOID1, testOID2},
+		},
+		key: leafKey,
+	}
+
+	// Generate a valid certificate chain from the templates.
+	mustGenerateCertificate("policy_root.pem", &root, &root)
+	mustGenerateCertificate("policy_intermediate.pem", &intermediate, &root)
+	mustGenerateCertificate("policy_leaf.pem", &leaf, &intermediate)
+
+	// Introduce syntax errors in the leaf and intermediate.
+	leafInvalid := leaf
+	leafInvalid.template.PolicyIdentifiers = nil
+	leafInvalid.template.ExtraExtensions = []pkix.Extension{{Id: certificatePoliciesOID, Value: []byte("INVALID")}}
+	mustGenerateCertificate("policy_leaf_invalid.pem", &leafInvalid, &root)
+
+	intermediateInvalid := intermediate
+	intermediateInvalid.template.PolicyIdentifiers = nil
+	intermediateInvalid.template.ExtraExtensions = []pkix.Extension{{Id: certificatePoliciesOID, Value: []byte("INVALID")}}
+	mustGenerateCertificate("policy_intermediate_invalid.pem", &intermediateInvalid, &root)
+
+	// TODO(davidben): Generate more certificates to test policy validation more
+	// extensively, including an intermediate with constraints. For now this
+	// just tests the basic case.
+}
+
+const leafKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk
+024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5
+w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X
+-----END PRIVATE KEY-----`
+
+const intermediateKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6
+vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE
+Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw
+-----END PRIVATE KEY-----`
+
+const rootKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J
+Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq
+ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt
+-----END PRIVATE KEY-----`
+
+func mustParseECDSAKey(in string) *ecdsa.PrivateKey {
+	keyBlock, _ := pem.Decode([]byte(in))
+	if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" {
+		panic("could not decode private key")
+	}
+	key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
+	if err != nil {
+		panic(err)
+	}
+	return key.(*ecdsa.PrivateKey)
+}
diff --git a/crypto/x509/test/policy_intermediate.pem b/crypto/x509/test/policy_intermediate.pem
new file mode 100644
index 0000000..b36868c
--- /dev/null
+++ b/crypto/x509/test/policy_intermediate.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBrDCCAVGgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
+AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
+jQ6Dg7CTpVZVVH+bguT7JTCjgYUwgYIwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQM
+MAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJDS9/4O7qhr
+CIRhwsXrPVBagG2uMCsGA1UdIAQkMCIwDwYNKoZIhvcSBAGEtwkCATAPBg0qhkiG
+9xIEAYS3CQICMAoGCCqGSM49BAMCA0kAMEYCIQCcgAbQr/HNdHwPEcWotOqtXXGH
+di6cAJtWaSynP8+UoQIhAPEMK79OO+tJHzmD0N01OdZefAwKlYZvDCQvAfAQVf7j
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_invalid.pem b/crypto/x509/test/policy_intermediate_invalid.pem
new file mode 100644
index 0000000..7467317
--- /dev/null
+++ b/crypto/x509/test/policy_intermediate_invalid.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBjDCCATKgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
+AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
+jQ6Dg7CTpVZVVH+bguT7JTCjZzBlMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAK
+BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ0vf+Du6oawiE
+YcLF6z1QWoBtrjAOBgNVHSAEB0lOVkFMSUQwCgYIKoZIzj0EAwIDSAAwRQIgf9Jt
+wpHxfA3j6Z8+h88MSh2MHkDGhWcnRY9VboMR/RoCIQDiSiaPGISK/31JBhNVvNnK
+IBo822QHPPMWDR/K/nyWiA==
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf.pem b/crypto/x509/test/policy_leaf.pem
new file mode 100644
index 0000000..b140704
--- /dev/null
+++ b/crypto/x509/test/policy_leaf.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBpjCCAU2gAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo34wfDAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wKwYDVR0gBCQwIjAPBg0qhkiG9xIEAYS3CQIBMA8GDSqGSIb3EgQB
+hLcJAgIwCgYIKoZIzj0EAwIDRwAwRAIgPTm7NO8gR+z8BqA6gV9FVwrSmOAJVzyu
+5loq9ZTtIS0CIEjBbvBcY4+Y3xWL4SUFQKQk3pNZ37xJoz2v+/yvEE5/
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_invalid.pem b/crypto/x509/test/policy_leaf_invalid.pem
new file mode 100644
index 0000000..6c0f56f
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_invalid.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBgjCCASigAwIBAgIBAzAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowGjEYMBYGA1UE
+AxMPd3d3LmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkSrY
+vFVtkZJmvirfY0JDDYrZQrNJecPLt0ksJux2URL5nAQiQY1SERGnEaiNLpoc0dle
+TS8wQT/cjw/wPgoeV6NhMF8wDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQMMAoGCCsG
+AQUFBwMBMAwGA1UdEwEB/wQCMAAwGgYDVR0RBBMwEYIPd3d3LmV4YW1wbGUuY29t
+MA4GA1UdIAQHSU5WQUxJRDAKBggqhkjOPQQDAgNIADBFAiBhnGGMJBM2gTBo9r4C
+NDR89ECTU7dwdvFyOGOIOOZEFgIhAIRIhGdQ9eRRi2qMhN1F19P5VsIUuc4VL1bW
+sXO8fwZM
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_root.pem b/crypto/x509/test/policy_root.pem
new file mode 100644
index 0000000..7bb209a
--- /dev/null
+++ b/crypto/x509/test/policy_root.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBdDCCARqgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowFjEUMBIGA1UE
+AxMLUG9saWN5IFJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQmdqXYl1Gv
+Y7y3jcTTK6MVXIQr44TqChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAP
+EPSJwPndjolto1cwVTAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU0GnnoB+yeN63WMthnh6Uh1HH
+dRIwCgYIKoZIzj0EAwIDSAAwRQIgctaVgroxlAkLhPEaTXvsE3ePYM2X+KGOJZXc
+usyO3YkCIQDN1RLJq9vHGjZzDCEehKjxHsV+XSAkdfU7nB7KjVHTKA==
+-----END CERTIFICATE-----
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index e5e1b84..a69b284 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -5094,3 +5094,93 @@
   ASSERT_TRUE(ASN1_INTEGER_get_int64(&val, X509_get0_serialNumber(root.get())));
   EXPECT_EQ(-1, val);
 }
+
+TEST(X509Test, Policy) {
+  bssl::UniquePtr<ASN1_OBJECT> oid1(
+      OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1));
+  ASSERT_TRUE(oid1);
+  bssl::UniquePtr<ASN1_OBJECT> oid2(
+      OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1));
+  ASSERT_TRUE(oid2);
+  bssl::UniquePtr<ASN1_OBJECT> oid3(
+      OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1));
+  ASSERT_TRUE(oid3);
+
+  bssl::UniquePtr<X509> root(
+      CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str()));
+  ASSERT_TRUE(root);
+  bssl::UniquePtr<X509> intermediate(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_intermediate.pem").c_str()));
+  ASSERT_TRUE(intermediate);
+  bssl::UniquePtr<X509> intermediate_invalid(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str()));
+  ASSERT_TRUE(intermediate_invalid);
+  bssl::UniquePtr<X509> leaf(
+      CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str()));
+  ASSERT_TRUE(leaf);
+  bssl::UniquePtr<X509> leaf_invalid(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str()));
+  ASSERT_TRUE(leaf_invalid);
+
+  // By default, OpenSSL does not check policies, so even syntax errors in the
+  // certificatePolicies extension go unnoticed. (This is probably not
+  // important.)
+  EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()},
+                              {intermediate.get()}, /*crls=*/{}));
+  EXPECT_EQ(X509_V_OK, Verify(leaf_invalid.get(), {root.get()},
+                              {intermediate.get()}, /*crls=*/{}));
+
+  auto set_policies = [](X509_VERIFY_PARAM *param,
+                         std::vector<const ASN1_OBJECT *> oids) {
+    for (const ASN1_OBJECT *oid : oids) {
+      bssl::UniquePtr<ASN1_OBJECT> copy(OBJ_dup(oid));
+      ASSERT_TRUE(copy);
+      ASSERT_TRUE(X509_VERIFY_PARAM_add0_policy(param, copy.get()));
+      copy.release();  // |X509_VERIFY_PARAM_add0_policy| takes ownership on
+                       // success.
+    }
+  };
+
+  // The chain is good for |oid1| and |oid2|, but not |oid3|.
+  EXPECT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{},
+                   X509_V_FLAG_EXPLICIT_POLICY, [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid1.get()});
+                   }));
+  EXPECT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{},
+                   X509_V_FLAG_EXPLICIT_POLICY, [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid2.get()});
+                   }));
+  EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{},
+                   X509_V_FLAG_EXPLICIT_POLICY, [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid3.get()});
+                   }));
+  EXPECT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{},
+                   X509_V_FLAG_EXPLICIT_POLICY, [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid1.get(), oid2.get()});
+                   }));
+  EXPECT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{},
+                   X509_V_FLAG_EXPLICIT_POLICY, [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid1.get(), oid3.get()});
+                   }));
+
+  // The policy extension in the intermediate cannot be parsed.
+  EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION,
+            Verify(leaf.get(), {root.get()}, {intermediate_invalid.get()},
+                   /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                   [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid1.get()});
+                   }));
+
+  // The policy extension in the leaf cannot be parsed.
+  EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION,
+            Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()},
+                   /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                   [&](X509_VERIFY_PARAM *param) {
+                     set_policies(param, {oid1.get()});
+                   }));
+}
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 7d445f7..ce110e6 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -1706,10 +1706,8 @@
   // Invalid or inconsistent extensions
   if (ret == -1) {
     // Locate certificates with bad extensions and notify callback.
-    X509 *x;
-    size_t i;
-    for (i = 1; i < sk_X509_num(ctx->chain); i++) {
-      x = sk_X509_value(ctx->chain, i);
+    for (size_t i = 0; i < sk_X509_num(ctx->chain); i++) {
+      X509 *x = sk_X509_value(ctx->chain, i);
       if (!(x->ex_flags & EXFLAG_INVALID_POLICY)) {
         continue;
       }
diff --git a/sources.cmake b/sources.cmake
index 294de93..69a1f64 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -111,6 +111,11 @@
   crypto/x509/test/many_names1.pem
   crypto/x509/test/many_names2.pem
   crypto/x509/test/many_names3.pem
+  crypto/x509/test/policy_root.pem
+  crypto/x509/test/policy_intermediate_invalid.pem
+  crypto/x509/test/policy_intermediate.pem
+  crypto/x509/test/policy_leaf_invalid.pem
+  crypto/x509/test/policy_leaf.pem
   crypto/x509/test/pss_sha1_explicit.pem
   crypto/x509/test/pss_sha1_mgf1_syntax_error.pem
   crypto/x509/test/pss_sha1.pem