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