Align with OpenSSL on constness of static ASN1_OBJECTs.

ASN1_OBJECTs are awkward. Sometimes they are static, when returned from
OBJ_nid2obj, and sometimes they are dynamic, when parsed from
crypto/asn1.

Most structures in crypto/asn1 need to support unknown OIDs and thus
must own their ASN1_OBJECTs. But they also may be initialized with
static ones in various APIs, such as X509_ALGOR_set0. To make that work,
ASN1_OBJECT_free detects static ASN1_OBJECTs and is a no-op.

Functions like X509_ALGOR_set0 take ownership, so OpenSSL has them take
a non-const ASN1_OBJECT*. To match, OBJ_nid2obj then returns a non-const
ASN1_OBJECT*, to signal that it is freeable.

However, this means OBJ_nid2obj's mutability doesn't match its return
type. In the fork, we switched OBJ_nid2obj to return const. But, in
doing so, we had to make X509_ALGOR_set0 and X509_PUBKEY_set0_param take
const ASN1_OBJECT, even though they would actually take ownership of
dynamic ASN1_OBJECTs. There are also a few internal casts with a TODO to
be const-correct.

Neither situation is ideal. (Perhaps a more sound model would be to copy
static ASN1_OBJECTs before putting them in most structs. But that would
not match current usage.) But I think aligning with OpenSSL is the
lesser evil here, since it avoids misleading set0 functions. Managing
ownership of ASN1_OBJECTs is much more common than mutating them. To
that end, I've added a note that ASN1_OBJECTs you didn't create must be
assumed immutable[*].

Update-Note: The change to OBJ_nid2obj should be compatible. The changes
to X509_PUBKEY_set0_param and X509_ALGOR_set0 may require fixing some
pointer types.

[*] This is *almost* honored by all of our functions. The exception is
c2i_ASN1_OBJECT, which instead checks the DYNAMIC flag as part of the
object reuse business. This would come up if we ever embedded
ASN1_OBJECTs directly in structs.

Change-Id: I1e6c700645c12b43323dd3887adb74e795c285b9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46164
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/obj/obj.c b/crypto/obj/obj.c
index 3bf1abf..3053132 100644
--- a/crypto/obj/obj.c
+++ b/crypto/obj/obj.c
@@ -338,12 +338,12 @@
   return 1;
 }
 
-const ASN1_OBJECT *OBJ_nid2obj(int nid) {
+ASN1_OBJECT *OBJ_nid2obj(int nid) {
   if (nid >= 0 && nid < NUM_NID) {
     if (nid != NID_undef && kObjects[nid].nid == NID_undef) {
       goto err;
     }
-    return &kObjects[nid];
+    return (ASN1_OBJECT *)&kObjects[nid];
   }
 
   CRYPTO_STATIC_MUTEX_lock_read(&global_added_lock);
@@ -411,7 +411,7 @@
     }
 
     if (nid != NID_undef) {
-      return (ASN1_OBJECT*) OBJ_nid2obj(nid);
+      return OBJ_nid2obj(nid);
     }
   }
 
diff --git a/crypto/pkcs7/pkcs7_x509.c b/crypto/pkcs7/pkcs7_x509.c
index 107bdea..5afd28b 100644
--- a/crypto/pkcs7/pkcs7_x509.c
+++ b/crypto/pkcs7/pkcs7_x509.c
@@ -235,7 +235,7 @@
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(PKCS7));
-  ret->type = (ASN1_OBJECT *)OBJ_nid2obj(NID_pkcs7_signed);
+  ret->type = OBJ_nid2obj(NID_pkcs7_signed);
   ret->d.sign = OPENSSL_malloc(sizeof(PKCS7_SIGNED));
   if (ret->d.sign == NULL) {
     goto err;
diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c
index 9ab6e9d..d7da620 100644
--- a/crypto/x509/x509_req.c
+++ b/crypto/x509/x509_req.c
@@ -245,7 +245,7 @@
         goto err;
     at = NULL;
     attr->single = 0;
-    attr->object = (ASN1_OBJECT *)OBJ_nid2obj(nid);
+    attr->object = OBJ_nid2obj(nid);
     if (!req->req_info->attributes) {
         if (!(req->req_info->attributes = sk_X509_ATTRIBUTE_new_null()))
             goto err;
diff --git a/crypto/x509/x_algor.c b/crypto/x509/x_algor.c
index 13c9a8c..7b3d790 100644
--- a/crypto/x509/x_algor.c
+++ b/crypto/x509/x_algor.c
@@ -77,8 +77,7 @@
 
 IMPLEMENT_ASN1_SET_OF(X509_ALGOR)
 
-int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype,
-                    void *pval)
+int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj, int ptype, void *pval)
 {
     if (!alg)
         return 0;
@@ -89,9 +88,8 @@
             return 0;
     }
     if (alg) {
-        if (alg->algorithm)
-            ASN1_OBJECT_free(alg->algorithm);
-        alg->algorithm = (ASN1_OBJECT *)aobj;
+        ASN1_OBJECT_free(alg->algorithm);
+        alg->algorithm = aobj;
     }
     if (ptype == 0)
         return 1;
diff --git a/crypto/x509/x_attrib.c b/crypto/x509/x_attrib.c
index 9d9397c..194f9e1 100644
--- a/crypto/x509/x_attrib.c
+++ b/crypto/x509/x_attrib.c
@@ -85,7 +85,7 @@
 
 X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value)
 {
-    const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+    ASN1_OBJECT *obj = OBJ_nid2obj(nid);
     if (obj == NULL) {
         return NULL;
     }
@@ -96,9 +96,7 @@
         goto err;
     }
 
-    /* TODO(fork): const correctness. |ASN1_OBJECT| is messy because static
-     * objects are const but freeable with a no-op |ASN1_OBJECT_free|. */
-    ret->object = (ASN1_OBJECT *)obj;
+    ret->object = obj;
     ret->single = 0;
     ret->value.set = sk_ASN1_TYPE_new_null();
     if (ret->value.set == NULL ||
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index 37dee49..c3fb1e3 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -180,9 +180,8 @@
     return NULL;
 }
 
-int X509_PUBKEY_set0_param(X509_PUBKEY *pub, const ASN1_OBJECT *aobj,
-                           int ptype, void *pval,
-                           unsigned char *penc, int penclen)
+int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj, int ptype,
+                           void *pval, unsigned char *penc, int penclen)
 {
     if (!X509_ALGOR_set0(pub->algor, aobj, ptype, pval))
         return 0;
diff --git a/crypto/x509v3/v3_cpols.c b/crypto/x509v3/v3_cpols.c
index 216e7ae..a2ed01c 100644
--- a/crypto/x509v3/v3_cpols.c
+++ b/crypto/x509v3/v3_cpols.c
@@ -239,8 +239,7 @@
                 goto merr;
             if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual))
                 goto merr;
-            /* TODO(fork): const correctness */
-            qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_cps);
+            qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
             if (qual->pqualid == NULL) {
                 OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR);
                 goto err;
@@ -307,8 +306,7 @@
     POLICYQUALINFO *qual;
     if (!(qual = POLICYQUALINFO_new()))
         goto merr;
-    /* TODO(fork): const correctness */
-    qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_unotice);
+    qual->pqualid = OBJ_nid2obj(NID_id_qt_unotice);
     if (qual->pqualid == NULL) {
         OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR);
         goto err;
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h
index 9269553..3c75ee8 100644
--- a/include/openssl/asn1.h
+++ b/include/openssl/asn1.h
@@ -356,6 +356,16 @@
 #define ASN1_OBJECT_FLAG_DYNAMIC 0x01          // internal use
 #define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04  // internal use
 #define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08     // internal use
+
+// An asn1_object_st (aka |ASN1_OBJECT|) represents an ASN.1 OBJECT IDENTIFIER.
+//
+// Note: Although the struct is exposed, mutating an |ASN1_OBJECT| is only
+// permitted when initializing it. The library maintains a table of static
+// |ASN1_OBJECT|s, which may be referenced by non-const |ASN1_OBJECT| pointers.
+// Code which receives an |ASN1_OBJECT| pointer externally must assume it is
+// immutable, even if the pointer is not const.
+//
+// TODO(davidben): Document this more completely in its own section.
 struct asn1_object_st {
   const char *sn, *ln;
   int nid;
diff --git a/include/openssl/obj.h b/include/openssl/obj.h
index 764188f..49b7adc 100644
--- a/include/openssl/obj.h
+++ b/include/openssl/obj.h
@@ -124,9 +124,22 @@
 
 // Getting information about nids.
 
-// OBJ_nid2obj returns the ASN1_OBJECT corresponding to |nid|, or NULL if |nid|
-// is unknown.
-OPENSSL_EXPORT const ASN1_OBJECT *OBJ_nid2obj(int nid);
+// OBJ_nid2obj returns the |ASN1_OBJECT| corresponding to |nid|, or NULL if
+// |nid| is unknown.
+//
+// This function returns a static, immutable |ASN1_OBJECT|. Although the output
+// is not const, callers may not mutate it. It is also not necessary to release
+// the object with |ASN1_OBJECT_free|.
+//
+// However, functions like |X509_ALGOR_set0| expect to take ownership of a
+// possibly dynamically-allocated |ASN1_OBJECT|. |ASN1_OBJECT_free| is a no-op
+// for static |ASN1_OBJECT|s, so |OBJ_nid2obj| is compatible with such
+// functions.
+//
+// Callers are encouraged to store the result of this function in a const
+// pointer. However, if using functions like |X509_ALGOR_set0|, callers may use
+// a non-const pointer and manage ownership.
+OPENSSL_EXPORT ASN1_OBJECT *OBJ_nid2obj(int nid);
 
 // OBJ_nid2sn returns the short name for |nid|, or NULL if |nid| is unknown.
 OPENSSL_EXPORT const char *OBJ_nid2sn(int nid);
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 86896af..5263d31 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -905,7 +905,7 @@
 OPENSSL_EXPORT X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev);
 OPENSSL_EXPORT X509_REQ *X509_REQ_dup(X509_REQ *req);
 OPENSSL_EXPORT X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn);
-OPENSSL_EXPORT int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj,
+OPENSSL_EXPORT int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj,
                                    int ptype, void *pval);
 OPENSSL_EXPORT void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype,
                                     const void **ppval,
@@ -1538,10 +1538,9 @@
                                    const unsigned char **pk, int *ppklen,
                                    X509_ALGOR **pa, PKCS8_PRIV_KEY_INFO *p8);
 
-OPENSSL_EXPORT int X509_PUBKEY_set0_param(X509_PUBKEY *pub,
-                                          const ASN1_OBJECT *aobj, int ptype,
-                                          void *pval, unsigned char *penc,
-                                          int penclen);
+OPENSSL_EXPORT int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj,
+                                          int ptype, void *pval,
+                                          unsigned char *penc, int penclen);
 OPENSSL_EXPORT int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg,
                                           const unsigned char **pk, int *ppklen,
                                           X509_ALGOR **pa, X509_PUBKEY *pub);