Document most X509_NAME functions.

Unfortunately, these functions are not const-correct, even the
accessors. Document what they should have been, especially since
mutating an X509_NAME_ENTRY directly won't even update the 'modified'
bit correctly.

Do a pass at adding consts to our code internally, but since the
functions return non-const pointers, this isn't checked anywhere. And
since serializing an X509_NAME is not always thread-safe, there's a
limit to how much we can correctly mark things as const.

Bug: 426
Change-Id: Ifa3d8bafb5396fbe7b91416f234de4585284c705
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/53326
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/name_print.c b/crypto/x509/name_print.c
index f871df9..29207cc 100644
--- a/crypto/x509/name_print.c
+++ b/crypto/x509/name_print.c
@@ -86,9 +86,6 @@
                       unsigned long flags) {
   int i, prev = -1, orflags, cnt;
   int fn_opt, fn_nid;
-  ASN1_OBJECT *fn;
-  ASN1_STRING *val;
-  X509_NAME_ENTRY *ent;
   char objtmp[80];
   const char *objbuf;
   int outlen, len;
@@ -149,6 +146,7 @@
 
   cnt = X509_NAME_entry_count(n);
   for (i = 0; i < cnt; i++) {
+    const X509_NAME_ENTRY *ent;
     if (flags & XN_FLAG_DN_REV) {
       ent = X509_NAME_get_entry(n, cnt - i - 1);
     } else {
@@ -172,8 +170,8 @@
       }
     }
     prev = X509_NAME_ENTRY_set(ent);
-    fn = X509_NAME_ENTRY_get_object(ent);
-    val = X509_NAME_ENTRY_get_data(ent);
+    const ASN1_OBJECT *fn = X509_NAME_ENTRY_get_object(ent);
+    const ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent);
     fn_nid = OBJ_obj2nid(fn);
     if (fn_opt != XN_FLAG_FN_NONE) {
       int objlen, fld_len;
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 8569d2a..5a97353 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -689,7 +689,7 @@
 }
 
 static int reject_dns_name_in_common_name(X509 *x509) {
-  X509_NAME *name = X509_get_subject_name(x509);
+  const X509_NAME *name = X509_get_subject_name(x509);
   int i = -1;
   for (;;) {
     i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
@@ -697,8 +697,8 @@
       return X509_V_OK;
     }
 
-    X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);
-    ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(entry);
+    const X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);
+    const ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(entry);
     unsigned char *idval;
     int idlen = ASN1_STRING_to_UTF8(&idval, common_name);
     if (idlen < 0) {
diff --git a/crypto/x509/x509name.c b/crypto/x509/x509name.c
index 6771b68..fcfbc0c 100644
--- a/crypto/x509/x509name.c
+++ b/crypto/x509/x509name.c
@@ -80,14 +80,12 @@
 
 int X509_NAME_get_text_by_OBJ(const X509_NAME *name, const ASN1_OBJECT *obj,
                               char *buf, int len) {
-  int i;
-  ASN1_STRING *data;
-
-  i = X509_NAME_get_index_by_OBJ(name, obj, -1);
+  int i = X509_NAME_get_index_by_OBJ(name, obj, -1);
   if (i < 0) {
     return -1;
   }
-  data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
+  const ASN1_STRING *data =
+      X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
   i = (data->length > (len - 1)) ? (len - 1) : data->length;
   if (buf == NULL) {
     return data->length;
@@ -148,51 +146,46 @@
 }
 
 X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name, int loc) {
-  X509_NAME_ENTRY *ret;
-  int i, n, set_prev, set_next;
-  STACK_OF(X509_NAME_ENTRY) *sk;
-
   if (name == NULL || loc < 0 ||
       sk_X509_NAME_ENTRY_num(name->entries) <= (size_t)loc) {
     return NULL;
   }
-  sk = name->entries;
-  ret = sk_X509_NAME_ENTRY_delete(sk, loc);
-  n = sk_X509_NAME_ENTRY_num(sk);
+
+  STACK_OF(X509_NAME_ENTRY) *sk = name->entries;
+  X509_NAME_ENTRY *ret = sk_X509_NAME_ENTRY_delete(sk, loc);
+  int n = sk_X509_NAME_ENTRY_num(sk);
   name->modified = 1;
   if (loc == n) {
     return ret;
   }
 
-  // else we need to fixup the set field
+  int set_prev;
   if (loc != 0) {
-    set_prev = (sk_X509_NAME_ENTRY_value(sk, loc - 1))->set;
+    set_prev = sk_X509_NAME_ENTRY_value(sk, loc - 1)->set;
   } else {
     set_prev = ret->set - 1;
   }
-  set_next = sk_X509_NAME_ENTRY_value(sk, loc)->set;
+  int set_next = sk_X509_NAME_ENTRY_value(sk, loc)->set;
 
-  // set_prev is the previous set set is the current set set_next is the
-  // following prev 1 1 1 1 1 1 1 1 set 1 1 2 2 next 1 1 2 2 2 2 3 2 so
-  // basically only if prev and next differ by 2, then re-number down by 1
+  // If we removed a singleton RDN, update the RDN indices so they are
+  // consecutive again.
   if (set_prev + 1 < set_next) {
-    for (i = loc; i < n; i++) {
+    for (int i = loc; i < n; i++) {
       sk_X509_NAME_ENTRY_value(sk, i)->set--;
     }
   }
   return ret;
 }
 
-int X509_NAME_add_entry_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj, int type,
-                               const unsigned char *bytes, int len, int loc,
-                               int set) {
-  X509_NAME_ENTRY *ne;
-  int ret;
-  ne = X509_NAME_ENTRY_create_by_OBJ(NULL, obj, type, bytes, len);
+int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
+                               int type, const unsigned char *bytes, int len,
+                               int loc, int set) {
+  X509_NAME_ENTRY *ne =
+      X509_NAME_ENTRY_create_by_OBJ(NULL, obj, type, bytes, len);
   if (!ne) {
     return 0;
   }
-  ret = X509_NAME_add_entry(name, ne, loc, set);
+  int ret = X509_NAME_add_entry(name, ne, loc, set);
   X509_NAME_ENTRY_free(ne);
   return ret;
 }
@@ -200,13 +193,12 @@
 int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid, int type,
                                const unsigned char *bytes, int len, int loc,
                                int set) {
-  X509_NAME_ENTRY *ne;
-  int ret;
-  ne = X509_NAME_ENTRY_create_by_NID(NULL, nid, type, bytes, len);
+  X509_NAME_ENTRY *ne =
+      X509_NAME_ENTRY_create_by_NID(NULL, nid, type, bytes, len);
   if (!ne) {
     return 0;
   }
-  ret = X509_NAME_add_entry(name, ne, loc, set);
+  int ret = X509_NAME_add_entry(name, ne, loc, set);
   X509_NAME_ENTRY_free(ne);
   return ret;
 }
@@ -214,20 +206,19 @@
 int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type,
                                const unsigned char *bytes, int len, int loc,
                                int set) {
-  X509_NAME_ENTRY *ne;
-  int ret;
-  ne = X509_NAME_ENTRY_create_by_txt(NULL, field, type, bytes, len);
+  X509_NAME_ENTRY *ne =
+      X509_NAME_ENTRY_create_by_txt(NULL, field, type, bytes, len);
   if (!ne) {
     return 0;
   }
-  ret = X509_NAME_add_entry(name, ne, loc, set);
+  int ret = X509_NAME_add_entry(name, ne, loc, set);
   X509_NAME_ENTRY_free(ne);
   return ret;
 }
 
 // if set is -1, append to previous set, 0 'a new one', and 1, prepend to the
 // guy we are about to stomp on.
-int X509_NAME_add_entry(X509_NAME *name, X509_NAME_ENTRY *ne, int loc,
+int X509_NAME_add_entry(X509_NAME *name, const X509_NAME_ENTRY *entry, int loc,
                         int set) {
   X509_NAME_ENTRY *new_name = NULL;
   int n, i, inc;
@@ -267,7 +258,7 @@
     }
   }
 
-  if ((new_name = X509_NAME_ENTRY_dup(ne)) == NULL) {
+  if ((new_name = X509_NAME_ENTRY_dup(entry)) == NULL) {
     goto err;
   }
   new_name->set = set;
diff --git a/crypto/x509v3/v3_ncons.c b/crypto/x509v3/v3_ncons.c
index 967423a..5505a62 100644
--- a/crypto/x509v3/v3_ncons.c
+++ b/crypto/x509v3/v3_ncons.c
@@ -76,14 +76,14 @@
 static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
                                    STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp,
                                    int ind, const char *name);
-static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
+static int print_nc_ipadd(BIO *bp, const ASN1_OCTET_STRING *ip);
 
 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
 static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
 static int nc_dn(X509_NAME *sub, X509_NAME *nm);
-static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
-static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
-static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
+static int nc_dns(const ASN1_IA5STRING *sub, const ASN1_IA5STRING *dns);
+static int nc_email(const ASN1_IA5STRING *sub, const ASN1_IA5STRING *eml);
+static int nc_uri(const ASN1_IA5STRING *uri, const ASN1_IA5STRING *base);
 
 const X509V3_EXT_METHOD v3_name_constraints = {
     NID_name_constraints,
@@ -196,7 +196,7 @@
   return 1;
 }
 
-static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) {
+static int print_nc_ipadd(BIO *bp, const ASN1_OCTET_STRING *ip) {
   int i, len;
   unsigned char *p;
   p = ip->data;
@@ -273,12 +273,11 @@
     // Process any email address attributes in subject name
 
     for (i = -1;;) {
-      X509_NAME_ENTRY *ne;
       i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i);
       if (i == -1) {
         break;
       }
-      ne = X509_NAME_get_entry(nm, i);
+      const X509_NAME_ENTRY *ne = X509_NAME_get_entry(nm, i);
       gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
       if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) {
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
@@ -429,7 +428,7 @@
   return equal_case(&copy, b);
 }
 
-static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) {
+static int nc_dns(const ASN1_IA5STRING *dns, const ASN1_IA5STRING *base) {
   CBS dns_cbs, base_cbs;
   CBS_init(&dns_cbs, dns->data, dns->length);
   CBS_init(&base_cbs, base->data, base->length);
@@ -465,7 +464,7 @@
   return X509_V_OK;
 }
 
-static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) {
+static int nc_email(const ASN1_IA5STRING *eml, const ASN1_IA5STRING *base) {
   CBS eml_cbs, base_cbs;
   CBS_init(&eml_cbs, eml->data, eml->length);
   CBS_init(&base_cbs, base->data, base->length);
@@ -513,7 +512,7 @@
   return X509_V_OK;
 }
 
-static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) {
+static int nc_uri(const ASN1_IA5STRING *uri, const ASN1_IA5STRING *base) {
   CBS uri_cbs, base_cbs;
   CBS_init(&uri_cbs, uri->data, uri->length);
   CBS_init(&base_cbs, base->data, base->length);
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
index 3eccf06..15ebe54 100644
--- a/crypto/x509v3/v3_utl.c
+++ b/crypto/x509v3/v3_utl.c
@@ -77,10 +77,11 @@
 
 static char *strip_spaces(char *name);
 static int sk_strcmp(const char **a, const char **b);
-static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name,
-                                           GENERAL_NAMES *gens);
+static STACK_OF(OPENSSL_STRING) *get_email(const X509_NAME *name,
+                                           const GENERAL_NAMES *gens);
 static void str_free(OPENSSL_STRING str);
-static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email);
+static int append_ia5(STACK_OF(OPENSSL_STRING) **sk,
+                      const ASN1_IA5STRING *email);
 
 static int ipv4_from_asc(unsigned char v4[4], const char *in);
 static int ipv6_from_asc(unsigned char v6[16], const char *in);
@@ -617,27 +618,22 @@
   return ret;
 }
 
-static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name,
-                                           GENERAL_NAMES *gens) {
+static STACK_OF(OPENSSL_STRING) *get_email(const X509_NAME *name,
+                                           const GENERAL_NAMES *gens) {
   STACK_OF(OPENSSL_STRING) *ret = NULL;
-  X509_NAME_ENTRY *ne;
-  ASN1_IA5STRING *email;
-  GENERAL_NAME *gen;
-  int i;
-  size_t j;
   // Now add any email address(es) to STACK
-  i = -1;
+  int i = -1;
   // First supplied X509_NAME
   while ((i = X509_NAME_get_index_by_NID(name, NID_pkcs9_emailAddress, i)) >=
          0) {
-    ne = X509_NAME_get_entry(name, i);
-    email = X509_NAME_ENTRY_get_data(ne);
+    const X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i);
+    const ASN1_IA5STRING *email = X509_NAME_ENTRY_get_data(ne);
     if (!append_ia5(&ret, email)) {
       return NULL;
     }
   }
-  for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) {
-    gen = sk_GENERAL_NAME_value(gens, j);
+  for (size_t j = 0; j < sk_GENERAL_NAME_num(gens); j++) {
+    const GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, j);
     if (gen->type != GEN_EMAIL) {
       continue;
     }
@@ -650,7 +646,8 @@
 
 static void str_free(OPENSSL_STRING str) { OPENSSL_free(str); }
 
-static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email) {
+static int append_ia5(STACK_OF(OPENSSL_STRING) **sk,
+                      const ASN1_IA5STRING *email) {
   // First some sanity checks
   if (email->type != V_ASN1_IA5STRING) {
     return 1;
@@ -952,7 +949,7 @@
 // cmp_type > 0 only compare if string matches the type, otherwise convert it
 // to UTF8.
 
-static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
+static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal,
                            unsigned int flags, int check_type, const char *b,
                            size_t blen, char **peername) {
   int rv = 0;
@@ -997,15 +994,10 @@
 
 static int do_x509_check(X509 *x, const char *chk, size_t chklen,
                          unsigned int flags, int check_type, char **peername) {
-  GENERAL_NAMES *gens = NULL;
-  X509_NAME *name = NULL;
-  size_t i;
-  int j;
   int cnid = NID_undef;
   int alt_type;
   int rv = 0;
   equal_fn equal;
-
   if (check_type == GEN_EMAIL) {
     cnid = NID_pkcs9_emailAddress;
     alt_type = V_ASN1_IA5STRING;
@@ -1023,15 +1015,14 @@
     equal = equal_case;
   }
 
-  gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+  GENERAL_NAMES *gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
   if (gens) {
-    for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
-      GENERAL_NAME *gen;
-      ASN1_STRING *cstr;
-      gen = sk_GENERAL_NAME_value(gens, i);
+    for (size_t i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+      const GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);
       if (gen->type != check_type) {
         continue;
       }
+      const ASN1_STRING *cstr;
       if (check_type == GEN_EMAIL) {
         cstr = gen->d.rfc822Name;
       } else if (check_type == GEN_DNS) {
@@ -1054,13 +1045,11 @@
     return 0;
   }
 
-  j = -1;
-  name = X509_get_subject_name(x);
+  int j = -1;
+  const X509_NAME *name = X509_get_subject_name(x);
   while ((j = X509_NAME_get_index_by_NID(name, cnid, j)) >= 0) {
-    X509_NAME_ENTRY *ne;
-    ASN1_STRING *str;
-    ne = X509_NAME_get_entry(name, j);
-    str = X509_NAME_ENTRY_get_data(ne);
+    const X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, j);
+    const ASN1_STRING *str = X509_NAME_ENTRY_get_data(ne);
     // Positive on success, negative on error!
     if ((rv = do_check_string(str, -1, equal, flags, check_type, chk, chklen,
                               peername)) != 0) {
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 1e8105b..34eb840 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -711,6 +711,246 @@
                                                  size_t sig_len);
 
 
+// Names.
+//
+// An |X509_NAME| represents an X.509 Name structure (RFC 5280). X.509 names are
+// a complex, hierarchical structure over a collection of attributes. Each name
+// is sequence of relative distinguished names (RDNs), decreasing in
+// specificity. For example, the first RDN may specify the country, while the
+// next RDN may specify a locality. Each RDN is, itself, a set of attributes.
+// Having more than one attribute in an RDN is uncommon, but possible. Within an
+// RDN, attributes have the same level in specificity. Attribute types are
+// OBJECT IDENTIFIERs. This determines the ASN.1 type of the value, which is
+// commonly a string but may be other types.
+//
+// The |X509_NAME| representation flattens this two-level structure into a
+// single list of attributes. Each attribute is stored in an |X509_NAME_ENTRY|,
+// with also maintains the index of the RDN it is part of, accessible via
+// |X509_NAME_ENTRY_set|. This can be used to recover the two-level structure.
+//
+// X.509 names are largely vestigial. Historically, DNS names were parsed out of
+// the subject's common name attribute, but this is deprecated and has since
+// moved to the subject alternative name extension. In modern usage, X.509 names
+// are primarily opaque identifiers to link a certificate with its issuer.
+
+DEFINE_STACK_OF(X509_NAME_ENTRY)
+DEFINE_STACK_OF(X509_NAME)
+
+// X509_NAME is an |ASN1_ITEM| whose ASN.1 type is X.509 Name (RFC 5280) and C
+// type is |X509_NAME*|.
+DECLARE_ASN1_ITEM(X509_NAME)
+
+// X509_NAME_new returns a new, empty |X509_NAME_new|, or NULL on
+// error.
+OPENSSL_EXPORT X509_NAME *X509_NAME_new(void);
+
+// X509_NAME_free releases memory associated with |name|.
+OPENSSL_EXPORT void X509_NAME_free(X509_NAME *name);
+
+// d2i_X509_NAME parses up to |len| bytes from |*inp| as a DER-encoded X.509
+// Name (RFC 5280), as described in |d2i_SAMPLE_with_reuse|.
+OPENSSL_EXPORT X509_NAME *d2i_X509_NAME(X509_NAME **out, const uint8_t **inp,
+                                        long len);
+
+// i2d_X509_NAME marshals |in| as a DER-encoded X.509 Name (RFC 5280), as
+// described in |i2d_SAMPLE|.
+//
+// TODO(https://crbug.com/boringssl/407): This function should be const and
+// thread-safe but is currently neither in some cases, notably if |in| was
+// mutated.
+OPENSSL_EXPORT int i2d_X509_NAME(X509_NAME *in, uint8_t **outp);
+
+// X509_NAME_dup returns a newly-allocated copy of |name|, or NULL on error.
+//
+// TODO(https://crbug.com/boringssl/407): This function should be const and
+// thread-safe but is currently neither in some cases, notably if |name| was
+// mutated.
+OPENSSL_EXPORT X509_NAME *X509_NAME_dup(X509_NAME *name);
+
+// X509_NAME_get0_der sets |*out_der| and |*out_der_len|
+//
+// Avoid this function and prefer |i2d_X509_NAME|. It is one of the reasons
+// these functions are not consistently thread-safe or const-correct. Depending
+// on the resolution of https://crbug.com/boringssl/407, this function may be
+// removed or cause poor performance.
+OPENSSL_EXPORT int X509_NAME_get0_der(X509_NAME *name, const uint8_t **out_der,
+                                      size_t *out_der_len);
+
+// X509_NAME_set makes a copy of |name|. On success, it frees |*xn|, sets |*xn|
+// to the copy, and returns one. Otherwise, it returns zero.
+//
+// TODO(https://crbug.com/boringssl/407): This function should be const and
+// thread-safe but is currently neither in some cases, notably if |name| was
+// mutated.
+OPENSSL_EXPORT int X509_NAME_set(X509_NAME **xn, X509_NAME *name);
+
+// X509_NAME_entry_count returns the number of entries in |name|.
+OPENSSL_EXPORT int X509_NAME_entry_count(const X509_NAME *name);
+
+// X509_NAME_get_index_by_NID returns the zero-based index of the first
+// attribute in |name| with type |nid|, or -1 if there is none. |nid| should be
+// one of the |NID_*| constants. If |lastpos| is non-negative, it begins
+// searching at |lastpos+1|. To search all attributes, pass in -1, not zero.
+//
+// Indices from this function refer to |X509_NAME|'s flattened representation.
+OPENSSL_EXPORT int X509_NAME_get_index_by_NID(const X509_NAME *name, int nid,
+                                              int lastpos);
+
+// X509_NAME_get_index_by_OBJ behaves like |X509_NAME_get_index_by_NID| but
+// looks for attributes with type |obj|.
+OPENSSL_EXPORT int X509_NAME_get_index_by_OBJ(const X509_NAME *name,
+                                              const ASN1_OBJECT *obj,
+                                              int lastpos);
+
+// X509_NAME_get_entry returns the attribute in |name| at index |loc|, or NULL
+// if |loc| is out of range. |loc| is interpreted using |X509_NAME|'s flattened
+// representation. This function returns a non-const pointer for OpenSSL
+// compatibility, but callers should not mutate the result. Doing so will break
+// internal invariants in the library.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_get_entry(const X509_NAME *name,
+                                                    int loc);
+
+// X509_NAME_delete_entry removes and returns the attribute in |name| at index
+// |loc|, or NULL if |loc| is out of range. |loc| is interpreted using
+// |X509_NAME|'s flattened representation. If the attribute is found, the caller
+// is responsible for releasing the result with |X509_NAME_ENTRY_free|.
+//
+// This function will internally update RDN indices (see |X509_NAME_ENTRY_set|)
+// so they continue to be consecutive.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name,
+                                                       int loc);
+
+// X509_NAME_add_entry adds a copy of |entry| to |name| and returns one on
+// success or zero on error. If |loc| is -1, the entry is appended to |name|.
+// Otherwise, it is inserted at index |loc|. If |set| is -1, the entry is added
+// to the previous entry's RDN. If it is 0, the entry becomes a singleton RDN.
+// If 1, it is added to next entry's RDN.
+//
+// This function will internally update RDN indices (see |X509_NAME_ENTRY_set|)
+// so they continue to be consecutive.
+OPENSSL_EXPORT int X509_NAME_add_entry(X509_NAME *name,
+                                       const X509_NAME_ENTRY *entry, int loc,
+                                       int set);
+
+// X509_NAME_add_entry_by_OBJ adds a new entry to |name| and returns one on
+// success or zero on error. The entry's attribute type is |obj|. The entry's
+// attribute value is determined by |type|, |bytes|, and |len|, as in
+// |X509_NAME_ENTRY_set_data|. The entry's position is determined by |loc| and
+// |set| as in |X509_NAME_entry|.
+OPENSSL_EXPORT int X509_NAME_add_entry_by_OBJ(X509_NAME *name,
+                                              const ASN1_OBJECT *obj, int type,
+                                              const uint8_t *bytes, int len,
+                                              int loc, int set);
+
+// X509_NAME_add_entry_by_NID behaves like |X509_NAME_add_entry_by_OBJ| but sets
+// the entry's attribute type to |nid|, which should be one of the |NID_*|
+// constants.
+OPENSSL_EXPORT int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid,
+                                              int type, const uint8_t *bytes,
+                                              int len, int loc, int set);
+
+// X509_NAME_add_entry_by_txt behaves like |X509_NAME_add_entry_by_OBJ| but sets
+// the entry's attribute type to |field|, which is passed to |OBJ_txt2obj|.
+OPENSSL_EXPORT int X509_NAME_add_entry_by_txt(X509_NAME *name,
+                                              const char *field, int type,
+                                              const uint8_t *bytes, int len,
+                                              int loc, int set);
+
+// X509_NAME_ENTRY is an |ASN1_ITEM| whose ASN.1 type is AttributeTypeAndValue
+// (RFC 5280) and C type is |X509_NAME_ENTRY*|.
+DECLARE_ASN1_ITEM(X509_NAME_ENTRY)
+
+// X509_NAME_ENTRY_new returns a new, empty |X509_NAME_ENTRY_new|, or NULL on
+// error.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_new(void);
+
+// X509_NAME_ENTRY_free releases memory associated with |entry|.
+OPENSSL_EXPORT void X509_NAME_ENTRY_free(X509_NAME_ENTRY *entry);
+
+// d2i_X509_NAME_ENTRY parses up to |len| bytes from |*inp| as a DER-encoded
+// AttributeTypeAndValue (RFC 5280), as described in |d2i_SAMPLE_with_reuse|.
+OPENSSL_EXPORT X509_NAME_ENTRY *d2i_X509_NAME_ENTRY(X509_NAME_ENTRY **out,
+                                                    const uint8_t **inp,
+                                                    long len);
+
+// i2d_X509_NAME_ENTRY marshals |in| as a DER-encoded AttributeTypeAndValue (RFC
+// 5280), as described in |i2d_SAMPLE|.
+OPENSSL_EXPORT int i2d_X509_NAME_ENTRY(const X509_NAME_ENTRY *in,
+                                       uint8_t **outp);
+
+// X509_NAME_ENTRY_dup returns a newly-allocated copy of |entry|, or NULL on
+// error.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_dup(
+    const X509_NAME_ENTRY *entry);
+
+// X509_NAME_ENTRY_get_object returns |entry|'s attribute type. This function
+// returns a non-const pointer for OpenSSL compatibility, but callers should not
+// mutate the result. Doing so will break internal invariants in the library.
+OPENSSL_EXPORT ASN1_OBJECT *X509_NAME_ENTRY_get_object(
+    const X509_NAME_ENTRY *entry);
+
+// X509_NAME_ENTRY_set_object sets |entry|'s attribute type to |obj|. It returns
+// one on success and zero on error.
+OPENSSL_EXPORT int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *entry,
+                                              const ASN1_OBJECT *obj);
+
+// X509_NAME_ENTRY_get_data returns |entry|'s attribute value, represented as an
+// |ASN1_STRING|. This value may have any ASN.1 type, so callers must check the
+// type before interpreting the contents. This function returns a non-const
+// pointer for OpenSSL compatibility, but callers should not mutate the result.
+// Doing so will break internal invariants in the library.
+//
+// TODO(https://crbug.com/boringssl/412): Although the spec says any ASN.1 type
+// is allowed, we currently only allow an ad-hoc set of types. Additionally, it
+// is unclear if some types can even be represented by this function.
+OPENSSL_EXPORT ASN1_STRING *X509_NAME_ENTRY_get_data(
+    const X509_NAME_ENTRY *entry);
+
+// X509_NAME_ENTRY_set_data sets |entry|'s value to |len| bytes from |bytes|. It
+// returns one on success and zero on error. If |len| is -1, |bytes| must be a
+// NUL-terminated C string and the length is determined by |strlen|. |bytes| is
+// converted to an ASN.1 type as follows:
+//
+// If |type| is a |MBSTRING_*| constant, the value is an ASN.1 string. The
+// string is determined by decoding |bytes| in the encoding specified by |type|,
+// and then re-encoding it in a form appropriate for |entry|'s attribute type.
+// See |ASN1_STRING_set_by_NID| for details.
+//
+// Otherwise, the value is an |ASN1_STRING| with type |type| and value |bytes|.
+// See |ASN1_STRING| for how to format ASN.1 types as an |ASN1_STRING|. If
+// |type| is |V_ASN1_UNDEF| the previous |ASN1_STRING| type is reused.
+OPENSSL_EXPORT int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *entry, int type,
+                                            const uint8_t *bytes, int len);
+
+// X509_NAME_ENTRY_set returns the zero-based index of the RDN which contains
+// |entry|. Consecutive entries with the same index are part of the same RDN.
+OPENSSL_EXPORT int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *entry);
+
+// X509_NAME_ENTRY_create_by_OBJ creates a new |X509_NAME_ENTRY| with attribute
+// type |obj|. The attribute value is determined from |type|, |bytes|, and |len|
+// as in |X509_NAME_ENTRY_set_data|. It returns the |X509_NAME_ENTRY| on success
+// and NULL on error.
+//
+// If |out| is non-NULL and |*out| is NULL, it additionally sets |*out| to the
+// result on success. If both |out| and |*out| are non-NULL, it updates the
+// object at |*out| instead of allocating a new one.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(
+    X509_NAME_ENTRY **out, const ASN1_OBJECT *obj, int type,
+    const uint8_t *bytes, int len);
+
+// X509_NAME_ENTRY_create_by_NID behaves like |X509_NAME_ENTRY_create_by_OBJ|
+// except the attribute type is |nid|, which should be one of the |NID_*|
+// constants.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(
+    X509_NAME_ENTRY **out, int nid, int type, const uint8_t *bytes, int len);
+
+// X509_NAME_ENTRY_create_by_txt behaves like |X509_NAME_ENTRY_create_by_OBJ|
+// except the attribute type is |field|, which is passed to |OBJ_txt2obj|.
+OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(
+    X509_NAME_ENTRY **out, const char *field, int type, const uint8_t *bytes,
+    int len);
+
+
 // Algorithm identifiers.
 //
 // An |X509_ALGOR| represents an AlgorithmIdentifier structure, used in X.509
@@ -874,6 +1114,31 @@
 // Prefer |X509_get0_serialNumber|.
 OPENSSL_EXPORT ASN1_INTEGER *X509_get_serialNumber(X509 *x509);
 
+// X509_NAME_get_text_by_OBJ finds the first attribute with type |obj| in
+// |name|. If found, it ignores the value's ASN.1 type, writes the raw
+// |ASN1_STRING| representation to |buf|, followed by a NUL byte, and
+// returns the number of bytes in output, excluding the NUL byte.
+//
+// This function writes at most |len| bytes, including the NUL byte. If |len| is
+// not large enough, it silently truncates the output to fit. If |buf| is NULL,
+// it instead writes enough and returns the number of bytes in the output,
+// excluding the NUL byte.
+//
+// WARNING: Do not use this function. It does not return enough information for
+// the caller to correctly interpret its output. The attribute value may be of
+// any type, including one of several ASN.1 string encodings, but this function
+// only outputs the raw |ASN1_STRING| representation. See
+// https://crbug.com/boringssl/436.
+OPENSSL_EXPORT int X509_NAME_get_text_by_OBJ(const X509_NAME *name,
+                                             const ASN1_OBJECT *obj, char *buf,
+                                             int len);
+
+// X509_NAME_get_text_by_NID behaves like |X509_NAME_get_text_by_OBJ| except it
+// finds an attribute of type |nid|, which should be one of the |NID_*|
+// constants.
+OPENSSL_EXPORT int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid,
+                                             char *buf, int len);
+
 
 // Private structures.
 
@@ -900,10 +1165,6 @@
 #define X509v3_KU_DECIPHER_ONLY 0x8000
 #define X509v3_KU_UNDEF 0xffff
 
-DEFINE_STACK_OF(X509_NAME_ENTRY)
-
-DEFINE_STACK_OF(X509_NAME)
-
 typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
 
 DEFINE_STACK_OF(X509_EXTENSION)
@@ -1269,13 +1530,6 @@
 OPENSSL_EXPORT X509_REQ *X509_REQ_dup(X509_REQ *req);
 OPENSSL_EXPORT X509_ALGOR *X509_ALGOR_dup(const X509_ALGOR *xn);
 
-OPENSSL_EXPORT X509_NAME *X509_NAME_dup(X509_NAME *xn);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_dup(const X509_NAME_ENTRY *ne);
-OPENSSL_EXPORT int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne);
-
-OPENSSL_EXPORT int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder,
-                                      size_t *pderlen);
-
 // X509_cmp_time compares |s| against |*t|. On success, it returns a negative
 // number if |s| <= |*t| and a positive number if |s| > |*t|. On error, it
 // returns zero. If |t| is NULL, it uses the current time instead of |*t|.
@@ -1335,16 +1589,6 @@
 DECLARE_ASN1_FUNCTIONS_const(X509_EXTENSION)
 DECLARE_ASN1_ENCODE_FUNCTIONS_const(X509_EXTENSIONS, X509_EXTENSIONS)
 
-DECLARE_ASN1_FUNCTIONS_const(X509_NAME_ENTRY)
-
-// TODO(https://crbug.com/boringssl/407): This is not const because serializing
-// an |X509_NAME| is sometimes not thread-safe.
-DECLARE_ASN1_FUNCTIONS(X509_NAME)
-
-// X509_NAME_set makes a copy of |name|. On success, it frees |*xn|, sets |*xn|
-// to the copy, and returns one. Otherwise, it returns zero.
-OPENSSL_EXPORT int X509_NAME_set(X509_NAME **xn, X509_NAME *name);
-
 OPENSSL_EXPORT int X509_add1_trust_object(X509 *x, ASN1_OBJECT *obj);
 OPENSSL_EXPORT int X509_add1_reject_object(X509 *x, ASN1_OBJECT *obj);
 OPENSSL_EXPORT void X509_trust_clear(X509 *x);
@@ -1559,56 +1803,6 @@
                                      unsigned long cflag);
 OPENSSL_EXPORT int X509_REQ_print(BIO *bp, X509_REQ *req);
 
-OPENSSL_EXPORT int X509_NAME_entry_count(const X509_NAME *name);
-OPENSSL_EXPORT int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid,
-                                             char *buf, int len);
-OPENSSL_EXPORT int X509_NAME_get_text_by_OBJ(const X509_NAME *name,
-                                             const ASN1_OBJECT *obj, char *buf,
-                                             int len);
-
-// NOTE: you should be passsing -1, not 0 as lastpos.  The functions that use
-// lastpos, search after that position on.
-OPENSSL_EXPORT int X509_NAME_get_index_by_NID(const X509_NAME *name, int nid,
-                                              int lastpos);
-OPENSSL_EXPORT int X509_NAME_get_index_by_OBJ(const X509_NAME *name,
-                                              const ASN1_OBJECT *obj,
-                                              int lastpos);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_get_entry(const X509_NAME *name,
-                                                    int loc);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name,
-                                                       int loc);
-OPENSSL_EXPORT int X509_NAME_add_entry(X509_NAME *name, X509_NAME_ENTRY *ne,
-                                       int loc, int set);
-OPENSSL_EXPORT int X509_NAME_add_entry_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj,
-                                              int type,
-                                              const unsigned char *bytes,
-                                              int len, int loc, int set);
-OPENSSL_EXPORT int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid,
-                                              int type,
-                                              const unsigned char *bytes,
-                                              int len, int loc, int set);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(
-    X509_NAME_ENTRY **ne, const char *field, int type,
-    const unsigned char *bytes, int len);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(
-    X509_NAME_ENTRY **ne, int nid, int type, const unsigned char *bytes,
-    int len);
-OPENSSL_EXPORT int X509_NAME_add_entry_by_txt(X509_NAME *name,
-                                              const char *field, int type,
-                                              const unsigned char *bytes,
-                                              int len, int loc, int set);
-OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(
-    X509_NAME_ENTRY **ne, const ASN1_OBJECT *obj, int type,
-    const unsigned char *bytes, int len);
-OPENSSL_EXPORT int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne,
-                                              const ASN1_OBJECT *obj);
-OPENSSL_EXPORT int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type,
-                                            const unsigned char *bytes,
-                                            int len);
-OPENSSL_EXPORT ASN1_OBJECT *X509_NAME_ENTRY_get_object(
-    const X509_NAME_ENTRY *ne);
-OPENSSL_EXPORT ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne);
-
 // X509v3_get_ext_count returns the number of extensions in |x|.
 OPENSSL_EXPORT int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x);
 
@@ -1969,8 +2163,6 @@
 // |attr|'s type. If |len| is -1, |strlen(data)| is used instead. See
 // |ASN1_STRING_set_by_NID| for details.
 //
-// TODO(davidben): Document |ASN1_STRING_set_by_NID| so the reference is useful.
-//
 // Otherwise, if |len| is not -1, the value is an ASN.1 string. |attrtype| is an
 // |ASN1_STRING| type value and the |len| bytes from |data| are copied as the
 // type-specific representation of |ASN1_STRING|. See |ASN1_STRING| for details.