[BACK]Return to asn1pars.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / openssl

Annotation of src/usr.bin/openssl/asn1pars.c, Revision 1.14

1.14    ! tb          1: /* $OpenBSD: asn1pars.c,v 1.13 2023/03/06 14:32:05 tb Exp $ */
1.1       jsing       2: /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
                      3:  * All rights reserved.
                      4:  *
                      5:  * This package is an SSL implementation written
                      6:  * by Eric Young (eay@cryptsoft.com).
                      7:  * The implementation was written so as to conform with Netscapes SSL.
                      8:  *
                      9:  * This library is free for commercial and non-commercial use as long as
                     10:  * the following conditions are aheared to.  The following conditions
                     11:  * apply to all code found in this distribution, be it the RC4, RSA,
                     12:  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
                     13:  * included with this distribution is covered by the same copyright terms
                     14:  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
                     15:  *
                     16:  * Copyright remains Eric Young's, and as such any Copyright notices in
                     17:  * the code are not to be removed.
                     18:  * If this package is used in a product, Eric Young should be given attribution
                     19:  * as the author of the parts of the library used.
                     20:  * This can be in the form of a textual message at program startup or
                     21:  * in documentation (online or textual) provided with the package.
                     22:  *
                     23:  * Redistribution and use in source and binary forms, with or without
                     24:  * modification, are permitted provided that the following conditions
                     25:  * are met:
                     26:  * 1. Redistributions of source code must retain the copyright
                     27:  *    notice, this list of conditions and the following disclaimer.
                     28:  * 2. Redistributions in binary form must reproduce the above copyright
                     29:  *    notice, this list of conditions and the following disclaimer in the
                     30:  *    documentation and/or other materials provided with the distribution.
                     31:  * 3. All advertising materials mentioning features or use of this software
                     32:  *    must display the following acknowledgement:
                     33:  *    "This product includes cryptographic software written by
                     34:  *     Eric Young (eay@cryptsoft.com)"
                     35:  *    The word 'cryptographic' can be left out if the rouines from the library
                     36:  *    being used are not cryptographic related :-).
                     37:  * 4. If you include any Windows specific code (or a derivative thereof) from
                     38:  *    the apps directory (application code) you must include an acknowledgement:
                     39:  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
                     40:  *
                     41:  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
                     42:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     43:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     44:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
                     45:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     46:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     47:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     48:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     49:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     50:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     51:  * SUCH DAMAGE.
                     52:  *
                     53:  * The licence and distribution terms for any publically available version or
                     54:  * derivative of this code cannot be changed.  i.e. this code cannot simply be
                     55:  * copied and put under another distribution licence
                     56:  * [including the GNU Public Licence.]
                     57:  */
                     58:
                     59: /* A nice addition from Dr Stephen Henson <steve@openssl.org> to
                     60:  * add the -strparse option which parses nested binary structures
                     61:  */
                     62:
                     63: #include <stdio.h>
                     64: #include <stdlib.h>
                     65: #include <limits.h>
                     66: #include <string.h>
                     67:
                     68: #include "apps.h"
1.4       deraadt    69: #include "progs.h"
1.1       jsing      70:
                     71: #include <openssl/err.h>
                     72: #include <openssl/evp.h>
                     73: #include <openssl/pem.h>
                     74: #include <openssl/x509.h>
                     75:
1.3       doug       76: static struct {
                     77:        char *derfile;
                     78:        int dump;
                     79:        char *genconf;
                     80:        char *genstr;
                     81:        int indent;
                     82:        char *infile;
                     83:        int informat;
                     84:        unsigned int length;
                     85:        int noout;
                     86:        int offset;
                     87:        char *oidfile;
                     88:        STACK_OF(OPENSSL_STRING) *osk;
1.13      tb         89: } cfg;
1.3       doug       90:
                     91: static int
                     92: asn1pars_opt_dlimit(char *arg)
                     93: {
                     94:        const char *errstr;
                     95:
1.13      tb         96:        cfg.dump = strtonum(arg, 1, INT_MAX, &errstr);
1.3       doug       97:        if (errstr) {
                     98:                fprintf(stderr, "-dlimit must be from 1 to INT_MAX: %s\n",
                     99:                    errstr);
                    100:                return (-1);
                    101:        }
                    102:        return (0);
                    103: }
                    104:
                    105: static int
                    106: asn1pars_opt_length(char *arg)
                    107: {
                    108:        const char *errstr;
                    109:
1.13      tb        110:        cfg.length = strtonum(arg, 1, UINT_MAX, &errstr);
1.3       doug      111:        if (errstr) {
                    112:                fprintf(stderr, "-length must be from 1 to UINT_MAX: %s\n",
                    113:                    errstr);
                    114:                return (-1);
                    115:        }
                    116:        return (0);
                    117: }
                    118:
                    119: static int
                    120: asn1pars_opt_strparse(char *arg)
                    121: {
1.13      tb        122:        if (sk_OPENSSL_STRING_push(cfg.osk, arg) == 0) {
1.3       doug      123:                fprintf(stderr, "-strparse cannot add argument\n");
                    124:                return (-1);
                    125:        }
                    126:        return (0);
                    127: }
                    128:
1.10      guenther  129: static const struct option asn1pars_options[] = {
1.3       doug      130:        {
                    131:                .name = "dump",
                    132:                .desc = "Dump unknown data in hex form",
                    133:                .type = OPTION_VALUE,
                    134:                .value = -1,
1.13      tb        135:                .opt.value = &cfg.dump,
1.3       doug      136:        },
                    137:        {
                    138:                .name = "dlimit",
                    139:                .argname = "num",
                    140:                .desc = "Dump the first num bytes of unknown data in hex form",
                    141:                .type = OPTION_ARG_FUNC,
                    142:                .opt.argfunc = asn1pars_opt_dlimit,
                    143:        },
                    144:        {
                    145:                .name = "genconf",
                    146:                .argname = "file",
                    147:                .desc = "File to generate ASN.1 structure from",
                    148:                .type = OPTION_ARG,
1.13      tb        149:                .opt.arg = &cfg.genconf,
1.3       doug      150:        },
                    151:        {
                    152:                .name = "genstr",
                    153:                .argname = "string",
                    154:                .desc = "String to generate ASN.1 structure from",
                    155:                .type = OPTION_ARG,
1.13      tb        156:                .opt.arg = &cfg.genstr,
1.3       doug      157:        },
                    158:        {
                    159:                .name = "i",
                    160:                .desc = "Indent output according to depth of structures",
                    161:                .type = OPTION_FLAG,
1.13      tb        162:                .opt.flag = &cfg.indent,
1.3       doug      163:        },
                    164:        {
                    165:                .name = "in",
                    166:                .argname = "file",
                    167:                .desc = "The input file (default stdin)",
                    168:                .type = OPTION_ARG,
1.13      tb        169:                .opt.arg = &cfg.infile,
1.3       doug      170:        },
                    171:        {
                    172:                .name = "inform",
                    173:                .argname = "fmt",
                    174:                .desc = "Input format (DER, TXT or PEM (default))",
                    175:                .type = OPTION_ARG_FORMAT,
1.13      tb        176:                .opt.value = &cfg.informat,
1.3       doug      177:        },
                    178:        {
                    179:                .name = "length",
                    180:                .argname = "num",
                    181:                .desc = "Number of bytes to parse (default until EOF)",
                    182:                .type = OPTION_ARG_FUNC,
                    183:                .opt.argfunc = asn1pars_opt_length,
                    184:        },
                    185:        {
                    186:                .name = "noout",
                    187:                .desc = "Do not produce any output",
                    188:                .type = OPTION_FLAG,
1.13      tb        189:                .opt.flag = &cfg.noout,
1.3       doug      190:        },
                    191:        {
                    192:                .name = "offset",
                    193:                .argname = "num",
                    194:                .desc = "Offset to begin parsing",
                    195:                .type = OPTION_ARG_INT,
1.13      tb        196:                .opt.value = &cfg.offset,
1.3       doug      197:        },
                    198:        {
                    199:                .name = "oid",
                    200:                .argname = "file",
                    201:                .desc = "File containing additional object identifiers (OIDs)",
                    202:                .type = OPTION_ARG,
1.13      tb        203:                .opt.arg = &cfg.oidfile,
1.3       doug      204:        },
                    205:        {
                    206:                .name = "out",
                    207:                .argname = "file",
                    208:                .desc = "Output file in DER format",
                    209:                .type = OPTION_ARG,
1.13      tb        210:                .opt.arg = &cfg.derfile,
1.3       doug      211:        },
                    212:        {
                    213:                .name = "strparse",
                    214:                .argname = "offset",
                    215:                .desc = "Parse the content octets of ASN.1 object starting at"
                    216:                " offset",
                    217:                .type = OPTION_ARG_FUNC,
                    218:                .opt.argfunc = asn1pars_opt_strparse,
                    219:        },
                    220:        { NULL },
                    221: };
1.1       jsing     222:
1.3       doug      223: static void
                    224: asn1pars_usage()
                    225: {
                    226:        fprintf(stderr,
                    227:            "usage: asn1parse [-i] [-dlimit num] [-dump] [-genconf file] "
                    228:            "[-genstr string]\n"
                    229:            "    [-in file] [-inform fmt] [-length num] [-noout] [-offset num] "
                    230:            "[-oid file]\n"
                    231:            "    [-out file] [-strparse offset]\n\n");
                    232:        options_usage(asn1pars_options);
                    233: }
1.1       jsing     234:
1.3       doug      235: static int do_generate(BIO *bio, char *genstr, char *genconf, BUF_MEM *buf);
1.1       jsing     236:
                    237: int
                    238: asn1parse_main(int argc, char **argv)
                    239: {
1.3       doug      240:        int i, j, ret = 1;
1.1       jsing     241:        long num, tmplen;
                    242:        BIO *in = NULL, *out = NULL, *b64 = NULL, *derout = NULL;
1.3       doug      243:        char *str = NULL;
1.1       jsing     244:        const char *errstr = NULL;
                    245:        unsigned char *tmpbuf;
                    246:        const unsigned char *ctmpbuf;
                    247:        BUF_MEM *buf = NULL;
                    248:        ASN1_TYPE *at = NULL;
1.5       doug      249:
1.12      joshua    250:        if (pledge("stdio cpath wpath rpath", NULL) == -1) {
                    251:                perror("pledge");
                    252:                exit(1);
1.5       doug      253:        }
1.1       jsing     254:
1.13      tb        255:        memset(&cfg, 0, sizeof(cfg));
1.1       jsing     256:
1.13      tb        257:        cfg.informat = FORMAT_PEM;
                    258:        if ((cfg.osk = sk_OPENSSL_STRING_new_null()) == NULL) {
1.1       jsing     259:                BIO_printf(bio_err, "Memory allocation failure\n");
                    260:                goto end;
                    261:        }
                    262:
1.3       doug      263:        if (options_parse(argc, argv, asn1pars_options, NULL, NULL) != 0) {
                    264:                asn1pars_usage();
                    265:                return (1);
1.1       jsing     266:        }
                    267:
                    268:        in = BIO_new(BIO_s_file());
                    269:        out = BIO_new(BIO_s_file());
1.14    ! tb        270:        if (in == NULL || out == NULL) {
1.1       jsing     271:                ERR_print_errors(bio_err);
                    272:                goto end;
                    273:        }
                    274:        BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT);
                    275:
1.13      tb        276:        if (cfg.oidfile != NULL) {
                    277:                if (BIO_read_filename(in, cfg.oidfile) <= 0) {
1.3       doug      278:                        BIO_printf(bio_err, "problems opening %s\n",
1.13      tb        279:                            cfg.oidfile);
1.1       jsing     280:                        ERR_print_errors(bio_err);
                    281:                        goto end;
                    282:                }
                    283:                OBJ_create_objects(in);
                    284:        }
1.13      tb        285:        if (cfg.infile == NULL)
1.1       jsing     286:                BIO_set_fp(in, stdin, BIO_NOCLOSE);
                    287:        else {
1.13      tb        288:                if (BIO_read_filename(in, cfg.infile) <= 0) {
                    289:                        perror(cfg.infile);
1.1       jsing     290:                        goto end;
                    291:                }
                    292:        }
                    293:
1.14    ! tb        294:        if (cfg.derfile != NULL) {
        !           295:                if ((derout = BIO_new_file(cfg.derfile, "wb")) == NULL) {
1.3       doug      296:                        BIO_printf(bio_err, "problems opening %s\n",
1.13      tb        297:                            cfg.derfile);
1.1       jsing     298:                        ERR_print_errors(bio_err);
                    299:                        goto end;
                    300:                }
                    301:        }
                    302:        if ((buf = BUF_MEM_new()) == NULL)
                    303:                goto end;
                    304:        if (!BUF_MEM_grow(buf, BUFSIZ * 8))
1.14    ! tb        305:                goto end;
1.1       jsing     306:
1.14    ! tb        307:        if (cfg.genstr != NULL || cfg.genconf) {
        !           308:                num = do_generate(bio_err, cfg.genstr, cfg.genconf, buf);
1.1       jsing     309:                if (num < 0) {
                    310:                        ERR_print_errors(bio_err);
                    311:                        goto end;
                    312:                }
                    313:        } else {
1.13      tb        314:                if (cfg.informat == FORMAT_PEM) {
1.1       jsing     315:                        BIO *tmp;
                    316:
                    317:                        if ((b64 = BIO_new(BIO_f_base64())) == NULL)
                    318:                                goto end;
                    319:                        BIO_push(b64, in);
                    320:                        tmp = in;
                    321:                        in = b64;
                    322:                        b64 = tmp;
                    323:                }
                    324:                num = 0;
                    325:                for (;;) {
                    326:                        if (!BUF_MEM_grow(buf, (int) num + BUFSIZ))
                    327:                                goto end;
                    328:                        i = BIO_read(in, &(buf->data[num]), BUFSIZ);
                    329:                        if (i <= 0)
                    330:                                break;
                    331:                        num += i;
                    332:                }
                    333:        }
                    334:        str = buf->data;
                    335:
                    336:        /* If any structs to parse go through in sequence */
                    337:
1.14    ! tb        338:        if (sk_OPENSSL_STRING_num(cfg.osk) > 0) {
1.1       jsing     339:                tmpbuf = (unsigned char *) str;
                    340:                tmplen = num;
1.14    ! tb        341:                for (i = 0; i < sk_OPENSSL_STRING_num(cfg.osk); i++) {
1.1       jsing     342:                        ASN1_TYPE *atmp;
                    343:                        int typ;
1.14    ! tb        344:                        j = strtonum(sk_OPENSSL_STRING_value(cfg.osk, i),
1.1       jsing     345:                            1, INT_MAX, &errstr);
                    346:                        if (errstr) {
                    347:                                BIO_printf(bio_err,
                    348:                                    "'%s' is an invalid number: %s\n",
1.14    ! tb        349:                                    sk_OPENSSL_STRING_value(cfg.osk, i), errstr);
1.1       jsing     350:                                continue;
                    351:                        }
                    352:                        tmpbuf += j;
                    353:                        tmplen -= j;
                    354:                        atmp = at;
                    355:                        ctmpbuf = tmpbuf;
                    356:                        at = d2i_ASN1_TYPE(NULL, &ctmpbuf, tmplen);
                    357:                        ASN1_TYPE_free(atmp);
                    358:                        if (!at) {
                    359:                                BIO_printf(bio_err, "Error parsing structure\n");
                    360:                                ERR_print_errors(bio_err);
                    361:                                goto end;
                    362:                        }
                    363:                        typ = ASN1_TYPE_get(at);
1.14    ! tb        364:                        if (typ == V_ASN1_OBJECT || typ == V_ASN1_NULL) {
1.1       jsing     365:                                BIO_printf(bio_err, "Can't parse %s type\n",
                    366:                                    typ == V_ASN1_NULL ? "NULL" : "OBJECT");
                    367:                                ERR_print_errors(bio_err);
                    368:                                goto end;
                    369:                        }
                    370:                        /* hmm... this is a little evil but it works */
                    371:                        tmpbuf = at->value.asn1_string->data;
                    372:                        tmplen = at->value.asn1_string->length;
                    373:                }
                    374:                str = (char *) tmpbuf;
                    375:                num = tmplen;
                    376:        }
1.13      tb        377:        if (cfg.offset >= num) {
1.1       jsing     378:                BIO_printf(bio_err, "Error: offset too large\n");
                    379:                goto end;
                    380:        }
1.13      tb        381:        num -= cfg.offset;
1.1       jsing     382:
1.14    ! tb        383:        if (cfg.length == 0 || (long)cfg.length > num)
1.13      tb        384:                cfg.length = (unsigned int) num;
1.14    ! tb        385:        if (derout != NULL) {
1.13      tb        386:                if (BIO_write(derout, str + cfg.offset,
                    387:                    cfg.length) != (int)cfg.length) {
1.1       jsing     388:                        BIO_printf(bio_err, "Error writing output\n");
                    389:                        ERR_print_errors(bio_err);
                    390:                        goto end;
                    391:                }
                    392:        }
1.14    ! tb        393:        if (!cfg.noout && !ASN1_parse_dump(out,
        !           394:            (unsigned char *)&str[cfg.offset], cfg.length, cfg.indent, cfg.dump)) {
1.1       jsing     395:                ERR_print_errors(bio_err);
                    396:                goto end;
                    397:        }
                    398:        ret = 0;
1.9       jsing     399:  end:
1.1       jsing     400:        BIO_free(derout);
1.3       doug      401:        BIO_free(in);
                    402:        BIO_free_all(out);
                    403:        BIO_free(b64);
1.1       jsing     404:        if (ret != 0)
                    405:                ERR_print_errors(bio_err);
1.3       doug      406:        BUF_MEM_free(buf);
1.8       jsing     407:        ASN1_TYPE_free(at);
1.13      tb        408:        sk_OPENSSL_STRING_free(cfg.osk);
1.1       jsing     409:        OBJ_cleanup();
                    410:
                    411:        return (ret);
                    412: }
                    413:
                    414: static int
1.11      tb        415: do_generate(BIO *bio, char *genstr, char *genconf, BUF_MEM *buf)
1.1       jsing     416: {
                    417:        CONF *cnf = NULL;
                    418:        int len;
                    419:        long errline;
                    420:        unsigned char *p;
                    421:        ASN1_TYPE *atyp = NULL;
                    422:
                    423:        if (genconf) {
                    424:                cnf = NCONF_new(NULL);
                    425:                if (!NCONF_load(cnf, genconf, &errline))
                    426:                        goto conferr;
                    427:                if (!genstr)
                    428:                        genstr = NCONF_get_string(cnf, "default", "asn1");
                    429:                if (!genstr) {
                    430:                        BIO_printf(bio, "Can't find 'asn1' in '%s'\n", genconf);
                    431:                        goto err;
                    432:                }
                    433:        }
                    434:        atyp = ASN1_generate_nconf(genstr, cnf);
                    435:        NCONF_free(cnf);
                    436:        cnf = NULL;
                    437:
                    438:        if (!atyp)
                    439:                return -1;
                    440:
                    441:        len = i2d_ASN1_TYPE(atyp, NULL);
                    442:        if (len <= 0)
                    443:                goto err;
                    444:
                    445:        if (!BUF_MEM_grow(buf, len))
                    446:                goto err;
                    447:
                    448:        p = (unsigned char *) buf->data;
                    449:
                    450:        i2d_ASN1_TYPE(atyp, &p);
                    451:
                    452:        ASN1_TYPE_free(atyp);
                    453:        return len;
                    454:
1.9       jsing     455:  conferr:
1.1       jsing     456:
                    457:        if (errline > 0)
                    458:                BIO_printf(bio, "Error on line %ld of config file '%s'\n",
                    459:                    errline, genconf);
                    460:        else
                    461:                BIO_printf(bio, "Error loading config file '%s'\n", genconf);
                    462:
1.9       jsing     463:  err:
1.1       jsing     464:        NCONF_free(cnf);
                    465:        ASN1_TYPE_free(atyp);
                    466:
                    467:        return -1;
                    468:
                    469: }