a_strex.c revision 296465
1/* a_strex.c */
2/*
3 * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
4 * 2000.
5 */
6/* ====================================================================
7 * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in
18 *    the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * 3. All advertising materials mentioning features or use of this
22 *    software must display the following acknowledgment:
23 *    "This product includes software developed by the OpenSSL Project
24 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25 *
26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27 *    endorse or promote products derived from this software without
28 *    prior written permission. For written permission, please contact
29 *    licensing@OpenSSL.org.
30 *
31 * 5. Products derived from this software may not be called "OpenSSL"
32 *    nor may "OpenSSL" appear in their names without prior written
33 *    permission of the OpenSSL Project.
34 *
35 * 6. Redistributions of any form whatsoever must retain the following
36 *    acknowledgment:
37 *    "This product includes software developed by the OpenSSL Project
38 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51 * OF THE POSSIBILITY OF SUCH DAMAGE.
52 * ====================================================================
53 *
54 * This product includes cryptographic software written by Eric Young
55 * (eay@cryptsoft.com).  This product includes software written by Tim
56 * Hudson (tjh@cryptsoft.com).
57 *
58 */
59
60#include <stdio.h>
61#include <string.h>
62#include "cryptlib.h"
63#include <openssl/crypto.h>
64#include <openssl/x509.h>
65#include <openssl/asn1.h>
66
67#include "charmap.h"
68
69/*
70 * ASN1_STRING_print_ex() and X509_NAME_print_ex(). Enhanced string and name
71 * printing routines handling multibyte characters, RFC2253 and a host of
72 * other options.
73 */
74
75#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
76
77#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
78                  ASN1_STRFLGS_ESC_QUOTE | \
79                  ASN1_STRFLGS_ESC_CTRL | \
80                  ASN1_STRFLGS_ESC_MSB)
81
82/*
83 * Three IO functions for sending data to memory, a BIO and and a FILE
84 * pointer.
85 */
86#if 0                           /* never used */
87static int send_mem_chars(void *arg, const void *buf, int len)
88{
89    unsigned char **out = arg;
90    if (!out)
91        return 1;
92    memcpy(*out, buf, len);
93    *out += len;
94    return 1;
95}
96#endif
97
98static int send_bio_chars(void *arg, const void *buf, int len)
99{
100    if (!arg)
101        return 1;
102    if (BIO_write(arg, buf, len) != len)
103        return 0;
104    return 1;
105}
106
107static int send_fp_chars(void *arg, const void *buf, int len)
108{
109    if (!arg)
110        return 1;
111    if (fwrite(buf, 1, len, arg) != (unsigned int)len)
112        return 0;
113    return 1;
114}
115
116typedef int char_io (void *arg, const void *buf, int len);
117
118/*
119 * This function handles display of strings, one character at a time. It is
120 * passed an unsigned long for each character because it could come from 2 or
121 * even 4 byte forms.
122 */
123
124static int do_esc_char(unsigned long c, unsigned char flags, char *do_quotes,
125                       char_io *io_ch, void *arg)
126{
127    unsigned char chflgs, chtmp;
128    char tmphex[HEX_SIZE(long) + 3];
129
130    if (c > 0xffffffffL)
131        return -1;
132    if (c > 0xffff) {
133        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08lX", c);
134        if (!io_ch(arg, tmphex, 10))
135            return -1;
136        return 10;
137    }
138    if (c > 0xff) {
139        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04lX", c);
140        if (!io_ch(arg, tmphex, 6))
141            return -1;
142        return 6;
143    }
144    chtmp = (unsigned char)c;
145    if (chtmp > 0x7f)
146        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
147    else
148        chflgs = char_type[chtmp] & flags;
149    if (chflgs & CHARTYPE_BS_ESC) {
150        /* If we don't escape with quotes, signal we need quotes */
151        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
152            if (do_quotes)
153                *do_quotes = 1;
154            if (!io_ch(arg, &chtmp, 1))
155                return -1;
156            return 1;
157        }
158        if (!io_ch(arg, "\\", 1))
159            return -1;
160        if (!io_ch(arg, &chtmp, 1))
161            return -1;
162        return 2;
163    }
164    if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
165        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
166        if (!io_ch(arg, tmphex, 3))
167            return -1;
168        return 3;
169    }
170    /*
171     * If we get this far and do any escaping at all must escape the escape
172     * character itself: backslash.
173     */
174    if (chtmp == '\\' && flags & ESC_FLAGS) {
175        if (!io_ch(arg, "\\\\", 2))
176            return -1;
177        return 2;
178    }
179    if (!io_ch(arg, &chtmp, 1))
180        return -1;
181    return 1;
182}
183
184#define BUF_TYPE_WIDTH_MASK     0x7
185#define BUF_TYPE_CONVUTF8       0x8
186
187/*
188 * This function sends each character in a buffer to do_esc_char(). It
189 * interprets the content formats and converts to or from UTF8 as
190 * appropriate.
191 */
192
193static int do_buf(unsigned char *buf, int buflen,
194                  int type, unsigned char flags, char *quotes, char_io *io_ch,
195                  void *arg)
196{
197    int i, outlen, len;
198    unsigned char orflags, *p, *q;
199    unsigned long c;
200    p = buf;
201    q = buf + buflen;
202    outlen = 0;
203    while (p != q) {
204        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
205            orflags = CHARTYPE_FIRST_ESC_2253;
206        else
207            orflags = 0;
208        switch (type & BUF_TYPE_WIDTH_MASK) {
209        case 4:
210            c = ((unsigned long)*p++) << 24;
211            c |= ((unsigned long)*p++) << 16;
212            c |= ((unsigned long)*p++) << 8;
213            c |= *p++;
214            break;
215
216        case 2:
217            c = ((unsigned long)*p++) << 8;
218            c |= *p++;
219            break;
220
221        case 1:
222            c = *p++;
223            break;
224
225        case 0:
226            i = UTF8_getc(p, buflen, &c);
227            if (i < 0)
228                return -1;      /* Invalid UTF8String */
229            p += i;
230            break;
231        default:
232            return -1;          /* invalid width */
233        }
234        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
235            orflags = CHARTYPE_LAST_ESC_2253;
236        if (type & BUF_TYPE_CONVUTF8) {
237            unsigned char utfbuf[6];
238            int utflen;
239            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
240            for (i = 0; i < utflen; i++) {
241                /*
242                 * We don't need to worry about setting orflags correctly
243                 * because if utflen==1 its value will be correct anyway
244                 * otherwise each character will be > 0x7f and so the
245                 * character will never be escaped on first and last.
246                 */
247                len =
248                    do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
249                                quotes, io_ch, arg);
250                if (len < 0)
251                    return -1;
252                outlen += len;
253            }
254        } else {
255            len =
256                do_esc_char(c, (unsigned char)(flags | orflags), quotes,
257                            io_ch, arg);
258            if (len < 0)
259                return -1;
260            outlen += len;
261        }
262    }
263    return outlen;
264}
265
266/* This function hex dumps a buffer of characters */
267
268static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf,
269                       int buflen)
270{
271    static const char hexdig[] = "0123456789ABCDEF";
272    unsigned char *p, *q;
273    char hextmp[2];
274    if (arg) {
275        p = buf;
276        q = buf + buflen;
277        while (p != q) {
278            hextmp[0] = hexdig[*p >> 4];
279            hextmp[1] = hexdig[*p & 0xf];
280            if (!io_ch(arg, hextmp, 2))
281                return -1;
282            p++;
283        }
284    }
285    return buflen << 1;
286}
287
288/*
289 * "dump" a string. This is done when the type is unknown, or the flags
290 * request it. We can either dump the content octets or the entire DER
291 * encoding. This uses the RFC2253 #01234 format.
292 */
293
294static int do_dump(unsigned long lflags, char_io *io_ch, void *arg,
295                   ASN1_STRING *str)
296{
297    /*
298     * Placing the ASN1_STRING in a temp ASN1_TYPE allows the DER encoding to
299     * readily obtained
300     */
301    ASN1_TYPE t;
302    unsigned char *der_buf, *p;
303    int outlen, der_len;
304
305    if (!io_ch(arg, "#", 1))
306        return -1;
307    /* If we don't dump DER encoding just dump content octets */
308    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
309        outlen = do_hex_dump(io_ch, arg, str->data, str->length);
310        if (outlen < 0)
311            return -1;
312        return outlen + 1;
313    }
314    t.type = str->type;
315    t.value.ptr = (char *)str;
316    der_len = i2d_ASN1_TYPE(&t, NULL);
317    der_buf = OPENSSL_malloc(der_len);
318    if (!der_buf)
319        return -1;
320    p = der_buf;
321    i2d_ASN1_TYPE(&t, &p);
322    outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
323    OPENSSL_free(der_buf);
324    if (outlen < 0)
325        return -1;
326    return outlen + 1;
327}
328
329/*
330 * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
331 * used for non string types otherwise it is the number of bytes per
332 * character
333 */
334
335static const signed char tag2nbyte[] = {
336    -1, -1, -1, -1, -1,         /* 0-4 */
337    -1, -1, -1, -1, -1,         /* 5-9 */
338    -1, -1, 0, -1,              /* 10-13 */
339    -1, -1, -1, -1,             /* 15-17 */
340    -1, 1, 1,                   /* 18-20 */
341    -1, 1, 1, 1,                /* 21-24 */
342    -1, 1, -1,                  /* 25-27 */
343    4, -1, 2                    /* 28-30 */
344};
345
346/*
347 * This is the main function, print out an ASN1_STRING taking note of various
348 * escape and display options. Returns number of characters written or -1 if
349 * an error occurred.
350 */
351
352static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags,
353                       ASN1_STRING *str)
354{
355    int outlen, len;
356    int type;
357    char quotes;
358    unsigned char flags;
359    quotes = 0;
360    /* Keep a copy of escape flags */
361    flags = (unsigned char)(lflags & ESC_FLAGS);
362
363    type = str->type;
364
365    outlen = 0;
366
367    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
368        const char *tagname;
369        tagname = ASN1_tag2str(type);
370        outlen += strlen(tagname);
371        if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1))
372            return -1;
373        outlen++;
374    }
375
376    /* Decide what to do with type, either dump content or display it */
377
378    /* Dump everything */
379    if (lflags & ASN1_STRFLGS_DUMP_ALL)
380        type = -1;
381    /* Ignore the string type */
382    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
383        type = 1;
384    else {
385        /* Else determine width based on type */
386        if ((type > 0) && (type < 31))
387            type = tag2nbyte[type];
388        else
389            type = -1;
390        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
391            type = 1;
392    }
393
394    if (type == -1) {
395        len = do_dump(lflags, io_ch, arg, str);
396        if (len < 0)
397            return -1;
398        outlen += len;
399        return outlen;
400    }
401
402    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
403        /*
404         * Note: if string is UTF8 and we want to convert to UTF8 then we
405         * just interpret it as 1 byte per character to avoid converting
406         * twice.
407         */
408        if (!type)
409            type = 1;
410        else
411            type |= BUF_TYPE_CONVUTF8;
412    }
413
414    len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
415    if (len < 0)
416        return -1;
417    outlen += len;
418    if (quotes)
419        outlen += 2;
420    if (!arg)
421        return outlen;
422    if (quotes && !io_ch(arg, "\"", 1))
423        return -1;
424    if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
425        return -1;
426    if (quotes && !io_ch(arg, "\"", 1))
427        return -1;
428    return outlen;
429}
430
431/* Used for line indenting: print 'indent' spaces */
432
433static int do_indent(char_io *io_ch, void *arg, int indent)
434{
435    int i;
436    for (i = 0; i < indent; i++)
437        if (!io_ch(arg, " ", 1))
438            return 0;
439    return 1;
440}
441
442#define FN_WIDTH_LN     25
443#define FN_WIDTH_SN     10
444
445static int do_name_ex(char_io *io_ch, void *arg, X509_NAME *n,
446                      int indent, unsigned long flags)
447{
448    int i, prev = -1, orflags, cnt;
449    int fn_opt, fn_nid;
450    ASN1_OBJECT *fn;
451    ASN1_STRING *val;
452    X509_NAME_ENTRY *ent;
453    char objtmp[80];
454    const char *objbuf;
455    int outlen, len;
456    char *sep_dn, *sep_mv, *sep_eq;
457    int sep_dn_len, sep_mv_len, sep_eq_len;
458    if (indent < 0)
459        indent = 0;
460    outlen = indent;
461    if (!do_indent(io_ch, arg, indent))
462        return -1;
463    switch (flags & XN_FLAG_SEP_MASK) {
464    case XN_FLAG_SEP_MULTILINE:
465        sep_dn = "\n";
466        sep_dn_len = 1;
467        sep_mv = " + ";
468        sep_mv_len = 3;
469        break;
470
471    case XN_FLAG_SEP_COMMA_PLUS:
472        sep_dn = ",";
473        sep_dn_len = 1;
474        sep_mv = "+";
475        sep_mv_len = 1;
476        indent = 0;
477        break;
478
479    case XN_FLAG_SEP_CPLUS_SPC:
480        sep_dn = ", ";
481        sep_dn_len = 2;
482        sep_mv = " + ";
483        sep_mv_len = 3;
484        indent = 0;
485        break;
486
487    case XN_FLAG_SEP_SPLUS_SPC:
488        sep_dn = "; ";
489        sep_dn_len = 2;
490        sep_mv = " + ";
491        sep_mv_len = 3;
492        indent = 0;
493        break;
494
495    default:
496        return -1;
497    }
498
499    if (flags & XN_FLAG_SPC_EQ) {
500        sep_eq = " = ";
501        sep_eq_len = 3;
502    } else {
503        sep_eq = "=";
504        sep_eq_len = 1;
505    }
506
507    fn_opt = flags & XN_FLAG_FN_MASK;
508
509    cnt = X509_NAME_entry_count(n);
510    for (i = 0; i < cnt; i++) {
511        if (flags & XN_FLAG_DN_REV)
512            ent = X509_NAME_get_entry(n, cnt - i - 1);
513        else
514            ent = X509_NAME_get_entry(n, i);
515        if (prev != -1) {
516            if (prev == ent->set) {
517                if (!io_ch(arg, sep_mv, sep_mv_len))
518                    return -1;
519                outlen += sep_mv_len;
520            } else {
521                if (!io_ch(arg, sep_dn, sep_dn_len))
522                    return -1;
523                outlen += sep_dn_len;
524                if (!do_indent(io_ch, arg, indent))
525                    return -1;
526                outlen += indent;
527            }
528        }
529        prev = ent->set;
530        fn = X509_NAME_ENTRY_get_object(ent);
531        val = X509_NAME_ENTRY_get_data(ent);
532        fn_nid = OBJ_obj2nid(fn);
533        if (fn_opt != XN_FLAG_FN_NONE) {
534            int objlen, fld_len;
535            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
536                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
537                fld_len = 0;    /* XXX: what should this be? */
538                objbuf = objtmp;
539            } else {
540                if (fn_opt == XN_FLAG_FN_SN) {
541                    fld_len = FN_WIDTH_SN;
542                    objbuf = OBJ_nid2sn(fn_nid);
543                } else if (fn_opt == XN_FLAG_FN_LN) {
544                    fld_len = FN_WIDTH_LN;
545                    objbuf = OBJ_nid2ln(fn_nid);
546                } else {
547                    fld_len = 0; /* XXX: what should this be? */
548                    objbuf = "";
549                }
550            }
551            objlen = strlen(objbuf);
552            if (!io_ch(arg, objbuf, objlen))
553                return -1;
554            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
555                if (!do_indent(io_ch, arg, fld_len - objlen))
556                    return -1;
557                outlen += fld_len - objlen;
558            }
559            if (!io_ch(arg, sep_eq, sep_eq_len))
560                return -1;
561            outlen += objlen + sep_eq_len;
562        }
563        /*
564         * If the field name is unknown then fix up the DER dump flag. We
565         * might want to limit this further so it will DER dump on anything
566         * other than a few 'standard' fields.
567         */
568        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
569            orflags = ASN1_STRFLGS_DUMP_ALL;
570        else
571            orflags = 0;
572
573        len = do_print_ex(io_ch, arg, flags | orflags, val);
574        if (len < 0)
575            return -1;
576        outlen += len;
577    }
578    return outlen;
579}
580
581/* Wrappers round the main functions */
582
583int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent,
584                       unsigned long flags)
585{
586    if (flags == XN_FLAG_COMPAT)
587        return X509_NAME_print(out, nm, indent);
588    return do_name_ex(send_bio_chars, out, nm, indent, flags);
589}
590
591#ifndef OPENSSL_NO_FP_API
592int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent,
593                          unsigned long flags)
594{
595    if (flags == XN_FLAG_COMPAT) {
596        BIO *btmp;
597        int ret;
598        btmp = BIO_new_fp(fp, BIO_NOCLOSE);
599        if (!btmp)
600            return -1;
601        ret = X509_NAME_print(btmp, nm, indent);
602        BIO_free(btmp);
603        return ret;
604    }
605    return do_name_ex(send_fp_chars, fp, nm, indent, flags);
606}
607#endif
608
609int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags)
610{
611    return do_print_ex(send_bio_chars, out, flags, str);
612}
613
614#ifndef OPENSSL_NO_FP_API
615int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags)
616{
617    return do_print_ex(send_fp_chars, fp, flags, str);
618}
619#endif
620
621/*
622 * Utility function: convert any string type to UTF8, returns number of bytes
623 * in output string or a negative error code
624 */
625
626int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
627{
628    ASN1_STRING stmp, *str = &stmp;
629    int mbflag, type, ret;
630    if (!in)
631        return -1;
632    type = in->type;
633    if ((type < 0) || (type > 30))
634        return -1;
635    mbflag = tag2nbyte[type];
636    if (mbflag == -1)
637        return -1;
638    mbflag |= MBSTRING_FLAG;
639    stmp.data = NULL;
640    stmp.length = 0;
641    ret =
642        ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
643                           B_ASN1_UTF8STRING);
644    if (ret < 0)
645        return ret;
646    *out = stmp.data;
647    return stmp.length;
648}
649