New ASN1_STRING_print_ex() and X509_NAME_print_ex()
authorDr. Stephen Henson <steve@openssl.org>
Fri, 28 Jul 2000 01:58:15 +0000 (01:58 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Fri, 28 Jul 2000 01:58:15 +0000 (01:58 +0000)
functions. These are intended to be replacements
for the ancient ASN1_STRING_print() and X509_NAME_print()
functions.

The new functions support RFC2253 and various pretty
printing options. It is also possible to display
international characters if the terminal properly handles
UTF8 encoding (Linux seems to tolerate this if the
"unicode_start" script is run).

Still needs to be documented, integrated into other
utilities and extensively tested.

CHANGES
apps/apps.c
apps/apps.h
apps/x509.c
crypto/asn1/Makefile.ssl
crypto/asn1/a_strex.c [new file with mode: 0644]
crypto/asn1/asn1.h
crypto/asn1/charmap.h [new file with mode: 0644]
crypto/asn1/charmap.pl [new file with mode: 0644]
crypto/x509/x509.h

diff --git a/CHANGES b/CHANGES
index a0dd549..e25b9ea 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,13 @@
      BIO_dump_indent() are added.
      [Richard Levitte]
 
+  *) New functions ASN1_STRING_print_ex() and X509_NAME_print_ex()
+     these print out strings and name structures based on various
+     flags including RFC2253 support and proper handling of
+     multibyte characters. Added options to the 'x509' utility 
+     to allow the various flags to be set.
+     [Steve Henson]
+
   *) Various fixes to use ASN1_TIME instead of ASN1_UTCTIME.
      Also change the functions X509_cmp_current_time() and
      X509_gmtime_adj() work with an ASN1_TIME structure,
index 0781c4b..183a2b1 100644 (file)
@@ -653,3 +653,83 @@ end:
        return(othercerts);
        }
 
+typedef struct {
+       char *name;
+       unsigned long flag;
+       unsigned long mask;
+} NAME_EX_TBL;
+
+int set_name_ex(unsigned long *flags, const char *arg)
+{
+       char c;
+       const NAME_EX_TBL *ptbl, ex_tbl[] = {
+               { "esc_2253", ASN1_STRFLGS_ESC_2253, 0},
+               { "esc_ctrl", ASN1_STRFLGS_ESC_CTRL, 0},
+               { "esc_msb", ASN1_STRFLGS_ESC_MSB, 0},
+               { "use_quote", ASN1_STRFLGS_ESC_QUOTE, 0},
+               { "utf8", ASN1_STRFLGS_UTF8_CONVERT, 0},
+               { "no_type", ASN1_STRFLGS_IGNORE_TYPE, 0},
+               { "show_name", ASN1_STRFLGS_SHOW_NAME, 0},
+               { "dump_all", ASN1_STRFLGS_DUMP_ALL, 0},
+               { "dump_nostr", ASN1_STRFLGS_DUMP_UNKNOWN, 0},
+               { "dump_der", ASN1_STRFLGS_DUMP_DER, 0},
+               { "compat", XN_FLAG_COMPAT, 0xffffffffL},
+               { "sep_comma_plus", XN_FLAG_SEP_COMMA_PLUS, XN_FLAG_SEP_MASK},
+               { "sep_comma_plus_space", XN_FLAG_SEP_CPLUS_SPC, XN_FLAG_SEP_MASK},
+               { "sep_semi_plus_space", XN_FLAG_SEP_SPLUS_SPC, XN_FLAG_SEP_MASK},
+               { "sep_multiline", XN_FLAG_SEP_MULTILINE, XN_FLAG_SEP_MASK},
+               { "dn_rev", XN_FLAG_DN_REV, 0},
+               { "nofname", XN_FLAG_FN_NONE, XN_FLAG_FN_MASK},
+               { "sname", XN_FLAG_FN_SN, XN_FLAG_FN_MASK},
+               { "lname", XN_FLAG_FN_LN, XN_FLAG_FN_MASK},
+               { "oid", XN_FLAG_FN_OID, XN_FLAG_FN_MASK},
+               { "space_eq", XN_FLAG_SPC_EQ, 0},
+               { "dump_unknown", XN_FLAG_DUMP_UNKNOWN_FIELDS, 0},
+               { "RFC2253", XN_FLAG_RFC2253, 0xffffffffL},
+               { "oneline", XN_FLAG_ONELINE, 0xffffffffL},
+               { "multiline", XN_FLAG_MULTILINE, 0xffffffffL},
+               { NULL, 0, 0}
+       };
+
+       c = arg[0];
+
+       if(c == '-') {
+               c = 0;
+               arg++;
+       } else if (c == '+') {
+               c = 1;
+               arg++;
+       } else c = 1;
+
+       for(ptbl = ex_tbl; ptbl->name; ptbl++) {
+               if(!strcmp(arg, ptbl->name)) {
+                       *flags &= ~ptbl->mask;
+                       if(c) *flags |= ptbl->flag;
+                       else *flags &= ~ptbl->flag;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+void print_name(BIO *out, char *title, X509_NAME *nm, unsigned long lflags)
+{
+       char buf[256];
+       char mline = 0;
+       int indent = 0;
+       if(title) BIO_puts(out, title);
+       if((lflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) {
+               mline = 1;
+               indent = 4;
+       }
+       if(lflags == XN_FLAG_COMPAT) {
+               X509_NAME_oneline(nm,buf,256);
+               BIO_puts(out,buf);
+               BIO_puts(out, "\n");
+       } else {
+               if(mline) BIO_puts(out, "\n");
+               X509_NAME_print_ex(out, nm, indent, lflags);
+               BIO_puts(out, "\n");
+       }
+}
+
index df939e0..c44b214 100644 (file)
@@ -145,7 +145,9 @@ void program_name(char *in,char *out,int size);
 int chopup_args(ARGS *arg,char *buf, int *argc, char **argv[]);
 #ifdef HEADER_X509_H
 int dump_cert_text(BIO *out, X509 *x);
+void print_name(BIO *out, char *title, X509_NAME *nm, unsigned long lflags);
 #endif
+int set_name_ex(unsigned long *flags, const char *arg);
 int app_passwd(BIO *err, char *arg1, char *arg2, char **pass1, char **pass2);
 int add_oid_section(BIO *err, LHASH *conf);
 X509 *load_cert(BIO *err, char *file, int format);
index 39fbb65..a071b20 100644 (file)
@@ -128,6 +128,7 @@ static char *x509_usage[]={
 " -extfile        - configuration file with X509V3 extensions to add\n",
 " -extensions     - section from config file with X509V3 extensions to add\n",
 " -clrext         - delete extensions before signing and input certificate\n",
+" -nameopt arg    - various certificate name options\n",
 NULL
 };
 
@@ -173,6 +174,7 @@ int MAIN(int argc, char **argv)
        char *extsect = NULL, *extfile = NULL, *passin = NULL, *passargin = NULL;
        int need_rand = 0;
        int checkend=0,checkoffset=0;
+       unsigned long nmflag = 0;
 
        reqfile=0;
 
@@ -316,6 +318,11 @@ int MAIN(int argc, char **argv)
                        alias= *(++argv);
                        trustout = 1;
                        }
+               else if (strcmp(*argv,"-nameopt") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       if(!set_name_ex(&nmflag, *(++argv))) goto bad;
+                       }
                else if (strcmp(*argv,"-setalias") == 0)
                        {
                        if (--argc < 1) goto bad;
@@ -524,9 +531,8 @@ bad:
                        }
                else
                        BIO_printf(bio_err,"Signature ok\n");
-               
-               X509_NAME_oneline(req->req_info->subject,buf,256);
-               BIO_printf(bio_err,"subject=%s\n",buf);
+
+               print_name(bio_err, "subject=", X509_REQ_get_subject_name(req), nmflag);
 
                if ((x=X509_new()) == NULL) goto end;
                ci=x->cert_info;
@@ -600,15 +606,13 @@ bad:
                        {
                        if (issuer == i)
                                {
-                               X509_NAME_oneline(X509_get_issuer_name(x),
-                                       buf,256);
-                               BIO_printf(STDout,"issuer= %s\n",buf);
+                               print_name(STDout, "issuer= ",
+                                       X509_get_issuer_name(x), nmflag);
                                }
                        else if (subject == i) 
                                {
-                               X509_NAME_oneline(X509_get_subject_name(x),
-                                       buf,256);
-                               BIO_printf(STDout,"subject=%s\n",buf);
+                               print_name(STDout, "issuer= ",
+                                       X509_get_subject_name(x), nmflag);
                                }
                        else if (serial == i)
                                {
@@ -1082,7 +1086,6 @@ end:
 
 static int MS_CALLBACK callb(int ok, X509_STORE_CTX *ctx)
        {
-       char buf[256];
        int err;
        X509 *err_cert;
 
@@ -1104,8 +1107,7 @@ static int MS_CALLBACK callb(int ok, X509_STORE_CTX *ctx)
        else
                {
                err_cert=X509_STORE_CTX_get_current_cert(ctx);
-               X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256);
-               BIO_printf(bio_err,"%s\n",buf);
+               print_name(bio_err, NULL, X509_get_subject_name(err_cert),0);
                BIO_printf(bio_err,"error with certificate - error %d at depth %d\n%s\n",
                        err,X509_STORE_CTX_get_error_depth(ctx),
                        X509_verify_cert_error_string(err));
index 9034d35..aa788ef 100644 (file)
@@ -24,7 +24,7 @@ APPS=
 LIB=$(TOP)/libcrypto.a
 LIBSRC=        a_object.c a_bitstr.c a_utctm.c a_gentm.c a_time.c a_int.c a_octet.c \
        a_null.c a_print.c a_type.c a_set.c a_dup.c a_d2i_fp.c a_i2d_fp.c a_bmp.c \
-       a_enum.c a_vis.c a_utf8.c a_sign.c a_digest.c a_verify.c a_mbstr.c \
+       a_enum.c a_vis.c a_utf8.c a_sign.c a_digest.c a_verify.c a_mbstr.c a_strex.c \
        x_algor.c x_val.c x_pubkey.c x_sig.c x_req.c x_attrib.c \
        x_name.c x_cinf.c x_x509.c x_x509a.c x_crl.c x_info.c x_spki.c nsseq.c \
        d2i_r_pr.c i2d_r_pr.c d2i_r_pu.c i2d_r_pu.c \
@@ -39,7 +39,7 @@ LIBSRC=       a_object.c a_bitstr.c a_utctm.c a_gentm.c a_time.c a_int.c a_octet.c \
        evp_asn1.c asn_pack.c p5_pbe.c p5_pbev2.c p8_pkey.c
 LIBOBJ= a_object.o a_bitstr.o a_utctm.o a_gentm.o a_time.o a_int.o a_octet.o \
        a_null.o a_print.o a_type.o a_set.o a_dup.o a_d2i_fp.o a_i2d_fp.o a_bmp.o \
-       a_enum.o a_vis.o a_utf8.o a_sign.o a_digest.o a_verify.o a_mbstr.o \
+       a_enum.o a_vis.o a_utf8.o a_sign.o a_digest.o a_verify.o a_mbstr.o a_strex.o \
        x_algor.o x_val.o x_pubkey.o x_sig.o x_req.o x_attrib.o \
        x_name.o x_cinf.o x_x509.o x_x509a.o x_crl.o x_info.o x_spki.o nsseq.o \
        d2i_r_pr.o i2d_r_pr.o d2i_r_pu.o i2d_r_pu.o \
@@ -284,6 +284,23 @@ a_sign.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
 a_sign.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
 a_sign.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
 a_sign.o: ../cryptlib.h
+a_strex.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
+a_strex.o: ../../include/openssl/blowfish.h ../../include/openssl/bn.h
+a_strex.o: ../../include/openssl/buffer.h ../../include/openssl/cast.h
+a_strex.o: ../../include/openssl/crypto.h ../../include/openssl/des.h
+a_strex.o: ../../include/openssl/dh.h ../../include/openssl/dsa.h
+a_strex.o: ../../include/openssl/e_os2.h ../../include/openssl/evp.h
+a_strex.o: ../../include/openssl/idea.h ../../include/openssl/lhash.h
+a_strex.o: ../../include/openssl/md2.h ../../include/openssl/md5.h
+a_strex.o: ../../include/openssl/mdc2.h ../../include/openssl/obj_mac.h
+a_strex.o: ../../include/openssl/objects.h ../../include/openssl/opensslconf.h
+a_strex.o: ../../include/openssl/opensslv.h ../../include/openssl/pkcs7.h
+a_strex.o: ../../include/openssl/rc2.h ../../include/openssl/rc4.h
+a_strex.o: ../../include/openssl/rc5.h ../../include/openssl/ripemd.h
+a_strex.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
+a_strex.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+a_strex.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
+a_strex.o: charmap.h
 a_strnid.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
 a_strnid.o: ../../include/openssl/bn.h ../../include/openssl/buffer.h
 a_strnid.o: ../../include/openssl/crypto.h ../../include/openssl/e_os.h
diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c
new file mode 100644 (file)
index 0000000..ef225be
--- /dev/null
@@ -0,0 +1,511 @@
+/* a_strex.c */
+/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
+ * project 2000.
+ */
+/* ====================================================================
+ * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#include <stdio.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/asn1.h>
+
+#include "charmap.h"
+
+/* ASN1_STRING_print_ex() and X509_NAME_print_ex().
+ * Enhanced string and name printing routines handling
+ * multibyte characters, RFC2253 and a host of other
+ * options.
+ */
+
+
+#define CHARTYPE_BS_ESC                (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
+
+
+/* Three IO functions for sending data to memory, a BIO and
+ * and a FILE pointer.
+ */
+
+int send_mem_chars(void *arg, const void *buf, int len)
+{
+       unsigned char **out = arg;
+       if(!out) return 1;
+       memcpy(*out, buf, len);
+       *out += len;
+       return 1;
+}
+
+int send_bio_chars(void *arg, const void *buf, int len)
+{
+       if(!arg) return 1;
+       if(BIO_write(arg, buf, len) != len) return 0;
+       return 1;
+}
+
+int send_fp_chars(void *arg, const void *buf, int len)
+{
+       if(!arg) return 1;
+       if(fwrite(buf, 1, len, arg) != len) return 0;
+       return 1;
+}
+
+typedef int char_io(void *arg, const void *buf, int len);
+
+/* This function handles display of
+ * strings, one character at a time.
+ * It is passed an unsigned long for each
+ * character because it could come from 2 or even
+ * 4 byte forms.
+ */
+
+static int do_esc_char(unsigned long c, unsigned char flags, char *do_quotes, char_io *io_ch, void *arg)
+{
+       unsigned char chflgs, chtmp;
+       char tmphex[11];
+       if(c > 0xffff) {
+               BIO_snprintf(tmphex, 11, "\\W%08lX", c);
+               if(!io_ch(arg, tmphex, 10)) return -1;
+               return 10;
+       }
+       if(c > 0xff) {
+               BIO_snprintf(tmphex, 11, "\\U%04lX", c);
+               if(!io_ch(arg, tmphex, 6)) return -1;
+               return 6;
+       }
+       chtmp = c;
+       if(chtmp > 0x7f) chflgs = flags & ASN1_STRFLGS_ESC_MSB;
+       else chflgs = char_type[chtmp] & flags;
+       if(chflgs & CHARTYPE_BS_ESC) {
+               /* If we don't escape with quotes, signal we need quotes */
+               if(chflgs & ASN1_STRFLGS_ESC_QUOTE) {
+                       if(do_quotes) *do_quotes = 1;
+                       if(!io_ch(arg, &chtmp, 1)) return -1;
+                       return 1;
+               }
+               if(!io_ch(arg, "\\", 1)) return -1;
+               if(!io_ch(arg, &chtmp, 1)) return -1;
+               return 2;
+       }
+       if(chflgs & (ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_ESC_MSB)) {
+               BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
+               if(!io_ch(arg, tmphex, 3)) return -1;
+               return 3;
+       }
+       if(!io_ch(arg, &chtmp, 1)) return -1;
+       return 1;
+}
+
+#define BUF_TYPE_WIDTH_MASK    0x7
+#define BUF_TYPE_CONVUTF8      0x8
+
+/* This function sends each character in a buffer to
+ * do_esc_char(). It interprets the content formats
+ * and converts to or from UTF8 as appropriate.
+ */
+
+static int do_buf(unsigned char *buf, int buflen,
+                       int type, unsigned char flags, char *quotes, char_io *io_ch, void *arg)
+{
+       int i, outlen, len;
+       unsigned char orflags, *p, *q;
+       unsigned long c;
+       p = buf;
+       q = buf + buflen;
+       outlen = 0;
+       while(p != q) {
+               if(p == buf) orflags = CHARTYPE_FIRST_ESC_2253;
+               else orflags = 0;
+               switch(type & BUF_TYPE_WIDTH_MASK) {
+                       case 4:
+                       c = ((unsigned long)*p++) << 24;
+                       c |= ((unsigned long)*p++) << 16;
+                       c |= ((unsigned long)*p++) << 8;
+                       c |= *p++;
+                       break;
+
+                       case 2:
+                       c = ((unsigned long)*p++) << 8;
+                       c |= *p++;
+                       break;
+
+                       case 1:
+                       c = *p++;
+                       break;
+                       
+                       case 0:
+                       i = UTF8_getc(p, buflen, &c);
+                       if(i < 0) return -1;    /* Invalid UTF8String */
+                       p += i;
+                       break;
+               }
+               if (p == q) orflags = CHARTYPE_LAST_ESC_2253;
+               if(type & BUF_TYPE_CONVUTF8) {
+                       unsigned char utfbuf[6];
+                       int utflen;
+                       utflen = UTF8_putc(utfbuf, 6, c);
+                       for(i = 0; i < utflen; i++) {
+                               /* We don't need to worry about setting orflags correctly
+                                * because if utflen==1 its value will be correct anyway 
+                                * otherwise each character will be > 0x7f and so the 
+                                * character will never be escaped on first and last.
+                                */
+                               len = do_esc_char(utfbuf[i], flags | orflags, quotes, io_ch, arg);
+                               if(len < 0) return -1;
+                               outlen += len;
+                       }
+               } else {
+                       len = do_esc_char(c, flags | orflags, quotes, io_ch, arg);
+                       if(len < 0) return -1;
+                       outlen += len;
+               }
+       }
+       return outlen;
+}
+
+/* This function hex dumps a buffer of characters */
+
+static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, int buflen)
+{
+       const static char hexdig[] = "0123456789ABCDEF";
+       unsigned char *p, *q;
+       char hextmp[2];
+       if(arg) {
+               p = buf;
+               q = buf + buflen;
+               while(p != q) {
+                       hextmp[0] = hexdig[*p >> 4];
+                       hextmp[1] = hexdig[*p & 0xf];
+                       if(!io_ch(arg, hextmp, 2)) return -1;
+                       p++;
+               }
+       }
+       return buflen << 1;
+}
+
+/* "dump" a string. This is done when the type is unknown,
+ * or the flags request it. We can either dump the content
+ * octets or the entire DER encoding. This uses the RFC2253
+ * #01234 format.
+ */
+
+int do_dump(unsigned long lflags, char_io *io_ch, void *arg, ASN1_STRING *str)
+{
+       /* Placing the ASN1_STRING in a temp ASN1_TYPE allows
+        * the DER encoding to readily obtained
+        */
+       ASN1_TYPE t;
+       unsigned char *der_buf, *p;
+       int outlen, der_len;
+
+       if(!io_ch(arg, "#", 1)) return -1;
+       /* If we don't dump DER encoding just dump content octets */
+       if(!(lflags & ASN1_STRFLGS_DUMP_DER)) {
+               outlen = do_hex_dump(io_ch, arg, str->data, str->length);
+               if(outlen < 0) return -1;
+               return outlen + 1;
+       }
+       t.type = str->type;
+       t.value.ptr = (char *)str;
+       der_len = i2d_ASN1_TYPE(&t, NULL);
+       der_buf = OPENSSL_malloc(der_len);
+       if(!der_buf) return -1;
+       p = der_buf;
+       i2d_ASN1_TYPE(&t, &p);
+       outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
+       OPENSSL_free(der_buf);
+       if(outlen < 0) return -1;
+       return outlen + 1;
+}
+
+/* Lookup table to convert tags to character widths,
+ * 0 = UTF8 encoded, -1 is used for non string types
+ * otherwise it is the number of bytes per character
+ */
+
+const static char tag2nbyte[] = {
+       -1, -1, -1, -1, -1,     /* 0-4 */
+       -1, -1, -1, -1, -1,     /* 5-9 */
+       -1, -1, 0, -1,          /* 10-13 */
+       -1, -1, -1, -1,         /* 15-17 */
+       -1, 1, 1,               /* 18-20 */
+       -1, 1, -1,-1,           /* 21-24 */
+       -1, 1, -1,              /* 25-27 */
+       4, -1, 2                /* 28-30 */
+};
+
+#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
+                 ASN1_STRFLGS_ESC_QUOTE | \
+                 ASN1_STRFLGS_ESC_CTRL | \
+                 ASN1_STRFLGS_ESC_MSB)
+
+/* This is the main function, print out an
+ * ASN1_STRING taking note of various escape
+ * and display options. Returns number of
+ * characters written or -1 if an error
+ * occurred.
+ */
+
+static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, ASN1_STRING *str)
+{
+       int outlen, len;
+       int type;
+       char quotes;
+       unsigned char flags;
+       quotes = 0;
+       /* Keep a copy of escape flags */
+       flags = lflags & ESC_FLAGS;
+
+       type = str->type;
+
+       outlen = 0;
+
+
+       if(lflags & ASN1_STRFLGS_SHOW_NAME) {
+               const char *tagname;
+               tagname = ASN1_tag2str(type);
+               outlen += strlen(tagname);
+               if(!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1)) return -1; 
+               outlen++;
+       }
+
+       /* Decide what to do with type, either dump content or display it */
+
+       /* Dump everything */
+       if(lflags & ASN1_STRFLGS_DUMP_ALL) type = -1;
+       /* Ignore the string type */
+       else if(lflags & ASN1_STRFLGS_IGNORE_TYPE) type = 1;
+       else {
+               /* Else determine width based on type */
+               if((type > 0) && (type < 31)) type = tag2nbyte[type];
+               else type = -1;
+               if((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN)) type = 1;
+       }
+
+       if(type == -1) {
+               len = do_dump(lflags, io_ch, arg, str);
+               if(len < 0) return -1;
+               outlen += len;
+               return outlen;
+       }
+
+       if(lflags & ASN1_STRFLGS_UTF8_CONVERT) {
+               /* Note: if string is UTF8 and we want
+                * to convert to UTF8 then we just interpret
+                * it as 1 byte per character to avoid converting
+                * twice.
+                */
+               if(!type) type = 1;
+               else type |= BUF_TYPE_CONVUTF8;
+       }
+
+       len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
+       if(outlen < 0) return -1;
+       outlen += len;
+       if(quotes) outlen += 2;
+       if(!arg) return outlen;
+       if(quotes && !io_ch(arg, "\"", 1)) return -1;
+       do_buf(str->data, str->length, type, flags, NULL, io_ch, arg);
+       if(quotes && !io_ch(arg, "\"", 1)) return -1;
+       return outlen;
+}
+
+/* Used for line indenting: print 'indent' spaces */
+
+static int do_indent(char_io *io_ch, void *arg, int indent)
+{
+       int i;
+       for(i = 0; i < indent; i++)
+                       if(!io_ch(arg, " ", 1)) return 0;
+       return 1;
+}
+
+
+static int do_name_ex(char_io *io_ch, void *arg, 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;
+       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(io_ch, arg, 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 = 1;
+               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;
+       }
+
+       fn_opt = flags & XN_FLAG_FN_MASK;
+
+       cnt = X509_NAME_entry_count(n); 
+       for(i = 0; i < cnt; i++) {
+               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 == ent->set) {
+                               if(!io_ch(arg, sep_mv, sep_mv_len)) return -1;
+                               outlen += sep_mv_len;
+                       } else {
+                               if(!io_ch(arg, sep_dn, sep_dn_len)) return -1;
+                               outlen += sep_dn_len;
+                       }
+                       if(!do_indent(io_ch, arg, indent)) return -1;
+                       outlen += indent;
+               }
+               prev = ent->set;
+               fn = X509_NAME_ENTRY_get_object(ent);
+               val = X509_NAME_ENTRY_get_data(ent);
+               fn_nid = OBJ_obj2nid(fn);
+               if(fn_opt != XN_FLAG_FN_NONE) {
+                       int objlen;
+                       if((fn_opt == XN_FLAG_FN_OID) || (fn_nid==NID_undef) ) {
+                               OBJ_obj2txt(objtmp, 80, fn, 1);
+                               objbuf = objtmp;
+                       } else {
+                               if(fn_opt == XN_FLAG_FN_SN) 
+                                       objbuf = OBJ_nid2sn(fn_nid);
+                               else if(fn_opt == XN_FLAG_FN_LN)
+                                       objbuf = OBJ_nid2ln(fn_nid);
+                               else objbuf = "";
+                       }
+                       objlen = strlen(objbuf);
+                       if(!io_ch(arg, objbuf, objlen)) return -1;
+                       if(!io_ch(arg, 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 = do_print_ex(io_ch, arg, flags | orflags, val);
+               if(len < 0) return -1;
+               outlen += len;
+       }
+       return outlen;
+}
+
+/* Wrappers round the main functions */
+
+int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags)
+{
+       return do_name_ex(send_bio_chars, out, nm, indent, flags);
+}
+
+
+int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags)
+{
+       return do_name_ex(send_fp_chars, fp, nm, indent, flags);
+}
+
+int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags)
+{
+       return do_print_ex(send_bio_chars, out, flags, str);
+}
+
+
+int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags)
+{
+       return do_print_ex(send_fp_chars, fp, flags, str);
+}
index e269644..9cf0fc0 100644 (file)
@@ -259,6 +259,85 @@ typedef int ASN1_BOOLEAN;
 
 typedef int ASN1_NULL;
 
+/* Parameters used by ASN1_STRING_print_ex() */
+
+/* These determine which characters to escape:
+ * RFC2253 special characters, control characters and
+ * MSB set characters
+ */
+
+#define ASN1_STRFLGS_ESC_2253          1
+#define ASN1_STRFLGS_ESC_CTRL          2
+#define ASN1_STRFLGS_ESC_MSB           4
+
+
+/* This flag determines how we do escaping: normally
+ * RC2253 backslash only, set this to use backslash and
+ * quote.
+ */
+
+#define ASN1_STRFLGS_ESC_QUOTE         8
+
+
+/* These three flags are internal use only. */
+
+/* Character is a valid PrintableString character */
+#define CHARTYPE_PRINTABLESTRING       0x10
+/* Character needs escaping if it is the first character */
+#define CHARTYPE_FIRST_ESC_2253                0x20
+/* Character needs escaping if it is the last character */
+#define CHARTYPE_LAST_ESC_2253         0x40
+
+/* NB the internal flags are safely reused below by flags
+ * handled at the top level.
+ */
+
+/* If this is set we convert all character strings
+ * to UTF8 first 
+ */
+
+#define ASN1_STRFLGS_UTF8_CONVERT      0x10
+
+/* If this is set we don't attempt to interpret content:
+ * just assume all strings are 1 byte per character. This
+ * will produce some pretty odd looking output!
+ */
+
+#define ASN1_STRFLGS_IGNORE_TYPE       0x20
+
+/* If this is set we include the string type in the output */
+#define ASN1_STRFLGS_SHOW_NAME         0x40
+
+/* This determines which strings to display and which to
+ * 'dump' (hex dump of content octets or DER encoding). We can
+ * only dump non character strings or everything. If we
+ * don't dump 'unknown' they are interpreted as character
+ * strings with 1 octet per character and are subject to
+ * the usual escaping options.
+ */
+
+#define ASN1_STRFLGS_DUMP_ALL          0x80
+#define ASN1_STRFLGS_DUMP_UNKNOWN      0x100
+
+/* These determine what 'dumping' does, we can dump the
+ * content octets or the DER encoding: both use the
+ * RFC2253 #XXXXX notation.
+ */
+
+#define ASN1_STRFLGS_DUMP_DER          0x200
+
+/* All the string flags consistent with RFC2253,
+ * escaping control characters isn't essential in
+ * RFC2253 but it is advisable anyway.
+ */
+
+#define ASN1_STRFLGS_RFC2253   (ASN1_STRFLGS_ESC_2253 | \
+                               ASN1_STRFLGS_ESC_CTRL | \
+                               ASN1_STRFLGS_ESC_MSB | \
+                               ASN1_STRFLGS_UTF8_CONVERT | \
+                               ASN1_STRFLGS_DUMP_UNKNOWN | \
+                               ASN1_STRFLGS_DUMP_DER)
+
 DECLARE_STACK_OF(ASN1_INTEGER)
 DECLARE_ASN1_SET_OF(ASN1_INTEGER)
 
@@ -727,6 +806,7 @@ char *ASN1_dup(int (*i2d)(),char *(*d2i)(),char *x);
 #ifndef NO_FP_API
 char *ASN1_d2i_fp(char *(*xnew)(),char *(*d2i)(),FILE *fp,unsigned char **x);
 int ASN1_i2d_fp(int (*i2d)(),FILE *out,unsigned char *x);
+int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags);
 #endif
 
 #ifndef NO_BIO
@@ -736,6 +816,7 @@ int ASN1_UTCTIME_print(BIO *fp,ASN1_UTCTIME *a);
 int ASN1_GENERALIZEDTIME_print(BIO *fp,ASN1_GENERALIZEDTIME *a);
 int ASN1_TIME_print(BIO *fp,ASN1_TIME *a);
 int ASN1_STRING_print(BIO *bp,ASN1_STRING *v);
+int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags);
 int ASN1_parse(BIO *bp,unsigned char *pp,long len,int indent);
 int ASN1_parse_dump(BIO *bp,unsigned char *pp,long len,int indent,int dump);
 #endif
diff --git a/crypto/asn1/charmap.h b/crypto/asn1/charmap.h
new file mode 100644 (file)
index 0000000..bd020a9
--- /dev/null
@@ -0,0 +1,15 @@
+/* Auto generated with chartype.pl script.
+ * Mask of various character properties
+ */
+
+static unsigned char char_type[] = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+120, 0, 1,40, 0, 0, 0,16,16,16, 0,25,25,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 9, 9,16, 9,16,
+ 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 0, 1, 0, 0, 0,
+ 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 2
+};
+
diff --git a/crypto/asn1/charmap.pl b/crypto/asn1/charmap.pl
new file mode 100644 (file)
index 0000000..2875c59
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+my ($i, @arr);
+
+# Set up an array with the type of ASCII characters
+# Each set bit represents a character property.
+
+# RFC2253 character properties
+my $RFC2253_ESC = 1;   # Character escaped with \
+my $ESC_CTRL   = 2;    # Escaped control character
+# These are used with RFC1779 quoting using "
+my $NOESC_QUOTE        = 8;    # Not escaped if quoted
+my $PSTRING_CHAR = 0x10;       # Valid PrintableString character
+my $RFC2253_FIRST_ESC = 0x20; # Escaped with \ if first character
+my $RFC2253_LAST_ESC = 0x40;  # Escaped with \ if last character
+
+for($i = 0; $i < 128; $i++) {
+       # Set the RFC2253 escape characters (control)
+       $arr[$i] = 0;
+       if(($i < 32) || ($i > 126)) {
+               $arr[$i] |= $ESC_CTRL;
+       }
+
+       # Some PrintableString characters
+       if(                ( ( $i >= ord("a")) && ( $i <= ord("z")) )
+                       || (  ( $i >= ord("A")) && ( $i <= ord("Z")) )
+                       || (  ( $i >= ord("0")) && ( $i <= ord("9")) )  ) {
+               $arr[$i] |= $PSTRING_CHAR;
+       }
+}
+
+# Now setup the rest
+
+# Remaining RFC2253 escaped characters
+
+$arr[ord(" ")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC | $RFC2253_LAST_ESC;
+$arr[ord("#")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC;
+
+$arr[ord(",")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("+")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("\"")] |= $RFC2253_ESC;
+$arr[ord("\\")] |= $RFC2253_ESC;
+$arr[ord("<")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(">")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(";")] |= $NOESC_QUOTE | $RFC2253_ESC;
+
+# Remaining PrintableString characters
+
+$arr[ord(" ")] |= $PSTRING_CHAR;
+$arr[ord("'")] |= $PSTRING_CHAR;
+$arr[ord("(")] |= $PSTRING_CHAR;
+$arr[ord(")")] |= $PSTRING_CHAR;
+$arr[ord("+")] |= $PSTRING_CHAR;
+$arr[ord(",")] |= $PSTRING_CHAR;
+$arr[ord("-")] |= $PSTRING_CHAR;
+$arr[ord(".")] |= $PSTRING_CHAR;
+$arr[ord("/")] |= $PSTRING_CHAR;
+$arr[ord(":")] |= $PSTRING_CHAR;
+$arr[ord("=")] |= $PSTRING_CHAR;
+$arr[ord("?")] |= $PSTRING_CHAR;
+
+# Now generate the C code
+
+print <<EOF;
+/* Auto generated with chartype.pl script.
+ * Mask of various character properties
+ */
+
+static unsigned char char_type[] = {
+EOF
+
+for($i = 0; $i < 128; $i++) {
+       print("\n") if($i && (($i % 16) == 0));
+       printf("%2d", $arr[$i]);
+       print(",") if ($i != 127);
+}
+print("\n};\n\n");
+
index 7955e5c..ef72135 100644 (file)
@@ -320,6 +320,61 @@ DECLARE_STACK_OF(X509_TRUST)
 #define X509_TRUST_REJECTED    2
 #define X509_TRUST_UNTRUSTED   3
 
+/* Flags specific to X509_NAME_print_ex() */   
+
+/* 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)       /* RFC2253 ,+ */
+#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_NONE                0               /* No field names */
+#define XN_FLAG_FN_SN          (1 << 21)       /* Object short name */
+#define XN_FLAG_FN_LN          (2 << 21)       /* Object long name */
+#define XN_FLAG_FN_OID         (3 << 21)       /* Always use OIDs */
+
+#define XN_FLAG_SPC_EQ         (1 << 23)       /* Put spaces round '=' */
+
+/* This determines if we dump fields we don't recognise:
+ * RFC2253 requires this.
+ */
+
+#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24)
+
+/* Complete set of RFC2253 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)
+
 typedef struct X509_revoked_st
        {
        ASN1_INTEGER *serialNumber;
@@ -975,10 +1030,12 @@ int              X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b);
 int            X509_print_fp(FILE *bp,X509 *x);
 int            X509_CRL_print_fp(FILE *bp,X509_CRL *x);
 int            X509_REQ_print_fp(FILE *bp,X509_REQ *req);
+int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags);
 #endif
 
 #ifndef NO_BIO
 int            X509_NAME_print(BIO *bp, X509_NAME *name, int obase);
+int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags);
 int            X509_print(BIO *bp,X509 *x);
 int            X509_CERT_AUX_print(BIO *bp,X509_CERT_AUX *x, int indent);
 int            X509_CRL_print(BIO *bp,X509_CRL *x);