From b6f47e88b09703e925c894ca77a5d90ee413e6b5 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 9 Jul 2022 00:57:54 -0400 Subject: [PATCH] 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 Commit-Queue: David Benjamin --- crypto/x509/name_print.c | 8 +- crypto/x509/x509_vfy.c | 6 +- crypto/x509/x509name.c | 63 ++++---- crypto/x509v3/v3_ncons.c | 19 ++- crypto/x509v3/v3_utl.c | 55 +++---- include/openssl/x509.h | 338 ++++++++++++++++++++++++++++++--------- 6 files changed, 329 insertions(+), 160 deletions(-) diff --git a/crypto/x509/name_print.c b/crypto/x509/name_print.c index f871df974..29207ccb6 100644 --- a/crypto/x509/name_print.c +++ b/crypto/x509/name_print.c @@ -86,9 +86,6 @@ static int do_name_ex(BIO *out, const X509_NAME *n, int indent, 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 @@ static int do_name_ex(BIO *out, const X509_NAME *n, int indent, 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 @@ static int do_name_ex(BIO *out, const X509_NAME *n, int indent, } } 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 8569d2ac0..5a9735398 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -689,7 +689,7 @@ end: } 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 @@ static int reject_dns_name_in_common_name(X509 *x509) { 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 6771b6829..fcfbc0c91 100644 --- a/crypto/x509/x509name.c +++ b/crypto/x509/x509name.c @@ -80,14 +80,12 @@ int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, char *buf, 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_get_entry(const X509_NAME *name, int loc) { } 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_OBJ(X509_NAME *name, ASN1_OBJECT *obj, int type, 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_NID(X509_NAME *name, int nid, int type, 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 @@ int X509_NAME_add_entry(X509_NAME *name, X509_NAME_ENTRY *ne, int loc, } } - 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 967423aaf..5505a620c 100644 --- a/crypto/x509v3/v3_ncons.c +++ b/crypto/x509v3/v3_ncons.c @@ -76,14 +76,14 @@ static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, 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 @@ static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, 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 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc) { // 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 @@ static int has_suffix_case(const CBS *a, const CBS *b) { return equal_case(©, 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 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) { 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 @@ static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) { 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 3eccf069b..15ebe540c 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 @@ STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x) { 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 STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, 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 @@ int x509v3_looks_like_dns_name(const unsigned char *in, size_t len) { // 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_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, 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 @@ static int do_x509_check(X509 *x, const char *chk, size_t chklen, 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 @@ static int do_x509_check(X509 *x, const char *chk, size_t chklen, 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 1e8105bef..34eb840a3 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -711,6 +711,246 @@ OPENSSL_EXPORT int X509_REQ_set1_signature_value(X509_REQ *req, 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 @@ OPENSSL_EXPORT ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *crl); // 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 @@ struct X509_algor_st { #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_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev); 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 @@ OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int attrtype, 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 @@ OPENSSL_EXPORT int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflag, 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 @@ OPENSSL_EXPORT int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, // |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.