Reject empty generation strings.
[openssl.git] / apps / asn1pars.c
1 /* apps/asn1pars.c */
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 /*
60  * A nice addition from Dr Stephen Henson <steve@openssl.org> to add the
61  * -strparse option which parses nested binary structures
62  */
63
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include "apps.h"
68 #include <openssl/err.h>
69 #include <openssl/evp.h>
70 #include <openssl/x509.h>
71 #include <openssl/pem.h>
72
73 /*-
74  * -inform arg  - input format - default PEM (DER or PEM)
75  * -in arg      - input file - default stdin
76  * -i           - indent the details by depth
77  * -offset      - where in the file to start
78  * -length      - how many bytes to use
79  * -oid file    - extra oid description file
80  */
81
82 #undef PROG
83 #define PROG    asn1parse_main
84
85 int MAIN(int, char **);
86
87 static int do_generate(BIO *bio, char *genstr, char *genconf, BUF_MEM *buf);
88
89 int MAIN(int argc, char **argv)
90 {
91     int i, badops = 0, offset = 0, ret = 1, j;
92     unsigned int length = 0;
93     long num, tmplen;
94     BIO *in = NULL, *out = NULL, *b64 = NULL, *derout = NULL;
95     int informat, indent = 0, noout = 0, dump = 0;
96     char *infile = NULL, *str = NULL, *prog, *oidfile = NULL, *derfile = NULL;
97     char *genstr = NULL, *genconf = NULL;
98     unsigned char *tmpbuf;
99     const unsigned char *ctmpbuf;
100     BUF_MEM *buf = NULL;
101     STACK_OF(OPENSSL_STRING) *osk = NULL;
102     ASN1_TYPE *at = NULL;
103
104     informat = FORMAT_PEM;
105
106     apps_startup();
107
108     if (bio_err == NULL)
109         if ((bio_err = BIO_new(BIO_s_file())) != NULL)
110             BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
111
112     if (!load_config(bio_err, NULL))
113         goto end;
114
115     prog = argv[0];
116     argc--;
117     argv++;
118     if ((osk = sk_OPENSSL_STRING_new_null()) == NULL) {
119         BIO_printf(bio_err, "Memory allocation failure\n");
120         goto end;
121     }
122     while (argc >= 1) {
123         if (strcmp(*argv, "-inform") == 0) {
124             if (--argc < 1)
125                 goto bad;
126             informat = str2fmt(*(++argv));
127         } else if (strcmp(*argv, "-in") == 0) {
128             if (--argc < 1)
129                 goto bad;
130             infile = *(++argv);
131         } else if (strcmp(*argv, "-out") == 0) {
132             if (--argc < 1)
133                 goto bad;
134             derfile = *(++argv);
135         } else if (strcmp(*argv, "-i") == 0) {
136             indent = 1;
137         } else if (strcmp(*argv, "-noout") == 0)
138             noout = 1;
139         else if (strcmp(*argv, "-oid") == 0) {
140             if (--argc < 1)
141                 goto bad;
142             oidfile = *(++argv);
143         } else if (strcmp(*argv, "-offset") == 0) {
144             if (--argc < 1)
145                 goto bad;
146             offset = atoi(*(++argv));
147         } else if (strcmp(*argv, "-length") == 0) {
148             if (--argc < 1)
149                 goto bad;
150             length = atoi(*(++argv));
151             if (length == 0)
152                 goto bad;
153         } else if (strcmp(*argv, "-dump") == 0) {
154             dump = -1;
155         } else if (strcmp(*argv, "-dlimit") == 0) {
156             if (--argc < 1)
157                 goto bad;
158             dump = atoi(*(++argv));
159             if (dump <= 0)
160                 goto bad;
161         } else if (strcmp(*argv, "-strparse") == 0) {
162             if (--argc < 1)
163                 goto bad;
164             sk_OPENSSL_STRING_push(osk, *(++argv));
165         } else if (strcmp(*argv, "-genstr") == 0) {
166             if (--argc < 1)
167                 goto bad;
168             genstr = *(++argv);
169         } else if (strcmp(*argv, "-genconf") == 0) {
170             if (--argc < 1)
171                 goto bad;
172             genconf = *(++argv);
173         } else {
174             BIO_printf(bio_err, "unknown option %s\n", *argv);
175             badops = 1;
176             break;
177         }
178         argc--;
179         argv++;
180     }
181
182     if (badops) {
183  bad:
184         BIO_printf(bio_err, "%s [options] <infile\n", prog);
185         BIO_printf(bio_err, "where options are\n");
186         BIO_printf(bio_err, " -inform arg   input format - one of DER PEM\n");
187         BIO_printf(bio_err, " -in arg       input file\n");
188         BIO_printf(bio_err,
189                    " -out arg      output file (output format is always DER\n");
190         BIO_printf(bio_err, " -noout arg    don't produce any output\n");
191         BIO_printf(bio_err, " -offset arg   offset into file\n");
192         BIO_printf(bio_err, " -length arg   length of section in file\n");
193         BIO_printf(bio_err, " -i            indent entries\n");
194         BIO_printf(bio_err, " -dump         dump unknown data in hex form\n");
195         BIO_printf(bio_err,
196                    " -dlimit arg   dump the first arg bytes of unknown data in hex form\n");
197         BIO_printf(bio_err, " -oid file     file of extra oid definitions\n");
198         BIO_printf(bio_err, " -strparse offset\n");
199         BIO_printf(bio_err,
200                    "               a series of these can be used to 'dig' into multiple\n");
201         BIO_printf(bio_err, "               ASN1 blob wrappings\n");
202         BIO_printf(bio_err,
203                    " -genstr str   string to generate ASN1 structure from\n");
204         BIO_printf(bio_err,
205                    " -genconf file file to generate ASN1 structure from\n");
206         goto end;
207     }
208
209     ERR_load_crypto_strings();
210
211     in = BIO_new(BIO_s_file());
212     out = BIO_new(BIO_s_file());
213     if ((in == NULL) || (out == NULL)) {
214         ERR_print_errors(bio_err);
215         goto end;
216     }
217     BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT);
218 #ifdef OPENSSL_SYS_VMS
219     {
220         BIO *tmpbio = BIO_new(BIO_f_linebuffer());
221         out = BIO_push(tmpbio, out);
222     }
223 #endif
224
225     if (oidfile != NULL) {
226         if (BIO_read_filename(in, oidfile) <= 0) {
227             BIO_printf(bio_err, "problems opening %s\n", oidfile);
228             ERR_print_errors(bio_err);
229             goto end;
230         }
231         OBJ_create_objects(in);
232     }
233
234     if (infile == NULL)
235         BIO_set_fp(in, stdin, BIO_NOCLOSE);
236     else {
237         if (BIO_read_filename(in, infile) <= 0) {
238             perror(infile);
239             goto end;
240         }
241     }
242
243     if (derfile) {
244         if (!(derout = BIO_new_file(derfile, "wb"))) {
245             BIO_printf(bio_err, "problems opening %s\n", derfile);
246             ERR_print_errors(bio_err);
247             goto end;
248         }
249     }
250
251     if ((buf = BUF_MEM_new()) == NULL)
252         goto end;
253     if (!BUF_MEM_grow(buf, BUFSIZ * 8))
254         goto end;               /* Pre-allocate :-) */
255
256     if (genstr || genconf) {
257         num = do_generate(bio_err, genstr, genconf, buf);
258         if (num < 0) {
259             ERR_print_errors(bio_err);
260             goto end;
261         }
262     }
263
264     else {
265
266         if (informat == FORMAT_PEM) {
267             BIO *tmp;
268
269             if ((b64 = BIO_new(BIO_f_base64())) == NULL)
270                 goto end;
271             BIO_push(b64, in);
272             tmp = in;
273             in = b64;
274             b64 = tmp;
275         }
276
277         num = 0;
278         for (;;) {
279             if (!BUF_MEM_grow(buf, (int)num + BUFSIZ))
280                 goto end;
281             i = BIO_read(in, &(buf->data[num]), BUFSIZ);
282             if (i <= 0)
283                 break;
284             num += i;
285         }
286     }
287     str = buf->data;
288
289     /* If any structs to parse go through in sequence */
290
291     if (sk_OPENSSL_STRING_num(osk)) {
292         tmpbuf = (unsigned char *)str;
293         tmplen = num;
294         for (i = 0; i < sk_OPENSSL_STRING_num(osk); i++) {
295             ASN1_TYPE *atmp;
296             int typ;
297             j = atoi(sk_OPENSSL_STRING_value(osk, i));
298             if (j == 0) {
299                 BIO_printf(bio_err, "'%s' is an invalid number\n",
300                            sk_OPENSSL_STRING_value(osk, i));
301                 continue;
302             }
303             tmpbuf += j;
304             tmplen -= j;
305             atmp = at;
306             ctmpbuf = tmpbuf;
307             at = d2i_ASN1_TYPE(NULL, &ctmpbuf, tmplen);
308             ASN1_TYPE_free(atmp);
309             if (!at) {
310                 BIO_printf(bio_err, "Error parsing structure\n");
311                 ERR_print_errors(bio_err);
312                 goto end;
313             }
314             typ = ASN1_TYPE_get(at);
315             if ((typ == V_ASN1_OBJECT)
316                 || (typ == V_ASN1_NULL)) {
317                 BIO_printf(bio_err, "Can't parse %s type\n",
318                            typ == V_ASN1_NULL ? "NULL" : "OBJECT");
319                 ERR_print_errors(bio_err);
320                 goto end;
321             }
322             /* hmm... this is a little evil but it works */
323             tmpbuf = at->value.asn1_string->data;
324             tmplen = at->value.asn1_string->length;
325         }
326         str = (char *)tmpbuf;
327         num = tmplen;
328     }
329
330     if (offset >= num) {
331         BIO_printf(bio_err, "Error: offset too large\n");
332         goto end;
333     }
334
335     num -= offset;
336
337     if ((length == 0) || ((long)length > num))
338         length = (unsigned int)num;
339     if (derout) {
340         if (BIO_write(derout, str + offset, length) != (int)length) {
341             BIO_printf(bio_err, "Error writing output\n");
342             ERR_print_errors(bio_err);
343             goto end;
344         }
345     }
346     if (!noout &&
347         !ASN1_parse_dump(out, (unsigned char *)&(str[offset]), length,
348                          indent, dump)) {
349         ERR_print_errors(bio_err);
350         goto end;
351     }
352     ret = 0;
353  end:
354     BIO_free(derout);
355     if (in != NULL)
356         BIO_free(in);
357     if (out != NULL)
358         BIO_free_all(out);
359     if (b64 != NULL)
360         BIO_free(b64);
361     if (ret != 0)
362         ERR_print_errors(bio_err);
363     if (buf != NULL)
364         BUF_MEM_free(buf);
365     if (at != NULL)
366         ASN1_TYPE_free(at);
367     if (osk != NULL)
368         sk_OPENSSL_STRING_free(osk);
369     OBJ_cleanup();
370     apps_shutdown();
371     OPENSSL_EXIT(ret);
372 }
373
374 static int do_generate(BIO *bio, char *genstr, char *genconf, BUF_MEM *buf)
375 {
376     CONF *cnf = NULL;
377     int len;
378     long errline;
379     unsigned char *p;
380     ASN1_TYPE *atyp = NULL;
381
382     if (genconf) {
383         cnf = NCONF_new(NULL);
384         if (!NCONF_load(cnf, genconf, &errline))
385             goto conferr;
386         if (!genstr)
387             genstr = NCONF_get_string(cnf, "default", "asn1");
388         if (!genstr) {
389             BIO_printf(bio, "Can't find 'asn1' in '%s'\n", genconf);
390             goto err;
391         }
392     }
393
394     atyp = ASN1_generate_nconf(genstr, cnf);
395     NCONF_free(cnf);
396     cnf = NULL;
397
398     if (!atyp)
399         return -1;
400
401     len = i2d_ASN1_TYPE(atyp, NULL);
402
403     if (len <= 0)
404         goto err;
405
406     if (!BUF_MEM_grow(buf, len))
407         goto err;
408
409     p = (unsigned char *)buf->data;
410
411     i2d_ASN1_TYPE(atyp, &p);
412
413     ASN1_TYPE_free(atyp);
414     return len;
415
416  conferr:
417
418     if (errline > 0)
419         BIO_printf(bio, "Error on line %ld of config file '%s'\n",
420                    errline, genconf);
421     else
422         BIO_printf(bio, "Error loading config file '%s'\n", genconf);
423
424  err:
425     NCONF_free(cnf);
426     ASN1_TYPE_free(atyp);
427
428     return -1;
429
430 }