1/*
2 * Copyright 1995-2023 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 "internal/cryptlib.h"
12#include <openssl/buffer.h>
13#include <openssl/objects.h>
14#include <openssl/asn1.h>
15
16#ifndef ASN1_PARSE_MAXDEPTH
17#define ASN1_PARSE_MAXDEPTH 128
18#endif
19
20static int asn1_parse2(BIO *bp, const unsigned char **pp, long length,
21                       int offset, int depth, int indent, int dump);
22static int asn1_print_info(BIO *bp, long offset, int depth, int hl, long len,
23                           int tag, int xclass, int constructed, int indent)
24{
25    char str[128];
26    const char *p;
27    int pop_f_prefix = 0;
28    long saved_indent = -1;
29    int i = 0;
30    BIO *bio = NULL;
31
32    if (constructed & V_ASN1_CONSTRUCTED)
33        p = "cons: ";
34    else
35        p = "prim: ";
36    if (constructed != (V_ASN1_CONSTRUCTED | 1)) {
37        if (BIO_snprintf(str, sizeof(str), "%5ld:d=%-2d hl=%ld l=%4ld %s",
38                         offset, depth, (long)hl, len, p) <= 0)
39            goto err;
40    } else {
41        if (BIO_snprintf(str, sizeof(str), "%5ld:d=%-2d hl=%ld l=inf  %s",
42                         offset, depth, (long)hl, p) <= 0)
43            goto err;
44    }
45    if (bp != NULL) {
46        if (BIO_set_prefix(bp, str) <= 0) {
47            if ((bio = BIO_new(BIO_f_prefix())) == NULL
48                    || (bp = BIO_push(bio, bp)) == NULL)
49                goto err;
50            pop_f_prefix = 1;
51        }
52        saved_indent = BIO_get_indent(bp);
53        if (BIO_set_prefix(bp, str) <= 0 || BIO_set_indent(bp, indent) <= 0)
54            goto err;
55    }
56
57    /*
58     * BIO_set_prefix made a copy of |str|, so we can safely use it for
59     * something else, ASN.1 tag printout.
60     */
61    p = str;
62    if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
63        BIO_snprintf(str, sizeof(str), "priv [ %d ] ", tag);
64    else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
65        BIO_snprintf(str, sizeof(str), "cont [ %d ]", tag);
66    else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
67        BIO_snprintf(str, sizeof(str), "appl [ %d ]", tag);
68    else if (tag > 30)
69        BIO_snprintf(str, sizeof(str), "<ASN1 %d>", tag);
70    else
71        p = ASN1_tag2str(tag);
72
73    i = (BIO_printf(bp, "%-18s", p) > 0);
74 err:
75    if (saved_indent >= 0)
76        BIO_set_indent(bp, saved_indent);
77    if (pop_f_prefix)
78        BIO_pop(bp);
79    BIO_free(bio);
80    return i;
81}
82
83int ASN1_parse(BIO *bp, const unsigned char *pp, long len, int indent)
84{
85    return asn1_parse2(bp, &pp, len, 0, 0, indent, 0);
86}
87
88int ASN1_parse_dump(BIO *bp, const unsigned char *pp, long len, int indent,
89                    int dump)
90{
91    return asn1_parse2(bp, &pp, len, 0, 0, indent, dump);
92}
93
94static int asn1_parse2(BIO *bp, const unsigned char **pp, long length,
95                       int offset, int depth, int indent, int dump)
96{
97    const unsigned char *p, *ep, *tot, *op, *opp;
98    long len;
99    int tag, xclass, ret = 0;
100    int nl, hl, j, r;
101    ASN1_OBJECT *o = NULL;
102    ASN1_OCTET_STRING *os = NULL;
103    ASN1_INTEGER *ai = NULL;
104    ASN1_ENUMERATED *ae = NULL;
105    /* ASN1_BMPSTRING *bmp=NULL; */
106    int dump_indent, dump_cont = 0;
107
108    if (depth > ASN1_PARSE_MAXDEPTH) {
109        BIO_puts(bp, "BAD RECURSION DEPTH\n");
110        return 0;
111    }
112
113    dump_indent = 6;            /* Because we know BIO_dump_indent() */
114    p = *pp;
115    tot = p + length;
116    while (length > 0) {
117        op = p;
118        j = ASN1_get_object(&p, &len, &tag, &xclass, length);
119        if (j & 0x80) {
120            BIO_puts(bp, "Error in encoding\n");
121            goto end;
122        }
123        hl = (p - op);
124        length -= hl;
125        /*
126         * if j == 0x21 it is a constructed indefinite length object
127         */
128        if (!asn1_print_info(bp, (long)offset + (long)(op - *pp), depth,
129                             hl, len, tag, xclass, j, (indent) ? depth : 0))
130            goto end;
131        if (j & V_ASN1_CONSTRUCTED) {
132            const unsigned char *sp = p;
133
134            ep = p + len;
135            if (BIO_write(bp, "\n", 1) <= 0)
136                goto end;
137            if (len > length) {
138                BIO_printf(bp, "length is greater than %ld\n", length);
139                goto end;
140            }
141            if ((j == 0x21) && (len == 0)) {
142                for (;;) {
143                    r = asn1_parse2(bp, &p, (long)(tot - p),
144                                    offset + (p - *pp), depth + 1,
145                                    indent, dump);
146                    if (r == 0)
147                        goto end;
148                    if ((r == 2) || (p >= tot)) {
149                        len = p - sp;
150                        break;
151                    }
152                }
153            } else {
154                long tmp = len;
155
156                while (p < ep) {
157                    sp = p;
158                    r = asn1_parse2(bp, &p, tmp,
159                                    offset + (p - *pp), depth + 1,
160                                    indent, dump);
161                    if (r == 0)
162                        goto end;
163                    tmp -= p - sp;
164                }
165            }
166        } else if (xclass != 0) {
167            p += len;
168            if (BIO_write(bp, "\n", 1) <= 0)
169                goto end;
170        } else {
171            nl = 0;
172            if ((tag == V_ASN1_PRINTABLESTRING) ||
173                (tag == V_ASN1_T61STRING) ||
174                (tag == V_ASN1_IA5STRING) ||
175                (tag == V_ASN1_VISIBLESTRING) ||
176                (tag == V_ASN1_NUMERICSTRING) ||
177                (tag == V_ASN1_UTF8STRING) ||
178                (tag == V_ASN1_UTCTIME) || (tag == V_ASN1_GENERALIZEDTIME)) {
179                if (BIO_write(bp, ":", 1) <= 0)
180                    goto end;
181                if ((len > 0) && BIO_write(bp, (const char *)p, (int)len)
182                    != (int)len)
183                    goto end;
184            } else if (tag == V_ASN1_OBJECT) {
185                opp = op;
186                if (d2i_ASN1_OBJECT(&o, &opp, len + hl) != NULL) {
187                    if (BIO_write(bp, ":", 1) <= 0)
188                        goto end;
189                    i2a_ASN1_OBJECT(bp, o);
190                } else {
191                    if (BIO_puts(bp, ":BAD OBJECT") <= 0)
192                        goto end;
193                    dump_cont = 1;
194                }
195            } else if (tag == V_ASN1_BOOLEAN) {
196                if (len != 1) {
197                    if (BIO_puts(bp, ":BAD BOOLEAN") <= 0)
198                        goto end;
199                    dump_cont = 1;
200                }
201                if (len > 0)
202                    BIO_printf(bp, ":%u", p[0]);
203            } else if (tag == V_ASN1_BMPSTRING) {
204                /* do the BMP thang */
205            } else if (tag == V_ASN1_OCTET_STRING) {
206                int i, printable = 1;
207
208                opp = op;
209                os = d2i_ASN1_OCTET_STRING(NULL, &opp, len + hl);
210                if (os != NULL && os->length > 0) {
211                    opp = os->data;
212                    /*
213                     * testing whether the octet string is printable
214                     */
215                    for (i = 0; i < os->length; i++) {
216                        if (((opp[i] < ' ') &&
217                             (opp[i] != '\n') &&
218                             (opp[i] != '\r') &&
219                             (opp[i] != '\t')) || (opp[i] > '~')) {
220                            printable = 0;
221                            break;
222                        }
223                    }
224                    if (printable)
225                        /* printable string */
226                    {
227                        if (BIO_write(bp, ":", 1) <= 0)
228                            goto end;
229                        if (BIO_write(bp, (const char *)opp, os->length) <= 0)
230                            goto end;
231                    } else if (!dump)
232                        /*
233                         * not printable => print octet string as hex dump
234                         */
235                    {
236                        if (BIO_write(bp, "[HEX DUMP]:", 11) <= 0)
237                            goto end;
238                        for (i = 0; i < os->length; i++) {
239                            if (BIO_printf(bp, "%02X", opp[i]) <= 0)
240                                goto end;
241                        }
242                    } else
243                        /* print the normal dump */
244                    {
245                        if (!nl) {
246                            if (BIO_write(bp, "\n", 1) <= 0)
247                                goto end;
248                        }
249                        if (BIO_dump_indent(bp,
250                                            (const char *)opp,
251                                            ((dump == -1 || dump >
252                                              os->
253                                              length) ? os->length : dump),
254                                            dump_indent) <= 0)
255                            goto end;
256                        nl = 1;
257                    }
258                }
259                ASN1_OCTET_STRING_free(os);
260                os = NULL;
261            } else if (tag == V_ASN1_INTEGER) {
262                int i;
263
264                opp = op;
265                ai = d2i_ASN1_INTEGER(NULL, &opp, len + hl);
266                if (ai != NULL) {
267                    if (BIO_write(bp, ":", 1) <= 0)
268                        goto end;
269                    if (ai->type == V_ASN1_NEG_INTEGER)
270                        if (BIO_write(bp, "-", 1) <= 0)
271                            goto end;
272                    for (i = 0; i < ai->length; i++) {
273                        if (BIO_printf(bp, "%02X", ai->data[i]) <= 0)
274                            goto end;
275                    }
276                    if (ai->length == 0) {
277                        if (BIO_write(bp, "00", 2) <= 0)
278                            goto end;
279                    }
280                } else {
281                    if (BIO_puts(bp, ":BAD INTEGER") <= 0)
282                        goto end;
283                    dump_cont = 1;
284                }
285                ASN1_INTEGER_free(ai);
286                ai = NULL;
287            } else if (tag == V_ASN1_ENUMERATED) {
288                int i;
289
290                opp = op;
291                ae = d2i_ASN1_ENUMERATED(NULL, &opp, len + hl);
292                if (ae != NULL) {
293                    if (BIO_write(bp, ":", 1) <= 0)
294                        goto end;
295                    if (ae->type == V_ASN1_NEG_ENUMERATED)
296                        if (BIO_write(bp, "-", 1) <= 0)
297                            goto end;
298                    for (i = 0; i < ae->length; i++) {
299                        if (BIO_printf(bp, "%02X", ae->data[i]) <= 0)
300                            goto end;
301                    }
302                    if (ae->length == 0) {
303                        if (BIO_write(bp, "00", 2) <= 0)
304                            goto end;
305                    }
306                } else {
307                    if (BIO_puts(bp, ":BAD ENUMERATED") <= 0)
308                        goto end;
309                    dump_cont = 1;
310                }
311                ASN1_ENUMERATED_free(ae);
312                ae = NULL;
313            } else if (len > 0 && dump) {
314                if (!nl) {
315                    if (BIO_write(bp, "\n", 1) <= 0)
316                        goto end;
317                }
318                if (BIO_dump_indent(bp, (const char *)p,
319                                    ((dump == -1 || dump > len) ? len : dump),
320                                    dump_indent) <= 0)
321                    goto end;
322                nl = 1;
323            }
324            if (dump_cont) {
325                int i;
326                const unsigned char *tmp = op + hl;
327                if (BIO_puts(bp, ":[") <= 0)
328                    goto end;
329                for (i = 0; i < len; i++) {
330                    if (BIO_printf(bp, "%02X", tmp[i]) <= 0)
331                        goto end;
332                }
333                if (BIO_puts(bp, "]") <= 0)
334                    goto end;
335                dump_cont = 0;
336            }
337
338            if (!nl) {
339                if (BIO_write(bp, "\n", 1) <= 0)
340                    goto end;
341            }
342            p += len;
343            if ((tag == V_ASN1_EOC) && (xclass == 0)) {
344                ret = 2;        /* End of sequence */
345                goto end;
346            }
347        }
348        length -= len;
349    }
350    ret = 1;
351 end:
352    ASN1_OBJECT_free(o);
353    ASN1_OCTET_STRING_free(os);
354    ASN1_INTEGER_free(ai);
355    ASN1_ENUMERATED_free(ae);
356    *pp = p;
357    return ret;
358}
359
360const char *ASN1_tag2str(int tag)
361{
362    static const char *const tag2str[] = {
363        /* 0-4 */
364        "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING",
365        /* 5-9 */
366        "NULL", "OBJECT", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL",
367        /* 10-13 */
368        "ENUMERATED", "<ASN1 11>", "UTF8STRING", "<ASN1 13>",
369        /* 15-17 */
370        "<ASN1 14>", "<ASN1 15>", "SEQUENCE", "SET",
371        /* 18-20 */
372        "NUMERICSTRING", "PRINTABLESTRING", "T61STRING",
373        /* 21-24 */
374        "VIDEOTEXSTRING", "IA5STRING", "UTCTIME", "GENERALIZEDTIME",
375        /* 25-27 */
376        "GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING",
377        /* 28-30 */
378        "UNIVERSALSTRING", "<ASN1 29>", "BMPSTRING"
379    };
380
381    if ((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
382        tag &= ~0x100;
383
384    if (tag < 0 || tag > 30)
385        return "(unknown)";
386    return tag2str[tag];
387}
388