Store NID_undef's ASN1_OBJECT outside the table

tasn_*.c have two dependencies on the OID table: initializing
ASN1_OBJECTs to the undef object, and the ADB (ANY DEFINED BY)
machinery. Fix the first by pulling the entry out of the table.  The
latter will be fixed by rewriting the certificate policy parser.

Bug: 551
Change-Id: I7c423ff9ce78b850555203a31c2d220d92d04f35
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/63530
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/asn1/tasn_new.c b/crypto/asn1/tasn_new.c
index cc30bc8..76c52c3 100644
--- a/crypto/asn1/tasn_new.c
+++ b/crypto/asn1/tasn_new.c
@@ -267,7 +267,7 @@
   }
   switch (utype) {
     case V_ASN1_OBJECT:
-      *pval = (ASN1_VALUE *)OBJ_nid2obj(NID_undef);
+      *pval = (ASN1_VALUE *)OBJ_get_undef();
       return 1;
 
     case V_ASN1_BOOLEAN:
diff --git a/crypto/obj/obj.c b/crypto/obj/obj.c
index 6519933..4106424 100644
--- a/crypto/obj/obj.c
+++ b/crypto/obj/obj.c
@@ -179,12 +179,19 @@
   return (size_t)obj->length;
 }
 
+static const ASN1_OBJECT *get_builtin_object(int nid) {
+  // |NID_undef| is stored separately, so all the indices are off by one. The
+  // caller of this function must have a valid built-in, non-undef NID.
+  BSSL_CHECK(nid > 0 && nid < NUM_NID);
+  return &kObjects[nid - 1];
+}
+
 // obj_cmp is called to search the kNIDsInOIDOrder array. The |key| argument is
 // an |ASN1_OBJECT|* that we're looking for and |element| is a pointer to an
 // unsigned int in the array.
 static int obj_cmp(const void *key, const void *element) {
   uint16_t nid = *((const uint16_t *)element);
-  return OBJ_cmp(key, &kObjects[nid]);
+  return OBJ_cmp(key, get_builtin_object(nid));
 }
 
 int OBJ_obj2nid(const ASN1_OBJECT *obj) {
@@ -215,7 +222,7 @@
     return NID_undef;
   }
 
-  return kObjects[*nid_ptr].nid;
+  return get_builtin_object(*nid_ptr)->nid;
 }
 
 int OBJ_cbs2nid(const CBS *cbs) {
@@ -238,7 +245,7 @@
   const char *name = (const char *)key;
   uint16_t nid = *((const uint16_t *)element);
 
-  return strcmp(name, kObjects[nid].sn);
+  return strcmp(name, get_builtin_object(nid)->sn);
 }
 
 int OBJ_sn2nid(const char *short_name) {
@@ -263,7 +270,7 @@
     return NID_undef;
   }
 
-  return kObjects[*nid_ptr].nid;
+  return get_builtin_object(*nid_ptr)->nid;
 }
 
 // long_name_cmp is called to search the kNIDsInLongNameOrder array. The
@@ -273,7 +280,7 @@
   const char *name = (const char *)key;
   uint16_t nid = *((const uint16_t *)element);
 
-  return strcmp(name, kObjects[nid].ln);
+  return strcmp(name, get_builtin_object(nid)->ln);
 }
 
 int OBJ_ln2nid(const char *long_name) {
@@ -297,7 +304,7 @@
     return NID_undef;
   }
 
-  return kObjects[*nid_ptr].nid;
+  return get_builtin_object(*nid_ptr)->nid;
 }
 
 int OBJ_txt2nid(const char *s) {
@@ -324,12 +331,29 @@
   return 1;
 }
 
+const ASN1_OBJECT *OBJ_get_undef(void) {
+  static const ASN1_OBJECT kUndef = {
+      /*sn=*/SN_undef,
+      /*ln=*/LN_undef,
+      /*nid=*/NID_undef,
+      /*length=*/0,
+      /*data=*/NULL,
+      /*flags=*/0,
+  };
+  return &kUndef;
+}
+
 ASN1_OBJECT *OBJ_nid2obj(int nid) {
-  if (nid >= 0 && nid < NUM_NID) {
-    if (nid != NID_undef && kObjects[nid].nid == NID_undef) {
+  if (nid == NID_undef) {
+    return (ASN1_OBJECT *)OBJ_get_undef();
+  }
+
+  if (nid > 0 && nid < NUM_NID) {
+    const ASN1_OBJECT *obj = get_builtin_object(nid);
+    if (nid != NID_undef && obj->nid == NID_undef) {
       goto err;
     }
-    return (ASN1_OBJECT *)&kObjects[nid];
+    return (ASN1_OBJECT *)obj;
   }
 
   CRYPTO_MUTEX_lock_read(&global_added_lock);
diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h
index 4a3bb88..71ef2d2 100644
--- a/crypto/obj/obj_dat.h
+++ b/crypto/obj/obj_dat.h
@@ -7140,7 +7140,6 @@
 };
 
 static const ASN1_OBJECT kObjects[NUM_NID] = {
-    {"UNDEF", "undefined", NID_undef, 0, NULL, 0},
     {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &kObjectData[0], 0},
     {"pkcs", "RSA Data Security, Inc. PKCS", NID_pkcs, 7, &kObjectData[6], 0},
     {"MD2", "md2", NID_md2, 8, &kObjectData[13], 0},
diff --git a/crypto/obj/obj_test.cc b/crypto/obj/obj_test.cc
index 025e1f0..abea30d 100644
--- a/crypto/obj/obj_test.cc
+++ b/crypto/obj/obj_test.cc
@@ -59,6 +59,7 @@
 
   EXPECT_EQ(NID_undef, OBJ_sn2nid("UNDEF"));
   EXPECT_EQ(NID_undef, OBJ_ln2nid("undefined"));
+  EXPECT_EQ(OBJ_get_undef(), OBJ_nid2obj(NID_undef));
 }
 
 TEST(ObjTest, TestSignatureAlgorithms) {
diff --git a/crypto/obj/objects.go b/crypto/obj/objects.go
index a0f35cb..f938e12 100644
--- a/crypto/obj/objects.go
+++ b/crypto/obj/objects.go
@@ -614,6 +614,12 @@
 	// Emit an ASN1_OBJECT for each object.
 	fmt.Fprintf(&b, "\nstatic const ASN1_OBJECT kObjects[NUM_NID] = {\n")
 	for nid, obj := range objs.byNID {
+		// Skip the entry for NID_undef. It is stored separately, so that
+		// OBJ_get_undef avoids pulling in the table.
+		if nid == 0 {
+			continue
+		}
+
 		if len(obj.name) == 0 {
 			fmt.Fprintf(&b, "{NULL, NULL, NID_undef, 0, NULL, 0},\n")
 			continue
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 68d75d5..5a75562 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -6456,7 +6456,7 @@
       MakeTestCert("Issuer", "Subject", key.get(), /*is_ca=*/true);
   ASSERT_TRUE(x509);
   bssl::UniquePtr<X509_EXTENSION> ext(X509_EXTENSION_new());
-  ASSERT_TRUE(X509_EXTENSION_set_object(ext.get(), OBJ_nid2obj(NID_undef)));
+  ASSERT_TRUE(X509_EXTENSION_set_object(ext.get(), OBJ_get_undef()));
   EXPECT_FALSE(X509_add_ext(x509.get(), ext.get(), /*loc=*/-1));
 }
 
diff --git a/include/openssl/obj.h b/include/openssl/obj.h
index 3fb8bde..51c5b30 100644
--- a/include/openssl/obj.h
+++ b/include/openssl/obj.h
@@ -148,6 +148,10 @@
 // a non-const pointer and manage ownership.
 OPENSSL_EXPORT ASN1_OBJECT *OBJ_nid2obj(int nid);
 
+// OBJ_get_undef returns the object for |NID_undef|. Prefer this function over
+// |OBJ_nid2obj| to avoid pulling in the full OID table.
+OPENSSL_EXPORT const ASN1_OBJECT *OBJ_get_undef(void);
+
 // OBJ_nid2sn returns the short name for |nid|, or NULL if |nid| is unknown.
 OPENSSL_EXPORT const char *OBJ_nid2sn(int nid);