Don't create partial X509 and X509_CRL objects to search the X509_STORE

This gets in the way of embedding X509_NAME into X509, as well as giving
X509 and X509_CRL proper C++ destructors.

Change-Id: I8415976a3fc5aa2774a24fb2f7a6c43c3687ceb5
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81893
Commit-Queue: David Benjamin <davidben@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/x509/x509_lu.cc b/crypto/x509/x509_lu.cc
index d3c0630..4599ef9 100644
--- a/crypto/x509/x509_lu.cc
+++ b/crypto/x509/x509_lu.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/err.h>
@@ -87,16 +89,32 @@
   return ctx->method->get_by_subject(ctx, type, name, ret) > 0;
 }
 
-static int x509_object_cmp(const X509_OBJECT *a, const X509_OBJECT *b) {
-  int ret = a->type - b->type;
+// x509_object_cmp_name compares |a| against the specified type and name. This
+// avoids needing to construct a reference certificate or CRL.
+static int x509_object_cmp_name(const X509_OBJECT *a, int type,
+                                const X509_NAME *name) {
+  int ret = a->type - type;
   if (ret) {
     return ret;
   }
-  switch (a->type) {
+  switch (type) {
     case X509_LU_X509:
-      return X509_subject_name_cmp(a->data.x509, b->data.x509);
+      return X509_NAME_cmp(X509_get_subject_name(a->data.x509), name);
     case X509_LU_CRL:
-      return X509_CRL_cmp(a->data.crl, b->data.crl);
+      return X509_NAME_cmp(X509_CRL_get_issuer(a->data.crl), name);
+    default:
+      // abort();
+      return 0;
+  }
+}
+
+static int x509_object_cmp(const X509_OBJECT *a, const X509_OBJECT *b) {
+  switch (b->type) {
+    case X509_LU_X509:
+      return x509_object_cmp_name(a, b->type,
+                                  X509_get_subject_name(b->data.x509));
+    case X509_LU_CRL:
+      return x509_object_cmp_name(a, b->type, X509_CRL_get_issuer(b->data.crl));
     default:
       // abort();
       return 0;
@@ -285,46 +303,46 @@
 }
 
 static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, int type,
-                               const X509_NAME *name, int *pnmatch) {
-  X509_OBJECT stmp;
-  X509 x509_s;
-  X509_CRL crl_s;
-  X509_CRL_INFO crl_info_s;
-
-  stmp.type = type;
-  switch (type) {
-    case X509_LU_X509:
-      stmp.data.x509 = &x509_s;
-      x509_s.subject = const_cast<X509_NAME*>(name);
-      break;
-    case X509_LU_CRL:
-      stmp.data.crl = &crl_s;
-      crl_s.crl = &crl_info_s;
-      crl_info_s.issuer = const_cast<X509_NAME*>(name);
-      break;
-    default:
-      // abort();
-      return -1;
-  }
-
-  size_t idx;
+                               const X509_NAME *name, int *out_num_match) {
   sk_X509_OBJECT_sort(h);
-  if (!sk_X509_OBJECT_find(h, &idx, &stmp)) {
+
+  // Find the first matching object. |sk_X509_OBJECT_find| would require
+  // constructing an |X509| or |X509_CRL| object, so implement our own binary
+  // search.
+  size_t start = 0, end = sk_X509_OBJECT_num(h);
+  while (end - start > 1) {
+    // Bias |mid| towards |start|. The range has more than one element, so |mid|
+    // is not the last element.
+    size_t mid = start + (end - start - 1) / 2;
+    assert(start <= mid && mid + 1 < end);
+    int r = x509_object_cmp_name(sk_X509_OBJECT_value(h, mid), type, name);
+    if (r < 0) {
+      start = mid + 1;  // |mid| is too low.
+    } else if (r > 0) {
+      end = mid;  // |mid| is too high.
+    } else {
+      // |mid| matches, but we need to keep searching to find the first match.
+      end = mid + 1;
+    }
+  }
+  if (start == end ||
+      x509_object_cmp_name(sk_X509_OBJECT_value(h, start), type, name) != 0) {
     return -1;
   }
 
-  if (pnmatch != NULL) {
-    *pnmatch = 1;
-    for (size_t tidx = idx + 1; tidx < sk_X509_OBJECT_num(h); tidx++) {
+  if (out_num_match != nullptr) {
+    *out_num_match = 1;
+    for (size_t tidx = start + 1; tidx < sk_X509_OBJECT_num(h); tidx++) {
       const X509_OBJECT *tobj = sk_X509_OBJECT_value(h, tidx);
-      if (x509_object_cmp(tobj, &stmp)) {
+      if (x509_object_cmp_name(tobj, type, name) != 0) {
         break;
       }
-      (*pnmatch)++;
+      (*out_num_match)++;
     }
   }
 
-  return (int)idx;
+  assert(start <= INT_MAX);  // |STACK_OF(T)| never stores more than |INT_MAX|.
+  return static_cast<int>(start);
 }
 
 static int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,