Regression test for I7cdf6cbdbd5f07093cdf3c191bfbd5c46a6a6964.

Bug: 495832128
Change-Id: Id95713074dd97cee8fa8fff78e59e0666a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/91528
Presubmit-BoringSSL-Verified: Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Rudolf Polzer <rpolzer@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index d0badfb..8d41872 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -9728,5 +9728,76 @@
   EXPECT_EQ(param_type, V_ASN1_NULL);
 }
 
+TEST(X509Test, X509StoreGet1IssuerMultipleMatches) {
+  // |kLeafPEM| is signed by |kIntermediatePEM|.
+  UniquePtr<X509> cert = CertFromPEM(kLeafPEM);
+  ASSERT_TRUE(cert);
+
+  // Get an intermediate certificate that will match |cert|.
+  UniquePtr<X509> issuer_ok = CertFromPEM(kIntermediatePEM);
+  ASSERT_TRUE(issuer_ok);
+
+  // |issuer_fail| has the same name but a different SKID, so it won't match the
+  // AKID in |cert|, thereby failing validation in
+  // |x509_check_issued_with_callback| via |X509_check_issued|.
+  UniquePtr<X509> issuer_fail(X509_dup(issuer_ok.get()));
+  ASSERT_TRUE(issuer_fail);
+  int loc =
+      X509_get_ext_by_NID(issuer_fail.get(), NID_subject_key_identifier, -1);
+  ASSERT_GE(loc, 0);
+  UniquePtr<X509_EXTENSION> ext(X509_delete_ext(issuer_fail.get(), loc));
+  ASSERT_TRUE(ext);
+
+  // Add a different SKID. 00 sorts before the original.
+  const uint8_t kTag[] = {0x00, 0x00, 0x00};
+  UniquePtr<ASN1_OCTET_STRING> skid(ASN1_OCTET_STRING_new());
+  ASSERT_TRUE(skid);
+  ASSERT_TRUE(ASN1_OCTET_STRING_set(skid.get(), kTag, sizeof(kTag)));
+  ASSERT_TRUE(X509_add1_ext_i2d(issuer_fail.get(), NID_subject_key_identifier,
+                                skid.get(), 0, 0));
+
+  // Sign it. The certificate will be rejected anyway, but it must be signed by
+  // _some_ key to be able to be added - otherwise its |cert_hash| will match
+  // |issuer_ok|'s and |X509_STORE_add_cert| will not even store it.
+  UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kRSAKey);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(X509_sign(issuer_fail.get(), key.get(), EVP_sha256()));
+
+  // Find an unrelated certificate that sorts _before_ the others.
+  // "O=BoringSSL TESTING, CN=Root CA" sorts before "O=BoringSSL TESTING,
+  // CN=Intermediate CA" because it is _shorter_. See |X509_NAME_cmp|.
+  UniquePtr<X509> unrelated_before = CertFromPEM(kRootCAPEM);
+  ASSERT_TRUE(unrelated_before);
+
+  // Create a store, adding |unrelated_before|, then |issuer_fail|, then
+  // |issuer_ok|. |X509_OBJECT_idx_by_subject| will get |issuer_fail|.
+  UniquePtr<X509_STORE> store(X509_STORE_new());
+  ASSERT_TRUE(store);
+  ASSERT_TRUE(X509_STORE_add_cert(store.get(), unrelated_before.get()));
+  ASSERT_TRUE(X509_STORE_add_cert(store.get(), issuer_fail.get()));
+  ASSERT_TRUE(X509_STORE_add_cert(store.get(), issuer_ok.get()));
+  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());
+  ASSERT_TRUE(ctx);
+  ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), nullptr));
+
+  // Validate that a lookup by issuer name will return |issuer_fail|.
+  X509_NAME *xn = X509_get_issuer_name(cert.get());
+  X509_OBJECT obj;
+  ASSERT_EQ(1,
+            X509_STORE_CTX_get_by_subject(ctx.get(), X509_LU_X509, xn, &obj));
+  ASSERT_EQ(0, X509_cmp(X509_OBJECT_get0_X509(&obj), issuer_fail.get()));
+  X509_OBJECT_free_contents(&obj);
+
+  // Check that by actually looking up using the certificate and not just its
+  // issuer name, we can do better and get |issuer_ok|.
+  X509 *found_issuer = nullptr;
+  int get1_ret =
+      X509_STORE_CTX_get1_issuer(&found_issuer, ctx.get(), cert.get());
+  ASSERT_EQ(1, get1_ret);
+  ASSERT_TRUE(found_issuer);
+  EXPECT_EQ(0, X509_cmp(found_issuer, issuer_ok.get()));
+  X509_free(found_issuer);
+}
+
 }  // namespace
 BSSL_NAMESPACE_END