|  | /* | 
|  | * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. | 
|  | * | 
|  | * Licensed under the OpenSSL license (the "License").  You may not use | 
|  | * this file except in compliance with the License.  You can obtain a copy | 
|  | * in the file LICENSE in the source distribution or at | 
|  | * https://www.openssl.org/source/license.html | 
|  | */ | 
|  |  | 
|  | #include <openssl/x509.h> | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <inttypes.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <openssl/asn1.h> | 
|  | #include <openssl/bio.h> | 
|  | #include <openssl/obj.h> | 
|  |  | 
|  |  | 
|  | static int maybe_write(BIO *out, const void *buf, int len) { | 
|  | // If |out| is NULL, ignore the output but report the length. | 
|  | return out == NULL || BIO_write(out, buf, len) == len; | 
|  | } | 
|  |  | 
|  | // do_indent prints |indent| spaces to |out|. | 
|  | static int do_indent(BIO *out, int indent) { | 
|  | for (int i = 0; i < indent; i++) { | 
|  | if (!maybe_write(out, " ", 1)) { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | #define FN_WIDTH_LN 25 | 
|  | #define FN_WIDTH_SN 10 | 
|  |  | 
|  | static int do_name_ex(BIO *out, const X509_NAME *n, int indent, | 
|  | unsigned long flags) { | 
|  | int prev = -1, orflags; | 
|  | char objtmp[80]; | 
|  | const char *objbuf; | 
|  | int outlen, len; | 
|  | const char *sep_dn, *sep_mv, *sep_eq; | 
|  | int sep_dn_len, sep_mv_len, sep_eq_len; | 
|  | if (indent < 0) { | 
|  | indent = 0; | 
|  | } | 
|  | outlen = indent; | 
|  | if (!do_indent(out, indent)) { | 
|  | return -1; | 
|  | } | 
|  | switch (flags & XN_FLAG_SEP_MASK) { | 
|  | case XN_FLAG_SEP_MULTILINE: | 
|  | sep_dn = "\n"; | 
|  | sep_dn_len = 1; | 
|  | sep_mv = " + "; | 
|  | sep_mv_len = 3; | 
|  | break; | 
|  |  | 
|  | case XN_FLAG_SEP_COMMA_PLUS: | 
|  | sep_dn = ","; | 
|  | sep_dn_len = 1; | 
|  | sep_mv = "+"; | 
|  | sep_mv_len = 1; | 
|  | indent = 0; | 
|  | break; | 
|  |  | 
|  | case XN_FLAG_SEP_CPLUS_SPC: | 
|  | sep_dn = ", "; | 
|  | sep_dn_len = 2; | 
|  | sep_mv = " + "; | 
|  | sep_mv_len = 3; | 
|  | indent = 0; | 
|  | break; | 
|  |  | 
|  | case XN_FLAG_SEP_SPLUS_SPC: | 
|  | sep_dn = "; "; | 
|  | sep_dn_len = 2; | 
|  | sep_mv = " + "; | 
|  | sep_mv_len = 3; | 
|  | indent = 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (flags & XN_FLAG_SPC_EQ) { | 
|  | sep_eq = " = "; | 
|  | sep_eq_len = 3; | 
|  | } else { | 
|  | sep_eq = "="; | 
|  | sep_eq_len = 1; | 
|  | } | 
|  |  | 
|  | int cnt = X509_NAME_entry_count(n); | 
|  | for (int 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 { | 
|  | ent = X509_NAME_get_entry(n, i); | 
|  | } | 
|  | if (prev != -1) { | 
|  | if (prev == X509_NAME_ENTRY_set(ent)) { | 
|  | if (!maybe_write(out, sep_mv, sep_mv_len)) { | 
|  | return -1; | 
|  | } | 
|  | outlen += sep_mv_len; | 
|  | } else { | 
|  | if (!maybe_write(out, sep_dn, sep_dn_len)) { | 
|  | return -1; | 
|  | } | 
|  | outlen += sep_dn_len; | 
|  | if (!do_indent(out, indent)) { | 
|  | return -1; | 
|  | } | 
|  | outlen += indent; | 
|  | } | 
|  | } | 
|  | prev = X509_NAME_ENTRY_set(ent); | 
|  | const ASN1_OBJECT *fn = X509_NAME_ENTRY_get_object(ent); | 
|  | const ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent); | 
|  | assert((flags & XN_FLAG_FN_MASK) == XN_FLAG_FN_SN); | 
|  | int fn_nid = OBJ_obj2nid(fn); | 
|  | if (fn_nid == NID_undef) { | 
|  | OBJ_obj2txt(objtmp, sizeof(objtmp), fn, 1); | 
|  | objbuf = objtmp; | 
|  | } else { | 
|  | objbuf = OBJ_nid2sn(fn_nid); | 
|  | } | 
|  | int objlen = strlen(objbuf); | 
|  | if (!maybe_write(out, objbuf, objlen) || | 
|  | !maybe_write(out, sep_eq, sep_eq_len)) { | 
|  | return -1; | 
|  | } | 
|  | outlen += objlen + sep_eq_len; | 
|  | // If the field name is unknown then fix up the DER dump flag. We | 
|  | // might want to limit this further so it will DER dump on anything | 
|  | // other than a few 'standard' fields. | 
|  | if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS)) { | 
|  | orflags = ASN1_STRFLGS_DUMP_ALL; | 
|  | } else { | 
|  | orflags = 0; | 
|  | } | 
|  |  | 
|  | len = ASN1_STRING_print_ex(out, val, flags | orflags); | 
|  | if (len < 0) { | 
|  | return -1; | 
|  | } | 
|  | outlen += len; | 
|  | } | 
|  | return outlen; | 
|  | } | 
|  |  | 
|  | int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, | 
|  | unsigned long flags) { | 
|  | if (flags == XN_FLAG_COMPAT) { | 
|  | return X509_NAME_print(out, nm, indent); | 
|  | } | 
|  | return do_name_ex(out, nm, indent, flags); | 
|  | } | 
|  |  | 
|  | int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent, | 
|  | unsigned long flags) { | 
|  | BIO *bio = NULL; | 
|  | if (fp != NULL) { | 
|  | // If |fp| is NULL, this function returns the number of bytes without | 
|  | // writing. | 
|  | bio = BIO_new_fp(fp, BIO_NOCLOSE); | 
|  | if (bio == NULL) { | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | int ret = X509_NAME_print_ex(bio, nm, indent, flags); | 
|  | BIO_free(bio); | 
|  | return ret; | 
|  | } |