Document a pile of X509 print functions I had a branch lying around to rewrite X509_NAME_print(_ex) because those functions are a disaster, but it needs more work and probably isn't high priority. In the meantime, document what we've got. Also tidy up X509_print_ex slightly. m was completely unused and some variable declarations could be moved closer to their definition. Bug: 426 Change-Id: I24295048c36268c745f579ad66f34736cfe6830f Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58925 Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: David Benjamin <davidben@google.com> Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c index 6694e3d..1d71576 100644 --- a/crypto/x509/t_x509.c +++ b/crypto/x509/t_x509.c
@@ -91,14 +91,8 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, unsigned long cflag) { - long l; - int ret = 0, i; - char *m = NULL, mlch = ' '; + char mlch = ' '; int nmindent = 0; - X509_CINF *ci; - EVP_PKEY *pkey = NULL; - const char *neg; - if ((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { mlch = '\n'; nmindent = 12; @@ -108,26 +102,26 @@ nmindent = 16; } - ci = x->cert_info; + const X509_CINF *ci = x->cert_info; if (!(cflag & X509_FLAG_NO_HEADER)) { if (BIO_write(bp, "Certificate:\n", 13) <= 0) { - goto err; + return 0; } if (BIO_write(bp, " Data:\n", 10) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_VERSION)) { - l = X509_get_version(x); + long l = X509_get_version(x); assert(X509_VERSION_1 <= l && l <= X509_VERSION_3); if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_SERIAL)) { if (BIO_write(bp, " Serial Number:", 22) <= 0) { - goto err; + return 0; } const ASN1_INTEGER *serial = X509_get0_serialNumber(x); @@ -136,19 +130,20 @@ assert(serial->type != V_ASN1_NEG_INTEGER); if (BIO_printf(bp, " %" PRIu64 " (0x%" PRIx64 ")\n", serial_u64, serial_u64) <= 0) { - goto err; + return 0; } } else { ERR_clear_error(); // Clear |ASN1_INTEGER_get_uint64|'s error. - neg = (serial->type == V_ASN1_NEG_INTEGER) ? " (Negative)" : ""; + const char *neg = + (serial->type == V_ASN1_NEG_INTEGER) ? " (Negative)" : ""; if (BIO_printf(bp, "\n%12s%s", "", neg) <= 0) { - goto err; + return 0; } - for (i = 0; i < serial->length; i++) { + for (int i = 0; i < serial->length; i++) { if (BIO_printf(bp, "%02x%c", serial->data[i], ((i + 1 == serial->length) ? '\n' : ':')) <= 0) { - goto err; + return 0; } } } @@ -156,69 +151,69 @@ if (!(cflag & X509_FLAG_NO_SIGNAME)) { if (X509_signature_print(bp, ci->signature, NULL) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_ISSUER)) { if (BIO_printf(bp, " Issuer:%c", mlch) <= 0) { - goto err; + return 0; } if (X509_NAME_print_ex(bp, X509_get_issuer_name(x), nmindent, nmflags) < 0) { - goto err; + return 0; } if (BIO_write(bp, "\n", 1) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_VALIDITY)) { if (BIO_write(bp, " Validity\n", 17) <= 0) { - goto err; + return 0; } if (BIO_write(bp, " Not Before: ", 24) <= 0) { - goto err; + return 0; } if (!ASN1_TIME_print(bp, X509_get_notBefore(x))) { - goto err; + return 0; } if (BIO_write(bp, "\n Not After : ", 25) <= 0) { - goto err; + return 0; } if (!ASN1_TIME_print(bp, X509_get_notAfter(x))) { - goto err; + return 0; } if (BIO_write(bp, "\n", 1) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_SUBJECT)) { if (BIO_printf(bp, " Subject:%c", mlch) <= 0) { - goto err; + return 0; } if (X509_NAME_print_ex(bp, X509_get_subject_name(x), nmindent, nmflags) < 0) { - goto err; + return 0; } if (BIO_write(bp, "\n", 1) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_PUBKEY)) { if (BIO_write(bp, " Subject Public Key Info:\n", 33) <= 0) { - goto err; + return 0; } if (BIO_printf(bp, "%12sPublic Key Algorithm: ", "") <= 0) { - goto err; + return 0; } if (i2a_ASN1_OBJECT(bp, ci->key->algor->algorithm) <= 0) { - goto err; + return 0; } if (BIO_puts(bp, "\n") <= 0) { - goto err; + return 0; } - pkey = X509_get_pubkey(x); + EVP_PKEY *pkey = X509_get_pubkey(x); if (pkey == NULL) { BIO_printf(bp, "%12sUnable to load Public Key\n", ""); ERR_print_errors(bp); @@ -231,18 +226,18 @@ if (!(cflag & X509_FLAG_NO_IDS)) { if (ci->issuerUID) { if (BIO_printf(bp, "%8sIssuer Unique ID: ", "") <= 0) { - goto err; + return 0; } if (!X509_signature_dump(bp, ci->issuerUID, 12)) { - goto err; + return 0; } } if (ci->subjectUID) { if (BIO_printf(bp, "%8sSubject Unique ID: ", "") <= 0) { - goto err; + return 0; } if (!X509_signature_dump(bp, ci->subjectUID, 12)) { - goto err; + return 0; } } } @@ -253,20 +248,16 @@ if (!(cflag & X509_FLAG_NO_SIGDUMP)) { if (X509_signature_print(bp, x->sig_alg, x->signature) <= 0) { - goto err; + return 0; } } if (!(cflag & X509_FLAG_NO_AUX)) { if (!X509_CERT_AUX_print(bp, x->aux, 0)) { - goto err; + return 0; } } - ret = 1; -err: - if (m != NULL) { - OPENSSL_free(m); - } - return ret; + + return 1; } int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg,
diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 2ab7564..8f32149 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h
@@ -1300,7 +1300,216 @@ // // The following functions output human-readable representations of // X.509-related structures. They should only be used for debugging or logging -// and not parsed programmatically. +// and not parsed programmatically. In many cases, the outputs are ambiguous, so +// attempting to parse them can lead to string injection vulnerabilities. + +// The following flags control |X509_print_ex| and |X509_REQ_print_ex|. + +// X509_FLAG_COMPAT disables all flags. It additionally causes names to be +// printed with a 16-byte indent. +#define X509_FLAG_COMPAT 0 + +// X509_FLAG_NO_HEADER skips a header identifying the type of object printed. +#define X509_FLAG_NO_HEADER 1L + +// X509_FLAG_NO_VERSION skips printing the X.509 version number. +#define X509_FLAG_NO_VERSION (1L << 1) + +// X509_FLAG_NO_SERIAL skips printing the serial number. It is ignored in +// |X509_REQ_print_fp|. +#define X509_FLAG_NO_SERIAL (1L << 2) + +// X509_FLAG_NO_SIGNAME skips printing the signature algorithm in the +// TBSCertificate. It is ignored in |X509_REQ_print_fp|. +#define X509_FLAG_NO_SIGNAME (1L << 3) + +// X509_FLAG_NO_ISSUER skips printing the issuer. +#define X509_FLAG_NO_ISSUER (1L << 4) + +// X509_FLAG_NO_ISSUER skips printing the notBefore and notAfter times. It is +// ignored in |X509_REQ_print_fp|. +#define X509_FLAG_NO_VALIDITY (1L << 5) + +// X509_FLAG_NO_ISSUER skips printing the subject. +#define X509_FLAG_NO_SUBJECT (1L << 6) + +// X509_FLAG_NO_PUBKEY skips printing the public key. +#define X509_FLAG_NO_PUBKEY (1L << 7) + +// X509_FLAG_NO_EXTENSIONS skips printing the extension list. It is ignored in +// |X509_REQ_print_fp|. CSRs instead have attributes, which is controlled by +// |X509_FLAG_NO_ATTRIBUTES|. +#define X509_FLAG_NO_EXTENSIONS (1L << 8) + +// X509_FLAG_NO_SIGDUMP skips printing the signature and outer signature +// algorithm. +#define X509_FLAG_NO_SIGDUMP (1L << 9) + +// X509_FLAG_NO_AUX skips printing auxiliary properties. (See |d2i_X509_AUX| and +// related functions.) +#define X509_FLAG_NO_AUX (1L << 10) + +// X509_FLAG_NO_ATTRIBUTES skips printing CSR attributes. It does nothing for +// certificates and CRLs. +#define X509_FLAG_NO_ATTRIBUTES (1L << 11) + +// X509_FLAG_NO_IDS skips printing the issuerUniqueID and subjectUniqueID in a +// certificate. It is ignored in |X509_REQ_print_fp|. +#define X509_FLAG_NO_IDS (1L << 12) + +// X509_print_ex writes a human-readable representation of |x| to |bp|. It +// returns one on success and zero on error. |nmflags| is the flags parameter +// for |X509_NAME_print_ex| when printing the subject and issuer. |cflag| should +// be some combination of the |X509_FLAG_*| constants. +OPENSSL_EXPORT int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflag, + unsigned long cflag); + +// X509_print_ex_fp behaves like |X509_print_ex| but writes to |fp|. +OPENSSL_EXPORT int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag, + unsigned long cflag); + +// X509_print calls |X509_print_ex| with |XN_FLAG_COMPAT| and |X509_FLAG_COMPAT| +// flags. +OPENSSL_EXPORT int X509_print(BIO *bp, X509 *x); + +// X509_print_fp behaves like |X509_print| but writes to |fp|. +OPENSSL_EXPORT int X509_print_fp(FILE *fp, X509 *x); + +// X509_CRL_print writes a human-readable representation of |x| to |bp|. It +// returns one on success and zero on error. +OPENSSL_EXPORT int X509_CRL_print(BIO *bp, X509_CRL *x); + +// X509_CRL_print_fp behaves like |X509_CRL_print| but writes to |fp|. +OPENSSL_EXPORT int X509_CRL_print_fp(FILE *fp, X509_CRL *x); + +// X509_REQ_print_ex writes a human-readable representation of |x| to |bp|. It +// returns one on success and zero on error. |nmflags| is the flags parameter +// for |X509_NAME_print_ex|, when printing the subject. |cflag| should be some +// combination of the |X509_FLAG_*| constants. +OPENSSL_EXPORT int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflag, + unsigned long cflag); + +// X509_REQ_print calls |X509_REQ_print_ex| with |XN_FLAG_COMPAT| and +// |X509_FLAG_COMPAT| flags. +OPENSSL_EXPORT int X509_REQ_print(BIO *bp, X509_REQ *req); + +// X509_REQ_print_fp behaves like |X509_REQ_print| but writes to |fp|. +OPENSSL_EXPORT int X509_REQ_print_fp(FILE *fp, X509_REQ *req); + +// The following flags are control |X509_NAME_print_ex|. They must not collide +// with |ASN1_STRFLGS_*|. +// +// TODO(davidben): This is far, far too many options and most of them are +// useless. Trim this down. + +// XN_FLAG_COMPAT prints with |X509_NAME_print|'s format and return value +// convention. +#define XN_FLAG_COMPAT 0 + +// XN_FLAG_SEP_MASK determines the separators to use between attributes. +#define XN_FLAG_SEP_MASK (0xf << 16) + +// XN_FLAG_SEP_COMMA_PLUS separates RDNs with "," and attributes within an RDN +// with "+", as in RFC 2253. +#define XN_FLAG_SEP_COMMA_PLUS (1 << 16) + +// XN_FLAG_SEP_CPLUS_SPC behaves like |XN_FLAG_SEP_COMMA_PLUS| but adds spaces +// between the separators. +#define XN_FLAG_SEP_CPLUS_SPC (2 << 16) + +// XN_FLAG_SEP_SPLUS_SPC separates RDNs with "; " and attributes within an RDN +// with " + ". +#define XN_FLAG_SEP_SPLUS_SPC (3 << 16) + +// XN_FLAG_SEP_MULTILINE prints each attribute on one line. +#define XN_FLAG_SEP_MULTILINE (4 << 16) + +// XN_FLAG_DN_REV prints RDNs in reverse, from least significant to most +// significant, as RFC 2253. +#define XN_FLAG_DN_REV (1 << 20) + +// XN_FLAG_FN_MASK determines how attribute types are displayed. +#define XN_FLAG_FN_MASK (0x3 << 21) + +// XN_FLAG_FN_SN uses the attribute type's short name, when available. +#define XN_FLAG_FN_SN 0 + +// XN_FLAG_FN_LN uses the attribute type's long name, when available. +#define XN_FLAG_FN_LN (1 << 21) + +// XN_FLAG_FN_OID always prints attribute types as OIDs. +#define XN_FLAG_FN_OID (2 << 21) + +// XN_FLAG_FN_NONE skips printing field names. +#define XN_FLAG_FN_NONE (3 << 21) + +// XN_FLAG_SPC_EQ wraps the "=" operator with spaces when printing attributes. +#define XN_FLAG_SPC_EQ (1 << 23) + +// XN_FLAG_DUMP_UNKNOWN_FIELDS causes unknown attribute types to be printed in +// hex, as in RFC 2253. +#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24) + +// XN_FLAG_FN_ALIGN aligns attribute names to 10 characters if using short +// names, and 25 characters if using long names. +#define XN_FLAG_FN_ALIGN (1 << 25) + +// XN_FLAG_RFC2253 prints like RFC 2253. +#define XN_FLAG_RFC2253 \ + (ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | \ + XN_FLAG_FN_SN | XN_FLAG_DUMP_UNKNOWN_FIELDS) + +// XN_FLAG_ONELINE prints a one-line representation of the name. +#define XN_FLAG_ONELINE \ + (ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE | XN_FLAG_SEP_CPLUS_SPC | \ + XN_FLAG_SPC_EQ | XN_FLAG_FN_SN) + +// XN_FLAG_MULTILINE prints a multi-line representation of the name. +#define XN_FLAG_MULTILINE \ + (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | XN_FLAG_SEP_MULTILINE | \ + XN_FLAG_SPC_EQ | XN_FLAG_FN_LN | XN_FLAG_FN_ALIGN) + +// X509_NAME_print_ex writes a human-readable representation of |nm| to |out|. +// Each line of output is indented by |indent| spaces. It returns the number of +// bytes written on success, and -1 on error. If |out| is NULL, it returns the +// number of bytes it would have written but does not write anything. |flags| +// should be some combination of |XN_FLAG_*| and |ASN1_STRFLGS_*| values and +// determines the output. If unsure, use |XN_FLAG_RFC2253|. +// +// If |flags| is |XN_FLAG_COMPAT|, or zero, this function calls +// |X509_NAME_print| instead. In that case, it returns one on success, rather +// than the output length. +OPENSSL_EXPORT int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, + unsigned long flags); + +// X509_NAME_print prints a human-readable representation of |name| to |bp|. It +// returns one on success and zero on error. |obase| is ignored. +// +// This function outputs a legacy format that does not correctly handle string +// encodings and other cases. Prefer |X509_NAME_print_ex| if printing a name for +// debugging purposes. +OPENSSL_EXPORT int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase); + +// X509_NAME_oneline writes a human-readable representation to |name| to a +// buffer as a NUL-terminated C string. +// +// If |buf| is NULL, returns a newly-allocated buffer containing the result on +// success, or NULL on error. The buffer must be released with |OPENSSL_free| +// when done. +// +// If |buf| is non-NULL, at most |size| bytes of output are written to |buf| +// instead. |size| includes the trailing NUL. The function then returns |buf| on +// success or NULL on error. If the output does not fit in |size| bytes, the +// output is silently truncated at an attribute boundary. +// +// This function outputs a legacy format that does not correctly handle string +// encodings and other cases. Prefer |X509_NAME_print_ex| if printing a name for +// debugging purposes. +OPENSSL_EXPORT char *X509_NAME_oneline(const X509_NAME *name, char *buf, int size); + +// X509_NAME_print_ex_fp behaves like |X509_NAME_print_ex| but writes to |fp|. +OPENSSL_EXPORT int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, + int indent, unsigned long flags); // X509_signature_dump writes a human-readable representation of |sig| to |bio|, // indented with |indent| spaces. It returns one on success and zero on error. @@ -1631,74 +1840,6 @@ #define X509_TRUST_REJECTED 2 #define X509_TRUST_UNTRUSTED 3 -// Flags for X509_print_ex() - -#define X509_FLAG_COMPAT 0 -#define X509_FLAG_NO_HEADER 1L -#define X509_FLAG_NO_VERSION (1L << 1) -#define X509_FLAG_NO_SERIAL (1L << 2) -#define X509_FLAG_NO_SIGNAME (1L << 3) -#define X509_FLAG_NO_ISSUER (1L << 4) -#define X509_FLAG_NO_VALIDITY (1L << 5) -#define X509_FLAG_NO_SUBJECT (1L << 6) -#define X509_FLAG_NO_PUBKEY (1L << 7) -#define X509_FLAG_NO_EXTENSIONS (1L << 8) -#define X509_FLAG_NO_SIGDUMP (1L << 9) -#define X509_FLAG_NO_AUX (1L << 10) -#define X509_FLAG_NO_ATTRIBUTES (1L << 11) -#define X509_FLAG_NO_IDS (1L << 12) - -// Flags specific to X509_NAME_print_ex(). These flags must not collide with -// |ASN1_STRFLGS_*|. - -// The field separator information - -#define XN_FLAG_SEP_MASK (0xf << 16) - -#define XN_FLAG_COMPAT 0 // Traditional SSLeay: use old X509_NAME_print -#define XN_FLAG_SEP_COMMA_PLUS (1 << 16) // RFC 2253 ,+ -#define XN_FLAG_SEP_CPLUS_SPC (2 << 16) // ,+ spaced: more readable -#define XN_FLAG_SEP_SPLUS_SPC (3 << 16) // ;+ spaced -#define XN_FLAG_SEP_MULTILINE (4 << 16) // One line per field - -#define XN_FLAG_DN_REV (1 << 20) // Reverse DN order - -// How the field name is shown - -#define XN_FLAG_FN_MASK (0x3 << 21) - -#define XN_FLAG_FN_SN 0 // Object short name -#define XN_FLAG_FN_LN (1 << 21) // Object long name -#define XN_FLAG_FN_OID (2 << 21) // Always use OIDs -#define XN_FLAG_FN_NONE (3 << 21) // No field names - -#define XN_FLAG_SPC_EQ (1 << 23) // Put spaces round '=' - -// This determines if we dump fields we don't recognise: -// RFC 2253 requires this. - -#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24) - -#define XN_FLAG_FN_ALIGN (1 << 25) // Align field names to 20 characters - -// Complete set of RFC 2253 flags - -#define XN_FLAG_RFC2253 \ - (ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | \ - XN_FLAG_FN_SN | XN_FLAG_DUMP_UNKNOWN_FIELDS) - -// readable oneline form - -#define XN_FLAG_ONELINE \ - (ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE | XN_FLAG_SEP_CPLUS_SPC | \ - XN_FLAG_SPC_EQ | XN_FLAG_FN_SN) - -// readable multiline form - -#define XN_FLAG_MULTILINE \ - (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | XN_FLAG_SEP_MULTILINE | \ - XN_FLAG_SPC_EQ | XN_FLAG_FN_LN | XN_FLAG_FN_ALIGN) - DEFINE_STACK_OF(X509_REVOKED) DECLARE_STACK_OF(GENERAL_NAMES) @@ -1921,7 +2062,6 @@ OPENSSL_EXPORT X509_INFO *X509_INFO_new(void); OPENSSL_EXPORT void X509_INFO_free(X509_INFO *a); -OPENSSL_EXPORT char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size); OPENSSL_EXPORT int ASN1_digest(i2d_of_void *i2d, const EVP_MD *type, char *data, unsigned char *md, unsigned int *len); @@ -2082,24 +2222,6 @@ OPENSSL_EXPORT int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b); OPENSSL_EXPORT int X509_CRL_match(const X509_CRL *a, const X509_CRL *b); -OPENSSL_EXPORT int X509_print_ex_fp(FILE *bp, X509 *x, unsigned long nmflag, - unsigned long cflag); -OPENSSL_EXPORT int X509_print_fp(FILE *bp, X509 *x); -OPENSSL_EXPORT int X509_CRL_print_fp(FILE *bp, X509_CRL *x); -OPENSSL_EXPORT int X509_REQ_print_fp(FILE *bp, X509_REQ *req); -OPENSSL_EXPORT int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, - int indent, unsigned long flags); - -OPENSSL_EXPORT int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase); -OPENSSL_EXPORT int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, - unsigned long flags); -OPENSSL_EXPORT int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflag, - unsigned long cflag); -OPENSSL_EXPORT int X509_print(BIO *bp, X509 *x); -OPENSSL_EXPORT int X509_CRL_print(BIO *bp, X509_CRL *x); -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); // X509_get_ext_d2i behaves like |X509V3_get_d2i| but looks for the extension in // |x509|'s extension list.